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