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