]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_pppoe.c
amd64: use register macros for gdb_cpu_getreg()
[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                 uint16_t        session;
1466                 uint16_t        length;
1467                 uint8_t         code;
1468
1469                 /*
1470                  * A PADI packet is being returned by the application
1471                  * that has set up this hook. This indicates that it
1472                  * wants us to offer service.
1473                  */
1474                 if (m->m_len < sizeof(*wh)) {
1475                         m = m_pullup(m, sizeof(*wh));
1476                         if (m == NULL)
1477                                 LEAVE(ENOBUFS);
1478                 }
1479                 wh = mtod(m, struct pppoe_full_hdr *);
1480                 ph = &wh->ph;
1481                 session = ntohs(wh->ph.sid);
1482                 length = ntohs(wh->ph.length);
1483                 code = wh->ph.code;
1484                 /* Use peers mode in session. */
1485                 neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type;
1486                 if (code != PADI_CODE)
1487                         LEAVE(EINVAL);
1488                 ng_uncallout(&neg->handle, node);
1489
1490                 /*
1491                  * This is the first time we hear
1492                  * from the client, so note it's
1493                  * unicast address, replacing the
1494                  * broadcast address.
1495                  */
1496                 bcopy(wh->eh.ether_shost,
1497                         neg->pkt->pkt_header.eh.ether_dhost,
1498                         ETHER_ADDR_LEN);
1499                 sp->state = PPPOE_SOFFER;
1500                 neg->timeout = 0;
1501                 neg->pkt->pkt_header.ph.code = PADO_CODE;
1502
1503                 /*
1504                  * Start working out the tags to respond with.
1505                  */
1506                 uniqtag.hdr.tag_type = PTT_AC_COOKIE;
1507                 uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
1508                 uniqtag.data.pointer = sp;
1509                 init_tags(sp);
1510                 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1511                 if ((tag = get_tag(ph, PTT_SRV_NAME)))
1512                         insert_tag(sp, tag);      /* return service */
1513                 /*
1514                  * If we have a NULL service request
1515                  * and have an extra service defined in this hook,
1516                  * then also add a tag for the extra service.
1517                  * XXX this is a hack. eventually we should be able
1518                  * to support advertising many services, not just one
1519                  */
1520                 if (((tag == NULL) || (tag->tag_len == 0)) &&
1521                     (neg->service.hdr.tag_len != 0)) {
1522                         insert_tag(sp, &neg->service.hdr); /* SERVICE */
1523                 }
1524                 if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1525                         insert_tag(sp, tag); /* returned hostunique */
1526                 insert_tag(sp, &uniqtag.hdr);
1527                 scan_tags(sp, ph);
1528                 make_packet(sp);
1529                 /*
1530                  * Send the offer but if they don't respond
1531                  * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
1532                  */
1533                 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz,
1534                     pppoe_ticker, NULL, 0);
1535                 m0 = m_copypacket(sp->neg->m, M_NOWAIT);
1536                 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1537                 privp->packets_out++;
1538                 break;
1539                 }
1540
1541         /*
1542          * Packets coming from the hook make no sense
1543          * to sessions in the rest of states. Throw them away.
1544          */
1545         default:
1546                 LEAVE(ENETUNREACH);
1547         }
1548 quit:
1549         if (item)
1550                 NG_FREE_ITEM(item);
1551         NG_FREE_M(m);
1552         return (error);
1553 }
1554
1555 /*
1556  * Receive data from ether and do something with it.
1557  */
1558 static int
1559 ng_pppoe_rcvdata_ether(hook_p hook, item_p item)
1560 {
1561         node_p                  node = NG_HOOK_NODE(hook);
1562         const priv_p            privp = NG_NODE_PRIVATE(node);
1563         sessp                   sp;
1564         const struct pppoe_tag  *utag = NULL, *tag = NULL;
1565         const struct pppoe_tag  sntag = { PTT_SRV_NAME, 0 };
1566         const struct pppoe_full_hdr *wh;
1567         const struct pppoe_hdr  *ph;
1568         negp                    neg = NULL;
1569         struct mbuf             *m;
1570         hook_p                  sendhook;
1571         int                     error = 0;
1572         uint16_t                session;
1573         uint16_t                length;
1574         uint8_t                 code;
1575         struct  mbuf            *m0;
1576
1577         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1578             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1579
1580         NGI_GET_M(item, m);
1581         /*
1582          * Dig out various fields from the packet.
1583          * Use them to decide where to send it.
1584          */
1585         privp->packets_in++;
1586         if( m->m_len < sizeof(*wh)) {
1587                 m = m_pullup(m, sizeof(*wh)); /* Checks length */
1588                 if (m == NULL) {
1589                         log(LOG_NOTICE, "ng_pppoe[%x]: couldn't "
1590                             "m_pullup(wh)\n", node->nd_ID);
1591                         LEAVE(ENOBUFS);
1592                 }
1593         }
1594         wh = mtod(m, struct pppoe_full_hdr *);
1595         length = ntohs(wh->ph.length);
1596         switch(wh->eh.ether_type) {
1597         case    ETHERTYPE_PPPOE_3COM_DISC: /* fall through */
1598         case    ETHERTYPE_PPPOE_DISC:
1599                 /*
1600                  * We need to try to make sure that the tag area
1601                  * is contiguous, or we could wander off the end
1602                  * of a buffer and make a mess.
1603                  * (Linux wouldn't have this problem).
1604                  */
1605                 if (m->m_pkthdr.len <= MHLEN) {
1606                         if( m->m_len < m->m_pkthdr.len) {
1607                                 m = m_pullup(m, m->m_pkthdr.len);
1608                                 if (m == NULL) {
1609                                         log(LOG_NOTICE, "ng_pppoe[%x]: "
1610                                             "couldn't m_pullup(pkthdr)\n",
1611                                             node->nd_ID);
1612                                         LEAVE(ENOBUFS);
1613                                 }
1614                         }
1615                 }
1616                 if (m->m_len != m->m_pkthdr.len) {
1617                         /*
1618                          * It's not all in one piece.
1619                          * We need to do extra work.
1620                          * Put it into a cluster.
1621                          */
1622                         struct mbuf *n;
1623                         n = m_dup(m, M_NOWAIT);
1624                         m_freem(m);
1625                         m = n;
1626                         if (m) {
1627                                 /* just check we got a cluster */
1628                                 if (m->m_len != m->m_pkthdr.len) {
1629                                         m_freem(m);
1630                                         m = NULL;
1631                                 }
1632                         }
1633                         if (m == NULL) {
1634                                 log(LOG_NOTICE, "ng_pppoe[%x]: packet "
1635                                     "fragmented\n", node->nd_ID);
1636                                 LEAVE(EMSGSIZE);
1637                         }
1638                 }
1639                 wh = mtod(m, struct pppoe_full_hdr *);
1640                 length = ntohs(wh->ph.length);
1641                 ph = &wh->ph;
1642                 session = ntohs(wh->ph.sid);
1643                 code = wh->ph.code;
1644
1645                 switch(code) {
1646                 case    PADI_CODE:
1647                         /*
1648                          * We are a server:
1649                          * Look for a hook with the required service and send
1650                          * the ENTIRE packet up there. It should come back to
1651                          * a new hook in PRIMED state. Look there for further
1652                          * processing.
1653                          */
1654                         tag = get_tag(ph, PTT_SRV_NAME);
1655                         if (tag == NULL)
1656                                 tag = &sntag;
1657
1658                         /*
1659                          * First, try to match Service-Name against our 
1660                          * listening hooks. If no success and we are in D-Link
1661                          * compat mode and Service-Name is empty, then we 
1662                          * broadcast the PADI to all listening hooks.
1663                          */
1664                         sendhook = pppoe_match_svc(node, tag);
1665                         if (sendhook != NULL)
1666                                 NG_FWD_NEW_DATA(error, item, sendhook, m);
1667                         else if (privp->flags & COMPAT_DLINK &&
1668                                  ntohs(tag->tag_len) == 0)
1669                                 error = pppoe_broadcast_padi(node, m);
1670                         else
1671                                 error = ENETUNREACH;
1672                         break;
1673                 case    PADO_CODE:
1674                         /*
1675                          * We are a client:
1676                          * Use the host_uniq tag to find the hook this is in
1677                          * response to. Received #2, now send #3
1678                          * For now simply accept the first we receive.
1679                          */
1680                         utag = get_tag(ph, PTT_HOST_UNIQ);
1681                         if (utag == NULL) {
1682                                 log(LOG_NOTICE, "ng_pppoe[%x]: no host "
1683                                     "unique field\n", node->nd_ID);
1684                                 LEAVE(ENETUNREACH);
1685                         }
1686
1687                         sendhook = pppoe_finduniq(node, utag);
1688                         if (sendhook == NULL) {
1689                                 log(LOG_NOTICE, "ng_pppoe[%x]: no "
1690                                     "matching session\n", node->nd_ID);
1691                                 LEAVE(ENETUNREACH);
1692                         }
1693
1694                         /*
1695                          * Check the session is in the right state.
1696                          * It needs to be in PPPOE_SINIT.
1697                          */
1698                         sp = NG_HOOK_PRIVATE(sendhook);
1699                         if (sp->state == PPPOE_SREQ ||
1700                             sp->state == PPPOE_CONNECTED) {
1701                                 break;  /* Multiple PADO is OK. */
1702                         }
1703                         if (sp->state != PPPOE_SINIT) {
1704                                 log(LOG_NOTICE, "ng_pppoe[%x]: session "
1705                                     "in wrong state\n", node->nd_ID);
1706                                 LEAVE(ENETUNREACH);
1707                         }
1708                         neg = sp->neg;
1709                         /* If requested specific AC-name, check it. */
1710                         if (neg->ac_name_len) {
1711                                 tag = get_tag(ph, PTT_AC_NAME);
1712                                 if (!tag) {
1713                                         /* No PTT_AC_NAME in PADO */
1714                                         break;
1715                                 }
1716                                 if (neg->ac_name_len != htons(tag->tag_len) ||
1717                                     strncmp(neg->ac_name.data,
1718                                     (const char *)(tag + 1),
1719                                     neg->ac_name_len) != 0) {
1720                                         break;
1721                                 }
1722                         }
1723                         sp->state = PPPOE_SREQ;
1724                         ng_uncallout(&neg->handle, node);
1725
1726                         /*
1727                          * This is the first time we hear
1728                          * from the server, so note it's
1729                          * unicast address, replacing the
1730                          * broadcast address .
1731                          */
1732                         bcopy(wh->eh.ether_shost,
1733                                 neg->pkt->pkt_header.eh.ether_dhost,
1734                                 ETHER_ADDR_LEN);
1735                         neg->timeout = 0;
1736                         neg->pkt->pkt_header.ph.code = PADR_CODE;
1737                         init_tags(sp);
1738                         insert_tag(sp, utag);           /* Host Unique */
1739                         if ((tag = get_tag(ph, PTT_AC_COOKIE)))
1740                                 insert_tag(sp, tag);    /* return cookie */
1741                         if ((tag = get_tag(ph, PTT_AC_NAME))) { 
1742                                 insert_tag(sp, tag);    /* return it */
1743                                 send_acname(sp, tag);
1744                         }
1745                         if ((tag = get_tag(ph, PTT_MAX_PAYL)) &&
1746                             (privp->max_payload.data != 0))
1747                                 insert_tag(sp, tag);    /* return it */
1748                         insert_tag(sp, &neg->service.hdr); /* Service */
1749                         scan_tags(sp, ph);
1750                         make_packet(sp);
1751                         sp->state = PPPOE_SREQ;
1752                         ng_callout(&neg->handle, node, sp->hook,
1753                             PPPOE_INITIAL_TIMEOUT * hz,
1754                             pppoe_ticker, NULL, 0);
1755                         neg->timeout = PPPOE_INITIAL_TIMEOUT * 2;
1756                         m0 = m_copypacket(neg->m, M_NOWAIT);
1757                         NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1758                         break;
1759                 case    PADR_CODE:
1760                         /*
1761                          * We are a server:
1762                          * Use the ac_cookie tag to find the
1763                          * hook this is in response to.
1764                          */
1765                         utag = get_tag(ph, PTT_AC_COOKIE);
1766                         if ((utag == NULL) ||
1767                             (ntohs(utag->tag_len) != sizeof(sp))) {
1768                                 LEAVE(ENETUNREACH);
1769                         }
1770
1771                         sendhook = pppoe_findcookie(node, utag);
1772                         if (sendhook == NULL)
1773                                 LEAVE(ENETUNREACH);
1774
1775                         /*
1776                          * Check the session is in the right state.
1777                          * It needs to be in PPPOE_SOFFER or PPPOE_NEWCONNECTED.
1778                          * If the latter, then this is a retry by the client,
1779                          * so be nice, and resend.
1780                          */
1781                         sp = NG_HOOK_PRIVATE(sendhook);
1782                         if (sp->state == PPPOE_NEWCONNECTED) {
1783                                 /*
1784                                  * Whoa! drop back to resend that PADS packet.
1785                                  * We should still have a copy of it.
1786                                  */
1787                                 sp->state = PPPOE_SOFFER;
1788                         } else if (sp->state != PPPOE_SOFFER)
1789                                 LEAVE (ENETUNREACH);
1790                         neg = sp->neg;
1791                         ng_uncallout(&neg->handle, node);
1792                         neg->pkt->pkt_header.ph.code = PADS_CODE;
1793                         if (sp->Session_ID == 0) {
1794                                 neg->pkt->pkt_header.ph.sid =
1795                                     htons(pppoe_getnewsession(sp));
1796                         }
1797                         send_sessionid(sp);
1798                         neg->timeout = 0;
1799                         /*
1800                          * start working out the tags to respond with.
1801                          */
1802                         init_tags(sp);
1803                         insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1804                         if ((tag = get_tag(ph, PTT_SRV_NAME)))
1805                                 insert_tag(sp, tag);/* return service */
1806                         if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1807                                 insert_tag(sp, tag); /* return it */
1808                         insert_tag(sp, utag);   /* ac_cookie */
1809                         scan_tags(sp, ph);
1810                         make_packet(sp);
1811                         sp->state = PPPOE_NEWCONNECTED;
1812
1813                         /* Send the PADS without a timeout - we're now connected. */
1814                         m0 = m_copypacket(sp->neg->m, M_NOWAIT);
1815                         NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0);
1816
1817                         /*
1818                          * Having sent the last Negotiation header,
1819                          * Set up the stored packet header to be correct for
1820                          * the actual session. But keep the negotialtion stuff
1821                          * around in case we need to resend this last packet.
1822                          * We'll discard it when we move from NEWCONNECTED
1823                          * to CONNECTED
1824                          */
1825                         sp->pkt_hdr = neg->pkt->pkt_header;
1826                         /* Configure ethertype depending on what
1827                          * ethertype was used at discovery phase */
1828                         if (sp->pkt_hdr.eh.ether_type ==
1829                             ETHERTYPE_PPPOE_3COM_DISC)
1830                                 sp->pkt_hdr.eh.ether_type
1831                                         = ETHERTYPE_PPPOE_3COM_SESS;
1832                         else
1833                                 sp->pkt_hdr.eh.ether_type
1834                                         = ETHERTYPE_PPPOE_SESS;
1835                         sp->pkt_hdr.ph.code = 0;
1836                         pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1837                         break;
1838                 case    PADS_CODE:
1839                         /*
1840                          * We are a client:
1841                          * Use the host_uniq tag to find the hook this is in
1842                          * response to. Take the session ID and store it away.
1843                          * Also make sure the pre-made header is correct and
1844                          * set us into Session mode.
1845                          */
1846                         utag = get_tag(ph, PTT_HOST_UNIQ);
1847                         if (utag == NULL) {
1848                                 LEAVE (ENETUNREACH);
1849                         }
1850                         sendhook = pppoe_finduniq(node, utag);
1851                         if (sendhook == NULL)
1852                                 LEAVE(ENETUNREACH);
1853
1854                         /*
1855                          * Check the session is in the right state.
1856                          * It needs to be in PPPOE_SREQ.
1857                          */
1858                         sp = NG_HOOK_PRIVATE(sendhook);
1859                         if (sp->state != PPPOE_SREQ)
1860                                 LEAVE(ENETUNREACH);
1861                         neg = sp->neg;
1862                         ng_uncallout(&neg->handle, node);
1863                         neg->pkt->pkt_header.ph.sid = wh->ph.sid;
1864                         sp->Session_ID = ntohs(wh->ph.sid);
1865                         pppoe_addsession(sp);
1866                         send_sessionid(sp);
1867                         neg->timeout = 0;
1868                         sp->state = PPPOE_CONNECTED;
1869                         /*
1870                          * Now we have gone to Connected mode,
1871                          * Free all resources needed for negotiation.
1872                          * Keep a copy of the header we will be using.
1873                          */
1874                         sp->pkt_hdr = neg->pkt->pkt_header;
1875                         if (privp->flags & COMPAT_3COM)
1876                                 sp->pkt_hdr.eh.ether_type
1877                                         = ETHERTYPE_PPPOE_3COM_SESS;
1878                         else
1879                                 sp->pkt_hdr.eh.ether_type
1880                                         = ETHERTYPE_PPPOE_SESS;
1881                         sp->pkt_hdr.ph.code = 0;
1882                         m_freem(neg->m);
1883                         free(sp->neg, M_NETGRAPH_PPPOE);
1884                         sp->neg = NULL;
1885                         if ((tag = get_tag(ph, PTT_MAX_PAYL)) &&
1886                             (privp->max_payload.data != 0))
1887                                 send_maxp(sp, tag);
1888                         pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1889                         break;
1890                 case    PADT_CODE:
1891                         /*
1892                          * Find matching peer/session combination.
1893                          */
1894                         sp = pppoe_findsession(privp, wh);
1895                         if (sp == NULL)
1896                                 LEAVE(ENETUNREACH);
1897                         /* Disconnect that hook. */
1898                         ng_rmhook_self(sp->hook);
1899                         break;
1900                 case    PADM_CODE:
1901                         /*
1902                          * We are a client:
1903                          * find matching peer/session combination.
1904                          */
1905                         sp = pppoe_findsession(privp, wh);
1906                         if (sp == NULL)
1907                                 LEAVE (ENETUNREACH);
1908                         if ((tag = get_tag(ph, PTT_HURL)))
1909                                 send_hurl(sp, tag);
1910                         if ((tag = get_tag(ph, PTT_MOTM)))
1911                                 send_motm(sp, tag);
1912                         break;
1913                 default:
1914                         LEAVE(EPFNOSUPPORT);
1915                 }
1916                 break;
1917         case    ETHERTYPE_PPPOE_3COM_SESS:
1918         case    ETHERTYPE_PPPOE_SESS:
1919                 /*
1920                  * Find matching peer/session combination.
1921                  */
1922                 sp = pppoe_findsession(privp, wh);
1923                 if (sp == NULL)
1924                         LEAVE (ENETUNREACH);
1925                 m_adj(m, sizeof(*wh));
1926
1927                 /* If packet too short, dump it. */
1928                 if (m->m_pkthdr.len < length)
1929                         LEAVE(EMSGSIZE);
1930                 /* Also need to trim excess at the end */
1931                 if (m->m_pkthdr.len > length) {
1932                         m_adj(m, -((int)(m->m_pkthdr.len - length)));
1933                 }
1934                 if ( sp->state != PPPOE_CONNECTED) {
1935                         if (sp->state == PPPOE_NEWCONNECTED) {
1936                                 sp->state = PPPOE_CONNECTED;
1937                                 /*
1938                                  * Now we have gone to Connected mode,
1939                                  * Free all resources needed for negotiation.
1940                                  * Be paranoid about whether there may be
1941                                  * a timeout.
1942                                  */
1943                                 m_freem(sp->neg->m);
1944                                 ng_uncallout(&sp->neg->handle, node);
1945                                 free(sp->neg, M_NETGRAPH_PPPOE);
1946                                 sp->neg = NULL;
1947                         } else {
1948                                 LEAVE (ENETUNREACH);
1949                         }
1950                 }
1951                 NG_FWD_NEW_DATA(error, item, sp->hook, m);
1952                 break;
1953         default:
1954                 LEAVE(EPFNOSUPPORT);
1955         }
1956 quit:
1957         if (item)
1958                 NG_FREE_ITEM(item);
1959         NG_FREE_M(m);
1960         return (error);
1961 }
1962
1963 /*
1964  * Receive data from debug hook and bypass it to ether.
1965  */
1966 static int
1967 ng_pppoe_rcvdata_debug(hook_p hook, item_p item)
1968 {
1969         node_p          node = NG_HOOK_NODE(hook);
1970         const priv_p    privp = NG_NODE_PRIVATE(node);
1971         int             error;
1972
1973         CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)",
1974             __func__, node->nd_ID, node, item, hook->hk_name, hook);
1975
1976         NG_FWD_ITEM_HOOK(error, item, privp->ethernet_hook);
1977         privp->packets_out++;
1978         return (error);
1979 }
1980
1981 /*
1982  * Do local shutdown processing..
1983  * If we are a persistent device, we might refuse to go away, and
1984  * we'd only remove our links and reset ourself.
1985  */
1986 static int
1987 ng_pppoe_shutdown(node_p node)
1988 {
1989         const priv_p privp = NG_NODE_PRIVATE(node);
1990         int     i;
1991
1992         for (i = 0; i < SESSHASHSIZE; i++)
1993             mtx_destroy(&privp->sesshash[i].mtx);
1994         NG_NODE_SET_PRIVATE(node, NULL);
1995         NG_NODE_UNREF(privp->node);
1996         free(privp, M_NETGRAPH_PPPOE);
1997         return (0);
1998 }
1999
2000 /*
2001  * Hook disconnection
2002  *
2003  * Clean up all dangling links and information about the session/hook.
2004  * For this type, removal of the last link destroys the node.
2005  */
2006 static int
2007 ng_pppoe_disconnect(hook_p hook)
2008 {
2009         node_p node = NG_HOOK_NODE(hook);
2010         priv_p privp = NG_NODE_PRIVATE(node);
2011         sessp   sp;
2012
2013         if (hook == privp->debug_hook) {
2014                 privp->debug_hook = NULL;
2015         } else if (hook == privp->ethernet_hook) {
2016                 privp->ethernet_hook = NULL;
2017                 if (NG_NODE_IS_VALID(node))
2018                         ng_rmnode_self(node);
2019         } else {
2020                 sp = NG_HOOK_PRIVATE(hook);
2021                 if (sp->state != PPPOE_SNONE ) {
2022                         pppoe_send_event(sp, NGM_PPPOE_CLOSE);
2023                 }
2024                 /*
2025                  * According to the spec, if we are connected,
2026                  * we should send a DISC packet if we are shutting down
2027                  * a session.
2028                  */
2029                 if ((privp->ethernet_hook)
2030                 && ((sp->state == PPPOE_CONNECTED)
2031                  || (sp->state == PPPOE_NEWCONNECTED))) {
2032                         struct mbuf *m;
2033
2034                         /* Generate a packet of that type. */
2035                         m = m_gethdr(M_NOWAIT, MT_DATA);
2036                         if (m == NULL)
2037                                 log(LOG_NOTICE, "ng_pppoe[%x]: session out of "
2038                                     "mbufs\n", node->nd_ID);
2039                         else {
2040                                 struct pppoe_full_hdr *wh;
2041                                 struct pppoe_tag *tag;
2042                                 int     msglen = strlen(SIGNOFF);
2043                                 int     error = 0;
2044
2045                                 wh = mtod(m, struct pppoe_full_hdr *);
2046                                 bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
2047
2048                                 /* Revert the stored header to DISC/PADT mode. */
2049                                 wh->ph.code = PADT_CODE;
2050                                 /*
2051                                  * Configure ethertype depending on what
2052                                  * was used during sessions stage.
2053                                  */
2054                                 if (wh->eh.ether_type == 
2055                                     ETHERTYPE_PPPOE_3COM_SESS)
2056                                         wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC;
2057                                 else
2058                                         wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
2059                                 /*
2060                                  * Add a General error message and adjust
2061                                  * sizes.
2062                                  */
2063                                 tag = (void *)(&wh->ph + 1);
2064                                 tag->tag_type = PTT_GEN_ERR;
2065                                 tag->tag_len = htons((u_int16_t)msglen);
2066                                 strncpy((char *)(tag + 1), SIGNOFF, msglen);
2067                                 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) +
2068                                     msglen;
2069                                 wh->ph.length = htons(sizeof(*tag) + msglen);
2070                                 NG_SEND_DATA_ONLY(error,
2071                                         privp->ethernet_hook, m);
2072                         }
2073                 }
2074                 if (sp->state == PPPOE_LISTENING)
2075                         LIST_REMOVE(sp, sessions);
2076                 else if (sp->Session_ID)
2077                         pppoe_delsession(sp);
2078                 /*
2079                  * As long as we have somewhere to store the timeout handle,
2080                  * we may have a timeout pending.. get rid of it.
2081                  */
2082                 if (sp->neg) {
2083                         ng_uncallout(&sp->neg->handle, node);
2084                         if (sp->neg->m)
2085                                 m_freem(sp->neg->m);
2086                         free(sp->neg, M_NETGRAPH_PPPOE);
2087                 }
2088                 free(sp, M_NETGRAPH_PPPOE);
2089                 NG_HOOK_SET_PRIVATE(hook, NULL);
2090         }
2091         if ((NG_NODE_NUMHOOKS(node) == 0) &&
2092             (NG_NODE_IS_VALID(node)))
2093                 ng_rmnode_self(node);
2094         return (0);
2095 }
2096
2097 /*
2098  * Timeouts come here.
2099  */
2100 static void
2101 pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2)
2102 {
2103         priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
2104         sessp   sp = NG_HOOK_PRIVATE(hook);
2105         negp    neg = sp->neg;
2106         struct mbuf *m0 = NULL;
2107         int     error = 0;
2108
2109         CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d",
2110             __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID);
2111         switch(sp->state) {
2112                 /*
2113                  * Resend the last packet, using an exponential backoff.
2114                  * After a period of time, stop growing the backoff,
2115                  * And either leave it, or revert to the start.
2116                  */
2117         case    PPPOE_SINIT:
2118         case    PPPOE_SREQ:
2119                 /* Timeouts on these produce resends. */
2120                 m0 = m_copypacket(sp->neg->m, M_NOWAIT);
2121                 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0);
2122                 ng_callout(&neg->handle, node, hook, neg->timeout * hz,
2123                     pppoe_ticker, NULL, 0);
2124                 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
2125                         if (sp->state == PPPOE_SREQ) {
2126                                 /* Revert to SINIT mode. */
2127                                 pppoe_start(sp);
2128                         } else {
2129                                 neg->timeout = PPPOE_TIMEOUT_LIMIT;
2130                         }
2131                 }
2132                 break;
2133         case    PPPOE_PRIMED:
2134         case    PPPOE_SOFFER:
2135                 /* A timeout on these says "give up" */
2136                 ng_rmhook_self(hook);
2137                 break;
2138         default:
2139                 /* Timeouts have no meaning in other states. */
2140                 log(LOG_NOTICE, "ng_pppoe[%x]: unexpected timeout\n",
2141                     node->nd_ID);
2142         }
2143 }
2144
2145 /*
2146  * Parse an incoming packet to see if any tags should be copied to the
2147  * output packet. Don't do any tags that have been handled in the main
2148  * state machine.
2149  */
2150 static const struct pppoe_tag*
2151 scan_tags(sessp sp, const struct pppoe_hdr* ph)
2152 {
2153         const char *const end = (const char *)next_tag(ph);
2154         const char *ptn;
2155         const struct pppoe_tag *pt = (const void *)(ph + 1);
2156
2157         /*
2158          * Keep processing tags while a tag header will still fit.
2159          */
2160         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
2161
2162         while((const char*)(pt + 1) <= end) {
2163                 /*
2164                  * If the tag data would go past the end of the packet, abort.
2165                  */
2166                 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len));
2167                 if(ptn > end)
2168                         return NULL;
2169
2170                 switch (pt->tag_type) {
2171                 case    PTT_RELAY_SID:
2172                         insert_tag(sp, pt);
2173                         break;
2174                 case    PTT_EOL:
2175                         return NULL;
2176                 case    PTT_SRV_NAME:
2177                 case    PTT_AC_NAME:
2178                 case    PTT_HOST_UNIQ:
2179                 case    PTT_AC_COOKIE:
2180                 case    PTT_VENDOR:
2181                 case    PTT_SRV_ERR:
2182                 case    PTT_SYS_ERR:
2183                 case    PTT_GEN_ERR:
2184                 case    PTT_MAX_PAYL:
2185                 case    PTT_HURL:
2186                 case    PTT_MOTM:
2187                         break;
2188                 }
2189                 pt = (const struct pppoe_tag*)ptn;
2190         }
2191         return NULL;
2192 }
2193
2194 static  int
2195 pppoe_send_event(sessp sp, enum cmd cmdid)
2196 {
2197         int error;
2198         struct ng_mesg *msg;
2199         struct ngpppoe_sts *sts;
2200
2201         CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID);
2202
2203         NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
2204                         sizeof(struct ngpppoe_sts), M_NOWAIT);
2205         if (msg == NULL)
2206                 return (ENOMEM);
2207         sts = (struct ngpppoe_sts *)msg->data;
2208         strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ);
2209         NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0);
2210         return (error);
2211 }