ng_pppoe.c revision 53172
152523Sjulian#define SIGNOFF "session closed"
252419Sjulian/*
352419Sjulian * ng_pppoe.c
452419Sjulian *
552419Sjulian * Copyright (c) 1996-1999 Whistle Communications, Inc.
652419Sjulian * All rights reserved.
752419Sjulian *
852419Sjulian * Subject to the following obligations and disclaimer of warranty, use and
952419Sjulian * redistribution of this software, in source or object code forms, with or
1052419Sjulian * without modifications are expressly permitted by Whistle Communications;
1152419Sjulian * provided, however, that:
1252419Sjulian * 1. Any and all reproductions of the source or object code must include the
1352419Sjulian *    copyright notice above and the following disclaimer of warranties; and
1452419Sjulian * 2. No rights are granted, in any manner or form, to use Whistle
1552419Sjulian *    Communications, Inc. trademarks, including the mark "WHISTLE
1652419Sjulian *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1752419Sjulian *    such appears in the above copyright notice or in the software.
1852419Sjulian *
1952419Sjulian * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2052419Sjulian * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2152419Sjulian * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2252419Sjulian * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2352419Sjulian * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2452419Sjulian * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2552419Sjulian * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2652419Sjulian * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2752419Sjulian * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2852419Sjulian * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
2952419Sjulian * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3052419Sjulian * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3152419Sjulian * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3252419Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3352419Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3452419Sjulian * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3552419Sjulian * OF SUCH DAMAGE.
3652419Sjulian *
3752419Sjulian * Author: Julian Elischer <julian@whistle.com>
3852419Sjulian *
3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_pppoe.c 53172 1999-11-15 04:03:34Z julian $
4052752Sjulian * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $
4152419Sjulian */
4252443Sjulian#if 0
4352443Sjulian#define AAA printf("pppoe: %s\n", __FUNCTION__ );
4452510Sjulian#define BBB printf("-%d-", __LINE__ );
4552443Sjulian#else
4652443Sjulian#define AAA
4752510Sjulian#define BBB
4852443Sjulian#endif
4952419Sjulian
5052419Sjulian#include <sys/param.h>
5152419Sjulian#include <sys/systm.h>
5252419Sjulian#include <sys/kernel.h>
5352419Sjulian#include <sys/mbuf.h>
5452419Sjulian#include <sys/malloc.h>
5552419Sjulian#include <sys/errno.h>
5652419Sjulian#include <sys/syslog.h>
5752419Sjulian#include <net/ethernet.h>
5852419Sjulian
5952419Sjulian#include <netgraph/ng_message.h>
6052419Sjulian#include <netgraph/netgraph.h>
6152419Sjulian#include <netgraph/ng_pppoe.h>
6252419Sjulian
6352419Sjulian/*
6452419Sjulian * This section contains the netgraph method declarations for the
6552419Sjulian * sample node. These methods define the netgraph 'type'.
6652419Sjulian */
6752419Sjulian
6852752Sjulianstatic ng_constructor_t	ng_pppoe_constructor;
6952752Sjulianstatic ng_rcvmsg_t	ng_pppoe_rcvmsg;
7052752Sjulianstatic ng_shutdown_t	ng_pppoe_rmnode;
7152752Sjulianstatic ng_newhook_t	ng_pppoe_newhook;
7252752Sjulianstatic ng_connect_t	ng_pppoe_connect;
7352752Sjulianstatic ng_rcvdata_t	ng_pppoe_rcvdata;
7452752Sjulianstatic ng_disconnect_t	ng_pppoe_disconnect;
7552419Sjulian
7652419Sjulian/* Netgraph node type descriptor */
7752419Sjulianstatic struct ng_type typestruct = {
7852419Sjulian	NG_VERSION,
7952419Sjulian	NG_PPPOE_NODE_TYPE,
8052419Sjulian	NULL,
8152562Sjulian	ng_pppoe_constructor,
8252562Sjulian	ng_pppoe_rcvmsg,
8352562Sjulian	ng_pppoe_rmnode,
8452562Sjulian	ng_pppoe_newhook,
8552419Sjulian	NULL,
8652562Sjulian	ng_pppoe_connect,
8752562Sjulian	ng_pppoe_rcvdata,
8852562Sjulian	ng_pppoe_rcvdata,
8952562Sjulian	ng_pppoe_disconnect
9052419Sjulian};
9152562SjulianNETGRAPH_INIT(pppoe, &typestruct);
9252419Sjulian
9352419Sjulian/*
9452419Sjulian * States for the session state machine.
9552419Sjulian * These have no meaning if there is no hook attached yet.
9652419Sjulian */
9752419Sjulianenum state {
9852419Sjulian    PPPOE_SNONE=0,	/* [both] Initial state */
9952419Sjulian    PPPOE_SINIT,	/* [Client] Sent discovery initiation */
10053145Sjulian    PPPOE_PRIMED,	/* [Server] Received discovery initiation */
10152419Sjulian    PPPOE_SOFFER,	/* [Server] Sent offer message */
10252419Sjulian    PPPOE_SREQ,		/* [Client] Sent a Request */
10352419Sjulian    PPPOE_LISTENING,	/* [Server] Listening for discover initiation msg */
10452419Sjulian    PPPOE_NEWCONNECTED,	/* [Both] Connection established, No data received */
10552419Sjulian    PPPOE_CONNECTED,	/* [Both] Connection established, Data received */
10652419Sjulian    PPPOE_DEAD		/* [Both] */
10752419Sjulian};
10852419Sjulian
10952419Sjulian#define NUMTAGS 20 /* number of tags we are set up to work with */
11052419Sjulian
11152419Sjulian/*
11252419Sjulian * Information we store for each hook on each node for negotiating the
11352419Sjulian * session. The mbuf and cluster are freed once negotiation has completed.
11452419Sjulian * The whole negotiation block is then discarded.
11552419Sjulian */
11652419Sjulian
11752419Sjulianstruct sess_neg {
11852419Sjulian	struct mbuf 		*m; /* holds cluster with last sent packet */
11952419Sjulian	union	packet		*pkt; /* points within the above cluster */
12052419Sjulian	struct callout_handle	timeout_handle;   /* see timeout(9) */
12152419Sjulian	u_int			timeout; /* 0,1,2,4,8,16 etc. seconds */
12252419Sjulian	u_int			numtags;
12352419Sjulian	struct pppoe_tag	*tags[NUMTAGS];
12452419Sjulian	u_int			service_len;
12552419Sjulian	u_int			ac_name_len;
12652419Sjulian
12752419Sjulian	struct datatag		service;
12852419Sjulian	struct datatag		ac_name;
12952419Sjulian};
13052419Sjuliantypedef struct sess_neg *negp;
13152419Sjulian
13252419Sjulian/*
13352419Sjulian * Session information that is needed after connection.
13452419Sjulian */
13552419Sjulianstruct session {
13652419Sjulian	hook_p  		hook;
13752419Sjulian	u_int16_t		Session_ID;
13852419Sjulian	struct session		*hash_next; /* not yet uesed */
13952419Sjulian	enum state		state;
14052419Sjulian	char			creator[NG_NODELEN + 1]; /* who to notify */
14152419Sjulian	struct pppoe_full_hdr	pkt_hdr;	/* used when connected */
14252419Sjulian	negp			neg;		/* used when negotiating */
14352419Sjulian};
14452419Sjuliantypedef struct session *sessp;
14552419Sjulian
14652419Sjulian/*
14752419Sjulian * Information we store for each node
14852419Sjulian */
14952419Sjulianstruct PPPOE {
15052419Sjulian	node_p		node;		/* back pointer to node */
15152419Sjulian	hook_p  	ethernet_hook;
15252419Sjulian	hook_p  	debug_hook;
15352419Sjulian	u_int   	packets_in;	/* packets in from ethernet */
15452419Sjulian	u_int   	packets_out;	/* packets out towards ethernet */
15552419Sjulian	u_int32_t	flags;
15652419Sjulian	/*struct session *buckets[HASH_SIZE];*/	/* not yet used */
15752419Sjulian};
15852419Sjuliantypedef struct PPPOE *priv_p;
15952419Sjulian
16052419Sjulianconst struct ether_header eh_prototype =
16152419Sjulian	{{0xff,0xff,0xff,0xff,0xff,0xff},
16252419Sjulian	 {0x00,0x00,0x00,0x00,0x00,0x00},
16352419Sjulian	 ETHERTYPE_PPPOE_DISC};
16452419Sjulian
16552419Sjulianunion uniq {
16652419Sjulian	char bytes[sizeof(void *)];
16752419Sjulian	void * pointer;
16852419Sjulian	};
16952419Sjulian
17052419Sjulian#define	LEAVE(x) do { error = x; goto quit; } while(0)
17152419Sjulianstatic void	pppoe_start(sessp sp);
17252419Sjulianstatic void	sendpacket(sessp sp);
17352419Sjulianstatic void	pppoe_ticker(void *arg);
17452419Sjulianstatic struct pppoe_tag* scan_tags(sessp	sp, struct pppoe_hdr* ph);
17552441Sjulianstatic	int	pppoe_send_event(sessp sp, enum cmd cmdid);
17652419Sjulian
17752419Sjulian/*************************************************************************
17852419Sjulian * Some basic utilities  from the Linux version with author's permission.*
17952419Sjulian * Author:	Michal Ostrowski <mostrows@styx.uwaterloo.ca>		 *
18052419Sjulian ************************************************************************/
18152419Sjulian
18252419Sjulian/*
18352419Sjulian * Generate a new session id
18453145Sjulian * XXX find out the FreeBSD locking scheme.
18552419Sjulian */
18652419Sjulianstatic u_int16_t
18752419Sjulianget_new_sid(node_p node)
18852419Sjulian{
18952419Sjulian	static int pppoe_sid = 10;
19052419Sjulian	sessp sp;
19152419Sjulian	hook_p	hook;
19252419Sjulian	u_int16_t val;
19352419Sjulian	priv_p privp = node->private;
19452419Sjulian
19552443SjulianAAA
19652419Sjulianrestart:
19752419Sjulian	val = pppoe_sid++;
19852419Sjulian	/*
19952419Sjulian	 * Spec says 0xFFFF is reserved.
20052419Sjulian	 * Also don't use 0x0000
20152419Sjulian	 */
20252419Sjulian	if (val == 0xffff) {
20352419Sjulian		pppoe_sid = 20;
20452419Sjulian		goto restart;
20552419Sjulian	}
20652419Sjulian
20752419Sjulian	/* Check it isn't already in use */
20852419Sjulian	LIST_FOREACH(hook, &node->hooks, hooks) {
20952419Sjulian		/* don't check special hooks */
21052419Sjulian		if ((hook->private == &privp->debug_hook)
21152419Sjulian		||  (hook->private == &privp->ethernet_hook))
21252419Sjulian			continue;
21352419Sjulian		sp = hook->private;
21452419Sjulian		if (sp->Session_ID == val)
21552419Sjulian			goto restart;
21652419Sjulian	}
21752419Sjulian
21852419Sjulian	return val;
21952419Sjulian}
22052419Sjulian
22152419Sjulian
22252419Sjulian/*
22352419Sjulian * Return the location where the next tag can be put
22452419Sjulian */
22552419Sjulianstatic __inline struct pppoe_tag*
22652419Sjuliannext_tag(struct pppoe_hdr* ph)
22752419Sjulian{
22852419Sjulian	return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length));
22952419Sjulian}
23052419Sjulian
23152419Sjulian/*
23252419Sjulian * Look for a tag of a specific type
23352419Sjulian * Don't trust any length the other end says.
23452419Sjulian * but assume we already sanity checked ph->length.
23552419Sjulian */
23652419Sjulianstatic struct pppoe_tag*
23752419Sjulianget_tag(struct pppoe_hdr* ph, u_int16_t idx)
23852419Sjulian{
23952419Sjulian	char *end = (char *)next_tag(ph);
24052419Sjulian	char *ptn;
24152419Sjulian	struct pppoe_tag *pt = &ph->tag[0];
24252419Sjulian	/*
24352419Sjulian	 * Keep processing tags while a tag header will still fit.
24452419Sjulian	 */
24552443SjulianAAA
24652419Sjulian	while((char*)(pt + 1) <= end) {
24752419Sjulian	    /*
24852419Sjulian	     * If the tag data would go past the end of the packet, abort.
24952419Sjulian	     */
25052419Sjulian	    ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
25152419Sjulian	    if(ptn > end)
25252419Sjulian		return NULL;
25352419Sjulian
25452419Sjulian	    if(pt->tag_type == idx)
25552419Sjulian		return pt;
25652419Sjulian
25752419Sjulian	    pt = (struct pppoe_tag*)ptn;
25852419Sjulian	}
25952419Sjulian	return NULL;
26052419Sjulian}
26152419Sjulian
26252419Sjulian/**************************************************************************
26352419Sjulian * inlines to initialise or add tags to a session's tag list,
26452419Sjulian **************************************************************************/
26552419Sjulian/*
26652419Sjulian * Initialise the session's tag list
26752419Sjulian */
26852419Sjulianstatic void
26952419Sjulianinit_tags(sessp sp)
27052419Sjulian{
27152443SjulianAAA
27252419Sjulian	if(sp->neg == NULL) {
27352419Sjulian		printf("pppoe: asked to init NULL neg pointer\n");
27452419Sjulian		return;
27552419Sjulian	}
27652419Sjulian	sp->neg->numtags = 0;
27752419Sjulian}
27852419Sjulian
27952419Sjulianstatic void
28052419Sjulianinsert_tag(sessp sp, struct pppoe_tag *tp)
28152419Sjulian{
28252419Sjulian	int	i;
28352419Sjulian	negp neg;
28452419Sjulian
28552443SjulianAAA
28652419Sjulian	if((neg = sp->neg) == NULL) {
28752419Sjulian		printf("pppoe: asked to use NULL neg pointer\n");
28852419Sjulian		return;
28952419Sjulian	}
29052419Sjulian	if ((i = neg->numtags++) < NUMTAGS) {
29152419Sjulian		neg->tags[i] = tp;
29252419Sjulian	} else {
29352419Sjulian		printf("pppoe: asked to add too many tags to packet\n");
29453042Sjulian		neg->numtags--;
29552419Sjulian	}
29652419Sjulian}
29752419Sjulian
29852419Sjulian/*
29952419Sjulian * Make up a packet, using the tags filled out for the session.
30052419Sjulian *
30152419Sjulian * Assume that the actual pppoe header and ethernet header
30252419Sjulian * are filled out externally to this routine.
30352419Sjulian * Also assume that neg->wh points to the correct
30452419Sjulian * location at the front of the buffer space.
30552419Sjulian */
30652419Sjulianstatic void
30752419Sjulianmake_packet(sessp sp) {
30852419Sjulian	struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header;
30952419Sjulian	struct pppoe_tag **tag;
31052419Sjulian	char *dp;
31152419Sjulian	int count;
31252419Sjulian	int tlen;
31352419Sjulian	u_int16_t length = 0;
31452419Sjulian
31552443SjulianAAA
31652443Sjulian	if ((sp->neg == NULL) || (sp->neg->m == NULL)) {
31752419Sjulian		printf("pppoe: make_packet called from wrong state\n");
31852419Sjulian	}
31952419Sjulian	dp = (char *)wh->ph.tag;
32052419Sjulian	for (count = 0, tag = sp->neg->tags;
32152419Sjulian	    ((count < sp->neg->numtags) && (count < NUMTAGS));
32252419Sjulian	    tag++, count++) {
32352419Sjulian		tlen = ntohs((*tag)->tag_len) + sizeof(**tag);
32452419Sjulian		if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) {
32552419Sjulian			printf("pppoe: tags too long\n");
32652419Sjulian			sp->neg->numtags = count;
32752419Sjulian			break;	/* XXX chop off what's too long */
32852419Sjulian		}
32952419Sjulian		bcopy((char *)*tag, (char *)dp, tlen);
33052419Sjulian		length += tlen;
33152419Sjulian		dp += tlen;
33252419Sjulian	}
33352419Sjulian 	wh->ph.length = htons(length);
33452419Sjulian	sp->neg->m->m_len = length + sizeof(*wh);
33552419Sjulian	sp->neg->m->m_pkthdr.len = length + sizeof(*wh);
33652419Sjulian}
33752419Sjulian
33852419Sjulian/**************************************************************************
33952419Sjulian * Routine to match a service offered					  *
34052419Sjulian **************************************************************************/
34152419Sjulian/*
34252419Sjulian * Find a hook that has a service string that matches that
34352419Sjulian * we are seeking. for now use a simple string.
34452419Sjulian * In the future we may need something like regexp().
34552419Sjulian * for testing allow a null string to match 1st found and a null service
34652419Sjulian * to match all requests. Also make '*' do the same.
34752419Sjulian */
34852419Sjulianstatic hook_p
34952419Sjulianpppoe_match_svc(node_p node, char *svc_name, int svc_len)
35052419Sjulian{
35152419Sjulian	sessp	sp	= NULL;
35252419Sjulian	negp	neg	= NULL;
35352419Sjulian	priv_p	privp	= node->private;
35452419Sjulian	hook_p hook;
35552419Sjulian
35652443SjulianAAA
35752419Sjulian	LIST_FOREACH(hook, &node->hooks, hooks) {
35852419Sjulian
35952419Sjulian		/* skip any hook that is debug or ethernet */
36052419Sjulian		if ((hook->private == &privp->debug_hook)
36152419Sjulian		||  (hook->private == &privp->ethernet_hook))
36252419Sjulian			continue;
36352419Sjulian		sp = hook->private;
36452419Sjulian
36552419Sjulian		/* Skip any sessions which are not in LISTEN mode. */
36652419Sjulian		if ( sp->state != PPPOE_LISTENING)
36752419Sjulian			continue;
36852419Sjulian
36952419Sjulian		neg = sp->neg;
37052419Sjulian		/* XXX check validity of this */
37152419Sjulian		/* special case, NULL request. match 1st found. */
37252419Sjulian		if (svc_len == 0)
37352419Sjulian			break;
37452419Sjulian
37552419Sjulian		/* XXX check validity of this */
37652419Sjulian		/* Special case for a blank or "*" service name (wildcard) */
37752419Sjulian		if ((neg->service_len == 0)
37852419Sjulian		||  ((neg->service_len == 1)
37952419Sjulian		  && (neg->service.data[0] == '*'))) {
38052419Sjulian			break;
38152419Sjulian		}
38252419Sjulian
38352419Sjulian		/* If the lengths don't match, that aint it. */
38452419Sjulian		if (neg->service_len != svc_len)
38552419Sjulian			continue;
38652419Sjulian
38752419Sjulian		/* An exact match? */
38852419Sjulian		if (strncmp(svc_name, neg->service.data, svc_len) == 0)
38952419Sjulian			break;
39052419Sjulian	}
39152419Sjulian	return (hook);
39252419Sjulian}
39352419Sjulian/**************************************************************************
39452419Sjulian * Routine to find a particular session that matches an incoming packet	  *
39552419Sjulian **************************************************************************/
39652419Sjulianstatic hook_p
39752419Sjulianpppoe_findsession(node_p node, struct pppoe_full_hdr *wh)
39852419Sjulian{
39952419Sjulian	sessp	sp = NULL;
40052419Sjulian	hook_p hook = NULL;
40152419Sjulian	priv_p	privp = node->private;
40252448Sjulian	u_int16_t	session = ntohs(wh->ph.sid);
40352419Sjulian
40452419Sjulian	/*
40552419Sjulian	 * find matching peer/session combination.
40652419Sjulian	 */
40752443SjulianAAA
40852419Sjulian	LIST_FOREACH(hook, &node->hooks, hooks) {
40952419Sjulian		/* don't check special hooks */
41052419Sjulian		if ((hook->private == &privp->debug_hook)
41152419Sjulian		||  (hook->private == &privp->ethernet_hook)) {
41252419Sjulian			continue;
41352419Sjulian		}
41452419Sjulian		sp = hook->private;
41552419Sjulian		if ( ( (sp->state == PPPOE_CONNECTED)
41652419Sjulian		    || (sp->state == PPPOE_NEWCONNECTED) )
41752419Sjulian		&& (sp->Session_ID == session)
41852419Sjulian		&& (bcmp(sp->pkt_hdr.eh.ether_dhost,
41952419Sjulian		    wh->eh.ether_shost,
42052419Sjulian		    ETHER_ADDR_LEN)) == 0) {
42152419Sjulian			break;
42252419Sjulian		}
42352419Sjulian	}
42452419Sjulian	return (hook);
42552419Sjulian}
42652419Sjulian
42752419Sjulianstatic hook_p
42852419Sjulianpppoe_finduniq(node_p node, struct pppoe_tag *tag)
42952419Sjulian{
43052419Sjulian	hook_p hook = NULL;
43152419Sjulian	priv_p	privp = node->private;
43252419Sjulian	union uniq		uniq;
43352419Sjulian
43452443SjulianAAA
43552419Sjulian	bcopy(tag->tag_data, uniq.bytes, sizeof(void *));
43652419Sjulian	/* cycle through all known hooks */
43752419Sjulian	LIST_FOREACH(hook, &node->hooks, hooks) {
43852419Sjulian		/* don't check special hooks */
43952419Sjulian		if ((hook->private == &privp->debug_hook)
44052419Sjulian		||  (hook->private == &privp->ethernet_hook))
44152419Sjulian			continue;
44252419Sjulian		if (uniq.pointer == hook->private)
44352419Sjulian			break;
44452419Sjulian	}
44552419Sjulian	return (hook);
44652419Sjulian}
44752419Sjulian
44852419Sjulian/**************************************************************************
44952419Sjulian * start of Netgraph entrypoints					  *
45052419Sjulian **************************************************************************/
45152419Sjulian
45252419Sjulian/*
45352419Sjulian * Allocate the private data structure and the generic node
45452419Sjulian * and link them together.
45552419Sjulian *
45652419Sjulian * ng_make_node_common() returns with a generic node struct
45752419Sjulian * with a single reference for us.. we transfer it to the
45852419Sjulian * private structure.. when we free the private struct we must
45952419Sjulian * unref the node so it gets freed too.
46052419Sjulian *
46152419Sjulian * If this were a device node than this work would be done in the attach()
46252419Sjulian * routine and the constructor would return EINVAL as you should not be able
46352419Sjulian * to creatednodes that depend on hardware (unless you can add the hardware :)
46452419Sjulian */
46552419Sjulianstatic int
46652562Sjulianng_pppoe_constructor(node_p *nodep)
46752419Sjulian{
46852419Sjulian	priv_p privdata;
46952419Sjulian	int error;
47052419Sjulian
47152443SjulianAAA
47252419Sjulian	/* Initialize private descriptor */
47352419Sjulian	MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, M_WAITOK);
47452419Sjulian	if (privdata == NULL)
47552419Sjulian		return (ENOMEM);
47652419Sjulian	bzero(privdata, sizeof(*privdata));
47752419Sjulian
47852419Sjulian	/* Call the 'generic' (ie, superclass) node constructor */
47952419Sjulian	if ((error = ng_make_node_common(&typestruct, nodep))) {
48052419Sjulian		FREE(privdata, M_NETGRAPH);
48152419Sjulian		return (error);
48252419Sjulian	}
48352419Sjulian
48452419Sjulian	/* Link structs together; this counts as our one reference to *nodep */
48552419Sjulian	(*nodep)->private = privdata;
48652419Sjulian	privdata->node = *nodep;
48752419Sjulian	return (0);
48852419Sjulian}
48952419Sjulian
49052419Sjulian/*
49152419Sjulian * Give our ok for a hook to be added...
49252419Sjulian * point the hook's private info to the hook structure.
49352419Sjulian *
49452419Sjulian * The following hook names are special:
49552419Sjulian *  Ethernet:  the hook that should be connected to a NIC.
49652419Sjulian *  debug:	copies of data sent out here  (when I write the code).
49752419Sjulian */
49852419Sjulianstatic int
49952562Sjulianng_pppoe_newhook(node_p node, hook_p hook, const char *name)
50052419Sjulian{
50152419Sjulian	const priv_p privp = node->private;
50252419Sjulian	sessp sp;
50352419Sjulian
50452443SjulianAAA
50552419Sjulian	if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) {
50652419Sjulian		privp->ethernet_hook = hook;
50752419Sjulian		hook->private = &privp->ethernet_hook;
50852419Sjulian	} else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) {
50952419Sjulian		privp->debug_hook = hook;
51052419Sjulian		hook->private = &privp->debug_hook;
51152419Sjulian	} else {
51252419Sjulian		/*
51352419Sjulian		 * Any other unique name is OK.
51452419Sjulian		 * The infrastructure has already checked that it's unique,
51552419Sjulian		 * so just allocate it and hook it in.
51652419Sjulian		 */
51752419Sjulian		MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_WAITOK);
51852419Sjulian		if (sp == NULL) {
51952419Sjulian				return (ENOMEM);
52052419Sjulian		}
52152419Sjulian		bzero(sp, sizeof(*sp));
52252419Sjulian
52352419Sjulian		hook->private = sp;
52452419Sjulian		sp->hook = hook;
52552419Sjulian	}
52652419Sjulian	return(0);
52752419Sjulian}
52852419Sjulian
52952419Sjulian/*
53052419Sjulian * Get a netgraph control message.
53152419Sjulian * Check it is one we understand. If needed, send a response.
53252419Sjulian * We sometimes save the address for an async action later.
53352419Sjulian * Always free the message.
53452419Sjulian */
53552419Sjulianstatic int
53652562Sjulianng_pppoe_rcvmsg(node_p node,
53752419Sjulian	   struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
53852419Sjulian{
53952419Sjulian	priv_p privp = node->private;
54052562Sjulian	struct ngpppoe_init_data *ourmsg = NULL;
54152419Sjulian	struct ng_mesg *resp = NULL;
54252419Sjulian	int error = 0;
54352419Sjulian	hook_p hook = NULL;
54452419Sjulian	sessp sp = NULL;
54552419Sjulian	negp neg = NULL;
54652419Sjulian
54752443SjulianAAA
54852419Sjulian	/* Deal with message according to cookie and command */
54952419Sjulian	switch (msg->header.typecookie) {
55052419Sjulian	case NGM_PPPOE_COOKIE:
55152419Sjulian		switch (msg->header.cmd) {
55252419Sjulian		case NGM_PPPOE_CONNECT:
55352419Sjulian		case NGM_PPPOE_LISTEN:
55452419Sjulian		case NGM_PPPOE_OFFER:
55552562Sjulian			ourmsg = (struct ngpppoe_init_data *)msg->data;
55652419Sjulian			if (( sizeof(*ourmsg) > msg->header.arglen)
55752419Sjulian			|| ((sizeof(*ourmsg) + ourmsg->data_len)
55852419Sjulian			    > msg->header.arglen)) {
55952562Sjulian				printf("pppoe_rcvmsg: bad arg size");
56052419Sjulian				LEAVE(EMSGSIZE);
56152419Sjulian			}
56252419Sjulian			if (ourmsg->data_len > PPPOE_SERVICE_NAME_SIZE) {
56352419Sjulian				printf("pppoe: init data too long (%d)\n",
56452419Sjulian							ourmsg->data_len);
56552419Sjulian				LEAVE(EMSGSIZE);
56652419Sjulian			}
56752419Sjulian			/* make sure strcmp will terminate safely */
56852419Sjulian			ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0';
56952419Sjulian
57052419Sjulian			/* cycle through all known hooks */
57152419Sjulian			LIST_FOREACH(hook, &node->hooks, hooks) {
57252419Sjulian				if (hook->name
57352419Sjulian				&& strcmp(hook->name, ourmsg->hook) == 0)
57452419Sjulian					break;
57552419Sjulian			}
57652419Sjulian			if (hook == NULL) {
57752419Sjulian				LEAVE(ENOENT);
57852419Sjulian			}
57952419Sjulian			if ((hook->private == &privp->debug_hook)
58052419Sjulian			||  (hook->private == &privp->ethernet_hook)) {
58152419Sjulian				LEAVE(EINVAL);
58252419Sjulian			}
58352419Sjulian			sp = hook->private;
58452419Sjulian			if (sp->state |= PPPOE_SNONE) {
58552419Sjulian				printf("pppoe: Session already active\n");
58652419Sjulian				LEAVE(EISCONN);
58752419Sjulian			}
58852443Sjulian
58952419Sjulian			/*
59052419Sjulian			 * set up prototype header
59152419Sjulian			 */
59252419Sjulian			MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, M_WAITOK);
59352419Sjulian
59452419Sjulian			if (neg == NULL) {
59552419Sjulian				printf("pppoe: Session out of memory\n");
59652419Sjulian				LEAVE(ENOMEM);
59752419Sjulian			}
59852419Sjulian			bzero(neg, sizeof(*neg));
59952419Sjulian			MGETHDR(neg->m, M_DONTWAIT, MT_DATA);
60052419Sjulian			if(neg->m == NULL) {
60152443Sjulian				printf("pppoe: Session out of mbufs\n");
60252419Sjulian				FREE(neg, M_NETGRAPH);
60352419Sjulian				LEAVE(ENOBUFS);
60452419Sjulian			}
60552419Sjulian			neg->m->m_pkthdr.rcvif = NULL;
60652419Sjulian			MCLGET(neg->m, M_DONTWAIT);
60752419Sjulian			if ((neg->m->m_flags & M_EXT) == 0) {
60852443Sjulian				printf("pppoe: Session out of mcls\n");
60952419Sjulian				m_freem(neg->m);
61052419Sjulian				FREE(neg, M_NETGRAPH);
61152419Sjulian				LEAVE(ENOBUFS);
61252419Sjulian			}
61352419Sjulian			sp->neg = neg;
61452443Sjulian			callout_handle_init( &neg->timeout_handle);
61552419Sjulian			neg->m->m_len = sizeof(struct pppoe_full_hdr);
61652419Sjulian			neg->pkt = mtod(neg->m, union packet*);
61752419Sjulian			neg->pkt->pkt_header.eh = eh_prototype;
61852419Sjulian			neg->pkt->pkt_header.ph.ver = 0x1;
61952419Sjulian			neg->pkt->pkt_header.ph.type = 0x1;
62052419Sjulian			neg->pkt->pkt_header.ph.sid = 0x0000;
62152419Sjulian			neg->timeout = 0;
62252419Sjulian
62352419Sjulian			strncpy(sp->creator, retaddr, NG_NODELEN);
62452419Sjulian			sp->creator[NG_NODELEN] = '\0';
62552419Sjulian		}
62652419Sjulian		switch (msg->header.cmd) {
62752419Sjulian		case NGM_PPPOE_GET_STATUS:
62852419Sjulian		    {
62952562Sjulian			struct ngpppoestat *stats;
63052419Sjulian
63152419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
63252419Sjulian			if (!resp) {
63352419Sjulian				LEAVE(ENOMEM);
63452419Sjulian			}
63552562Sjulian			stats = (struct ngpppoestat *) resp->data;
63652419Sjulian			stats->packets_in = privp->packets_in;
63752419Sjulian			stats->packets_out = privp->packets_out;
63852419Sjulian			break;
63952419Sjulian		    }
64052419Sjulian		case NGM_PPPOE_CONNECT:
64152419Sjulian			/*
64252419Sjulian			 * Check the hook exists and is Uninitialised.
64352419Sjulian			 * Send a PADI request, and start the timeout logic.
64452419Sjulian			 * Store the originator of this message so we can send
64552419Sjulian			 * a success of fail message to them later.
64652419Sjulian			 * Move the session to SINIT
64752419Sjulian			 * Set up the session to the correct state and
64852419Sjulian			 * start it.
64952419Sjulian			 */
65052419Sjulian			neg->service.hdr.tag_type = PTT_SRV_NAME;
65152419Sjulian			neg->service.hdr.tag_len =
65252419Sjulian					htons((u_int16_t)ourmsg->data_len);
65352443Sjulian			if (ourmsg->data_len) {
65452443Sjulian				bcopy(ourmsg->data,
65552443Sjulian					neg->service.data, ourmsg->data_len);
65652443Sjulian			}
65752419Sjulian			neg->service_len = ourmsg->data_len;
65852419Sjulian			pppoe_start(sp);
65952419Sjulian			break;
66052419Sjulian		case NGM_PPPOE_LISTEN:
66152419Sjulian			/*
66252419Sjulian			 * Check the hook exists and is Uninitialised.
66352419Sjulian			 * Install the service matching string.
66452419Sjulian			 * Store the originator of this message so we can send
66552419Sjulian			 * a success of fail message to them later.
66652419Sjulian			 * Move the hook to 'LISTENING'
66752443Sjulian
66852419Sjulian			 */
66952419Sjulian			neg->service.hdr.tag_type = PTT_SRV_NAME;
67052419Sjulian			neg->service.hdr.tag_len =
67152419Sjulian					htons((u_int16_t)ourmsg->data_len);
67252443Sjulian
67352443Sjulian			if (ourmsg->data_len) {
67452443Sjulian				bcopy(ourmsg->data,
67552443Sjulian					neg->service.data, ourmsg->data_len);
67652443Sjulian			}
67752419Sjulian			neg->service_len = ourmsg->data_len;
67852419Sjulian			neg->pkt->pkt_header.ph.code = PADT_CODE;
67952419Sjulian			/*
68052419Sjulian			 * wait for PADI packet coming from ethernet
68152419Sjulian			 */
68252419Sjulian			sp->state = PPPOE_LISTENING;
68352419Sjulian			break;
68452419Sjulian		case NGM_PPPOE_OFFER:
68552419Sjulian			/*
68652419Sjulian			 * Check the hook exists and is Uninitialised.
68752419Sjulian			 * Store the originator of this message so we can send
68852419Sjulian			 * a success of fail message to them later.
68952419Sjulian			 * Store the AC-Name given and go to PRIMED.
69052419Sjulian			 */
69152419Sjulian			neg->ac_name.hdr.tag_type = PTT_AC_NAME;
69252419Sjulian			neg->ac_name.hdr.tag_len =
69352419Sjulian					htons((u_int16_t)ourmsg->data_len);
69452443Sjulian			if (ourmsg->data_len) {
69552443Sjulian				bcopy(ourmsg->data,
69652443Sjulian					neg->ac_name.data, ourmsg->data_len);
69752443Sjulian			}
69852419Sjulian			neg->ac_name_len = ourmsg->data_len;
69952419Sjulian			neg->pkt->pkt_header.ph.code = PADO_CODE;
70052419Sjulian			/*
70152419Sjulian			 * Wait for PADI packet coming from hook
70252419Sjulian			 */
70352419Sjulian			sp->state = PPPOE_PRIMED;
70452419Sjulian			break;
70552419Sjulian		default:
70652419Sjulian			LEAVE(EINVAL);
70752419Sjulian		}
70852419Sjulian		break;
70952419Sjulian	default:
71052419Sjulian		LEAVE(EINVAL);
71152419Sjulian	}
71252419Sjulian
71352419Sjulian	/* Take care of synchronous response, if any */
71452419Sjulian	if (rptr)
71552419Sjulian		*rptr = resp;
71652419Sjulian	else if (resp)
71752419Sjulian		FREE(resp, M_NETGRAPH);
71852419Sjulian
71952419Sjulian	/* Free the message and return */
72052419Sjulianquit:
72152419Sjulian	FREE(msg, M_NETGRAPH);
72252419Sjulian	return(error);
72352419Sjulian}
72452419Sjulian
72552443Sjulian/*
72652443Sjulian * Start a client into the first state. A separate function because
72752443Sjulian * it can be needed if the negotiation times out.
72852443Sjulian */
72952419Sjulianstatic void
73052419Sjulianpppoe_start(sessp sp)
73152419Sjulian{
73252419Sjulian	struct {
73352419Sjulian		struct pppoe_tag hdr;
73452419Sjulian		union	uniq	data;
73552419Sjulian	} uniqtag;
73652419Sjulian
73752419Sjulian	/*
73852419Sjulian	 * kick the state machine into starting up
73952419Sjulian	 */
74052443SjulianAAA
74152419Sjulian	sp->state = PPPOE_SINIT;
74252443Sjulian	/* reset the packet header to broadcast */
74352443Sjulian	sp->neg->pkt->pkt_header.eh = eh_prototype;
74452443Sjulian	sp->neg->pkt->pkt_header.ph.code = PADI_CODE;
74552419Sjulian	uniqtag.hdr.tag_type = PTT_HOST_UNIQ;
74652419Sjulian	uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data));
74752419Sjulian	uniqtag.data.pointer = sp;
74852419Sjulian	init_tags(sp);
74953154Sjulian	insert_tag(sp, &sp->neg->service.hdr);
75052419Sjulian	insert_tag(sp, &uniqtag.hdr);
75152419Sjulian	make_packet(sp);
75252419Sjulian	sendpacket(sp);
75352419Sjulian}
75452419Sjulian
75552419Sjulian/*
75652419Sjulian * Receive data, and do something with it.
75752419Sjulian * The caller will never free m or meta, so
75852419Sjulian * if we use up this data or abort we must free BOTH of these.
75952419Sjulian */
76052419Sjulianstatic int
76152562Sjulianng_pppoe_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
76252419Sjulian{
76352419Sjulian	node_p			node = hook->node;
76452419Sjulian	const priv_p		privp = node->private;
76552419Sjulian	sessp			sp = hook->private;
76652419Sjulian	struct pppoe_full_hdr	*wh;
76752419Sjulian	struct pppoe_hdr	*ph;
76852419Sjulian	int			error = 0;
76952419Sjulian	u_int16_t		session;
77052419Sjulian	u_int16_t		length;
77152419Sjulian	u_int8_t		code;
77253154Sjulian	struct pppoe_tag	*utag = NULL, *tag = NULL;
77352419Sjulian	hook_p 			sendhook;
77452419Sjulian	struct {
77552419Sjulian		struct pppoe_tag hdr;
77652419Sjulian		union	uniq	data;
77752419Sjulian	} uniqtag;
77852419Sjulian	negp			neg = NULL;
77952419Sjulian
78052443SjulianAAA
78152419Sjulian	if (hook->private == &privp->debug_hook) {
78252419Sjulian		/*
78352419Sjulian		 * Data from the debug hook gets sent without modification
78452419Sjulian		 * straight to the ethernet.
78552419Sjulian		 */
78652419Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
78752419Sjulian	 	privp->packets_out++;
78852419Sjulian	} else if (hook->private == &privp->ethernet_hook) {
78952419Sjulian		/*
79052419Sjulian		 * Incoming data.
79152419Sjulian		 * Dig out various fields from the packet.
79252419Sjulian		 * use them to decide where to send it.
79352419Sjulian		 */
79452419Sjulian
79552419Sjulian 		privp->packets_in++;
79652510Sjulian		if( m->m_len < sizeof(*wh)) {
79752510Sjulian			m = m_pullup(m, sizeof(*wh)); /* Checks length */
79852510Sjulian			if (m == NULL) {
79952510Sjulian				printf("couldn't m_pullup\n");
80052510Sjulian				LEAVE(ENOBUFS);
80152510Sjulian			}
80252419Sjulian		}
80352419Sjulian		wh = mtod(m, struct pppoe_full_hdr *);
80452419Sjulian		ph = &wh->ph;
80552419Sjulian		session = ntohs(wh->ph.sid);
80652419Sjulian		length = ntohs(wh->ph.length);
80752419Sjulian		code = wh->ph.code;
80852510Sjulian		switch(wh->eh.ether_type) {
80952419Sjulian		case	ETHERTYPE_PPPOE_DISC:
81052419Sjulian			/*
81153172Sjulian			 * We need to try to make sure that the tag area
81253172Sjulian			 * is contiguous, or we could wander off the end
81352419Sjulian			 * of a buffer and make a mess.
81452419Sjulian			 * (Linux wouldn't have this problem).
81552419Sjulian			 */
81652510Sjulian/*XXX fix this mess */
81752510Sjulian
81852510Sjulian			if (m->m_pkthdr.len <= MHLEN) {
81952510Sjulian				if( m->m_len < m->m_pkthdr.len) {
82052510Sjulian					m = m_pullup(m, m->m_pkthdr.len);
82152510Sjulian					if (m == NULL) {
82252510Sjulian						printf("couldn't m_pullup\n");
82352510Sjulian						LEAVE(ENOBUFS);
82452510Sjulian					}
82552510Sjulian				}
82652510Sjulian			}
82752510Sjulian			if (m->m_len != m->m_pkthdr.len) {
82852419Sjulian				/*
82952419Sjulian				 * It's not all in one piece.
83052419Sjulian				 * We need to do extra work.
83152419Sjulian				 */
83252419Sjulian				printf("packet fragmented\n");
83352448Sjulian				LEAVE(EMSGSIZE);
83452419Sjulian			 }
83552419Sjulian
83652419Sjulian			switch(code) {
83752419Sjulian			case	PADI_CODE:
83852419Sjulian				/*
83952419Sjulian				 * We are a server:
84052419Sjulian				 * Look for a hook with the required service
84152419Sjulian				 * and send the ENTIRE packet up there.
84252419Sjulian				 * It should come back to a new hook in
84352419Sjulian				 * PRIMED state. Look there for further
84452419Sjulian				 * processing.
84552419Sjulian				 */
84652419Sjulian				tag = get_tag(ph, PTT_SRV_NAME);
84752419Sjulian				if (tag == NULL) {
84852448Sjulian					printf("no service tag\n");
84952419Sjulian					LEAVE(ENETUNREACH);
85052419Sjulian				}
85152419Sjulian				sendhook = pppoe_match_svc(hook->node,
85252419Sjulian			    		tag->tag_data, ntohs(tag->tag_len));
85352419Sjulian				if (sendhook) {
85452419Sjulian					NG_SEND_DATA(error, sendhook, m, meta);
85552419Sjulian				} else {
85652448Sjulian					printf("no such service\n");
85752419Sjulian					LEAVE(ENETUNREACH);
85852419Sjulian				}
85952419Sjulian				break;
86052419Sjulian			case	PADO_CODE:
86152419Sjulian				/*
86252419Sjulian				 * We are a client:
86352419Sjulian				 * Use the host_uniq tag to find the
86452419Sjulian				 * hook this is in response to.
86552448Sjulian				 * Received #2, now send #3
86652419Sjulian				 * For now simply accept the first we receive.
86752419Sjulian				 */
86853154Sjulian				utag = get_tag(ph, PTT_HOST_UNIQ);
86953154Sjulian				if ((utag == NULL)
87053154Sjulian				|| (ntohs(utag->tag_len) != sizeof(sp))) {
87152448Sjulian					printf("no host unique field\n");
87252419Sjulian					LEAVE(ENETUNREACH);
87352419Sjulian				}
87452419Sjulian
87553154Sjulian				sendhook = pppoe_finduniq(node, utag);
87652419Sjulian				if (sendhook == NULL) {
87752448Sjulian					printf("no matching session\n");
87852419Sjulian					LEAVE(ENETUNREACH);
87952419Sjulian				}
88052419Sjulian
88152419Sjulian				/*
88252419Sjulian				 * Check the session is in the right state.
88352419Sjulian				 * It needs to be in PPPOE_SINIT.
88452419Sjulian				 */
88552419Sjulian				sp = sendhook->private;
88652419Sjulian				if (sp->state != PPPOE_SINIT) {
88752448Sjulian					printf("session in wrong state\n");
88852419Sjulian					LEAVE(ENETUNREACH);
88952419Sjulian				}
89052419Sjulian				neg = sp->neg;
89152419Sjulian				untimeout(pppoe_ticker, sendhook,
89252419Sjulian				    neg->timeout_handle);
89352419Sjulian
89452419Sjulian				/*
89552419Sjulian				 * This is the first time we hear
89652419Sjulian				 * from the server, so note it's
89752419Sjulian				 * unicast address, replacing the
89852419Sjulian				 * broadcast address .
89952419Sjulian				 */
90052419Sjulian				bcopy(wh->eh.ether_shost,
90152419Sjulian					neg->pkt->pkt_header.eh.ether_dhost,
90252419Sjulian					ETHER_ADDR_LEN);
90352419Sjulian				neg->timeout = 0;
90452419Sjulian				neg->pkt->pkt_header.ph.code = PADR_CODE;
90552419Sjulian				init_tags(sp);
90652448Sjulian				insert_tag(sp, &neg->service.hdr); /* Service */
90753154Sjulian				if ((tag = get_tag(ph, PTT_AC_COOKIE)))
90852448Sjulian					insert_tag(sp, tag); /* return cookie */
90953154Sjulian				if ((tag = get_tag(ph, PTT_AC_NAME)))
91053154Sjulian					insert_tag(sp, tag); /* return it */
91153154Sjulian				insert_tag(sp, utag);      /* Host Unique */
91252419Sjulian				scan_tags(sp, ph);
91352419Sjulian				make_packet(sp);
91452419Sjulian				sp->state = PPPOE_SREQ;
91552419Sjulian				sendpacket(sp);
91652419Sjulian				break;
91752419Sjulian			case	PADR_CODE:
91852419Sjulian
91952419Sjulian				/*
92052419Sjulian				 * We are a server:
92152419Sjulian				 * Use the ac_cookie tag to find the
92252419Sjulian				 * hook this is in response to.
92352419Sjulian				 */
92453154Sjulian				utag = get_tag(ph, PTT_AC_COOKIE);
92553154Sjulian				if ((utag == NULL)
92653154Sjulian				|| (ntohs(utag->tag_len) != sizeof(sp))) {
92752419Sjulian					LEAVE(ENETUNREACH);
92852419Sjulian				}
92952419Sjulian
93053154Sjulian				sendhook = pppoe_finduniq(node, utag);
93152419Sjulian				if (sendhook == NULL) {
93252419Sjulian					LEAVE(ENETUNREACH);
93352419Sjulian				}
93452419Sjulian
93552419Sjulian				/*
93652419Sjulian				 * Check the session is in the right state.
93752419Sjulian				 * It needs to be in PPPOE_SOFFER
93852419Sjulian				 * or PPPOE_NEWCONNECTED. If the latter,
93952419Sjulian				 * then this is a retry by the client.
94052419Sjulian				 * so be nice, and resend.
94152419Sjulian				 */
94252419Sjulian				sp = sendhook->private;
94352419Sjulian				if (sp->state == PPPOE_NEWCONNECTED) {
94452419Sjulian					/*
94552419Sjulian					 * Whoa! drop back to resend that
94652419Sjulian					 * PADS packet.
94752419Sjulian					 * We should still have a copy of it.
94852419Sjulian					 */
94952419Sjulian					sp->state = PPPOE_SOFFER;
95052419Sjulian				}
95152419Sjulian				if (sp->state != PPPOE_SOFFER) {
95252419Sjulian					LEAVE (ENETUNREACH);
95352419Sjulian					break;
95452419Sjulian				}
95552419Sjulian				neg = sp->neg;
95652419Sjulian				untimeout(pppoe_ticker, sendhook,
95752419Sjulian				    neg->timeout_handle);
95852419Sjulian				neg->pkt->pkt_header.ph.code = PADS_CODE;
95952419Sjulian				if (sp->Session_ID == 0)
96052419Sjulian					neg->pkt->pkt_header.ph.sid =
96152448Sjulian					    htons(sp->Session_ID
96252448Sjulian						= get_new_sid(node));
96352419Sjulian				neg->timeout = 0;
96452419Sjulian				/*
96552419Sjulian				 * start working out the tags to respond with.
96652419Sjulian				 */
96752419Sjulian				init_tags(sp);
96852419Sjulian				insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
96953172Sjulian				if ((tag = get_tag(ph, PTT_SRV_NAME)))
97053145Sjulian					insert_tag(sp, tag);/* return service */
97153154Sjulian				if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
97253145Sjulian					insert_tag(sp, tag); /* return it */
97353154Sjulian				insert_tag(sp, utag);	/* ac_cookie */
97452419Sjulian				scan_tags(sp, ph);
97552419Sjulian				make_packet(sp);
97653172Sjulian				sendpacket(sp);
97752419Sjulian				sp->state = PPPOE_NEWCONNECTED;
97852419Sjulian				/*
97952419Sjulian				 * Having sent the last Negotiation header,
98052419Sjulian				 * Set up the stored packet header to
98152419Sjulian				 * be correct for the actual session.
98252419Sjulian				 * But keep the negotialtion stuff
98352419Sjulian				 * around in case we need to resend this last
98452419Sjulian				 * packet. We'll discard it when we move
98552419Sjulian				 * from NEWCONNECTED to CONNECTED
98652419Sjulian				 */
98752419Sjulian				sp->pkt_hdr = neg->pkt->pkt_header;
98852419Sjulian				sp->pkt_hdr.eh.ether_type
98952419Sjulian						= ETHERTYPE_PPPOE_SESS;
99052419Sjulian				sp->pkt_hdr.ph.code = 0;
99152441Sjulian				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
99252419Sjulian				break;
99352419Sjulian			case	PADS_CODE:
99452419Sjulian				/*
99552419Sjulian				 * We are a client:
99652419Sjulian				 * Use the host_uniq tag to find the
99752419Sjulian				 * hook this is in response to.
99852419Sjulian				 * take the session ID and store it away.
99952419Sjulian				 * Also make sure the pre-made header is
100052419Sjulian				 * correct and set us into Session mode.
100152419Sjulian				 */
100253154Sjulian				utag = get_tag(ph, PTT_HOST_UNIQ);
100353154Sjulian				if ((utag == NULL)
100453154Sjulian				|| (ntohs(utag->tag_len) != sizeof(sp))) {
100552419Sjulian					LEAVE (ENETUNREACH);
100652419Sjulian					break;
100752419Sjulian				}
100853154Sjulian				sendhook = pppoe_finduniq(node, utag);
100952419Sjulian				if (sendhook == NULL) {
101052419Sjulian					LEAVE(ENETUNREACH);
101152419Sjulian				}
101252419Sjulian
101352419Sjulian				/*
101452419Sjulian				 * Check the session is in the right state.
101552419Sjulian				 * It needs to be in PPPOE_SREQ.
101652419Sjulian				 */
101752419Sjulian				sp = sendhook->private;
101852419Sjulian				if (sp->state != PPPOE_SREQ) {
101952419Sjulian					LEAVE(ENETUNREACH);
102052419Sjulian				}
102152419Sjulian				neg = sp->neg;
102252419Sjulian				untimeout(pppoe_ticker, sendhook,
102352419Sjulian				    neg->timeout_handle);
102452524Sjulian				neg->pkt->pkt_header.ph.sid = wh->ph.sid;
102552448Sjulian				sp->Session_ID = ntohs(wh->ph.sid);
102652419Sjulian				neg->timeout = 0;
102752419Sjulian				sp->state = PPPOE_CONNECTED;
102852419Sjulian				/*
102952419Sjulian				 * Now we have gone to Connected mode,
103052419Sjulian				 * Free all resources needed for
103152419Sjulian				 * negotiation.
103252419Sjulian				 * Keep a copy of the header we will be using.
103352419Sjulian				 */
103452419Sjulian				sp->pkt_hdr = neg->pkt->pkt_header;
103552419Sjulian				sp->pkt_hdr.eh.ether_type
103652419Sjulian						= ETHERTYPE_PPPOE_SESS;
103752419Sjulian				sp->pkt_hdr.ph.code = 0;
103852419Sjulian				m_freem(neg->m);
103952419Sjulian				FREE(sp->neg, M_NETGRAPH);
104052419Sjulian				sp->neg = NULL;
104152441Sjulian				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
104252419Sjulian				break;
104352419Sjulian			case	PADT_CODE:
104452419Sjulian				/*
104552419Sjulian				 * Send a 'close' message to the controlling
104652419Sjulian				 * process (the one that set us up);
104752419Sjulian				 * And then tear everything down.
104852419Sjulian				 *
104952419Sjulian				 * Find matching peer/session combination.
105052419Sjulian				 */
105152419Sjulian				sendhook = pppoe_findsession(node, wh);
105252419Sjulian				NG_FREE_DATA(m, meta); /* no longer needed */
105352419Sjulian				if (sendhook == NULL) {
105452419Sjulian					LEAVE(ENETUNREACH);
105552419Sjulian				}
105652419Sjulian				/* send message to creator */
105752419Sjulian				/* close hook */
105852441Sjulian				if (sendhook) {
105952441Sjulian					ng_destroy_hook(sendhook);
106052441Sjulian				}
106152419Sjulian				break;
106252419Sjulian			default:
106352419Sjulian				LEAVE(EPFNOSUPPORT);
106452419Sjulian			}
106552419Sjulian			break;
106652419Sjulian		case	ETHERTYPE_PPPOE_SESS:
106752419Sjulian			/*
106852419Sjulian			 * find matching peer/session combination.
106952419Sjulian			 */
107052419Sjulian			sendhook = pppoe_findsession(node, wh);
107152419Sjulian			if (sendhook == NULL) {
107252419Sjulian				LEAVE (ENETUNREACH);
107352419Sjulian				break;
107452419Sjulian			}
107552522Sjulian			sp = sendhook->private;
107652419Sjulian			m_adj(m, sizeof(*wh));
107752419Sjulian			if (m->m_pkthdr.len < length) {
107852419Sjulian				/* Packet too short, dump it */
107952419Sjulian				LEAVE(EMSGSIZE);
108052419Sjulian			}
108152523Sjulian
108253145Sjulian			/* Also need to trim excess at the end */
108352523Sjulian			if (m->m_pkthdr.len > length) {
108452523Sjulian				m_adj(m, -((int)(m->m_pkthdr.len - length)));
108552523Sjulian			}
108652419Sjulian			if ( sp->state != PPPOE_CONNECTED) {
108752419Sjulian				if (sp->state == PPPOE_NEWCONNECTED) {
108852419Sjulian					sp->state = PPPOE_CONNECTED;
108952419Sjulian					/*
109052419Sjulian					 * Now we have gone to Connected mode,
109152419Sjulian					 * Free all resources needed for
109252419Sjulian					 * negotiation.
109352419Sjulian					 */
109452419Sjulian					m_freem(sp->neg->m);
109552419Sjulian					FREE(sp->neg, M_NETGRAPH);
109652419Sjulian					sp->neg = NULL;
109752419Sjulian				} else {
109852419Sjulian					LEAVE (ENETUNREACH);
109952419Sjulian					break;
110052419Sjulian				}
110152419Sjulian			}
110252419Sjulian			NG_SEND_DATA( error, sendhook, m, meta);
110352419Sjulian			break;
110452419Sjulian		default:
110552522Sjulian			LEAVE(EPFNOSUPPORT);
110652419Sjulian		}
110752419Sjulian	} else {
110852419Sjulian		/*
110952419Sjulian		 * 	Not ethernet or debug hook..
111052419Sjulian		 *
111152419Sjulian		 * The packet has come in on a normal hook.
111252419Sjulian		 * We need to find out what kind of hook,
111352419Sjulian		 * So we can decide how to handle it.
111452419Sjulian		 * Check the hook's state.
111552419Sjulian		 */
111652419Sjulian		sp = hook->private;
111752419Sjulian		switch (sp->state) {
111852419Sjulian		case	PPPOE_NEWCONNECTED:
111952419Sjulian		case	PPPOE_CONNECTED: {
112052419Sjulian			struct pppoe_full_hdr *wh;
112152419Sjulian			/*
112252419Sjulian			 * Bang in a pre-made header, and set the length up
112352419Sjulian			 * to be correct. Then send it to the ethernet driver.
112452614Sjulian			 * But first correct the length.
112552419Sjulian			 */
112652614Sjulian			sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len));
112752419Sjulian			M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
112852419Sjulian			if (m == NULL) {
112952419Sjulian				LEAVE(ENOBUFS);
113052419Sjulian			}
113152419Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
113252419Sjulian			bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
113352419Sjulian			NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
113452419Sjulian			privp->packets_out++;
113552419Sjulian			break;
113652419Sjulian			}
113752419Sjulian		case	PPPOE_PRIMED:
113852419Sjulian			/*
113952419Sjulian			 * A PADI packet is being returned by the application
114052419Sjulian			 * that has set up this hook. This indicates that it
114152419Sjulian			 * wants us to offer service.
114252419Sjulian			 */
114352419Sjulian			neg = sp->neg;
114453172Sjulian			if (m->m_len < sizeof(*wh)) {
114553172Sjulian				m = m_pullup(m, sizeof(*wh));
114653172Sjulian				if (m == NULL) {
114753172Sjulian					LEAVE(ENOBUFS);
114853172Sjulian				}
114952419Sjulian			}
115052419Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
115152419Sjulian			ph = &wh->ph;
115252419Sjulian			session = ntohs(wh->ph.sid);
115352419Sjulian			length = ntohs(wh->ph.length);
115452419Sjulian			code = wh->ph.code;
115552443Sjulian			if ( code != PADI_CODE) {
115652443Sjulian				LEAVE(EINVAL);
115752443Sjulian			};
115852443Sjulian			untimeout(pppoe_ticker, hook,
115952443Sjulian				    neg->timeout_handle);
116052419Sjulian
116152419Sjulian			/*
116252419Sjulian			 * This is the first time we hear
116352419Sjulian			 * from the client, so note it's
116452419Sjulian			 * unicast address, replacing the
116553145Sjulian			 * broadcast address.
116652419Sjulian			 */
116752419Sjulian			bcopy(wh->eh.ether_shost,
116852419Sjulian				neg->pkt->pkt_header.eh.ether_dhost,
116952419Sjulian				ETHER_ADDR_LEN);
117052419Sjulian			sp->state = PPPOE_SOFFER;
117152419Sjulian			neg->timeout = 0;
117252419Sjulian			neg->pkt->pkt_header.ph.code = PADO_CODE;
117352419Sjulian
117452419Sjulian			/*
117552419Sjulian			 * start working out the tags to respond with.
117652419Sjulian			 */
117752419Sjulian			uniqtag.hdr.tag_type = PTT_AC_COOKIE;
117852419Sjulian			uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
117952419Sjulian			uniqtag.data.pointer = sp;
118052419Sjulian			init_tags(sp);
118152419Sjulian			insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
118253154Sjulian			if ((tag = get_tag(ph, PTT_SRV_NAME)))
118353154Sjulian				insert_tag(sp, tag);	  /* return service */
118453154Sjulian			if ((tag = get_tag(ph, PTT_HOST_UNIQ)))
118553145Sjulian				insert_tag(sp, tag); /* returned hostunique */
118653154Sjulian			insert_tag(sp, &uniqtag.hdr);
118752419Sjulian			/* XXX maybe put the tag in the session store */
118852419Sjulian			scan_tags(sp, ph);
118952419Sjulian			make_packet(sp);
119052419Sjulian			sendpacket(sp);
119152419Sjulian			break;
119252419Sjulian
119352419Sjulian		/*
119452419Sjulian		 * Packets coming from the hook make no sense
119552419Sjulian		 * to sessions in these states. Throw them away.
119652419Sjulian		 */
119752419Sjulian		case	PPPOE_SINIT:
119852419Sjulian		case	PPPOE_SREQ:
119952419Sjulian		case	PPPOE_SOFFER:
120052419Sjulian		case	PPPOE_SNONE:
120152419Sjulian		case	PPPOE_LISTENING:
120252419Sjulian		case	PPPOE_DEAD:
120352419Sjulian		default:
120452419Sjulian			LEAVE(ENETUNREACH);
120552419Sjulian		}
120652419Sjulian	}
120752419Sjulianquit:
120852419Sjulian	NG_FREE_DATA(m, meta);
120952419Sjulian	return error;
121052419Sjulian}
121152419Sjulian
121252419Sjulian/*
121352419Sjulian * Do local shutdown processing..
121452419Sjulian * If we are a persistant device, we might refuse to go away, and
121552419Sjulian * we'd only remove our links and reset ourself.
121652419Sjulian */
121752419Sjulianstatic int
121852562Sjulianng_pppoe_rmnode(node_p node)
121952419Sjulian{
122052419Sjulian	const priv_p privdata = node->private;
122152419Sjulian
122252443SjulianAAA
122352419Sjulian	node->flags |= NG_INVALID;
122452419Sjulian	ng_cutlinks(node);
122552419Sjulian	ng_unname(node);
122652419Sjulian	node->private = NULL;
122752419Sjulian	ng_unref(privdata->node);
122852419Sjulian	FREE(privdata, M_NETGRAPH);
122952419Sjulian	return (0);
123052419Sjulian}
123152419Sjulian
123252419Sjulian/*
123352419Sjulian * This is called once we've already connected a new hook to the other node.
123452419Sjulian * It gives us a chance to balk at the last minute.
123552419Sjulian */
123652419Sjulianstatic int
123752562Sjulianng_pppoe_connect(hook_p hook)
123852419Sjulian{
123952419Sjulian	/* be really amiable and just say "YUP that's OK by me! " */
124052419Sjulian	return (0);
124152419Sjulian}
124252419Sjulian
124352419Sjulian/*
124452419Sjulian * Hook disconnection
124552419Sjulian *
124652419Sjulian * Clean up all dangling links and infirmation about the session/hook.
124752419Sjulian * For this type, removal of the last link destroys the node
124852419Sjulian */
124952419Sjulianstatic int
125052562Sjulianng_pppoe_disconnect(hook_p hook)
125152419Sjulian{
125252419Sjulian	node_p node = hook->node;
125352419Sjulian	priv_p privp = node->private;
125452419Sjulian	sessp	sp;
125552563Sjulian	int 	hooks;
125652419Sjulian
125752443SjulianAAA
125852419Sjulian	if (hook->private == &privp->debug_hook) {
125952419Sjulian		privp->debug_hook = NULL;
126052419Sjulian	} else if (hook->private == &privp->ethernet_hook) {
126152419Sjulian		privp->ethernet_hook = NULL;
126252563Sjulian		ng_rmnode(node);
126352419Sjulian	} else {
126452419Sjulian		sp = hook->private;
126552441Sjulian		if (sp->state != PPPOE_SNONE ) {
126652441Sjulian			pppoe_send_event(sp, NGM_PPPOE_CLOSE);
126752441Sjulian		}
126852523Sjulian		if ((privp->ethernet_hook)
126952523Sjulian		&& ((sp->state == PPPOE_CONNECTED)
127052523Sjulian		 || (sp->state == PPPOE_NEWCONNECTED))) {
127152523Sjulian			struct mbuf *m;
127252523Sjulian			struct pppoe_full_hdr *wh;
127352523Sjulian			struct pppoe_tag *tag;
127452523Sjulian			int	msglen = strlen(SIGNOFF);
127552523Sjulian			void *dummy = NULL;
127652523Sjulian			int error = 0;
127752523Sjulian
127852523Sjulian			/* revert the stored header to DISC/PADT mode */
127952523Sjulian		 	wh = &sp->pkt_hdr;
128052523Sjulian			wh->ph.code = PADT_CODE;
128152523Sjulian			wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
128252523Sjulian
128352523Sjulian			/* generate a packet of that type */
128452523Sjulian			MGETHDR(m, M_DONTWAIT, MT_DATA);
128552523Sjulian			m->m_pkthdr.rcvif = NULL;
128652523Sjulian			m->m_pkthdr.len = m->m_len = sizeof(*wh);
128752523Sjulian			bcopy((caddr_t)wh, mtod(m, caddr_t), sizeof(*wh));
128852523Sjulian			/* Add a General error message and adjust sizes */
128952523Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
129052523Sjulian			tag = wh->ph.tag;
129152523Sjulian			tag->tag_type = PTT_GEN_ERR;
129252523Sjulian			tag->tag_len = htons((u_int16_t)msglen);
129352523Sjulian			strncpy(tag->tag_data, SIGNOFF, msglen);
129452523Sjulian			m->m_pkthdr.len = (m->m_len += sizeof(*tag) + msglen);
129552523Sjulian			wh->ph.length = htons(sizeof(*tag) + msglen);
129652524Sjulian			NG_SEND_DATA(error, privp->ethernet_hook, m, dummy);
129752523Sjulian		}
129852443Sjulian		if (sp->neg) {
129952443Sjulian			untimeout(pppoe_ticker, hook, sp->neg->timeout_handle);
130052443Sjulian			if (sp->neg->m)
130152443Sjulian				m_freem(sp->neg->m);
130252443Sjulian			FREE(sp->neg, M_NETGRAPH);
130352443Sjulian		}
130452419Sjulian		FREE(sp, M_NETGRAPH);
130552443Sjulian		hook->private = NULL;
130652564Sjulian		/* work out how many session hooks there are */
130752563Sjulian		/* Node goes away on last session hook removal */
130852563Sjulian		hooks = node->numhooks; /* this one already not counted */
130952563Sjulian		if (privp->ethernet_hook) hooks -= 1;
131052564Sjulian		if (privp->debug_hook) hooks -= 1;
131152563Sjulian		if (hooks == 0)
131252563Sjulian			ng_rmnode(node);
131352419Sjulian	}
131452419Sjulian	if (node->numhooks == 0)
131552419Sjulian		ng_rmnode(node);
131652419Sjulian	return (0);
131752419Sjulian}
131852419Sjulian
131952419Sjulian/*
132052419Sjulian * timeouts come here.
132152419Sjulian */
132252419Sjulianstatic void
132352419Sjulianpppoe_ticker(void *arg)
132452419Sjulian{
132552419Sjulian	int s = splnet();
132652419Sjulian	hook_p hook = arg;
132752419Sjulian	sessp	sp = hook->private;
132852419Sjulian	negp	neg = sp->neg;
132952419Sjulian	int	error = 0;
133052419Sjulian	struct mbuf *m0 = NULL;
133152419Sjulian	priv_p privp = hook->node->private;
133252419Sjulian	meta_p dummy = NULL;
133352419Sjulian
133452443SjulianAAA
133552419Sjulian	switch(sp->state) {
133652419Sjulian		/*
133752419Sjulian		 * resend the last packet, using an exponential backoff.
133852419Sjulian		 * After a period of time, stop growing the backoff,
133953145Sjulian		 * and either leave it, or revert to the start.
134052419Sjulian		 */
134152419Sjulian	case	PPPOE_SINIT:
134252419Sjulian	case	PPPOE_SREQ:
134352419Sjulian		/* timeouts on these produce resends */
134452419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
134552419Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
134652419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
134752419Sjulian					hook, neg->timeout * hz);
134852419Sjulian		if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
134952419Sjulian			if (sp->state == PPPOE_SREQ) {
135052419Sjulian				/* revert to SINIT mode */
135152441Sjulian				pppoe_start(sp);
135252419Sjulian			} else {
135352419Sjulian				neg->timeout = PPPOE_TIMEOUT_LIMIT;
135452419Sjulian			}
135552419Sjulian		}
135652419Sjulian		break;
135752419Sjulian	case	PPPOE_PRIMED:
135852419Sjulian	case	PPPOE_SOFFER:
135952419Sjulian		/* a timeout on these says "give up" */
136052419Sjulian		ng_destroy_hook(hook);
136152419Sjulian		break;
136252419Sjulian	default:
136352419Sjulian		/* timeouts have no meaning in other states */
136452419Sjulian		printf("pppoe: unexpected timeout\n");
136552419Sjulian	}
136652419Sjulian	splx(s);
136752419Sjulian}
136852419Sjulian
136952419Sjulian
137052419Sjulianstatic void
137152419Sjuliansendpacket(sessp sp)
137252419Sjulian{
137352419Sjulian	int	error = 0;
137452419Sjulian	struct mbuf *m0 = NULL;
137552419Sjulian	hook_p hook = sp->hook;
137652419Sjulian	negp	neg = sp->neg;
137752419Sjulian	priv_p	privp = hook->node->private;
137852419Sjulian	meta_p dummy = NULL;
137952419Sjulian
138052443SjulianAAA
138152419Sjulian	switch(sp->state) {
138252419Sjulian	case	PPPOE_LISTENING:
138352419Sjulian	case	PPPOE_DEAD:
138452419Sjulian	case	PPPOE_SNONE:
138552419Sjulian	case	PPPOE_NEWCONNECTED:
138652419Sjulian	case	PPPOE_CONNECTED:
138752448Sjulian		printf("pppoe: sendpacket: unexpected state\n");
138852419Sjulian		break;
138952419Sjulian
139052419Sjulian	case	PPPOE_PRIMED:
139152419Sjulian		/* No packet to send, but set up the timeout */
139252419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
139352419Sjulian					hook, PPPOE_OFFER_TIMEOUT * hz);
139452419Sjulian		break;
139552419Sjulian
139652419Sjulian	case	PPPOE_SOFFER:
139752419Sjulian		/*
139852419Sjulian		 * send the offer but if they don't respond
139952419Sjulian		 * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
140052419Sjulian		 */
140152419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
140252419Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
140352419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
140452419Sjulian					hook, PPPOE_OFFER_TIMEOUT * hz);
140552419Sjulian		break;
140652419Sjulian
140752419Sjulian	case	PPPOE_SINIT:
140852419Sjulian	case	PPPOE_SREQ:
140952419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
141052448Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
141152419Sjulian		neg->timeout_handle = timeout(pppoe_ticker, hook, hz);
141252419Sjulian		neg->timeout = 2;
141352419Sjulian		break;
141452419Sjulian
141552419Sjulian	default:
141652419Sjulian		error = EINVAL;
141752419Sjulian		printf("pppoe: timeout: bad state\n");
141852419Sjulian	}
141952419Sjulian	/* return (error); */
142052419Sjulian}
142152419Sjulian
142252419Sjulian/*
142352419Sjulian * Parse an incoming packet to see if any tags should be copied to the
142453145Sjulian * output packet. Don't do any tags that have been handled in the main
142553145Sjulian * state machine.
142652419Sjulian */
142752419Sjulianstatic struct pppoe_tag*
142852419Sjulianscan_tags(sessp	sp, struct pppoe_hdr* ph)
142952419Sjulian{
143052419Sjulian	char *end = (char *)next_tag(ph);
143152419Sjulian	char *ptn;
143252419Sjulian	struct pppoe_tag *pt = &ph->tag[0];
143352419Sjulian	/*
143452419Sjulian	 * Keep processing tags while a tag header will still fit.
143552419Sjulian	 */
143652443SjulianAAA
143752419Sjulian	while((char*)(pt + 1) <= end) {
143852419Sjulian		/*
143952419Sjulian		 * If the tag data would go past the end of the packet, abort.
144052419Sjulian		 */
144152419Sjulian		ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
144252419Sjulian		if(ptn > end)
144352419Sjulian			return NULL;
144452419Sjulian
144552419Sjulian		switch (pt->tag_type) {
144652419Sjulian		case	PTT_RELAY_SID:
144752419Sjulian			insert_tag(sp, pt);
144852419Sjulian			break;
144952419Sjulian		case	PTT_EOL:
145052419Sjulian			return NULL;
145152419Sjulian		case	PTT_SRV_NAME:
145252419Sjulian		case	PTT_AC_NAME:
145352419Sjulian		case	PTT_HOST_UNIQ:
145452419Sjulian		case	PTT_AC_COOKIE:
145552419Sjulian		case	PTT_VENDOR:
145652419Sjulian		case	PTT_SRV_ERR:
145752419Sjulian		case	PTT_SYS_ERR:
145852419Sjulian		case	PTT_GEN_ERR:
145952419Sjulian			break;
146052419Sjulian		}
146152419Sjulian		pt = (struct pppoe_tag*)ptn;
146252419Sjulian	}
146352419Sjulian	return NULL;
146452419Sjulian}
146552419Sjulian
146652441Sjulianstatic	int
146752441Sjulianpppoe_send_event(sessp sp, enum cmd cmdid)
146852441Sjulian{
146952441Sjulian	int error;
147052441Sjulian	struct ng_mesg *msg;
147152562Sjulian	struct ngpppoe_sts *sts;
147252441Sjulian
147352443SjulianAAA
147452441Sjulian	NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
147552562Sjulian			sizeof(struct ngpppoe_sts), M_NOWAIT);
147652562Sjulian	sts = (struct ngpppoe_sts *)msg->data;
147752441Sjulian	strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1);
147852441Sjulian	error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
147952441Sjulian	return (error);
148052441Sjulian}
1481