]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_pppoe.c
dma: update to 2022-01-27 snapshot
[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 <sys/epoch.h>
52 #include <sys/syslog.h>
53 #include <net/ethernet.h>
54
55 #include <netgraph/ng_message.h>
56 #include <netgraph/netgraph.h>
57 #include <netgraph/ng_parse.h>
58 #include <netgraph/ng_pppoe.h>
59 #include <netgraph/ng_ether.h>
60
61 #ifdef NG_SEPARATE_MALLOC
62 static MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node");
63 #else
64 #define M_NETGRAPH_PPPOE M_NETGRAPH
65 #endif
66
67 #define SIGNOFF "session closed"
68
69 /*
70  * This section contains the netgraph method declarations for the
71  * pppoe node. These methods define the netgraph pppoe 'type'.
72  */
73
74 static ng_constructor_t ng_pppoe_constructor;
75 static ng_rcvmsg_t      ng_pppoe_rcvmsg;
76 static ng_shutdown_t    ng_pppoe_shutdown;
77 static ng_newhook_t     ng_pppoe_newhook;
78 static ng_connect_t     ng_pppoe_connect;
79 static ng_rcvdata_t     ng_pppoe_rcvdata;
80 static ng_rcvdata_t     ng_pppoe_rcvdata_ether;
81 static ng_rcvdata_t     ng_pppoe_rcvdata_debug;
82 static ng_disconnect_t  ng_pppoe_disconnect;
83
84 /* Parse type for struct ngpppoe_init_data */
85 static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[]
86         = NG_PPPOE_INIT_DATA_TYPE_INFO;
87 static const struct ng_parse_type ngpppoe_init_data_state_type = {
88         &ng_parse_struct_type,
89         &ngpppoe_init_data_type_fields
90 };
91
92 /* Parse type for struct ngpppoe_sts */
93 static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[]
94         = NG_PPPOE_STS_TYPE_INFO;
95 static const struct ng_parse_type ng_pppoe_sts_state_type = {
96         &ng_parse_struct_type,
97         &ng_pppoe_sts_type_fields
98 };
99
100 /* List of commands and how to convert arguments to/from ASCII */
101 static const struct ng_cmdlist ng_pppoe_cmds[] = {
102         {
103           NGM_PPPOE_COOKIE,
104           NGM_PPPOE_CONNECT,
105           "pppoe_connect",
106           &ngpppoe_init_data_state_type,
107           NULL
108         },
109         {
110           NGM_PPPOE_COOKIE,
111           NGM_PPPOE_LISTEN,
112           "pppoe_listen",
113           &ngpppoe_init_data_state_type,
114           NULL
115         },
116         {
117           NGM_PPPOE_COOKIE,
118           NGM_PPPOE_OFFER,
119           "pppoe_offer",
120           &ngpppoe_init_data_state_type,
121           NULL
122         },
123         {
124           NGM_PPPOE_COOKIE,
125           NGM_PPPOE_SERVICE,
126           "pppoe_service",
127           &ngpppoe_init_data_state_type,
128           NULL
129         },
130         {
131           NGM_PPPOE_COOKIE,
132           NGM_PPPOE_SUCCESS,
133           "pppoe_success",
134           &ng_pppoe_sts_state_type,
135           NULL
136         },
137         {
138           NGM_PPPOE_COOKIE,
139           NGM_PPPOE_FAIL,
140           "pppoe_fail",
141           &ng_pppoe_sts_state_type,
142           NULL
143         },
144         {
145           NGM_PPPOE_COOKIE,
146           NGM_PPPOE_CLOSE,
147           "pppoe_close",
148           &ng_pppoe_sts_state_type,
149           NULL
150         },
151         {
152           NGM_PPPOE_COOKIE,
153           NGM_PPPOE_SETMODE,
154           "pppoe_setmode",
155           &ng_parse_string_type,
156           NULL
157         },
158         {
159           NGM_PPPOE_COOKIE,
160           NGM_PPPOE_GETMODE,
161           "pppoe_getmode",
162           NULL,
163           &ng_parse_string_type
164         },
165         {
166           NGM_PPPOE_COOKIE,
167           NGM_PPPOE_SETENADDR,
168           "setenaddr",
169           &ng_parse_enaddr_type,
170           NULL
171         },
172         {
173           NGM_PPPOE_COOKIE,
174           NGM_PPPOE_SETMAXP,
175           "setmaxp",
176           &ng_parse_uint16_type,
177           NULL
178         },
179         {
180           NGM_PPPOE_COOKIE,
181           NGM_PPPOE_SEND_HURL,
182           "send_hurl",
183           &ngpppoe_init_data_state_type,
184           NULL
185         },
186         {
187           NGM_PPPOE_COOKIE,
188           NGM_PPPOE_SEND_MOTM,
189           "send_motm",
190           &ngpppoe_init_data_state_type,
191           NULL
192         },
193         { 0 }
194 };
195
196 /* Netgraph node type descriptor */
197 static struct ng_type typestruct = {
198         .version =      NG_ABI_VERSION,
199         .name =         NG_PPPOE_NODE_TYPE,
200         .constructor =  ng_pppoe_constructor,
201         .rcvmsg =       ng_pppoe_rcvmsg,
202         .shutdown =     ng_pppoe_shutdown,
203         .newhook =      ng_pppoe_newhook,
204         .connect =      ng_pppoe_connect,
205         .rcvdata =      ng_pppoe_rcvdata,
206         .disconnect =   ng_pppoe_disconnect,
207         .cmdlist =      ng_pppoe_cmds,
208 };
209 NETGRAPH_INIT(pppoe, &typestruct);
210
211 /*
212  * States for the session state machine.
213  * These have no meaning if there is no hook attached yet.
214  */
215 enum state {
216     PPPOE_SNONE=0,      /* [both] Initial state */
217     PPPOE_LISTENING,    /* [Daemon] Listening for discover initiation pkt */
218     PPPOE_SINIT,        /* [Client] Sent discovery initiation */
219     PPPOE_PRIMED,       /* [Server] Awaiting PADI from daemon */
220     PPPOE_SOFFER,       /* [Server] Sent offer message  (got PADI)*/
221     PPPOE_SREQ,         /* [Client] Sent a Request */
222     PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */
223     PPPOE_CONNECTED,    /* [Both] Connection established, Data received */
224     PPPOE_DEAD          /* [Both] */
225 };
226
227 #define NUMTAGS 20 /* number of tags we are set up to work with */
228
229 /*
230  * Information we store for each hook on each node for negotiating the
231  * session. The mbuf and cluster are freed once negotiation has completed.
232  * The whole negotiation block is then discarded.
233  */
234
235 struct sess_neg {
236         struct mbuf             *m; /* holds cluster with last sent packet */
237         union   packet          *pkt; /* points within the above cluster */
238         struct callout          handle;   /* see timeout(9) */
239         u_int                   timeout; /* 0,1,2,4,8,16 etc. seconds */
240         u_int                   numtags;
241         const struct pppoe_tag  *tags[NUMTAGS];
242         u_int                   service_len;
243         u_int                   ac_name_len;
244         u_int                   host_uniq_len;
245
246         struct datatag          service;
247         struct datatag          ac_name;
248         struct datatag          host_uniq;
249 };
250 typedef struct sess_neg *negp;
251
252 /*
253  * Session information that is needed after connection.
254  */
255 struct sess_con {
256         hook_p                  hook;
257         uint16_t                Session_ID;
258         enum state              state;
259         ng_ID_t                 creator;        /* who to notify */
260         struct pppoe_full_hdr   pkt_hdr;        /* used when connected */
261         negp                    neg;            /* used when negotiating */
262         LIST_ENTRY(sess_con)    sessions;
263 };
264 typedef struct sess_con *sessp;
265
266 #define SESSHASHSIZE    0x0100
267 #define SESSHASH(x)     (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1))
268
269 struct sess_hash_entry {
270         struct mtx      mtx;
271         LIST_HEAD(hhead, sess_con) head;
272 };
273
274 /*
275  * Information we store for each node
276  */
277 struct PPPoE {
278         node_p          node;           /* back pointer to node */
279         hook_p          ethernet_hook;
280         hook_p          debug_hook;
281         u_int           packets_in;     /* packets in from ethernet */
282         u_int           packets_out;    /* packets out towards ethernet */
283         uint32_t        flags;
284 #define COMPAT_3COM     0x00000001
285 #define COMPAT_DLINK    0x00000002
286         struct ether_header     eh;
287         LIST_HEAD(, sess_con) listeners;
288         struct sess_hash_entry  sesshash[SESSHASHSIZE];
289         struct maxptag  max_payload;    /* PPP-Max-Payload (RFC4638) */
290 };
291 typedef struct PPPoE *priv_p;
292
293 union uniq {
294         char bytes[sizeof(void *)];
295         void *pointer;
296 };
297
298 #define LEAVE(x) do { error = x; goto quit; } while(0)
299 static void     pppoe_start(sessp sp);
300 static void     pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2);
301 static const    struct pppoe_tag *scan_tags(sessp sp,
302                         const struct pppoe_hdr* ph);
303 static  int     pppoe_send_event(sessp sp, enum cmd cmdid);
304
305 /*************************************************************************
306  * Some basic utilities  from the Linux version with author's permission.*
307  * Author:      Michal Ostrowski <mostrows@styx.uwaterloo.ca>            *
308  ************************************************************************/
309
310 /*
311  * Return the location where the next tag can be put
312  */
313 static __inline const struct pppoe_tag*
314 next_tag(const struct pppoe_hdr* ph)
315 {
316         return (const struct pppoe_tag*)(((const char*)(ph + 1))
317             + ntohs(ph->length));
318 }
319
320 /*
321  * Look for a tag of a specific type.
322  * Don't trust any length the other end says,
323  * but assume we already sanity checked ph->length.
324  */
325 static const struct pppoe_tag*
326 get_tag(const struct pppoe_hdr* ph, uint16_t idx)
327 {
328         const char *const end = (const char *)next_tag(ph);
329         const struct pppoe_tag *pt = (const void *)(ph + 1);
330         const char *ptn;
331
332         /*
333          * Keep processing tags while a tag header will still fit.
334          */
335         while((const char*)(pt + 1) <= end) {
336                 /*
337                  * If the tag data would go past the end of the packet, abort.
338                  */
339                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
340                 if (ptn > end) {
341                         CTR2(KTR_NET, "%20s: invalid length for tag %d",
342                             __func__, idx);
343                         return (NULL);
344                 }
345                 if (pt->tag_type == idx) {
346                         CTR2(KTR_NET, "%20s: found tag %d", __func__, idx);
347                         return (pt);
348                 }
349
350                 pt = (const struct pppoe_tag*)ptn;
351         }
352
353         CTR2(KTR_NET, "%20s: not found tag %d", __func__, idx);
354         return (NULL);
355 }
356
357 /**************************************************************************
358  * Inlines to initialise or add tags to a session's tag list.
359  **************************************************************************/
360 /*
361  * Initialise the session's tag list.
362  */
363 static void
364 init_tags(sessp sp)
365 {
366         KASSERT(sp->neg != NULL, ("%s: no neg", __func__));
367         sp->neg->numtags = 0;
368 }
369
370 static void
371 insert_tag(sessp sp, const struct pppoe_tag *tp)
372 {
373         negp neg = sp->neg;
374         int i;
375
376         KASSERT(neg != NULL, ("%s: no neg", __func__));
377         if ((i = neg->numtags++) < NUMTAGS) {
378                 neg->tags[i] = tp;
379         } else {
380                 log(LOG_NOTICE, "ng_pppoe: asked to add too many tags to "
381                     "packet\n");
382                 neg->numtags--;
383         }
384 }
385
386 /*
387  * Make up a packet, using the tags filled out for the session.
388  *
389  * Assume that the actual pppoe header and ethernet header
390  * are filled out externally to this routine.
391  * Also assume that neg->wh points to the correct
392  * location at the front of the buffer space.
393  */
394 static void
395 make_packet(sessp sp) {
396         struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header;
397         const struct pppoe_tag **tag;
398         char *dp;
399         int count;
400         int tlen;
401         uint16_t length = 0;
402
403         KASSERT((sp->neg != NULL) && (sp->neg->m != NULL),
404             ("%s: called from wrong state", __func__));
405         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
406
407         dp = (char *)(&wh->ph + 1);
408         for (count = 0, tag = sp->neg->tags;
409             ((count < sp->neg->numtags) && (count < NUMTAGS));
410             tag++, count++) {
411                 tlen = ntohs((*tag)->tag_len) + sizeof(**tag);
412                 if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) {
413                         log(LOG_NOTICE, "ng_pppoe: tags too long\n");
414                         sp->neg->numtags = count;
415                         break;  /* XXX chop off what's too long */
416                 }
417                 bcopy(*tag, (char *)dp, tlen);
418                 length += tlen;
419                 dp += tlen;
420         }
421         wh->ph.length = htons(length);
422         sp->neg->m->m_len = length + sizeof(*wh);
423         sp->neg->m->m_pkthdr.len = length + sizeof(*wh);
424 }
425
426 /**************************************************************************
427  * Routines to match a service.                                           *
428  **************************************************************************/
429
430 /*
431  * Find a hook that has a service string that matches that
432  * we are seeking. For now use a simple string.
433  * In the future we may need something like regexp().
434  *
435  * Null string is a wildcard (ANY service), according to RFC2516.
436  * And historical FreeBSD wildcard is also "*".
437  */
438
439 static hook_p
440 pppoe_match_svc(node_p node, const struct pppoe_tag *tag)
441 {
442         const priv_p privp = NG_NODE_PRIVATE(node);
443         sessp sp;
444
445         LIST_FOREACH(sp, &privp->listeners, sessions) {
446                 negp neg = sp->neg;
447
448                 /* Empty Service-Name matches any service. */
449                 if (neg->service_len == 0)
450                         break;
451
452                 /* Special case for a blank or "*" service name (wildcard). */
453                 if (neg->service_len == 1 && neg->service.data[0] == '*')
454                         break;
455
456                 /* If the lengths don't match, that aint it. */
457                 if (neg->service_len != ntohs(tag->tag_len))
458                         continue;
459
460                 if (strncmp((const char *)(tag + 1), neg->service.data,
461                     ntohs(tag->tag_len)) == 0)
462                         break;
463         }
464         CTR3(KTR_NET, "%20s: matched %p for %s", __func__,
465             sp?sp->hook:NULL, (const char *)(tag + 1));
466
467         return (sp?sp->hook:NULL);
468 }
469
470 /*
471  * Broadcast the PADI packet in m0 to all listening hooks.
472  * This routine is called when a PADI with empty Service-Name
473  * tag is received. Client should receive PADOs with all
474  * available services.
475  */
476 static int
477 pppoe_broadcast_padi(node_p node, struct mbuf *m0)
478 {
479         const priv_p privp = NG_NODE_PRIVATE(node);
480         sessp sp;
481         int error = 0;
482
483         LIST_FOREACH(sp, &privp->listeners, sessions) {
484                 struct mbuf *m;
485
486                 m = m_dup(m0, M_NOWAIT);
487                 if (m == NULL)
488                         return (ENOMEM);
489                 NG_SEND_DATA_ONLY(error, sp->hook, m);
490                 if (error)
491                         return (error);
492         }
493
494         return (0);
495 }
496
497 /*
498  * Find a hook, which name equals to given service.
499  */
500 static hook_p
501 pppoe_find_svc(node_p node, const char *svc_name, int svc_len)
502 {
503         const priv_p privp = NG_NODE_PRIVATE(node);
504         sessp sp;
505
506         LIST_FOREACH(sp, &privp->listeners, sessions) {
507                 negp neg = sp->neg;
508
509                 if (neg->service_len == svc_len &&
510                     strncmp(svc_name, neg->service.data, svc_len) == 0)
511                         return (sp->hook);
512         }
513
514         return (NULL);
515 }
516
517 /**************************************************************************
518  * Routines to find a particular session that matches an incoming packet. *
519  **************************************************************************/
520 /* Find free session and add to hash. */
521 static uint16_t
522 pppoe_getnewsession(sessp sp)
523 {
524         const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook));
525         static uint16_t pppoe_sid = 1;
526         sessp   tsp;
527         uint16_t val, hash;
528
529 restart:
530         /* Atomicity is not needed here as value will be checked. */
531         val = pppoe_sid++;
532         /* Spec says 0xFFFF is reserved, also don't use 0x0000. */
533         if (val == 0xffff || val == 0x0000)
534                 val = pppoe_sid = 1;
535
536         /* Check it isn't already in use. */
537         hash = SESSHASH(val);
538         mtx_lock(&privp->sesshash[hash].mtx);
539         LIST_FOREACH(tsp, &privp->sesshash[hash].head, sessions) {
540                 if (tsp->Session_ID == val)
541                         break;
542         }
543         if (!tsp) {
544                 sp->Session_ID = val;
545                 LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions);
546         }
547         mtx_unlock(&privp->sesshash[hash].mtx);
548         if (tsp)
549                 goto restart;
550
551         CTR2(KTR_NET, "%20s: new sid %d", __func__, val);
552
553         return (val);
554 }
555
556 /* Add specified session to hash. */
557 static void
558 pppoe_addsession(sessp sp)
559 {
560         const priv_p    privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook));
561         uint16_t        hash = SESSHASH(sp->Session_ID);
562
563         mtx_lock(&privp->sesshash[hash].mtx);
564         LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions);
565         mtx_unlock(&privp->sesshash[hash].mtx);
566 }
567
568 /* Delete specified session from hash. */
569 static void
570 pppoe_delsession(sessp sp)
571 {
572         const priv_p    privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook));
573         uint16_t        hash = SESSHASH(sp->Session_ID);
574
575         mtx_lock(&privp->sesshash[hash].mtx);
576         LIST_REMOVE(sp, sessions);
577         mtx_unlock(&privp->sesshash[hash].mtx);
578 }
579
580 /* Find matching peer/session combination. */
581 static sessp
582 pppoe_findsession(priv_p privp, const struct pppoe_full_hdr *wh)
583 {
584         uint16_t        session = ntohs(wh->ph.sid);
585         uint16_t        hash = SESSHASH(session);
586         sessp           sp = NULL;
587
588         mtx_lock(&privp->sesshash[hash].mtx);
589         LIST_FOREACH(sp, &privp->sesshash[hash].head, sessions) {
590                 if (sp->Session_ID == session &&
591                     bcmp(sp->pkt_hdr.eh.ether_dhost,
592                      wh->eh.ether_shost, ETHER_ADDR_LEN) == 0) {
593                         break;
594                 }
595         }
596         mtx_unlock(&privp->sesshash[hash].mtx);
597         CTR3(KTR_NET, "%20s: matched %p for %d", __func__, sp?sp->hook:NULL,
598             session);
599
600         return (sp);
601 }
602
603 static hook_p
604 pppoe_finduniq(node_p node, const struct pppoe_tag *tag)
605 {
606         hook_p  hook = NULL;
607         sessp   sp;
608
609         /* Cycle through all known hooks. */
610         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
611                 /* Skip any nonsession hook. */
612                 if (NG_HOOK_PRIVATE(hook) == NULL)
613                         continue;
614                 sp = NG_HOOK_PRIVATE(hook);
615                 /* Skip already connected sessions. */
616                 if (sp->neg == NULL)
617                         continue;
618                 if (sp->neg->host_uniq_len == ntohs(tag->tag_len) &&
619                     bcmp(sp->neg->host_uniq.data, (const char *)(tag + 1),
620                      sp->neg->host_uniq_len) == 0)
621                         break;
622         }
623         CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, sp);
624
625         return (hook);
626 }
627
628 static hook_p
629 pppoe_findcookie(node_p node, const struct pppoe_tag *tag)
630 {
631         hook_p  hook = NULL;
632         union uniq cookie;
633
634         bcopy(tag + 1, cookie.bytes, sizeof(void *));
635         /* Cycle through all known hooks. */
636         LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
637                 /* Skip any nonsession hook. */
638                 if (NG_HOOK_PRIVATE(hook) == NULL)
639                         continue;
640                 if (cookie.pointer == NG_HOOK_PRIVATE(hook))
641                         break;
642         }
643         CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, cookie.pointer);
644
645         return (hook);
646 }
647
648 /**************************************************************************
649  * Start of Netgraph entrypoints.                                         *
650  **************************************************************************/
651
652 /*
653  * Allocate the private data structure and link it with node.
654  */
655 static int
656 ng_pppoe_constructor(node_p node)
657 {
658         priv_p  privp;
659         int     i;
660
661         /* Initialize private descriptor. */
662         privp = malloc(sizeof(*privp), M_NETGRAPH_PPPOE, M_WAITOK | M_ZERO);
663
664         /* Link structs together; this counts as our one reference to *node. */
665         NG_NODE_SET_PRIVATE(node, privp);
666         privp->node = node;
667
668         /* Initialize to standard mode. */
669         memset(&privp->eh.ether_dhost, 0xff, ETHER_ADDR_LEN);
670         privp->eh.ether_type = ETHERTYPE_PPPOE_DISC;
671
672         LIST_INIT(&privp->listeners);
673         for (i = 0; i < SESSHASHSIZE; i++) {
674             mtx_init(&privp->sesshash[i].mtx, "PPPoE hash mutex", NULL, MTX_DEF);
675             LIST_INIT(&privp->sesshash[i].head);
676         }
677
678         CTR3(KTR_NET, "%20s: created node [%x] (%p)",
679             __func__, node->nd_ID, node);
680
681         return (0);
682 }
683
684 /*
685  * Give our ok for a hook to be added...
686  * point the hook's private info to the hook structure.
687  *
688  * The following hook names are special:
689  *  "ethernet":  the hook that should be connected to a NIC.
690  *  "debug":    copies of data sent out here  (when I write the code).
691  * All other hook names need only be unique. (the framework checks this).
692  */
693 static int
694 ng_pppoe_newhook(node_p node, hook_p hook, const char *name)
695 {
696         const priv_p privp = NG_NODE_PRIVATE(node);
697         sessp sp;
698
699         if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) {
700                 privp->ethernet_hook = hook;
701                 NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_ether);
702         } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) {
703                 privp->debug_hook = hook;
704                 NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_debug);
705         } else {
706                 /*
707                  * Any other unique name is OK.
708                  * The infrastructure has already checked that it's unique,
709                  * so just allocate it and hook it in.
710                  */
711                 sp = malloc(sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO);
712                 if (sp == NULL)
713                         return (ENOMEM);
714
715                 NG_HOOK_SET_PRIVATE(hook, sp);
716                 sp->hook = hook;
717         }
718         CTR5(KTR_NET, "%20s: node [%x] (%p) connected hook %s (%p)",
719             __func__, node->nd_ID, node, name, hook);
720
721         return(0);
722 }
723
724 /*
725  * Hook has been added successfully. Request the MAC address of
726  * the underlying Ethernet node.
727  */
728 static int
729 ng_pppoe_connect(hook_p hook)
730 {
731         const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
732         struct ng_mesg *msg;
733         int error;
734
735         if (hook != privp->ethernet_hook)
736                 return (0);
737
738         /*
739          * If this is Ethernet hook, then request MAC address
740          * from our downstream.
741          */
742         NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, 0, M_NOWAIT);
743         if (msg == NULL)
744                 return (ENOBUFS);
745
746         /*
747          * Our hook and peer hook have HK_INVALID flag set,
748          * so we can't use NG_SEND_MSG_HOOK() macro here.
749          */
750         NG_SEND_MSG_ID(error, privp->node, msg,
751             NG_NODE_ID(NG_PEER_NODE(privp->ethernet_hook)),
752             NG_NODE_ID(privp->node));
753
754         return (error);
755 }
756 /*
757  * Get a netgraph control message.
758  * Check it is one we understand. If needed, send a response.
759  * We sometimes save the address for an async action later.
760  * Always free the message.
761  */
762 static int
763 ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook)
764 {
765         struct epoch_tracker et;
766         priv_p privp = NG_NODE_PRIVATE(node);
767         struct ngpppoe_init_data *ourmsg = NULL;
768         struct ng_mesg *resp = NULL;
769         int error = 0;
770         hook_p hook = NULL;
771         sessp sp = NULL;
772         negp neg = NULL;
773         struct ng_mesg *msg;
774
775         NGI_GET_MSG(item, msg);
776         CTR5(KTR_NET, "%20s: node [%x] (%p) got message %d with cookie %d",
777             __func__, node->nd_ID, node, msg->header.cmd,
778             msg->header.typecookie);
779
780         /* Deal with message according to cookie and command. */
781         switch (msg->header.typecookie) {
782         case NGM_PPPOE_COOKIE:
783                 switch (msg->header.cmd) {
784                 case NGM_PPPOE_CONNECT:
785                 case NGM_PPPOE_LISTEN:
786                 case NGM_PPPOE_OFFER:
787                 case NGM_PPPOE_SERVICE:
788                 case NGM_PPPOE_SEND_HURL:
789                 case NGM_PPPOE_SEND_MOTM:
790                         ourmsg = (struct ngpppoe_init_data *)msg->data;
791                         if (msg->header.arglen < sizeof(*ourmsg)) {
792                                 log(LOG_ERR, "ng_pppoe[%x]: init data too "
793                                     "small\n", node->nd_ID);
794                                 LEAVE(EMSGSIZE);
795                         }
796                         if (msg->header.cmd == NGM_PPPOE_SEND_HURL ||
797                             msg->header.cmd == NGM_PPPOE_SEND_MOTM) {
798                                 if (msg->header.arglen - sizeof(*ourmsg) >
799                                     PPPOE_PADM_VALUE_SIZE) {
800                                         log(LOG_ERR, "ng_pppoe[%x]: message "
801                                             "too big\n", node->nd_ID);
802                                         LEAVE(EMSGSIZE);
803                                 }
804                         } else {
805                                 if (msg->header.arglen - sizeof(*ourmsg) >
806                                     PPPOE_SERVICE_NAME_SIZE) {
807                                         log(LOG_ERR, "ng_pppoe[%x]: service name "
808                                             "too big\n", node->nd_ID);
809                                         LEAVE(EMSGSIZE);
810                                 }
811                         }
812                         if (msg->header.arglen - sizeof(*ourmsg) <
813                             ourmsg->data_len) {
814                                 log(LOG_ERR, "ng_pppoe[%x]: init data has bad "
815                                     "length, %d should be %zd\n", node->nd_ID,
816                                     ourmsg->data_len,
817                                     msg->header.arglen - sizeof (*ourmsg));
818                                 LEAVE(EMSGSIZE);
819                         }
820
821                         /* Make sure strcmp will terminate safely. */
822                         ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0';
823
824                         /* Find hook by name. */
825                         hook = ng_findhook(node, ourmsg->hook);
826                         if (hook == NULL)
827                                 LEAVE(ENOENT);
828
829                         sp = NG_HOOK_PRIVATE(hook);
830                         if (sp == NULL)
831                                 LEAVE(EINVAL);
832
833                         if (msg->header.cmd == NGM_PPPOE_LISTEN) {
834                                 /*
835                                  * Ensure we aren't already listening for this
836                                  * service.
837                                  */
838                                 if (pppoe_find_svc(node, ourmsg->data,
839                                     ourmsg->data_len) != NULL)
840                                         LEAVE(EEXIST);
841                         }
842
843                         /*
844                          * PPPOE_SERVICE advertisements are set up
845                          * on sessions that are in PRIMED state.
846                          */
847                         if (msg->header.cmd == NGM_PPPOE_SERVICE)
848                                 break;
849
850                         /*
851                          * PADM messages are set up on active sessions.
852                          */
853                         if (msg->header.cmd == NGM_PPPOE_SEND_HURL ||
854                             msg->header.cmd == NGM_PPPOE_SEND_MOTM) {
855                                 if (sp->state != PPPOE_NEWCONNECTED &&
856                                     sp->state != PPPOE_CONNECTED) {
857                                         log(LOG_NOTICE, "ng_pppoe[%x]: session is not "
858                                             "active\n", node->nd_ID);
859                                         LEAVE(EISCONN);
860                                 }
861                                 break;
862                         }
863
864                         if (sp->state != PPPOE_SNONE) {
865                                 log(LOG_NOTICE, "ng_pppoe[%x]: Session already "
866                                     "active\n", node->nd_ID);
867                                 LEAVE(EISCONN);
868                         }
869
870                         /*
871                          * Set up prototype header.
872                          */
873                         neg = malloc(sizeof(*neg), M_NETGRAPH_PPPOE,
874                             M_NOWAIT | M_ZERO);
875
876                         if (neg == NULL)
877                                 LEAVE(ENOMEM);
878
879                         neg->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
880                         if (neg->m == NULL) {
881                                 free(neg, M_NETGRAPH_PPPOE);
882                                 LEAVE(ENOBUFS);
883                         }
884                         neg->m->m_pkthdr.rcvif = NULL;
885                         sp->neg = neg;
886                         ng_callout_init(&neg->handle);
887                         neg->m->m_len = sizeof(struct pppoe_full_hdr);
888                         neg->pkt = mtod(neg->m, union packet*);
889                         memcpy((void *)&neg->pkt->pkt_header.eh,
890                             &privp->eh, sizeof(struct ether_header));
891                         neg->pkt->pkt_header.ph.ver = 0x1;
892                         neg->pkt->pkt_header.ph.type = 0x1;
893                         neg->pkt->pkt_header.ph.sid = 0x0000;
894                         neg->timeout = 0;
895
896                         sp->creator = NGI_RETADDR(item);
897                 }
898                 switch (msg->header.cmd) {
899                 case NGM_PPPOE_GET_STATUS:
900                     {
901                         struct ngpppoestat *stats;
902
903                         NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
904                         if (!resp)
905                                 LEAVE(ENOMEM);
906
907                         stats = (struct ngpppoestat *) resp->data;
908                         stats->packets_in = privp->packets_in;
909                         stats->packets_out = privp->packets_out;
910                         break;
911                     }
912                 case NGM_PPPOE_CONNECT:
913                     {
914                         /*
915                          * Check the hook exists and is Uninitialised.
916                          * Send a PADI request, and start the timeout logic.
917                          * Store the originator of this message so we can send
918                          * a success or fail message to them later.
919                          * Move the session to SINIT.
920                          * Set up the session to the correct state and
921                          * start it.
922                          */
923                         int     acnpos, acnlen = 0, acnsep = 0;
924                         int     hupos, hulen = 0, husep = 0;
925                         int     i, srvpos, srvlen;
926                         acnpos = 0;
927                         for (i = 0; i < ourmsg->data_len; i++) {
928                                 if (ourmsg->data[i] == '\\') {
929                                         acnlen = i;
930                                         acnsep = 1;
931                                         break;
932                                 }
933                         }
934                         hupos = acnlen + acnsep;
935                         for (i = hupos; i < ourmsg->data_len; i++) {
936                                 if (ourmsg->data[i] == '|') {
937                                         hulen = i - hupos;
938                                         husep = 1;
939                                         break;
940                                 }
941                         }
942                         srvpos = hupos + hulen + husep;
943                         srvlen = ourmsg->data_len - srvpos;
944
945                         bcopy(ourmsg->data + acnpos, neg->ac_name.data, acnlen);
946                         neg->ac_name_len = acnlen;
947
948                         neg->host_uniq.hdr.tag_type = PTT_HOST_UNIQ;
949                         if (hulen == 0) {
950                                 /* Not provided, generate one */
951                                 neg->host_uniq.hdr.tag_len = htons(sizeof(sp));
952                                 bcopy(&sp, neg->host_uniq.data, sizeof(sp));
953                                 neg->host_uniq_len = sizeof(sp);
954                         } else if (hulen > 2 && ourmsg->data[hupos] == '0' &&
955                           ourmsg->data[hupos + 1] == 'x' && hulen % 2 == 0) {
956                                 /* Hex encoded */
957                                 static const char hexdig[16] = "0123456789abcdef";
958                                 int j;
959
960                                 neg->host_uniq.hdr.tag_len = htons((uint16_t)(hulen / 2 - 1));
961                                 for (i = 0; i < hulen - 2; i++) {
962                                         for (j = 0;
963                                              j < 16 &&
964                                              ourmsg->data[hupos + 2 + i] != hexdig[j];
965                                              j++);
966                                         if (j == 16)
967                                                 LEAVE(EINVAL);
968                                         if (i % 2 == 0)
969                                                 neg->host_uniq.data[i / 2] = j << 4;
970                                         else
971                                                 neg->host_uniq.data[i / 2] |= j;
972                                 }
973                                 neg->host_uniq_len = hulen / 2 - 1;
974                         } else {
975                                 /* Plain string */
976                                 neg->host_uniq.hdr.tag_len = htons((uint16_t)hulen);
977                                 bcopy(ourmsg->data + hupos, neg->host_uniq.data, hulen);
978                                 neg->host_uniq_len = hulen;
979                         }
980
981                         neg->service.hdr.tag_type = PTT_SRV_NAME;
982                         neg->service.hdr.tag_len = htons((uint16_t)srvlen);
983                         bcopy(ourmsg->data + srvpos, neg->service.data, srvlen);
984                         neg->service_len = srvlen;
985                         NET_EPOCH_ENTER(et);
986                         pppoe_start(sp);
987                         NET_EPOCH_EXIT(et);
988                         break;
989                     }
990                 case NGM_PPPOE_LISTEN:
991                         /*
992                          * Check the hook exists and is Uninitialised.
993                          * Install the service matching string.
994                          * Store the originator of this message so we can send
995                          * a success or fail message to them later.
996                          * Move the hook to 'LISTENING'
997                          */
998                         neg->service.hdr.tag_type = PTT_SRV_NAME;
999                         neg->service.hdr.tag_len =
1000                             htons((uint16_t)ourmsg->data_len);
1001
1002                         if (ourmsg->data_len)
1003                                 bcopy(ourmsg->data, neg->service.data,
1004                                     ourmsg->data_len);
1005                         neg->service_len = ourmsg->data_len;
1006                         neg->pkt->pkt_header.ph.code = PADT_CODE;
1007                         /*
1008                          * Wait for PADI packet coming from Ethernet.
1009                          */
1010                         sp->state = PPPOE_LISTENING;
1011                         LIST_INSERT_HEAD(&privp->listeners, sp, sessions);
1012                         break;
1013                 case NGM_PPPOE_OFFER:
1014                         /*
1015                          * Check the hook exists and is Uninitialised.
1016                          * Store the originator of this message so we can send
1017                          * a success of fail message to them later.
1018                          * Store the AC-Name given and go to PRIMED.
1019                          */
1020                         neg->ac_name.hdr.tag_type = PTT_AC_NAME;
1021                         neg->ac_name.hdr.tag_len =
1022                             htons((uint16_t)ourmsg->data_len);
1023                         if (ourmsg->data_len)
1024                                 bcopy(ourmsg->data, neg->ac_name.data,
1025                                     ourmsg->data_len);
1026                         neg->ac_name_len = ourmsg->data_len;
1027                         neg->pkt->pkt_header.ph.code = PADO_CODE;
1028                         /*
1029                          * Wait for PADI packet coming from hook.
1030                          */
1031                         sp->state = PPPOE_PRIMED;
1032                         break;
1033                 case NGM_PPPOE_SERVICE:
1034                         /*
1035                          * Check the session is primed.
1036                          * for now just allow ONE service to be advertised.
1037                          * If you do it twice you just overwrite.
1038                          */
1039                         if (sp->state != PPPOE_PRIMED) {
1040                                 log(LOG_NOTICE, "ng_pppoe[%x]: session not "
1041                                     "primed\n", node->nd_ID);
1042                                 LEAVE(EISCONN);
1043                         }
1044                         neg = sp->neg;
1045                         neg->service.hdr.tag_type = PTT_SRV_NAME;
1046                         neg->service.hdr.tag_len =
1047                             htons((uint16_t)ourmsg->data_len);
1048
1049                         if (ourmsg->data_len)
1050                                 bcopy(ourmsg->data, neg->service.data,
1051                                     ourmsg->data_len);
1052                         neg->service_len = ourmsg->data_len;
1053                         break;
1054                 case NGM_PPPOE_SETMODE:
1055                     {
1056                         char *s;
1057                         size_t len;
1058
1059                         if (msg->header.arglen == 0)
1060                                 LEAVE(EINVAL);
1061
1062                         s = (char *)msg->data;
1063                         len = msg->header.arglen - 1;
1064
1065                         /* Search for matching mode string. */
1066                         if (len == strlen(NG_PPPOE_STANDARD) &&
1067                             (strncmp(NG_PPPOE_STANDARD, s, len) == 0)) {
1068                                 privp->flags = 0;
1069                                 privp->eh.ether_type = ETHERTYPE_PPPOE_DISC;
1070                                 break;
1071                         }
1072                         if (len == strlen(NG_PPPOE_3COM) &&
1073                             (strncmp(NG_PPPOE_3COM, s, len) == 0)) {
1074                                 privp->flags |= COMPAT_3COM;
1075                                 privp->eh.ether_type =
1076                                     ETHERTYPE_PPPOE_3COM_DISC;
1077                                 break;
1078                         }
1079                         if (len == strlen(NG_PPPOE_DLINK) &&
1080                             (strncmp(NG_PPPOE_DLINK, s, len) == 0)) {
1081                                 privp->flags |= COMPAT_DLINK;
1082                                 break;
1083                         }
1084                         error = EINVAL;
1085                         break;
1086                     }
1087                 case NGM_PPPOE_GETMODE:
1088                     {
1089                         char *s;
1090                         size_t len = 0;
1091
1092                         if (privp->flags == 0)
1093                                 len += strlen(NG_PPPOE_STANDARD) + 1;
1094                         if (privp->flags & COMPAT_3COM)
1095                                 len += strlen(NG_PPPOE_3COM) + 1;
1096                         if (privp->flags & COMPAT_DLINK)
1097                                 len += strlen(NG_PPPOE_DLINK) + 1;
1098
1099                         NG_MKRESPONSE(resp, msg, len, M_NOWAIT);
1100                         if (resp == NULL)
1101                                 LEAVE(ENOMEM);
1102
1103                         s = (char *)resp->data;
1104                         if (privp->flags == 0) {
1105                                 len = strlen(NG_PPPOE_STANDARD);
1106                                 strlcpy(s, NG_PPPOE_STANDARD, len + 1);
1107                                 break;
1108                         }
1109                         if (privp->flags & COMPAT_3COM) {
1110                                 len = strlen(NG_PPPOE_3COM);
1111                                 strlcpy(s, NG_PPPOE_3COM, len + 1);
1112                                 s += len;
1113                         }
1114                         if (privp->flags & COMPAT_DLINK) {
1115                                 if (s != resp->data)
1116                                         *s++ = '|';
1117                                 len = strlen(NG_PPPOE_DLINK);
1118                                 strlcpy(s, NG_PPPOE_DLINK, len + 1);
1119                         }
1120                         break;
1121                     }
1122                 case NGM_PPPOE_SETENADDR:
1123                         if (msg->header.arglen != ETHER_ADDR_LEN)
1124                                 LEAVE(EINVAL);
1125                         bcopy(msg->data, &privp->eh.ether_shost,
1126                             ETHER_ADDR_LEN);
1127                         break;
1128                 case NGM_PPPOE_SETMAXP:
1129                         if (msg->header.arglen != sizeof(uint16_t))
1130                                 LEAVE(EINVAL);
1131                         privp->max_payload.hdr.tag_type = PTT_MAX_PAYL;
1132                         privp->max_payload.hdr.tag_len = htons(sizeof(uint16_t));
1133                         privp->max_payload.data = htons(*((uint16_t *)msg->data));
1134                         break;
1135                 case NGM_PPPOE_SEND_HURL:
1136                     {
1137                         struct mbuf *m;
1138
1139                         /* Generate a packet of that type. */
1140                         m = m_gethdr(M_NOWAIT, MT_DATA);
1141                         if (m == NULL)
1142                                 log(LOG_NOTICE, "ng_pppoe[%x]: session out of "
1143                                     "mbufs\n", node->nd_ID);
1144                         else {
1145                                 struct pppoe_full_hdr *wh;
1146                                 struct pppoe_tag *tag;
1147                                 int     error = 0;
1148
1149                                 wh = mtod(m, struct pppoe_full_hdr *);
1150                                 bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1151
1152                                 /* Revert the stored header to DISC/PADM mode. */
1153                                 wh->ph.code = PADM_CODE;
1154                                 /*
1155                                  * Configure ethertype depending on what
1156                                  * was used during sessions stage.
1157                                  */
1158                                 if (wh->eh.ether_type ==
1159                                     ETHERTYPE_PPPOE_3COM_SESS)
1160                                         wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC;
1161                                 else
1162                                         wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
1163                                 /*
1164                                  * Add PADM message and adjust sizes.
1165                                  */
1166                                 tag = (void *)(&wh->ph + 1);
1167                                 tag->tag_type = PTT_HURL;
1168                                 tag->tag_len = htons(ourmsg->data_len);
1169                                 strncpy((char *)(tag + 1), ourmsg->data, ourmsg->data_len);
1170                                 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) +
1171                                     ourmsg->data_len;
1172                                 wh->ph.length = htons(sizeof(*tag) + ourmsg->data_len);
1173                                 NET_EPOCH_ENTER(et);
1174                                 NG_SEND_DATA_ONLY(error,
1175                                     privp->ethernet_hook, m);
1176                                 NET_EPOCH_EXIT(et);
1177                         }
1178                         break;
1179                     }
1180                 case NGM_PPPOE_SEND_MOTM:
1181                     {
1182                         struct mbuf *m;
1183
1184                         /* Generate a packet of that type. */
1185                         m = m_gethdr(M_NOWAIT, MT_DATA);
1186                         if (m == NULL)
1187                                 log(LOG_NOTICE, "ng_pppoe[%x]: session out of "
1188                                     "mbufs\n", node->nd_ID);
1189                         else {
1190                                 struct pppoe_full_hdr *wh;
1191                                 struct pppoe_tag *tag;
1192                                 int     error = 0;
1193
1194                                 wh = mtod(m, struct pppoe_full_hdr *);
1195                                 bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1196
1197                                 /* Revert the stored header to DISC/PADM mode. */
1198                                 wh->ph.code = PADM_CODE;
1199                                 /*
1200                                  * Configure ethertype depending on what
1201                                  * was used during sessions stage.
1202                                  */
1203                                 if (wh->eh.ether_type ==
1204                                     ETHERTYPE_PPPOE_3COM_SESS)
1205                                         wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC;
1206                                 else
1207                                         wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
1208                                 /*
1209                                  * Add PADM message and adjust sizes.
1210                                  */
1211                                 tag = (void *)(&wh->ph + 1);
1212                                 tag->tag_type = PTT_MOTM;
1213                                 tag->tag_len = htons(ourmsg->data_len);
1214                                 strncpy((char *)(tag + 1), ourmsg->data, ourmsg->data_len);
1215                                 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) +
1216                                     ourmsg->data_len;
1217                                 wh->ph.length = htons(sizeof(*tag) + ourmsg->data_len);
1218                                 NET_EPOCH_ENTER(et);
1219                                 NG_SEND_DATA_ONLY(error,
1220                                     privp->ethernet_hook, m);
1221                                 NET_EPOCH_EXIT(et);
1222                         }
1223                         break;
1224                     }
1225                 default:
1226                         LEAVE(EINVAL);
1227                 }
1228                 break;
1229         case NGM_ETHER_COOKIE:
1230                 if (!(msg->header.flags & NGF_RESP))
1231                         LEAVE(EINVAL);
1232                 switch (msg->header.cmd) {
1233                 case NGM_ETHER_GET_ENADDR:
1234                         if (msg->header.arglen != ETHER_ADDR_LEN)
1235                                 LEAVE(EINVAL);
1236                         bcopy(msg->data, &privp->eh.ether_shost,
1237                             ETHER_ADDR_LEN);
1238                         break;
1239                 default:
1240                         LEAVE(EINVAL);
1241                 }
1242                 break;
1243         default:
1244                 LEAVE(EINVAL);
1245         }
1246
1247         /* Take care of synchronous response, if any. */
1248 quit:
1249         CTR2(KTR_NET, "%20s: returning %d", __func__, error);
1250         NG_RESPOND_MSG(error, node, item, resp);
1251         /* Free the message and return. */
1252         NG_FREE_MSG(msg);
1253         return(error);
1254 }
1255
1256 /*
1257  * Start a client into the first state. A separate function because
1258  * it can be needed if the negotiation times out.
1259  */
1260 static void
1261 pppoe_start(sessp sp)
1262 {
1263         hook_p  hook = sp->hook;
1264         node_p  node = NG_HOOK_NODE(hook);
1265         priv_p  privp = NG_NODE_PRIVATE(node);
1266         negp    neg = sp->neg;
1267         struct  mbuf *m0;
1268         int     error;
1269
1270         /*
1271          * Kick the state machine into starting up.
1272          */
1273         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1274         sp->state = PPPOE_SINIT;
1275         /*
1276          * Reset the packet header to broadcast. Since we are
1277          * in a client mode use configured ethertype.
1278          */
1279         memcpy((void *)&neg->pkt->pkt_header.eh, &privp->eh,
1280             sizeof(struct ether_header));
1281         neg->pkt->pkt_header.ph.code = PADI_CODE;
1282         init_tags(sp);
1283         insert_tag(sp, &neg->host_uniq.hdr);
1284         insert_tag(sp, &neg->service.hdr);
1285         if (privp->max_payload.data != 0)
1286                 insert_tag(sp, &privp->max_payload.hdr);
1287         make_packet(sp);
1288         /*
1289          * Send packet and prepare to retransmit it after timeout.
1290          */
1291         ng_callout(&neg->handle, node, hook, PPPOE_INITIAL_TIMEOUT * hz,
1292             pppoe_ticker, NULL, 0);
1293         neg->timeout = PPPOE_INITIAL_TIMEOUT * 2;
1294         m0 = m_copypacket(neg->m, M_NOWAIT);
1295         NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0);
1296 }
1297
1298 static int
1299 send_acname(sessp sp, const struct pppoe_tag *tag)
1300 {
1301         int error, tlen;
1302         struct ng_mesg *msg;
1303         struct ngpppoe_sts *sts;
1304
1305         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1306
1307         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME,
1308             sizeof(struct ngpppoe_sts), M_NOWAIT);
1309         if (msg == NULL)
1310                 return (ENOMEM);
1311
1312         sts = (struct ngpppoe_sts *)msg->data;
1313         tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len));
1314         strncpy(sts->hook, (const char *)(tag + 1), tlen);
1315         sts->hook[tlen] = '\0';
1316         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1317
1318         return (error);
1319 }
1320
1321 static int
1322 send_sessionid(sessp sp)
1323 {
1324         int error;
1325         struct ng_mesg *msg;
1326
1327         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1328
1329         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID,
1330             sizeof(uint16_t), M_NOWAIT);
1331         if (msg == NULL)
1332                 return (ENOMEM);
1333
1334         *(uint16_t *)msg->data = sp->Session_ID;
1335         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1336
1337         return (error);
1338 }
1339
1340 static int
1341 send_maxp(sessp sp, const struct pppoe_tag *tag)
1342 {
1343         int error;
1344         struct ng_mesg *msg;
1345         struct ngpppoe_maxp *maxp;
1346
1347         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1348
1349         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SETMAXP,
1350             sizeof(struct ngpppoe_maxp), M_NOWAIT);
1351         if (msg == NULL)
1352                 return (ENOMEM);
1353
1354         maxp = (struct ngpppoe_maxp *)msg->data;
1355         strncpy(maxp->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ);
1356         maxp->data = ntohs(((const struct maxptag *)tag)->data);
1357         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1358
1359         return (error);
1360 }
1361
1362 static int
1363 send_hurl(sessp sp, const struct pppoe_tag *tag)
1364 {
1365         int error, tlen;
1366         struct ng_mesg *msg;
1367         struct ngpppoe_padm *padm;
1368
1369         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1370
1371         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_HURL,
1372             sizeof(struct ngpppoe_padm), M_NOWAIT);
1373         if (msg == NULL)
1374                 return (ENOMEM);
1375
1376         padm = (struct ngpppoe_padm *)msg->data;
1377         tlen = min(PPPOE_PADM_VALUE_SIZE - 1, ntohs(tag->tag_len));
1378         strncpy(padm->msg, (const char *)(tag + 1), tlen);
1379         padm->msg[tlen] = '\0';
1380         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1381
1382         return (error);
1383 }
1384
1385 static int
1386 send_motm(sessp sp, const struct pppoe_tag *tag)
1387 {
1388         int error, tlen;
1389         struct ng_mesg *msg;
1390         struct ngpppoe_padm *padm;
1391
1392         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
1393
1394         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_MOTM,
1395             sizeof(struct ngpppoe_padm), M_NOWAIT);
1396         if (msg == NULL)
1397                 return (ENOMEM);
1398
1399         padm = (struct ngpppoe_padm *)msg->data;
1400         tlen = min(PPPOE_PADM_VALUE_SIZE - 1, ntohs(tag->tag_len));
1401         strncpy(padm->msg, (const char *)(tag + 1), tlen);
1402         padm->msg[tlen] = '\0';
1403         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
1404
1405         return (error);
1406 }
1407
1408 /*
1409  * Receive data from session hook and do something with it.
1410  */
1411 static int
1412 ng_pppoe_rcvdata(hook_p hook, item_p item)
1413 {
1414         node_p                  node = NG_HOOK_NODE(hook);
1415         const priv_p            privp = NG_NODE_PRIVATE(node);
1416         sessp                   sp = NG_HOOK_PRIVATE(hook);
1417         struct pppoe_full_hdr   *wh;
1418         struct mbuf             *m;
1419         int                     error;
1420
1421         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1422             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1423
1424         NGI_GET_M(item, m);
1425         switch (sp->state) {
1426         case    PPPOE_NEWCONNECTED:
1427         case    PPPOE_CONNECTED: {
1428                 /*
1429                  * Remove PPP address and control fields, if any.
1430                  * For example, ng_ppp(4) always sends LCP packets
1431                  * with address and control fields as required by
1432                  * generic PPP. PPPoE is an exception to the rule.
1433                  */
1434                 if (m->m_pkthdr.len >= 2) {
1435                         if (m->m_len < 2 && !(m = m_pullup(m, 2)))
1436                                 LEAVE(ENOBUFS);
1437                         if (mtod(m, u_char *)[0] == 0xff &&
1438                             mtod(m, u_char *)[1] == 0x03)
1439                                 m_adj(m, 2);
1440                 }
1441                 /*
1442                  * Bang in a pre-made header, and set the length up
1443                  * to be correct. Then send it to the ethernet driver.
1444                  */
1445                 M_PREPEND(m, sizeof(*wh), M_NOWAIT);
1446                 if (m == NULL)
1447                         LEAVE(ENOBUFS);
1448
1449                 wh = mtod(m, struct pppoe_full_hdr *);
1450                 bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1451                 wh->ph.length = htons(m->m_pkthdr.len - sizeof(*wh));
1452                 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m);
1453                 privp->packets_out++;
1454                 break;
1455                 }
1456         case    PPPOE_PRIMED: {
1457                 struct {
1458                         struct pppoe_tag hdr;
1459                         union   uniq    data;
1460                 } __packed      uniqtag;
1461                 const struct pppoe_tag  *tag;
1462                 struct mbuf     *m0;
1463                 const struct pppoe_hdr  *ph;
1464                 negp            neg = sp->neg;
1465                 uint8_t         code;
1466
1467                 /*
1468                  * A PADI packet is being returned by the application
1469                  * that has set up this hook. This indicates that it
1470                  * wants us to offer service.
1471                  */
1472                 if (m->m_len < sizeof(*wh)) {
1473                         m = m_pullup(m, sizeof(*wh));
1474                         if (m == NULL)
1475                                 LEAVE(ENOBUFS);
1476                 }
1477                 wh = mtod(m, struct pppoe_full_hdr *);
1478                 ph = &wh->ph;
1479                 code = wh->ph.code;
1480                 /* Use peers mode in session. */
1481                 neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type;
1482                 if (code != PADI_CODE)
1483                         LEAVE(EINVAL);
1484                 ng_uncallout(&neg->handle, node);
1485
1486                 /*
1487                  * This is the first time we hear
1488                  * from the client, so note it's
1489                  * unicast address, replacing the
1490                  * broadcast address.
1491                  */
1492                 bcopy(wh->eh.ether_shost,
1493                         neg->pkt->pkt_header.eh.ether_dhost,
1494                         ETHER_ADDR_LEN);
1495                 sp->state = PPPOE_SOFFER;
1496                 neg->timeout = 0;
1497                 neg->pkt->pkt_header.ph.code = PADO_CODE;
1498
1499                 /*
1500                  * Start working out the tags to respond with.
1501                  */
1502                 uniqtag.hdr.tag_type = PTT_AC_COOKIE;
1503                 uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
1504                 uniqtag.data.pointer = sp;
1505                 init_tags(sp);
1506                 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1507                 if ((tag = get_tag(ph, PTT_SRV_NAME)))
1508                         insert_tag(sp, tag);      /* return service */
1509                 /*
1510                  * If we have a NULL service request
1511                  * and have an extra service defined in this hook,
1512                  * then also add a tag for the extra service.
1513                  * XXX this is a hack. eventually we should be able
1514                  * to support advertising many services, not just one
1515                  */
1516                 if (((tag == NULL) || (tag->tag_len == 0)) &&
1517                     (neg->service.hdr.tag_len != 0)) {
1518                         insert_tag(sp, &neg->service.hdr); /* SERVICE */
1519                 }
1520                 if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1521                         insert_tag(sp, tag); /* returned hostunique */
1522                 insert_tag(sp, &uniqtag.hdr);
1523                 scan_tags(sp, ph);
1524                 make_packet(sp);
1525                 /*
1526                  * Send the offer but if they don't respond
1527                  * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
1528                  */
1529                 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz,
1530                     pppoe_ticker, NULL, 0);
1531                 m0 = m_copypacket(sp->neg->m, M_NOWAIT);
1532                 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1533                 privp->packets_out++;
1534                 break;
1535                 }
1536
1537         /*
1538          * Packets coming from the hook make no sense
1539          * to sessions in the rest of states. Throw them away.
1540          */
1541         default:
1542                 LEAVE(ENETUNREACH);
1543         }
1544 quit:
1545         if (item)
1546                 NG_FREE_ITEM(item);
1547         NG_FREE_M(m);
1548         return (error);
1549 }
1550
1551 /*
1552  * Receive data from ether and do something with it.
1553  */
1554 static int
1555 ng_pppoe_rcvdata_ether(hook_p hook, item_p item)
1556 {
1557         node_p                  node = NG_HOOK_NODE(hook);
1558         const priv_p            privp = NG_NODE_PRIVATE(node);
1559         sessp                   sp;
1560         const struct pppoe_tag  *utag = NULL, *tag = NULL;
1561         const struct pppoe_tag  sntag = { PTT_SRV_NAME, 0 };
1562         const struct pppoe_full_hdr *wh;
1563         const struct pppoe_hdr  *ph;
1564         negp                    neg = NULL;
1565         struct mbuf             *m;
1566         hook_p                  sendhook;
1567         int                     error = 0;
1568         uint16_t                length;
1569         uint8_t                 code;
1570         struct  mbuf            *m0;
1571
1572         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1573             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1574
1575         NGI_GET_M(item, m);
1576         /*
1577          * Dig out various fields from the packet.
1578          * Use them to decide where to send it.
1579          */
1580         privp->packets_in++;
1581         if( m->m_len < sizeof(*wh)) {
1582                 m = m_pullup(m, sizeof(*wh)); /* Checks length */
1583                 if (m == NULL) {
1584                         log(LOG_NOTICE, "ng_pppoe[%x]: couldn't "
1585                             "m_pullup(wh)\n", node->nd_ID);
1586                         LEAVE(ENOBUFS);
1587                 }
1588         }
1589         wh = mtod(m, struct pppoe_full_hdr *);
1590         length = ntohs(wh->ph.length);
1591         switch(wh->eh.ether_type) {
1592         case    ETHERTYPE_PPPOE_3COM_DISC: /* fall through */
1593         case    ETHERTYPE_PPPOE_DISC:
1594                 /*
1595                  * We need to try to make sure that the tag area
1596                  * is contiguous, or we could wander off the end
1597                  * of a buffer and make a mess.
1598                  * (Linux wouldn't have this problem).
1599                  */
1600                 if (m->m_pkthdr.len <= MHLEN) {
1601                         if( m->m_len < m->m_pkthdr.len) {
1602                                 m = m_pullup(m, m->m_pkthdr.len);
1603                                 if (m == NULL) {
1604                                         log(LOG_NOTICE, "ng_pppoe[%x]: "
1605                                             "couldn't m_pullup(pkthdr)\n",
1606                                             node->nd_ID);
1607                                         LEAVE(ENOBUFS);
1608                                 }
1609                         }
1610                 }
1611                 if (m->m_len != m->m_pkthdr.len) {
1612                         /*
1613                          * It's not all in one piece.
1614                          * We need to do extra work.
1615                          * Put it into a cluster.
1616                          */
1617                         struct mbuf *n;
1618                         n = m_dup(m, M_NOWAIT);
1619                         m_freem(m);
1620                         m = n;
1621                         if (m) {
1622                                 /* just check we got a cluster */
1623                                 if (m->m_len != m->m_pkthdr.len) {
1624                                         m_freem(m);
1625                                         m = NULL;
1626                                 }
1627                         }
1628                         if (m == NULL) {
1629                                 log(LOG_NOTICE, "ng_pppoe[%x]: packet "
1630                                     "fragmented\n", node->nd_ID);
1631                                 LEAVE(EMSGSIZE);
1632                         }
1633                 }
1634                 wh = mtod(m, struct pppoe_full_hdr *);
1635                 length = ntohs(wh->ph.length);
1636                 ph = &wh->ph;
1637                 code = wh->ph.code;
1638
1639                 switch(code) {
1640                 case    PADI_CODE:
1641                         /*
1642                          * We are a server:
1643                          * Look for a hook with the required service and send
1644                          * the ENTIRE packet up there. It should come back to
1645                          * a new hook in PRIMED state. Look there for further
1646                          * processing.
1647                          */
1648                         tag = get_tag(ph, PTT_SRV_NAME);
1649                         if (tag == NULL)
1650                                 tag = &sntag;
1651
1652                         /*
1653                          * First, try to match Service-Name against our 
1654                          * listening hooks. If no success and we are in D-Link
1655                          * compat mode and Service-Name is empty, then we 
1656                          * broadcast the PADI to all listening hooks.
1657                          */
1658                         sendhook = pppoe_match_svc(node, tag);
1659                         if (sendhook != NULL)
1660                                 NG_FWD_NEW_DATA(error, item, sendhook, m);
1661                         else if (privp->flags & COMPAT_DLINK &&
1662                                  ntohs(tag->tag_len) == 0)
1663                                 error = pppoe_broadcast_padi(node, m);
1664                         else
1665                                 error = ENETUNREACH;
1666                         break;
1667                 case    PADO_CODE:
1668                         /*
1669                          * We are a client:
1670                          * Use the host_uniq tag to find the hook this is in
1671                          * response to. Received #2, now send #3
1672                          * For now simply accept the first we receive.
1673                          */
1674                         utag = get_tag(ph, PTT_HOST_UNIQ);
1675                         if (utag == NULL) {
1676                                 log(LOG_NOTICE, "ng_pppoe[%x]: no host "
1677                                     "unique field\n", node->nd_ID);
1678                                 LEAVE(ENETUNREACH);
1679                         }
1680
1681                         sendhook = pppoe_finduniq(node, utag);
1682                         if (sendhook == NULL) {
1683                                 log(LOG_NOTICE, "ng_pppoe[%x]: no "
1684                                     "matching session\n", node->nd_ID);
1685                                 LEAVE(ENETUNREACH);
1686                         }
1687
1688                         /*
1689                          * Check the session is in the right state.
1690                          * It needs to be in PPPOE_SINIT.
1691                          */
1692                         sp = NG_HOOK_PRIVATE(sendhook);
1693                         if (sp->state == PPPOE_SREQ ||
1694                             sp->state == PPPOE_CONNECTED) {
1695                                 break;  /* Multiple PADO is OK. */
1696                         }
1697                         if (sp->state != PPPOE_SINIT) {
1698                                 log(LOG_NOTICE, "ng_pppoe[%x]: session "
1699                                     "in wrong state\n", node->nd_ID);
1700                                 LEAVE(ENETUNREACH);
1701                         }
1702                         neg = sp->neg;
1703                         /* If requested specific AC-name, check it. */
1704                         if (neg->ac_name_len) {
1705                                 tag = get_tag(ph, PTT_AC_NAME);
1706                                 if (!tag) {
1707                                         /* No PTT_AC_NAME in PADO */
1708                                         break;
1709                                 }
1710                                 if (neg->ac_name_len != htons(tag->tag_len) ||
1711                                     strncmp(neg->ac_name.data,
1712                                     (const char *)(tag + 1),
1713                                     neg->ac_name_len) != 0) {
1714                                         break;
1715                                 }
1716                         }
1717                         sp->state = PPPOE_SREQ;
1718                         ng_uncallout(&neg->handle, node);
1719
1720                         /*
1721                          * This is the first time we hear
1722                          * from the server, so note it's
1723                          * unicast address, replacing the
1724                          * broadcast address .
1725                          */
1726                         bcopy(wh->eh.ether_shost,
1727                                 neg->pkt->pkt_header.eh.ether_dhost,
1728                                 ETHER_ADDR_LEN);
1729                         neg->timeout = 0;
1730                         neg->pkt->pkt_header.ph.code = PADR_CODE;
1731                         init_tags(sp);
1732                         insert_tag(sp, utag);           /* Host Unique */
1733                         if ((tag = get_tag(ph, PTT_AC_COOKIE)))
1734                                 insert_tag(sp, tag);    /* return cookie */
1735                         if ((tag = get_tag(ph, PTT_AC_NAME))) { 
1736                                 insert_tag(sp, tag);    /* return it */
1737                                 send_acname(sp, tag);
1738                         }
1739                         if ((tag = get_tag(ph, PTT_MAX_PAYL)) &&
1740                             (privp->max_payload.data != 0))
1741                                 insert_tag(sp, tag);    /* return it */
1742                         insert_tag(sp, &neg->service.hdr); /* Service */
1743                         scan_tags(sp, ph);
1744                         make_packet(sp);
1745                         sp->state = PPPOE_SREQ;
1746                         ng_callout(&neg->handle, node, sp->hook,
1747                             PPPOE_INITIAL_TIMEOUT * hz,
1748                             pppoe_ticker, NULL, 0);
1749                         neg->timeout = PPPOE_INITIAL_TIMEOUT * 2;
1750                         m0 = m_copypacket(neg->m, M_NOWAIT);
1751                         NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1752                         break;
1753                 case    PADR_CODE:
1754                         /*
1755                          * We are a server:
1756                          * Use the ac_cookie tag to find the
1757                          * hook this is in response to.
1758                          */
1759                         utag = get_tag(ph, PTT_AC_COOKIE);
1760                         if ((utag == NULL) ||
1761                             (ntohs(utag->tag_len) != sizeof(sp))) {
1762                                 LEAVE(ENETUNREACH);
1763                         }
1764
1765                         sendhook = pppoe_findcookie(node, utag);
1766                         if (sendhook == NULL)
1767                                 LEAVE(ENETUNREACH);
1768
1769                         /*
1770                          * Check the session is in the right state.
1771                          * It needs to be in PPPOE_SOFFER or PPPOE_NEWCONNECTED.
1772                          * If the latter, then this is a retry by the client,
1773                          * so be nice, and resend.
1774                          */
1775                         sp = NG_HOOK_PRIVATE(sendhook);
1776                         if (sp->state == PPPOE_NEWCONNECTED) {
1777                                 /*
1778                                  * Whoa! drop back to resend that PADS packet.
1779                                  * We should still have a copy of it.
1780                                  */
1781                                 sp->state = PPPOE_SOFFER;
1782                         } else if (sp->state != PPPOE_SOFFER)
1783                                 LEAVE (ENETUNREACH);
1784                         neg = sp->neg;
1785                         ng_uncallout(&neg->handle, node);
1786                         neg->pkt->pkt_header.ph.code = PADS_CODE;
1787                         if (sp->Session_ID == 0) {
1788                                 neg->pkt->pkt_header.ph.sid =
1789                                     htons(pppoe_getnewsession(sp));
1790                         }
1791                         send_sessionid(sp);
1792                         neg->timeout = 0;
1793                         /*
1794                          * start working out the tags to respond with.
1795                          */
1796                         init_tags(sp);
1797                         insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1798                         if ((tag = get_tag(ph, PTT_SRV_NAME)))
1799                                 insert_tag(sp, tag);/* return service */
1800                         if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1801                                 insert_tag(sp, tag); /* return it */
1802                         insert_tag(sp, utag);   /* ac_cookie */
1803                         scan_tags(sp, ph);
1804                         make_packet(sp);
1805                         sp->state = PPPOE_NEWCONNECTED;
1806
1807                         /* Send the PADS without a timeout - we're now connected. */
1808                         m0 = m_copypacket(sp->neg->m, M_NOWAIT);
1809                         NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1810
1811                         /*
1812                          * Having sent the last Negotiation header,
1813                          * Set up the stored packet header to be correct for
1814                          * the actual session. But keep the negotialtion stuff
1815                          * around in case we need to resend this last packet.
1816                          * We'll discard it when we move from NEWCONNECTED
1817                          * to CONNECTED
1818                          */
1819                         sp->pkt_hdr = neg->pkt->pkt_header;
1820                         /* Configure ethertype depending on what
1821                          * ethertype was used at discovery phase */
1822                         if (sp->pkt_hdr.eh.ether_type ==
1823                             ETHERTYPE_PPPOE_3COM_DISC)
1824                                 sp->pkt_hdr.eh.ether_type
1825                                         = ETHERTYPE_PPPOE_3COM_SESS;
1826                         else
1827                                 sp->pkt_hdr.eh.ether_type
1828                                         = ETHERTYPE_PPPOE_SESS;
1829                         sp->pkt_hdr.ph.code = 0;
1830                         pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1831                         break;
1832                 case    PADS_CODE:
1833                         /*
1834                          * We are a client:
1835                          * Use the host_uniq tag to find the hook this is in
1836                          * response to. Take the session ID and store it away.
1837                          * Also make sure the pre-made header is correct and
1838                          * set us into Session mode.
1839                          */
1840                         utag = get_tag(ph, PTT_HOST_UNIQ);
1841                         if (utag == NULL) {
1842                                 LEAVE (ENETUNREACH);
1843                         }
1844                         sendhook = pppoe_finduniq(node, utag);
1845                         if (sendhook == NULL)
1846                                 LEAVE(ENETUNREACH);
1847
1848                         /*
1849                          * Check the session is in the right state.
1850                          * It needs to be in PPPOE_SREQ.
1851                          */
1852                         sp = NG_HOOK_PRIVATE(sendhook);
1853                         if (sp->state != PPPOE_SREQ)
1854                                 LEAVE(ENETUNREACH);
1855                         neg = sp->neg;
1856                         ng_uncallout(&neg->handle, node);
1857                         neg->pkt->pkt_header.ph.sid = wh->ph.sid;
1858                         sp->Session_ID = ntohs(wh->ph.sid);
1859                         pppoe_addsession(sp);
1860                         send_sessionid(sp);
1861                         neg->timeout = 0;
1862                         sp->state = PPPOE_CONNECTED;
1863                         /*
1864                          * Now we have gone to Connected mode,
1865                          * Free all resources needed for negotiation.
1866                          * Keep a copy of the header we will be using.
1867                          */
1868                         sp->pkt_hdr = neg->pkt->pkt_header;
1869                         if (privp->flags & COMPAT_3COM)
1870                                 sp->pkt_hdr.eh.ether_type
1871                                         = ETHERTYPE_PPPOE_3COM_SESS;
1872                         else
1873                                 sp->pkt_hdr.eh.ether_type
1874                                         = ETHERTYPE_PPPOE_SESS;
1875                         sp->pkt_hdr.ph.code = 0;
1876                         m_freem(neg->m);
1877                         free(sp->neg, M_NETGRAPH_PPPOE);
1878                         sp->neg = NULL;
1879                         if ((tag = get_tag(ph, PTT_MAX_PAYL)) &&
1880                             (privp->max_payload.data != 0))
1881                                 send_maxp(sp, tag);
1882                         pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1883                         break;
1884                 case    PADT_CODE:
1885                         /*
1886                          * Find matching peer/session combination.
1887                          */
1888                         sp = pppoe_findsession(privp, wh);
1889                         if (sp == NULL)
1890                                 LEAVE(ENETUNREACH);
1891                         /* Disconnect that hook. */
1892                         ng_rmhook_self(sp->hook);
1893                         break;
1894                 case    PADM_CODE:
1895                         /*
1896                          * We are a client:
1897                          * find matching peer/session combination.
1898                          */
1899                         sp = pppoe_findsession(privp, wh);
1900                         if (sp == NULL)
1901                                 LEAVE (ENETUNREACH);
1902                         if ((tag = get_tag(ph, PTT_HURL)))
1903                                 send_hurl(sp, tag);
1904                         if ((tag = get_tag(ph, PTT_MOTM)))
1905                                 send_motm(sp, tag);
1906                         break;
1907                 default:
1908                         LEAVE(EPFNOSUPPORT);
1909                 }
1910                 break;
1911         case    ETHERTYPE_PPPOE_3COM_SESS:
1912         case    ETHERTYPE_PPPOE_SESS:
1913                 /*
1914                  * Find matching peer/session combination.
1915                  */
1916                 sp = pppoe_findsession(privp, wh);
1917                 if (sp == NULL)
1918                         LEAVE (ENETUNREACH);
1919                 m_adj(m, sizeof(*wh));
1920
1921                 /* If packet too short, dump it. */
1922                 if (m->m_pkthdr.len < length)
1923                         LEAVE(EMSGSIZE);
1924                 /* Also need to trim excess at the end */
1925                 if (m->m_pkthdr.len > length) {
1926                         m_adj(m, -((int)(m->m_pkthdr.len - length)));
1927                 }
1928                 if ( sp->state != PPPOE_CONNECTED) {
1929                         if (sp->state == PPPOE_NEWCONNECTED) {
1930                                 sp->state = PPPOE_CONNECTED;
1931                                 /*
1932                                  * Now we have gone to Connected mode,
1933                                  * Free all resources needed for negotiation.
1934                                  * Be paranoid about whether there may be
1935                                  * a timeout.
1936                                  */
1937                                 m_freem(sp->neg->m);
1938                                 ng_uncallout(&sp->neg->handle, node);
1939                                 free(sp->neg, M_NETGRAPH_PPPOE);
1940                                 sp->neg = NULL;
1941                         } else {
1942                                 LEAVE (ENETUNREACH);
1943                         }
1944                 }
1945                 NG_FWD_NEW_DATA(error, item, sp->hook, m);
1946                 break;
1947         default:
1948                 LEAVE(EPFNOSUPPORT);
1949         }
1950 quit:
1951         if (item)
1952                 NG_FREE_ITEM(item);
1953         NG_FREE_M(m);
1954         return (error);
1955 }
1956
1957 /*
1958  * Receive data from debug hook and bypass it to ether.
1959  */
1960 static int
1961 ng_pppoe_rcvdata_debug(hook_p hook, item_p item)
1962 {
1963         node_p          node = NG_HOOK_NODE(hook);
1964         const priv_p    privp = NG_NODE_PRIVATE(node);
1965         int             error;
1966
1967         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1968             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1969
1970         NG_FWD_ITEM_HOOK(error, item, privp->ethernet_hook);
1971         privp->packets_out++;
1972         return (error);
1973 }
1974
1975 /*
1976  * Do local shutdown processing..
1977  * If we are a persistent device, we might refuse to go away, and
1978  * we'd only remove our links and reset ourself.
1979  */
1980 static int
1981 ng_pppoe_shutdown(node_p node)
1982 {
1983         const priv_p privp = NG_NODE_PRIVATE(node);
1984         int     i;
1985
1986         for (i = 0; i < SESSHASHSIZE; i++)
1987             mtx_destroy(&privp->sesshash[i].mtx);
1988         NG_NODE_SET_PRIVATE(node, NULL);
1989         NG_NODE_UNREF(privp->node);
1990         free(privp, M_NETGRAPH_PPPOE);
1991         return (0);
1992 }
1993
1994 /*
1995  * Hook disconnection
1996  *
1997  * Clean up all dangling links and information about the session/hook.
1998  * For this type, removal of the last link destroys the node.
1999  */
2000 static int
2001 ng_pppoe_disconnect(hook_p hook)
2002 {
2003         node_p node = NG_HOOK_NODE(hook);
2004         priv_p privp = NG_NODE_PRIVATE(node);
2005         sessp   sp;
2006
2007         if (hook == privp->debug_hook) {
2008                 privp->debug_hook = NULL;
2009         } else if (hook == privp->ethernet_hook) {
2010                 privp->ethernet_hook = NULL;
2011                 if (NG_NODE_IS_VALID(node))
2012                         ng_rmnode_self(node);
2013         } else {
2014                 sp = NG_HOOK_PRIVATE(hook);
2015                 if (sp->state != PPPOE_SNONE ) {
2016                         pppoe_send_event(sp, NGM_PPPOE_CLOSE);
2017                 }
2018                 /*
2019                  * According to the spec, if we are connected,
2020                  * we should send a DISC packet if we are shutting down
2021                  * a session.
2022                  */
2023                 if ((privp->ethernet_hook)
2024                 && ((sp->state == PPPOE_CONNECTED)
2025                  || (sp->state == PPPOE_NEWCONNECTED))) {
2026                         struct mbuf *m;
2027
2028                         /* Generate a packet of that type. */
2029                         m = m_gethdr(M_NOWAIT, MT_DATA);
2030                         if (m == NULL)
2031                                 log(LOG_NOTICE, "ng_pppoe[%x]: session out of "
2032                                     "mbufs\n", node->nd_ID);
2033                         else {
2034                                 struct pppoe_full_hdr *wh;
2035                                 struct pppoe_tag *tag;
2036                                 int     msglen = strlen(SIGNOFF);
2037                                 int     error = 0;
2038
2039                                 wh = mtod(m, struct pppoe_full_hdr *);
2040                                 bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
2041
2042                                 /* Revert the stored header to DISC/PADT mode. */
2043                                 wh->ph.code = PADT_CODE;
2044                                 /*
2045                                  * Configure ethertype depending on what
2046                                  * was used during sessions stage.
2047                                  */
2048                                 if (wh->eh.ether_type == 
2049                                     ETHERTYPE_PPPOE_3COM_SESS)
2050                                         wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC;
2051                                 else
2052                                         wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
2053                                 /*
2054                                  * Add a General error message and adjust
2055                                  * sizes.
2056                                  */
2057                                 tag = (void *)(&wh->ph + 1);
2058                                 tag->tag_type = PTT_GEN_ERR;
2059                                 tag->tag_len = htons((u_int16_t)msglen);
2060                                 strncpy((char *)(tag + 1), SIGNOFF, msglen);
2061                                 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) +
2062                                     msglen;
2063                                 wh->ph.length = htons(sizeof(*tag) + msglen);
2064                                 NG_SEND_DATA_ONLY(error,
2065                                         privp->ethernet_hook, m);
2066                         }
2067                 }
2068                 if (sp->state == PPPOE_LISTENING)
2069                         LIST_REMOVE(sp, sessions);
2070                 else if (sp->Session_ID)
2071                         pppoe_delsession(sp);
2072                 /*
2073                  * As long as we have somewhere to store the timeout handle,
2074                  * we may have a timeout pending.. get rid of it.
2075                  */
2076                 if (sp->neg) {
2077                         ng_uncallout(&sp->neg->handle, node);
2078                         if (sp->neg->m)
2079                                 m_freem(sp->neg->m);
2080                         free(sp->neg, M_NETGRAPH_PPPOE);
2081                 }
2082                 free(sp, M_NETGRAPH_PPPOE);
2083                 NG_HOOK_SET_PRIVATE(hook, NULL);
2084         }
2085         if ((NG_NODE_NUMHOOKS(node) == 0) &&
2086             (NG_NODE_IS_VALID(node)))
2087                 ng_rmnode_self(node);
2088         return (0);
2089 }
2090
2091 /*
2092  * Timeouts come here.
2093  */
2094 static void
2095 pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2)
2096 {
2097         priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
2098         sessp   sp = NG_HOOK_PRIVATE(hook);
2099         negp    neg = sp->neg;
2100         struct mbuf *m0 = NULL;
2101         int     error = 0;
2102
2103         CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d",
2104             __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID);
2105         switch(sp->state) {
2106                 /*
2107                  * Resend the last packet, using an exponential backoff.
2108                  * After a period of time, stop growing the backoff,
2109                  * And either leave it, or revert to the start.
2110                  */
2111         case    PPPOE_SINIT:
2112         case    PPPOE_SREQ:
2113                 /* Timeouts on these produce resends. */
2114                 m0 = m_copypacket(sp->neg->m, M_NOWAIT);
2115                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
2116                 ng_callout(&neg->handle, node, hook, neg->timeout * hz,
2117                     pppoe_ticker, NULL, 0);
2118                 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
2119                         if (sp->state == PPPOE_SREQ) {
2120                                 /* Revert to SINIT mode. */
2121                                 pppoe_start(sp);
2122                         } else {
2123                                 neg->timeout = PPPOE_TIMEOUT_LIMIT;
2124                         }
2125                 }
2126                 break;
2127         case    PPPOE_PRIMED:
2128         case    PPPOE_SOFFER:
2129                 /* A timeout on these says "give up" */
2130                 ng_rmhook_self(hook);
2131                 break;
2132         default:
2133                 /* Timeouts have no meaning in other states. */
2134                 log(LOG_NOTICE, "ng_pppoe[%x]: unexpected timeout\n",
2135                     node->nd_ID);
2136         }
2137 }
2138
2139 /*
2140  * Parse an incoming packet to see if any tags should be copied to the
2141  * output packet. Don't do any tags that have been handled in the main
2142  * state machine.
2143  */
2144 static const struct pppoe_tag*
2145 scan_tags(sessp sp, const struct pppoe_hdr* ph)
2146 {
2147         const char *const end = (const char *)next_tag(ph);
2148         const char *ptn;
2149         const struct pppoe_tag *pt = (const void *)(ph + 1);
2150
2151         /*
2152          * Keep processing tags while a tag header will still fit.
2153          */
2154         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
2155
2156         while((const char*)(pt + 1) <= end) {
2157                 /*
2158                  * If the tag data would go past the end of the packet, abort.
2159                  */
2160                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
2161                 if(ptn > end)
2162                         return NULL;
2163
2164                 switch (pt->tag_type) {
2165                 case    PTT_RELAY_SID:
2166                         insert_tag(sp, pt);
2167                         break;
2168                 case    PTT_EOL:
2169                         return NULL;
2170                 case    PTT_SRV_NAME:
2171                 case    PTT_AC_NAME:
2172                 case    PTT_HOST_UNIQ:
2173                 case    PTT_AC_COOKIE:
2174                 case    PTT_VENDOR:
2175                 case    PTT_SRV_ERR:
2176                 case    PTT_SYS_ERR:
2177                 case    PTT_GEN_ERR:
2178                 case    PTT_MAX_PAYL:
2179                 case    PTT_HURL:
2180                 case    PTT_MOTM:
2181                         break;
2182                 }
2183                 pt = (const struct pppoe_tag*)ptn;
2184         }
2185         return NULL;
2186 }
2187
2188 static  int
2189 pppoe_send_event(sessp sp, enum cmd cmdid)
2190 {
2191         int error;
2192         struct ng_mesg *msg;
2193         struct ngpppoe_sts *sts;
2194
2195         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
2196
2197         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
2198                         sizeof(struct ngpppoe_sts), M_NOWAIT);
2199         if (msg == NULL)
2200                 return (ENOMEM);
2201         sts = (struct ngpppoe_sts *)msg->data;
2202         strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ);
2203         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
2204         return (error);
2205 }