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