ng_pppoe.c revision 70935
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 70935 2001-01-11 19:27:54Z 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 6370870Sjulian#ifdef NG_SEPARATE_MALLOC 6470870SjulianMALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 6570870Sjulian#else 6670870Sjulian#define M_NETGRAPH_PPPOE M_NETGRAPH 6770870Sjulian#endif 6870870Sjulian 6953405Sarchie#define SIGNOFF "session closed" 7068031Sbrian#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 7153405Sarchie 7252419Sjulian/* 7352419Sjulian * This section contains the netgraph method declarations for the 7452419Sjulian * sample node. These methods define the netgraph 'type'. 7552419Sjulian */ 7652419Sjulian 7752752Sjulianstatic ng_constructor_t ng_pppoe_constructor; 7852752Sjulianstatic ng_rcvmsg_t ng_pppoe_rcvmsg; 7970700Sjulianstatic ng_shutdown_t ng_pppoe_shutdown; 8052752Sjulianstatic ng_newhook_t ng_pppoe_newhook; 8152752Sjulianstatic ng_connect_t ng_pppoe_connect; 8252752Sjulianstatic ng_rcvdata_t ng_pppoe_rcvdata; 8352752Sjulianstatic ng_disconnect_t ng_pppoe_disconnect; 8452419Sjulian 8568031Sbrian/* Parse type for struct ngpppoe_init_data */ 8668845Sbrianstatic const struct ng_parse_struct_info ngpppoe_init_data_type_info 8768031Sbrian = NG_PPPOE_INIT_DATA_TYPE_INFO; 8868845Sbrianstatic const struct ng_parse_type ngpppoe_init_data_state_type = { 8968031Sbrian &ng_parse_struct_type, 9068845Sbrian &ngpppoe_init_data_type_info 9168031Sbrian}; 9268031Sbrian 9368031Sbrian/* Parse type for struct ngpppoe_sts */ 9468031Sbrianstatic const struct ng_parse_struct_info ng_pppoe_sts_type_info 9568031Sbrian = NG_PPPOE_STS_TYPE_INFO; 9668031Sbrianstatic const struct ng_parse_type ng_pppoe_sts_state_type = { 9768031Sbrian &ng_parse_struct_type, 9868031Sbrian &ng_pppoe_sts_type_info 9968031Sbrian}; 10068031Sbrian 10168031Sbrian/* List of commands and how to convert arguments to/from ASCII */ 10268031Sbrianstatic const struct ng_cmdlist ng_pppoe_cmds[] = { 10368031Sbrian { 10468031Sbrian NGM_PPPOE_COOKIE, 10568031Sbrian NGM_PPPOE_CONNECT, 10668031Sbrian "pppoe_connect", 10768845Sbrian &ngpppoe_init_data_state_type, 10868031Sbrian NULL 10968031Sbrian }, 11068031Sbrian { 11168031Sbrian NGM_PPPOE_COOKIE, 11268031Sbrian NGM_PPPOE_LISTEN, 11368031Sbrian "pppoe_listen", 11468845Sbrian &ngpppoe_init_data_state_type, 11568031Sbrian NULL 11668031Sbrian }, 11768031Sbrian { 11868031Sbrian NGM_PPPOE_COOKIE, 11968031Sbrian NGM_PPPOE_OFFER, 12068031Sbrian "pppoe_offer", 12168845Sbrian &ngpppoe_init_data_state_type, 12268031Sbrian NULL 12368031Sbrian }, 12468031Sbrian { 12568031Sbrian NGM_PPPOE_COOKIE, 12669922Sjulian NGM_PPPOE_SERVICE, 12769922Sjulian "pppoe_service", 12869922Sjulian &ngpppoe_init_data_state_type, 12969922Sjulian NULL 13069922Sjulian }, 13169922Sjulian { 13269922Sjulian NGM_PPPOE_COOKIE, 13368031Sbrian NGM_PPPOE_SUCCESS, 13468031Sbrian "pppoe_success", 13568031Sbrian &ng_pppoe_sts_state_type, 13668031Sbrian NULL 13768031Sbrian }, 13868031Sbrian { 13968031Sbrian NGM_PPPOE_COOKIE, 14068031Sbrian NGM_PPPOE_FAIL, 14168031Sbrian "pppoe_fail", 14268031Sbrian &ng_pppoe_sts_state_type, 14368031Sbrian NULL 14468031Sbrian }, 14568031Sbrian { 14668031Sbrian NGM_PPPOE_COOKIE, 14768031Sbrian NGM_PPPOE_CLOSE, 14868031Sbrian "pppoe_close", 14968031Sbrian &ng_pppoe_sts_state_type, 15068031Sbrian NULL 15168031Sbrian }, 15268031Sbrian { 0 } 15368031Sbrian}; 15468031Sbrian 15552419Sjulian/* Netgraph node type descriptor */ 15652419Sjulianstatic struct ng_type typestruct = { 15770159Sjulian NG_ABI_VERSION, 15852419Sjulian NG_PPPOE_NODE_TYPE, 15952419Sjulian NULL, 16052562Sjulian ng_pppoe_constructor, 16152562Sjulian ng_pppoe_rcvmsg, 16270700Sjulian ng_pppoe_shutdown, 16352562Sjulian ng_pppoe_newhook, 16452419Sjulian NULL, 16552562Sjulian ng_pppoe_connect, 16652562Sjulian ng_pppoe_rcvdata, 16753913Sarchie ng_pppoe_disconnect, 16868031Sbrian ng_pppoe_cmds 16952419Sjulian}; 17052562SjulianNETGRAPH_INIT(pppoe, &typestruct); 17152419Sjulian 17252419Sjulian/* 17352419Sjulian * States for the session state machine. 17452419Sjulian * These have no meaning if there is no hook attached yet. 17552419Sjulian */ 17652419Sjulianenum state { 17752419Sjulian PPPOE_SNONE=0, /* [both] Initial state */ 17853498Sjulian PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 17952419Sjulian PPPOE_SINIT, /* [Client] Sent discovery initiation */ 18053498Sjulian PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 18153498Sjulian PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 18252419Sjulian PPPOE_SREQ, /* [Client] Sent a Request */ 18353498Sjulian PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 18452419Sjulian PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 18552419Sjulian PPPOE_DEAD /* [Both] */ 18652419Sjulian}; 18752419Sjulian 18852419Sjulian#define NUMTAGS 20 /* number of tags we are set up to work with */ 18952419Sjulian 19052419Sjulian/* 19152419Sjulian * Information we store for each hook on each node for negotiating the 19252419Sjulian * session. The mbuf and cluster are freed once negotiation has completed. 19352419Sjulian * The whole negotiation block is then discarded. 19452419Sjulian */ 19552419Sjulian 19652419Sjulianstruct sess_neg { 19752419Sjulian struct mbuf *m; /* holds cluster with last sent packet */ 19852419Sjulian union packet *pkt; /* points within the above cluster */ 19952419Sjulian struct callout_handle timeout_handle; /* see timeout(9) */ 20052419Sjulian u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 20152419Sjulian u_int numtags; 20252419Sjulian struct pppoe_tag *tags[NUMTAGS]; 20352419Sjulian u_int service_len; 20452419Sjulian u_int ac_name_len; 20552419Sjulian 20652419Sjulian struct datatag service; 20752419Sjulian struct datatag ac_name; 20852419Sjulian}; 20952419Sjuliantypedef struct sess_neg *negp; 21052419Sjulian 21152419Sjulian/* 21252419Sjulian * Session information that is needed after connection. 21352419Sjulian */ 21466052Sarchiestruct sess_con { 21552419Sjulian hook_p hook; 21652419Sjulian u_int16_t Session_ID; 21752419Sjulian enum state state; 21870700Sjulian ng_ID_t creator; /* who to notify */ 21952419Sjulian struct pppoe_full_hdr pkt_hdr; /* used when connected */ 22052419Sjulian negp neg; /* used when negotiating */ 22166052Sarchie /*struct sess_con *hash_next;*/ /* not yet used */ 22252419Sjulian}; 22366052Sarchietypedef struct sess_con *sessp; 22452419Sjulian 22552419Sjulian/* 22652419Sjulian * Information we store for each node 22752419Sjulian */ 22852419Sjulianstruct PPPOE { 22952419Sjulian node_p node; /* back pointer to node */ 23052419Sjulian hook_p ethernet_hook; 23152419Sjulian hook_p debug_hook; 23252419Sjulian u_int packets_in; /* packets in from ethernet */ 23352419Sjulian u_int packets_out; /* packets out towards ethernet */ 23452419Sjulian u_int32_t flags; 23566052Sarchie /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ 23652419Sjulian}; 23752419Sjuliantypedef struct PPPOE *priv_p; 23852419Sjulian 23952419Sjulianconst struct ether_header eh_prototype = 24052419Sjulian {{0xff,0xff,0xff,0xff,0xff,0xff}, 24152419Sjulian {0x00,0x00,0x00,0x00,0x00,0x00}, 24252419Sjulian ETHERTYPE_PPPOE_DISC}; 24352419Sjulian 24452419Sjulianunion uniq { 24552419Sjulian char bytes[sizeof(void *)]; 24652419Sjulian void * pointer; 24752419Sjulian }; 24852419Sjulian 24952419Sjulian#define LEAVE(x) do { error = x; goto quit; } while(0) 25052419Sjulianstatic void pppoe_start(sessp sp); 25152419Sjulianstatic void sendpacket(sessp sp); 25252419Sjulianstatic void pppoe_ticker(void *arg); 25352419Sjulianstatic struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); 25452441Sjulianstatic int pppoe_send_event(sessp sp, enum cmd cmdid); 25552419Sjulian 25652419Sjulian/************************************************************************* 25752419Sjulian * Some basic utilities from the Linux version with author's permission.* 25852419Sjulian * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 25952419Sjulian ************************************************************************/ 26052419Sjulian 26152419Sjulian/* 26252419Sjulian * Generate a new session id 26353145Sjulian * XXX find out the FreeBSD locking scheme. 26452419Sjulian */ 26552419Sjulianstatic u_int16_t 26652419Sjulianget_new_sid(node_p node) 26752419Sjulian{ 26852419Sjulian static int pppoe_sid = 10; 26952419Sjulian sessp sp; 27052419Sjulian hook_p hook; 27152419Sjulian u_int16_t val; 27270784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 27352419Sjulian 27452443SjulianAAA 27552419Sjulianrestart: 27652419Sjulian val = pppoe_sid++; 27752419Sjulian /* 27852419Sjulian * Spec says 0xFFFF is reserved. 27952419Sjulian * Also don't use 0x0000 28052419Sjulian */ 28152419Sjulian if (val == 0xffff) { 28252419Sjulian pppoe_sid = 20; 28352419Sjulian goto restart; 28452419Sjulian } 28552419Sjulian 28652419Sjulian /* Check it isn't already in use */ 28770784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 28852419Sjulian /* don't check special hooks */ 28970784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 29070784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 29152419Sjulian continue; 29270784Sjulian sp = NG_HOOK_PRIVATE(hook); 29352419Sjulian if (sp->Session_ID == val) 29452419Sjulian goto restart; 29552419Sjulian } 29652419Sjulian 29752419Sjulian return val; 29852419Sjulian} 29952419Sjulian 30052419Sjulian 30152419Sjulian/* 30252419Sjulian * Return the location where the next tag can be put 30352419Sjulian */ 30452419Sjulianstatic __inline struct pppoe_tag* 30552419Sjuliannext_tag(struct pppoe_hdr* ph) 30652419Sjulian{ 30752419Sjulian return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); 30852419Sjulian} 30952419Sjulian 31052419Sjulian/* 31152419Sjulian * Look for a tag of a specific type 31252419Sjulian * Don't trust any length the other end says. 31352419Sjulian * but assume we already sanity checked ph->length. 31452419Sjulian */ 31552419Sjulianstatic struct pppoe_tag* 31652419Sjulianget_tag(struct pppoe_hdr* ph, u_int16_t idx) 31752419Sjulian{ 31852419Sjulian char *end = (char *)next_tag(ph); 31952419Sjulian char *ptn; 32052419Sjulian struct pppoe_tag *pt = &ph->tag[0]; 32152419Sjulian /* 32252419Sjulian * Keep processing tags while a tag header will still fit. 32352419Sjulian */ 32452443SjulianAAA 32552419Sjulian while((char*)(pt + 1) <= end) { 32652419Sjulian /* 32752419Sjulian * If the tag data would go past the end of the packet, abort. 32852419Sjulian */ 32952419Sjulian ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 33052419Sjulian if(ptn > end) 33152419Sjulian return NULL; 33252419Sjulian 33352419Sjulian if(pt->tag_type == idx) 33452419Sjulian return pt; 33552419Sjulian 33652419Sjulian pt = (struct pppoe_tag*)ptn; 33752419Sjulian } 33852419Sjulian return NULL; 33952419Sjulian} 34052419Sjulian 34152419Sjulian/************************************************************************** 34252419Sjulian * inlines to initialise or add tags to a session's tag list, 34352419Sjulian **************************************************************************/ 34452419Sjulian/* 34552419Sjulian * Initialise the session's tag list 34652419Sjulian */ 34752419Sjulianstatic void 34852419Sjulianinit_tags(sessp sp) 34952419Sjulian{ 35052443SjulianAAA 35152419Sjulian if(sp->neg == NULL) { 35252419Sjulian printf("pppoe: asked to init NULL neg pointer\n"); 35352419Sjulian return; 35452419Sjulian } 35552419Sjulian sp->neg->numtags = 0; 35652419Sjulian} 35752419Sjulian 35852419Sjulianstatic void 35952419Sjulianinsert_tag(sessp sp, struct pppoe_tag *tp) 36052419Sjulian{ 36152419Sjulian int i; 36252419Sjulian negp neg; 36352419Sjulian 36452443SjulianAAA 36552419Sjulian if((neg = sp->neg) == NULL) { 36652419Sjulian printf("pppoe: asked to use NULL neg pointer\n"); 36752419Sjulian return; 36852419Sjulian } 36952419Sjulian if ((i = neg->numtags++) < NUMTAGS) { 37052419Sjulian neg->tags[i] = tp; 37152419Sjulian } else { 37252419Sjulian printf("pppoe: asked to add too many tags to packet\n"); 37353042Sjulian neg->numtags--; 37452419Sjulian } 37552419Sjulian} 37652419Sjulian 37752419Sjulian/* 37852419Sjulian * Make up a packet, using the tags filled out for the session. 37952419Sjulian * 38052419Sjulian * Assume that the actual pppoe header and ethernet header 38152419Sjulian * are filled out externally to this routine. 38252419Sjulian * Also assume that neg->wh points to the correct 38352419Sjulian * location at the front of the buffer space. 38452419Sjulian */ 38552419Sjulianstatic void 38652419Sjulianmake_packet(sessp sp) { 38752419Sjulian struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 38852419Sjulian struct pppoe_tag **tag; 38952419Sjulian char *dp; 39052419Sjulian int count; 39152419Sjulian int tlen; 39252419Sjulian u_int16_t length = 0; 39352419Sjulian 39452443SjulianAAA 39552443Sjulian if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 39652419Sjulian printf("pppoe: make_packet called from wrong state\n"); 39752419Sjulian } 39852419Sjulian dp = (char *)wh->ph.tag; 39952419Sjulian for (count = 0, tag = sp->neg->tags; 40052419Sjulian ((count < sp->neg->numtags) && (count < NUMTAGS)); 40152419Sjulian tag++, count++) { 40252419Sjulian tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 40352419Sjulian if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 40452419Sjulian printf("pppoe: tags too long\n"); 40552419Sjulian sp->neg->numtags = count; 40652419Sjulian break; /* XXX chop off what's too long */ 40752419Sjulian } 40852419Sjulian bcopy((char *)*tag, (char *)dp, tlen); 40952419Sjulian length += tlen; 41052419Sjulian dp += tlen; 41152419Sjulian } 41252419Sjulian wh->ph.length = htons(length); 41352419Sjulian sp->neg->m->m_len = length + sizeof(*wh); 41452419Sjulian sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 41552419Sjulian} 41652419Sjulian 41752419Sjulian/************************************************************************** 41852419Sjulian * Routine to match a service offered * 41952419Sjulian **************************************************************************/ 42052419Sjulian/* 42152419Sjulian * Find a hook that has a service string that matches that 42252419Sjulian * we are seeking. for now use a simple string. 42352419Sjulian * In the future we may need something like regexp(). 42452419Sjulian * for testing allow a null string to match 1st found and a null service 42552419Sjulian * to match all requests. Also make '*' do the same. 42652419Sjulian */ 42752419Sjulianstatic hook_p 42852419Sjulianpppoe_match_svc(node_p node, char *svc_name, int svc_len) 42952419Sjulian{ 43052419Sjulian sessp sp = NULL; 43152419Sjulian negp neg = NULL; 43270784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 43352419Sjulian hook_p hook; 43452419Sjulian 43552443SjulianAAA 43670784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 43752419Sjulian 43852419Sjulian /* skip any hook that is debug or ethernet */ 43970784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 44070784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 44152419Sjulian continue; 44270784Sjulian sp = NG_HOOK_PRIVATE(hook); 44352419Sjulian 44452419Sjulian /* Skip any sessions which are not in LISTEN mode. */ 44552419Sjulian if ( sp->state != PPPOE_LISTENING) 44652419Sjulian continue; 44752419Sjulian 44852419Sjulian neg = sp->neg; 44952419Sjulian /* XXX check validity of this */ 45052419Sjulian /* special case, NULL request. match 1st found. */ 45152419Sjulian if (svc_len == 0) 45252419Sjulian break; 45352419Sjulian 45452419Sjulian /* XXX check validity of this */ 45552419Sjulian /* Special case for a blank or "*" service name (wildcard) */ 45652419Sjulian if ((neg->service_len == 0) 45752419Sjulian || ((neg->service_len == 1) 45852419Sjulian && (neg->service.data[0] == '*'))) { 45952419Sjulian break; 46052419Sjulian } 46152419Sjulian 46252419Sjulian /* If the lengths don't match, that aint it. */ 46352419Sjulian if (neg->service_len != svc_len) 46452419Sjulian continue; 46552419Sjulian 46652419Sjulian /* An exact match? */ 46752419Sjulian if (strncmp(svc_name, neg->service.data, svc_len) == 0) 46852419Sjulian break; 46952419Sjulian } 47052419Sjulian return (hook); 47152419Sjulian} 47252419Sjulian/************************************************************************** 47352419Sjulian * Routine to find a particular session that matches an incoming packet * 47452419Sjulian **************************************************************************/ 47552419Sjulianstatic hook_p 47652419Sjulianpppoe_findsession(node_p node, struct pppoe_full_hdr *wh) 47752419Sjulian{ 47852419Sjulian sessp sp = NULL; 47952419Sjulian hook_p hook = NULL; 48070784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 48152448Sjulian u_int16_t session = ntohs(wh->ph.sid); 48252419Sjulian 48352419Sjulian /* 48452419Sjulian * find matching peer/session combination. 48552419Sjulian */ 48652443SjulianAAA 48770784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 48852419Sjulian /* don't check special hooks */ 48970784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 49070784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 49152419Sjulian continue; 49252419Sjulian } 49370784Sjulian sp = NG_HOOK_PRIVATE(hook); 49452419Sjulian if ( ( (sp->state == PPPOE_CONNECTED) 49552419Sjulian || (sp->state == PPPOE_NEWCONNECTED) ) 49652419Sjulian && (sp->Session_ID == session) 49752419Sjulian && (bcmp(sp->pkt_hdr.eh.ether_dhost, 49852419Sjulian wh->eh.ether_shost, 49952419Sjulian ETHER_ADDR_LEN)) == 0) { 50052419Sjulian break; 50152419Sjulian } 50252419Sjulian } 50352419Sjulian return (hook); 50452419Sjulian} 50552419Sjulian 50652419Sjulianstatic hook_p 50752419Sjulianpppoe_finduniq(node_p node, struct pppoe_tag *tag) 50852419Sjulian{ 50952419Sjulian hook_p hook = NULL; 51070784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 51152419Sjulian union uniq uniq; 51252419Sjulian 51352443SjulianAAA 51452419Sjulian bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 51552419Sjulian /* cycle through all known hooks */ 51670784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 51752419Sjulian /* don't check special hooks */ 51870784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 51970784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 52052419Sjulian continue; 52170784Sjulian if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 52252419Sjulian break; 52352419Sjulian } 52452419Sjulian return (hook); 52552419Sjulian} 52652419Sjulian 52752419Sjulian/************************************************************************** 52852419Sjulian * start of Netgraph entrypoints * 52952419Sjulian **************************************************************************/ 53052419Sjulian 53152419Sjulian/* 53252419Sjulian * Allocate the private data structure and the generic node 53352419Sjulian * and link them together. 53452419Sjulian * 53552419Sjulian * ng_make_node_common() returns with a generic node struct 53652419Sjulian * with a single reference for us.. we transfer it to the 53752419Sjulian * private structure.. when we free the private struct we must 53852419Sjulian * unref the node so it gets freed too. 53952419Sjulian */ 54052419Sjulianstatic int 54170700Sjulianng_pppoe_constructor(node_p node) 54252419Sjulian{ 54352419Sjulian priv_p privdata; 54452419Sjulian 54552443SjulianAAA 54652419Sjulian /* Initialize private descriptor */ 54770870Sjulian MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH_PPPOE, 54868876Sdwmalone M_NOWAIT | M_ZERO); 54952419Sjulian if (privdata == NULL) 55052419Sjulian return (ENOMEM); 55152419Sjulian 55252419Sjulian /* Link structs together; this counts as our one reference to *nodep */ 55370784Sjulian NG_NODE_SET_PRIVATE(node, privdata); 55470700Sjulian privdata->node = node; 55552419Sjulian return (0); 55652419Sjulian} 55752419Sjulian 55852419Sjulian/* 55952419Sjulian * Give our ok for a hook to be added... 56052419Sjulian * point the hook's private info to the hook structure. 56152419Sjulian * 56252419Sjulian * The following hook names are special: 56352419Sjulian * Ethernet: the hook that should be connected to a NIC. 56452419Sjulian * debug: copies of data sent out here (when I write the code). 56569922Sjulian * All other hook names need only be unique. (the framework checks this). 56652419Sjulian */ 56752419Sjulianstatic int 56852562Sjulianng_pppoe_newhook(node_p node, hook_p hook, const char *name) 56952419Sjulian{ 57070784Sjulian const priv_p privp = NG_NODE_PRIVATE(node); 57152419Sjulian sessp sp; 57252419Sjulian 57352443SjulianAAA 57452419Sjulian if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 57552419Sjulian privp->ethernet_hook = hook; 57670784Sjulian NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 57752419Sjulian } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 57852419Sjulian privp->debug_hook = hook; 57970784Sjulian NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 58052419Sjulian } else { 58152419Sjulian /* 58252419Sjulian * Any other unique name is OK. 58352419Sjulian * The infrastructure has already checked that it's unique, 58452419Sjulian * so just allocate it and hook it in. 58552419Sjulian */ 58670870Sjulian MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 58752419Sjulian if (sp == NULL) { 58852419Sjulian return (ENOMEM); 58952419Sjulian } 59052419Sjulian 59170784Sjulian NG_HOOK_SET_PRIVATE(hook, sp); 59252419Sjulian sp->hook = hook; 59352419Sjulian } 59452419Sjulian return(0); 59552419Sjulian} 59652419Sjulian 59752419Sjulian/* 59852419Sjulian * Get a netgraph control message. 59952419Sjulian * Check it is one we understand. If needed, send a response. 60052419Sjulian * We sometimes save the address for an async action later. 60152419Sjulian * Always free the message. 60252419Sjulian */ 60352419Sjulianstatic int 60470700Sjulianng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 60552419Sjulian{ 60670784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 60752562Sjulian struct ngpppoe_init_data *ourmsg = NULL; 60852419Sjulian struct ng_mesg *resp = NULL; 60952419Sjulian int error = 0; 61052419Sjulian hook_p hook = NULL; 61152419Sjulian sessp sp = NULL; 61252419Sjulian negp neg = NULL; 61370700Sjulian struct ng_mesg *msg; 61452419Sjulian 61552443SjulianAAA 61670700Sjulian NGI_GET_MSG(item, msg); 61752419Sjulian /* Deal with message according to cookie and command */ 61852419Sjulian switch (msg->header.typecookie) { 61952419Sjulian case NGM_PPPOE_COOKIE: 62052419Sjulian switch (msg->header.cmd) { 62152419Sjulian case NGM_PPPOE_CONNECT: 62252419Sjulian case NGM_PPPOE_LISTEN: 62352419Sjulian case NGM_PPPOE_OFFER: 62469922Sjulian case NGM_PPPOE_SERVICE: 62568845Sbrian ourmsg = (struct ngpppoe_init_data *)msg->data; 62668845Sbrian if (msg->header.arglen < sizeof(*ourmsg)) { 62768845Sbrian printf("pppoe: init data too small\n"); 62868845Sbrian LEAVE(EMSGSIZE); 62968031Sbrian } 63068031Sbrian if (msg->header.arglen - sizeof(*ourmsg) > 63168031Sbrian PPPOE_SERVICE_NAME_SIZE) { 63268031Sbrian printf("pppoe_rcvmsg: service name too big"); 63352419Sjulian LEAVE(EMSGSIZE); 63452419Sjulian } 63568845Sbrian if (msg->header.arglen - sizeof(*ourmsg) < 63668845Sbrian ourmsg->data_len) { 63768845Sbrian printf("pppoe: init data has bad length," 63868845Sbrian " %d should be %d\n", ourmsg->data_len, 63968845Sbrian msg->header.arglen - sizeof (*ourmsg)); 64052419Sjulian LEAVE(EMSGSIZE); 64152419Sjulian } 64268031Sbrian 64352419Sjulian /* make sure strcmp will terminate safely */ 64452419Sjulian ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 64552419Sjulian 64652419Sjulian /* cycle through all known hooks */ 64770784Sjulian LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 64870784Sjulian if (NG_HOOK_NAME(hook) 64970784Sjulian && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 65052419Sjulian break; 65152419Sjulian } 65252419Sjulian if (hook == NULL) { 65352419Sjulian LEAVE(ENOENT); 65452419Sjulian } 65570784Sjulian if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 65670784Sjulian || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 65752419Sjulian LEAVE(EINVAL); 65852419Sjulian } 65970784Sjulian sp = NG_HOOK_PRIVATE(hook); 66069922Sjulian 66169922Sjulian /* 66269922Sjulian * PPPOE_SERVICE advertisments are set up 66369922Sjulian * on sessions that are in PRIMED state. 66469922Sjulian */ 66569922Sjulian if (msg->header.cmd == NGM_PPPOE_SERVICE) { 66669922Sjulian break; 66769922Sjulian } 66852419Sjulian if (sp->state |= PPPOE_SNONE) { 66952419Sjulian printf("pppoe: Session already active\n"); 67052419Sjulian LEAVE(EISCONN); 67152419Sjulian } 67252443Sjulian 67352419Sjulian /* 67452419Sjulian * set up prototype header 67552419Sjulian */ 67670870Sjulian MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH_PPPOE, 67768876Sdwmalone M_NOWAIT | M_ZERO); 67852419Sjulian 67952419Sjulian if (neg == NULL) { 68052419Sjulian printf("pppoe: Session out of memory\n"); 68152419Sjulian LEAVE(ENOMEM); 68252419Sjulian } 68352419Sjulian MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 68452419Sjulian if(neg->m == NULL) { 68552443Sjulian printf("pppoe: Session out of mbufs\n"); 68670870Sjulian FREE(neg, M_NETGRAPH_PPPOE); 68752419Sjulian LEAVE(ENOBUFS); 68852419Sjulian } 68952419Sjulian neg->m->m_pkthdr.rcvif = NULL; 69052419Sjulian MCLGET(neg->m, M_DONTWAIT); 69152419Sjulian if ((neg->m->m_flags & M_EXT) == 0) { 69252443Sjulian printf("pppoe: Session out of mcls\n"); 69352419Sjulian m_freem(neg->m); 69470870Sjulian FREE(neg, M_NETGRAPH_PPPOE); 69552419Sjulian LEAVE(ENOBUFS); 69652419Sjulian } 69752419Sjulian sp->neg = neg; 69852443Sjulian callout_handle_init( &neg->timeout_handle); 69952419Sjulian neg->m->m_len = sizeof(struct pppoe_full_hdr); 70052419Sjulian neg->pkt = mtod(neg->m, union packet*); 70152419Sjulian neg->pkt->pkt_header.eh = eh_prototype; 70252419Sjulian neg->pkt->pkt_header.ph.ver = 0x1; 70352419Sjulian neg->pkt->pkt_header.ph.type = 0x1; 70452419Sjulian neg->pkt->pkt_header.ph.sid = 0x0000; 70552419Sjulian neg->timeout = 0; 70652419Sjulian 70770700Sjulian sp->creator = NGI_RETADDR(item); 70852419Sjulian } 70952419Sjulian switch (msg->header.cmd) { 71052419Sjulian case NGM_PPPOE_GET_STATUS: 71152419Sjulian { 71252562Sjulian struct ngpppoestat *stats; 71352419Sjulian 71452419Sjulian NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 71552419Sjulian if (!resp) { 71652419Sjulian LEAVE(ENOMEM); 71752419Sjulian } 71852562Sjulian stats = (struct ngpppoestat *) resp->data; 71952419Sjulian stats->packets_in = privp->packets_in; 72052419Sjulian stats->packets_out = privp->packets_out; 72152419Sjulian break; 72252419Sjulian } 72352419Sjulian case NGM_PPPOE_CONNECT: 72452419Sjulian /* 72552419Sjulian * Check the hook exists and is Uninitialised. 72652419Sjulian * Send a PADI request, and start the timeout logic. 72752419Sjulian * Store the originator of this message so we can send 72852419Sjulian * a success of fail message to them later. 72952419Sjulian * Move the session to SINIT 73052419Sjulian * Set up the session to the correct state and 73152419Sjulian * start it. 73252419Sjulian */ 73352419Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 73468845Sbrian neg->service.hdr.tag_len = 73568845Sbrian htons((u_int16_t)ourmsg->data_len); 73668845Sbrian if (ourmsg->data_len) 73768845Sbrian bcopy(ourmsg->data, neg->service.data, 73868845Sbrian ourmsg->data_len); 73968845Sbrian neg->service_len = ourmsg->data_len; 74052419Sjulian pppoe_start(sp); 74152419Sjulian break; 74252419Sjulian case NGM_PPPOE_LISTEN: 74352419Sjulian /* 74452419Sjulian * Check the hook exists and is Uninitialised. 74552419Sjulian * Install the service matching string. 74652419Sjulian * Store the originator of this message so we can send 74752419Sjulian * a success of fail message to them later. 74852419Sjulian * Move the hook to 'LISTENING' 74952419Sjulian */ 75052419Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 75168845Sbrian neg->service.hdr.tag_len = 75268845Sbrian htons((u_int16_t)ourmsg->data_len); 75352443Sjulian 75468845Sbrian if (ourmsg->data_len) 75568845Sbrian bcopy(ourmsg->data, neg->service.data, 75668845Sbrian ourmsg->data_len); 75768845Sbrian neg->service_len = ourmsg->data_len; 75852419Sjulian neg->pkt->pkt_header.ph.code = PADT_CODE; 75952419Sjulian /* 76052419Sjulian * wait for PADI packet coming from ethernet 76152419Sjulian */ 76252419Sjulian sp->state = PPPOE_LISTENING; 76352419Sjulian break; 76452419Sjulian case NGM_PPPOE_OFFER: 76552419Sjulian /* 76652419Sjulian * Check the hook exists and is Uninitialised. 76752419Sjulian * Store the originator of this message so we can send 76852419Sjulian * a success of fail message to them later. 76952419Sjulian * Store the AC-Name given and go to PRIMED. 77052419Sjulian */ 77152419Sjulian neg->ac_name.hdr.tag_type = PTT_AC_NAME; 77268845Sbrian neg->ac_name.hdr.tag_len = 77368845Sbrian htons((u_int16_t)ourmsg->data_len); 77468845Sbrian if (ourmsg->data_len) 77568845Sbrian bcopy(ourmsg->data, neg->ac_name.data, 77668845Sbrian ourmsg->data_len); 77768845Sbrian neg->ac_name_len = ourmsg->data_len; 77852419Sjulian neg->pkt->pkt_header.ph.code = PADO_CODE; 77952419Sjulian /* 78052419Sjulian * Wait for PADI packet coming from hook 78152419Sjulian */ 78252419Sjulian sp->state = PPPOE_PRIMED; 78352419Sjulian break; 78469922Sjulian case NGM_PPPOE_SERVICE: 78569922Sjulian /* 78669922Sjulian * Check the session is primed. 78769922Sjulian * for now just allow ONE service to be advertised. 78869922Sjulian * If you do it twice you just overwrite. 78969922Sjulian */ 79070148Sjulian if (sp->state != PPPOE_PRIMED) { 79169922Sjulian printf("pppoe: Session not primed\n"); 79269922Sjulian LEAVE(EISCONN); 79369922Sjulian } 79470931Sjulian neg = sp->neg; 79569922Sjulian neg->service.hdr.tag_type = PTT_SRV_NAME; 79669922Sjulian neg->service.hdr.tag_len = 79769922Sjulian htons((u_int16_t)ourmsg->data_len); 79869922Sjulian 79969922Sjulian if (ourmsg->data_len) 80069922Sjulian bcopy(ourmsg->data, neg->service.data, 80169922Sjulian ourmsg->data_len); 80269922Sjulian neg->service_len = ourmsg->data_len; 80369922Sjulian break; 80452419Sjulian default: 80552419Sjulian LEAVE(EINVAL); 80652419Sjulian } 80752419Sjulian break; 80852419Sjulian default: 80952419Sjulian LEAVE(EINVAL); 81052419Sjulian } 81152419Sjulian 81252419Sjulian /* Take care of synchronous response, if any */ 81370700Sjulianquit: 81470700Sjulian NG_RESPOND_MSG(error, node, item, resp); 81552419Sjulian /* Free the message and return */ 81670700Sjulian NG_FREE_MSG(msg); 81752419Sjulian return(error); 81852419Sjulian} 81952419Sjulian 82052443Sjulian/* 82152443Sjulian * Start a client into the first state. A separate function because 82252443Sjulian * it can be needed if the negotiation times out. 82352443Sjulian */ 82452419Sjulianstatic void 82552419Sjulianpppoe_start(sessp sp) 82652419Sjulian{ 82752419Sjulian struct { 82852419Sjulian struct pppoe_tag hdr; 82952419Sjulian union uniq data; 83052419Sjulian } uniqtag; 83152419Sjulian 83252419Sjulian /* 83352419Sjulian * kick the state machine into starting up 83452419Sjulian */ 83552443SjulianAAA 83652419Sjulian sp->state = PPPOE_SINIT; 83752443Sjulian /* reset the packet header to broadcast */ 83852443Sjulian sp->neg->pkt->pkt_header.eh = eh_prototype; 83952443Sjulian sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 84052419Sjulian uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 84152419Sjulian uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 84252419Sjulian uniqtag.data.pointer = sp; 84352419Sjulian init_tags(sp); 84468079Sjulian insert_tag(sp, &uniqtag.hdr); 84553154Sjulian insert_tag(sp, &sp->neg->service.hdr); 84652419Sjulian make_packet(sp); 84752419Sjulian sendpacket(sp); 84852419Sjulian} 84952419Sjulian 85052419Sjulian/* 85152419Sjulian * Receive data, and do something with it. 85252419Sjulian * The caller will never free m or meta, so 85352419Sjulian * if we use up this data or abort we must free BOTH of these. 85452419Sjulian */ 85552419Sjulianstatic int 85670700Sjulianng_pppoe_rcvdata(hook_p hook, item_p item) 85752419Sjulian{ 85870784Sjulian node_p node = NG_HOOK_NODE(hook); 85970784Sjulian const priv_p privp = NG_NODE_PRIVATE(node); 86070784Sjulian sessp sp = NG_HOOK_PRIVATE(hook); 86152419Sjulian struct pppoe_full_hdr *wh; 86252419Sjulian struct pppoe_hdr *ph; 86352419Sjulian int error = 0; 86452419Sjulian u_int16_t session; 86552419Sjulian u_int16_t length; 86652419Sjulian u_int8_t code; 86753154Sjulian struct pppoe_tag *utag = NULL, *tag = NULL; 86852419Sjulian hook_p sendhook; 86952419Sjulian struct { 87052419Sjulian struct pppoe_tag hdr; 87152419Sjulian union uniq data; 87252419Sjulian } uniqtag; 87352419Sjulian negp neg = NULL; 87470700Sjulian struct mbuf *m; 87552419Sjulian 87652443SjulianAAA 87770700Sjulian NGI_GET_M(item, m); 87870784Sjulian if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 87952419Sjulian /* 88052419Sjulian * Data from the debug hook gets sent without modification 88152419Sjulian * straight to the ethernet. 88252419Sjulian */ 88370784Sjulian NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 88452419Sjulian privp->packets_out++; 88570784Sjulian } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 88652419Sjulian /* 88752419Sjulian * Incoming data. 88852419Sjulian * Dig out various fields from the packet. 88952419Sjulian * use them to decide where to send it. 89052419Sjulian */ 89152419Sjulian 89252419Sjulian privp->packets_in++; 89352510Sjulian if( m->m_len < sizeof(*wh)) { 89452510Sjulian m = m_pullup(m, sizeof(*wh)); /* Checks length */ 89552510Sjulian if (m == NULL) { 89652510Sjulian printf("couldn't m_pullup\n"); 89752510Sjulian LEAVE(ENOBUFS); 89852510Sjulian } 89952419Sjulian } 90052419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 90152419Sjulian length = ntohs(wh->ph.length); 90252510Sjulian switch(wh->eh.ether_type) { 90352419Sjulian case ETHERTYPE_PPPOE_DISC: 90452419Sjulian /* 90553172Sjulian * We need to try to make sure that the tag area 90653172Sjulian * is contiguous, or we could wander off the end 90752419Sjulian * of a buffer and make a mess. 90852419Sjulian * (Linux wouldn't have this problem). 90952419Sjulian */ 91052510Sjulian if (m->m_pkthdr.len <= MHLEN) { 91152510Sjulian if( m->m_len < m->m_pkthdr.len) { 91252510Sjulian m = m_pullup(m, m->m_pkthdr.len); 91352510Sjulian if (m == NULL) { 91452510Sjulian printf("couldn't m_pullup\n"); 91552510Sjulian LEAVE(ENOBUFS); 91652510Sjulian } 91752510Sjulian } 91852510Sjulian } 91952510Sjulian if (m->m_len != m->m_pkthdr.len) { 92052419Sjulian /* 92152419Sjulian * It's not all in one piece. 92252419Sjulian * We need to do extra work. 92370700Sjulian * Put it into a cluster. 92452419Sjulian */ 92570700Sjulian struct mbuf *n; 92670700Sjulian n = m_dup(m, M_DONTWAIT); 92770700Sjulian m_freem(m); 92870700Sjulian m = n; 92970700Sjulian if (m) { 93070700Sjulian /* just check we got a cluster */ 93170700Sjulian if (m->m_len != m->m_pkthdr.len) { 93270700Sjulian m_freem(m); 93370700Sjulian m = NULL; 93470700Sjulian } 93570700Sjulian } 93670700Sjulian if (m == NULL) { 93770700Sjulian printf("packet fragmented\n"); 93870700Sjulian LEAVE(EMSGSIZE); 93970700Sjulian } 94053498Sjulian } 94170700Sjulian wh = mtod(m, struct pppoe_full_hdr *); 94270700Sjulian length = ntohs(wh->ph.length); 94370700Sjulian ph = &wh->ph; 94470700Sjulian session = ntohs(wh->ph.sid); 94570700Sjulian code = wh->ph.code; 94652419Sjulian 94752419Sjulian switch(code) { 94852419Sjulian case PADI_CODE: 94952419Sjulian /* 95052419Sjulian * We are a server: 95152419Sjulian * Look for a hook with the required service 95252419Sjulian * and send the ENTIRE packet up there. 95352419Sjulian * It should come back to a new hook in 95452419Sjulian * PRIMED state. Look there for further 95552419Sjulian * processing. 95652419Sjulian */ 95752419Sjulian tag = get_tag(ph, PTT_SRV_NAME); 95852419Sjulian if (tag == NULL) { 95952448Sjulian printf("no service tag\n"); 96052419Sjulian LEAVE(ENETUNREACH); 96152419Sjulian } 96270784Sjulian sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 96352419Sjulian tag->tag_data, ntohs(tag->tag_len)); 96452419Sjulian if (sendhook) { 96570700Sjulian NG_FWD_NEW_DATA(error, item, 96670700Sjulian sendhook, m); 96752419Sjulian } else { 96852448Sjulian printf("no such service\n"); 96952419Sjulian LEAVE(ENETUNREACH); 97052419Sjulian } 97152419Sjulian break; 97252419Sjulian case PADO_CODE: 97352419Sjulian /* 97452419Sjulian * We are a client: 97552419Sjulian * Use the host_uniq tag to find the 97652419Sjulian * hook this is in response to. 97752448Sjulian * Received #2, now send #3 97852419Sjulian * For now simply accept the first we receive. 97952419Sjulian */ 98053154Sjulian utag = get_tag(ph, PTT_HOST_UNIQ); 98153154Sjulian if ((utag == NULL) 98253154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 98352448Sjulian printf("no host unique field\n"); 98452419Sjulian LEAVE(ENETUNREACH); 98552419Sjulian } 98652419Sjulian 98753154Sjulian sendhook = pppoe_finduniq(node, utag); 98852419Sjulian if (sendhook == NULL) { 98952448Sjulian printf("no matching session\n"); 99052419Sjulian LEAVE(ENETUNREACH); 99152419Sjulian } 99252419Sjulian 99352419Sjulian /* 99452419Sjulian * Check the session is in the right state. 99552419Sjulian * It needs to be in PPPOE_SINIT. 99652419Sjulian */ 99770784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 99852419Sjulian if (sp->state != PPPOE_SINIT) { 99952448Sjulian printf("session in wrong state\n"); 100052419Sjulian LEAVE(ENETUNREACH); 100152419Sjulian } 100252419Sjulian neg = sp->neg; 100352419Sjulian untimeout(pppoe_ticker, sendhook, 100452419Sjulian neg->timeout_handle); 100552419Sjulian 100652419Sjulian /* 100752419Sjulian * This is the first time we hear 100852419Sjulian * from the server, so note it's 100952419Sjulian * unicast address, replacing the 101052419Sjulian * broadcast address . 101152419Sjulian */ 101252419Sjulian bcopy(wh->eh.ether_shost, 101352419Sjulian neg->pkt->pkt_header.eh.ether_dhost, 101452419Sjulian ETHER_ADDR_LEN); 101552419Sjulian neg->timeout = 0; 101652419Sjulian neg->pkt->pkt_header.ph.code = PADR_CODE; 101752419Sjulian init_tags(sp); 101868079Sjulian insert_tag(sp, utag); /* Host Unique */ 101953154Sjulian if ((tag = get_tag(ph, PTT_AC_COOKIE))) 102052448Sjulian insert_tag(sp, tag); /* return cookie */ 102153154Sjulian if ((tag = get_tag(ph, PTT_AC_NAME))) 102253154Sjulian insert_tag(sp, tag); /* return it */ 102368079Sjulian insert_tag(sp, &neg->service.hdr); /* Service */ 102452419Sjulian scan_tags(sp, ph); 102552419Sjulian make_packet(sp); 102652419Sjulian sp->state = PPPOE_SREQ; 102752419Sjulian sendpacket(sp); 102852419Sjulian break; 102952419Sjulian case PADR_CODE: 103052419Sjulian 103152419Sjulian /* 103252419Sjulian * We are a server: 103352419Sjulian * Use the ac_cookie tag to find the 103452419Sjulian * hook this is in response to. 103552419Sjulian */ 103653154Sjulian utag = get_tag(ph, PTT_AC_COOKIE); 103753154Sjulian if ((utag == NULL) 103853154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 103952419Sjulian LEAVE(ENETUNREACH); 104052419Sjulian } 104152419Sjulian 104253154Sjulian sendhook = pppoe_finduniq(node, utag); 104352419Sjulian if (sendhook == NULL) { 104452419Sjulian LEAVE(ENETUNREACH); 104552419Sjulian } 104652419Sjulian 104752419Sjulian /* 104852419Sjulian * Check the session is in the right state. 104952419Sjulian * It needs to be in PPPOE_SOFFER 105052419Sjulian * or PPPOE_NEWCONNECTED. If the latter, 105152419Sjulian * then this is a retry by the client. 105252419Sjulian * so be nice, and resend. 105352419Sjulian */ 105470784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 105552419Sjulian if (sp->state == PPPOE_NEWCONNECTED) { 105652419Sjulian /* 105752419Sjulian * Whoa! drop back to resend that 105852419Sjulian * PADS packet. 105952419Sjulian * We should still have a copy of it. 106052419Sjulian */ 106152419Sjulian sp->state = PPPOE_SOFFER; 106252419Sjulian } 106352419Sjulian if (sp->state != PPPOE_SOFFER) { 106452419Sjulian LEAVE (ENETUNREACH); 106552419Sjulian break; 106652419Sjulian } 106752419Sjulian neg = sp->neg; 106852419Sjulian untimeout(pppoe_ticker, sendhook, 106952419Sjulian neg->timeout_handle); 107052419Sjulian neg->pkt->pkt_header.ph.code = PADS_CODE; 107152419Sjulian if (sp->Session_ID == 0) 107252419Sjulian neg->pkt->pkt_header.ph.sid = 107352448Sjulian htons(sp->Session_ID 107452448Sjulian = get_new_sid(node)); 107552419Sjulian neg->timeout = 0; 107652419Sjulian /* 107752419Sjulian * start working out the tags to respond with. 107852419Sjulian */ 107952419Sjulian init_tags(sp); 108052419Sjulian insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 108153172Sjulian if ((tag = get_tag(ph, PTT_SRV_NAME))) 108253145Sjulian insert_tag(sp, tag);/* return service */ 108353154Sjulian if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 108453145Sjulian insert_tag(sp, tag); /* return it */ 108553154Sjulian insert_tag(sp, utag); /* ac_cookie */ 108652419Sjulian scan_tags(sp, ph); 108752419Sjulian make_packet(sp); 108853498Sjulian sp->state = PPPOE_NEWCONNECTED; 108953172Sjulian sendpacket(sp); 109052419Sjulian /* 109152419Sjulian * Having sent the last Negotiation header, 109252419Sjulian * Set up the stored packet header to 109352419Sjulian * be correct for the actual session. 109452419Sjulian * But keep the negotialtion stuff 109552419Sjulian * around in case we need to resend this last 109652419Sjulian * packet. We'll discard it when we move 109752419Sjulian * from NEWCONNECTED to CONNECTED 109852419Sjulian */ 109952419Sjulian sp->pkt_hdr = neg->pkt->pkt_header; 110052419Sjulian sp->pkt_hdr.eh.ether_type 110152419Sjulian = ETHERTYPE_PPPOE_SESS; 110252419Sjulian sp->pkt_hdr.ph.code = 0; 110352441Sjulian pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 110452419Sjulian break; 110552419Sjulian case PADS_CODE: 110652419Sjulian /* 110752419Sjulian * We are a client: 110852419Sjulian * Use the host_uniq tag to find the 110952419Sjulian * hook this is in response to. 111052419Sjulian * take the session ID and store it away. 111152419Sjulian * Also make sure the pre-made header is 111252419Sjulian * correct and set us into Session mode. 111352419Sjulian */ 111453154Sjulian utag = get_tag(ph, PTT_HOST_UNIQ); 111553154Sjulian if ((utag == NULL) 111653154Sjulian || (ntohs(utag->tag_len) != sizeof(sp))) { 111752419Sjulian LEAVE (ENETUNREACH); 111852419Sjulian break; 111952419Sjulian } 112053154Sjulian sendhook = pppoe_finduniq(node, utag); 112152419Sjulian if (sendhook == NULL) { 112252419Sjulian LEAVE(ENETUNREACH); 112352419Sjulian } 112452419Sjulian 112552419Sjulian /* 112652419Sjulian * Check the session is in the right state. 112752419Sjulian * It needs to be in PPPOE_SREQ. 112852419Sjulian */ 112970784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 113052419Sjulian if (sp->state != PPPOE_SREQ) { 113152419Sjulian LEAVE(ENETUNREACH); 113252419Sjulian } 113352419Sjulian neg = sp->neg; 113452419Sjulian untimeout(pppoe_ticker, sendhook, 113552419Sjulian neg->timeout_handle); 113652524Sjulian neg->pkt->pkt_header.ph.sid = wh->ph.sid; 113752448Sjulian sp->Session_ID = ntohs(wh->ph.sid); 113852419Sjulian neg->timeout = 0; 113952419Sjulian sp->state = PPPOE_CONNECTED; 114052419Sjulian /* 114152419Sjulian * Now we have gone to Connected mode, 114252419Sjulian * Free all resources needed for 114352419Sjulian * negotiation. 114452419Sjulian * Keep a copy of the header we will be using. 114552419Sjulian */ 114652419Sjulian sp->pkt_hdr = neg->pkt->pkt_header; 114752419Sjulian sp->pkt_hdr.eh.ether_type 114852419Sjulian = ETHERTYPE_PPPOE_SESS; 114952419Sjulian sp->pkt_hdr.ph.code = 0; 115052419Sjulian m_freem(neg->m); 115170870Sjulian FREE(sp->neg, M_NETGRAPH_PPPOE); 115252419Sjulian sp->neg = NULL; 115352441Sjulian pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 115452419Sjulian break; 115552419Sjulian case PADT_CODE: 115652419Sjulian /* 115752419Sjulian * Send a 'close' message to the controlling 115852419Sjulian * process (the one that set us up); 115952419Sjulian * And then tear everything down. 116052419Sjulian * 116152419Sjulian * Find matching peer/session combination. 116252419Sjulian */ 116352419Sjulian sendhook = pppoe_findsession(node, wh); 116452419Sjulian if (sendhook == NULL) { 116552419Sjulian LEAVE(ENETUNREACH); 116652419Sjulian } 116752419Sjulian /* send message to creator */ 116852419Sjulian /* close hook */ 116952441Sjulian if (sendhook) { 117070935Sjulian ng_rmhook_self(sendhook); 117152441Sjulian } 117252419Sjulian break; 117352419Sjulian default: 117452419Sjulian LEAVE(EPFNOSUPPORT); 117552419Sjulian } 117652419Sjulian break; 117752419Sjulian case ETHERTYPE_PPPOE_SESS: 117852419Sjulian /* 117952419Sjulian * find matching peer/session combination. 118052419Sjulian */ 118152419Sjulian sendhook = pppoe_findsession(node, wh); 118252419Sjulian if (sendhook == NULL) { 118352419Sjulian LEAVE (ENETUNREACH); 118452419Sjulian break; 118552419Sjulian } 118670784Sjulian sp = NG_HOOK_PRIVATE(sendhook); 118752419Sjulian m_adj(m, sizeof(*wh)); 118852419Sjulian if (m->m_pkthdr.len < length) { 118952419Sjulian /* Packet too short, dump it */ 119052419Sjulian LEAVE(EMSGSIZE); 119152419Sjulian } 119252523Sjulian 119353145Sjulian /* Also need to trim excess at the end */ 119452523Sjulian if (m->m_pkthdr.len > length) { 119552523Sjulian m_adj(m, -((int)(m->m_pkthdr.len - length))); 119652523Sjulian } 119752419Sjulian if ( sp->state != PPPOE_CONNECTED) { 119852419Sjulian if (sp->state == PPPOE_NEWCONNECTED) { 119952419Sjulian sp->state = PPPOE_CONNECTED; 120052419Sjulian /* 120152419Sjulian * Now we have gone to Connected mode, 120252419Sjulian * Free all resources needed for 120359728Sjulian * negotiation. Be paranoid about 120459728Sjulian * whether there may be a timeout. 120552419Sjulian */ 120652419Sjulian m_freem(sp->neg->m); 120759728Sjulian untimeout(pppoe_ticker, sendhook, 120859728Sjulian sp->neg->timeout_handle); 120970870Sjulian FREE(sp->neg, M_NETGRAPH_PPPOE); 121052419Sjulian sp->neg = NULL; 121152419Sjulian } else { 121252419Sjulian LEAVE (ENETUNREACH); 121352419Sjulian break; 121452419Sjulian } 121552419Sjulian } 121670700Sjulian NG_FWD_NEW_DATA( error, item, sendhook, m); 121752419Sjulian break; 121852419Sjulian default: 121952522Sjulian LEAVE(EPFNOSUPPORT); 122052419Sjulian } 122152419Sjulian } else { 122252419Sjulian /* 122352419Sjulian * Not ethernet or debug hook.. 122452419Sjulian * 122552419Sjulian * The packet has come in on a normal hook. 122652419Sjulian * We need to find out what kind of hook, 122752419Sjulian * So we can decide how to handle it. 122852419Sjulian * Check the hook's state. 122952419Sjulian */ 123070784Sjulian sp = NG_HOOK_PRIVATE(hook); 123152419Sjulian switch (sp->state) { 123252419Sjulian case PPPOE_NEWCONNECTED: 123352419Sjulian case PPPOE_CONNECTED: { 123464502Sarchie static const u_char addrctrl[] = { 0xff, 0x03 }; 123552419Sjulian struct pppoe_full_hdr *wh; 123664502Sarchie 123752419Sjulian /* 123864502Sarchie * Remove PPP address and control fields, if any. 123964502Sarchie * For example, ng_ppp(4) always sends LCP packets 124064502Sarchie * with address and control fields as required by 124164502Sarchie * generic PPP. PPPoE is an exception to the rule. 124264502Sarchie */ 124364502Sarchie if (m->m_pkthdr.len >= 2) { 124464502Sarchie if (m->m_len < 2 && !(m = m_pullup(m, 2))) 124564502Sarchie LEAVE(ENOBUFS); 124664502Sarchie if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 124764502Sarchie m_adj(m, 2); 124864502Sarchie } 124964502Sarchie /* 125052419Sjulian * Bang in a pre-made header, and set the length up 125152419Sjulian * to be correct. Then send it to the ethernet driver. 125252614Sjulian * But first correct the length. 125352419Sjulian */ 125452614Sjulian sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 125552419Sjulian M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 125652419Sjulian if (m == NULL) { 125752419Sjulian LEAVE(ENOBUFS); 125852419Sjulian } 125952419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 126052419Sjulian bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 126170700Sjulian NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 126252419Sjulian privp->packets_out++; 126352419Sjulian break; 126452419Sjulian } 126552419Sjulian case PPPOE_PRIMED: 126652419Sjulian /* 126752419Sjulian * A PADI packet is being returned by the application 126852419Sjulian * that has set up this hook. This indicates that it 126952419Sjulian * wants us to offer service. 127052419Sjulian */ 127152419Sjulian neg = sp->neg; 127253172Sjulian if (m->m_len < sizeof(*wh)) { 127353172Sjulian m = m_pullup(m, sizeof(*wh)); 127453172Sjulian if (m == NULL) { 127553172Sjulian LEAVE(ENOBUFS); 127653172Sjulian } 127752419Sjulian } 127852419Sjulian wh = mtod(m, struct pppoe_full_hdr *); 127952419Sjulian ph = &wh->ph; 128052419Sjulian session = ntohs(wh->ph.sid); 128152419Sjulian length = ntohs(wh->ph.length); 128252419Sjulian code = wh->ph.code; 128352443Sjulian if ( code != PADI_CODE) { 128452443Sjulian LEAVE(EINVAL); 128552443Sjulian }; 128652443Sjulian untimeout(pppoe_ticker, hook, 128752443Sjulian neg->timeout_handle); 128852419Sjulian 128952419Sjulian /* 129052419Sjulian * This is the first time we hear 129152419Sjulian * from the client, so note it's 129252419Sjulian * unicast address, replacing the 129353145Sjulian * broadcast address. 129452419Sjulian */ 129552419Sjulian bcopy(wh->eh.ether_shost, 129652419Sjulian neg->pkt->pkt_header.eh.ether_dhost, 129752419Sjulian ETHER_ADDR_LEN); 129852419Sjulian sp->state = PPPOE_SOFFER; 129952419Sjulian neg->timeout = 0; 130052419Sjulian neg->pkt->pkt_header.ph.code = PADO_CODE; 130152419Sjulian 130252419Sjulian /* 130352419Sjulian * start working out the tags to respond with. 130452419Sjulian */ 130552419Sjulian uniqtag.hdr.tag_type = PTT_AC_COOKIE; 130652419Sjulian uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 130752419Sjulian uniqtag.data.pointer = sp; 130852419Sjulian init_tags(sp); 130952419Sjulian insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 131053154Sjulian if ((tag = get_tag(ph, PTT_SRV_NAME))) 131153154Sjulian insert_tag(sp, tag); /* return service */ 131269922Sjulian /* 131369922Sjulian * If we have a NULL service request 131469922Sjulian * and have an extra service defined in this hook, 131569922Sjulian * then also add a tag for the extra service. 131669922Sjulian * XXX this is a hack. eventually we should be able 131769922Sjulian * to support advertising many services, not just one 131869922Sjulian */ 131969922Sjulian if (((tag == NULL) || (tag->tag_len == 0)) 132069922Sjulian && (neg->service.hdr.tag_len != 0)) { 132169922Sjulian insert_tag(sp, &neg->service.hdr); /* SERVICE */ 132269922Sjulian } 132353154Sjulian if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 132453145Sjulian insert_tag(sp, tag); /* returned hostunique */ 132553154Sjulian insert_tag(sp, &uniqtag.hdr); 132652419Sjulian scan_tags(sp, ph); 132752419Sjulian make_packet(sp); 132852419Sjulian sendpacket(sp); 132952419Sjulian break; 133052419Sjulian 133152419Sjulian /* 133252419Sjulian * Packets coming from the hook make no sense 133352419Sjulian * to sessions in these states. Throw them away. 133452419Sjulian */ 133552419Sjulian case PPPOE_SINIT: 133652419Sjulian case PPPOE_SREQ: 133752419Sjulian case PPPOE_SOFFER: 133852419Sjulian case PPPOE_SNONE: 133952419Sjulian case PPPOE_LISTENING: 134052419Sjulian case PPPOE_DEAD: 134152419Sjulian default: 134252419Sjulian LEAVE(ENETUNREACH); 134352419Sjulian } 134452419Sjulian } 134552419Sjulianquit: 134670914Sjulian if (item) 134770914Sjulian NG_FREE_ITEM(item); 134870700Sjulian NG_FREE_M(m); 134952419Sjulian return error; 135052419Sjulian} 135152419Sjulian 135252419Sjulian/* 135352419Sjulian * Do local shutdown processing.. 135452419Sjulian * If we are a persistant device, we might refuse to go away, and 135552419Sjulian * we'd only remove our links and reset ourself. 135652419Sjulian */ 135752419Sjulianstatic int 135870700Sjulianng_pppoe_shutdown(node_p node) 135952419Sjulian{ 136070784Sjulian const priv_p privdata = NG_NODE_PRIVATE(node); 136152419Sjulian 136252443SjulianAAA 136370784Sjulian NG_NODE_SET_PRIVATE(node, NULL); 136470784Sjulian NG_NODE_UNREF(privdata->node); 136570870Sjulian FREE(privdata, M_NETGRAPH_PPPOE); 136652419Sjulian return (0); 136752419Sjulian} 136852419Sjulian 136952419Sjulian/* 137052419Sjulian * This is called once we've already connected a new hook to the other node. 137152419Sjulian * It gives us a chance to balk at the last minute. 137252419Sjulian */ 137352419Sjulianstatic int 137452562Sjulianng_pppoe_connect(hook_p hook) 137552419Sjulian{ 137652419Sjulian /* be really amiable and just say "YUP that's OK by me! " */ 137752419Sjulian return (0); 137852419Sjulian} 137952419Sjulian 138052419Sjulian/* 138152419Sjulian * Hook disconnection 138252419Sjulian * 138353498Sjulian * Clean up all dangling links and information about the session/hook. 138452419Sjulian * For this type, removal of the last link destroys the node 138552419Sjulian */ 138652419Sjulianstatic int 138752562Sjulianng_pppoe_disconnect(hook_p hook) 138852419Sjulian{ 138970784Sjulian node_p node = NG_HOOK_NODE(hook); 139070784Sjulian priv_p privp = NG_NODE_PRIVATE(node); 139152419Sjulian sessp sp; 139252563Sjulian int hooks; 139352419Sjulian 139452443SjulianAAA 139570784Sjulian hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 139670784Sjulian if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 139752419Sjulian privp->debug_hook = NULL; 139870784Sjulian } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 139952419Sjulian privp->ethernet_hook = NULL; 140070784Sjulian if (NG_NODE_IS_VALID(node)) 140170700Sjulian ng_rmnode_self(node); 140252419Sjulian } else { 140370784Sjulian sp = NG_HOOK_PRIVATE(hook); 140452441Sjulian if (sp->state != PPPOE_SNONE ) { 140552441Sjulian pppoe_send_event(sp, NGM_PPPOE_CLOSE); 140652441Sjulian } 140759728Sjulian /* 140859728Sjulian * According to the spec, if we are connected, 140959728Sjulian * we should send a DISC packet if we are shutting down 141059728Sjulian * a session. 141159728Sjulian */ 141252523Sjulian if ((privp->ethernet_hook) 141352523Sjulian && ((sp->state == PPPOE_CONNECTED) 141452523Sjulian || (sp->state == PPPOE_NEWCONNECTED))) { 141552523Sjulian struct mbuf *m; 141652523Sjulian struct pppoe_full_hdr *wh; 141752523Sjulian struct pppoe_tag *tag; 141852523Sjulian int msglen = strlen(SIGNOFF); 141952523Sjulian int error = 0; 142052523Sjulian 142152523Sjulian /* revert the stored header to DISC/PADT mode */ 142252523Sjulian wh = &sp->pkt_hdr; 142352523Sjulian wh->ph.code = PADT_CODE; 142452523Sjulian wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 142552523Sjulian 142652523Sjulian /* generate a packet of that type */ 142752523Sjulian MGETHDR(m, M_DONTWAIT, MT_DATA); 142853498Sjulian if(m == NULL) 142953498Sjulian printf("pppoe: Session out of mbufs\n"); 143053498Sjulian else { 143153498Sjulian m->m_pkthdr.rcvif = NULL; 143253498Sjulian m->m_pkthdr.len = m->m_len = sizeof(*wh); 143353498Sjulian bcopy((caddr_t)wh, mtod(m, caddr_t), 143453498Sjulian sizeof(*wh)); 143553498Sjulian /* 143653498Sjulian * Add a General error message and adjust 143753498Sjulian * sizes 143853498Sjulian */ 143953498Sjulian wh = mtod(m, struct pppoe_full_hdr *); 144053498Sjulian tag = wh->ph.tag; 144153498Sjulian tag->tag_type = PTT_GEN_ERR; 144253498Sjulian tag->tag_len = htons((u_int16_t)msglen); 144353498Sjulian strncpy(tag->tag_data, SIGNOFF, msglen); 144453498Sjulian m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 144553498Sjulian msglen); 144653498Sjulian wh->ph.length = htons(sizeof(*tag) + msglen); 144770700Sjulian NG_SEND_DATA_ONLY(error, 144870700Sjulian privp->ethernet_hook, m); 144953498Sjulian } 145052523Sjulian } 145159728Sjulian /* 145263138Sasmodai * As long as we have somewhere to store the timeout handle, 145359728Sjulian * we may have a timeout pending.. get rid of it. 145459728Sjulian */ 145552443Sjulian if (sp->neg) { 145652443Sjulian untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 145752443Sjulian if (sp->neg->m) 145852443Sjulian m_freem(sp->neg->m); 145970870Sjulian FREE(sp->neg, M_NETGRAPH_PPPOE); 146052443Sjulian } 146170870Sjulian FREE(sp, M_NETGRAPH_PPPOE); 146270784Sjulian NG_HOOK_SET_PRIVATE(hook, NULL); 146352564Sjulian /* work out how many session hooks there are */ 146452563Sjulian /* Node goes away on last session hook removal */ 146552563Sjulian if (privp->ethernet_hook) hooks -= 1; 146652564Sjulian if (privp->debug_hook) hooks -= 1; 146752419Sjulian } 146870784Sjulian if ((NG_NODE_NUMHOOKS(node) == 0) 146970784Sjulian && (NG_NODE_IS_VALID(node))) 147070700Sjulian ng_rmnode_self(node); 147152419Sjulian return (0); 147252419Sjulian} 147352419Sjulian 147452419Sjulian/* 147552419Sjulian * timeouts come here. 147652419Sjulian */ 147752419Sjulianstatic void 147852419Sjulianpppoe_ticker(void *arg) 147952419Sjulian{ 148052419Sjulian int s = splnet(); 148152419Sjulian hook_p hook = arg; 148270784Sjulian sessp sp = NG_HOOK_PRIVATE(hook); 148352419Sjulian negp neg = sp->neg; 148452419Sjulian int error = 0; 148552419Sjulian struct mbuf *m0 = NULL; 148670784Sjulian priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 148752419Sjulian 148852443SjulianAAA 148952419Sjulian switch(sp->state) { 149052419Sjulian /* 149152419Sjulian * resend the last packet, using an exponential backoff. 149252419Sjulian * After a period of time, stop growing the backoff, 149353145Sjulian * and either leave it, or revert to the start. 149452419Sjulian */ 149552419Sjulian case PPPOE_SINIT: 149652419Sjulian case PPPOE_SREQ: 149752419Sjulian /* timeouts on these produce resends */ 149852419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 149970700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 150052419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 150152419Sjulian hook, neg->timeout * hz); 150252419Sjulian if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 150352419Sjulian if (sp->state == PPPOE_SREQ) { 150452419Sjulian /* revert to SINIT mode */ 150552441Sjulian pppoe_start(sp); 150652419Sjulian } else { 150752419Sjulian neg->timeout = PPPOE_TIMEOUT_LIMIT; 150852419Sjulian } 150952419Sjulian } 151052419Sjulian break; 151152419Sjulian case PPPOE_PRIMED: 151252419Sjulian case PPPOE_SOFFER: 151352419Sjulian /* a timeout on these says "give up" */ 151470935Sjulian ng_rmhook_self(hook); 151552419Sjulian break; 151652419Sjulian default: 151752419Sjulian /* timeouts have no meaning in other states */ 151852419Sjulian printf("pppoe: unexpected timeout\n"); 151952419Sjulian } 152052419Sjulian splx(s); 152152419Sjulian} 152252419Sjulian 152352419Sjulian 152452419Sjulianstatic void 152552419Sjuliansendpacket(sessp sp) 152652419Sjulian{ 152752419Sjulian int error = 0; 152852419Sjulian struct mbuf *m0 = NULL; 152952419Sjulian hook_p hook = sp->hook; 153052419Sjulian negp neg = sp->neg; 153170784Sjulian priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 153252419Sjulian 153352443SjulianAAA 153452419Sjulian switch(sp->state) { 153552419Sjulian case PPPOE_LISTENING: 153652419Sjulian case PPPOE_DEAD: 153752419Sjulian case PPPOE_SNONE: 153852419Sjulian case PPPOE_CONNECTED: 153952448Sjulian printf("pppoe: sendpacket: unexpected state\n"); 154052419Sjulian break; 154152419Sjulian 154253498Sjulian case PPPOE_NEWCONNECTED: 154353498Sjulian /* send the PADS without a timeout - we're now connected */ 154453498Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 154570700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 154653498Sjulian break; 154753498Sjulian 154852419Sjulian case PPPOE_PRIMED: 154952419Sjulian /* No packet to send, but set up the timeout */ 155052419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 155152419Sjulian hook, PPPOE_OFFER_TIMEOUT * hz); 155252419Sjulian break; 155352419Sjulian 155452419Sjulian case PPPOE_SOFFER: 155552419Sjulian /* 155652419Sjulian * send the offer but if they don't respond 155752419Sjulian * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 155852419Sjulian */ 155952419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 156070700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 156152419Sjulian neg->timeout_handle = timeout(pppoe_ticker, 156252419Sjulian hook, PPPOE_OFFER_TIMEOUT * hz); 156352419Sjulian break; 156452419Sjulian 156552419Sjulian case PPPOE_SINIT: 156652419Sjulian case PPPOE_SREQ: 156752419Sjulian m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 156870700Sjulian NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 156953979Sjulian neg->timeout_handle = timeout(pppoe_ticker, hook, 157053979Sjulian (hz * PPPOE_INITIAL_TIMEOUT)); 157153979Sjulian neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 157252419Sjulian break; 157352419Sjulian 157452419Sjulian default: 157552419Sjulian error = EINVAL; 157652419Sjulian printf("pppoe: timeout: bad state\n"); 157752419Sjulian } 157852419Sjulian /* return (error); */ 157952419Sjulian} 158052419Sjulian 158152419Sjulian/* 158252419Sjulian * Parse an incoming packet to see if any tags should be copied to the 158353145Sjulian * output packet. Don't do any tags that have been handled in the main 158453145Sjulian * state machine. 158552419Sjulian */ 158652419Sjulianstatic struct pppoe_tag* 158752419Sjulianscan_tags(sessp sp, struct pppoe_hdr* ph) 158852419Sjulian{ 158952419Sjulian char *end = (char *)next_tag(ph); 159052419Sjulian char *ptn; 159152419Sjulian struct pppoe_tag *pt = &ph->tag[0]; 159252419Sjulian /* 159352419Sjulian * Keep processing tags while a tag header will still fit. 159452419Sjulian */ 159552443SjulianAAA 159652419Sjulian while((char*)(pt + 1) <= end) { 159752419Sjulian /* 159852419Sjulian * If the tag data would go past the end of the packet, abort. 159952419Sjulian */ 160052419Sjulian ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 160152419Sjulian if(ptn > end) 160252419Sjulian return NULL; 160352419Sjulian 160452419Sjulian switch (pt->tag_type) { 160552419Sjulian case PTT_RELAY_SID: 160652419Sjulian insert_tag(sp, pt); 160752419Sjulian break; 160852419Sjulian case PTT_EOL: 160952419Sjulian return NULL; 161052419Sjulian case PTT_SRV_NAME: 161152419Sjulian case PTT_AC_NAME: 161252419Sjulian case PTT_HOST_UNIQ: 161352419Sjulian case PTT_AC_COOKIE: 161452419Sjulian case PTT_VENDOR: 161552419Sjulian case PTT_SRV_ERR: 161652419Sjulian case PTT_SYS_ERR: 161752419Sjulian case PTT_GEN_ERR: 161852419Sjulian break; 161952419Sjulian } 162052419Sjulian pt = (struct pppoe_tag*)ptn; 162152419Sjulian } 162252419Sjulian return NULL; 162352419Sjulian} 162452419Sjulian 162552441Sjulianstatic int 162652441Sjulianpppoe_send_event(sessp sp, enum cmd cmdid) 162752441Sjulian{ 162852441Sjulian int error; 162952441Sjulian struct ng_mesg *msg; 163052562Sjulian struct ngpppoe_sts *sts; 163152441Sjulian 163252443SjulianAAA 163368845Sbrian NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 163452562Sjulian sizeof(struct ngpppoe_sts), M_NOWAIT); 163569922Sjulian if (msg == NULL) 163669922Sjulian return (ENOMEM); 163752562Sjulian sts = (struct ngpppoe_sts *)msg->data; 163870784Sjulian strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKLEN + 1); 163970784Sjulian NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 164052441Sjulian return (error); 164152441Sjulian} 1642