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