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