ng_pppoe.c revision 70784
153405Sarchie 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 * 3767506Sjulian * Author: Julian Elischer <julian@freebsd.org> 3852419Sjulian * 3952419Sjulian * $FreeBSD: head/sys/netgraph/ng_pppoe.c 70784 2001-01-08 05:34:06Z 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 <net/ethernet.h> 5752419Sjulian 5852419Sjulian#include <netgraph/ng_message.h> 5952419Sjulian#include <netgraph/netgraph.h> 6068031Sbrian#include <netgraph/ng_parse.h> 6152419Sjulian#include <netgraph/ng_pppoe.h> 6252419Sjulian 6353405Sarchie#define SIGNOFF "session closed" 6468031Sbrian#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 6553405Sarchie 6652419Sjulian/* 6752419Sjulian * This section contains the netgraph method declarations for the 6852419Sjulian * sample node. These methods define the netgraph 'type'. 6952419Sjulian */ 7052419Sjulian 7152752Sjulianstatic ng_constructor_t ng_pppoe_constructor; 7252752Sjulianstatic ng_rcvmsg_t ng_pppoe_rcvmsg; 7370700Sjulianstatic ng_shutdown_t ng_pppoe_shutdown; 7452752Sjulianstatic ng_newhook_t ng_pppoe_newhook; 7552752Sjulianstatic ng_connect_t ng_pppoe_connect; 7652752Sjulianstatic ng_rcvdata_t ng_pppoe_rcvdata; 7752752Sjulianstatic ng_disconnect_t ng_pppoe_disconnect; 7852419Sjulian 7968031Sbrian/* Parse type for struct ngpppoe_init_data */ 8068845Sbrianstatic const struct ng_parse_struct_info ngpppoe_init_data_type_info 8168031Sbrian = NG_PPPOE_INIT_DATA_TYPE_INFO; 8268845Sbrianstatic const struct ng_parse_type ngpppoe_init_data_state_type = { 8368031Sbrian &ng_parse_struct_type, 8468845Sbrian &ngpppoe_init_data_type_info 8568031Sbrian}; 8668031Sbrian 8768031Sbrian/* Parse type for struct ngpppoe_sts */ 8868031Sbrianstatic const struct ng_parse_struct_info ng_pppoe_sts_type_info 8968031Sbrian = NG_PPPOE_STS_TYPE_INFO; 9068031Sbrianstatic const struct ng_parse_type ng_pppoe_sts_state_type = { 9168031Sbrian &ng_parse_struct_type, 9268031Sbrian &ng_pppoe_sts_type_info 9368031Sbrian}; 9468031Sbrian 9568031Sbrian/* List of commands and how to convert arguments to/from ASCII */ 9668031Sbrianstatic const struct ng_cmdlist ng_pppoe_cmds[] = { 9768031Sbrian { 9868031Sbrian NGM_PPPOE_COOKIE, 9968031Sbrian NGM_PPPOE_CONNECT, 10068031Sbrian "pppoe_connect", 10168845Sbrian &ngpppoe_init_data_state_type, 10268031Sbrian NULL 10368031Sbrian }, 10468031Sbrian { 10568031Sbrian NGM_PPPOE_COOKIE, 10668031Sbrian NGM_PPPOE_LISTEN, 10768031Sbrian "pppoe_listen", 10868845Sbrian &ngpppoe_init_data_state_type, 10968031Sbrian NULL 11068031Sbrian }, 11168031Sbrian { 11268031Sbrian NGM_PPPOE_COOKIE, 11368031Sbrian NGM_PPPOE_OFFER, 11468031Sbrian "pppoe_offer", 11568845Sbrian &ngpppoe_init_data_state_type, 11668031Sbrian NULL 11768031Sbrian }, 11868031Sbrian { 11968031Sbrian NGM_PPPOE_COOKIE, 12069922Sjulian NGM_PPPOE_SERVICE, 12169922Sjulian "pppoe_service", 12269922Sjulian &ngpppoe_init_data_state_type, 12369922Sjulian NULL 12469922Sjulian }, 12569922Sjulian { 12669922Sjulian NGM_PPPOE_COOKIE, 12768031Sbrian NGM_PPPOE_SUCCESS, 12868031Sbrian "pppoe_success", 12968031Sbrian &ng_pppoe_sts_state_type, 13068031Sbrian NULL 13168031Sbrian }, 13268031Sbrian { 13368031Sbrian NGM_PPPOE_COOKIE, 13468031Sbrian NGM_PPPOE_FAIL, 13568031Sbrian "pppoe_fail", 13668031Sbrian &ng_pppoe_sts_state_type, 13768031Sbrian NULL 13868031Sbrian }, 13968031Sbrian { 14068031Sbrian NGM_PPPOE_COOKIE, 14168031Sbrian NGM_PPPOE_CLOSE, 14268031Sbrian "pppoe_close", 14368031Sbrian &ng_pppoe_sts_state_type, 14468031Sbrian NULL 14568031Sbrian }, 14668031Sbrian { 0 } 14768031Sbrian}; 14868031Sbrian 14952419Sjulian/* Netgraph node type descriptor */ 15052419Sjulianstatic struct ng_type typestruct = { 15170159Sjulian NG_ABI_VERSION, 15252419Sjulian NG_PPPOE_NODE_TYPE, 15352419Sjulian NULL, 15452562Sjulian ng_pppoe_constructor, 15552562Sjulian ng_pppoe_rcvmsg, 15670700Sjulian ng_pppoe_shutdown, 15752562Sjulian ng_pppoe_newhook, 15852419Sjulian NULL, 15952562Sjulian ng_pppoe_connect, 16052562Sjulian ng_pppoe_rcvdata, 16153913Sarchie ng_pppoe_disconnect, 16268031Sbrian ng_pppoe_cmds 16352419Sjulian}; 16452562SjulianNETGRAPH_INIT(pppoe, &typestruct); 16552419Sjulian 16652419Sjulian/* 16752419Sjulian * States for the session state machine. 16852419Sjulian * These have no meaning if there is no hook attached yet. 16952419Sjulian */ 17052419Sjulianenum state { 17152419Sjulian PPPOE_SNONE=0, /* [both] Initial state */ 17253498Sjulian PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 17352419Sjulian PPPOE_SINIT, /* [Client] Sent discovery initiation */ 17453498Sjulian PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 17553498Sjulian PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 17652419Sjulian PPPOE_SREQ, /* [Client] Sent a Request */ 17753498Sjulian PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 17852419Sjulian PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 17952419Sjulian PPPOE_DEAD /* [Both] */ 18052419Sjulian}; 18152419Sjulian 18252419Sjulian#define NUMTAGS 20 /* number of tags we are set up to work with */ 18352419Sjulian 18452419Sjulian/* 18552419Sjulian * Information we store for each hook on each node for negotiating the 18652419Sjulian * session. The mbuf and cluster are freed once negotiation has completed. 18752419Sjulian * The whole negotiation block is then discarded. 18852419Sjulian */ 18952419Sjulian 19052419Sjulianstruct sess_neg { 19152419Sjulian struct mbuf *m; /* holds cluster with last sent packet */ 19252419Sjulian union packet *pkt; /* points within the above cluster */ 19352419Sjulian struct callout_handle timeout_handle; /* see timeout(9) */ 19452419Sjulian u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 19552419Sjulian u_int numtags; 19652419Sjulian struct pppoe_tag *tags[NUMTAGS]; 19752419Sjulian u_int service_len; 19852419Sjulian u_int ac_name_len; 19952419Sjulian 20052419Sjulian struct datatag service; 20152419Sjulian struct datatag ac_name; 20252419Sjulian}; 20352419Sjuliantypedef struct sess_neg *negp; 20452419Sjulian 20552419Sjulian/* 20652419Sjulian * Session information that is needed after connection. 20752419Sjulian */ 20866052Sarchiestruct sess_con { 20952419Sjulian hook_p hook; 21052419Sjulian u_int16_t Session_ID; 21152419Sjulian enum state state; 21270700Sjulian ng_ID_t creator; /* who to notify */ 21352419Sjulian struct pppoe_full_hdr pkt_hdr; /* used when connected */ 21452419Sjulian negp neg; /* used when negotiating */ 21566052Sarchie /*struct sess_con *hash_next;*/ /* not yet used */ 21652419Sjulian}; 21766052Sarchietypedef struct sess_con *sessp; 21852419Sjulian 21952419Sjulian/* 22052419Sjulian * Information we store for each node 22152419Sjulian */ 22252419Sjulianstruct PPPOE { 22352419Sjulian node_p node; /* back pointer to node */ 22452419Sjulian hook_p ethernet_hook; 22552419Sjulian hook_p debug_hook; 22652419Sjulian u_int packets_in; /* packets in from ethernet */ 22752419Sjulian u_int packets_out; /* packets out towards ethernet */ 22852419Sjulian u_int32_t flags; 22966052Sarchie /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ 23052419Sjulian}; 23152419Sjuliantypedef struct PPPOE *priv_p; 23252419Sjulian 23352419Sjulianconst struct ether_header eh_prototype = 23452419Sjulian {{0xff,0xff,0xff,0xff,0xff,0xff}, 23552419Sjulian {0x00,0x00,0x00,0x00,0x00,0x00}, 23652419Sjulian ETHERTYPE_PPPOE_DISC}; 23752419Sjulian 23852419Sjulianunion uniq { 23952419Sjulian char bytes[sizeof(void *)]; 24052419Sjulian void * pointer; 24152419Sjulian }; 24252419Sjulian 24352419Sjulian#define LEAVE(x) do { error = x; goto quit; } while(0) 24452419Sjulianstatic void pppoe_start(sessp sp); 24552419Sjulianstatic void sendpacket(sessp sp); 24652419Sjulianstatic void pppoe_ticker(void *arg); 24752419Sjulianstatic struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); 24852441Sjulianstatic int pppoe_send_event(sessp sp, enum cmd cmdid); 24952419Sjulian 25052419Sjulian/************************************************************************* 25152419Sjulian * Some basic utilities from the Linux version with author's permission.* 25252419Sjulian * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 25352419Sjulian ************************************************************************/ 25452419Sjulian 25552419Sjulian/* 25652419Sjulian * Generate a new session id 25753145Sjulian * XXX find out the FreeBSD locking scheme. 25852419Sjulian */ 25952419Sjulianstatic u_int16_t 26052419Sjulianget_new_sid(node_p node) 26152419Sjulian{ 26252419Sjulian static int pppoe_sid = 10; 26352419Sjulian sessp sp; 26452419Sjulian hook_p hook; 26552419Sjulian u_int16_t val; 26670784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 26752419Sjulian 26852443SjulianAAA 26952419Sjulianrestart: 27052419Sjulian val = pppoe_sid++; 27152419Sjulian /* 27252419Sjulian * Spec says 0xFFFF is reserved. 27352419Sjulian * Also don't use 0x0000 27452419Sjulian */ 27552419Sjulian if (val == 0xffff) { 27652419Sjulian pppoe_sid = 20; 27752419Sjulian goto restart; 27852419Sjulian } 27952419Sjulian 28052419Sjulian /* Check it isn't already in use */ 28170784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 28252419Sjulian /* don't check special hooks */ 28370784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 28470784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 28552419Sjulian continue; 28670784Sjulian sp = NG_HOOK_PRIVATE(hook); 28752419Sjulian if (sp->Session_ID == val) 28852419Sjulian goto restart; 28952419Sjulian } 29052419Sjulian 29152419Sjulian return val; 29252419Sjulian} 29352419Sjulian 29452419Sjulian 29552419Sjulian/* 29652419Sjulian * Return the location where the next tag can be put 29752419Sjulian */ 29852419Sjulianstatic __inline struct pppoe_tag* 29952419Sjuliannext_tag(struct pppoe_hdr* ph) 30052419Sjulian{ 30152419Sjulian return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); 30252419Sjulian} 30352419Sjulian 30452419Sjulian/* 30552419Sjulian * Look for a tag of a specific type 30652419Sjulian * Don't trust any length the other end says. 30752419Sjulian * but assume we already sanity checked ph->length. 30852419Sjulian */ 30952419Sjulianstatic struct pppoe_tag* 31052419Sjulianget_tag(struct pppoe_hdr* ph, u_int16_t idx) 31152419Sjulian{ 31252419Sjulian char *end = (char *)next_tag(ph); 31352419Sjulian char *ptn; 31452419Sjulian struct pppoe_tag *pt = &ph->tag[0]; 31552419Sjulian /* 31652419Sjulian * Keep processing tags while a tag header will still fit. 31752419Sjulian */ 31852443SjulianAAA 31952419Sjulian while((char*)(pt + 1) <= end) { 32052419Sjulian /* 32152419Sjulian * If the tag data would go past the end of the packet, abort. 32252419Sjulian */ 32352419Sjulian ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 32452419Sjulian if(ptn > end) 32552419Sjulian return NULL; 32652419Sjulian 32752419Sjulian if(pt->tag_type == idx) 32852419Sjulian return pt; 32952419Sjulian 33052419Sjulian pt = (struct pppoe_tag*)ptn; 33152419Sjulian } 33252419Sjulian return NULL; 33352419Sjulian} 33452419Sjulian 33552419Sjulian/************************************************************************** 33652419Sjulian * inlines to initialise or add tags to a session's tag list, 33752419Sjulian **************************************************************************/ 33852419Sjulian/* 33952419Sjulian * Initialise the session's tag list 34052419Sjulian */ 34152419Sjulianstatic void 34252419Sjulianinit_tags(sessp sp) 34352419Sjulian{ 34452443SjulianAAA 34552419Sjulian if(sp->neg == NULL) { 34652419Sjulian printf("pppoe: asked to init NULL neg pointer\n"); 34752419Sjulian return; 34852419Sjulian } 34952419Sjulian sp->neg->numtags = 0; 35052419Sjulian} 35152419Sjulian 35252419Sjulianstatic void 35352419Sjulianinsert_tag(sessp sp, struct pppoe_tag *tp) 35452419Sjulian{ 35552419Sjulian int i; 35652419Sjulian negp neg; 35752419Sjulian 35852443SjulianAAA 35952419Sjulian if((neg = sp->neg) == NULL) { 36052419Sjulian printf("pppoe: asked to use NULL neg pointer\n"); 36152419Sjulian return; 36252419Sjulian } 36352419Sjulian if ((i = neg->numtags++) < NUMTAGS) { 36452419Sjulian neg->tags[i] = tp; 36552419Sjulian } else { 36652419Sjulian printf("pppoe: asked to add too many tags to packet\n"); 36753042Sjulian neg->numtags--; 36852419Sjulian } 36952419Sjulian} 37052419Sjulian 37152419Sjulian/* 37252419Sjulian * Make up a packet, using the tags filled out for the session. 37352419Sjulian * 37452419Sjulian * Assume that the actual pppoe header and ethernet header 37552419Sjulian * are filled out externally to this routine. 37652419Sjulian * Also assume that neg->wh points to the correct 37752419Sjulian * location at the front of the buffer space. 37852419Sjulian */ 37952419Sjulianstatic void 38052419Sjulianmake_packet(sessp sp) { 38152419Sjulian struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 38252419Sjulian struct pppoe_tag **tag; 38352419Sjulian char *dp; 38452419Sjulian int count; 38552419Sjulian int tlen; 38652419Sjulian u_int16_t length = 0; 38752419Sjulian 38852443SjulianAAA 38952443Sjulian if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 39052419Sjulian printf("pppoe: make_packet called from wrong state\n"); 39152419Sjulian } 39252419Sjulian dp = (char *)wh->ph.tag; 39352419Sjulian for (count = 0, tag = sp->neg->tags; 39452419Sjulian ((count < sp->neg->numtags) && (count < NUMTAGS)); 39552419Sjulian tag++, count++) { 39652419Sjulian tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 39752419Sjulian if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 39852419Sjulian printf("pppoe: tags too long\n"); 39952419Sjulian sp->neg->numtags = count; 40052419Sjulian break; /* XXX chop off what's too long */ 40152419Sjulian } 40252419Sjulian bcopy((char *)*tag, (char *)dp, tlen); 40352419Sjulian length += tlen; 40452419Sjulian dp += tlen; 40552419Sjulian } 40652419Sjulian wh->ph.length = htons(length); 40752419Sjulian sp->neg->m->m_len = length + sizeof(*wh); 40852419Sjulian sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 40952419Sjulian} 41052419Sjulian 41152419Sjulian/************************************************************************** 41252419Sjulian * Routine to match a service offered * 41352419Sjulian **************************************************************************/ 41452419Sjulian/* 41552419Sjulian * Find a hook that has a service string that matches that 41652419Sjulian * we are seeking. for now use a simple string. 41752419Sjulian * In the future we may need something like regexp(). 41852419Sjulian * for testing allow a null string to match 1st found and a null service 41952419Sjulian * to match all requests. Also make '*' do the same. 42052419Sjulian */ 42152419Sjulianstatic hook_p 42252419Sjulianpppoe_match_svc(node_p node, char *svc_name, int svc_len) 42352419Sjulian{ 42452419Sjulian sessp sp = NULL; 42552419Sjulian negp neg = NULL; 42670784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 42752419Sjulian hook_p hook; 42852419Sjulian 42952443SjulianAAA 43070784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 43152419Sjulian 43252419Sjulian /* skip any hook that is debug or ethernet */ 43370784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 43470784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 43552419Sjulian continue; 43670784Sjulian sp = NG_HOOK_PRIVATE(hook); 43752419Sjulian 43852419Sjulian /* Skip any sessions which are not in LISTEN mode. */ 43952419Sjulian if ( sp->state != PPPOE_LISTENING) 44052419Sjulian continue; 44152419Sjulian 44252419Sjulian neg = sp->neg; 44352419Sjulian /* XXX check validity of this */ 44452419Sjulian /* special case, NULL request. match 1st found. */ 44552419Sjulian if (svc_len == 0) 44652419Sjulian break; 44752419Sjulian 44852419Sjulian /* XXX check validity of this */ 44952419Sjulian /* Special case for a blank or "*" service name (wildcard) */ 45052419Sjulian if ((neg->service_len == 0) 45152419Sjulian || ((neg->service_len == 1) 45252419Sjulian && (neg->service.data[0] == '*'))) { 45352419Sjulian break; 45452419Sjulian } 45552419Sjulian 45652419Sjulian /* If the lengths don't match, that aint it. */ 45752419Sjulian if (neg->service_len != svc_len) 45852419Sjulian continue; 45952419Sjulian 46052419Sjulian /* An exact match? */ 46152419Sjulian if (strncmp(svc_name, neg->service.data, svc_len) == 0) 46252419Sjulian break; 46352419Sjulian } 46452419Sjulian return (hook); 46552419Sjulian} 46652419Sjulian/************************************************************************** 46752419Sjulian * Routine to find a particular session that matches an incoming packet * 46852419Sjulian **************************************************************************/ 46952419Sjulianstatic hook_p 47052419Sjulianpppoe_findsession(node_p node, struct pppoe_full_hdr *wh) 47152419Sjulian{ 47252419Sjulian sessp sp = NULL; 47352419Sjulian hook_p hook = NULL; 47470784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 47552448Sjulian u_int16_t session = ntohs(wh->ph.sid); 47652419Sjulian 47752419Sjulian /* 47852419Sjulian * find matching peer/session combination. 47952419Sjulian */ 48052443SjulianAAA 48170784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 48252419Sjulian /* don't check special hooks */ 48370784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 48470784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 48552419Sjulian continue; 48652419Sjulian } 48770784Sjulian sp = NG_HOOK_PRIVATE(hook); 48852419Sjulian if ( ( (sp->state == PPPOE_CONNECTED) 48952419Sjulian || (sp->state == PPPOE_NEWCONNECTED) ) 49052419Sjulian && (sp->Session_ID == session) 49152419Sjulian && (bcmp(sp->pkt_hdr.eh.ether_dhost, 49252419Sjulian wh->eh.ether_shost, 49352419Sjulian ETHER_ADDR_LEN)) == 0) { 49452419Sjulian break; 49552419Sjulian } 49652419Sjulian } 49752419Sjulian return (hook); 49852419Sjulian} 49952419Sjulian 50052419Sjulianstatic hook_p 50152419Sjulianpppoe_finduniq(node_p node, struct pppoe_tag *tag) 50252419Sjulian{ 50352419Sjulian hook_p hook = NULL; 50470784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 50552419Sjulian union uniq uniq; 50652419Sjulian 50752443SjulianAAA 50852419Sjulian bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 50952419Sjulian /* cycle through all known hooks */ 51070784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 51152419Sjulian /* don't check special hooks */ 51270784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 51370784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 51452419Sjulian continue; 51570784Sjulian if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 51652419Sjulian break; 51752419Sjulian } 51852419Sjulian return (hook); 51952419Sjulian} 52052419Sjulian 52152419Sjulian/************************************************************************** 52252419Sjulian * start of Netgraph entrypoints * 52352419Sjulian **************************************************************************/ 52452419Sjulian 52552419Sjulian/* 52652419Sjulian * Allocate the private data structure and the generic node 52752419Sjulian * and link them together. 52852419Sjulian * 52952419Sjulian * ng_make_node_common() returns with a generic node struct 53052419Sjulian * with a single reference for us.. we transfer it to the 53152419Sjulian * private structure.. when we free the private struct we must 53252419Sjulian * unref the node so it gets freed too. 53352419Sjulian */ 53452419Sjulianstatic int 53570700Sjulianng_pppoe_constructor(node_p node) 53652419Sjulian{ 53752419Sjulian priv_p privdata; 53852419Sjulian 53952443SjulianAAA 54052419Sjulian /* Initialize private descriptor */ 54168876Sdwmalone MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH, 54268876Sdwmalone M_NOWAIT | M_ZERO); 54352419Sjulian if (privdata == NULL) 54452419Sjulian return (ENOMEM); 54552419Sjulian 54652419Sjulian /* Link structs together; this counts as our one reference to *nodep */ 54770784Sjulian NG_NODE_SET_PRIVATE(node, privdata); 54870700Sjulian privdata->node = node; 54952419Sjulian return (0); 55052419Sjulian} 55152419Sjulian 55252419Sjulian/* 55352419Sjulian * Give our ok for a hook to be added... 55452419Sjulian * point the hook's private info to the hook structure. 55552419Sjulian * 55652419Sjulian * The following hook names are special: 55752419Sjulian * Ethernet: the hook that should be connected to a NIC. 55852419Sjulian * debug: copies of data sent out here (when I write the code). 55969922Sjulian * All other hook names need only be unique. (the framework checks this). 56052419Sjulian */ 56152419Sjulianstatic int 56252562Sjulianng_pppoe_newhook(node_p node, hook_p hook, const char *name) 56352419Sjulian{ 56470784Sjulian const priv_p privp = NG_NODE_PRIVATE(node); 56552419Sjulian sessp sp; 56652419Sjulian 56752443SjulianAAA 56852419Sjulian if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 56952419Sjulian privp->ethernet_hook = hook; 57070784Sjulian NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 57152419Sjulian } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 57252419Sjulian privp->debug_hook = hook; 57370784Sjulian NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 57452419Sjulian } else { 57552419Sjulian /* 57652419Sjulian * Any other unique name is OK. 57752419Sjulian * The infrastructure has already checked that it's unique, 57852419Sjulian * so just allocate it and hook it in. 57952419Sjulian */ 58068876Sdwmalone MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH, M_NOWAIT | M_ZERO); 58152419Sjulian if (sp == NULL) { 58252419Sjulian return (ENOMEM); 58352419Sjulian } 58452419Sjulian 58570784Sjulian NG_HOOK_SET_PRIVATE(hook, sp); 58652419Sjulian sp->hook = hook; 58752419Sjulian } 58852419Sjulian return(0); 58952419Sjulian} 59052419Sjulian 59152419Sjulian/* 59252419Sjulian * Get a netgraph control message. 59352419Sjulian * Check it is one we understand. If needed, send a response. 59452419Sjulian * We sometimes save the address for an async action later. 59552419Sjulian * Always free the message. 59652419Sjulian */ 59752419Sjulianstatic int 59870700Sjulianng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 59952419Sjulian{ 60070784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 60152562Sjulian struct ngpppoe_init_data *ourmsg = NULL; 60252419Sjulian struct ng_mesg *resp = NULL; 60352419Sjulian int error = 0; 60452419Sjulian hook_p hook = NULL; 60552419Sjulian sessp sp = NULL; 60652419Sjulian negp neg = NULL; 60770700Sjulian struct ng_mesg *msg; 60852419Sjulian 60952443SjulianAAA 61070700Sjulian NGI_GET_MSG(item, msg); 61152419Sjulian /* Deal with message according to cookie and command */ 61252419Sjulian switch (msg->header.typecookie) { 61352419Sjulian case NGM_PPPOE_COOKIE: 61452419Sjulian switch (msg->header.cmd) { 61552419Sjulian case NGM_PPPOE_CONNECT: 61652419Sjulian case NGM_PPPOE_LISTEN: 61752419Sjulian case NGM_PPPOE_OFFER: 61869922Sjulian case NGM_PPPOE_SERVICE: 61968845Sbrian ourmsg = (struct ngpppoe_init_data *)msg->data; 62068845Sbrian if (msg->header.arglen < sizeof(*ourmsg)) { 62168845Sbrian printf("pppoe: init data too small\n"); 62268845Sbrian LEAVE(EMSGSIZE); 62368031Sbrian } 62468031Sbrian if (msg->header.arglen - sizeof(*ourmsg) > 62568031Sbrian PPPOE_SERVICE_NAME_SIZE) { 62668031Sbrian printf("pppoe_rcvmsg: service name too big"); 62752419Sjulian LEAVE(EMSGSIZE); 62852419Sjulian } 62968845Sbrian if (msg->header.arglen - sizeof(*ourmsg) < 63068845Sbrian ourmsg->data_len) { 63168845Sbrian printf("pppoe: init data has bad length," 63268845Sbrian " %d should be %d\n", ourmsg->data_len, 63368845Sbrian msg->header.arglen - sizeof (*ourmsg)); 63452419Sjulian LEAVE(EMSGSIZE); 63552419Sjulian } 63668031Sbrian 63752419Sjulian /* make sure strcmp will terminate safely */ 63852419Sjulian ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 63952419Sjulian 64052419Sjulian /* cycle through all known hooks */ 64170784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 64270784Sjulian if (NG_HOOK_NAME(hook) 64370784Sjulian && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 64452419Sjulian break; 64552419Sjulian } 64652419Sjulian if (hook == NULL) { 64752419Sjulian LEAVE(ENOENT); 64852419Sjulian } 64970784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 65070784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 65152419Sjulian LEAVE(EINVAL); 65252419Sjulian } 65370784Sjulian sp = NG_HOOK_PRIVATE(hook); 65469922Sjulian 65569922Sjulian /* 65669922Sjulian * PPPOE_SERVICE advertisments are set up 65769922Sjulian * on sessions that are in PRIMED state. 65869922Sjulian */ 65969922Sjulian if (msg->header.cmd == NGM_PPPOE_SERVICE) { 66069922Sjulian break; 66169922Sjulian } 66252419Sjulian if (sp->state |= PPPOE_SNONE) { 66352419Sjulian printf("pppoe: Session already active\n"); 66452419Sjulian LEAVE(EISCONN); 66552419Sjulian } 66652443Sjulian 66752419Sjulian /* 66852419Sjulian * set up prototype header 66952419Sjulian */ 67068876Sdwmalone MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH, 67168876Sdwmalone M_NOWAIT | M_ZERO); 67252419Sjulian 67352419Sjulian if (neg == NULL) { 67452419Sjulian printf("pppoe: Session out of memory\n"); 67552419Sjulian LEAVE(ENOMEM); 67652419Sjulian } 67752419Sjulian MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 67852419Sjulian if(neg->m == NULL) { 67952443Sjulian printf("pppoe: Session out of mbufs\n"); 68052419Sjulian FREE(neg, M_NETGRAPH); 68152419Sjulian LEAVE(ENOBUFS); 68252419Sjulian } 68352419Sjulian neg->m->m_pkthdr.rcvif = NULL; 68452419Sjulian MCLGET(neg->m, M_DONTWAIT); 68552419Sjulian if ((neg->m->m_flags & M_EXT) == 0) { 68652443Sjulian printf("pppoe: Session out of mcls\n"); 68752419Sjulian m_freem(neg->m); 68852419Sjulian FREE(neg, M_NETGRAPH); 68952419Sjulian LEAVE(ENOBUFS); 69052419Sjulian } 69152419Sjulian sp->neg = neg; 69252443Sjulian callout_handle_init( &neg->timeout_handle); 69352419Sjulian neg->m->m_len = sizeof(struct pppoe_full_hdr); 69452419Sjulian neg->pkt = mtod(neg->m, union packet*); 69552419Sjulian neg->pkt->pkt_header.eh = eh_prototype; 69652419Sjulian neg->pkt->pkt_header.ph.ver = 0x1; 69752419Sjulian neg->pkt->pkt_header.ph.type = 0x1; 69852419Sjulian neg->pkt->pkt_header.ph.sid = 0x0000; 69952419Sjulian neg->timeout = 0; 70052419Sjulian 70170700Sjulian sp->creator = NGI_RETADDR(item); 70252419Sjulian } 70352419Sjulian switch (msg->header.cmd) { 70452419Sjulian case NGM_PPPOE_GET_STATUS: 70552419Sjulian { 70652562Sjulian struct ngpppoestat *stats; 70752419Sjulian 70852419Sjulian NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 70952419Sjulian if (!resp) { 71052419Sjulian LEAVE(ENOMEM); 71152419Sjulian } 71252562Sjulian stats = (struct ngpppoestat *) resp->data; 71352419Sjulian stats->packets_in = privp->packets_in; 71452419Sjulian stats->packets_out = privp->packets_out; 71552419Sjulian break; 71652419Sjulian } 71752419Sjulian case NGM_PPPOE_CONNECT: 71852419Sjulian /* 71952419Sjulian * Check the hook exists and is Uninitialised. 72052419Sjulian * Send a PADI request, and start the timeout logic. 72152419Sjulian * Store the originator of this message so we can send 72252419Sjulian * a success of fail message to them later. 72352419Sjulian * Move the session to SINIT 72452419Sjulian * Set up the session to the correct state and 72552419Sjulian * start it. 72652419Sjulian */ 72752419Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 72868845Sbrian neg->service.hdr.tag_len = 72968845Sbrian htons((u_int16_t)ourmsg->data_len); 73068845Sbrian if (ourmsg->data_len) 73168845Sbrian bcopy(ourmsg->data, neg->service.data, 73268845Sbrian ourmsg->data_len); 73368845Sbrian neg->service_len = ourmsg->data_len; 73452419Sjulian pppoe_start(sp); 73552419Sjulian break; 73652419Sjulian case NGM_PPPOE_LISTEN: 73752419Sjulian /* 73852419Sjulian * Check the hook exists and is Uninitialised. 73952419Sjulian * Install the service matching string. 74052419Sjulian * Store the originator of this message so we can send 74152419Sjulian * a success of fail message to them later. 74252419Sjulian * Move the hook to 'LISTENING' 74352419Sjulian */ 74452419Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 74568845Sbrian neg->service.hdr.tag_len = 74668845Sbrian htons((u_int16_t)ourmsg->data_len); 74752443Sjulian 74868845Sbrian if (ourmsg->data_len) 74968845Sbrian bcopy(ourmsg->data, neg->service.data, 75068845Sbrian ourmsg->data_len); 75168845Sbrian neg->service_len = ourmsg->data_len; 75252419Sjulian neg->pkt->pkt_header.ph.code = PADT_CODE; 75352419Sjulian /* 75452419Sjulian * wait for PADI packet coming from ethernet 75552419Sjulian */ 75652419Sjulian sp->state = PPPOE_LISTENING; 75752419Sjulian break; 75852419Sjulian case NGM_PPPOE_OFFER: 75952419Sjulian /* 76052419Sjulian * Check the hook exists and is Uninitialised. 76152419Sjulian * Store the originator of this message so we can send 76252419Sjulian * a success of fail message to them later. 76352419Sjulian * Store the AC-Name given and go to PRIMED. 76452419Sjulian */ 76552419Sjulian neg->ac_name.hdr.tag_type = PTT_AC_NAME; 76668845Sbrian neg->ac_name.hdr.tag_len = 76768845Sbrian htons((u_int16_t)ourmsg->data_len); 76868845Sbrian if (ourmsg->data_len) 76968845Sbrian bcopy(ourmsg->data, neg->ac_name.data, 77068845Sbrian ourmsg->data_len); 77168845Sbrian neg->ac_name_len = ourmsg->data_len; 77252419Sjulian neg->pkt->pkt_header.ph.code = PADO_CODE; 77352419Sjulian /* 77452419Sjulian * Wait for PADI packet coming from hook 77552419Sjulian */ 77652419Sjulian sp->state = PPPOE_PRIMED; 77752419Sjulian break; 77869922Sjulian case NGM_PPPOE_SERVICE: 77969922Sjulian /* 78069922Sjulian * Check the session is primed. 78169922Sjulian * for now just allow ONE service to be advertised. 78269922Sjulian * If you do it twice you just overwrite. 78369922Sjulian */ 78470148Sjulian if (sp->state != PPPOE_PRIMED) { 78569922Sjulian printf("pppoe: Session not primed\n"); 78669922Sjulian LEAVE(EISCONN); 78769922Sjulian } 78869922Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 78969922Sjulian neg->service.hdr.tag_len = 79069922Sjulian htons((u_int16_t)ourmsg->data_len); 79169922Sjulian 79269922Sjulian if (ourmsg->data_len) 79369922Sjulian bcopy(ourmsg->data, neg->service.data, 79469922Sjulian ourmsg->data_len); 79569922Sjulian neg->service_len = ourmsg->data_len; 79669922Sjulian break; 79752419Sjulian default: 79852419Sjulian LEAVE(EINVAL); 79952419Sjulian } 80052419Sjulian break; 80152419Sjulian default: 80252419Sjulian LEAVE(EINVAL); 80352419Sjulian } 80452419Sjulian 80552419Sjulian /* Take care of synchronous response, if any */ 80670700Sjulianquit: 80770700Sjulian NG_RESPOND_MSG(error, node, item, resp); 80852419Sjulian /* Free the message and return */ 80970700Sjulian NG_FREE_MSG(msg); 81052419Sjulian return(error); 81152419Sjulian} 81252419Sjulian 81352443Sjulian/* 81452443Sjulian * Start a client into the first state. A separate function because 81552443Sjulian * it can be needed if the negotiation times out. 81652443Sjulian */ 81752419Sjulianstatic void 81852419Sjulianpppoe_start(sessp sp) 81952419Sjulian{ 82052419Sjulian struct { 82152419Sjulian struct pppoe_tag hdr; 82252419Sjulian union uniq data; 82352419Sjulian } uniqtag; 82452419Sjulian 82552419Sjulian /* 82652419Sjulian * kick the state machine into starting up 82752419Sjulian */ 82852443SjulianAAA 82952419Sjulian sp->state = PPPOE_SINIT; 83052443Sjulian /* reset the packet header to broadcast */ 83152443Sjulian sp->neg->pkt->pkt_header.eh = eh_prototype; 83252443Sjulian sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 83352419Sjulian uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 83452419Sjulian uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 83552419Sjulian uniqtag.data.pointer = sp; 83652419Sjulian init_tags(sp); 83768079Sjulian insert_tag(sp, &uniqtag.hdr); 83853154Sjulian insert_tag(sp, &sp->neg->service.hdr); 83952419Sjulian make_packet(sp); 84052419Sjulian sendpacket(sp); 84152419Sjulian} 84252419Sjulian 84352419Sjulian/* 84452419Sjulian * Receive data, and do something with it. 84552419Sjulian * The caller will never free m or meta, so 84652419Sjulian * if we use up this data or abort we must free BOTH of these. 84752419Sjulian */ 84852419Sjulianstatic int 84970700Sjulianng_pppoe_rcvdata(hook_p hook, item_p item) 85052419Sjulian{ 85170784Sjulian node_p node = NG_HOOK_NODE(hook); 85270784Sjulian const priv_p privp = NG_NODE_PRIVATE(node); 85370784Sjulian sessp sp = NG_HOOK_PRIVATE(hook); 85452419Sjulian struct pppoe_full_hdr *wh; 85552419Sjulian struct pppoe_hdr *ph; 85652419Sjulian int error = 0; 85752419Sjulian u_int16_t session; 85852419Sjulian u_int16_t length; 85952419Sjulian u_int8_t code; 86053154Sjulian struct pppoe_tag *utag = NULL, *tag = NULL; 86152419Sjulian hook_p sendhook; 86252419Sjulian struct { 86352419Sjulian struct pppoe_tag hdr; 86452419Sjulian union uniq data; 86552419Sjulian } uniqtag; 86652419Sjulian negp neg = NULL; 86770700Sjulian struct mbuf *m; 86852419Sjulian 86952443SjulianAAA 87070700Sjulian NGI_GET_M(item, m); 87170784Sjulian if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 87252419Sjulian /* 87352419Sjulian * Data from the debug hook gets sent without modification 87452419Sjulian * straight to the ethernet. 87552419Sjulian */ 87670784Sjulian NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 87752419Sjulian privp->packets_out++; 87870784Sjulian } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 87952419Sjulian /* 88052419Sjulian * Incoming data. 88152419Sjulian * Dig out various fields from the packet. 88252419Sjulian * use them to decide where to send it. 88352419Sjulian */ 88452419Sjulian 88552419Sjulian privp->packets_in++; 88652510Sjulian if( m->m_len < sizeof(*wh)) { 88752510Sjulian m = m_pullup(m, sizeof(*wh)); /* Checks length */ 88852510Sjulian if (m == NULL) { 88952510Sjulian printf("couldn't m_pullup\n"); 89052510Sjulian LEAVE(ENOBUFS); 89152510Sjulian } 89252419Sjulian } 89352419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 89452419Sjulian length = ntohs(wh->ph.length); 89552510Sjulian switch(wh->eh.ether_type) { 89652419Sjulian case ETHERTYPE_PPPOE_DISC: 89752419Sjulian /* 89853172Sjulian * We need to try to make sure that the tag area 89953172Sjulian * is contiguous, or we could wander off the end 90052419Sjulian * of a buffer and make a mess. 90152419Sjulian * (Linux wouldn't have this problem). 90252419Sjulian */ 90352510Sjulian if (m->m_pkthdr.len <= MHLEN) { 90452510Sjulian if( m->m_len < m->m_pkthdr.len) { 90552510Sjulian m = m_pullup(m, m->m_pkthdr.len); 90652510Sjulian if (m == NULL) { 90752510Sjulian printf("couldn't m_pullup\n"); 90852510Sjulian LEAVE(ENOBUFS); 90952510Sjulian } 91052510Sjulian } 91152510Sjulian } 91252510Sjulian if (m->m_len != m->m_pkthdr.len) { 91352419Sjulian /* 91452419Sjulian * It's not all in one piece. 91552419Sjulian * We need to do extra work. 91670700Sjulian * Put it into a cluster. 91752419Sjulian */ 91870700Sjulian struct mbuf *n; 91970700Sjulian n = m_dup(m, M_DONTWAIT); 92070700Sjulian m_freem(m); 92170700Sjulian m = n; 92270700Sjulian if (m) { 92370700Sjulian /* just check we got a cluster */ 92470700Sjulian if (m->m_len != m->m_pkthdr.len) { 92570700Sjulian m_freem(m); 92670700Sjulian m = NULL; 92770700Sjulian } 92870700Sjulian } 92970700Sjulian if (m == NULL) { 93070700Sjulian printf("packet fragmented\n"); 93170700Sjulian LEAVE(EMSGSIZE); 93270700Sjulian } 93353498Sjulian } 93470700Sjulian wh = mtod(m, struct pppoe_full_hdr *); 93570700Sjulian length = ntohs(wh->ph.length); 93670700Sjulian ph = &wh->ph; 93770700Sjulian session = ntohs(wh->ph.sid); 93870700Sjulian code = wh->ph.code; 93952419Sjulian 94052419Sjulian switch(code) { 94152419Sjulian case PADI_CODE: 94252419Sjulian /* 94352419Sjulian * We are a server: 94452419Sjulian * Look for a hook with the required service 94552419Sjulian * and send the ENTIRE packet up there. 94652419Sjulian * It should come back to a new hook in 94752419Sjulian * PRIMED state. Look there for further 94852419Sjulian * processing. 94952419Sjulian */ 95052419Sjulian tag = get_tag(ph, PTT_SRV_NAME); 95152419Sjulian if (tag == NULL) { 95252448Sjulian printf("no service tag\n"); 95352419Sjulian LEAVE(ENETUNREACH); 95452419Sjulian } 95570784Sjulian sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 95652419Sjulian tag->tag_data, ntohs(tag->tag_len)); 95752419Sjulian if (sendhook) { 95870700Sjulian NG_FWD_NEW_DATA(error, item, 95970700Sjulian sendhook, m); 96052419Sjulian } else { 96152448Sjulian printf("no such service\n"); 96252419Sjulian LEAVE(ENETUNREACH); 96352419Sjulian } 96452419Sjulian break; 96552419Sjulian case PADO_CODE: 96652419Sjulian /* 96752419Sjulian * We are a client: 96852419Sjulian * Use the host_uniq tag to find the 96952419Sjulian * hook this is in response to. 97052448Sjulian * Received #2, now send #3 97152419Sjulian * For now simply accept the first we receive. 97252419Sjulian */ 97353154Sjulian utag = get_tag(ph, PTT_HOST_UNIQ); 97453154Sjulian if ((utag == NULL) 97553154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 97652448Sjulian printf("no host unique field\n"); 97752419Sjulian LEAVE(ENETUNREACH); 97852419Sjulian } 97952419Sjulian 98053154Sjulian sendhook = pppoe_finduniq(node, utag); 98152419Sjulian if (sendhook == NULL) { 98252448Sjulian printf("no matching session\n"); 98352419Sjulian LEAVE(ENETUNREACH); 98452419Sjulian } 98552419Sjulian 98652419Sjulian /* 98752419Sjulian * Check the session is in the right state. 98852419Sjulian * It needs to be in PPPOE_SINIT. 98952419Sjulian */ 99070784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 99152419Sjulian if (sp->state != PPPOE_SINIT) { 99252448Sjulian printf("session in wrong state\n"); 99352419Sjulian LEAVE(ENETUNREACH); 99452419Sjulian } 99552419Sjulian neg = sp->neg; 99652419Sjulian untimeout(pppoe_ticker, sendhook, 99752419Sjulian neg->timeout_handle); 99852419Sjulian 99952419Sjulian /* 100052419Sjulian * This is the first time we hear 100152419Sjulian * from the server, so note it's 100252419Sjulian * unicast address, replacing the 100352419Sjulian * broadcast address . 100452419Sjulian */ 100552419Sjulian bcopy(wh->eh.ether_shost, 100652419Sjulian neg->pkt->pkt_header.eh.ether_dhost, 100752419Sjulian ETHER_ADDR_LEN); 100852419Sjulian neg->timeout = 0; 100952419Sjulian neg->pkt->pkt_header.ph.code = PADR_CODE; 101052419Sjulian init_tags(sp); 101168079Sjulian insert_tag(sp, utag); /* Host Unique */ 101253154Sjulian if ((tag = get_tag(ph, PTT_AC_COOKIE))) 101352448Sjulian insert_tag(sp, tag); /* return cookie */ 101453154Sjulian if ((tag = get_tag(ph, PTT_AC_NAME))) 101553154Sjulian insert_tag(sp, tag); /* return it */ 101668079Sjulian insert_tag(sp, &neg->service.hdr); /* Service */ 101752419Sjulian scan_tags(sp, ph); 101852419Sjulian make_packet(sp); 101952419Sjulian sp->state = PPPOE_SREQ; 102052419Sjulian sendpacket(sp); 102152419Sjulian break; 102252419Sjulian case PADR_CODE: 102352419Sjulian 102452419Sjulian /* 102552419Sjulian * We are a server: 102652419Sjulian * Use the ac_cookie tag to find the 102752419Sjulian * hook this is in response to. 102852419Sjulian */ 102953154Sjulian utag = get_tag(ph, PTT_AC_COOKIE); 103053154Sjulian if ((utag == NULL) 103153154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 103252419Sjulian LEAVE(ENETUNREACH); 103352419Sjulian } 103452419Sjulian 103553154Sjulian sendhook = pppoe_finduniq(node, utag); 103652419Sjulian if (sendhook == NULL) { 103752419Sjulian LEAVE(ENETUNREACH); 103852419Sjulian } 103952419Sjulian 104052419Sjulian /* 104152419Sjulian * Check the session is in the right state. 104252419Sjulian * It needs to be in PPPOE_SOFFER 104352419Sjulian * or PPPOE_NEWCONNECTED. If the latter, 104452419Sjulian * then this is a retry by the client. 104552419Sjulian * so be nice, and resend. 104652419Sjulian */ 104770784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 104852419Sjulian if (sp->state == PPPOE_NEWCONNECTED) { 104952419Sjulian /* 105052419Sjulian * Whoa! drop back to resend that 105152419Sjulian * PADS packet. 105252419Sjulian * We should still have a copy of it. 105352419Sjulian */ 105452419Sjulian sp->state = PPPOE_SOFFER; 105552419Sjulian } 105652419Sjulian if (sp->state != PPPOE_SOFFER) { 105752419Sjulian LEAVE (ENETUNREACH); 105852419Sjulian break; 105952419Sjulian } 106052419Sjulian neg = sp->neg; 106152419Sjulian untimeout(pppoe_ticker, sendhook, 106252419Sjulian neg->timeout_handle); 106352419Sjulian neg->pkt->pkt_header.ph.code = PADS_CODE; 106452419Sjulian if (sp->Session_ID == 0) 106552419Sjulian neg->pkt->pkt_header.ph.sid = 106652448Sjulian htons(sp->Session_ID 106752448Sjulian = get_new_sid(node)); 106852419Sjulian neg->timeout = 0; 106952419Sjulian /* 107052419Sjulian * start working out the tags to respond with. 107152419Sjulian */ 107252419Sjulian init_tags(sp); 107352419Sjulian insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 107453172Sjulian if ((tag = get_tag(ph, PTT_SRV_NAME))) 107553145Sjulian insert_tag(sp, tag);/* return service */ 107653154Sjulian if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 107753145Sjulian insert_tag(sp, tag); /* return it */ 107853154Sjulian insert_tag(sp, utag); /* ac_cookie */ 107952419Sjulian scan_tags(sp, ph); 108052419Sjulian make_packet(sp); 108153498Sjulian sp->state = PPPOE_NEWCONNECTED; 108253172Sjulian sendpacket(sp); 108352419Sjulian /* 108452419Sjulian * Having sent the last Negotiation header, 108552419Sjulian * Set up the stored packet header to 108652419Sjulian * be correct for the actual session. 108752419Sjulian * But keep the negotialtion stuff 108852419Sjulian * around in case we need to resend this last 108952419Sjulian * packet. We'll discard it when we move 109052419Sjulian * from NEWCONNECTED to CONNECTED 109152419Sjulian */ 109252419Sjulian sp->pkt_hdr = neg->pkt->pkt_header; 109352419Sjulian sp->pkt_hdr.eh.ether_type 109452419Sjulian = ETHERTYPE_PPPOE_SESS; 109552419Sjulian sp->pkt_hdr.ph.code = 0; 109652441Sjulian pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 109752419Sjulian break; 109852419Sjulian case PADS_CODE: 109952419Sjulian /* 110052419Sjulian * We are a client: 110152419Sjulian * Use the host_uniq tag to find the 110252419Sjulian * hook this is in response to. 110352419Sjulian * take the session ID and store it away. 110452419Sjulian * Also make sure the pre-made header is 110552419Sjulian * correct and set us into Session mode. 110652419Sjulian */ 110753154Sjulian utag = get_tag(ph, PTT_HOST_UNIQ); 110853154Sjulian if ((utag == NULL) 110953154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 111052419Sjulian LEAVE (ENETUNREACH); 111152419Sjulian break; 111252419Sjulian } 111353154Sjulian sendhook = pppoe_finduniq(node, utag); 111452419Sjulian if (sendhook == NULL) { 111552419Sjulian LEAVE(ENETUNREACH); 111652419Sjulian } 111752419Sjulian 111852419Sjulian /* 111952419Sjulian * Check the session is in the right state. 112052419Sjulian * It needs to be in PPPOE_SREQ. 112152419Sjulian */ 112270784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 112352419Sjulian if (sp->state != PPPOE_SREQ) { 112452419Sjulian LEAVE(ENETUNREACH); 112552419Sjulian } 112652419Sjulian neg = sp->neg; 112752419Sjulian untimeout(pppoe_ticker, sendhook, 112852419Sjulian neg->timeout_handle); 112952524Sjulian neg->pkt->pkt_header.ph.sid = wh->ph.sid; 113052448Sjulian sp->Session_ID = ntohs(wh->ph.sid); 113152419Sjulian neg->timeout = 0; 113252419Sjulian sp->state = PPPOE_CONNECTED; 113352419Sjulian /* 113452419Sjulian * Now we have gone to Connected mode, 113552419Sjulian * Free all resources needed for 113652419Sjulian * negotiation. 113752419Sjulian * Keep a copy of the header we will be using. 113852419Sjulian */ 113952419Sjulian sp->pkt_hdr = neg->pkt->pkt_header; 114052419Sjulian sp->pkt_hdr.eh.ether_type 114152419Sjulian = ETHERTYPE_PPPOE_SESS; 114252419Sjulian sp->pkt_hdr.ph.code = 0; 114352419Sjulian m_freem(neg->m); 114452419Sjulian FREE(sp->neg, M_NETGRAPH); 114552419Sjulian sp->neg = NULL; 114652441Sjulian pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 114752419Sjulian break; 114852419Sjulian case PADT_CODE: 114952419Sjulian /* 115052419Sjulian * Send a 'close' message to the controlling 115152419Sjulian * process (the one that set us up); 115252419Sjulian * And then tear everything down. 115352419Sjulian * 115452419Sjulian * Find matching peer/session combination. 115552419Sjulian */ 115652419Sjulian sendhook = pppoe_findsession(node, wh); 115752419Sjulian if (sendhook == NULL) { 115852419Sjulian LEAVE(ENETUNREACH); 115952419Sjulian } 116052419Sjulian /* send message to creator */ 116152419Sjulian /* close hook */ 116252441Sjulian if (sendhook) { 116352441Sjulian ng_destroy_hook(sendhook); 116452441Sjulian } 116552419Sjulian break; 116652419Sjulian default: 116752419Sjulian LEAVE(EPFNOSUPPORT); 116852419Sjulian } 116952419Sjulian break; 117052419Sjulian case ETHERTYPE_PPPOE_SESS: 117152419Sjulian /* 117252419Sjulian * find matching peer/session combination. 117352419Sjulian */ 117452419Sjulian sendhook = pppoe_findsession(node, wh); 117552419Sjulian if (sendhook == NULL) { 117652419Sjulian LEAVE (ENETUNREACH); 117752419Sjulian break; 117852419Sjulian } 117970784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 118052419Sjulian m_adj(m, sizeof(*wh)); 118152419Sjulian if (m->m_pkthdr.len < length) { 118252419Sjulian /* Packet too short, dump it */ 118352419Sjulian LEAVE(EMSGSIZE); 118452419Sjulian } 118552523Sjulian 118653145Sjulian /* Also need to trim excess at the end */ 118752523Sjulian if (m->m_pkthdr.len > length) { 118852523Sjulian m_adj(m, -((int)(m->m_pkthdr.len - length))); 118952523Sjulian } 119052419Sjulian if ( sp->state != PPPOE_CONNECTED) { 119152419Sjulian if (sp->state == PPPOE_NEWCONNECTED) { 119252419Sjulian sp->state = PPPOE_CONNECTED; 119352419Sjulian /* 119452419Sjulian * Now we have gone to Connected mode, 119552419Sjulian * Free all resources needed for 119659728Sjulian * negotiation. Be paranoid about 119759728Sjulian * whether there may be a timeout. 119852419Sjulian */ 119952419Sjulian m_freem(sp->neg->m); 120059728Sjulian untimeout(pppoe_ticker, sendhook, 120159728Sjulian sp->neg->timeout_handle); 120252419Sjulian FREE(sp->neg, M_NETGRAPH); 120352419Sjulian sp->neg = NULL; 120452419Sjulian } else { 120552419Sjulian LEAVE (ENETUNREACH); 120652419Sjulian break; 120752419Sjulian } 120852419Sjulian } 120970700Sjulian NG_FWD_NEW_DATA( error, item, sendhook, m); 121052419Sjulian break; 121152419Sjulian default: 121252522Sjulian LEAVE(EPFNOSUPPORT); 121352419Sjulian } 121452419Sjulian } else { 121552419Sjulian /* 121652419Sjulian * Not ethernet or debug hook.. 121752419Sjulian * 121852419Sjulian * The packet has come in on a normal hook. 121952419Sjulian * We need to find out what kind of hook, 122052419Sjulian * So we can decide how to handle it. 122152419Sjulian * Check the hook's state. 122252419Sjulian */ 122370784Sjulian sp = NG_HOOK_PRIVATE(hook); 122452419Sjulian switch (sp->state) { 122552419Sjulian case PPPOE_NEWCONNECTED: 122652419Sjulian case PPPOE_CONNECTED: { 122764502Sarchie static const u_char addrctrl[] = { 0xff, 0x03 }; 122852419Sjulian struct pppoe_full_hdr *wh; 122964502Sarchie 123052419Sjulian /* 123164502Sarchie * Remove PPP address and control fields, if any. 123264502Sarchie * For example, ng_ppp(4) always sends LCP packets 123364502Sarchie * with address and control fields as required by 123464502Sarchie * generic PPP. PPPoE is an exception to the rule. 123564502Sarchie */ 123664502Sarchie if (m->m_pkthdr.len >= 2) { 123764502Sarchie if (m->m_len < 2 && !(m = m_pullup(m, 2))) 123864502Sarchie LEAVE(ENOBUFS); 123964502Sarchie if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 124064502Sarchie m_adj(m, 2); 124164502Sarchie } 124264502Sarchie /* 124352419Sjulian * Bang in a pre-made header, and set the length up 124452419Sjulian * to be correct. Then send it to the ethernet driver. 124552614Sjulian * But first correct the length. 124652419Sjulian */ 124752614Sjulian sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 124852419Sjulian M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 124952419Sjulian if (m == NULL) { 125052419Sjulian LEAVE(ENOBUFS); 125152419Sjulian } 125252419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 125352419Sjulian bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 125470700Sjulian NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 125552419Sjulian privp->packets_out++; 125652419Sjulian break; 125752419Sjulian } 125852419Sjulian case PPPOE_PRIMED: 125952419Sjulian /* 126052419Sjulian * A PADI packet is being returned by the application 126152419Sjulian * that has set up this hook. This indicates that it 126252419Sjulian * wants us to offer service. 126352419Sjulian */ 126452419Sjulian neg = sp->neg; 126553172Sjulian if (m->m_len < sizeof(*wh)) { 126653172Sjulian m = m_pullup(m, sizeof(*wh)); 126753172Sjulian if (m == NULL) { 126853172Sjulian LEAVE(ENOBUFS); 126953172Sjulian } 127052419Sjulian } 127152419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 127252419Sjulian ph = &wh->ph; 127352419Sjulian session = ntohs(wh->ph.sid); 127452419Sjulian length = ntohs(wh->ph.length); 127552419Sjulian code = wh->ph.code; 127652443Sjulian if ( code != PADI_CODE) { 127752443Sjulian LEAVE(EINVAL); 127852443Sjulian }; 127952443Sjulian untimeout(pppoe_ticker, hook, 128052443Sjulian neg->timeout_handle); 128152419Sjulian 128252419Sjulian /* 128352419Sjulian * This is the first time we hear 128452419Sjulian * from the client, so note it's 128552419Sjulian * unicast address, replacing the 128653145Sjulian * broadcast address. 128752419Sjulian */ 128852419Sjulian bcopy(wh->eh.ether_shost, 128952419Sjulian neg->pkt->pkt_header.eh.ether_dhost, 129052419Sjulian ETHER_ADDR_LEN); 129152419Sjulian sp->state = PPPOE_SOFFER; 129252419Sjulian neg->timeout = 0; 129352419Sjulian neg->pkt->pkt_header.ph.code = PADO_CODE; 129452419Sjulian 129552419Sjulian /* 129652419Sjulian * start working out the tags to respond with. 129752419Sjulian */ 129852419Sjulian uniqtag.hdr.tag_type = PTT_AC_COOKIE; 129952419Sjulian uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 130052419Sjulian uniqtag.data.pointer = sp; 130152419Sjulian init_tags(sp); 130252419Sjulian insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 130353154Sjulian if ((tag = get_tag(ph, PTT_SRV_NAME))) 130453154Sjulian insert_tag(sp, tag); /* return service */ 130569922Sjulian /* 130669922Sjulian * If we have a NULL service request 130769922Sjulian * and have an extra service defined in this hook, 130869922Sjulian * then also add a tag for the extra service. 130969922Sjulian * XXX this is a hack. eventually we should be able 131069922Sjulian * to support advertising many services, not just one 131169922Sjulian */ 131269922Sjulian if (((tag == NULL) || (tag->tag_len == 0)) 131369922Sjulian && (neg->service.hdr.tag_len != 0)) { 131469922Sjulian insert_tag(sp, &neg->service.hdr); /* SERVICE */ 131569922Sjulian } 131653154Sjulian if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 131753145Sjulian insert_tag(sp, tag); /* returned hostunique */ 131853154Sjulian insert_tag(sp, &uniqtag.hdr); 131952419Sjulian scan_tags(sp, ph); 132052419Sjulian make_packet(sp); 132152419Sjulian sendpacket(sp); 132252419Sjulian break; 132352419Sjulian 132452419Sjulian /* 132552419Sjulian * Packets coming from the hook make no sense 132652419Sjulian * to sessions in these states. Throw them away. 132752419Sjulian */ 132852419Sjulian case PPPOE_SINIT: 132952419Sjulian case PPPOE_SREQ: 133052419Sjulian case PPPOE_SOFFER: 133152419Sjulian case PPPOE_SNONE: 133252419Sjulian case PPPOE_LISTENING: 133352419Sjulian case PPPOE_DEAD: 133452419Sjulian default: 133552419Sjulian LEAVE(ENETUNREACH); 133652419Sjulian } 133752419Sjulian } 133852419Sjulianquit: 133970700Sjulian NG_FREE_ITEM(item); 134070700Sjulian NG_FREE_M(m); 134152419Sjulian return error; 134252419Sjulian} 134352419Sjulian 134452419Sjulian/* 134552419Sjulian * Do local shutdown processing.. 134652419Sjulian * If we are a persistant device, we might refuse to go away, and 134752419Sjulian * we'd only remove our links and reset ourself. 134852419Sjulian */ 134952419Sjulianstatic int 135070700Sjulianng_pppoe_shutdown(node_p node) 135152419Sjulian{ 135270784Sjulian const priv_p privdata = NG_NODE_PRIVATE(node); 135352419Sjulian 135452443SjulianAAA 135570784Sjulian NG_NODE_SET_PRIVATE(node, NULL); 135670784Sjulian NG_NODE_UNREF(privdata->node); 135752419Sjulian FREE(privdata, M_NETGRAPH); 135852419Sjulian return (0); 135952419Sjulian} 136052419Sjulian 136152419Sjulian/* 136252419Sjulian * This is called once we've already connected a new hook to the other node. 136352419Sjulian * It gives us a chance to balk at the last minute. 136452419Sjulian */ 136552419Sjulianstatic int 136652562Sjulianng_pppoe_connect(hook_p hook) 136752419Sjulian{ 136852419Sjulian /* be really amiable and just say "YUP that's OK by me! " */ 136952419Sjulian return (0); 137052419Sjulian} 137152419Sjulian 137252419Sjulian/* 137352419Sjulian * Hook disconnection 137452419Sjulian * 137553498Sjulian * Clean up all dangling links and information about the session/hook. 137652419Sjulian * For this type, removal of the last link destroys the node 137752419Sjulian */ 137852419Sjulianstatic int 137952562Sjulianng_pppoe_disconnect(hook_p hook) 138052419Sjulian{ 138170784Sjulian node_p node = NG_HOOK_NODE(hook); 138270784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 138352419Sjulian sessp sp; 138452563Sjulian int hooks; 138552419Sjulian 138652443SjulianAAA 138770784Sjulian hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 138870784Sjulian if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 138952419Sjulian privp->debug_hook = NULL; 139070784Sjulian } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 139152419Sjulian privp->ethernet_hook = NULL; 139270784Sjulian if (NG_NODE_IS_VALID(node)) 139370700Sjulian ng_rmnode_self(node); 139452419Sjulian } else { 139570784Sjulian sp = NG_HOOK_PRIVATE(hook); 139652441Sjulian if (sp->state != PPPOE_SNONE ) { 139752441Sjulian pppoe_send_event(sp, NGM_PPPOE_CLOSE); 139852441Sjulian } 139959728Sjulian /* 140059728Sjulian * According to the spec, if we are connected, 140159728Sjulian * we should send a DISC packet if we are shutting down 140259728Sjulian * a session. 140359728Sjulian */ 140452523Sjulian if ((privp->ethernet_hook) 140552523Sjulian && ((sp->state == PPPOE_CONNECTED) 140652523Sjulian || (sp->state == PPPOE_NEWCONNECTED))) { 140752523Sjulian struct mbuf *m; 140852523Sjulian struct pppoe_full_hdr *wh; 140952523Sjulian struct pppoe_tag *tag; 141052523Sjulian int msglen = strlen(SIGNOFF); 141152523Sjulian int error = 0; 141252523Sjulian 141352523Sjulian /* revert the stored header to DISC/PADT mode */ 141452523Sjulian wh = &sp->pkt_hdr; 141552523Sjulian wh->ph.code = PADT_CODE; 141652523Sjulian wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 141752523Sjulian 141852523Sjulian /* generate a packet of that type */ 141952523Sjulian MGETHDR(m, M_DONTWAIT, MT_DATA); 142053498Sjulian if(m == NULL) 142153498Sjulian printf("pppoe: Session out of mbufs\n"); 142253498Sjulian else { 142353498Sjulian m->m_pkthdr.rcvif = NULL; 142453498Sjulian m->m_pkthdr.len = m->m_len = sizeof(*wh); 142553498Sjulian bcopy((caddr_t)wh, mtod(m, caddr_t), 142653498Sjulian sizeof(*wh)); 142753498Sjulian /* 142853498Sjulian * Add a General error message and adjust 142953498Sjulian * sizes 143053498Sjulian */ 143153498Sjulian wh = mtod(m, struct pppoe_full_hdr *); 143253498Sjulian tag = wh->ph.tag; 143353498Sjulian tag->tag_type = PTT_GEN_ERR; 143453498Sjulian tag->tag_len = htons((u_int16_t)msglen); 143553498Sjulian strncpy(tag->tag_data, SIGNOFF, msglen); 143653498Sjulian m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 143753498Sjulian msglen); 143853498Sjulian wh->ph.length = htons(sizeof(*tag) + msglen); 143970700Sjulian NG_SEND_DATA_ONLY(error, 144070700Sjulian privp->ethernet_hook, m); 144153498Sjulian } 144252523Sjulian } 144359728Sjulian /* 144463138Sasmodai * As long as we have somewhere to store the timeout handle, 144559728Sjulian * we may have a timeout pending.. get rid of it. 144659728Sjulian */ 144752443Sjulian if (sp->neg) { 144852443Sjulian untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 144952443Sjulian if (sp->neg->m) 145052443Sjulian m_freem(sp->neg->m); 145152443Sjulian FREE(sp->neg, M_NETGRAPH); 145252443Sjulian } 145352419Sjulian FREE(sp, M_NETGRAPH); 145470784Sjulian NG_HOOK_SET_PRIVATE(hook, NULL); 145552564Sjulian /* work out how many session hooks there are */ 145652563Sjulian /* Node goes away on last session hook removal */ 145752563Sjulian if (privp->ethernet_hook) hooks -= 1; 145852564Sjulian if (privp->debug_hook) hooks -= 1; 145952419Sjulian } 146070784Sjulian if ((NG_NODE_NUMHOOKS(node) == 0) 146170784Sjulian && (NG_NODE_IS_VALID(node))) 146270700Sjulian ng_rmnode_self(node); 146352419Sjulian return (0); 146452419Sjulian} 146552419Sjulian 146652419Sjulian/* 146752419Sjulian * timeouts come here. 146852419Sjulian */ 146952419Sjulianstatic void 147052419Sjulianpppoe_ticker(void *arg) 147152419Sjulian{ 147252419Sjulian int s = splnet(); 147352419Sjulian hook_p hook = arg; 147470784Sjulian sessp sp = NG_HOOK_PRIVATE(hook); 147552419Sjulian negp neg = sp->neg; 147652419Sjulian int error = 0; 147752419Sjulian struct mbuf *m0 = NULL; 147870784Sjulian priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 147952419Sjulian 148052443SjulianAAA 148152419Sjulian switch(sp->state) { 148252419Sjulian /* 148352419Sjulian * resend the last packet, using an exponential backoff. 148452419Sjulian * After a period of time, stop growing the backoff, 148553145Sjulian * and either leave it, or revert to the start. 148652419Sjulian */ 148752419Sjulian case PPPOE_SINIT: 148852419Sjulian case PPPOE_SREQ: 148952419Sjulian /* timeouts on these produce resends */ 149052419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 149170700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 149252419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 149352419Sjulian hook, neg->timeout * hz); 149452419Sjulian if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 149552419Sjulian if (sp->state == PPPOE_SREQ) { 149652419Sjulian /* revert to SINIT mode */ 149752441Sjulian pppoe_start(sp); 149852419Sjulian } else { 149952419Sjulian neg->timeout = PPPOE_TIMEOUT_LIMIT; 150052419Sjulian } 150152419Sjulian } 150252419Sjulian break; 150352419Sjulian case PPPOE_PRIMED: 150452419Sjulian case PPPOE_SOFFER: 150552419Sjulian /* a timeout on these says "give up" */ 150652419Sjulian ng_destroy_hook(hook); 150752419Sjulian break; 150852419Sjulian default: 150952419Sjulian /* timeouts have no meaning in other states */ 151052419Sjulian printf("pppoe: unexpected timeout\n"); 151152419Sjulian } 151252419Sjulian splx(s); 151352419Sjulian} 151452419Sjulian 151552419Sjulian 151652419Sjulianstatic void 151752419Sjuliansendpacket(sessp sp) 151852419Sjulian{ 151952419Sjulian int error = 0; 152052419Sjulian struct mbuf *m0 = NULL; 152152419Sjulian hook_p hook = sp->hook; 152252419Sjulian negp neg = sp->neg; 152370784Sjulian priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 152452419Sjulian 152552443SjulianAAA 152652419Sjulian switch(sp->state) { 152752419Sjulian case PPPOE_LISTENING: 152852419Sjulian case PPPOE_DEAD: 152952419Sjulian case PPPOE_SNONE: 153052419Sjulian case PPPOE_CONNECTED: 153152448Sjulian printf("pppoe: sendpacket: unexpected state\n"); 153252419Sjulian break; 153352419Sjulian 153453498Sjulian case PPPOE_NEWCONNECTED: 153553498Sjulian /* send the PADS without a timeout - we're now connected */ 153653498Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 153770700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 153853498Sjulian break; 153953498Sjulian 154052419Sjulian case PPPOE_PRIMED: 154152419Sjulian /* No packet to send, but set up the timeout */ 154252419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 154352419Sjulian hook, PPPOE_OFFER_TIMEOUT * hz); 154452419Sjulian break; 154552419Sjulian 154652419Sjulian case PPPOE_SOFFER: 154752419Sjulian /* 154852419Sjulian * send the offer but if they don't respond 154952419Sjulian * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 155052419Sjulian */ 155152419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 155270700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 155352419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 155452419Sjulian hook, PPPOE_OFFER_TIMEOUT * hz); 155552419Sjulian break; 155652419Sjulian 155752419Sjulian case PPPOE_SINIT: 155852419Sjulian case PPPOE_SREQ: 155952419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 156070700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 156153979Sjulian neg->timeout_handle = timeout(pppoe_ticker, hook, 156253979Sjulian (hz * PPPOE_INITIAL_TIMEOUT)); 156353979Sjulian neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 156452419Sjulian break; 156552419Sjulian 156652419Sjulian default: 156752419Sjulian error = EINVAL; 156852419Sjulian printf("pppoe: timeout: bad state\n"); 156952419Sjulian } 157052419Sjulian /* return (error); */ 157152419Sjulian} 157252419Sjulian 157352419Sjulian/* 157452419Sjulian * Parse an incoming packet to see if any tags should be copied to the 157553145Sjulian * output packet. Don't do any tags that have been handled in the main 157653145Sjulian * state machine. 157752419Sjulian */ 157852419Sjulianstatic struct pppoe_tag* 157952419Sjulianscan_tags(sessp sp, struct pppoe_hdr* ph) 158052419Sjulian{ 158152419Sjulian char *end = (char *)next_tag(ph); 158252419Sjulian char *ptn; 158352419Sjulian struct pppoe_tag *pt = &ph->tag[0]; 158452419Sjulian /* 158552419Sjulian * Keep processing tags while a tag header will still fit. 158652419Sjulian */ 158752443SjulianAAA 158852419Sjulian while((char*)(pt + 1) <= end) { 158952419Sjulian /* 159052419Sjulian * If the tag data would go past the end of the packet, abort. 159152419Sjulian */ 159252419Sjulian ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 159352419Sjulian if(ptn > end) 159452419Sjulian return NULL; 159552419Sjulian 159652419Sjulian switch (pt->tag_type) { 159752419Sjulian case PTT_RELAY_SID: 159852419Sjulian insert_tag(sp, pt); 159952419Sjulian break; 160052419Sjulian case PTT_EOL: 160152419Sjulian return NULL; 160252419Sjulian case PTT_SRV_NAME: 160352419Sjulian case PTT_AC_NAME: 160452419Sjulian case PTT_HOST_UNIQ: 160552419Sjulian case PTT_AC_COOKIE: 160652419Sjulian case PTT_VENDOR: 160752419Sjulian case PTT_SRV_ERR: 160852419Sjulian case PTT_SYS_ERR: 160952419Sjulian case PTT_GEN_ERR: 161052419Sjulian break; 161152419Sjulian } 161252419Sjulian pt = (struct pppoe_tag*)ptn; 161352419Sjulian } 161452419Sjulian return NULL; 161552419Sjulian} 161652419Sjulian 161752441Sjulianstatic int 161852441Sjulianpppoe_send_event(sessp sp, enum cmd cmdid) 161952441Sjulian{ 162052441Sjulian int error; 162152441Sjulian struct ng_mesg *msg; 162252562Sjulian struct ngpppoe_sts *sts; 162352441Sjulian 162452443SjulianAAA 162568845Sbrian NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 162652562Sjulian sizeof(struct ngpppoe_sts), M_NOWAIT); 162769922Sjulian if (msg == NULL) 162869922Sjulian return (ENOMEM); 162952562Sjulian sts = (struct ngpppoe_sts *)msg->data; 163070784Sjulian strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKLEN + 1); 163170784Sjulian NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 163252441Sjulian return (error); 163352441Sjulian} 1634