xref: /freebsd/sys/netgraph/ng_pppoe.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
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@whistle.com>
38  *
39  * $FreeBSD$
40  * $Whistle: ng_pppoe.c,v 1.7 1999/10/16 10:16:43 julian Exp $
41  */
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/mbuf.h>
47 #include <sys/malloc.h>
48 #include <sys/errno.h>
49 #include <sys/syslog.h>
50 #include <net/ethernet.h>
51 
52 #include <netgraph/ng_message.h>
53 #include <netgraph/netgraph.h>
54 #include <netgraph/ng_pppoe.h>
55 
56 /*
57  * This section contains the netgraph method declarations for the
58  * sample node. These methods define the netgraph 'type'.
59  */
60 
61 static int	ng_PPPoE_constructor(node_p *node);
62 static int	ng_PPPoE_rcvmsg(node_p node, struct ng_mesg *msg,
63 		  const char *retaddr, struct ng_mesg **resp);
64 static int	ng_PPPoE_rmnode(node_p node);
65 static int	ng_PPPoE_newhook(node_p node, hook_p hook, const char *name);
66 static int	ng_PPPoE_connect(hook_p hook);
67 static int	ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
68 static int	ng_PPPoE_disconnect(hook_p hook);
69 
70 /* Netgraph node type descriptor */
71 static struct ng_type typestruct = {
72 	NG_VERSION,
73 	NG_PPPOE_NODE_TYPE,
74 	NULL,
75 	ng_PPPoE_constructor,
76 	ng_PPPoE_rcvmsg,
77 	ng_PPPoE_rmnode,
78 	ng_PPPoE_newhook,
79 	NULL,
80 	ng_PPPoE_connect,
81 	ng_PPPoE_rcvdata,
82 	ng_PPPoE_rcvdata,
83 	ng_PPPoE_disconnect
84 };
85 NETGRAPH_INIT(PPPoE, &typestruct);
86 
87 /*
88  * States for the session state machine.
89  * These have no meaning if there is no hook attached yet.
90  */
91 enum state {
92     PPPOE_SNONE=0,	/* [both] Initial state */
93     PPPOE_SINIT,	/* [Client] Sent discovery initiation */
94     PPPOE_PRIMED,	/* [Server] Sent offer message */
95     PPPOE_SOFFER,	/* [Server] Sent offer message */
96     PPPOE_SREQ,		/* [Client] Sent a Request */
97     PPPOE_LISTENING,	/* [Server] Listening for discover initiation msg */
98     PPPOE_NEWCONNECTED,	/* [Both] Connection established, No data received */
99     PPPOE_CONNECTED,	/* [Both] Connection established, Data received */
100     PPPOE_DEAD		/* [Both] */
101 };
102 /*
103  * Events for the state machine
104  */
105 enum event {
106     PPPOE_TIMEOUT,	/* It's time to do something */
107     PPPOE_PACKET,	/* a packet has been received. */
108     PPPOE_CLOSE		/* start shutdown processing */
109 };
110 
111 #define NUMTAGS 20 /* number of tags we are set up to work with */
112 
113 /*
114  * Information we store for each hook on each node for negotiating the
115  * session. The mbuf and cluster are freed once negotiation has completed.
116  * The whole negotiation block is then discarded.
117  */
118 
119 struct sess_neg {
120 	struct mbuf 		*m; /* holds cluster with last sent packet */
121 	union	packet		*pkt; /* points within the above cluster */
122 	struct callout_handle	timeout_handle;   /* see timeout(9) */
123 	u_int			timeout; /* 0,1,2,4,8,16 etc. seconds */
124 	u_int			numtags;
125 	struct pppoe_tag	*tags[NUMTAGS];
126 	u_int			service_len;
127 	u_int			ac_name_len;
128 
129 	struct datatag		service;
130 	struct datatag		ac_name;
131 };
132 typedef struct sess_neg *negp;
133 
134 /*
135  * Session information that is needed after connection.
136  */
137 struct session {
138 	hook_p  		hook;
139 	u_int16_t		Session_ID;
140 	struct session		*hash_next; /* not yet uesed */
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 };
146 typedef struct session *sessp;
147 
148 /*
149  * Information we store for each node
150  */
151 struct PPPOE {
152 	node_p		node;		/* back pointer to node */
153 	hook_p  	ethernet_hook;
154 	hook_p  	debug_hook;
155 	u_int   	packets_in;	/* packets in from ethernet */
156 	u_int   	packets_out;	/* packets out towards ethernet */
157 	u_int32_t	flags;
158 	/*struct session *buckets[HASH_SIZE];*/	/* not yet used */
159 };
160 typedef struct PPPOE *priv_p;
161 
162 const struct ether_header eh_prototype =
163 	{{0xff,0xff,0xff,0xff,0xff,0xff},
164 	 {0x00,0x00,0x00,0x00,0x00,0x00},
165 	 ETHERTYPE_PPPOE_DISC};
166 
167 union uniq {
168 	char bytes[sizeof(void *)];
169 	void * pointer;
170 	};
171 
172 #define	LEAVE(x) do { error = x; goto quit; } while(0)
173 static void	pppoe_start(sessp sp);
174 static void	sendpacket(sessp sp);
175 static void	pppoe_ticker(void *arg);
176 static struct pppoe_tag* scan_tags(sessp	sp, struct pppoe_hdr* ph);
177 
178 /*************************************************************************
179  * Some basic utilities  from the Linux version with author's permission.*
180  * Author:	Michal Ostrowski <mostrows@styx.uwaterloo.ca>		 *
181  ************************************************************************/
182 
183 /*
184  * Generate a new session id
185  * XXX find out the freeBSD locking scheme.
186  */
187 static u_int16_t
188 get_new_sid(node_p node)
189 {
190 	static int pppoe_sid = 10;
191 	sessp sp;
192 	hook_p	hook;
193 	u_int16_t val;
194 	priv_p privp = node->private;
195 
196 restart:
197 	val = pppoe_sid++;
198 	/*
199 	 * Spec says 0xFFFF is reserved.
200 	 * Also don't use 0x0000
201 	 */
202 	if (val == 0xffff) {
203 		pppoe_sid = 20;
204 		goto restart;
205 	}
206 
207 	/* Check it isn't already in use */
208 	LIST_FOREACH(hook, &node->hooks, hooks) {
209 		/* don't check special hooks */
210 		if ((hook->private == &privp->debug_hook)
211 		||  (hook->private == &privp->ethernet_hook))
212 			continue;
213 		sp = hook->private;
214 		if (sp->Session_ID == val)
215 			goto restart;
216 	}
217 
218 	return val;
219 }
220 
221 
222 /*
223  * Return the location where the next tag can be put
224  */
225 static __inline struct pppoe_tag*
226 next_tag(struct pppoe_hdr* ph)
227 {
228 	return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length));
229 }
230 
231 /*
232  * Look for a tag of a specific type
233  * Don't trust any length the other end says.
234  * but assume we already sanity checked ph->length.
235  */
236 static struct pppoe_tag*
237 get_tag(struct pppoe_hdr* ph, u_int16_t idx)
238 {
239 	char *end = (char *)next_tag(ph);
240 	char *ptn;
241 	struct pppoe_tag *pt = &ph->tag[0];
242 	/*
243 	 * Keep processing tags while a tag header will still fit.
244 	 */
245 	while((char*)(pt + 1) <= end) {
246 	    /*
247 	     * If the tag data would go past the end of the packet, abort.
248 	     */
249 	    ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
250 	    if(ptn > end)
251 		return NULL;
252 
253 	    if(pt->tag_type == idx)
254 		return pt;
255 
256 	    pt = (struct pppoe_tag*)ptn;
257 	}
258 	return NULL;
259 }
260 
261 /**************************************************************************
262  * inlines to initialise or add tags to a session's tag list,
263  **************************************************************************/
264 /*
265  * Initialise the session's tag list
266  */
267 static void
268 init_tags(sessp sp)
269 {
270 	if(sp->neg == NULL) {
271 		printf("pppoe: asked to init NULL neg pointer\n");
272 		return;
273 	}
274 	sp->neg->numtags = 0;
275 }
276 
277 static void
278 insert_tag(sessp sp, struct pppoe_tag *tp)
279 {
280 	int	i;
281 	negp neg;
282 
283 	if((neg = sp->neg) == NULL) {
284 		printf("pppoe: asked to use NULL neg pointer\n");
285 		return;
286 	}
287 	if ((i = neg->numtags++) < NUMTAGS) {
288 		neg->tags[i] = tp;
289 	} else {
290 		printf("pppoe: asked to add too many tags to packet\n");
291 	}
292 }
293 
294 /*
295  * Make up a packet, using the tags filled out for the session.
296  *
297  * Assume that the actual pppoe header and ethernet header
298  * are filled out externally to this routine.
299  * Also assume that neg->wh points to the correct
300  * location at the front of the buffer space.
301  */
302 static void
303 make_packet(sessp sp) {
304 	struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header;
305 	struct pppoe_tag **tag;
306 	char *dp;
307 	int count;
308 	int tlen;
309 	u_int16_t length = 0;
310 
311 	if ((sp->neg == NULL) || (sp->neg->m = NULL)) {
312 		printf("pppoe: make_packet called from wrong state\n");
313 	}
314 	dp = (char *)wh->ph.tag;
315 	for (count = 0, tag = sp->neg->tags;
316 	    ((count < sp->neg->numtags) && (count < NUMTAGS));
317 	    tag++, count++) {
318 		tlen = ntohs((*tag)->tag_len) + sizeof(**tag);
319 		if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) {
320 			printf("pppoe: tags too long\n");
321 			sp->neg->numtags = count;
322 			break;	/* XXX chop off what's too long */
323 		}
324 		bcopy((char *)*tag, (char *)dp, tlen);
325 		length += tlen;
326 		dp += tlen;
327 	}
328  	wh->ph.length = htons(length);
329 	sp->neg->m->m_len = length + sizeof(*wh);
330 	sp->neg->m->m_pkthdr.len = length + sizeof(*wh);
331 }
332 
333 /**************************************************************************
334  * Routine to match a service offered					  *
335  **************************************************************************/
336 /*
337  * Find a hook that has a service string that matches that
338  * we are seeking. for now use a simple string.
339  * In the future we may need something like regexp().
340  * for testing allow a null string to match 1st found and a null service
341  * to match all requests. Also make '*' do the same.
342  */
343 static hook_p
344 pppoe_match_svc(node_p node, char *svc_name, int svc_len)
345 {
346 	sessp	sp	= NULL;
347 	negp	neg	= NULL;
348 	priv_p	privp	= node->private;
349 	hook_p hook;
350 
351 	LIST_FOREACH(hook, &node->hooks, hooks) {
352 
353 		/* skip any hook that is debug or ethernet */
354 		if ((hook->private == &privp->debug_hook)
355 		||  (hook->private == &privp->ethernet_hook))
356 			continue;
357 		sp = hook->private;
358 
359 		/* Skip any sessions which are not in LISTEN mode. */
360 		if ( sp->state != PPPOE_LISTENING)
361 			continue;
362 
363 		neg = sp->neg;
364 		/* XXX check validity of this */
365 		/* special case, NULL request. match 1st found. */
366 		if (svc_len == 0)
367 			break;
368 
369 		/* XXX check validity of this */
370 		/* Special case for a blank or "*" service name (wildcard) */
371 		if ((neg->service_len == 0)
372 		||  ((neg->service_len == 1)
373 		  && (neg->service.data[0] == '*'))) {
374 			break;
375 		}
376 
377 		/* If the lengths don't match, that aint it. */
378 		if (neg->service_len != svc_len)
379 			continue;
380 
381 		/* An exact match? */
382 		if (strncmp(svc_name, neg->service.data, svc_len) == 0)
383 			break;
384 	}
385 	return (hook);
386 }
387 /**************************************************************************
388  * Routine to find a particular session that matches an incoming packet	  *
389  **************************************************************************/
390 static hook_p
391 pppoe_findsession(node_p node, struct pppoe_full_hdr *wh)
392 {
393 	sessp	sp = NULL;
394 	hook_p hook = NULL;
395 	priv_p	privp = node->private;
396 	u_int16_t	session = wh->ph.sid;
397 
398 	/*
399 	 * find matching peer/session combination.
400 	 */
401 	LIST_FOREACH(hook, &node->hooks, hooks) {
402 		/* don't check special hooks */
403 		if ((hook->private == &privp->debug_hook)
404 		||  (hook->private == &privp->ethernet_hook)) {
405 			continue;
406 		}
407 		sp = hook->private;
408 		if ( ( (sp->state == PPPOE_CONNECTED)
409 		    || (sp->state == PPPOE_NEWCONNECTED) )
410 		&& (sp->Session_ID == session)
411 		&& (bcmp(sp->pkt_hdr.eh.ether_dhost,
412 		    wh->eh.ether_shost,
413 		    ETHER_ADDR_LEN)) == 0) {
414 			break;
415 		}
416 	}
417 	return (hook);
418 }
419 
420 static hook_p
421 pppoe_finduniq(node_p node, struct pppoe_tag *tag)
422 {
423 	hook_p hook = NULL;
424 	priv_p	privp = node->private;
425 	union uniq		uniq;
426 
427 	bcopy(tag->tag_data, uniq.bytes, sizeof(void *));
428 	/* cycle through all known hooks */
429 	LIST_FOREACH(hook, &node->hooks, hooks) {
430 		/* don't check special hooks */
431 		if ((hook->private == &privp->debug_hook)
432 		||  (hook->private == &privp->ethernet_hook))
433 			continue;
434 		if (uniq.pointer == hook->private)
435 			break;
436 	}
437 	return (hook);
438 }
439 
440 /**************************************************************************
441  * start of Netgraph entrypoints					  *
442  **************************************************************************/
443 
444 /*
445  * Allocate the private data structure and the generic node
446  * and link them together.
447  *
448  * ng_make_node_common() returns with a generic node struct
449  * with a single reference for us.. we transfer it to the
450  * private structure.. when we free the private struct we must
451  * unref the node so it gets freed too.
452  *
453  * If this were a device node than this work would be done in the attach()
454  * routine and the constructor would return EINVAL as you should not be able
455  * to creatednodes that depend on hardware (unless you can add the hardware :)
456  */
457 static int
458 ng_PPPoE_constructor(node_p *nodep)
459 {
460 	priv_p privdata;
461 	int error;
462 
463 	/* Initialize private descriptor */
464 	MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK);
465 	if (privdata == NULL)
466 		return (ENOMEM);
467 	bzero(privdata, sizeof(*privdata));
468 
469 	/* Call the 'generic' (ie, superclass) node constructor */
470 	if ((error = ng_make_node_common(&typestruct, nodep))) {
471 		FREE(privdata, M_NETGRAPH);
472 		return (error);
473 	}
474 
475 	/* Link structs together; this counts as our one reference to *nodep */
476 	(*nodep)->private = privdata;
477 	privdata->node = *nodep;
478 	return (0);
479 }
480 
481 /*
482  * Give our ok for a hook to be added...
483  * point the hook's private info to the hook structure.
484  *
485  * The following hook names are special:
486  *  Ethernet:  the hook that should be connected to a NIC.
487  *  debug:	copies of data sent out here  (when I write the code).
488  */
489 static int
490 ng_PPPoE_newhook(node_p node, hook_p hook, const char *name)
491 {
492 	const priv_p privp = node->private;
493 	sessp sp;
494 
495 	if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) {
496 		privp->ethernet_hook = hook;
497 		hook->private = &privp->ethernet_hook;
498 	} else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) {
499 		privp->debug_hook = hook;
500 		hook->private = &privp->debug_hook;
501 	} else {
502 		/*
503 		 * Any other unique name is OK.
504 		 * The infrastructure has already checked that it's unique,
505 		 * so just allocate it and hook it in.
506 		 */
507 		MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_WAITOK);
508 		if (sp == NULL) {
509 				return (ENOMEM);
510 		}
511 		bzero(sp, sizeof(*sp));
512 
513 		hook->private = sp;
514 		sp->hook = hook;
515 		callout_handle_init( &sp->neg->timeout_handle);
516 	}
517 	return(0);
518 }
519 
520 /*
521  * Get a netgraph control message.
522  * Check it is one we understand. If needed, send a response.
523  * We sometimes save the address for an async action later.
524  * Always free the message.
525  */
526 static int
527 ng_PPPoE_rcvmsg(node_p node,
528 	   struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
529 {
530 	priv_p privp = node->private;
531 	struct ngPPPoE_init_data *ourmsg = NULL;
532 	struct ng_mesg *resp = NULL;
533 	int error = 0;
534 	hook_p hook = NULL;
535 	sessp sp = NULL;
536 	negp neg = NULL;
537 
538 	/* Deal with message according to cookie and command */
539 	switch (msg->header.typecookie) {
540 	case NGM_PPPOE_COOKIE:
541 		switch (msg->header.cmd) {
542 		case NGM_PPPOE_CONNECT:
543 		case NGM_PPPOE_LISTEN:
544 		case NGM_PPPOE_OFFER:
545 			ourmsg = (struct ngPPPoE_init_data *)msg->data;
546 			if (( sizeof(*ourmsg) > msg->header.arglen)
547 			|| ((sizeof(*ourmsg) + ourmsg->data_len)
548 			    > msg->header.arglen)) {
549 				printf("PPPoE_rcvmsg: bad arg size");
550 				LEAVE(EMSGSIZE);
551 			}
552 			if (ourmsg->data_len > PPPOE_SERVICE_NAME_SIZE) {
553 				printf("pppoe: init data too long (%d)\n",
554 							ourmsg->data_len);
555 				LEAVE(EMSGSIZE);
556 			}
557 			/* make sure strcmp will terminate safely */
558 			ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0';
559 
560 			/* cycle through all known hooks */
561 			LIST_FOREACH(hook, &node->hooks, hooks) {
562 				if (hook->name
563 				&& strcmp(hook->name, ourmsg->hook) == 0)
564 					break;
565 			}
566 			if (hook == NULL) {
567 				LEAVE(ENOENT);
568 			}
569 			if ((hook->private == &privp->debug_hook)
570 			||  (hook->private == &privp->ethernet_hook)) {
571 				LEAVE(EINVAL);
572 			}
573 			sp = hook->private;
574 			if (sp->state |= PPPOE_SNONE) {
575 				printf("pppoe: Session already active\n");
576 				LEAVE(EISCONN);
577 			}
578 			/*
579 			 * set up prototype header
580 			 */
581 
582 			MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, M_WAITOK);
583 
584 			if (neg == NULL) {
585 				printf("pppoe: Session out of memory\n");
586 				LEAVE(ENOMEM);
587 			}
588 			bzero(neg, sizeof(*neg));
589 			MGETHDR(neg->m, M_DONTWAIT, MT_DATA);
590 			if(neg->m == NULL) {
591 				FREE(neg, M_NETGRAPH);
592 				LEAVE(ENOBUFS);
593 			}
594 			neg->m->m_pkthdr.rcvif = NULL;
595 			MCLGET(neg->m, M_DONTWAIT);
596 			if ((neg->m->m_flags & M_EXT) == 0) {
597 				m_freem(neg->m);
598 				FREE(neg, M_NETGRAPH);
599 				LEAVE(ENOBUFS);
600 			}
601 			sp->neg = neg;
602 			neg->m->m_len = sizeof(struct pppoe_full_hdr);
603 			neg->pkt = mtod(neg->m, union packet*);
604 			neg->pkt->pkt_header.eh = eh_prototype;
605 			neg->pkt->pkt_header.ph.ver = 0x1;
606 			neg->pkt->pkt_header.ph.type = 0x1;
607 			neg->pkt->pkt_header.ph.sid = 0x0000;
608 			neg->timeout = 0;
609 
610 			strncpy(sp->creator, retaddr, NG_NODELEN);
611 			sp->creator[NG_NODELEN] = '\0';
612 		}
613 		switch (msg->header.cmd) {
614 		case NGM_PPPOE_GET_STATUS:
615 		    {
616 			struct ngPPPoEstat *stats;
617 
618 			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
619 			if (!resp) {
620 				LEAVE(ENOMEM);
621 			}
622 			stats = (struct ngPPPoEstat *) resp->data;
623 			stats->packets_in = privp->packets_in;
624 			stats->packets_out = privp->packets_out;
625 			break;
626 		    }
627 		case NGM_PPPOE_CONNECT:
628 			/*
629 			 * Check the hook exists and is Uninitialised.
630 			 * Send a PADI request, and start the timeout logic.
631 			 * Store the originator of this message so we can send
632 			 * a success of fail message to them later.
633 			 * Move the session to SINIT
634 			 * Set up the session to the correct state and
635 			 * start it.
636 			 */
637 			neg->service.hdr.tag_type = PTT_SRV_NAME;
638 			neg->service.hdr.tag_len =
639 					htons((u_int16_t)ourmsg->data_len);
640 			bcopy(ourmsg->data,
641 				neg->service.data, ourmsg->data_len);
642 			neg->service_len = ourmsg->data_len;
643 			neg->pkt->pkt_header.ph.code = PADI_CODE;
644 			pppoe_start(sp);
645 			break;
646 		case NGM_PPPOE_LISTEN:
647 			/*
648 			 * Check the hook exists and is Uninitialised.
649 			 * Install the service matching string.
650 			 * Store the originator of this message so we can send
651 			 * a success of fail message to them later.
652 			 * Move the hook to 'LISTENING'
653 			 */
654 			neg->service.hdr.tag_type = PTT_SRV_NAME;
655 			neg->service.hdr.tag_len =
656 					htons((u_int16_t)ourmsg->data_len);
657 			bcopy(ourmsg->data,
658 				neg->service.data, ourmsg->data_len);
659 			neg->service_len = ourmsg->data_len;
660 			neg->pkt->pkt_header.ph.code = PADT_CODE;
661 			/*
662 			 * wait for PADI packet coming from ethernet
663 			 */
664 			sp->state = PPPOE_LISTENING;
665 			break;
666 		case NGM_PPPOE_OFFER:
667 			/*
668 			 * Check the hook exists and is Uninitialised.
669 			 * Store the originator of this message so we can send
670 			 * a success of fail message to them later.
671 			 * Store the AC-Name given and go to PRIMED.
672 			 */
673 			neg->ac_name.hdr.tag_type = PTT_AC_NAME;
674 			neg->ac_name.hdr.tag_len =
675 					htons((u_int16_t)ourmsg->data_len);
676 			bcopy(ourmsg->data,
677 				neg->ac_name.data, ourmsg->data_len);
678 			neg->ac_name_len = ourmsg->data_len;
679 			neg->pkt->pkt_header.ph.code = PADO_CODE;
680 			/*
681 			 * Wait for PADI packet coming from hook
682 			 */
683 			sp->state = PPPOE_PRIMED;
684 			break;
685 		default:
686 			LEAVE(EINVAL);
687 		}
688 		break;
689 	default:
690 		LEAVE(EINVAL);
691 	}
692 
693 	/* Take care of synchronous response, if any */
694 	if (rptr)
695 		*rptr = resp;
696 	else if (resp)
697 		FREE(resp, M_NETGRAPH);
698 
699 	/* Free the message and return */
700 quit:
701 	FREE(msg, M_NETGRAPH);
702 	return(error);
703 }
704 
705 static void
706 pppoe_start(sessp sp)
707 {
708 	struct {
709 		struct pppoe_tag hdr;
710 		union	uniq	data;
711 	} uniqtag;
712 
713 	/*
714 	 * kick the state machine into starting up
715 	 */
716 	sp->state = PPPOE_SINIT;
717 	uniqtag.hdr.tag_type = PTT_HOST_UNIQ;
718 	uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data));
719 	uniqtag.data.pointer = sp;
720 	init_tags(sp);
721 	insert_tag(sp, &uniqtag.hdr);
722 	insert_tag(sp, &sp->neg->service.hdr);
723 	make_packet(sp);
724 	sendpacket(sp);
725 }
726 
727 /*
728  * Receive data, and do something with it.
729  * The caller will never free m or meta, so
730  * if we use up this data or abort we must free BOTH of these.
731  */
732 static int
733 ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
734 {
735 	node_p			node = hook->node;
736 	const priv_p		privp = node->private;
737 	sessp			sp = hook->private;
738 	struct pppoe_full_hdr	*wh;
739 	struct pppoe_hdr	*ph;
740 	int			error = 0;
741 	u_int16_t		session;
742 	u_int16_t		length;
743 	u_int8_t		code;
744 	struct pppoe_tag	*tag = NULL;
745 	hook_p 			sendhook;
746 	struct {
747 		struct pppoe_tag hdr;
748 		union	uniq	data;
749 	} uniqtag;
750 	negp			neg = NULL;
751 
752 	if (hook->private == &privp->debug_hook) {
753 		/*
754 		 * Data from the debug hook gets sent without modification
755 		 * straight to the ethernet.
756 		 */
757 		NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
758 	 	privp->packets_out++;
759 	} else if (hook->private == &privp->ethernet_hook) {
760 		/*
761 		 * Incoming data.
762 		 * Dig out various fields from the packet.
763 		 * use them to decide where to send it.
764 		 */
765 
766  		privp->packets_in++;
767 		m_pullup(m, sizeof(*wh)); /* Checks length */
768 		if (m == NULL) {
769 			LEAVE(ENOBUFS);
770 		}
771 		wh = mtod(m, struct pppoe_full_hdr *);
772 		ph = &wh->ph;
773 		session = ntohs(wh->ph.sid);
774 		length = ntohs(wh->ph.length);
775 		code = wh->ph.code;
776 		switch(ntohs(wh->eh.ether_type)) {
777 		case	ETHERTYPE_PPPOE_DISC:
778 			/*
779 			 * We need to try make sure that the tag area
780 			 * is contiguous, or we could wander of the end
781 			 * of a buffer and make a mess.
782 			 * (Linux wouldn't have this problem).
783 			 */
784 			 if (m->m_len != m->m_pkthdr.len) {
785 				/*
786 				 * It's not all in one piece.
787 				 * We need to do extra work.
788 				 */
789 				printf("packet fragmented\n");
790 			 }
791 
792 			switch(code) {
793 			case	PADI_CODE:
794 				/*
795 				 * We are a server:
796 				 * Look for a hook with the required service
797 				 * and send the ENTIRE packet up there.
798 				 * It should come back to a new hook in
799 				 * PRIMED state. Look there for further
800 				 * processing.
801 				 */
802 				tag = get_tag(ph, PTT_SRV_NAME);
803 				if (tag == NULL) {
804 					LEAVE(ENETUNREACH);
805 				}
806 				sendhook = pppoe_match_svc(hook->node,
807 			    		tag->tag_data, ntohs(tag->tag_len));
808 				if (sendhook) {
809 					NG_SEND_DATA(error, sendhook, m, meta);
810 				} else {
811 					LEAVE(ENETUNREACH);
812 				}
813 				break;
814 			case	PADO_CODE:
815 				/*
816 				 * We are a client:
817 				 * Use the host_uniq tag to find the
818 				 * hook this is in response to.
819 				 *
820 				 * For now simply accept the first we receive.
821 				 */
822 				tag = get_tag(ph, PTT_HOST_UNIQ);
823 				if ((tag == NULL)
824 				|| (ntohs(tag->tag_len) != sizeof(sp))) {
825 					LEAVE(ENETUNREACH);
826 				}
827 
828 				sendhook = pppoe_finduniq(node, tag);
829 				if (sendhook == NULL) {
830 					LEAVE(ENETUNREACH);
831 				}
832 
833 				/*
834 				 * Check the session is in the right state.
835 				 * It needs to be in PPPOE_SINIT.
836 				 */
837 				sp = sendhook->private;
838 				if (sp->state != PPPOE_SINIT) {
839 					LEAVE(ENETUNREACH);
840 				}
841 				neg = sp->neg;
842 				untimeout(pppoe_ticker, sendhook,
843 				    neg->timeout_handle);
844 
845 				/*
846 				 * This is the first time we hear
847 				 * from the server, so note it's
848 				 * unicast address, replacing the
849 				 * broadcast address .
850 				 */
851 				bcopy(wh->eh.ether_shost,
852 					neg->pkt->pkt_header.eh.ether_dhost,
853 					ETHER_ADDR_LEN);
854 				neg->timeout = 0;
855 				neg->pkt->pkt_header.ph.code = PADR_CODE;
856 				init_tags(sp);
857 				insert_tag(sp,&neg->service.hdr); /* Service */
858 				insert_tag(sp, tag);	      /* Host Unique */
859 				tag = get_tag(ph, PTT_AC_COOKIE);
860 				insert_tag(sp, tag);	 /* returned cookie */
861 				scan_tags(sp, ph);
862 				make_packet(sp);
863 				sp->state = PPPOE_SREQ;
864 				sendpacket(sp);
865 				break;
866 			case	PADR_CODE:
867 
868 				/*
869 				 * We are a server:
870 				 * Use the ac_cookie tag to find the
871 				 * hook this is in response to.
872 				 */
873 				tag = get_tag(ph, PTT_AC_COOKIE);
874 				if ((tag == NULL)
875 				|| (ntohs(tag->tag_len) != sizeof(sp))) {
876 					LEAVE(ENETUNREACH);
877 				}
878 
879 				sendhook = pppoe_finduniq(node, tag);
880 				if (sendhook == NULL) {
881 					LEAVE(ENETUNREACH);
882 				}
883 
884 				/*
885 				 * Check the session is in the right state.
886 				 * It needs to be in PPPOE_SOFFER
887 				 * or PPPOE_NEWCONNECTED. If the latter,
888 				 * then this is a retry by the client.
889 				 * so be nice, and resend.
890 				 */
891 				sp = sendhook->private;
892 				if (sp->state == PPPOE_NEWCONNECTED) {
893 					/*
894 					 * Whoa! drop back to resend that
895 					 * PADS packet.
896 					 * We should still have a copy of it.
897 					 */
898 					sp->state = PPPOE_SOFFER;
899 				}
900 				if (sp->state != PPPOE_SOFFER) {
901 					LEAVE (ENETUNREACH);
902 					break;
903 				}
904 				neg = sp->neg;
905 				untimeout(pppoe_ticker, sendhook,
906 				    neg->timeout_handle);
907 				neg->pkt->pkt_header.ph.code = PADS_CODE;
908 				if (sp->Session_ID == 0)
909 					neg->pkt->pkt_header.ph.sid =
910 					    sp->Session_ID = get_new_sid(node);
911 				neg->timeout = 0;
912 				/*
913 				 * start working out the tags to respond with.
914 				 */
915 				init_tags(sp);
916 				insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
917 				insert_tag(sp, tag);	/* ac_cookie */
918 				tag = get_tag(ph, PTT_SRV_NAME);
919 				insert_tag(sp, tag);	/* returned service */
920 				tag = get_tag(ph, PTT_HOST_UNIQ);
921 				insert_tag(sp, tag);    /* returned hostuniq */
922 				scan_tags(sp, ph);
923 				make_packet(sp);
924 				sp->state = PPPOE_NEWCONNECTED;
925 				sendpacket(sp);
926 				/*
927 				 * Having sent the last Negotiation header,
928 				 * Set up the stored packet header to
929 				 * be correct for the actual session.
930 				 * But keep the negotialtion stuff
931 				 * around in case we need to resend this last
932 				 * packet. We'll discard it when we move
933 				 * from NEWCONNECTED to CONNECTED
934 				 */
935 				sp->pkt_hdr = neg->pkt->pkt_header;
936 				sp->pkt_hdr.eh.ether_type
937 						= ETHERTYPE_PPPOE_SESS;
938 				sp->pkt_hdr.ph.code = 0;
939 				break;
940 			case	PADS_CODE:
941 				/*
942 				 * We are a client:
943 				 * Use the host_uniq tag to find the
944 				 * hook this is in response to.
945 				 * take the session ID and store it away.
946 				 * Also make sure the pre-made header is
947 				 * correct and set us into Session mode.
948 				 */
949 				tag = get_tag(ph, PTT_HOST_UNIQ);
950 				if ((tag == NULL)
951 				|| (ntohs(tag->tag_len) != sizeof(sp))) {
952 					LEAVE (ENETUNREACH);
953 					break;
954 				}
955 
956 				sendhook = pppoe_finduniq(node, tag);
957 				if (sendhook == NULL) {
958 					LEAVE(ENETUNREACH);
959 				}
960 
961 				/*
962 				 * Check the session is in the right state.
963 				 * It needs to be in PPPOE_SREQ.
964 				 */
965 				sp = sendhook->private;
966 				if (sp->state != PPPOE_SREQ) {
967 					LEAVE(ENETUNREACH);
968 				}
969 				neg = sp->neg;
970 				untimeout(pppoe_ticker, sendhook,
971 				    neg->timeout_handle);
972 				sp->Session_ID = wh->ph.sid;
973 				neg->timeout = 0;
974 				sp->state = PPPOE_CONNECTED;
975 				sendpacket(sp);
976 				/*
977 				 * Now we have gone to Connected mode,
978 				 * Free all resources needed for
979 				 * negotiation.
980 				 * Keep a copy of the header we will be using.
981 				 */
982 				sp->pkt_hdr = neg->pkt->pkt_header;
983 				sp->pkt_hdr.eh.ether_type
984 						= ETHERTYPE_PPPOE_SESS;
985 				sp->pkt_hdr.ph.code = 0;
986 				m_freem(neg->m);
987 				FREE(sp->neg, M_NETGRAPH);
988 				sp->neg = NULL;
989 				break;
990 			case	PADT_CODE:
991 				/*
992 				 * Send a 'close' message to the controlling
993 				 * process (the one that set us up);
994 				 * And then tear everything down.
995 				 *
996 				 * Find matching peer/session combination.
997 				 */
998 				sendhook = pppoe_findsession(node, wh);
999 				NG_FREE_DATA(m, meta); /* no longer needed */
1000 				if (sendhook == NULL) {
1001 					LEAVE(ENETUNREACH);
1002 				}
1003 				/* send message to creator */
1004 				/* close hook */
1005 				ng_destroy_hook(sendhook);
1006 				break;
1007 			default:
1008 				LEAVE(EPFNOSUPPORT);
1009 			}
1010 			break;
1011 		case	ETHERTYPE_PPPOE_SESS:
1012 			/*
1013 			 * find matching peer/session combination.
1014 			 */
1015 			sendhook = pppoe_findsession(node, wh);
1016 			if (sendhook == NULL) {
1017 				LEAVE (ENETUNREACH);
1018 				break;
1019 			}
1020 			m_adj(m, sizeof(*wh));
1021 			if (m->m_pkthdr.len < length) {
1022 				/* Packet too short, dump it */
1023 				LEAVE(EMSGSIZE);
1024 			}
1025 			/* XXX also need to trim excess at end I should think */
1026 			if ( sp->state != PPPOE_CONNECTED) {
1027 				if (sp->state == PPPOE_NEWCONNECTED) {
1028 					sp->state = PPPOE_CONNECTED;
1029 					/*
1030 					 * Now we have gone to Connected mode,
1031 					 * Free all resources needed for
1032 					 * negotiation.
1033 					 */
1034 					m_freem(sp->neg->m);
1035 					FREE(sp->neg, M_NETGRAPH);
1036 					sp->neg = NULL;
1037 				} else {
1038 					LEAVE (ENETUNREACH);
1039 					break;
1040 				}
1041 			}
1042 			NG_SEND_DATA( error, sendhook, m, meta);
1043 			break;
1044 		default:
1045 			LEAVE(EPFNOSUPPORT);
1046 		}
1047 	} else {
1048 		/*
1049 		 * 	Not ethernet or debug hook..
1050 		 *
1051 		 * The packet has come in on a normal hook.
1052 		 * We need to find out what kind of hook,
1053 		 * So we can decide how to handle it.
1054 		 * Check the hook's state.
1055 		 */
1056 		sp = hook->private;
1057 		switch (sp->state) {
1058 		case	PPPOE_NEWCONNECTED:
1059 		case	PPPOE_CONNECTED: {
1060 			struct pppoe_full_hdr *wh;
1061 			/*
1062 			 * Bang in a pre-made header, and set the length up
1063 			 * to be correct. Then send it to the ethernet driver.
1064 			 */
1065 			M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
1066 			if (m == NULL) {
1067 				LEAVE(ENOBUFS);
1068 			}
1069 			wh = mtod(m, struct pppoe_full_hdr *);
1070 			bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1071 			wh->ph.length = htons((short)(m->m_pkthdr.len));
1072 			NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
1073 			privp->packets_out++;
1074 			break;
1075 			}
1076 		case	PPPOE_PRIMED:
1077 			/*
1078 			 * A PADI packet is being returned by the application
1079 			 * that has set up this hook. This indicates that it
1080 			 * wants us to offer service.
1081 			 */
1082 			neg = sp->neg;
1083 			m_pullup(m, sizeof(*wh)); /* Checks length */
1084 			if (m == NULL) {
1085 				LEAVE(ENOBUFS);
1086 			}
1087 			wh = mtod(m, struct pppoe_full_hdr *);
1088 			ph = &wh->ph;
1089 			session = ntohs(wh->ph.sid);
1090 			length = ntohs(wh->ph.length);
1091 			code = wh->ph.code;
1092 
1093 			/*
1094 			 * This is the first time we hear
1095 			 * from the client, so note it's
1096 			 * unicast address, replacing the
1097 			 * broadcast address .
1098 			 */
1099 			bcopy(wh->eh.ether_shost,
1100 				neg->pkt->pkt_header.eh.ether_dhost,
1101 				ETHER_ADDR_LEN);
1102 			sp->state = PPPOE_SOFFER;
1103 			neg->timeout = 0;
1104 			neg->pkt->pkt_header.ph.code = PADO_CODE;
1105 
1106 			/*
1107 			 * start working out the tags to respond with.
1108 			 */
1109 			uniqtag.hdr.tag_type = PTT_AC_COOKIE;
1110 			uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
1111 			uniqtag.data.pointer = sp;
1112 			init_tags(sp);
1113 			insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1114 			insert_tag(sp, tag);	      /* returned hostunique */
1115 			insert_tag(sp, &uniqtag.hdr);      /* AC cookie */
1116 			tag = get_tag(ph, PTT_SRV_NAME);
1117 			insert_tag(sp, tag);	      /* returned service */
1118 			/* XXX maybe put the tag in the session store */
1119 			scan_tags(sp, ph);
1120 			make_packet(sp);
1121 			sendpacket(sp);
1122 			break;
1123 
1124 		/*
1125 		 * Packets coming from the hook make no sense
1126 		 * to sessions in these states. Throw them away.
1127 		 */
1128 		case	PPPOE_SINIT:
1129 		case	PPPOE_SREQ:
1130 		case	PPPOE_SOFFER:
1131 		case	PPPOE_SNONE:
1132 		case	PPPOE_LISTENING:
1133 		case	PPPOE_DEAD:
1134 		default:
1135 			LEAVE(ENETUNREACH);
1136 		}
1137 	}
1138 quit:
1139 	NG_FREE_DATA(m, meta);
1140 	return error;
1141 }
1142 
1143 /*
1144  * Do local shutdown processing..
1145  * If we are a persistant device, we might refuse to go away, and
1146  * we'd only remove our links and reset ourself.
1147  */
1148 static int
1149 ng_PPPoE_rmnode(node_p node)
1150 {
1151 	const priv_p privdata = node->private;
1152 
1153 	node->flags |= NG_INVALID;
1154 	ng_cutlinks(node);
1155 	ng_unname(node);
1156 	node->private = NULL;
1157 	ng_unref(privdata->node);
1158 	FREE(privdata, M_NETGRAPH);
1159 	return (0);
1160 }
1161 
1162 /*
1163  * This is called once we've already connected a new hook to the other node.
1164  * It gives us a chance to balk at the last minute.
1165  */
1166 static int
1167 ng_PPPoE_connect(hook_p hook)
1168 {
1169 	/* be really amiable and just say "YUP that's OK by me! " */
1170 	return (0);
1171 }
1172 
1173 /*
1174  * Hook disconnection
1175  *
1176  * Clean up all dangling links and infirmation about the session/hook.
1177  * For this type, removal of the last link destroys the node
1178  */
1179 static int
1180 ng_PPPoE_disconnect(hook_p hook)
1181 {
1182 	node_p node = hook->node;
1183 	priv_p privp = node->private;
1184 	sessp	sp;
1185 
1186 	if (hook->private == &privp->debug_hook) {
1187 		privp->debug_hook = NULL;
1188 	} else if (hook->private == &privp->ethernet_hook) {
1189 		privp->ethernet_hook = NULL;
1190 	} else {
1191 		sp = hook->private;
1192 		untimeout(pppoe_ticker, hook, sp->neg->timeout_handle);
1193 		FREE(sp, M_NETGRAPH);
1194 	}
1195 	if (node->numhooks == 0)
1196 		ng_rmnode(node);
1197 	return (0);
1198 }
1199 
1200 /*
1201  * timeouts come here.
1202  */
1203 static void
1204 pppoe_ticker(void *arg)
1205 {
1206 	int s = splnet();
1207 	hook_p hook = arg;
1208 	sessp	sp = hook->private;
1209 	negp	neg = sp->neg;
1210 	int	error = 0;
1211 	struct mbuf *m0 = NULL;
1212 	priv_p privp = hook->node->private;
1213 	meta_p dummy = NULL;
1214 
1215 	switch(sp->state) {
1216 		/*
1217 		 * resend the last packet, using an exponential backoff.
1218 		 * After a period of time, stop growing the backoff,
1219 		 * and either leave it, or reverst to the start.
1220 		 */
1221 	case	PPPOE_SINIT:
1222 	case	PPPOE_SREQ:
1223 		/* timeouts on these produce resends */
1224 		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1225 		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
1226 		neg->timeout_handle = timeout(pppoe_ticker,
1227 					hook, neg->timeout * hz);
1228 		if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
1229 			if (sp->state == PPPOE_SREQ) {
1230 				/* revert to SINIT mode */
1231 			} else {
1232 				neg->timeout = PPPOE_TIMEOUT_LIMIT;
1233 			}
1234 		}
1235 		break;
1236 	case	PPPOE_PRIMED:
1237 	case	PPPOE_SOFFER:
1238 		/* a timeout on these says "give up" */
1239 		/* XXX should notify creator */
1240 		ng_destroy_hook(hook);
1241 		break;
1242 	default:
1243 		/* timeouts have no meaning in other states */
1244 		printf("pppoe: unexpected timeout\n");
1245 	}
1246 	splx(s);
1247 }
1248 
1249 
1250 static void
1251 sendpacket(sessp sp)
1252 {
1253 	int	error = 0;
1254 	struct mbuf *m0 = NULL;
1255 	hook_p hook = sp->hook;
1256 	negp	neg = sp->neg;
1257 	priv_p	privp = hook->node->private;
1258 	meta_p dummy = NULL;
1259 
1260 	switch(sp->state) {
1261 	case	PPPOE_LISTENING:
1262 	case	PPPOE_DEAD:
1263 	case	PPPOE_SNONE:
1264 	case	PPPOE_NEWCONNECTED:
1265 	case	PPPOE_CONNECTED:
1266 		printf("pppoe: timeout: unexpected state\n");
1267 		break;
1268 
1269 	case	PPPOE_PRIMED:
1270 		/* No packet to send, but set up the timeout */
1271 		neg->timeout_handle = timeout(pppoe_ticker,
1272 					hook, PPPOE_OFFER_TIMEOUT * hz);
1273 		break;
1274 
1275 	case	PPPOE_SOFFER:
1276 		/*
1277 		 * send the offer but if they don't respond
1278 		 * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
1279 		 */
1280 		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1281 		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
1282 		neg->timeout_handle = timeout(pppoe_ticker,
1283 					hook, PPPOE_OFFER_TIMEOUT * hz);
1284 		break;
1285 
1286 	case	PPPOE_SINIT:
1287 	case	PPPOE_SREQ:
1288 		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1289 		NG_SEND_DATA( error, sp->hook, m0, dummy);
1290 		neg->timeout_handle = timeout(pppoe_ticker, hook, hz);
1291 		neg->timeout = 2;
1292 		break;
1293 
1294 	default:
1295 		error = EINVAL;
1296 		printf("pppoe: timeout: bad state\n");
1297 	}
1298 	/* return (error); */
1299 }
1300 
1301 /*
1302  * Parse an incoming packet to see if any tags should be copied to the
1303  * output packet. DOon't do any tags that are likely to have been
1304  * handles a the main state machine.
1305  */
1306 static struct pppoe_tag*
1307 scan_tags(sessp	sp, struct pppoe_hdr* ph)
1308 {
1309 	char *end = (char *)next_tag(ph);
1310 	char *ptn;
1311 	struct pppoe_tag *pt = &ph->tag[0];
1312 	/*
1313 	 * Keep processing tags while a tag header will still fit.
1314 	 */
1315 	while((char*)(pt + 1) <= end) {
1316 		/*
1317 		 * If the tag data would go past the end of the packet, abort.
1318 		 */
1319 		ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
1320 		if(ptn > end)
1321 			return NULL;
1322 
1323 		switch (pt->tag_type) {
1324 		case	PTT_RELAY_SID:
1325 			insert_tag(sp, pt);
1326 			break;
1327 		case	PTT_EOL:
1328 			return NULL;
1329 		case	PTT_SRV_NAME:
1330 		case	PTT_AC_NAME:
1331 		case	PTT_HOST_UNIQ:
1332 		case	PTT_AC_COOKIE:
1333 		case	PTT_VENDOR:
1334 		case	PTT_SRV_ERR:
1335 		case	PTT_SYS_ERR:
1336 		case	PTT_GEN_ERR:
1337 			break;
1338 		}
1339 		pt = (struct pppoe_tag*)ptn;
1340 	}
1341 	return NULL;
1342 }
1343 
1344