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