]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_pppoe.c
- Remove debugging printfs.
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_pppoe.c
1 /*
2  * ng_pppoe.c
3  */
4
5 /*-
6  * Copyright (c) 1996-1999 Whistle Communications, Inc.
7  * All rights reserved.
8  * 
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  * 
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Author: Julian Elischer <julian@freebsd.org>
39  *
40  * $FreeBSD$
41  * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $
42  */
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/ktr.h>
48 #include <sys/mbuf.h>
49 #include <sys/malloc.h>
50 #include <sys/errno.h>
51 #include <net/ethernet.h>
52
53 #include <netgraph/ng_message.h>
54 #include <netgraph/netgraph.h>
55 #include <netgraph/ng_parse.h>
56 #include <netgraph/ng_pppoe.h>
57
58 #ifdef NG_SEPARATE_MALLOC
59 MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node");
60 #else
61 #define M_NETGRAPH_PPPOE M_NETGRAPH
62 #endif
63
64 #define SIGNOFF "session closed"
65 #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
66
67 /*
68  * This section contains the netgraph method declarations for the
69  * pppoe node. These methods define the netgraph pppoe 'type'.
70  */
71
72 static ng_constructor_t ng_pppoe_constructor;
73 static ng_rcvmsg_t      ng_pppoe_rcvmsg;
74 static ng_shutdown_t    ng_pppoe_shutdown;
75 static ng_newhook_t     ng_pppoe_newhook;
76 static ng_rcvdata_t     ng_pppoe_rcvdata;
77 static ng_disconnect_t  ng_pppoe_disconnect;
78
79 /* Parse type for struct ngpppoe_init_data */
80 static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[]
81         = NG_PPPOE_INIT_DATA_TYPE_INFO;
82 static const struct ng_parse_type ngpppoe_init_data_state_type = {
83         &ng_parse_struct_type,
84         &ngpppoe_init_data_type_fields
85 };
86
87 /* Parse type for struct ngpppoe_sts */
88 static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[]
89         = NG_PPPOE_STS_TYPE_INFO;
90 static const struct ng_parse_type ng_pppoe_sts_state_type = {
91         &ng_parse_struct_type,
92         &ng_pppoe_sts_type_fields
93 };
94
95 /* List of commands and how to convert arguments to/from ASCII */
96 static const struct ng_cmdlist ng_pppoe_cmds[] = {
97         {
98           NGM_PPPOE_COOKIE,
99           NGM_PPPOE_CONNECT,
100           "pppoe_connect",
101           &ngpppoe_init_data_state_type,
102           NULL
103         },
104         {
105           NGM_PPPOE_COOKIE,
106           NGM_PPPOE_LISTEN,
107           "pppoe_listen",
108           &ngpppoe_init_data_state_type,
109           NULL
110         },
111         {
112           NGM_PPPOE_COOKIE,
113           NGM_PPPOE_OFFER,
114           "pppoe_offer",
115           &ngpppoe_init_data_state_type,
116           NULL
117         },
118         {
119           NGM_PPPOE_COOKIE,
120           NGM_PPPOE_SERVICE,
121           "pppoe_service",
122           &ngpppoe_init_data_state_type,
123           NULL
124         },
125         {
126           NGM_PPPOE_COOKIE,
127           NGM_PPPOE_SUCCESS,
128           "pppoe_success",
129           &ng_pppoe_sts_state_type,
130           NULL
131         },
132         {
133           NGM_PPPOE_COOKIE,
134           NGM_PPPOE_FAIL,
135           "pppoe_fail",
136           &ng_pppoe_sts_state_type,
137           NULL
138         },
139         {
140           NGM_PPPOE_COOKIE,
141           NGM_PPPOE_CLOSE,
142           "pppoe_close",
143           &ng_pppoe_sts_state_type,
144           NULL
145         },
146         {
147           NGM_PPPOE_COOKIE,
148           NGM_PPPOE_SETMODE,
149           "pppoe_setmode",
150           &ng_parse_string_type,
151           NULL
152         },
153         {
154           NGM_PPPOE_COOKIE,
155           NGM_PPPOE_GETMODE,
156           "pppoe_getmode",
157           NULL,
158           &ng_parse_string_type
159         },
160         { 0 }
161 };
162
163 /* Netgraph node type descriptor */
164 static struct ng_type typestruct = {
165         .version =      NG_ABI_VERSION,
166         .name =         NG_PPPOE_NODE_TYPE,
167         .constructor =  ng_pppoe_constructor,
168         .rcvmsg =       ng_pppoe_rcvmsg,
169         .shutdown =     ng_pppoe_shutdown,
170         .newhook =      ng_pppoe_newhook,
171         .rcvdata =      ng_pppoe_rcvdata,
172         .disconnect =   ng_pppoe_disconnect,
173         .cmdlist =      ng_pppoe_cmds,
174 };
175 NETGRAPH_INIT(pppoe, &typestruct);
176
177 /*
178  * States for the session state machine.
179  * These have no meaning if there is no hook attached yet.
180  */
181 enum state {
182     PPPOE_SNONE=0,      /* [both] Initial state */
183     PPPOE_LISTENING,    /* [Daemon] Listening for discover initiation pkt */
184     PPPOE_SINIT,        /* [Client] Sent discovery initiation */
185     PPPOE_PRIMED,       /* [Server] Awaiting PADI from daemon */
186     PPPOE_SOFFER,       /* [Server] Sent offer message  (got PADI)*/
187     PPPOE_SREQ,         /* [Client] Sent a Request */
188     PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */
189     PPPOE_CONNECTED,    /* [Both] Connection established, Data received */
190     PPPOE_DEAD          /* [Both] */
191 };
192
193 #define NUMTAGS 20 /* number of tags we are set up to work with */
194
195 /*
196  * Information we store for each hook on each node for negotiating the
197  * session. The mbuf and cluster are freed once negotiation has completed.
198  * The whole negotiation block is then discarded.
199  */
200
201 struct sess_neg {
202         struct mbuf             *m; /* holds cluster with last sent packet */
203         union   packet          *pkt; /* points within the above cluster */
204         struct callout          handle;   /* see timeout(9) */
205         u_int                   timeout; /* 0,1,2,4,8,16 etc. seconds */
206         u_int                   numtags;
207         const struct pppoe_tag  *tags[NUMTAGS];
208         u_int                   service_len;
209         u_int                   ac_name_len;
210
211         struct datatag          service;
212         struct datatag          ac_name;
213 };
214 typedef struct sess_neg *negp;
215
216 /*
217  * Session information that is needed after connection.
218  */
219 struct sess_con {
220         hook_p                  hook;
221         uint16_t                Session_ID;
222         enum state              state;
223         ng_ID_t                 creator;        /* who to notify */
224         struct pppoe_full_hdr   pkt_hdr;        /* used when connected */
225         negp                    neg;            /* used when negotiating */
226 };
227 typedef struct sess_con *sessp;
228
229 #define NG_PPPOE_SESSION_NODE(sp) NG_HOOK_NODE(sp->hook)
230
231 enum {
232         PPPOE_STANDARD  = 1,    /* standard RFC2516 mode */
233         PPPOE_NONSTANDARD,      /* 3Com proprietary mode */
234 };
235
236 struct ng_pppoe_mode_t {
237         uint8_t                         id;
238         const struct ether_header       *eh_prototype;
239         const char                      *name;
240 };
241
242 static const struct ether_header eh_standard =
243         {{0xff,0xff,0xff,0xff,0xff,0xff},
244         {0x00,0x00,0x00,0x00,0x00,0x00},
245         ETHERTYPE_PPPOE_DISC};
246
247 static const struct ether_header eh_3Com =
248         {{0xff,0xff,0xff,0xff,0xff,0xff},
249         {0x00,0x00,0x00,0x00,0x00,0x00},
250         ETHERTYPE_PPPOE_STUPID_DISC};
251
252 static const struct ng_pppoe_mode_t ng_pppoe_modes[] = {
253         { PPPOE_STANDARD,       &eh_standard,   NG_PPPOE_STANDARD },
254         { PPPOE_NONSTANDARD,    &eh_3Com,       NG_PPPOE_NONSTANDARD },
255         { 0, NULL},
256 };
257
258 /*
259  * Information we store for each node
260  */
261 struct PPPoE {
262         node_p          node;           /* back pointer to node */
263         hook_p          ethernet_hook;
264         hook_p          debug_hook;
265         u_int           packets_in;     /* packets in from ethernet */
266         u_int           packets_out;    /* packets out towards ethernet */
267         uint32_t        flags;
268         const struct    ng_pppoe_mode_t *mode;  /* standard PPPoE or 3Com? */
269 };
270 typedef struct PPPoE *priv_p;
271
272 union uniq {
273         char bytes[sizeof(void *)];
274         void *pointer;
275 };
276
277 #define LEAVE(x) do { error = x; goto quit; } while(0)
278 static void     pppoe_start(sessp sp);
279 static void     sendpacket(sessp sp);
280 static void     pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2);
281 static const    struct pppoe_tag *scan_tags(sessp sp,
282                         const struct pppoe_hdr* ph);
283 static  int     pppoe_send_event(sessp sp, enum cmd cmdid);
284
285 /*************************************************************************
286  * Some basic utilities  from the Linux version with author's permission.*
287  * Author:      Michal Ostrowski <mostrows@styx.uwaterloo.ca>            *
288  ************************************************************************/
289
290 /*
291  * Generate a new session id
292  * XXX find out the FreeBSD locking scheme.
293  */
294 static uint16_t
295 get_new_sid(node_p node)
296 {
297         priv_p privp = NG_NODE_PRIVATE(node);
298         static int pppoe_sid = 10;
299         sessp sp;
300         hook_p  hook;
301         uint16_t val;
302
303 restart:
304         val = pppoe_sid++;
305         /*
306          * Spec says 0xFFFF is reserved.
307          * Also don't use 0x0000
308          */
309         if (val == 0xffff) {
310                 pppoe_sid = 20;
311                 goto restart;
312         }
313
314         /* Check it isn't already in use. */
315         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
316                 /* Don't check special hooks. */
317                 if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook)
318                 ||  (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook))
319                         continue;
320                 sp = NG_HOOK_PRIVATE(hook);
321                 if (sp->Session_ID == val)
322                         goto restart;
323         }
324
325         CTR2(KTR_NET, "%20s: new sid %d", __func__, val);
326
327         return (val);
328 }
329
330
331 /*
332  * Return the location where the next tag can be put
333  */
334 static __inline const struct pppoe_tag*
335 next_tag(const struct pppoe_hdr* ph)
336 {
337         return (const struct pppoe_tag*)(((const char*)&ph->tag[0])
338             + ntohs(ph->length));
339 }
340
341 /*
342  * Look for a tag of a specific type.
343  * Don't trust any length the other end says,
344  * but assume we already sanity checked ph->length.
345  */
346 static const struct pppoe_tag*
347 get_tag(const struct pppoe_hdr* ph, uint16_t idx)
348 {
349         const char *const end = (const char *)next_tag(ph);
350         const struct pppoe_tag *pt = &ph->tag[0];
351         const char *ptn;
352
353         /*
354          * Keep processing tags while a tag header will still fit.
355          */
356         while((const char*)(pt + 1) <= end) {
357                 /*
358                  * If the tag data would go past the end of the packet, abort.
359                  */
360                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
361                 if (ptn > end) {
362                         CTR2(KTR_NET, "%20s: invalid length for tag %d",
363                             __func__, idx);
364                         return (NULL);
365                 }
366                 if (pt->tag_type == idx) {
367                         CTR2(KTR_NET, "%20s: found tag %d", __func__, idx);
368                         return (pt);
369                 }
370
371                 pt = (const struct pppoe_tag*)ptn;
372         }
373
374         CTR2(KTR_NET, "%20s: not found tag %d", __func__, idx);
375         return (NULL);
376 }
377
378 /**************************************************************************
379  * Inlines to initialise or add tags to a session's tag list.
380  **************************************************************************/
381 /*
382  * Initialise the session's tag list.
383  */
384 static void
385 init_tags(sessp sp)
386 {
387         KASSERT(sp->neg != NULL, ("%s: no neg", __func__));
388         sp->neg->numtags = 0;
389 }
390
391 static void
392 insert_tag(sessp sp, const struct pppoe_tag *tp)
393 {
394         negp neg = sp->neg;
395         int i;
396
397         KASSERT(neg != NULL, ("%s: no neg", __func__));
398         if ((i = neg->numtags++) < NUMTAGS) {
399                 neg->tags[i] = tp;
400         } else {
401                 printf("pppoe: asked to add too many tags to packet\n");
402                 neg->numtags--;
403         }
404 }
405
406 /*
407  * Make up a packet, using the tags filled out for the session.
408  *
409  * Assume that the actual pppoe header and ethernet header
410  * are filled out externally to this routine.
411  * Also assume that neg->wh points to the correct
412  * location at the front of the buffer space.
413  */
414 static void
415 make_packet(sessp sp) {
416         struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header;
417         const struct pppoe_tag **tag;
418         char *dp;
419         int count;
420         int tlen;
421         uint16_t length = 0;
422
423         KASSERT((sp->neg != NULL) && (sp->neg->m != NULL),
424             ("%s: make_packet called from wrong state", __func__));
425         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
426
427         dp = (char *)wh->ph.tag;
428         for (count = 0, tag = sp->neg->tags;
429             ((count < sp->neg->numtags) && (count < NUMTAGS));
430             tag++, count++) {
431                 tlen = ntohs((*tag)->tag_len) + sizeof(**tag);
432                 if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) {
433                         printf("pppoe: tags too long\n");
434                         sp->neg->numtags = count;
435                         break;  /* XXX chop off what's too long */
436                 }
437                 bcopy(*tag, (char *)dp, tlen);
438                 length += tlen;
439                 dp += tlen;
440         }
441         wh->ph.length = htons(length);
442         sp->neg->m->m_len = length + sizeof(*wh);
443         sp->neg->m->m_pkthdr.len = length + sizeof(*wh);
444 }
445
446 /**************************************************************************
447  * Routine to match a service offered                                     *
448  **************************************************************************/
449 /*
450  * Find a hook that has a service string that matches that
451  * we are seeking. for now use a simple string.
452  * In the future we may need something like regexp().
453  * for testing allow a null string to match 1st found and a null service
454  * to match all requests. Also make '*' do the same.
455  */
456
457 #define NG_MATCH_EXACT  1
458 #define NG_MATCH_ANY    2
459
460 static hook_p
461 pppoe_match_svc(node_p node, const char *svc_name, int svc_len, int match)
462 {
463         sessp   sp      = NULL;
464         negp    neg     = NULL;
465         priv_p  privp   = NG_NODE_PRIVATE(node);
466         hook_p  allhook = NULL;
467         hook_p  hook;
468
469         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
470
471                 /* skip any hook that is debug or ethernet */
472                 if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook)
473                 ||  (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook))
474                         continue;
475                 sp = NG_HOOK_PRIVATE(hook);
476
477                 /* Skip any sessions which are not in LISTEN mode. */
478                 if ( sp->state != PPPOE_LISTENING)
479                         continue;
480
481                 neg = sp->neg;
482
483                 /* Special case for a blank or "*" service name (wildcard) */
484                 if (match == NG_MATCH_ANY && neg->service_len == 1 &&
485                     neg->service.data[0] == '*') {
486                         allhook = hook;
487                         continue;
488                 }
489
490                 /* If the lengths don't match, that aint it. */
491                 if (neg->service_len != svc_len)
492                         continue;
493
494                 /* An exact match? */
495                 if (svc_len == 0)
496                         break;
497
498                 if (strncmp(svc_name, neg->service.data, svc_len) == 0)
499                         break;
500         }
501         CTR3(KTR_NET, "%20s: matched %p for %s", __func__, hook, svc_name);
502
503         return (hook ? hook : allhook);
504 }
505
506 /**************************************************************************
507  * Routine to find a particular session that matches an incoming packet.  *
508  **************************************************************************/
509 static hook_p
510 pppoe_findsession(node_p node, const struct pppoe_full_hdr *wh)
511 {
512         priv_p  privp = NG_NODE_PRIVATE(node);
513         sessp   sp = NULL;
514         hook_p  hook = NULL;
515         uint16_t session = ntohs(wh->ph.sid);
516
517         /*
518          * Find matching peer/session combination.
519          */
520         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
521                 /* don't check special hooks */
522                 if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook)
523                 ||  (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) {
524                         continue;
525                 }
526                 sp = NG_HOOK_PRIVATE(hook);
527                 if ( ( (sp->state == PPPOE_CONNECTED)
528                     || (sp->state == PPPOE_NEWCONNECTED) )
529                 && (sp->Session_ID == session)
530                 && (bcmp(sp->pkt_hdr.eh.ether_dhost,
531                     wh->eh.ether_shost,
532                     ETHER_ADDR_LEN)) == 0) {
533                         break;
534                 }
535         }
536         CTR3(KTR_NET, "%20s: matched %p for %d", __func__, hook, session);
537
538         return (hook);
539 }
540
541 static hook_p
542 pppoe_finduniq(node_p node, const struct pppoe_tag *tag)
543 {
544         priv_p  privp = NG_NODE_PRIVATE(node);
545         hook_p  hook = NULL;
546         union uniq uniq;
547
548         bcopy(tag->tag_data, uniq.bytes, sizeof(void *));
549         /* Cycle through all known hooks. */
550         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
551                 /* Don't check special hooks. */
552                 if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook)
553                 ||  (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook))
554                         continue;
555                 if (uniq.pointer == NG_HOOK_PRIVATE(hook))
556                         break;
557         }
558         CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, uniq.pointer);
559
560         return (hook);
561 }
562
563 /**************************************************************************
564  * Start of Netgraph entrypoints.                                         *
565  **************************************************************************/
566
567 /*
568  * Allocate the private data structure and link it with node.
569  */
570 static int
571 ng_pppoe_constructor(node_p node)
572 {
573         priv_p privp;
574
575         /* Initialize private descriptor. */
576         privp = malloc(sizeof(*privp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO);
577         if (privp == NULL)
578                 return (ENOMEM);
579
580         /* Link structs together; this counts as our one reference to *node. */
581         NG_NODE_SET_PRIVATE(node, privp);
582         privp->node = node;
583
584         /* Initialize to standard mode (the first one in ng_pppoe_modes[]). */
585         privp->mode = &ng_pppoe_modes[0];
586
587         CTR3(KTR_NET, "%20s: created node [%x] (%p)",
588             __func__, node->nd_ID, node);
589
590         return (0);
591 }
592
593 /*
594  * Give our ok for a hook to be added...
595  * point the hook's private info to the hook structure.
596  *
597  * The following hook names are special:
598  *  "ethernet":  the hook that should be connected to a NIC.
599  *  "debug":    copies of data sent out here  (when I write the code).
600  * All other hook names need only be unique. (the framework checks this).
601  */
602 static int
603 ng_pppoe_newhook(node_p node, hook_p hook, const char *name)
604 {
605         const priv_p privp = NG_NODE_PRIVATE(node);
606         sessp sp;
607
608         if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) {
609                 privp->ethernet_hook = hook;
610                 NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook);
611         } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) {
612                 privp->debug_hook = hook;
613                 NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook);
614         } else {
615                 /*
616                  * Any other unique name is OK.
617                  * The infrastructure has already checked that it's unique,
618                  * so just allocate it and hook it in.
619                  */
620                 sp = malloc(sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO);
621                 if (sp == NULL)
622                         return (ENOMEM);
623
624                 NG_HOOK_SET_PRIVATE(hook, sp);
625                 sp->hook = hook;
626         }
627         CTR5(KTR_NET, "%20s: node [%x] (%p) connected hook %s (%p)",
628             __func__, node->nd_ID, node, name, hook);
629
630         return(0);
631 }
632
633 /*
634  * Get a netgraph control message.
635  * Check it is one we understand. If needed, send a response.
636  * We sometimes save the address for an async action later.
637  * Always free the message.
638  */
639 static int
640 ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook)
641 {
642         priv_p privp = NG_NODE_PRIVATE(node);
643         struct ngpppoe_init_data *ourmsg = NULL;
644         struct ng_mesg *resp = NULL;
645         int error = 0;
646         hook_p hook = NULL;
647         sessp sp = NULL;
648         negp neg = NULL;
649         struct ng_mesg *msg;
650
651         NGI_GET_MSG(item, msg);
652         CTR5(KTR_NET, "%20s: node [%x] (%p) got message %d with cookie %d",
653             __func__, node->nd_ID, node, msg->header.cmd,
654             msg->header.typecookie);
655
656         /* Deal with message according to cookie and command. */
657         switch (msg->header.typecookie) {
658         case NGM_PPPOE_COOKIE:
659                 switch (msg->header.cmd) {
660                 case NGM_PPPOE_CONNECT:
661                 case NGM_PPPOE_LISTEN:
662                 case NGM_PPPOE_OFFER:
663                 case NGM_PPPOE_SERVICE:
664                         ourmsg = (struct ngpppoe_init_data *)msg->data;
665                         if (msg->header.arglen < sizeof(*ourmsg)) {
666                                 printf("pppoe: init data too small\n");
667                                 LEAVE(EMSGSIZE);
668                         }
669                         if (msg->header.arglen - sizeof(*ourmsg) >
670                             PPPOE_SERVICE_NAME_SIZE) {
671                                 printf("pppoe_rcvmsg: service name too big");
672                                 LEAVE(EMSGSIZE);
673                         }
674                         if (msg->header.arglen - sizeof(*ourmsg) <
675                             ourmsg->data_len) {
676                                 printf("pppoe: init data has bad length,"
677                                     " %d should be %zd\n", ourmsg->data_len,
678                                     msg->header.arglen - sizeof (*ourmsg));
679                                 LEAVE(EMSGSIZE);
680                         }
681
682                         /* Make sure strcmp will terminate safely. */
683                         ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0';
684
685                         /* Cycle through all known hooks. */
686                         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
687                                 if (NG_HOOK_NAME(hook) &&
688                                     strcmp(NG_HOOK_NAME(hook), ourmsg->hook) ==
689                                     0)
690                                         break;
691                         }
692                         if (hook == NULL)
693                                 LEAVE(ENOENT);
694
695                         if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) ||
696                             (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook))
697                                 LEAVE(EINVAL);
698
699                         sp = NG_HOOK_PRIVATE(hook);
700
701                         if (msg->header.cmd == NGM_PPPOE_LISTEN) {
702                                 /*
703                                  * Ensure we aren't already listening for this
704                                  * service.
705                                  */
706                                 if (pppoe_match_svc(node, ourmsg->data,
707                                     ourmsg->data_len, NG_MATCH_EXACT) != NULL)
708                                         LEAVE(EEXIST);
709                         }
710
711                         /*
712                          * PPPOE_SERVICE advertisments are set up
713                          * on sessions that are in PRIMED state.
714                          */
715                         if (msg->header.cmd == NGM_PPPOE_SERVICE)
716                                 break;
717
718                         if (sp->state != PPPOE_SNONE) {
719                                 printf("pppoe: Session already active\n");
720                                 LEAVE(EISCONN);
721                         }
722
723                         /*
724                          * Set up prototype header.
725                          */
726                         neg = malloc(sizeof(*neg), M_NETGRAPH_PPPOE,
727                             M_NOWAIT | M_ZERO);
728
729                         if (neg == NULL)
730                                 LEAVE(ENOMEM);
731
732                         neg->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
733                         if (neg->m == NULL) {
734                                 free(neg, M_NETGRAPH_PPPOE);
735                                 LEAVE(ENOBUFS);
736                         }
737                         neg->m->m_pkthdr.rcvif = NULL;
738                         sp->neg = neg;
739                         ng_callout_init(&neg->handle);
740                         neg->m->m_len = sizeof(struct pppoe_full_hdr);
741                         neg->pkt = mtod(neg->m, union packet*);
742                         memcpy((void *)&neg->pkt->pkt_header.eh,
743                             (const void *)privp->mode->eh_prototype,
744                             sizeof(struct ether_header));
745                         neg->pkt->pkt_header.ph.ver = 0x1;
746                         neg->pkt->pkt_header.ph.type = 0x1;
747                         neg->pkt->pkt_header.ph.sid = 0x0000;
748                         neg->timeout = 0;
749
750                         sp->creator = NGI_RETADDR(item);
751                 }
752                 switch (msg->header.cmd) {
753                 case NGM_PPPOE_GET_STATUS:
754                     {
755                         struct ngpppoestat *stats;
756
757                         NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
758                         if (!resp)
759                                 LEAVE(ENOMEM);
760
761                         stats = (struct ngpppoestat *) resp->data;
762                         stats->packets_in = privp->packets_in;
763                         stats->packets_out = privp->packets_out;
764                         break;
765                     }
766                 case NGM_PPPOE_CONNECT:
767                         /*
768                          * Check the hook exists and is Uninitialised.
769                          * Send a PADI request, and start the timeout logic.
770                          * Store the originator of this message so we can send
771                          * a success of fail message to them later.
772                          * Move the session to SINIT.
773                          * Set up the session to the correct state and
774                          * start it.
775                          */
776                         neg->service.hdr.tag_type = PTT_SRV_NAME;
777                         neg->service.hdr.tag_len =
778                             htons((uint16_t)ourmsg->data_len);
779                         if (ourmsg->data_len)
780                                 bcopy(ourmsg->data, neg->service.data,
781                                     ourmsg->data_len);
782                         neg->service_len = ourmsg->data_len;
783                         pppoe_start(sp);
784                         break;
785                 case NGM_PPPOE_LISTEN:
786                         /*
787                          * Check the hook exists and is Uninitialised.
788                          * Install the service matching string.
789                          * Store the originator of this message so we can send
790                          * a success of fail message to them later.
791                          * Move the hook to 'LISTENING'
792                          */
793                         neg->service.hdr.tag_type = PTT_SRV_NAME;
794                         neg->service.hdr.tag_len =
795                             htons((uint16_t)ourmsg->data_len);
796
797                         if (ourmsg->data_len)
798                                 bcopy(ourmsg->data, neg->service.data,
799                                     ourmsg->data_len);
800                         neg->service_len = ourmsg->data_len;
801                         neg->pkt->pkt_header.ph.code = PADT_CODE;
802                         /*
803                          * Wait for PADI packet coming from Ethernet.
804                          */
805                         sp->state = PPPOE_LISTENING;
806                         break;
807                 case NGM_PPPOE_OFFER:
808                         /*
809                          * Check the hook exists and is Uninitialised.
810                          * Store the originator of this message so we can send
811                          * a success of fail message to them later.
812                          * Store the AC-Name given and go to PRIMED.
813                          */
814                         neg->ac_name.hdr.tag_type = PTT_AC_NAME;
815                         neg->ac_name.hdr.tag_len =
816                             htons((uint16_t)ourmsg->data_len);
817                         if (ourmsg->data_len)
818                                 bcopy(ourmsg->data, neg->ac_name.data,
819                                     ourmsg->data_len);
820                         neg->ac_name_len = ourmsg->data_len;
821                         neg->pkt->pkt_header.ph.code = PADO_CODE;
822                         /*
823                          * Wait for PADI packet coming from hook.
824                          */
825                         sp->state = PPPOE_PRIMED;
826                         break;
827                 case NGM_PPPOE_SERVICE:
828                         /*
829                          * Check the session is primed.
830                          * for now just allow ONE service to be advertised.
831                          * If you do it twice you just overwrite.
832                          */
833                         if (sp->state != PPPOE_PRIMED) {
834                                 printf("pppoe: Session not primed\n");
835                                 LEAVE(EISCONN);
836                         }
837                         neg = sp->neg;
838                         neg->service.hdr.tag_type = PTT_SRV_NAME;
839                         neg->service.hdr.tag_len =
840                             htons((uint16_t)ourmsg->data_len);
841
842                         if (ourmsg->data_len)
843                                 bcopy(ourmsg->data, neg->service.data,
844                                     ourmsg->data_len);
845                         neg->service_len = ourmsg->data_len;
846                         break;
847                 case NGM_PPPOE_SETMODE:
848                     {
849                         const struct ng_pppoe_mode_t *mode;
850                         char *s;
851                         size_t len;
852
853                         if (msg->header.arglen == 0)
854                                 LEAVE(EINVAL);
855
856                         s = (char *)msg->data;
857                         len = msg->header.arglen - 1;
858
859                         /* Search for matching mode string. */
860                         for (mode = ng_pppoe_modes; mode->id != 0; mode++ )
861                                 if ((strlen(mode->name) == len) &&
862                                     !strncmp(mode->name, s, len))
863                                         break;  /* found */
864
865                         if (mode->id != 0)
866                                 privp->mode = mode;
867                         else
868                                 LEAVE(EINVAL);
869                         break;
870                     }
871                 case NGM_PPPOE_GETMODE:
872                         NG_MKRESPONSE(resp, msg, strlen(privp->mode->name) + 1,
873                             M_NOWAIT);
874                         if (resp == NULL)
875                                 LEAVE(ENOMEM);
876                         strlcpy((char *)resp->data, privp->mode->name,
877                             strlen(privp->mode->name) + 1);
878                         break;
879                 default:
880                         LEAVE(EINVAL);
881                 }
882                 break;
883         default:
884                 LEAVE(EINVAL);
885         }
886
887         CTR2(KTR_NET, "%20s: returning %d", __func__, error);
888
889         /* Take care of synchronous response, if any. */
890 quit:
891         NG_RESPOND_MSG(error, node, item, resp);
892         /* Free the message and return. */
893         NG_FREE_MSG(msg);
894         return(error);
895 }
896
897 /*
898  * Start a client into the first state. A separate function because
899  * it can be needed if the negotiation times out.
900  */
901 static void
902 pppoe_start(sessp sp)
903 {
904         priv_p  privp = NG_NODE_PRIVATE(NG_PPPOE_SESSION_NODE(sp));
905         struct {
906                 struct pppoe_tag hdr;
907                 union   uniq    data;
908         } __packed uniqtag;
909
910         /*
911          * Kick the state machine into starting up.
912          */
913         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
914         sp->state = PPPOE_SINIT;
915         /*
916          * Reset the packet header to broadcast. Since we are
917          * in a client
918          * mode use configured ethertype.
919          */
920         memcpy((void *)&sp->neg->pkt->pkt_header.eh,
921             (const void *)privp->mode->eh_prototype,
922             sizeof(struct ether_header));
923         sp->neg->pkt->pkt_header.ph.code = PADI_CODE;
924         uniqtag.hdr.tag_type = PTT_HOST_UNIQ;
925         uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data));
926         uniqtag.data.pointer = sp;
927         init_tags(sp);
928         insert_tag(sp, &uniqtag.hdr);
929         insert_tag(sp, &sp->neg->service.hdr);
930         make_packet(sp);
931         sendpacket(sp);
932 }
933
934 static int
935 send_acname(sessp sp, const struct pppoe_tag *tag)
936 {
937         int error, tlen;
938         struct ng_mesg *msg;
939         struct ngpppoe_sts *sts;
940
941         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
942
943         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME,
944             sizeof(struct ngpppoe_sts), M_NOWAIT);
945         if (msg == NULL)
946                 return (ENOMEM);
947
948         sts = (struct ngpppoe_sts *)msg->data;
949         tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len));
950         strncpy(sts->hook, tag->tag_data, tlen);
951         sts->hook[tlen] = '\0';
952         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
953
954         return (error);
955 }
956
957 static int
958 send_sessionid(sessp sp)
959 {
960         int error;
961         struct ng_mesg *msg;
962
963         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
964
965         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID,
966             sizeof(uint16_t), M_NOWAIT);
967         if (msg == NULL)
968                 return (ENOMEM);
969
970         *(uint16_t *)msg->data = sp->Session_ID;
971         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
972
973         return (error);
974 }
975
976 /*
977  * Receive data, and do something with it.
978  * The caller will never free m, so if we use up this data
979  * or abort we must free it.
980  */
981 static int
982 ng_pppoe_rcvdata(hook_p hook, item_p item)
983 {
984         node_p                  node = NG_HOOK_NODE(hook);
985         const priv_p            privp = NG_NODE_PRIVATE(node);
986         sessp                   sp = NG_HOOK_PRIVATE(hook);
987         const struct pppoe_tag  *utag = NULL, *tag = NULL;
988         const struct pppoe_full_hdr *wh;
989         const struct pppoe_hdr  *ph;
990         negp                    neg = NULL;
991         struct mbuf             *m;
992         hook_p                  sendhook;
993         int                     error = 0;
994         uint16_t                session;
995         uint16_t                length;
996         uint8_t                 code;
997         struct {
998                 struct pppoe_tag hdr;
999                 union   uniq    data;
1000         } __packed uniqtag;
1001
1002         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1003             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1004
1005         NGI_GET_M(item, m);
1006         if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) {
1007                 /*
1008                  * Data from the debug hook gets sent without modification
1009                  * straight to the ethernet.
1010                  */
1011                 NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook);
1012                 privp->packets_out++;
1013         } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) {
1014                 /*
1015                  * Incoming data.
1016                  * Dig out various fields from the packet.
1017                  * Use them to decide where to send it.
1018                  */
1019                 
1020                 privp->packets_in++;
1021                 if( m->m_len < sizeof(*wh)) {
1022                         m = m_pullup(m, sizeof(*wh)); /* Checks length */
1023                         if (m == NULL) {
1024                                 printf("couldn't m_pullup\n");
1025                                 LEAVE(ENOBUFS);
1026                         }
1027                 }
1028                 wh = mtod(m, struct pppoe_full_hdr *);
1029                 length = ntohs(wh->ph.length);
1030                 switch(wh->eh.ether_type) {
1031                 case    ETHERTYPE_PPPOE_STUPID_DISC: /* fall through */
1032                 case    ETHERTYPE_PPPOE_DISC:
1033                         /*
1034                          * We need to try to make sure that the tag area
1035                          * is contiguous, or we could wander off the end
1036                          * of a buffer and make a mess.
1037                          * (Linux wouldn't have this problem).
1038                          */
1039                         if (m->m_pkthdr.len <= MHLEN) {
1040                                 if( m->m_len < m->m_pkthdr.len) {
1041                                         m = m_pullup(m, m->m_pkthdr.len);
1042                                         if (m == NULL) {
1043                                                 printf("couldn't m_pullup\n");
1044                                                 LEAVE(ENOBUFS);
1045                                         }
1046                                 }
1047                         }
1048                         if (m->m_len != m->m_pkthdr.len) {
1049                                 /*
1050                                  * It's not all in one piece.
1051                                  * We need to do extra work.
1052                                  * Put it into a cluster.
1053                                  */
1054                                 struct mbuf *n;
1055                                 n = m_dup(m, M_DONTWAIT);
1056                                 m_freem(m);
1057                                 m = n;
1058                                 if (m) {
1059                                         /* just check we got a cluster */
1060                                         if (m->m_len != m->m_pkthdr.len) {
1061                                                 m_freem(m);
1062                                                 m = NULL;
1063                                         }
1064                                 }
1065                                 if (m == NULL) {
1066                                         printf("packet fragmented\n");
1067                                         LEAVE(EMSGSIZE);
1068                                 }
1069                         }
1070                         wh = mtod(m, struct pppoe_full_hdr *);
1071                         length = ntohs(wh->ph.length);
1072                         ph = &wh->ph;
1073                         session = ntohs(wh->ph.sid);
1074                         code = wh->ph.code;
1075
1076                         switch(code) {
1077                         case    PADI_CODE:
1078                                 /*
1079                                  * We are a server:
1080                                  * Look for a hook with the required service
1081                                  * and send the ENTIRE packet up there.
1082                                  * It should come back to a new hook in
1083                                  * PRIMED state. Look there for further
1084                                  * processing.
1085                                  */
1086                                 tag = get_tag(ph, PTT_SRV_NAME);
1087                                 if (tag == NULL) {
1088                                         printf("no service tag\n");
1089                                         LEAVE(ENETUNREACH);
1090                                 }
1091                                 sendhook = pppoe_match_svc(NG_HOOK_NODE(hook),
1092                                         tag->tag_data, ntohs(tag->tag_len),
1093                                         NG_MATCH_ANY);
1094                                 if (sendhook) {
1095                                         NG_FWD_NEW_DATA(error, item,
1096                                                                 sendhook, m);
1097                                 } else {
1098                                         LEAVE(ENETUNREACH);
1099                                 }
1100                                 break;
1101                         case    PADO_CODE:
1102                                 /*
1103                                  * We are a client:
1104                                  * Use the host_uniq tag to find the
1105                                  * hook this is in response to.
1106                                  * Received #2, now send #3
1107                                  * For now simply accept the first we receive.
1108                                  */
1109                                 utag = get_tag(ph, PTT_HOST_UNIQ);
1110                                 if ((utag == NULL)
1111                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
1112                                         printf("no host unique field\n");
1113                                         LEAVE(ENETUNREACH);
1114                                 }
1115
1116                                 sendhook = pppoe_finduniq(node, utag);
1117                                 if (sendhook == NULL) {
1118                                         printf("no matching session\n");
1119                                         LEAVE(ENETUNREACH);
1120                                 }
1121
1122                                 /*
1123                                  * Check the session is in the right state.
1124                                  * It needs to be in PPPOE_SINIT.
1125                                  */
1126                                 sp = NG_HOOK_PRIVATE(sendhook);
1127                                 if (sp->state != PPPOE_SINIT) {
1128                                         printf("session in wrong state\n");
1129                                         LEAVE(ENETUNREACH);
1130                                 }
1131                                 neg = sp->neg;
1132                                 ng_uncallout(&neg->handle, node);
1133
1134                                 /*
1135                                  * This is the first time we hear
1136                                  * from the server, so note it's
1137                                  * unicast address, replacing the
1138                                  * broadcast address .
1139                                  */
1140                                 bcopy(wh->eh.ether_shost,
1141                                         neg->pkt->pkt_header.eh.ether_dhost,
1142                                         ETHER_ADDR_LEN);
1143                                 neg->timeout = 0;
1144                                 neg->pkt->pkt_header.ph.code = PADR_CODE;
1145                                 init_tags(sp);
1146                                 insert_tag(sp, utag);      /* Host Unique */
1147                                 if ((tag = get_tag(ph, PTT_AC_COOKIE)))
1148                                         insert_tag(sp, tag); /* return cookie */
1149                                 if ((tag = get_tag(ph, PTT_AC_NAME))) { 
1150                                         insert_tag(sp, tag); /* return it */
1151                                         send_acname(sp, tag);
1152                                 }
1153                                 insert_tag(sp, &neg->service.hdr); /* Service */
1154                                 scan_tags(sp, ph);
1155                                 make_packet(sp);
1156                                 sp->state = PPPOE_SREQ;
1157                                 sendpacket(sp);
1158                                 break;
1159                         case    PADR_CODE:
1160
1161                                 /*
1162                                  * We are a server:
1163                                  * Use the ac_cookie tag to find the
1164                                  * hook this is in response to.
1165                                  */
1166                                 utag = get_tag(ph, PTT_AC_COOKIE);
1167                                 if ((utag == NULL)
1168                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
1169                                         LEAVE(ENETUNREACH);
1170                                 }
1171
1172                                 sendhook = pppoe_finduniq(node, utag);
1173                                 if (sendhook == NULL) {
1174                                         LEAVE(ENETUNREACH);
1175                                 }
1176
1177                                 /*
1178                                  * Check the session is in the right state.
1179                                  * It needs to be in PPPOE_SOFFER
1180                                  * or PPPOE_NEWCONNECTED. If the latter,
1181                                  * then this is a retry by the client.
1182                                  * so be nice, and resend.
1183                                  */
1184                                 sp = NG_HOOK_PRIVATE(sendhook);
1185                                 if (sp->state == PPPOE_NEWCONNECTED) {
1186                                         /*
1187                                          * Whoa! drop back to resend that
1188                                          * PADS packet.
1189                                          * We should still have a copy of it.
1190                                          */
1191                                         sp->state = PPPOE_SOFFER;
1192                                 }
1193                                 if (sp->state != PPPOE_SOFFER) {
1194                                         LEAVE (ENETUNREACH);
1195                                         break;
1196                                 }
1197                                 neg = sp->neg;
1198                                 ng_uncallout(&neg->handle, node);
1199                                 neg->pkt->pkt_header.ph.code = PADS_CODE;
1200                                 if (sp->Session_ID == 0)
1201                                         neg->pkt->pkt_header.ph.sid =
1202                                             htons(sp->Session_ID
1203                                                 = get_new_sid(node));
1204                                 send_sessionid(sp);
1205                                 neg->timeout = 0;
1206                                 /*
1207                                  * start working out the tags to respond with.
1208                                  */
1209                                 init_tags(sp);
1210                                 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1211                                 if ((tag = get_tag(ph, PTT_SRV_NAME)))
1212                                         insert_tag(sp, tag);/* return service */
1213                                 if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1214                                         insert_tag(sp, tag); /* return it */
1215                                 insert_tag(sp, utag);   /* ac_cookie */
1216                                 scan_tags(sp, ph);
1217                                 make_packet(sp);
1218                                 sp->state = PPPOE_NEWCONNECTED;
1219                                 sendpacket(sp);
1220                                 /*
1221                                  * Having sent the last Negotiation header,
1222                                  * Set up the stored packet header to
1223                                  * be correct for the actual session.
1224                                  * But keep the negotialtion stuff
1225                                  * around in case we need to resend this last
1226                                  * packet. We'll discard it when we move
1227                                  * from NEWCONNECTED to CONNECTED
1228                                  */
1229                                 sp->pkt_hdr = neg->pkt->pkt_header;
1230                                 /* Configure ethertype depending on what
1231                                  * ethertype was used at discovery phase */
1232                                 if (sp->pkt_hdr.eh.ether_type ==
1233                                     ETHERTYPE_PPPOE_STUPID_DISC)
1234                                         sp->pkt_hdr.eh.ether_type
1235                                                 = ETHERTYPE_PPPOE_STUPID_SESS;
1236                                 else
1237                                         sp->pkt_hdr.eh.ether_type
1238                                                 = ETHERTYPE_PPPOE_SESS;
1239                                 sp->pkt_hdr.ph.code = 0;
1240                                 pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1241                                 break;
1242                         case    PADS_CODE:
1243                                 /*
1244                                  * We are a client:
1245                                  * Use the host_uniq tag to find the
1246                                  * hook this is in response to.
1247                                  * take the session ID and store it away.
1248                                  * Also make sure the pre-made header is
1249                                  * correct and set us into Session mode.
1250                                  */
1251                                 utag = get_tag(ph, PTT_HOST_UNIQ);
1252                                 if ((utag == NULL)
1253                                 || (ntohs(utag->tag_len) != sizeof(sp))) {
1254                                         LEAVE (ENETUNREACH);
1255                                         break;
1256                                 }
1257                                 sendhook = pppoe_finduniq(node, utag);
1258                                 if (sendhook == NULL) {
1259                                         LEAVE(ENETUNREACH);
1260                                 }
1261
1262                                 /*
1263                                  * Check the session is in the right state.
1264                                  * It needs to be in PPPOE_SREQ.
1265                                  */
1266                                 sp = NG_HOOK_PRIVATE(sendhook);
1267                                 if (sp->state != PPPOE_SREQ) {
1268                                         LEAVE(ENETUNREACH);
1269                                 }
1270                                 neg = sp->neg;
1271                                 ng_uncallout(&neg->handle, node);
1272                                 neg->pkt->pkt_header.ph.sid = wh->ph.sid;
1273                                 sp->Session_ID = ntohs(wh->ph.sid);
1274                                 send_sessionid(sp);
1275                                 neg->timeout = 0;
1276                                 sp->state = PPPOE_CONNECTED;
1277                                 /*
1278                                  * Now we have gone to Connected mode,
1279                                  * Free all resources needed for
1280                                  * negotiation.
1281                                  * Keep a copy of the header we will be using.
1282                                  */
1283                                 sp->pkt_hdr = neg->pkt->pkt_header;
1284                                 if (privp->mode->id == PPPOE_NONSTANDARD)
1285                                         sp->pkt_hdr.eh.ether_type
1286                                                 = ETHERTYPE_PPPOE_STUPID_SESS;
1287                                 else
1288                                         sp->pkt_hdr.eh.ether_type
1289                                                 = ETHERTYPE_PPPOE_SESS;
1290                                 sp->pkt_hdr.ph.code = 0;
1291                                 m_freem(neg->m);
1292                                 free(sp->neg, M_NETGRAPH_PPPOE);
1293                                 sp->neg = NULL;
1294                                 pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1295                                 break;
1296                         case    PADT_CODE:
1297                                 /*
1298                                  * Send a 'close' message to the controlling
1299                                  * process (the one that set us up);
1300                                  * And then tear everything down.
1301                                  *
1302                                  * Find matching peer/session combination.
1303                                  */
1304                                 sendhook = pppoe_findsession(node, wh);
1305                                 if (sendhook == NULL) {
1306                                         LEAVE(ENETUNREACH);
1307                                 }
1308                                 /* send message to creator */
1309                                 /* close hook */
1310                                 if (sendhook) {
1311                                         ng_rmhook_self(sendhook);
1312                                 }
1313                                 break;
1314                         default:
1315                                 LEAVE(EPFNOSUPPORT);
1316                         }
1317                         break;
1318                 case    ETHERTYPE_PPPOE_STUPID_SESS:
1319                 case    ETHERTYPE_PPPOE_SESS:
1320                         /*
1321                          * Find matching peer/session combination.
1322                          */
1323                         sendhook = pppoe_findsession(node, wh);
1324                         if (sendhook == NULL) {
1325                                 LEAVE (ENETUNREACH);
1326                                 break;
1327                         }
1328                         sp = NG_HOOK_PRIVATE(sendhook);
1329                         m_adj(m, sizeof(*wh));
1330                         if (m->m_pkthdr.len < length) {
1331                                 /* Packet too short, dump it */
1332                                 LEAVE(EMSGSIZE);
1333                         }
1334
1335                         /* Also need to trim excess at the end */
1336                         if (m->m_pkthdr.len > length) {
1337                                 m_adj(m, -((int)(m->m_pkthdr.len - length)));
1338                         }
1339                         if ( sp->state != PPPOE_CONNECTED) {
1340                                 if (sp->state == PPPOE_NEWCONNECTED) {
1341                                         sp->state = PPPOE_CONNECTED;
1342                                         /*
1343                                          * Now we have gone to Connected mode,
1344                                          * Free all resources needed for
1345                                          * negotiation. Be paranoid about
1346                                          * whether there may be a timeout.
1347                                          */
1348                                         m_freem(sp->neg->m);
1349                                         ng_uncallout(&sp->neg->handle, node);
1350                                         free(sp->neg, M_NETGRAPH_PPPOE);
1351                                         sp->neg = NULL;
1352                                 } else {
1353                                         LEAVE (ENETUNREACH);
1354                                         break;
1355                                 }
1356                         }
1357                         NG_FWD_NEW_DATA( error, item, sendhook, m);
1358                         break;
1359                 default:
1360                         LEAVE(EPFNOSUPPORT);
1361                 }
1362         } else {
1363                 /*
1364                  * Not ethernet or debug hook..
1365                  *
1366                  * The packet has come in on a normal hook.
1367                  * We need to find out what kind of hook,
1368                  * So we can decide how to handle it.
1369                  * Check the hook's state.
1370                  */
1371                 sp = NG_HOOK_PRIVATE(hook);
1372                 switch (sp->state) {
1373                 case    PPPOE_NEWCONNECTED:
1374                 case    PPPOE_CONNECTED: {
1375                         static const u_char addrctrl[] = { 0xff, 0x03 };
1376                         struct pppoe_full_hdr *wh;
1377
1378                         /*
1379                          * Remove PPP address and control fields, if any.
1380                          * For example, ng_ppp(4) always sends LCP packets
1381                          * with address and control fields as required by
1382                          * generic PPP. PPPoE is an exception to the rule.
1383                          */
1384                         if (m->m_pkthdr.len >= 2) {
1385                                 if (m->m_len < 2 && !(m = m_pullup(m, 2)))
1386                                         LEAVE(ENOBUFS);
1387                                 if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0)
1388                                         m_adj(m, 2);
1389                         }
1390                         /*
1391                          * Bang in a pre-made header, and set the length up
1392                          * to be correct. Then send it to the ethernet driver.
1393                          * But first correct the length.
1394                          */
1395                         sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len));
1396                         M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
1397                         if (m == NULL)
1398                                 LEAVE(ENOBUFS);
1399
1400                         wh = mtod(m, struct pppoe_full_hdr *);
1401                         bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1402                         NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m);
1403                         privp->packets_out++;
1404                         break;
1405                         }
1406                 case    PPPOE_PRIMED:
1407                         /*
1408                          * A PADI packet is being returned by the application
1409                          * that has set up this hook. This indicates that it
1410                          * wants us to offer service.
1411                          */
1412                         neg = sp->neg;
1413                         if (m->m_len < sizeof(*wh)) {
1414                                 m = m_pullup(m, sizeof(*wh));
1415                                 if (m == NULL)
1416                                         LEAVE(ENOBUFS);
1417                         }
1418                         wh = mtod(m, struct pppoe_full_hdr *);
1419                         ph = &wh->ph;
1420                         session = ntohs(wh->ph.sid);
1421                         length = ntohs(wh->ph.length);
1422                         code = wh->ph.code;
1423                         /* Use peers mode in session. */
1424                         neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type;
1425                         if (code != PADI_CODE)
1426                                 LEAVE(EINVAL);
1427                         ng_uncallout(&neg->handle, node);
1428
1429                         /*
1430                          * This is the first time we hear
1431                          * from the client, so note it's
1432                          * unicast address, replacing the
1433                          * broadcast address.
1434                          */
1435                         bcopy(wh->eh.ether_shost,
1436                                 neg->pkt->pkt_header.eh.ether_dhost,
1437                                 ETHER_ADDR_LEN);
1438                         sp->state = PPPOE_SOFFER;
1439                         neg->timeout = 0;
1440                         neg->pkt->pkt_header.ph.code = PADO_CODE;
1441
1442                         /*
1443                          * Start working out the tags to respond with.
1444                          */
1445                         uniqtag.hdr.tag_type = PTT_AC_COOKIE;
1446                         uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
1447                         uniqtag.data.pointer = sp;
1448                         init_tags(sp);
1449                         insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1450                         if ((tag = get_tag(ph, PTT_SRV_NAME)))
1451                                 insert_tag(sp, tag);      /* return service */
1452                         /*
1453                          * If we have a NULL service request
1454                          * and have an extra service defined in this hook,
1455                          * then also add a tag for the extra service.
1456                          * XXX this is a hack. eventually we should be able
1457                          * to support advertising many services, not just one
1458                          */
1459                         if (((tag == NULL) || (tag->tag_len == 0)) &&
1460                             (neg->service.hdr.tag_len != 0)) {
1461                                 insert_tag(sp, &neg->service.hdr); /* SERVICE */
1462                         }
1463                         if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1464                                 insert_tag(sp, tag); /* returned hostunique */
1465                         insert_tag(sp, &uniqtag.hdr);
1466                         scan_tags(sp, ph);
1467                         make_packet(sp);
1468                         sendpacket(sp);
1469                         break;
1470
1471                 /*
1472                  * Packets coming from the hook make no sense
1473                  * to sessions in these states. Throw them away.
1474                  */
1475                 case    PPPOE_SINIT:
1476                 case    PPPOE_SREQ:
1477                 case    PPPOE_SOFFER:
1478                 case    PPPOE_SNONE:
1479                 case    PPPOE_LISTENING:
1480                 case    PPPOE_DEAD:
1481                 default:
1482                         LEAVE(ENETUNREACH);
1483                 }
1484         }
1485 quit:
1486         if (item)
1487                 NG_FREE_ITEM(item);
1488         NG_FREE_M(m);
1489         return error;
1490 }
1491
1492 /*
1493  * Do local shutdown processing..
1494  * If we are a persistant device, we might refuse to go away, and
1495  * we'd only remove our links and reset ourself.
1496  */
1497 static int
1498 ng_pppoe_shutdown(node_p node)
1499 {
1500         const priv_p privdata = NG_NODE_PRIVATE(node);
1501
1502         NG_NODE_SET_PRIVATE(node, NULL);
1503         NG_NODE_UNREF(privdata->node);
1504         free(privdata, M_NETGRAPH_PPPOE);
1505         return (0);
1506 }
1507
1508 /*
1509  * Hook disconnection
1510  *
1511  * Clean up all dangling links and information about the session/hook.
1512  * For this type, removal of the last link destroys the node.
1513  */
1514 static int
1515 ng_pppoe_disconnect(hook_p hook)
1516 {
1517         node_p node = NG_HOOK_NODE(hook);
1518         priv_p privp = NG_NODE_PRIVATE(node);
1519         sessp   sp;
1520         int     hooks;
1521
1522         hooks = NG_NODE_NUMHOOKS(node); /* This one already not counted. */
1523         if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) {
1524                 privp->debug_hook = NULL;
1525         } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) {
1526                 privp->ethernet_hook = NULL;
1527                 if (NG_NODE_IS_VALID(node))
1528                         ng_rmnode_self(node);
1529         } else {
1530                 sp = NG_HOOK_PRIVATE(hook);
1531                 if (sp->state != PPPOE_SNONE ) {
1532                         pppoe_send_event(sp, NGM_PPPOE_CLOSE);
1533                 }
1534                 /*
1535                  * According to the spec, if we are connected,
1536                  * we should send a DISC packet if we are shutting down
1537                  * a session.
1538                  */
1539                 if ((privp->ethernet_hook)
1540                 && ((sp->state == PPPOE_CONNECTED)
1541                  || (sp->state == PPPOE_NEWCONNECTED))) {
1542                         struct mbuf *m;
1543                         struct pppoe_full_hdr *wh;
1544                         struct pppoe_tag *tag;
1545                         int     msglen = strlen(SIGNOFF);
1546                         int error = 0;
1547
1548                         /* Revert the stored header to DISC/PADT mode. */
1549                         wh = &sp->pkt_hdr;
1550                         wh->ph.code = PADT_CODE;
1551                         /*
1552                          * Configure ethertype depending on what was used
1553                          * during sessions stage.
1554                          */
1555                         if (sp->pkt_hdr.eh.ether_type ==
1556                             ETHERTYPE_PPPOE_STUPID_SESS)
1557                                 wh->eh.ether_type = ETHERTYPE_PPPOE_STUPID_DISC;
1558                         else
1559                                 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
1560
1561                         /* Generate a packet of that type. */
1562                         MGETHDR(m, M_DONTWAIT, MT_DATA);
1563                         if(m == NULL)
1564                                 printf("pppoe: Session out of mbufs\n");
1565                         else {
1566                                 m->m_pkthdr.rcvif = NULL;
1567                                 m->m_pkthdr.len = m->m_len = sizeof(*wh);
1568                                 bcopy((caddr_t)wh, mtod(m, caddr_t),
1569                                     sizeof(*wh));
1570                                 /*
1571                                  * Add a General error message and adjust
1572                                  * sizes.
1573                                  */
1574                                 wh = mtod(m, struct pppoe_full_hdr *);
1575                                 tag = wh->ph.tag;
1576                                 tag->tag_type = PTT_GEN_ERR;
1577                                 tag->tag_len = htons((u_int16_t)msglen);
1578                                 strncpy(tag->tag_data, SIGNOFF, msglen);
1579                                 m->m_pkthdr.len = (m->m_len += sizeof(*tag) +
1580                                     msglen);
1581                                 wh->ph.length = htons(sizeof(*tag) + msglen);
1582                                 NG_SEND_DATA_ONLY(error,
1583                                         privp->ethernet_hook, m);
1584                         }
1585                 }
1586                 /*
1587                  * As long as we have somewhere to store the timeout handle,
1588                  * we may have a timeout pending.. get rid of it.
1589                  */
1590                 if (sp->neg) {
1591                         ng_uncallout(&sp->neg->handle, node);
1592                         if (sp->neg->m)
1593                                 m_freem(sp->neg->m);
1594                         free(sp->neg, M_NETGRAPH_PPPOE);
1595                 }
1596                 free(sp, M_NETGRAPH_PPPOE);
1597                 NG_HOOK_SET_PRIVATE(hook, NULL);
1598
1599                 /*
1600                  * Work out how many session hooks there are.
1601                  * Node goes away on last session hook removal.
1602                  */
1603                 if (privp->ethernet_hook)
1604                         hooks -= 1;
1605                 if (privp->debug_hook)
1606                         hooks -= 1;
1607         }
1608         if ((NG_NODE_NUMHOOKS(node) == 0) &&
1609             (NG_NODE_IS_VALID(node)))
1610                 ng_rmnode_self(node);
1611         return (0);
1612 }
1613
1614 /*
1615  * Timeouts come here.
1616  */
1617 static void
1618 pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2)
1619 {
1620         priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1621         sessp   sp = NG_HOOK_PRIVATE(hook);
1622         negp    neg = sp->neg;
1623         struct mbuf *m0 = NULL;
1624         int     error = 0;
1625
1626         CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d",
1627             __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID);
1628         switch(sp->state) {
1629                 /*
1630                  * Resend the last packet, using an exponential backoff.
1631                  * After a period of time, stop growing the backoff,
1632                  * And either leave it, or revert to the start.
1633                  */
1634         case    PPPOE_SINIT:
1635         case    PPPOE_SREQ:
1636                 /* Timeouts on these produce resends. */
1637                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1638                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
1639                 ng_callout(&neg->handle, node, hook, neg->timeout * hz,
1640                     pppoe_ticker, NULL, 0);
1641                 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
1642                         if (sp->state == PPPOE_SREQ) {
1643                                 /* Revert to SINIT mode. */
1644                                 pppoe_start(sp);
1645                         } else {
1646                                 neg->timeout = PPPOE_TIMEOUT_LIMIT;
1647                         }
1648                 }
1649                 break;
1650         case    PPPOE_PRIMED:
1651         case    PPPOE_SOFFER:
1652                 /* A timeout on these says "give up" */
1653                 ng_rmhook_self(hook);
1654                 break;
1655         default:
1656                 /* Timeouts have no meaning in other states. */
1657                 printf("pppoe: unexpected timeout\n");
1658         }
1659 }
1660
1661
1662 static void
1663 sendpacket(sessp sp)
1664 {
1665         struct  mbuf *m0 = NULL;
1666         hook_p  hook = sp->hook;
1667         node_p  node = NG_HOOK_NODE(hook);
1668         priv_p  privp = NG_NODE_PRIVATE(node);
1669         negp    neg = sp->neg;
1670         int     error = 0;
1671
1672         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1673         switch(sp->state) {
1674         case    PPPOE_LISTENING:
1675         case    PPPOE_DEAD:
1676         case    PPPOE_SNONE:
1677         case    PPPOE_CONNECTED:
1678                 printf("pppoe: sendpacket: unexpected state\n");
1679                 break;
1680
1681         case    PPPOE_NEWCONNECTED:
1682                 /* Send the PADS without a timeout - we're now connected. */
1683                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1684                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
1685                 break;
1686
1687         case    PPPOE_PRIMED:
1688                 /* No packet to send, but set up the timeout. */
1689                 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz,
1690                     pppoe_ticker, NULL, 0);
1691                 break;
1692
1693         case    PPPOE_SOFFER:
1694                 /*
1695                  * Send the offer but if they don't respond
1696                  * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
1697                  */
1698                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1699                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
1700                 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz,
1701                     pppoe_ticker, NULL, 0);
1702                 break;
1703
1704         case    PPPOE_SINIT:
1705         case    PPPOE_SREQ:
1706                 m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1707                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
1708                 ng_callout(&neg->handle, node, hook, PPPOE_INITIAL_TIMEOUT * hz,
1709                     pppoe_ticker, NULL, 0);
1710                 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2;
1711                 break;
1712
1713         default:
1714                 error = EINVAL;
1715                 printf("pppoe: timeout: bad state\n");
1716         }
1717 }
1718
1719 /*
1720  * Parse an incoming packet to see if any tags should be copied to the
1721  * output packet. Don't do any tags that have been handled in the main
1722  * state machine.
1723  */
1724 static const struct pppoe_tag*
1725 scan_tags(sessp sp, const struct pppoe_hdr* ph)
1726 {
1727         const char *const end = (const char *)next_tag(ph);
1728         const char *ptn;
1729         const struct pppoe_tag *pt = &ph->tag[0];
1730
1731         /*
1732          * Keep processing tags while a tag header will still fit.
1733          */
1734         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1735
1736         while((const char*)(pt + 1) <= end) {
1737                 /*
1738                  * If the tag data would go past the end of the packet, abort.
1739                  */
1740                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
1741                 if(ptn > end)
1742                         return NULL;
1743
1744                 switch (pt->tag_type) {
1745                 case    PTT_RELAY_SID:
1746                         insert_tag(sp, pt);
1747                         break;
1748                 case    PTT_EOL:
1749                         return NULL;
1750                 case    PTT_SRV_NAME:
1751                 case    PTT_AC_NAME:
1752                 case    PTT_HOST_UNIQ:
1753                 case    PTT_AC_COOKIE:
1754                 case    PTT_VENDOR:
1755                 case    PTT_SRV_ERR:
1756                 case    PTT_SYS_ERR:
1757                 case    PTT_GEN_ERR:
1758                         break;
1759                 }
1760                 pt = (const struct pppoe_tag*)ptn;
1761         }
1762         return NULL;
1763 }
1764         
1765 static  int
1766 pppoe_send_event(sessp sp, enum cmd cmdid)
1767 {
1768         int error;
1769         struct ng_mesg *msg;
1770         struct ngpppoe_sts *sts;
1771
1772         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1773
1774         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
1775                         sizeof(struct ngpppoe_sts), M_NOWAIT);
1776         if (msg == NULL)
1777                 return (ENOMEM);
1778         sts = (struct ngpppoe_sts *)msg->data;
1779         strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ);
1780         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1781         return (error);
1782 }