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