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