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