ng_pppoe.c revision 52524
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 52524 1999-10-26 11:26:23Z julian $
4052419Sjulian * $Whistle: ng_pppoe.c,v 1.7 1999/10/16 10:16:43 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
6852419Sjulianstatic int	ng_PPPoE_constructor(node_p *node);
6952419Sjulianstatic int	ng_PPPoE_rcvmsg(node_p node, struct ng_mesg *msg,
7052419Sjulian		  const char *retaddr, struct ng_mesg **resp);
7152419Sjulianstatic int	ng_PPPoE_rmnode(node_p node);
7252419Sjulianstatic int	ng_PPPoE_newhook(node_p node, hook_p hook, const char *name);
7352419Sjulianstatic int	ng_PPPoE_connect(hook_p hook);
7452419Sjulianstatic int	ng_PPPoE_rcvdata(hook_p hook, struct mbuf *m, meta_p meta);
7552419Sjulianstatic int	ng_PPPoE_disconnect(hook_p hook);
7652419Sjulian
7752419Sjulian/* Netgraph node type descriptor */
7852419Sjulianstatic struct ng_type typestruct = {
7952419Sjulian	NG_VERSION,
8052419Sjulian	NG_PPPOE_NODE_TYPE,
8152419Sjulian	NULL,
8252419Sjulian	ng_PPPoE_constructor,
8352419Sjulian	ng_PPPoE_rcvmsg,
8452419Sjulian	ng_PPPoE_rmnode,
8552419Sjulian	ng_PPPoE_newhook,
8652419Sjulian	NULL,
8752419Sjulian	ng_PPPoE_connect,
8852419Sjulian	ng_PPPoE_rcvdata,
8952419Sjulian	ng_PPPoE_rcvdata,
9052419Sjulian	ng_PPPoE_disconnect
9152419Sjulian};
9252419SjulianNETGRAPH_INIT(PPPoE, &typestruct);
9352419Sjulian
9452419Sjulian/*
9552419Sjulian * States for the session state machine.
9652419Sjulian * These have no meaning if there is no hook attached yet.
9752419Sjulian */
9852419Sjulianenum state {
9952419Sjulian    PPPOE_SNONE=0,	/* [both] Initial state */
10052419Sjulian    PPPOE_SINIT,	/* [Client] Sent discovery initiation */
10152419Sjulian    PPPOE_PRIMED,	/* [Server] Sent offer message */
10252419Sjulian    PPPOE_SOFFER,	/* [Server] Sent offer message */
10352419Sjulian    PPPOE_SREQ,		/* [Client] Sent a Request */
10452419Sjulian    PPPOE_LISTENING,	/* [Server] Listening for discover initiation msg */
10552419Sjulian    PPPOE_NEWCONNECTED,	/* [Both] Connection established, No data received */
10652419Sjulian    PPPOE_CONNECTED,	/* [Both] Connection established, Data received */
10752419Sjulian    PPPOE_DEAD		/* [Both] */
10852419Sjulian};
10952419Sjulian
11052419Sjulian#define NUMTAGS 20 /* number of tags we are set up to work with */
11152419Sjulian
11252419Sjulian/*
11352419Sjulian * Information we store for each hook on each node for negotiating the
11452419Sjulian * session. The mbuf and cluster are freed once negotiation has completed.
11552419Sjulian * The whole negotiation block is then discarded.
11652419Sjulian */
11752419Sjulian
11852419Sjulianstruct sess_neg {
11952419Sjulian	struct mbuf 		*m; /* holds cluster with last sent packet */
12052419Sjulian	union	packet		*pkt; /* points within the above cluster */
12152419Sjulian	struct callout_handle	timeout_handle;   /* see timeout(9) */
12252419Sjulian	u_int			timeout; /* 0,1,2,4,8,16 etc. seconds */
12352419Sjulian	u_int			numtags;
12452419Sjulian	struct pppoe_tag	*tags[NUMTAGS];
12552419Sjulian	u_int			service_len;
12652419Sjulian	u_int			ac_name_len;
12752419Sjulian
12852419Sjulian	struct datatag		service;
12952419Sjulian	struct datatag		ac_name;
13052419Sjulian};
13152419Sjuliantypedef struct sess_neg *negp;
13252419Sjulian
13352419Sjulian/*
13452419Sjulian * Session information that is needed after connection.
13552419Sjulian */
13652419Sjulianstruct session {
13752419Sjulian	hook_p  		hook;
13852419Sjulian	u_int16_t		Session_ID;
13952419Sjulian	struct session		*hash_next; /* not yet uesed */
14052419Sjulian	enum state		state;
14152419Sjulian	char			creator[NG_NODELEN + 1]; /* who to notify */
14252419Sjulian	struct pppoe_full_hdr	pkt_hdr;	/* used when connected */
14352419Sjulian	negp			neg;		/* used when negotiating */
14452419Sjulian};
14552419Sjuliantypedef struct session *sessp;
14652419Sjulian
14752419Sjulian/*
14852419Sjulian * Information we store for each node
14952419Sjulian */
15052419Sjulianstruct PPPOE {
15152419Sjulian	node_p		node;		/* back pointer to node */
15252419Sjulian	hook_p  	ethernet_hook;
15352419Sjulian	hook_p  	debug_hook;
15452419Sjulian	u_int   	packets_in;	/* packets in from ethernet */
15552419Sjulian	u_int   	packets_out;	/* packets out towards ethernet */
15652419Sjulian	u_int32_t	flags;
15752419Sjulian	/*struct session *buckets[HASH_SIZE];*/	/* not yet used */
15852419Sjulian};
15952419Sjuliantypedef struct PPPOE *priv_p;
16052419Sjulian
16152419Sjulianconst struct ether_header eh_prototype =
16252419Sjulian	{{0xff,0xff,0xff,0xff,0xff,0xff},
16352419Sjulian	 {0x00,0x00,0x00,0x00,0x00,0x00},
16452419Sjulian	 ETHERTYPE_PPPOE_DISC};
16552419Sjulian
16652419Sjulianunion uniq {
16752419Sjulian	char bytes[sizeof(void *)];
16852419Sjulian	void * pointer;
16952419Sjulian	};
17052419Sjulian
17152419Sjulian#define	LEAVE(x) do { error = x; goto quit; } while(0)
17252419Sjulianstatic void	pppoe_start(sessp sp);
17352419Sjulianstatic void	sendpacket(sessp sp);
17452419Sjulianstatic void	pppoe_ticker(void *arg);
17552419Sjulianstatic struct pppoe_tag* scan_tags(sessp	sp, struct pppoe_hdr* ph);
17652441Sjulianstatic	int	pppoe_send_event(sessp sp, enum cmd cmdid);
17752419Sjulian
17852419Sjulian/*************************************************************************
17952419Sjulian * Some basic utilities  from the Linux version with author's permission.*
18052419Sjulian * Author:	Michal Ostrowski <mostrows@styx.uwaterloo.ca>		 *
18152419Sjulian ************************************************************************/
18252419Sjulian
18352419Sjulian/*
18452419Sjulian * Generate a new session id
18552419Sjulian * XXX find out the freeBSD locking scheme.
18652419Sjulian */
18752419Sjulianstatic u_int16_t
18852419Sjulianget_new_sid(node_p node)
18952419Sjulian{
19052419Sjulian	static int pppoe_sid = 10;
19152419Sjulian	sessp sp;
19252419Sjulian	hook_p	hook;
19352419Sjulian	u_int16_t val;
19452419Sjulian	priv_p privp = node->private;
19552419Sjulian
19652443SjulianAAA
19752419Sjulianrestart:
19852419Sjulian	val = pppoe_sid++;
19952419Sjulian	/*
20052419Sjulian	 * Spec says 0xFFFF is reserved.
20152419Sjulian	 * Also don't use 0x0000
20252419Sjulian	 */
20352419Sjulian	if (val == 0xffff) {
20452419Sjulian		pppoe_sid = 20;
20552419Sjulian		goto restart;
20652419Sjulian	}
20752419Sjulian
20852419Sjulian	/* Check it isn't already in use */
20952419Sjulian	LIST_FOREACH(hook, &node->hooks, hooks) {
21052419Sjulian		/* don't check special hooks */
21152419Sjulian		if ((hook->private == &privp->debug_hook)
21252419Sjulian		||  (hook->private == &privp->ethernet_hook))
21352419Sjulian			continue;
21452419Sjulian		sp = hook->private;
21552419Sjulian		if (sp->Session_ID == val)
21652419Sjulian			goto restart;
21752419Sjulian	}
21852419Sjulian
21952419Sjulian	return val;
22052419Sjulian}
22152419Sjulian
22252419Sjulian
22352419Sjulian/*
22452419Sjulian * Return the location where the next tag can be put
22552419Sjulian */
22652419Sjulianstatic __inline struct pppoe_tag*
22752419Sjuliannext_tag(struct pppoe_hdr* ph)
22852419Sjulian{
22952419Sjulian	return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length));
23052419Sjulian}
23152419Sjulian
23252419Sjulian/*
23352419Sjulian * Look for a tag of a specific type
23452419Sjulian * Don't trust any length the other end says.
23552419Sjulian * but assume we already sanity checked ph->length.
23652419Sjulian */
23752419Sjulianstatic struct pppoe_tag*
23852419Sjulianget_tag(struct pppoe_hdr* ph, u_int16_t idx)
23952419Sjulian{
24052419Sjulian	char *end = (char *)next_tag(ph);
24152419Sjulian	char *ptn;
24252419Sjulian	struct pppoe_tag *pt = &ph->tag[0];
24352419Sjulian	/*
24452419Sjulian	 * Keep processing tags while a tag header will still fit.
24552419Sjulian	 */
24652443SjulianAAA
24752419Sjulian	while((char*)(pt + 1) <= end) {
24852419Sjulian	    /*
24952419Sjulian	     * If the tag data would go past the end of the packet, abort.
25052419Sjulian	     */
25152419Sjulian	    ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
25252419Sjulian	    if(ptn > end)
25352419Sjulian		return NULL;
25452419Sjulian
25552419Sjulian	    if(pt->tag_type == idx)
25652419Sjulian		return pt;
25752419Sjulian
25852419Sjulian	    pt = (struct pppoe_tag*)ptn;
25952419Sjulian	}
26052419Sjulian	return NULL;
26152419Sjulian}
26252419Sjulian
26352419Sjulian/**************************************************************************
26452419Sjulian * inlines to initialise or add tags to a session's tag list,
26552419Sjulian **************************************************************************/
26652419Sjulian/*
26752419Sjulian * Initialise the session's tag list
26852419Sjulian */
26952419Sjulianstatic void
27052419Sjulianinit_tags(sessp sp)
27152419Sjulian{
27252443SjulianAAA
27352419Sjulian	if(sp->neg == NULL) {
27452419Sjulian		printf("pppoe: asked to init NULL neg pointer\n");
27552419Sjulian		return;
27652419Sjulian	}
27752419Sjulian	sp->neg->numtags = 0;
27852419Sjulian}
27952419Sjulian
28052419Sjulianstatic void
28152419Sjulianinsert_tag(sessp sp, struct pppoe_tag *tp)
28252419Sjulian{
28352419Sjulian	int	i;
28452419Sjulian	negp neg;
28552419Sjulian
28652443SjulianAAA
28752419Sjulian	if((neg = sp->neg) == NULL) {
28852419Sjulian		printf("pppoe: asked to use NULL neg pointer\n");
28952419Sjulian		return;
29052419Sjulian	}
29152419Sjulian	if ((i = neg->numtags++) < NUMTAGS) {
29252419Sjulian		neg->tags[i] = tp;
29352419Sjulian	} else {
29452419Sjulian		printf("pppoe: asked to add too many tags to packet\n");
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
46652419Sjulianng_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
49952419Sjulianng_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
53652419Sjulianng_PPPoE_rcvmsg(node_p node,
53752419Sjulian	   struct ng_mesg *msg, const char *retaddr, struct ng_mesg **rptr)
53852419Sjulian{
53952419Sjulian	priv_p privp = node->private;
54052419Sjulian	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:
55552419Sjulian			ourmsg = (struct ngPPPoE_init_data *)msg->data;
55652419Sjulian			if (( sizeof(*ourmsg) > msg->header.arglen)
55752419Sjulian			|| ((sizeof(*ourmsg) + ourmsg->data_len)
55852419Sjulian			    > msg->header.arglen)) {
55952419Sjulian				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		    {
62952419Sjulian			struct ngPPPoEstat *stats;
63052419Sjulian
63152419Sjulian			NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
63252419Sjulian			if (!resp) {
63352419Sjulian				LEAVE(ENOMEM);
63452419Sjulian			}
63552419Sjulian			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);
74952419Sjulian	insert_tag(sp, &uniqtag.hdr);
75052419Sjulian	insert_tag(sp, &sp->neg->service.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
76152419Sjulianng_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;
77252419Sjulian	struct pppoe_tag	*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			/*
81152419Sjulian			 * We need to try make sure that the tag area
81252419Sjulian			 * is contiguous, or we could wander of 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				 */
86852419Sjulian				tag = get_tag(ph, PTT_HOST_UNIQ);
86952419Sjulian				if ((tag == NULL)
87052419Sjulian				|| (ntohs(tag->tag_len) != sizeof(sp))) {
87152448Sjulian					printf("no host unique field\n");
87252419Sjulian					LEAVE(ENETUNREACH);
87352419Sjulian				}
87452419Sjulian
87552419Sjulian				sendhook = pppoe_finduniq(node, tag);
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 */
90752419Sjulian				insert_tag(sp, tag);	      /* Host Unique */
90852419Sjulian				tag = get_tag(ph, PTT_AC_COOKIE);
90952448Sjulian				if (tag)
91052448Sjulian					insert_tag(sp, tag); /* return cookie */
91152522Sjulian				tag = get_tag(ph, PTT_AC_NAME);
91252522Sjulian				if (tag)
91352522Sjulian					insert_tag(sp, tag); /* return cookie */
91452419Sjulian				scan_tags(sp, ph);
91552419Sjulian				make_packet(sp);
91652419Sjulian				sp->state = PPPOE_SREQ;
91752419Sjulian				sendpacket(sp);
91852419Sjulian				break;
91952419Sjulian			case	PADR_CODE:
92052419Sjulian
92152419Sjulian				/*
92252419Sjulian				 * We are a server:
92352419Sjulian				 * Use the ac_cookie tag to find the
92452419Sjulian				 * hook this is in response to.
92552419Sjulian				 */
92652419Sjulian				tag = get_tag(ph, PTT_AC_COOKIE);
92752419Sjulian				if ((tag == NULL)
92852419Sjulian				|| (ntohs(tag->tag_len) != sizeof(sp))) {
92952419Sjulian					LEAVE(ENETUNREACH);
93052419Sjulian				}
93152419Sjulian
93252419Sjulian				sendhook = pppoe_finduniq(node, tag);
93352419Sjulian				if (sendhook == NULL) {
93452419Sjulian					LEAVE(ENETUNREACH);
93552419Sjulian				}
93652419Sjulian
93752419Sjulian				/*
93852419Sjulian				 * Check the session is in the right state.
93952419Sjulian				 * It needs to be in PPPOE_SOFFER
94052419Sjulian				 * or PPPOE_NEWCONNECTED. If the latter,
94152419Sjulian				 * then this is a retry by the client.
94252419Sjulian				 * so be nice, and resend.
94352419Sjulian				 */
94452419Sjulian				sp = sendhook->private;
94552419Sjulian				if (sp->state == PPPOE_NEWCONNECTED) {
94652419Sjulian					/*
94752419Sjulian					 * Whoa! drop back to resend that
94852419Sjulian					 * PADS packet.
94952419Sjulian					 * We should still have a copy of it.
95052419Sjulian					 */
95152419Sjulian					sp->state = PPPOE_SOFFER;
95252419Sjulian				}
95352419Sjulian				if (sp->state != PPPOE_SOFFER) {
95452419Sjulian					LEAVE (ENETUNREACH);
95552419Sjulian					break;
95652419Sjulian				}
95752419Sjulian				neg = sp->neg;
95852419Sjulian				untimeout(pppoe_ticker, sendhook,
95952419Sjulian				    neg->timeout_handle);
96052419Sjulian				neg->pkt->pkt_header.ph.code = PADS_CODE;
96152419Sjulian				if (sp->Session_ID == 0)
96252419Sjulian					neg->pkt->pkt_header.ph.sid =
96352448Sjulian					    htons(sp->Session_ID
96452448Sjulian						= get_new_sid(node));
96552419Sjulian				neg->timeout = 0;
96652419Sjulian				/*
96752419Sjulian				 * start working out the tags to respond with.
96852419Sjulian				 */
96952419Sjulian				init_tags(sp);
97052419Sjulian				insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
97152419Sjulian				insert_tag(sp, tag);	/* ac_cookie */
97252419Sjulian				tag = get_tag(ph, PTT_SRV_NAME);
97352419Sjulian				insert_tag(sp, tag);	/* returned service */
97452419Sjulian				tag = get_tag(ph, PTT_HOST_UNIQ);
97552419Sjulian				insert_tag(sp, tag);    /* returned hostuniq */
97652419Sjulian				scan_tags(sp, ph);
97752419Sjulian				make_packet(sp);
97852419Sjulian				sp->state = PPPOE_NEWCONNECTED;
97952419Sjulian				sendpacket(sp);
98052441Sjulian				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
98152419Sjulian				/*
98252419Sjulian				 * Having sent the last Negotiation header,
98352419Sjulian				 * Set up the stored packet header to
98452419Sjulian				 * be correct for the actual session.
98552419Sjulian				 * But keep the negotialtion stuff
98652419Sjulian				 * around in case we need to resend this last
98752419Sjulian				 * packet. We'll discard it when we move
98852419Sjulian				 * from NEWCONNECTED to CONNECTED
98952419Sjulian				 */
99052419Sjulian				sp->pkt_hdr = neg->pkt->pkt_header;
99152419Sjulian				sp->pkt_hdr.eh.ether_type
99252419Sjulian						= ETHERTYPE_PPPOE_SESS;
99352419Sjulian				sp->pkt_hdr.ph.code = 0;
99452441Sjulian				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
99552419Sjulian				break;
99652419Sjulian			case	PADS_CODE:
99752419Sjulian				/*
99852419Sjulian				 * We are a client:
99952419Sjulian				 * Use the host_uniq tag to find the
100052419Sjulian				 * hook this is in response to.
100152419Sjulian				 * take the session ID and store it away.
100252419Sjulian				 * Also make sure the pre-made header is
100352419Sjulian				 * correct and set us into Session mode.
100452419Sjulian				 */
100552419Sjulian				tag = get_tag(ph, PTT_HOST_UNIQ);
100652419Sjulian				if ((tag == NULL)
100752419Sjulian				|| (ntohs(tag->tag_len) != sizeof(sp))) {
100852419Sjulian					LEAVE (ENETUNREACH);
100952419Sjulian					break;
101052419Sjulian				}
101152419Sjulian
101252419Sjulian				sendhook = pppoe_finduniq(node, tag);
101352419Sjulian				if (sendhook == NULL) {
101452419Sjulian					LEAVE(ENETUNREACH);
101552419Sjulian				}
101652419Sjulian
101752419Sjulian				/*
101852419Sjulian				 * Check the session is in the right state.
101952419Sjulian				 * It needs to be in PPPOE_SREQ.
102052419Sjulian				 */
102152419Sjulian				sp = sendhook->private;
102252419Sjulian				if (sp->state != PPPOE_SREQ) {
102352419Sjulian					LEAVE(ENETUNREACH);
102452419Sjulian				}
102552419Sjulian				neg = sp->neg;
102652419Sjulian				untimeout(pppoe_ticker, sendhook,
102752419Sjulian				    neg->timeout_handle);
102852524Sjulian				neg->pkt->pkt_header.ph.sid = wh->ph.sid;
102952448Sjulian				sp->Session_ID = ntohs(wh->ph.sid);
103052419Sjulian				neg->timeout = 0;
103152419Sjulian				sp->state = PPPOE_CONNECTED;
103252419Sjulian				/*
103352419Sjulian				 * Now we have gone to Connected mode,
103452419Sjulian				 * Free all resources needed for
103552419Sjulian				 * negotiation.
103652419Sjulian				 * Keep a copy of the header we will be using.
103752419Sjulian				 */
103852419Sjulian				sp->pkt_hdr = neg->pkt->pkt_header;
103952419Sjulian				sp->pkt_hdr.eh.ether_type
104052419Sjulian						= ETHERTYPE_PPPOE_SESS;
104152419Sjulian				sp->pkt_hdr.ph.code = 0;
104252419Sjulian				m_freem(neg->m);
104352419Sjulian				FREE(sp->neg, M_NETGRAPH);
104452419Sjulian				sp->neg = NULL;
104552441Sjulian				pppoe_send_event(sp, NGM_PPPOE_SUCCESS);
104652419Sjulian				break;
104752419Sjulian			case	PADT_CODE:
104852419Sjulian				/*
104952419Sjulian				 * Send a 'close' message to the controlling
105052419Sjulian				 * process (the one that set us up);
105152419Sjulian				 * And then tear everything down.
105252419Sjulian				 *
105352419Sjulian				 * Find matching peer/session combination.
105452419Sjulian				 */
105552419Sjulian				sendhook = pppoe_findsession(node, wh);
105652419Sjulian				NG_FREE_DATA(m, meta); /* no longer needed */
105752419Sjulian				if (sendhook == NULL) {
105852419Sjulian					LEAVE(ENETUNREACH);
105952419Sjulian				}
106052419Sjulian				/* send message to creator */
106152419Sjulian				/* close hook */
106252441Sjulian				if (sendhook) {
106352441Sjulian					ng_destroy_hook(sendhook);
106452441Sjulian				}
106552419Sjulian				break;
106652419Sjulian			default:
106752419Sjulian				LEAVE(EPFNOSUPPORT);
106852419Sjulian			}
106952419Sjulian			break;
107052419Sjulian		case	ETHERTYPE_PPPOE_SESS:
107152419Sjulian			/*
107252419Sjulian			 * find matching peer/session combination.
107352419Sjulian			 */
107452419Sjulian			sendhook = pppoe_findsession(node, wh);
107552419Sjulian			if (sendhook == NULL) {
107652419Sjulian				LEAVE (ENETUNREACH);
107752419Sjulian				break;
107852419Sjulian			}
107952522Sjulian			sp = sendhook->private;
108052419Sjulian			m_adj(m, sizeof(*wh));
108152419Sjulian			if (m->m_pkthdr.len < length) {
108252419Sjulian				/* Packet too short, dump it */
108352419Sjulian				LEAVE(EMSGSIZE);
108452419Sjulian			}
108552523Sjulian
108652523Sjulian			if (m->m_pkthdr.len > length) {
108752523Sjulian				m_adj(m, -((int)(m->m_pkthdr.len - length)));
108852523Sjulian			}
108952419Sjulian			/* XXX also need to trim excess at end I should think */
109052419Sjulian			if ( sp->state != PPPOE_CONNECTED) {
109152419Sjulian				if (sp->state == PPPOE_NEWCONNECTED) {
109252419Sjulian					sp->state = PPPOE_CONNECTED;
109352419Sjulian					/*
109452419Sjulian					 * Now we have gone to Connected mode,
109552419Sjulian					 * Free all resources needed for
109652419Sjulian					 * negotiation.
109752419Sjulian					 */
109852419Sjulian					m_freem(sp->neg->m);
109952419Sjulian					FREE(sp->neg, M_NETGRAPH);
110052419Sjulian					sp->neg = NULL;
110152419Sjulian				} else {
110252419Sjulian					LEAVE (ENETUNREACH);
110352419Sjulian					break;
110452419Sjulian				}
110552419Sjulian			}
110652419Sjulian			NG_SEND_DATA( error, sendhook, m, meta);
110752419Sjulian			break;
110852419Sjulian		default:
110952522Sjulian			LEAVE(EPFNOSUPPORT);
111052419Sjulian		}
111152419Sjulian	} else {
111252419Sjulian		/*
111352419Sjulian		 * 	Not ethernet or debug hook..
111452419Sjulian		 *
111552419Sjulian		 * The packet has come in on a normal hook.
111652419Sjulian		 * We need to find out what kind of hook,
111752419Sjulian		 * So we can decide how to handle it.
111852419Sjulian		 * Check the hook's state.
111952419Sjulian		 */
112052419Sjulian		sp = hook->private;
112152419Sjulian		switch (sp->state) {
112252419Sjulian		case	PPPOE_NEWCONNECTED:
112352419Sjulian		case	PPPOE_CONNECTED: {
112452419Sjulian			struct pppoe_full_hdr *wh;
112552419Sjulian			/*
112652419Sjulian			 * Bang in a pre-made header, and set the length up
112752419Sjulian			 * to be correct. Then send it to the ethernet driver.
112852419Sjulian			 */
112952419Sjulian			M_PREPEND(m, sizeof(*wh), M_DONTWAIT);
113052419Sjulian			if (m == NULL) {
113152419Sjulian				LEAVE(ENOBUFS);
113252419Sjulian			}
113352419Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
113452419Sjulian			bcopy(&sp->pkt_hdr, wh, sizeof(*wh));
113552419Sjulian			wh->ph.length = htons((short)(m->m_pkthdr.len));
113652419Sjulian			NG_SEND_DATA( error, privp->ethernet_hook, m, meta);
113752419Sjulian			privp->packets_out++;
113852419Sjulian			break;
113952419Sjulian			}
114052419Sjulian		case	PPPOE_PRIMED:
114152419Sjulian			/*
114252419Sjulian			 * A PADI packet is being returned by the application
114352419Sjulian			 * that has set up this hook. This indicates that it
114452419Sjulian			 * wants us to offer service.
114552419Sjulian			 */
114652419Sjulian			neg = sp->neg;
114752419Sjulian			m_pullup(m, sizeof(*wh)); /* Checks length */
114852419Sjulian			if (m == NULL) {
114952419Sjulian				LEAVE(ENOBUFS);
115052419Sjulian			}
115152419Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
115252419Sjulian			ph = &wh->ph;
115352419Sjulian			session = ntohs(wh->ph.sid);
115452419Sjulian			length = ntohs(wh->ph.length);
115552419Sjulian			code = wh->ph.code;
115652443Sjulian			if ( code != PADI_CODE) {
115752443Sjulian				LEAVE(EINVAL);
115852443Sjulian			};
115952443Sjulian			untimeout(pppoe_ticker, hook,
116052443Sjulian				    neg->timeout_handle);
116152419Sjulian
116252419Sjulian			/*
116352419Sjulian			 * This is the first time we hear
116452419Sjulian			 * from the client, so note it's
116552419Sjulian			 * unicast address, replacing the
116652419Sjulian			 * broadcast address .
116752419Sjulian			 */
116852419Sjulian			bcopy(wh->eh.ether_shost,
116952419Sjulian				neg->pkt->pkt_header.eh.ether_dhost,
117052419Sjulian				ETHER_ADDR_LEN);
117152419Sjulian			sp->state = PPPOE_SOFFER;
117252419Sjulian			neg->timeout = 0;
117352419Sjulian			neg->pkt->pkt_header.ph.code = PADO_CODE;
117452419Sjulian
117552419Sjulian			/*
117652419Sjulian			 * start working out the tags to respond with.
117752419Sjulian			 */
117852419Sjulian			uniqtag.hdr.tag_type = PTT_AC_COOKIE;
117952419Sjulian			uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp));
118052419Sjulian			uniqtag.data.pointer = sp;
118152419Sjulian			init_tags(sp);
118252419Sjulian			insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */
118352419Sjulian			insert_tag(sp, tag);	      /* returned hostunique */
118452419Sjulian			insert_tag(sp, &uniqtag.hdr);      /* AC cookie */
118552419Sjulian			tag = get_tag(ph, PTT_SRV_NAME);
118652419Sjulian			insert_tag(sp, tag);	      /* returned service */
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
121852419Sjulianng_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
123752419Sjulianng_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
125052419Sjulianng_PPPoE_disconnect(hook_p hook)
125152419Sjulian{
125252419Sjulian	node_p node = hook->node;
125352419Sjulian	priv_p privp = node->private;
125452419Sjulian	sessp	sp;
125552419Sjulian
125652443SjulianAAA
125752419Sjulian	if (hook->private == &privp->debug_hook) {
125852419Sjulian		privp->debug_hook = NULL;
125952419Sjulian	} else if (hook->private == &privp->ethernet_hook) {
126052419Sjulian		privp->ethernet_hook = NULL;
126152419Sjulian	} else {
126252419Sjulian		sp = hook->private;
126352441Sjulian		if (sp->state != PPPOE_SNONE ) {
126452441Sjulian			pppoe_send_event(sp, NGM_PPPOE_CLOSE);
126552441Sjulian		}
126652523Sjulian		if ((privp->ethernet_hook)
126752523Sjulian		&& ((sp->state == PPPOE_CONNECTED)
126852523Sjulian		 || (sp->state == PPPOE_NEWCONNECTED))) {
126952523Sjulian			struct mbuf *m;
127052523Sjulian			struct pppoe_full_hdr *wh;
127152523Sjulian			struct pppoe_tag *tag;
127252523Sjulian			int	msglen = strlen(SIGNOFF);
127352523Sjulian			void *dummy = NULL;
127452523Sjulian			int error = 0;
127552523Sjulian
127652523Sjulian			/* revert the stored header to DISC/PADT mode */
127752523Sjulian		 	wh = &sp->pkt_hdr;
127852523Sjulian			wh->ph.code = PADT_CODE;
127952523Sjulian			wh->eh.ether_type = ETHERTYPE_PPPOE_DISC;
128052523Sjulian
128152523Sjulian			/* generate a packet of that type */
128252523Sjulian			MGETHDR(m, M_DONTWAIT, MT_DATA);
128352523Sjulian			m->m_pkthdr.rcvif = NULL;
128452523Sjulian			m->m_pkthdr.len = m->m_len = sizeof(*wh);
128552523Sjulian			bcopy((caddr_t)wh, mtod(m, caddr_t), sizeof(*wh));
128652523Sjulian			/* Add a General error message and adjust sizes */
128752523Sjulian			wh = mtod(m, struct pppoe_full_hdr *);
128852523Sjulian			tag = wh->ph.tag;
128952523Sjulian			tag->tag_type = PTT_GEN_ERR;
129052523Sjulian			tag->tag_len = htons((u_int16_t)msglen);
129152523Sjulian			strncpy(tag->tag_data, SIGNOFF, msglen);
129252523Sjulian			m->m_pkthdr.len = (m->m_len += sizeof(*tag) + msglen);
129352523Sjulian			wh->ph.length = htons(sizeof(*tag) + msglen);
129452524Sjulian			NG_SEND_DATA(error, privp->ethernet_hook, m, dummy);
129552523Sjulian		}
129652443Sjulian		if (sp->neg) {
129752443Sjulian			untimeout(pppoe_ticker, hook, sp->neg->timeout_handle);
129852443Sjulian			if (sp->neg->m)
129952443Sjulian				m_freem(sp->neg->m);
130052443Sjulian			FREE(sp->neg, M_NETGRAPH);
130152443Sjulian		}
130252419Sjulian		FREE(sp, M_NETGRAPH);
130352443Sjulian		hook->private = NULL;
130452419Sjulian	}
130552419Sjulian	if (node->numhooks == 0)
130652419Sjulian		ng_rmnode(node);
130752419Sjulian	return (0);
130852419Sjulian}
130952419Sjulian
131052419Sjulian/*
131152419Sjulian * timeouts come here.
131252419Sjulian */
131352419Sjulianstatic void
131452419Sjulianpppoe_ticker(void *arg)
131552419Sjulian{
131652419Sjulian	int s = splnet();
131752419Sjulian	hook_p hook = arg;
131852419Sjulian	sessp	sp = hook->private;
131952419Sjulian	negp	neg = sp->neg;
132052419Sjulian	int	error = 0;
132152419Sjulian	struct mbuf *m0 = NULL;
132252419Sjulian	priv_p privp = hook->node->private;
132352419Sjulian	meta_p dummy = NULL;
132452419Sjulian
132552443SjulianAAA
132652419Sjulian	switch(sp->state) {
132752419Sjulian		/*
132852419Sjulian		 * resend the last packet, using an exponential backoff.
132952419Sjulian		 * After a period of time, stop growing the backoff,
133052419Sjulian		 * and either leave it, or reverst to the start.
133152419Sjulian		 */
133252419Sjulian	case	PPPOE_SINIT:
133352419Sjulian	case	PPPOE_SREQ:
133452419Sjulian		/* timeouts on these produce resends */
133552419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
133652419Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
133752419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
133852419Sjulian					hook, neg->timeout * hz);
133952419Sjulian		if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) {
134052419Sjulian			if (sp->state == PPPOE_SREQ) {
134152419Sjulian				/* revert to SINIT mode */
134252441Sjulian				pppoe_start(sp);
134352419Sjulian			} else {
134452419Sjulian				neg->timeout = PPPOE_TIMEOUT_LIMIT;
134552419Sjulian			}
134652419Sjulian		}
134752419Sjulian		break;
134852419Sjulian	case	PPPOE_PRIMED:
134952419Sjulian	case	PPPOE_SOFFER:
135052419Sjulian		/* a timeout on these says "give up" */
135152419Sjulian		ng_destroy_hook(hook);
135252419Sjulian		break;
135352419Sjulian	default:
135452419Sjulian		/* timeouts have no meaning in other states */
135552419Sjulian		printf("pppoe: unexpected timeout\n");
135652419Sjulian	}
135752419Sjulian	splx(s);
135852419Sjulian}
135952419Sjulian
136052419Sjulian
136152419Sjulianstatic void
136252419Sjuliansendpacket(sessp sp)
136352419Sjulian{
136452419Sjulian	int	error = 0;
136552419Sjulian	struct mbuf *m0 = NULL;
136652419Sjulian	hook_p hook = sp->hook;
136752419Sjulian	negp	neg = sp->neg;
136852419Sjulian	priv_p	privp = hook->node->private;
136952419Sjulian	meta_p dummy = NULL;
137052419Sjulian
137152443SjulianAAA
137252419Sjulian	switch(sp->state) {
137352419Sjulian	case	PPPOE_LISTENING:
137452419Sjulian	case	PPPOE_DEAD:
137552419Sjulian	case	PPPOE_SNONE:
137652419Sjulian	case	PPPOE_NEWCONNECTED:
137752419Sjulian	case	PPPOE_CONNECTED:
137852448Sjulian		printf("pppoe: sendpacket: unexpected state\n");
137952419Sjulian		break;
138052419Sjulian
138152419Sjulian	case	PPPOE_PRIMED:
138252419Sjulian		/* No packet to send, but set up the timeout */
138352419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
138452419Sjulian					hook, PPPOE_OFFER_TIMEOUT * hz);
138552419Sjulian		break;
138652419Sjulian
138752419Sjulian	case	PPPOE_SOFFER:
138852419Sjulian		/*
138952419Sjulian		 * send the offer but if they don't respond
139052419Sjulian		 * in PPPOE_OFFER_TIMEOUT seconds, forget about it.
139152419Sjulian		 */
139252419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
139352419Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
139452419Sjulian		neg->timeout_handle = timeout(pppoe_ticker,
139552419Sjulian					hook, PPPOE_OFFER_TIMEOUT * hz);
139652419Sjulian		break;
139752419Sjulian
139852419Sjulian	case	PPPOE_SINIT:
139952419Sjulian	case	PPPOE_SREQ:
140052419Sjulian		m0 = m_copypacket(sp->neg->m, M_DONTWAIT);
140152448Sjulian		NG_SEND_DATA( error, privp->ethernet_hook, m0, dummy);
140252419Sjulian		neg->timeout_handle = timeout(pppoe_ticker, hook, hz);
140352419Sjulian		neg->timeout = 2;
140452419Sjulian		break;
140552419Sjulian
140652419Sjulian	default:
140752419Sjulian		error = EINVAL;
140852419Sjulian		printf("pppoe: timeout: bad state\n");
140952419Sjulian	}
141052419Sjulian	/* return (error); */
141152419Sjulian}
141252419Sjulian
141352419Sjulian/*
141452419Sjulian * Parse an incoming packet to see if any tags should be copied to the
141552419Sjulian * output packet. DOon't do any tags that are likely to have been
141652419Sjulian * handles a the main state machine.
141752419Sjulian */
141852419Sjulianstatic struct pppoe_tag*
141952419Sjulianscan_tags(sessp	sp, struct pppoe_hdr* ph)
142052419Sjulian{
142152419Sjulian	char *end = (char *)next_tag(ph);
142252419Sjulian	char *ptn;
142352419Sjulian	struct pppoe_tag *pt = &ph->tag[0];
142452419Sjulian	/*
142552419Sjulian	 * Keep processing tags while a tag header will still fit.
142652419Sjulian	 */
142752443SjulianAAA
142852419Sjulian	while((char*)(pt + 1) <= end) {
142952419Sjulian		/*
143052419Sjulian		 * If the tag data would go past the end of the packet, abort.
143152419Sjulian		 */
143252419Sjulian		ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len));
143352419Sjulian		if(ptn > end)
143452419Sjulian			return NULL;
143552419Sjulian
143652419Sjulian		switch (pt->tag_type) {
143752419Sjulian		case	PTT_RELAY_SID:
143852419Sjulian			insert_tag(sp, pt);
143952419Sjulian			break;
144052419Sjulian		case	PTT_EOL:
144152419Sjulian			return NULL;
144252419Sjulian		case	PTT_SRV_NAME:
144352419Sjulian		case	PTT_AC_NAME:
144452419Sjulian		case	PTT_HOST_UNIQ:
144552419Sjulian		case	PTT_AC_COOKIE:
144652419Sjulian		case	PTT_VENDOR:
144752419Sjulian		case	PTT_SRV_ERR:
144852419Sjulian		case	PTT_SYS_ERR:
144952419Sjulian		case	PTT_GEN_ERR:
145052419Sjulian			break;
145152419Sjulian		}
145252419Sjulian		pt = (struct pppoe_tag*)ptn;
145352419Sjulian	}
145452419Sjulian	return NULL;
145552419Sjulian}
145652419Sjulian
145752441Sjulianstatic	int
145852441Sjulianpppoe_send_event(sessp sp, enum cmd cmdid)
145952441Sjulian{
146052441Sjulian	int error;
146152441Sjulian	struct ng_mesg *msg;
146252441Sjulian	struct ngPPPoE_sts *sts;
146352441Sjulian
146452443SjulianAAA
146552441Sjulian	NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid,
146652441Sjulian			sizeof(struct ngPPPoE_sts), M_NOWAIT);
146752441Sjulian	sts = (struct ngPPPoE_sts *)msg->data;
146852441Sjulian	strncpy(sts->hook, sp->hook->name, NG_HOOKLEN + 1);
146952441Sjulian	error = ng_send_msg(sp->hook->node, msg, sp->creator, NULL);
147052441Sjulian	return (error);
147152441Sjulian}
1472