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