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