ng_pppoe.c revision 53172
1#define SIGNOFF "session closed"
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 53172 1999-11-15 04:03:34Z julian $
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/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 ng_constructor_t	ng_pppoe_constructor;
69static ng_rcvmsg_t	ng_pppoe_rcvmsg;
70static ng_shutdown_t	ng_pppoe_rmnode;
71static ng_newhook_t	ng_pppoe_newhook;
72static ng_connect_t	ng_pppoe_connect;
73static ng_rcvdata_t	ng_pppoe_rcvdata;
74static ng_disconnect_t	ng_pppoe_disconnect;
75
76/* Netgraph node type descriptor */
77static struct ng_type typestruct = {
78	NG_VERSION,
79	NG_PPPOE_NODE_TYPE,
80	NULL,
81	ng_pppoe_constructor,
82	ng_pppoe_rcvmsg,
83	ng_pppoe_rmnode,
84	ng_pppoe_newhook,
85	NULL,
86	ng_pppoe_connect,
87	ng_pppoe_rcvdata,
88	ng_pppoe_rcvdata,
89	ng_pppoe_disconnect
90};
91NETGRAPH_INIT(pppoe, &typestruct);
92
93/*
94 * States for the session state machine.
95 * These have no meaning if there is no hook attached yet.
96 */
97enum state {
98    PPPOE_SNONE=0,	/* [both] Initial state */
99    PPPOE_SINIT,	/* [Client] Sent discovery initiation */
100    PPPOE_PRIMED,	/* [Server] Received discovery initiation */
101    PPPOE_SOFFER,	/* [Server] Sent offer message */
102    PPPOE_SREQ,		/* [Client] Sent a Request */
103    PPPOE_LISTENING,	/* [Server] Listening for discover initiation msg */
104    PPPOE_NEWCONNECTED,	/* [Both] Connection established, No data received */
105    PPPOE_CONNECTED,	/* [Both] Connection established, Data received */
106    PPPOE_DEAD		/* [Both] */
107};
108
109#define NUMTAGS 20 /* number of tags we are set up to work with */
110
111/*
112 * Information we store for each hook on each node for negotiating the
113 * session. The mbuf and cluster are freed once negotiation has completed.
114 * The whole negotiation block is then discarded.
115 */
116
117struct sess_neg {
118	struct mbuf 		*m; /* holds cluster with last sent packet */
119	union	packet		*pkt; /* points within the above cluster */
120	struct callout_handle	timeout_handle;   /* see timeout(9) */
121	u_int			timeout; /* 0,1,2,4,8,16 etc. seconds */
122	u_int			numtags;
123	struct pppoe_tag	*tags[NUMTAGS];
124	u_int			service_len;
125	u_int			ac_name_len;
126
127	struct datatag		service;
128	struct datatag		ac_name;
129};
130typedef struct sess_neg *negp;
131
132/*
133 * Session information that is needed after connection.
134 */
135struct session {
136	hook_p  		hook;
137	u_int16_t		Session_ID;
138	struct session		*hash_next; /* not yet uesed */
139	enum state		state;
140	char			creator[NG_NODELEN + 1]; /* who to notify */
141	struct pppoe_full_hdr	pkt_hdr;	/* used when connected */
142	negp			neg;		/* used when negotiating */
143};
144typedef struct session *sessp;
145
146/*
147 * Information we store for each node
148 */
149struct PPPOE {
150	node_p		node;		/* back pointer to node */
151	hook_p  	ethernet_hook;
152	hook_p  	debug_hook;
153	u_int   	packets_in;	/* packets in from ethernet */
154	u_int   	packets_out;	/* packets out towards ethernet */
155	u_int32_t	flags;
156	/*struct session *buckets[HASH_SIZE];*/	/* not yet used */
157};
158typedef struct PPPOE *priv_p;
159
160const struct ether_header eh_prototype =
161	{{0xff,0xff,0xff,0xff,0xff,0xff},
162	 {0x00,0x00,0x00,0x00,0x00,0x00},
163	 ETHERTYPE_PPPOE_DISC};
164
165union uniq {
166	char bytes[sizeof(void *)];
167	void * pointer;
168	};
169
170#define	LEAVE(x) do { error = x; goto quit; } while(0)
171static void	pppoe_start(sessp sp);
172static void	sendpacket(sessp sp);
173static void	pppoe_ticker(void *arg);
174static struct pppoe_tag* scan_tags(sessp	sp, struct pppoe_hdr* ph);
175static	int	pppoe_send_event(sessp sp, enum cmd cmdid);
176
177/*************************************************************************
178 * Some basic utilities  from the Linux version with author's permission.*
179 * Author:	Michal Ostrowski <mostrows@styx.uwaterloo.ca>		 *
180 ************************************************************************/
181
182/*
183 * Generate a new session id
184 * XXX find out the FreeBSD locking scheme.
185 */
186static u_int16_t
187get_new_sid(node_p node)
188{
189	static int pppoe_sid = 10;
190	sessp sp;
191	hook_p	hook;
192	u_int16_t val;
193	priv_p privp = node->private;
194
195AAA
196restart:
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 */
225static __inline struct pppoe_tag*
226next_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 */
236static struct pppoe_tag*
237get_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	 */
245AAA
246	while((char*)(pt + 1) <= end) {
247	    /*
248	     * If the tag data would go past the end of the packet, abort.
249	     */
250	    ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
251	    if(ptn > end)
252		return NULL;
253
254	    if(pt->tag_type == idx)
255		return pt;
256
257	    pt = (struct pppoe_tag*)ptn;
258	}
259	return NULL;
260}
261
262/**************************************************************************
263 * inlines to initialise or add tags to a session's tag list,
264 **************************************************************************/
265/*
266 * Initialise the session's tag list
267 */
268static void
269init_tags(sessp sp)
270{
271AAA
272	if(sp->neg == NULL) {
273		printf("pppoe: asked to init NULL neg pointer\n");
274		return;
275	}
276	sp->neg->numtags = 0;
277}
278
279static void
280insert_tag(sessp sp, struct pppoe_tag *tp)
281{
282	int	i;
283	negp neg;
284
285AAA
286	if((neg = sp->neg) == NULL) {
287		printf("pppoe: asked to use NULL neg pointer\n");
288		return;
289	}
290	if ((i = neg->numtags++) < NUMTAGS) {
291		neg->tags[i] = tp;
292	} else {
293		printf("pppoe: asked to add too many tags to packet\n");
294		neg->numtags--;
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, &sp->neg->service.hdr);
750	insert_tag(sp, &uniqtag.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	*utag = NULL, *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		}
803		wh = mtod(m, struct pppoe_full_hdr *);
804		ph = &wh->ph;
805		session = ntohs(wh->ph.sid);
806		length = ntohs(wh->ph.length);
807		code = wh->ph.code;
808		switch(wh->eh.ether_type) {
809		case	ETHERTYPE_PPPOE_DISC:
810			/*
811			 * We need to try to make sure that the tag area
812			 * is contiguous, or we could wander off the end
813			 * of a buffer and make a mess.
814			 * (Linux wouldn't have this problem).
815			 */
816/*XXX fix this mess */
817
818			if (m->m_pkthdr.len <= MHLEN) {
819				if( m->m_len < m->m_pkthdr.len) {
820					m = m_pullup(m, m->m_pkthdr.len);
821					if (m == NULL) {
822						printf("couldn't m_pullup\n");
823						LEAVE(ENOBUFS);
824					}
825				}
826			}
827			if (m->m_len != m->m_pkthdr.len) {
828				/*
829				 * It's not all in one piece.
830				 * We need to do extra work.
831				 */
832				printf("packet fragmented\n");
833				LEAVE(EMSGSIZE);
834			 }
835
836			switch(code) {
837			case	PADI_CODE:
838				/*
839				 * We are a server:
840				 * Look for a hook with the required service
841				 * and send the ENTIRE packet up there.
842				 * It should come back to a new hook in
843				 * PRIMED state. Look there for further
844				 * processing.
845				 */
846				tag = get_tag(ph, PTT_SRV_NAME);
847				if (tag == NULL) {
848					printf("no service tag\n");
849					LEAVE(ENETUNREACH);
850				}
851				sendhook = pppoe_match_svc(hook->node,
852			    		tag->tag_data, ntohs(tag->tag_len));
853				if (sendhook) {
854					NG_SEND_DATA(error, sendhook, m, meta);
855				} else {
856					printf("no such service\n");
857					LEAVE(ENETUNREACH);
858				}
859				break;
860			case	PADO_CODE:
861				/*
862				 * We are a client:
863				 * Use the host_uniq tag to find the
864				 * hook this is in response to.
865				 * Received #2, now send #3
866				 * For now simply accept the first we receive.
867				 */
868				utag = get_tag(ph, PTT_HOST_UNIQ);
869				if ((utag == NULL)
870				|| (ntohs(utag->tag_len) != sizeof(sp))) {
871					printf("no host unique field\n");
872					LEAVE(ENETUNREACH);
873				}
874
875				sendhook = pppoe_finduniq(node, utag);
876				if (sendhook == NULL) {
877					printf("no matching session\n");
878					LEAVE(ENETUNREACH);
879				}
880
881				/*
882				 * Check the session is in the right state.
883				 * It needs to be in PPPOE_SINIT.
884				 */
885				sp = sendhook->private;
886				if (sp->state != PPPOE_SINIT) {
887					printf("session in wrong state\n");
888					LEAVE(ENETUNREACH);
889				}
890				neg = sp->neg;
891				untimeout(pppoe_ticker, sendhook,
892				    neg->timeout_handle);
893
894				/*
895				 * This is the first time we hear
896				 * from the server, so note it's
897				 * unicast address, replacing the
898				 * broadcast address .
899				 */
900				bcopy(wh->eh.ether_shost,
901					neg->pkt->pkt_header.eh.ether_dhost,
902					ETHER_ADDR_LEN);
903				neg->timeout = 0;
904				neg->pkt->pkt_header.ph.code = PADR_CODE;
905				init_tags(sp);
906				insert_tag(sp, &neg->service.hdr); /* Service */
907				if ((tag = get_tag(ph, PTT_AC_COOKIE)))
908					insert_tag(sp, tag); /* return cookie */
909				if ((tag = get_tag(ph, PTT_AC_NAME)))
910					insert_tag(sp, tag); /* return it */
911				insert_tag(sp, utag);      /* Host Unique */
912				scan_tags(sp, ph);
913				make_packet(sp);
914				sp->state = PPPOE_SREQ;
915				sendpacket(sp);
916				break;
917			case	PADR_CODE:
918
919				/*
920				 * We are a server:
921				 * Use the ac_cookie tag to find the
922				 * hook this is in response to.
923				 */
924				utag = get_tag(ph, PTT_AC_COOKIE);
925				if ((utag == NULL)
926				|| (ntohs(utag->tag_len) != sizeof(sp))) {
927					LEAVE(ENETUNREACH);
928				}
929
930				sendhook = pppoe_finduniq(node, utag);
931				if (sendhook == NULL) {
932					LEAVE(ENETUNREACH);
933				}
934
935				/*
936				 * Check the session is in the right state.
937				 * It needs to be in PPPOE_SOFFER
938				 * or PPPOE_NEWCONNECTED. If the latter,
939				 * then this is a retry by the client.
940				 * so be nice, and resend.
941				 */
942				sp = sendhook->private;
943				if (sp->state == PPPOE_NEWCONNECTED) {
944					/*
945					 * Whoa! drop back to resend that
946					 * PADS packet.
947					 * We should still have a copy of it.
948					 */
949					sp->state = PPPOE_SOFFER;
950				}
951				if (sp->state != PPPOE_SOFFER) {
952					LEAVE (ENETUNREACH);
953					break;
954				}
955				neg = sp->neg;
956				untimeout(pppoe_ticker, sendhook,
957				    neg->timeout_handle);
958				neg->pkt->pkt_header.ph.code = PADS_CODE;
959				if (sp->Session_ID == 0)
960					neg->pkt->pkt_header.ph.sid =
961					    htons(sp->Session_ID
962						= get_new_sid(node));
963				neg->timeout = 0;
964				/*
965				 * start working out the tags to respond with.
966				 */
967				init_tags(sp);
968				insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
969				if ((tag = get_tag(ph, PTT_SRV_NAME)))
970					insert_tag(sp, tag);/* return service */
971				if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
972					insert_tag(sp, tag); /* return it */
973				insert_tag(sp, utag);	/* ac_cookie */
974				scan_tags(sp, ph);
975				make_packet(sp);
976				sendpacket(sp);
977				sp->state = PPPOE_NEWCONNECTED;
978				/*
979				 * Having sent the last Negotiation header,
980				 * Set up the stored packet header to
981				 * be correct for the actual session.
982				 * But keep the negotialtion stuff
983				 * around in case we need to resend this last
984				 * packet. We'll discard it when we move
985				 * from NEWCONNECTED to CONNECTED
986				 */
987				sp->pkt_hdr = neg->pkt->pkt_header;
988				sp->pkt_hdr.eh.ether_type
989						= ETHERTYPE_PPPOE_SESS;
990				sp->pkt_hdr.ph.code = 0;
991				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
992				break;
993			case	PADS_CODE:
994				/*
995				 * We are a client:
996				 * Use the host_uniq tag to find the
997				 * hook this is in response to.
998				 * take the session ID and store it away.
999				 * Also make sure the pre-made header is
1000				 * correct and set us into Session mode.
1001				 */
1002				utag = get_tag(ph, PTT_HOST_UNIQ);
1003				if ((utag == NULL)
1004				|| (ntohs(utag->tag_len) != sizeof(sp))) {
1005					LEAVE (ENETUNREACH);
1006					break;
1007				}
1008				sendhook = pppoe_finduniq(node, utag);
1009				if (sendhook == NULL) {
1010					LEAVE(ENETUNREACH);
1011				}
1012
1013				/*
1014				 * Check the session is in the right state.
1015				 * It needs to be in PPPOE_SREQ.
1016				 */
1017				sp = sendhook->private;
1018				if (sp->state != PPPOE_SREQ) {
1019					LEAVE(ENETUNREACH);
1020				}
1021				neg = sp->neg;
1022				untimeout(pppoe_ticker, sendhook,
1023				    neg->timeout_handle);
1024				neg->pkt->pkt_header.ph.sid = wh->ph.sid;
1025				sp->Session_ID = ntohs(wh->ph.sid);
1026				neg->timeout = 0;
1027				sp->state = PPPOE_CONNECTED;
1028				/*
1029				 * Now we have gone to Connected mode,
1030				 * Free all resources needed for
1031				 * negotiation.
1032				 * Keep a copy of the header we will be using.
1033				 */
1034				sp->pkt_hdr = neg->pkt->pkt_header;
1035				sp->pkt_hdr.eh.ether_type
1036						= ETHERTYPE_PPPOE_SESS;
1037				sp->pkt_hdr.ph.code = 0;
1038				m_freem(neg->m);
1039				FREE(sp->neg, M_NETGRAPH);
1040				sp->neg = NULL;
1041				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
1042				break;
1043			case	PADT_CODE:
1044				/*
1045				 * Send a 'close' message to the controlling
1046				 * process (the one that set us up);
1047				 * And then tear everything down.
1048				 *
1049				 * Find matching peer/session combination.
1050				 */
1051				sendhook = pppoe_findsession(node, wh);
1052				NG_FREE_DATA(m, meta); /* no longer needed */
1053				if (sendhook == NULL) {
1054					LEAVE(ENETUNREACH);
1055				}
1056				/* send message to creator */
1057				/* close hook */
1058				if (sendhook) {
1059					ng_destroy_hook(sendhook);
1060				}
1061				break;
1062			default:
1063				LEAVE(EPFNOSUPPORT);
1064			}
1065			break;
1066		case	ETHERTYPE_PPPOE_SESS:
1067			/*
1068			 * find matching peer/session combination.
1069			 */
1070			sendhook = pppoe_findsession(node, wh);
1071			if (sendhook == NULL) {
1072				LEAVE (ENETUNREACH);
1073				break;
1074			}
1075			sp = sendhook->private;
1076			m_adj(m, sizeof(*wh));
1077			if (m->m_pkthdr.len < length) {
1078				/* Packet too short, dump it */
1079				LEAVE(EMSGSIZE);
1080			}
1081
1082			/* Also need to trim excess at the end */
1083			if (m->m_pkthdr.len > length) {
1084				m_adj(m, -((int)(m->m_pkthdr.len - length)));
1085			}
1086			if ( sp->state != PPPOE_CONNECTED) {
1087				if (sp->state == PPPOE_NEWCONNECTED) {
1088					sp->state = PPPOE_CONNECTED;
1089					/*
1090					 * Now we have gone to Connected mode,
1091					 * Free all resources needed for
1092					 * negotiation.
1093					 */
1094					m_freem(sp->neg->m);
1095					FREE(sp->neg, M_NETGRAPH);
1096					sp->neg = NULL;
1097				} else {
1098					LEAVE (ENETUNREACH);
1099					break;
1100				}
1101			}
1102			NG_SEND_DATA( error, sendhook, m, meta);
1103			break;
1104		default:
1105			LEAVE(EPFNOSUPPORT);
1106		}
1107	} else {
1108		/*
1109		 * 	Not ethernet or debug hook..
1110		 *
1111		 * The packet has come in on a normal hook.
1112		 * We need to find out what kind of hook,
1113		 * So we can decide how to handle it.
1114		 * Check the hook's state.
1115		 */
1116		sp = hook->private;
1117		switch (sp->state) {
1118		case	PPPOE_NEWCONNECTED:
1119		case	PPPOE_CONNECTED: {
1120			struct pppoe_full_hdr *wh;
1121			/*
1122			 * Bang in a pre-made header, and set the length up
1123			 * to be correct. Then send it to the ethernet driver.
1124			 * But first correct the length.
1125			 */
1126			sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len));
1127			M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
1128			if (m == NULL) {
1129				LEAVE(ENOBUFS);
1130			}
1131			wh = mtod(m, struct pppoe_full_hdr *);
1132			bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
1133			NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
1134			privp->packets_out++;
1135			break;
1136			}
1137		case	PPPOE_PRIMED:
1138			/*
1139			 * A PADI packet is being returned by the application
1140			 * that has set up this hook. This indicates that it
1141			 * wants us to offer service.
1142			 */
1143			neg = sp->neg;
1144			if (m->m_len < sizeof(*wh)) {
1145				m = m_pullup(m, sizeof(*wh));
1146				if (m == NULL) {
1147					LEAVE(ENOBUFS);
1148				}
1149			}
1150			wh = mtod(m, struct pppoe_full_hdr *);
1151			ph = &wh->ph;
1152			session = ntohs(wh->ph.sid);
1153			length = ntohs(wh->ph.length);
1154			code = wh->ph.code;
1155			if ( code != PADI_CODE) {
1156				LEAVE(EINVAL);
1157			};
1158			untimeout(pppoe_ticker, hook,
1159				    neg->timeout_handle);
1160
1161			/*
1162			 * This is the first time we hear
1163			 * from the client, so note it's
1164			 * unicast address, replacing the
1165			 * broadcast address.
1166			 */
1167			bcopy(wh->eh.ether_shost,
1168				neg->pkt->pkt_header.eh.ether_dhost,
1169				ETHER_ADDR_LEN);
1170			sp->state = PPPOE_SOFFER;
1171			neg->timeout = 0;
1172			neg->pkt->pkt_header.ph.code = PADO_CODE;
1173
1174			/*
1175			 * start working out the tags to respond with.
1176			 */
1177			uniqtag.hdr.tag_type = PTT_AC_COOKIE;
1178			uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
1179			uniqtag.data.pointer = sp;
1180			init_tags(sp);
1181			insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
1182			if ((tag = get_tag(ph, PTT_SRV_NAME)))
1183				insert_tag(sp, tag);	  /* return service */
1184			if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
1185				insert_tag(sp, tag); /* returned hostunique */
1186			insert_tag(sp, &uniqtag.hdr);
1187			/* XXX maybe put the tag in the session store */
1188			scan_tags(sp, ph);
1189			make_packet(sp);
1190			sendpacket(sp);
1191			break;
1192
1193		/*
1194		 * Packets coming from the hook make no sense
1195		 * to sessions in these states. Throw them away.
1196		 */
1197		case	PPPOE_SINIT:
1198		case	PPPOE_SREQ:
1199		case	PPPOE_SOFFER:
1200		case	PPPOE_SNONE:
1201		case	PPPOE_LISTENING:
1202		case	PPPOE_DEAD:
1203		default:
1204			LEAVE(ENETUNREACH);
1205		}
1206	}
1207quit:
1208	NG_FREE_DATA(m, meta);
1209	return error;
1210}
1211
1212/*
1213 * Do local shutdown processing..
1214 * If we are a persistant device, we might refuse to go away, and
1215 * we'd only remove our links and reset ourself.
1216 */
1217static int
1218ng_pppoe_rmnode(node_p node)
1219{
1220	const priv_p privdata = node->private;
1221
1222AAA
1223	node->flags |= NG_INVALID;
1224	ng_cutlinks(node);
1225	ng_unname(node);
1226	node->private = NULL;
1227	ng_unref(privdata->node);
1228	FREE(privdata, M_NETGRAPH);
1229	return (0);
1230}
1231
1232/*
1233 * This is called once we've already connected a new hook to the other node.
1234 * It gives us a chance to balk at the last minute.
1235 */
1236static int
1237ng_pppoe_connect(hook_p hook)
1238{
1239	/* be really amiable and just say "YUP that's OK by me! " */
1240	return (0);
1241}
1242
1243/*
1244 * Hook disconnection
1245 *
1246 * Clean up all dangling links and infirmation about the session/hook.
1247 * For this type, removal of the last link destroys the node
1248 */
1249static int
1250ng_pppoe_disconnect(hook_p hook)
1251{
1252	node_p node = hook->node;
1253	priv_p privp = node->private;
1254	sessp	sp;
1255	int 	hooks;
1256
1257AAA
1258	if (hook->private == &privp->debug_hook) {
1259		privp->debug_hook = NULL;
1260	} else if (hook->private == &privp->ethernet_hook) {
1261		privp->ethernet_hook = NULL;
1262		ng_rmnode(node);
1263	} else {
1264		sp = hook->private;
1265		if (sp->state != PPPOE_SNONE ) {
1266			pppoe_send_event(sp, NGM_PPPOE_CLOSE);
1267		}
1268		if ((privp->ethernet_hook)
1269		&& ((sp->state == PPPOE_CONNECTED)
1270		 || (sp->state == PPPOE_NEWCONNECTED))) {
1271			struct mbuf *m;
1272			struct pppoe_full_hdr *wh;
1273			struct pppoe_tag *tag;
1274			int	msglen = strlen(SIGNOFF);
1275			void *dummy = NULL;
1276			int error = 0;
1277
1278			/* revert the stored header to DISC/PADT mode */
1279		 	wh = &sp->pkt_hdr;
1280			wh->ph.code = PADT_CODE;
1281			wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
1282
1283			/* generate a packet of that type */
1284			MGETHDR(m, M_DONTWAIT, MT_DATA);
1285			m->m_pkthdr.rcvif = NULL;
1286			m->m_pkthdr.len = m->m_len = sizeof(*wh);
1287			bcopy((caddr_t)wh, mtod(m, caddr_t), sizeof(*wh));
1288			/* Add a General error message and adjust sizes */
1289			wh = mtod(m, struct pppoe_full_hdr *);
1290			tag = wh->ph.tag;
1291			tag->tag_type = PTT_GEN_ERR;
1292			tag->tag_len = htons((u_int16_t)msglen);
1293			strncpy(tag->tag_data, SIGNOFF, msglen);
1294			m->m_pkthdr.len = (m->m_len += sizeof(*tag) + msglen);
1295			wh->ph.length = htons(sizeof(*tag) + msglen);
1296			NG_SEND_DATA(error, privp->ethernet_hook, m, dummy);
1297		}
1298		if (sp->neg) {
1299			untimeout(pppoe_ticker, hook, sp->neg->timeout_handle);
1300			if (sp->neg->m)
1301				m_freem(sp->neg->m);
1302			FREE(sp->neg, M_NETGRAPH);
1303		}
1304		FREE(sp, M_NETGRAPH);
1305		hook->private = NULL;
1306		/* work out how many session hooks there are */
1307		/* Node goes away on last session hook removal */
1308		hooks = node->numhooks; /* this one already not counted */
1309		if (privp->ethernet_hook) hooks -= 1;
1310		if (privp->debug_hook) hooks -= 1;
1311		if (hooks == 0)
1312			ng_rmnode(node);
1313	}
1314	if (node->numhooks == 0)
1315		ng_rmnode(node);
1316	return (0);
1317}
1318
1319/*
1320 * timeouts come here.
1321 */
1322static void
1323pppoe_ticker(void *arg)
1324{
1325	int s = splnet();
1326	hook_p hook = arg;
1327	sessp	sp = hook->private;
1328	negp	neg = sp->neg;
1329	int	error = 0;
1330	struct mbuf *m0 = NULL;
1331	priv_p privp = hook->node->private;
1332	meta_p dummy = NULL;
1333
1334AAA
1335	switch(sp->state) {
1336		/*
1337		 * resend the last packet, using an exponential backoff.
1338		 * After a period of time, stop growing the backoff,
1339		 * and either leave it, or revert to the start.
1340		 */
1341	case	PPPOE_SINIT:
1342	case	PPPOE_SREQ:
1343		/* timeouts on these produce resends */
1344		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1345		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
1346		neg->timeout_handle = timeout(pppoe_ticker,
1347					hook, neg->timeout * hz);
1348		if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
1349			if (sp->state == PPPOE_SREQ) {
1350				/* revert to SINIT mode */
1351				pppoe_start(sp);
1352			} else {
1353				neg->timeout = PPPOE_TIMEOUT_LIMIT;
1354			}
1355		}
1356		break;
1357	case	PPPOE_PRIMED:
1358	case	PPPOE_SOFFER:
1359		/* a timeout on these says "give up" */
1360		ng_destroy_hook(hook);
1361		break;
1362	default:
1363		/* timeouts have no meaning in other states */
1364		printf("pppoe: unexpected timeout\n");
1365	}
1366	splx(s);
1367}
1368
1369
1370static void
1371sendpacket(sessp sp)
1372{
1373	int	error = 0;
1374	struct mbuf *m0 = NULL;
1375	hook_p hook = sp->hook;
1376	negp	neg = sp->neg;
1377	priv_p	privp = hook->node->private;
1378	meta_p dummy = NULL;
1379
1380AAA
1381	switch(sp->state) {
1382	case	PPPOE_LISTENING:
1383	case	PPPOE_DEAD:
1384	case	PPPOE_SNONE:
1385	case	PPPOE_NEWCONNECTED:
1386	case	PPPOE_CONNECTED:
1387		printf("pppoe: sendpacket: unexpected state\n");
1388		break;
1389
1390	case	PPPOE_PRIMED:
1391		/* No packet to send, but set up the timeout */
1392		neg->timeout_handle = timeout(pppoe_ticker,
1393					hook, PPPOE_OFFER_TIMEOUT * hz);
1394		break;
1395
1396	case	PPPOE_SOFFER:
1397		/*
1398		 * send the offer but if they don't respond
1399		 * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
1400		 */
1401		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1402		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
1403		neg->timeout_handle = timeout(pppoe_ticker,
1404					hook, PPPOE_OFFER_TIMEOUT * hz);
1405		break;
1406
1407	case	PPPOE_SINIT:
1408	case	PPPOE_SREQ:
1409		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
1410		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
1411		neg->timeout_handle = timeout(pppoe_ticker, hook, hz);
1412		neg->timeout = 2;
1413		break;
1414
1415	default:
1416		error = EINVAL;
1417		printf("pppoe: timeout: bad state\n");
1418	}
1419	/* return (error); */
1420}
1421
1422/*
1423 * Parse an incoming packet to see if any tags should be copied to the
1424 * output packet. Don't do any tags that have been handled in the main
1425 * state machine.
1426 */
1427static struct pppoe_tag*
1428scan_tags(sessp	sp, struct pppoe_hdr* ph)
1429{
1430	char *end = (char *)next_tag(ph);
1431	char *ptn;
1432	struct pppoe_tag *pt = &ph->tag[0];
1433	/*
1434	 * Keep processing tags while a tag header will still fit.
1435	 */
1436AAA
1437	while((char*)(pt + 1) <= end) {
1438		/*
1439		 * If the tag data would go past the end of the packet, abort.
1440		 */
1441		ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
1442		if(ptn > end)
1443			return NULL;
1444
1445		switch (pt->tag_type) {
1446		case	PTT_RELAY_SID:
1447			insert_tag(sp, pt);
1448			break;
1449		case	PTT_EOL:
1450			return NULL;
1451		case	PTT_SRV_NAME:
1452		case	PTT_AC_NAME:
1453		case	PTT_HOST_UNIQ:
1454		case	PTT_AC_COOKIE:
1455		case	PTT_VENDOR:
1456		case	PTT_SRV_ERR:
1457		case	PTT_SYS_ERR:
1458		case	PTT_GEN_ERR:
1459			break;
1460		}
1461		pt = (struct pppoe_tag*)ptn;
1462	}
1463	return NULL;
1464}
1465
1466static	int
1467pppoe_send_event(sessp sp, enum cmd cmdid)
1468{
1469	int error;
1470	struct ng_mesg *msg;
1471	struct ngpppoe_sts *sts;
1472
1473AAA
1474	NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
1475			sizeof(struct ngpppoe_sts), M_NOWAIT);
1476	sts = (struct ngpppoe_sts *)msg->data;
1477	strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1);
1478	error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
1479	return (error);
1480}
1481