1da092930SArchie Cobbs 24cf49a43SJulian Elischer /* 34cf49a43SJulian Elischer * ng_pppoe.c 44cf49a43SJulian Elischer * 54cf49a43SJulian Elischer * Copyright (c) 1996-1999 Whistle Communications, Inc. 64cf49a43SJulian Elischer * All rights reserved. 74cf49a43SJulian Elischer * 84cf49a43SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 94cf49a43SJulian Elischer * redistribution of this software, in source or object code forms, with or 104cf49a43SJulian Elischer * without modifications are expressly permitted by Whistle Communications; 114cf49a43SJulian Elischer * provided, however, that: 124cf49a43SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 134cf49a43SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 144cf49a43SJulian Elischer * 2. No rights are granted, in any manner or form, to use Whistle 154cf49a43SJulian Elischer * Communications, Inc. trademarks, including the mark "WHISTLE 164cf49a43SJulian Elischer * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 174cf49a43SJulian Elischer * such appears in the above copyright notice or in the software. 184cf49a43SJulian Elischer * 194cf49a43SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 204cf49a43SJulian Elischer * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 214cf49a43SJulian Elischer * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 224cf49a43SJulian Elischer * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 234cf49a43SJulian Elischer * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 244cf49a43SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 254cf49a43SJulian Elischer * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 264cf49a43SJulian Elischer * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 274cf49a43SJulian Elischer * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 284cf49a43SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 294cf49a43SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 304cf49a43SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 314cf49a43SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 324cf49a43SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 334cf49a43SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 344cf49a43SJulian Elischer * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 354cf49a43SJulian Elischer * OF SUCH DAMAGE. 364cf49a43SJulian Elischer * 37cc3bbd68SJulian Elischer * Author: Julian Elischer <julian@freebsd.org> 384cf49a43SJulian Elischer * 394cf49a43SJulian Elischer * $FreeBSD$ 4074f5c6aaSJulian Elischer * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $ 414cf49a43SJulian Elischer */ 421e2510f8SJulian Elischer #if 0 431e2510f8SJulian Elischer #define AAA printf("pppoe: %s\n", __FUNCTION__ ); 440c65c135SJulian Elischer #define BBB printf("-%d-", __LINE__ ); 451e2510f8SJulian Elischer #else 461e2510f8SJulian Elischer #define AAA 470c65c135SJulian Elischer #define BBB 481e2510f8SJulian Elischer #endif 494cf49a43SJulian Elischer 504cf49a43SJulian Elischer #include <sys/param.h> 514cf49a43SJulian Elischer #include <sys/systm.h> 524cf49a43SJulian Elischer #include <sys/kernel.h> 534cf49a43SJulian Elischer #include <sys/mbuf.h> 544cf49a43SJulian Elischer #include <sys/malloc.h> 554cf49a43SJulian Elischer #include <sys/errno.h> 56bfa7e882SJulian Elischer #include <sys/sysctl.h> 574cf49a43SJulian Elischer #include <net/ethernet.h> 584cf49a43SJulian Elischer 594cf49a43SJulian Elischer #include <netgraph/ng_message.h> 604cf49a43SJulian Elischer #include <netgraph/netgraph.h> 6176a70671SBrian Somers #include <netgraph/ng_parse.h> 624cf49a43SJulian Elischer #include <netgraph/ng_pppoe.h> 634cf49a43SJulian Elischer 649c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC 659c8c302fSJulian Elischer MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 669c8c302fSJulian Elischer #else 679c8c302fSJulian Elischer #define M_NETGRAPH_PPPOE M_NETGRAPH 689c8c302fSJulian Elischer #endif 699c8c302fSJulian Elischer 70da092930SArchie Cobbs #define SIGNOFF "session closed" 7176a70671SBrian Somers #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 72da092930SArchie Cobbs 734cf49a43SJulian Elischer /* 744cf49a43SJulian Elischer * This section contains the netgraph method declarations for the 75bfa7e882SJulian Elischer * pppoe node. These methods define the netgraph pppoe 'type'. 764cf49a43SJulian Elischer */ 774cf49a43SJulian Elischer 7874f5c6aaSJulian Elischer static ng_constructor_t ng_pppoe_constructor; 7974f5c6aaSJulian Elischer static ng_rcvmsg_t ng_pppoe_rcvmsg; 80069154d5SJulian Elischer static ng_shutdown_t ng_pppoe_shutdown; 8174f5c6aaSJulian Elischer static ng_newhook_t ng_pppoe_newhook; 8274f5c6aaSJulian Elischer static ng_connect_t ng_pppoe_connect; 8374f5c6aaSJulian Elischer static ng_rcvdata_t ng_pppoe_rcvdata; 8474f5c6aaSJulian Elischer static ng_disconnect_t ng_pppoe_disconnect; 854cf49a43SJulian Elischer 8676a70671SBrian Somers /* Parse type for struct ngpppoe_init_data */ 8727121ab1SBrian Somers static const struct ng_parse_struct_info ngpppoe_init_data_type_info 8876a70671SBrian Somers = NG_PPPOE_INIT_DATA_TYPE_INFO; 8927121ab1SBrian Somers static const struct ng_parse_type ngpppoe_init_data_state_type = { 9076a70671SBrian Somers &ng_parse_struct_type, 9127121ab1SBrian Somers &ngpppoe_init_data_type_info 9276a70671SBrian Somers }; 9376a70671SBrian Somers 9476a70671SBrian Somers /* Parse type for struct ngpppoe_sts */ 9576a70671SBrian Somers static const struct ng_parse_struct_info ng_pppoe_sts_type_info 9676a70671SBrian Somers = NG_PPPOE_STS_TYPE_INFO; 9776a70671SBrian Somers static const struct ng_parse_type ng_pppoe_sts_state_type = { 9876a70671SBrian Somers &ng_parse_struct_type, 9976a70671SBrian Somers &ng_pppoe_sts_type_info 10076a70671SBrian Somers }; 10176a70671SBrian Somers 10276a70671SBrian Somers /* List of commands and how to convert arguments to/from ASCII */ 10376a70671SBrian Somers static const struct ng_cmdlist ng_pppoe_cmds[] = { 10476a70671SBrian Somers { 10576a70671SBrian Somers NGM_PPPOE_COOKIE, 10676a70671SBrian Somers NGM_PPPOE_CONNECT, 10776a70671SBrian Somers "pppoe_connect", 10827121ab1SBrian Somers &ngpppoe_init_data_state_type, 10976a70671SBrian Somers NULL 11076a70671SBrian Somers }, 11176a70671SBrian Somers { 11276a70671SBrian Somers NGM_PPPOE_COOKIE, 11376a70671SBrian Somers NGM_PPPOE_LISTEN, 11476a70671SBrian Somers "pppoe_listen", 11527121ab1SBrian Somers &ngpppoe_init_data_state_type, 11676a70671SBrian Somers NULL 11776a70671SBrian Somers }, 11876a70671SBrian Somers { 11976a70671SBrian Somers NGM_PPPOE_COOKIE, 12076a70671SBrian Somers NGM_PPPOE_OFFER, 12176a70671SBrian Somers "pppoe_offer", 12227121ab1SBrian Somers &ngpppoe_init_data_state_type, 12376a70671SBrian Somers NULL 12476a70671SBrian Somers }, 12576a70671SBrian Somers { 12676a70671SBrian Somers NGM_PPPOE_COOKIE, 127859a4d16SJulian Elischer NGM_PPPOE_SERVICE, 128859a4d16SJulian Elischer "pppoe_service", 129859a4d16SJulian Elischer &ngpppoe_init_data_state_type, 130859a4d16SJulian Elischer NULL 131859a4d16SJulian Elischer }, 132859a4d16SJulian Elischer { 133859a4d16SJulian Elischer NGM_PPPOE_COOKIE, 13476a70671SBrian Somers NGM_PPPOE_SUCCESS, 13576a70671SBrian Somers "pppoe_success", 13676a70671SBrian Somers &ng_pppoe_sts_state_type, 13776a70671SBrian Somers NULL 13876a70671SBrian Somers }, 13976a70671SBrian Somers { 14076a70671SBrian Somers NGM_PPPOE_COOKIE, 14176a70671SBrian Somers NGM_PPPOE_FAIL, 14276a70671SBrian Somers "pppoe_fail", 14376a70671SBrian Somers &ng_pppoe_sts_state_type, 14476a70671SBrian Somers NULL 14576a70671SBrian Somers }, 14676a70671SBrian Somers { 14776a70671SBrian Somers NGM_PPPOE_COOKIE, 14876a70671SBrian Somers NGM_PPPOE_CLOSE, 14976a70671SBrian Somers "pppoe_close", 15076a70671SBrian Somers &ng_pppoe_sts_state_type, 15176a70671SBrian Somers NULL 15276a70671SBrian Somers }, 15376a70671SBrian Somers { 0 } 15476a70671SBrian Somers }; 15576a70671SBrian Somers 1564cf49a43SJulian Elischer /* Netgraph node type descriptor */ 1574cf49a43SJulian Elischer static struct ng_type typestruct = { 158589f6ed8SJulian Elischer NG_ABI_VERSION, 1594cf49a43SJulian Elischer NG_PPPOE_NODE_TYPE, 1604cf49a43SJulian Elischer NULL, 1618876b55dSJulian Elischer ng_pppoe_constructor, 1628876b55dSJulian Elischer ng_pppoe_rcvmsg, 163069154d5SJulian Elischer ng_pppoe_shutdown, 1648876b55dSJulian Elischer ng_pppoe_newhook, 1654cf49a43SJulian Elischer NULL, 1668876b55dSJulian Elischer ng_pppoe_connect, 1678876b55dSJulian Elischer ng_pppoe_rcvdata, 168f8307e12SArchie Cobbs ng_pppoe_disconnect, 16976a70671SBrian Somers ng_pppoe_cmds 1704cf49a43SJulian Elischer }; 1718876b55dSJulian Elischer NETGRAPH_INIT(pppoe, &typestruct); 1721acb27c6SJulian Elischer /* Depend on ng_ether so we can use the Ethernet parse type */ 1731acb27c6SJulian Elischer MODULE_DEPEND(ng_pppoe, ng_ether, 1, 1, 1); 1744cf49a43SJulian Elischer 1754cf49a43SJulian Elischer /* 1764cf49a43SJulian Elischer * States for the session state machine. 1774cf49a43SJulian Elischer * These have no meaning if there is no hook attached yet. 1784cf49a43SJulian Elischer */ 1794cf49a43SJulian Elischer enum state { 1804cf49a43SJulian Elischer PPPOE_SNONE=0, /* [both] Initial state */ 1816faf164cSJulian Elischer PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 1824cf49a43SJulian Elischer PPPOE_SINIT, /* [Client] Sent discovery initiation */ 1836faf164cSJulian Elischer PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 1846faf164cSJulian Elischer PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 1854cf49a43SJulian Elischer PPPOE_SREQ, /* [Client] Sent a Request */ 1866faf164cSJulian Elischer PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 1874cf49a43SJulian Elischer PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 1884cf49a43SJulian Elischer PPPOE_DEAD /* [Both] */ 1894cf49a43SJulian Elischer }; 1904cf49a43SJulian Elischer 1914cf49a43SJulian Elischer #define NUMTAGS 20 /* number of tags we are set up to work with */ 1924cf49a43SJulian Elischer 1934cf49a43SJulian Elischer /* 1944cf49a43SJulian Elischer * Information we store for each hook on each node for negotiating the 1954cf49a43SJulian Elischer * session. The mbuf and cluster are freed once negotiation has completed. 1964cf49a43SJulian Elischer * The whole negotiation block is then discarded. 1974cf49a43SJulian Elischer */ 1984cf49a43SJulian Elischer 1994cf49a43SJulian Elischer struct sess_neg { 2004cf49a43SJulian Elischer struct mbuf *m; /* holds cluster with last sent packet */ 2014cf49a43SJulian Elischer union packet *pkt; /* points within the above cluster */ 2024cf49a43SJulian Elischer struct callout_handle timeout_handle; /* see timeout(9) */ 2034cf49a43SJulian Elischer u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 2044cf49a43SJulian Elischer u_int numtags; 2054cf49a43SJulian Elischer struct pppoe_tag *tags[NUMTAGS]; 2064cf49a43SJulian Elischer u_int service_len; 2074cf49a43SJulian Elischer u_int ac_name_len; 2084cf49a43SJulian Elischer 2094cf49a43SJulian Elischer struct datatag service; 2104cf49a43SJulian Elischer struct datatag ac_name; 2114cf49a43SJulian Elischer }; 2124cf49a43SJulian Elischer typedef struct sess_neg *negp; 2134cf49a43SJulian Elischer 2144cf49a43SJulian Elischer /* 2154cf49a43SJulian Elischer * Session information that is needed after connection. 2164cf49a43SJulian Elischer */ 2172b9cf2f7SArchie Cobbs struct sess_con { 2184cf49a43SJulian Elischer hook_p hook; 2194cf49a43SJulian Elischer u_int16_t Session_ID; 2204cf49a43SJulian Elischer enum state state; 221069154d5SJulian Elischer ng_ID_t creator; /* who to notify */ 2224cf49a43SJulian Elischer struct pppoe_full_hdr pkt_hdr; /* used when connected */ 2234cf49a43SJulian Elischer negp neg; /* used when negotiating */ 2242b9cf2f7SArchie Cobbs /*struct sess_con *hash_next;*/ /* not yet used */ 2254cf49a43SJulian Elischer }; 2262b9cf2f7SArchie Cobbs typedef struct sess_con *sessp; 2274cf49a43SJulian Elischer 2284cf49a43SJulian Elischer /* 2294cf49a43SJulian Elischer * Information we store for each node 2304cf49a43SJulian Elischer */ 2314cf49a43SJulian Elischer struct PPPOE { 2324cf49a43SJulian Elischer node_p node; /* back pointer to node */ 2334cf49a43SJulian Elischer hook_p ethernet_hook; 2344cf49a43SJulian Elischer hook_p debug_hook; 2354cf49a43SJulian Elischer u_int packets_in; /* packets in from ethernet */ 2364cf49a43SJulian Elischer u_int packets_out; /* packets out towards ethernet */ 2374cf49a43SJulian Elischer u_int32_t flags; 2382b9cf2f7SArchie Cobbs /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ 2394cf49a43SJulian Elischer }; 2404cf49a43SJulian Elischer typedef struct PPPOE *priv_p; 2414cf49a43SJulian Elischer 242bfa7e882SJulian Elischer struct ether_header eh_prototype = 2434cf49a43SJulian Elischer {{0xff,0xff,0xff,0xff,0xff,0xff}, 2444cf49a43SJulian Elischer {0x00,0x00,0x00,0x00,0x00,0x00}, 2454cf49a43SJulian Elischer ETHERTYPE_PPPOE_DISC}; 2464cf49a43SJulian Elischer 247bfa7e882SJulian Elischer int stupid_isp; 248bfa7e882SJulian Elischer static int 249bfa7e882SJulian Elischer ngpppoe_set_ethertype(SYSCTL_HANDLER_ARGS) 250bfa7e882SJulian Elischer { 251bfa7e882SJulian Elischer int error; 252bfa7e882SJulian Elischer int val; 253bfa7e882SJulian Elischer 254bfa7e882SJulian Elischer val = stupid_isp; 255bfa7e882SJulian Elischer error = sysctl_handle_int(oidp, &val, sizeof(int), req); 256bfa7e882SJulian Elischer if (error != 0 || req->newptr == NULL) 257bfa7e882SJulian Elischer return (error); 258bfa7e882SJulian Elischer if (val == 1) { 259bfa7e882SJulian Elischer stupid_isp = 1; 260bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 261bfa7e882SJulian Elischer } else { 262bfa7e882SJulian Elischer stupid_isp = 0; 263bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_DISC; 264bfa7e882SJulian Elischer } 265bfa7e882SJulian Elischer return (0); 266bfa7e882SJulian Elischer } 267bfa7e882SJulian Elischer 268bfa7e882SJulian Elischer SYSCTL_PROC(_net_graph, OID_AUTO, stupid_isp, CTLTYPE_INT | CTLFLAG_RW, 269bfa7e882SJulian Elischer 0, sizeof(int), ngpppoe_set_ethertype, "I", "select normal or stupid ISP"); 270bfa7e882SJulian Elischer 2714cf49a43SJulian Elischer union uniq { 2724cf49a43SJulian Elischer char bytes[sizeof(void *)]; 2734cf49a43SJulian Elischer void * pointer; 2744cf49a43SJulian Elischer }; 2754cf49a43SJulian Elischer 2764cf49a43SJulian Elischer #define LEAVE(x) do { error = x; goto quit; } while(0) 2774cf49a43SJulian Elischer static void pppoe_start(sessp sp); 2784cf49a43SJulian Elischer static void sendpacket(sessp sp); 2794cf49a43SJulian Elischer static void pppoe_ticker(void *arg); 2804cf49a43SJulian Elischer static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); 281b58a8a3bSJulian Elischer static int pppoe_send_event(sessp sp, enum cmd cmdid); 2824cf49a43SJulian Elischer 2834cf49a43SJulian Elischer /************************************************************************* 2844cf49a43SJulian Elischer * Some basic utilities from the Linux version with author's permission.* 2854cf49a43SJulian Elischer * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 2864cf49a43SJulian Elischer ************************************************************************/ 2874cf49a43SJulian Elischer 2884cf49a43SJulian Elischer /* 2894cf49a43SJulian Elischer * Generate a new session id 2904adb13fdSJulian Elischer * XXX find out the FreeBSD locking scheme. 2914cf49a43SJulian Elischer */ 2924cf49a43SJulian Elischer static u_int16_t 2934cf49a43SJulian Elischer get_new_sid(node_p node) 2944cf49a43SJulian Elischer { 2954cf49a43SJulian Elischer static int pppoe_sid = 10; 2964cf49a43SJulian Elischer sessp sp; 2974cf49a43SJulian Elischer hook_p hook; 2984cf49a43SJulian Elischer u_int16_t val; 29930400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 3004cf49a43SJulian Elischer 3011e2510f8SJulian Elischer AAA 3024cf49a43SJulian Elischer restart: 3034cf49a43SJulian Elischer val = pppoe_sid++; 3044cf49a43SJulian Elischer /* 3054cf49a43SJulian Elischer * Spec says 0xFFFF is reserved. 3064cf49a43SJulian Elischer * Also don't use 0x0000 3074cf49a43SJulian Elischer */ 3084cf49a43SJulian Elischer if (val == 0xffff) { 3094cf49a43SJulian Elischer pppoe_sid = 20; 3104cf49a43SJulian Elischer goto restart; 3114cf49a43SJulian Elischer } 3124cf49a43SJulian Elischer 3134cf49a43SJulian Elischer /* Check it isn't already in use */ 31430400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 3154cf49a43SJulian Elischer /* don't check special hooks */ 31630400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 31730400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 3184cf49a43SJulian Elischer continue; 31930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 3204cf49a43SJulian Elischer if (sp->Session_ID == val) 3214cf49a43SJulian Elischer goto restart; 3224cf49a43SJulian Elischer } 3234cf49a43SJulian Elischer 3244cf49a43SJulian Elischer return val; 3254cf49a43SJulian Elischer } 3264cf49a43SJulian Elischer 3274cf49a43SJulian Elischer 3284cf49a43SJulian Elischer /* 3294cf49a43SJulian Elischer * Return the location where the next tag can be put 3304cf49a43SJulian Elischer */ 3314cf49a43SJulian Elischer static __inline struct pppoe_tag* 3324cf49a43SJulian Elischer next_tag(struct pppoe_hdr* ph) 3334cf49a43SJulian Elischer { 3344cf49a43SJulian Elischer return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); 3354cf49a43SJulian Elischer } 3364cf49a43SJulian Elischer 3374cf49a43SJulian Elischer /* 3384cf49a43SJulian Elischer * Look for a tag of a specific type 3394cf49a43SJulian Elischer * Don't trust any length the other end says. 3404cf49a43SJulian Elischer * but assume we already sanity checked ph->length. 3414cf49a43SJulian Elischer */ 3424cf49a43SJulian Elischer static struct pppoe_tag* 3434cf49a43SJulian Elischer get_tag(struct pppoe_hdr* ph, u_int16_t idx) 3444cf49a43SJulian Elischer { 3454cf49a43SJulian Elischer char *end = (char *)next_tag(ph); 3464cf49a43SJulian Elischer char *ptn; 3474cf49a43SJulian Elischer struct pppoe_tag *pt = &ph->tag[0]; 3484cf49a43SJulian Elischer /* 3494cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 3504cf49a43SJulian Elischer */ 3511e2510f8SJulian Elischer AAA 3524cf49a43SJulian Elischer while((char*)(pt + 1) <= end) { 3534cf49a43SJulian Elischer /* 3544cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 3554cf49a43SJulian Elischer */ 3564cf49a43SJulian Elischer ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 3574cf49a43SJulian Elischer if(ptn > end) 3584cf49a43SJulian Elischer return NULL; 3594cf49a43SJulian Elischer 3604cf49a43SJulian Elischer if(pt->tag_type == idx) 3614cf49a43SJulian Elischer return pt; 3624cf49a43SJulian Elischer 3634cf49a43SJulian Elischer pt = (struct pppoe_tag*)ptn; 3644cf49a43SJulian Elischer } 3654cf49a43SJulian Elischer return NULL; 3664cf49a43SJulian Elischer } 3674cf49a43SJulian Elischer 3684cf49a43SJulian Elischer /************************************************************************** 3694cf49a43SJulian Elischer * inlines to initialise or add tags to a session's tag list, 3704cf49a43SJulian Elischer **************************************************************************/ 3714cf49a43SJulian Elischer /* 3724cf49a43SJulian Elischer * Initialise the session's tag list 3734cf49a43SJulian Elischer */ 3744cf49a43SJulian Elischer static void 3754cf49a43SJulian Elischer init_tags(sessp sp) 3764cf49a43SJulian Elischer { 3771e2510f8SJulian Elischer AAA 3784cf49a43SJulian Elischer if(sp->neg == NULL) { 3794cf49a43SJulian Elischer printf("pppoe: asked to init NULL neg pointer\n"); 3804cf49a43SJulian Elischer return; 3814cf49a43SJulian Elischer } 3824cf49a43SJulian Elischer sp->neg->numtags = 0; 3834cf49a43SJulian Elischer } 3844cf49a43SJulian Elischer 3854cf49a43SJulian Elischer static void 3864cf49a43SJulian Elischer insert_tag(sessp sp, struct pppoe_tag *tp) 3874cf49a43SJulian Elischer { 3884cf49a43SJulian Elischer int i; 3894cf49a43SJulian Elischer negp neg; 3904cf49a43SJulian Elischer 3911e2510f8SJulian Elischer AAA 3924cf49a43SJulian Elischer if((neg = sp->neg) == NULL) { 3934cf49a43SJulian Elischer printf("pppoe: asked to use NULL neg pointer\n"); 3944cf49a43SJulian Elischer return; 3954cf49a43SJulian Elischer } 3964cf49a43SJulian Elischer if ((i = neg->numtags++) < NUMTAGS) { 3974cf49a43SJulian Elischer neg->tags[i] = tp; 3984cf49a43SJulian Elischer } else { 3994cf49a43SJulian Elischer printf("pppoe: asked to add too many tags to packet\n"); 40012f035e0SJulian Elischer neg->numtags--; 4014cf49a43SJulian Elischer } 4024cf49a43SJulian Elischer } 4034cf49a43SJulian Elischer 4044cf49a43SJulian Elischer /* 4054cf49a43SJulian Elischer * Make up a packet, using the tags filled out for the session. 4064cf49a43SJulian Elischer * 4074cf49a43SJulian Elischer * Assume that the actual pppoe header and ethernet header 4084cf49a43SJulian Elischer * are filled out externally to this routine. 4094cf49a43SJulian Elischer * Also assume that neg->wh points to the correct 4104cf49a43SJulian Elischer * location at the front of the buffer space. 4114cf49a43SJulian Elischer */ 4124cf49a43SJulian Elischer static void 4134cf49a43SJulian Elischer make_packet(sessp sp) { 4144cf49a43SJulian Elischer struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 4154cf49a43SJulian Elischer struct pppoe_tag **tag; 4164cf49a43SJulian Elischer char *dp; 4174cf49a43SJulian Elischer int count; 4184cf49a43SJulian Elischer int tlen; 4194cf49a43SJulian Elischer u_int16_t length = 0; 4204cf49a43SJulian Elischer 4211e2510f8SJulian Elischer AAA 4221e2510f8SJulian Elischer if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 4234cf49a43SJulian Elischer printf("pppoe: make_packet called from wrong state\n"); 4244cf49a43SJulian Elischer } 4254cf49a43SJulian Elischer dp = (char *)wh->ph.tag; 4264cf49a43SJulian Elischer for (count = 0, tag = sp->neg->tags; 4274cf49a43SJulian Elischer ((count < sp->neg->numtags) && (count < NUMTAGS)); 4284cf49a43SJulian Elischer tag++, count++) { 4294cf49a43SJulian Elischer tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 4304cf49a43SJulian Elischer if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 4314cf49a43SJulian Elischer printf("pppoe: tags too long\n"); 4324cf49a43SJulian Elischer sp->neg->numtags = count; 4334cf49a43SJulian Elischer break; /* XXX chop off what's too long */ 4344cf49a43SJulian Elischer } 4354cf49a43SJulian Elischer bcopy((char *)*tag, (char *)dp, tlen); 4364cf49a43SJulian Elischer length += tlen; 4374cf49a43SJulian Elischer dp += tlen; 4384cf49a43SJulian Elischer } 4394cf49a43SJulian Elischer wh->ph.length = htons(length); 4404cf49a43SJulian Elischer sp->neg->m->m_len = length + sizeof(*wh); 4414cf49a43SJulian Elischer sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 4424cf49a43SJulian Elischer } 4434cf49a43SJulian Elischer 4444cf49a43SJulian Elischer /************************************************************************** 4454cf49a43SJulian Elischer * Routine to match a service offered * 4464cf49a43SJulian Elischer **************************************************************************/ 4474cf49a43SJulian Elischer /* 4484cf49a43SJulian Elischer * Find a hook that has a service string that matches that 4494cf49a43SJulian Elischer * we are seeking. for now use a simple string. 4504cf49a43SJulian Elischer * In the future we may need something like regexp(). 4514cf49a43SJulian Elischer * for testing allow a null string to match 1st found and a null service 4524cf49a43SJulian Elischer * to match all requests. Also make '*' do the same. 4534cf49a43SJulian Elischer */ 4549088fa05SBrian Somers 4559088fa05SBrian Somers #define NG_MATCH_EXACT 1 4569088fa05SBrian Somers #define NG_MATCH_ANY 2 4579088fa05SBrian Somers 4584cf49a43SJulian Elischer static hook_p 4599088fa05SBrian Somers pppoe_match_svc(node_p node, char *svc_name, int svc_len, int match) 4604cf49a43SJulian Elischer { 4614cf49a43SJulian Elischer sessp sp = NULL; 4624cf49a43SJulian Elischer negp neg = NULL; 46330400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 4649088fa05SBrian Somers hook_p allhook = NULL; 4654cf49a43SJulian Elischer hook_p hook; 4664cf49a43SJulian Elischer 4671e2510f8SJulian Elischer AAA 46830400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 4694cf49a43SJulian Elischer 4704cf49a43SJulian Elischer /* skip any hook that is debug or ethernet */ 47130400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 47230400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 4734cf49a43SJulian Elischer continue; 47430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 4754cf49a43SJulian Elischer 4764cf49a43SJulian Elischer /* Skip any sessions which are not in LISTEN mode. */ 4774cf49a43SJulian Elischer if ( sp->state != PPPOE_LISTENING) 4784cf49a43SJulian Elischer continue; 4794cf49a43SJulian Elischer 4804cf49a43SJulian Elischer neg = sp->neg; 4814cf49a43SJulian Elischer 4824cf49a43SJulian Elischer /* Special case for a blank or "*" service name (wildcard) */ 4839088fa05SBrian Somers if (match == NG_MATCH_ANY && neg->service_len == 1 && 4849088fa05SBrian Somers neg->service.data[0] == '*') { 4859088fa05SBrian Somers allhook = hook; 4869088fa05SBrian Somers continue; 4874cf49a43SJulian Elischer } 4884cf49a43SJulian Elischer 4894cf49a43SJulian Elischer /* If the lengths don't match, that aint it. */ 4904cf49a43SJulian Elischer if (neg->service_len != svc_len) 4914cf49a43SJulian Elischer continue; 4924cf49a43SJulian Elischer 4934cf49a43SJulian Elischer /* An exact match? */ 4949088fa05SBrian Somers if (svc_len == 0) 4959088fa05SBrian Somers break; 4969088fa05SBrian Somers 4974cf49a43SJulian Elischer if (strncmp(svc_name, neg->service.data, svc_len) == 0) 4984cf49a43SJulian Elischer break; 4994cf49a43SJulian Elischer } 5009088fa05SBrian Somers return (hook ? hook : allhook); 5014cf49a43SJulian Elischer } 5024cf49a43SJulian Elischer /************************************************************************** 5034cf49a43SJulian Elischer * Routine to find a particular session that matches an incoming packet * 5044cf49a43SJulian Elischer **************************************************************************/ 5054cf49a43SJulian Elischer static hook_p 5064cf49a43SJulian Elischer pppoe_findsession(node_p node, struct pppoe_full_hdr *wh) 5074cf49a43SJulian Elischer { 5084cf49a43SJulian Elischer sessp sp = NULL; 5094cf49a43SJulian Elischer hook_p hook = NULL; 51030400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 511b86d0a9eSJulian Elischer u_int16_t session = ntohs(wh->ph.sid); 5124cf49a43SJulian Elischer 5134cf49a43SJulian Elischer /* 5144cf49a43SJulian Elischer * find matching peer/session combination. 5154cf49a43SJulian Elischer */ 5161e2510f8SJulian Elischer AAA 51730400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5184cf49a43SJulian Elischer /* don't check special hooks */ 51930400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 52030400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 5214cf49a43SJulian Elischer continue; 5224cf49a43SJulian Elischer } 52330400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 5244cf49a43SJulian Elischer if ( ( (sp->state == PPPOE_CONNECTED) 5254cf49a43SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED) ) 5264cf49a43SJulian Elischer && (sp->Session_ID == session) 5274cf49a43SJulian Elischer && (bcmp(sp->pkt_hdr.eh.ether_dhost, 5284cf49a43SJulian Elischer wh->eh.ether_shost, 5294cf49a43SJulian Elischer ETHER_ADDR_LEN)) == 0) { 5304cf49a43SJulian Elischer break; 5314cf49a43SJulian Elischer } 5324cf49a43SJulian Elischer } 5334cf49a43SJulian Elischer return (hook); 5344cf49a43SJulian Elischer } 5354cf49a43SJulian Elischer 5364cf49a43SJulian Elischer static hook_p 5374cf49a43SJulian Elischer pppoe_finduniq(node_p node, struct pppoe_tag *tag) 5384cf49a43SJulian Elischer { 5394cf49a43SJulian Elischer hook_p hook = NULL; 54030400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 5414cf49a43SJulian Elischer union uniq uniq; 5424cf49a43SJulian Elischer 5431e2510f8SJulian Elischer AAA 5444cf49a43SJulian Elischer bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 5454cf49a43SJulian Elischer /* cycle through all known hooks */ 54630400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5474cf49a43SJulian Elischer /* don't check special hooks */ 54830400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 54930400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 5504cf49a43SJulian Elischer continue; 55130400f03SJulian Elischer if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 5524cf49a43SJulian Elischer break; 5534cf49a43SJulian Elischer } 5544cf49a43SJulian Elischer return (hook); 5554cf49a43SJulian Elischer } 5564cf49a43SJulian Elischer 5574cf49a43SJulian Elischer /************************************************************************** 5584cf49a43SJulian Elischer * start of Netgraph entrypoints * 5594cf49a43SJulian Elischer **************************************************************************/ 5604cf49a43SJulian Elischer 5614cf49a43SJulian Elischer /* 5624cf49a43SJulian Elischer * Allocate the private data structure and the generic node 5634cf49a43SJulian Elischer * and link them together. 5644cf49a43SJulian Elischer * 5654cf49a43SJulian Elischer * ng_make_node_common() returns with a generic node struct 5664cf49a43SJulian Elischer * with a single reference for us.. we transfer it to the 5674cf49a43SJulian Elischer * private structure.. when we free the private struct we must 5684cf49a43SJulian Elischer * unref the node so it gets freed too. 5694cf49a43SJulian Elischer */ 5704cf49a43SJulian Elischer static int 571069154d5SJulian Elischer ng_pppoe_constructor(node_p node) 5724cf49a43SJulian Elischer { 5734cf49a43SJulian Elischer priv_p privdata; 5744cf49a43SJulian Elischer 5751e2510f8SJulian Elischer AAA 5764cf49a43SJulian Elischer /* Initialize private descriptor */ 5779c8c302fSJulian Elischer MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH_PPPOE, 57899cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 5794cf49a43SJulian Elischer if (privdata == NULL) 5804cf49a43SJulian Elischer return (ENOMEM); 5814cf49a43SJulian Elischer 5824cf49a43SJulian Elischer /* Link structs together; this counts as our one reference to *nodep */ 58330400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, privdata); 584069154d5SJulian Elischer privdata->node = node; 5854cf49a43SJulian Elischer return (0); 5864cf49a43SJulian Elischer } 5874cf49a43SJulian Elischer 5884cf49a43SJulian Elischer /* 5894cf49a43SJulian Elischer * Give our ok for a hook to be added... 5904cf49a43SJulian Elischer * point the hook's private info to the hook structure. 5914cf49a43SJulian Elischer * 5924cf49a43SJulian Elischer * The following hook names are special: 5934cf49a43SJulian Elischer * Ethernet: the hook that should be connected to a NIC. 5944cf49a43SJulian Elischer * debug: copies of data sent out here (when I write the code). 595859a4d16SJulian Elischer * All other hook names need only be unique. (the framework checks this). 5964cf49a43SJulian Elischer */ 5974cf49a43SJulian Elischer static int 5988876b55dSJulian Elischer ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 5994cf49a43SJulian Elischer { 60030400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 6014cf49a43SJulian Elischer sessp sp; 6024cf49a43SJulian Elischer 6031e2510f8SJulian Elischer AAA 6044cf49a43SJulian Elischer if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 6054cf49a43SJulian Elischer privp->ethernet_hook = hook; 60630400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 6074cf49a43SJulian Elischer } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 6084cf49a43SJulian Elischer privp->debug_hook = hook; 60930400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 6104cf49a43SJulian Elischer } else { 6114cf49a43SJulian Elischer /* 6124cf49a43SJulian Elischer * Any other unique name is OK. 6134cf49a43SJulian Elischer * The infrastructure has already checked that it's unique, 6144cf49a43SJulian Elischer * so just allocate it and hook it in. 6154cf49a43SJulian Elischer */ 6169c8c302fSJulian Elischer MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 6174cf49a43SJulian Elischer if (sp == NULL) { 6184cf49a43SJulian Elischer return (ENOMEM); 6194cf49a43SJulian Elischer } 6204cf49a43SJulian Elischer 62130400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, sp); 6224cf49a43SJulian Elischer sp->hook = hook; 6234cf49a43SJulian Elischer } 6244cf49a43SJulian Elischer return(0); 6254cf49a43SJulian Elischer } 6264cf49a43SJulian Elischer 6274cf49a43SJulian Elischer /* 6284cf49a43SJulian Elischer * Get a netgraph control message. 6294cf49a43SJulian Elischer * Check it is one we understand. If needed, send a response. 6304cf49a43SJulian Elischer * We sometimes save the address for an async action later. 6314cf49a43SJulian Elischer * Always free the message. 6324cf49a43SJulian Elischer */ 6334cf49a43SJulian Elischer static int 634069154d5SJulian Elischer ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 6354cf49a43SJulian Elischer { 63630400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 6378876b55dSJulian Elischer struct ngpppoe_init_data *ourmsg = NULL; 6384cf49a43SJulian Elischer struct ng_mesg *resp = NULL; 6394cf49a43SJulian Elischer int error = 0; 6404cf49a43SJulian Elischer hook_p hook = NULL; 6414cf49a43SJulian Elischer sessp sp = NULL; 6424cf49a43SJulian Elischer negp neg = NULL; 643069154d5SJulian Elischer struct ng_mesg *msg; 6444cf49a43SJulian Elischer 6451e2510f8SJulian Elischer AAA 646069154d5SJulian Elischer NGI_GET_MSG(item, msg); 6474cf49a43SJulian Elischer /* Deal with message according to cookie and command */ 6484cf49a43SJulian Elischer switch (msg->header.typecookie) { 6494cf49a43SJulian Elischer case NGM_PPPOE_COOKIE: 6504cf49a43SJulian Elischer switch (msg->header.cmd) { 6514cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 6524cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 6534cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 654859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 65527121ab1SBrian Somers ourmsg = (struct ngpppoe_init_data *)msg->data; 65627121ab1SBrian Somers if (msg->header.arglen < sizeof(*ourmsg)) { 65727121ab1SBrian Somers printf("pppoe: init data too small\n"); 6584cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6594cf49a43SJulian Elischer } 66076a70671SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) > 66176a70671SBrian Somers PPPOE_SERVICE_NAME_SIZE) { 66276a70671SBrian Somers printf("pppoe_rcvmsg: service name too big"); 6634cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6644cf49a43SJulian Elischer } 66527121ab1SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) < 66627121ab1SBrian Somers ourmsg->data_len) { 66727121ab1SBrian Somers printf("pppoe: init data has bad length," 66827121ab1SBrian Somers " %d should be %d\n", ourmsg->data_len, 66927121ab1SBrian Somers msg->header.arglen - sizeof (*ourmsg)); 67076a70671SBrian Somers LEAVE(EMSGSIZE); 67176a70671SBrian Somers } 67276a70671SBrian Somers 6734cf49a43SJulian Elischer /* make sure strcmp will terminate safely */ 6744cf49a43SJulian Elischer ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 6754cf49a43SJulian Elischer 6764cf49a43SJulian Elischer /* cycle through all known hooks */ 67730400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 67830400f03SJulian Elischer if (NG_HOOK_NAME(hook) 67930400f03SJulian Elischer && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 6804cf49a43SJulian Elischer break; 6814cf49a43SJulian Elischer } 6824cf49a43SJulian Elischer if (hook == NULL) { 6834cf49a43SJulian Elischer LEAVE(ENOENT); 6844cf49a43SJulian Elischer } 68530400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 68630400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 6874cf49a43SJulian Elischer LEAVE(EINVAL); 6884cf49a43SJulian Elischer } 68930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 690859a4d16SJulian Elischer 6919088fa05SBrian Somers if (msg->header.cmd == NGM_PPPOE_LISTEN) { 6929088fa05SBrian Somers /* 6939088fa05SBrian Somers * Ensure we aren't already listening for this 6949088fa05SBrian Somers * service. 6959088fa05SBrian Somers */ 6969088fa05SBrian Somers if (pppoe_match_svc(node, ourmsg->data, 6979088fa05SBrian Somers ourmsg->data_len, NG_MATCH_EXACT) != NULL) { 6989088fa05SBrian Somers LEAVE(EEXIST); 6999088fa05SBrian Somers } 7009088fa05SBrian Somers } 7019088fa05SBrian Somers 702859a4d16SJulian Elischer /* 703859a4d16SJulian Elischer * PPPOE_SERVICE advertisments are set up 704859a4d16SJulian Elischer * on sessions that are in PRIMED state. 705859a4d16SJulian Elischer */ 706859a4d16SJulian Elischer if (msg->header.cmd == NGM_PPPOE_SERVICE) { 707859a4d16SJulian Elischer break; 708859a4d16SJulian Elischer } 7094cf49a43SJulian Elischer if (sp->state |= PPPOE_SNONE) { 7104cf49a43SJulian Elischer printf("pppoe: Session already active\n"); 7114cf49a43SJulian Elischer LEAVE(EISCONN); 7124cf49a43SJulian Elischer } 7131e2510f8SJulian Elischer 7144cf49a43SJulian Elischer /* 7154cf49a43SJulian Elischer * set up prototype header 7164cf49a43SJulian Elischer */ 7179c8c302fSJulian Elischer MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH_PPPOE, 71899cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 7194cf49a43SJulian Elischer 7204cf49a43SJulian Elischer if (neg == NULL) { 7214cf49a43SJulian Elischer printf("pppoe: Session out of memory\n"); 7224cf49a43SJulian Elischer LEAVE(ENOMEM); 7234cf49a43SJulian Elischer } 7244cf49a43SJulian Elischer MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 7254cf49a43SJulian Elischer if(neg->m == NULL) { 7261e2510f8SJulian Elischer printf("pppoe: Session out of mbufs\n"); 7279c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7284cf49a43SJulian Elischer LEAVE(ENOBUFS); 7294cf49a43SJulian Elischer } 7304cf49a43SJulian Elischer neg->m->m_pkthdr.rcvif = NULL; 7314cf49a43SJulian Elischer MCLGET(neg->m, M_DONTWAIT); 7324cf49a43SJulian Elischer if ((neg->m->m_flags & M_EXT) == 0) { 7331e2510f8SJulian Elischer printf("pppoe: Session out of mcls\n"); 7344cf49a43SJulian Elischer m_freem(neg->m); 7359c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7364cf49a43SJulian Elischer LEAVE(ENOBUFS); 7374cf49a43SJulian Elischer } 7384cf49a43SJulian Elischer sp->neg = neg; 7391e2510f8SJulian Elischer callout_handle_init( &neg->timeout_handle); 7404cf49a43SJulian Elischer neg->m->m_len = sizeof(struct pppoe_full_hdr); 7414cf49a43SJulian Elischer neg->pkt = mtod(neg->m, union packet*); 7424cf49a43SJulian Elischer neg->pkt->pkt_header.eh = eh_prototype; 7434cf49a43SJulian Elischer neg->pkt->pkt_header.ph.ver = 0x1; 7444cf49a43SJulian Elischer neg->pkt->pkt_header.ph.type = 0x1; 7454cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 0x0000; 7464cf49a43SJulian Elischer neg->timeout = 0; 7474cf49a43SJulian Elischer 748069154d5SJulian Elischer sp->creator = NGI_RETADDR(item); 7494cf49a43SJulian Elischer } 7504cf49a43SJulian Elischer switch (msg->header.cmd) { 7514cf49a43SJulian Elischer case NGM_PPPOE_GET_STATUS: 7524cf49a43SJulian Elischer { 7538876b55dSJulian Elischer struct ngpppoestat *stats; 7544cf49a43SJulian Elischer 7554cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 7564cf49a43SJulian Elischer if (!resp) { 7574cf49a43SJulian Elischer LEAVE(ENOMEM); 7584cf49a43SJulian Elischer } 7598876b55dSJulian Elischer stats = (struct ngpppoestat *) resp->data; 7604cf49a43SJulian Elischer stats->packets_in = privp->packets_in; 7614cf49a43SJulian Elischer stats->packets_out = privp->packets_out; 7624cf49a43SJulian Elischer break; 7634cf49a43SJulian Elischer } 7644cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 7654cf49a43SJulian Elischer /* 7664cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7674cf49a43SJulian Elischer * Send a PADI request, and start the timeout logic. 7684cf49a43SJulian Elischer * Store the originator of this message so we can send 7694cf49a43SJulian Elischer * a success of fail message to them later. 7704cf49a43SJulian Elischer * Move the session to SINIT 7714cf49a43SJulian Elischer * Set up the session to the correct state and 7724cf49a43SJulian Elischer * start it. 7734cf49a43SJulian Elischer */ 7744cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 77527121ab1SBrian Somers neg->service.hdr.tag_len = 77627121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 77727121ab1SBrian Somers if (ourmsg->data_len) 77827121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 77927121ab1SBrian Somers ourmsg->data_len); 78027121ab1SBrian Somers neg->service_len = ourmsg->data_len; 7814cf49a43SJulian Elischer pppoe_start(sp); 7824cf49a43SJulian Elischer break; 7834cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 7844cf49a43SJulian Elischer /* 7854cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7864cf49a43SJulian Elischer * Install the service matching string. 7874cf49a43SJulian Elischer * Store the originator of this message so we can send 7884cf49a43SJulian Elischer * a success of fail message to them later. 7894cf49a43SJulian Elischer * Move the hook to 'LISTENING' 7904cf49a43SJulian Elischer */ 7914cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 79227121ab1SBrian Somers neg->service.hdr.tag_len = 79327121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 7941e2510f8SJulian Elischer 79527121ab1SBrian Somers if (ourmsg->data_len) 79627121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 79727121ab1SBrian Somers ourmsg->data_len); 79827121ab1SBrian Somers neg->service_len = ourmsg->data_len; 7994cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADT_CODE; 8004cf49a43SJulian Elischer /* 8014cf49a43SJulian Elischer * wait for PADI packet coming from ethernet 8024cf49a43SJulian Elischer */ 8034cf49a43SJulian Elischer sp->state = PPPOE_LISTENING; 8044cf49a43SJulian Elischer break; 8054cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 8064cf49a43SJulian Elischer /* 8074cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8084cf49a43SJulian Elischer * Store the originator of this message so we can send 8094cf49a43SJulian Elischer * a success of fail message to them later. 8104cf49a43SJulian Elischer * Store the AC-Name given and go to PRIMED. 8114cf49a43SJulian Elischer */ 8124cf49a43SJulian Elischer neg->ac_name.hdr.tag_type = PTT_AC_NAME; 81327121ab1SBrian Somers neg->ac_name.hdr.tag_len = 81427121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 81527121ab1SBrian Somers if (ourmsg->data_len) 81627121ab1SBrian Somers bcopy(ourmsg->data, neg->ac_name.data, 81727121ab1SBrian Somers ourmsg->data_len); 81827121ab1SBrian Somers neg->ac_name_len = ourmsg->data_len; 8194cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 8204cf49a43SJulian Elischer /* 8214cf49a43SJulian Elischer * Wait for PADI packet coming from hook 8224cf49a43SJulian Elischer */ 8234cf49a43SJulian Elischer sp->state = PPPOE_PRIMED; 8244cf49a43SJulian Elischer break; 825859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 826859a4d16SJulian Elischer /* 827859a4d16SJulian Elischer * Check the session is primed. 828859a4d16SJulian Elischer * for now just allow ONE service to be advertised. 829859a4d16SJulian Elischer * If you do it twice you just overwrite. 830859a4d16SJulian Elischer */ 8315078fb0bSJulian Elischer if (sp->state != PPPOE_PRIMED) { 832859a4d16SJulian Elischer printf("pppoe: Session not primed\n"); 833859a4d16SJulian Elischer LEAVE(EISCONN); 834859a4d16SJulian Elischer } 8350069b9cbSJulian Elischer neg = sp->neg; 836859a4d16SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 837859a4d16SJulian Elischer neg->service.hdr.tag_len = 838859a4d16SJulian Elischer htons((u_int16_t)ourmsg->data_len); 839859a4d16SJulian Elischer 840859a4d16SJulian Elischer if (ourmsg->data_len) 841859a4d16SJulian Elischer bcopy(ourmsg->data, neg->service.data, 842859a4d16SJulian Elischer ourmsg->data_len); 843859a4d16SJulian Elischer neg->service_len = ourmsg->data_len; 844859a4d16SJulian Elischer break; 8454cf49a43SJulian Elischer default: 8464cf49a43SJulian Elischer LEAVE(EINVAL); 8474cf49a43SJulian Elischer } 8484cf49a43SJulian Elischer break; 8494cf49a43SJulian Elischer default: 8504cf49a43SJulian Elischer LEAVE(EINVAL); 8514cf49a43SJulian Elischer } 8524cf49a43SJulian Elischer 8534cf49a43SJulian Elischer /* Take care of synchronous response, if any */ 8544cf49a43SJulian Elischer quit: 855069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 856069154d5SJulian Elischer /* Free the message and return */ 857069154d5SJulian Elischer NG_FREE_MSG(msg); 8584cf49a43SJulian Elischer return(error); 8594cf49a43SJulian Elischer } 8604cf49a43SJulian Elischer 8611e2510f8SJulian Elischer /* 8621e2510f8SJulian Elischer * Start a client into the first state. A separate function because 8631e2510f8SJulian Elischer * it can be needed if the negotiation times out. 8641e2510f8SJulian Elischer */ 8654cf49a43SJulian Elischer static void 8664cf49a43SJulian Elischer pppoe_start(sessp sp) 8674cf49a43SJulian Elischer { 8684cf49a43SJulian Elischer struct { 8694cf49a43SJulian Elischer struct pppoe_tag hdr; 8704cf49a43SJulian Elischer union uniq data; 8712b5dcd2fSBrian Somers } __attribute ((packed)) uniqtag; 8724cf49a43SJulian Elischer 8734cf49a43SJulian Elischer /* 8744cf49a43SJulian Elischer * kick the state machine into starting up 8754cf49a43SJulian Elischer */ 8761e2510f8SJulian Elischer AAA 8774cf49a43SJulian Elischer sp->state = PPPOE_SINIT; 8781e2510f8SJulian Elischer /* reset the packet header to broadcast */ 8791e2510f8SJulian Elischer sp->neg->pkt->pkt_header.eh = eh_prototype; 8801e2510f8SJulian Elischer sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 8814cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 8824cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 8834cf49a43SJulian Elischer uniqtag.data.pointer = sp; 8844cf49a43SJulian Elischer init_tags(sp); 8851f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 8867ccbb17bSJulian Elischer insert_tag(sp, &sp->neg->service.hdr); 8874cf49a43SJulian Elischer make_packet(sp); 8884cf49a43SJulian Elischer sendpacket(sp); 8894cf49a43SJulian Elischer } 8904cf49a43SJulian Elischer 8914cf49a43SJulian Elischer /* 8924cf49a43SJulian Elischer * Receive data, and do something with it. 8934cf49a43SJulian Elischer * The caller will never free m or meta, so 8944cf49a43SJulian Elischer * if we use up this data or abort we must free BOTH of these. 8954cf49a43SJulian Elischer */ 8964cf49a43SJulian Elischer static int 897069154d5SJulian Elischer ng_pppoe_rcvdata(hook_p hook, item_p item) 8984cf49a43SJulian Elischer { 89930400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 90030400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 90130400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 9024cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 9034cf49a43SJulian Elischer struct pppoe_hdr *ph; 9044cf49a43SJulian Elischer int error = 0; 9054cf49a43SJulian Elischer u_int16_t session; 9064cf49a43SJulian Elischer u_int16_t length; 9074cf49a43SJulian Elischer u_int8_t code; 9081f89d938SJulian Elischer struct pppoe_tag *utag = NULL, *tag = NULL; 9094cf49a43SJulian Elischer hook_p sendhook; 9104cf49a43SJulian Elischer struct { 9114cf49a43SJulian Elischer struct pppoe_tag hdr; 9124cf49a43SJulian Elischer union uniq data; 9132b5dcd2fSBrian Somers } __attribute ((packed)) uniqtag; 9144cf49a43SJulian Elischer negp neg = NULL; 915069154d5SJulian Elischer struct mbuf *m; 9164cf49a43SJulian Elischer 9171e2510f8SJulian Elischer AAA 918069154d5SJulian Elischer NGI_GET_M(item, m); 91930400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 9204cf49a43SJulian Elischer /* 9214cf49a43SJulian Elischer * Data from the debug hook gets sent without modification 9224cf49a43SJulian Elischer * straight to the ethernet. 9234cf49a43SJulian Elischer */ 92430400f03SJulian Elischer NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 9254cf49a43SJulian Elischer privp->packets_out++; 92630400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 9274cf49a43SJulian Elischer /* 9284cf49a43SJulian Elischer * Incoming data. 9294cf49a43SJulian Elischer * Dig out various fields from the packet. 9304cf49a43SJulian Elischer * use them to decide where to send it. 9314cf49a43SJulian Elischer */ 9324cf49a43SJulian Elischer 9334cf49a43SJulian Elischer privp->packets_in++; 9340c65c135SJulian Elischer if( m->m_len < sizeof(*wh)) { 9350c65c135SJulian Elischer m = m_pullup(m, sizeof(*wh)); /* Checks length */ 9364cf49a43SJulian Elischer if (m == NULL) { 937b86d0a9eSJulian Elischer printf("couldn't m_pullup\n"); 9384cf49a43SJulian Elischer LEAVE(ENOBUFS); 9394cf49a43SJulian Elischer } 9400c65c135SJulian Elischer } 9414cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 9424cf49a43SJulian Elischer length = ntohs(wh->ph.length); 9430c65c135SJulian Elischer switch(wh->eh.ether_type) { 944bfa7e882SJulian Elischer case ETHERTYPE_PPPOE_STUPID_DISC: 945bfa7e882SJulian Elischer stupid_isp = 1; 946bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 947bfa7e882SJulian Elischer /* fall through */ 9484cf49a43SJulian Elischer case ETHERTYPE_PPPOE_DISC: 9494cf49a43SJulian Elischer /* 950bdaf2e81SJulian Elischer * We need to try to make sure that the tag area 951bdaf2e81SJulian Elischer * is contiguous, or we could wander off the end 9524cf49a43SJulian Elischer * of a buffer and make a mess. 9534cf49a43SJulian Elischer * (Linux wouldn't have this problem). 9544cf49a43SJulian Elischer */ 9550c65c135SJulian Elischer if (m->m_pkthdr.len <= MHLEN) { 9560c65c135SJulian Elischer if( m->m_len < m->m_pkthdr.len) { 9570c65c135SJulian Elischer m = m_pullup(m, m->m_pkthdr.len); 9580c65c135SJulian Elischer if (m == NULL) { 9590c65c135SJulian Elischer printf("couldn't m_pullup\n"); 9600c65c135SJulian Elischer LEAVE(ENOBUFS); 9610c65c135SJulian Elischer } 9620c65c135SJulian Elischer } 9630c65c135SJulian Elischer } 9644cf49a43SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 9654cf49a43SJulian Elischer /* 9664cf49a43SJulian Elischer * It's not all in one piece. 9674cf49a43SJulian Elischer * We need to do extra work. 968069154d5SJulian Elischer * Put it into a cluster. 9694cf49a43SJulian Elischer */ 970069154d5SJulian Elischer struct mbuf *n; 971069154d5SJulian Elischer n = m_dup(m, M_DONTWAIT); 972069154d5SJulian Elischer m_freem(m); 973069154d5SJulian Elischer m = n; 974069154d5SJulian Elischer if (m) { 975069154d5SJulian Elischer /* just check we got a cluster */ 976069154d5SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 977069154d5SJulian Elischer m_freem(m); 978069154d5SJulian Elischer m = NULL; 979069154d5SJulian Elischer } 980069154d5SJulian Elischer } 981069154d5SJulian Elischer if (m == NULL) { 9824cf49a43SJulian Elischer printf("packet fragmented\n"); 983b86d0a9eSJulian Elischer LEAVE(EMSGSIZE); 9844cf49a43SJulian Elischer } 985069154d5SJulian Elischer } 986069154d5SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 987069154d5SJulian Elischer length = ntohs(wh->ph.length); 988069154d5SJulian Elischer ph = &wh->ph; 989069154d5SJulian Elischer session = ntohs(wh->ph.sid); 990069154d5SJulian Elischer code = wh->ph.code; 9914cf49a43SJulian Elischer 9924cf49a43SJulian Elischer switch(code) { 9934cf49a43SJulian Elischer case PADI_CODE: 9944cf49a43SJulian Elischer /* 9954cf49a43SJulian Elischer * We are a server: 9964cf49a43SJulian Elischer * Look for a hook with the required service 9974cf49a43SJulian Elischer * and send the ENTIRE packet up there. 9984cf49a43SJulian Elischer * It should come back to a new hook in 9994cf49a43SJulian Elischer * PRIMED state. Look there for further 10004cf49a43SJulian Elischer * processing. 10014cf49a43SJulian Elischer */ 10024cf49a43SJulian Elischer tag = get_tag(ph, PTT_SRV_NAME); 10034cf49a43SJulian Elischer if (tag == NULL) { 1004b86d0a9eSJulian Elischer printf("no service tag\n"); 10054cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10064cf49a43SJulian Elischer } 100730400f03SJulian Elischer sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 10089088fa05SBrian Somers tag->tag_data, ntohs(tag->tag_len), 10099088fa05SBrian Somers NG_MATCH_ANY); 10104cf49a43SJulian Elischer if (sendhook) { 1011069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, 1012069154d5SJulian Elischer sendhook, m); 10134cf49a43SJulian Elischer } else { 10144cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10154cf49a43SJulian Elischer } 10164cf49a43SJulian Elischer break; 10174cf49a43SJulian Elischer case PADO_CODE: 10184cf49a43SJulian Elischer /* 10194cf49a43SJulian Elischer * We are a client: 10204cf49a43SJulian Elischer * Use the host_uniq tag to find the 10214cf49a43SJulian Elischer * hook this is in response to. 1022b86d0a9eSJulian Elischer * Received #2, now send #3 10234cf49a43SJulian Elischer * For now simply accept the first we receive. 10244cf49a43SJulian Elischer */ 10251f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 10261f89d938SJulian Elischer if ((utag == NULL) 10271f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 1028b86d0a9eSJulian Elischer printf("no host unique field\n"); 10294cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10304cf49a43SJulian Elischer } 10314cf49a43SJulian Elischer 10321f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 10334cf49a43SJulian Elischer if (sendhook == NULL) { 1034b86d0a9eSJulian Elischer printf("no matching session\n"); 10354cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10364cf49a43SJulian Elischer } 10374cf49a43SJulian Elischer 10384cf49a43SJulian Elischer /* 10394cf49a43SJulian Elischer * Check the session is in the right state. 10404cf49a43SJulian Elischer * It needs to be in PPPOE_SINIT. 10414cf49a43SJulian Elischer */ 104230400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 10434cf49a43SJulian Elischer if (sp->state != PPPOE_SINIT) { 1044b86d0a9eSJulian Elischer printf("session in wrong state\n"); 10454cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10464cf49a43SJulian Elischer } 10474cf49a43SJulian Elischer neg = sp->neg; 10484cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 10494cf49a43SJulian Elischer neg->timeout_handle); 10504cf49a43SJulian Elischer 10514cf49a43SJulian Elischer /* 10524cf49a43SJulian Elischer * This is the first time we hear 10534cf49a43SJulian Elischer * from the server, so note it's 10544cf49a43SJulian Elischer * unicast address, replacing the 10554cf49a43SJulian Elischer * broadcast address . 10564cf49a43SJulian Elischer */ 10574cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 10584cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 10594cf49a43SJulian Elischer ETHER_ADDR_LEN); 10604cf49a43SJulian Elischer neg->timeout = 0; 10614cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADR_CODE; 10624cf49a43SJulian Elischer init_tags(sp); 10637ccbb17bSJulian Elischer insert_tag(sp, utag); /* Host Unique */ 10641f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1065b86d0a9eSJulian Elischer insert_tag(sp, tag); /* return cookie */ 10661f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_NAME))) 10671f89d938SJulian Elischer insert_tag(sp, tag); /* return it */ 10687ccbb17bSJulian Elischer insert_tag(sp, &neg->service.hdr); /* Service */ 10694cf49a43SJulian Elischer scan_tags(sp, ph); 10704cf49a43SJulian Elischer make_packet(sp); 10714cf49a43SJulian Elischer sp->state = PPPOE_SREQ; 10724cf49a43SJulian Elischer sendpacket(sp); 10734cf49a43SJulian Elischer break; 10744cf49a43SJulian Elischer case PADR_CODE: 10754cf49a43SJulian Elischer 10764cf49a43SJulian Elischer /* 10774cf49a43SJulian Elischer * We are a server: 10784cf49a43SJulian Elischer * Use the ac_cookie tag to find the 10794cf49a43SJulian Elischer * hook this is in response to. 10804cf49a43SJulian Elischer */ 10811f89d938SJulian Elischer utag = get_tag(ph, PTT_AC_COOKIE); 10821f89d938SJulian Elischer if ((utag == NULL) 10831f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 10844cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10854cf49a43SJulian Elischer } 10864cf49a43SJulian Elischer 10871f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 10884cf49a43SJulian Elischer if (sendhook == NULL) { 10894cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10904cf49a43SJulian Elischer } 10914cf49a43SJulian Elischer 10924cf49a43SJulian Elischer /* 10934cf49a43SJulian Elischer * Check the session is in the right state. 10944cf49a43SJulian Elischer * It needs to be in PPPOE_SOFFER 10954cf49a43SJulian Elischer * or PPPOE_NEWCONNECTED. If the latter, 10964cf49a43SJulian Elischer * then this is a retry by the client. 10974cf49a43SJulian Elischer * so be nice, and resend. 10984cf49a43SJulian Elischer */ 109930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11004cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 11014cf49a43SJulian Elischer /* 11024cf49a43SJulian Elischer * Whoa! drop back to resend that 11034cf49a43SJulian Elischer * PADS packet. 11044cf49a43SJulian Elischer * We should still have a copy of it. 11054cf49a43SJulian Elischer */ 11064cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 11074cf49a43SJulian Elischer } 11084cf49a43SJulian Elischer if (sp->state != PPPOE_SOFFER) { 11094cf49a43SJulian Elischer LEAVE (ENETUNREACH); 11104cf49a43SJulian Elischer break; 11114cf49a43SJulian Elischer } 11124cf49a43SJulian Elischer neg = sp->neg; 11134cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 11144cf49a43SJulian Elischer neg->timeout_handle); 11154cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADS_CODE; 11164cf49a43SJulian Elischer if (sp->Session_ID == 0) 11174cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 1118b86d0a9eSJulian Elischer htons(sp->Session_ID 1119b86d0a9eSJulian Elischer = get_new_sid(node)); 11204cf49a43SJulian Elischer neg->timeout = 0; 11214cf49a43SJulian Elischer /* 11224cf49a43SJulian Elischer * start working out the tags to respond with. 11234cf49a43SJulian Elischer */ 11244cf49a43SJulian Elischer init_tags(sp); 11254cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1126bdaf2e81SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 11274adb13fdSJulian Elischer insert_tag(sp, tag);/* return service */ 11281f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 11294adb13fdSJulian Elischer insert_tag(sp, tag); /* return it */ 11301f89d938SJulian Elischer insert_tag(sp, utag); /* ac_cookie */ 11314cf49a43SJulian Elischer scan_tags(sp, ph); 11324cf49a43SJulian Elischer make_packet(sp); 1133bdaf2e81SJulian Elischer sp->state = PPPOE_NEWCONNECTED; 11346faf164cSJulian Elischer sendpacket(sp); 11354cf49a43SJulian Elischer /* 11364cf49a43SJulian Elischer * Having sent the last Negotiation header, 11374cf49a43SJulian Elischer * Set up the stored packet header to 11384cf49a43SJulian Elischer * be correct for the actual session. 11394cf49a43SJulian Elischer * But keep the negotialtion stuff 11404cf49a43SJulian Elischer * around in case we need to resend this last 11414cf49a43SJulian Elischer * packet. We'll discard it when we move 11424cf49a43SJulian Elischer * from NEWCONNECTED to CONNECTED 11434cf49a43SJulian Elischer */ 11444cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 1145bfa7e882SJulian Elischer if (stupid_isp) 1146bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1147bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1148bfa7e882SJulian Elischer else 11494cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 11504cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 11514cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 1152b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 11534cf49a43SJulian Elischer break; 11544cf49a43SJulian Elischer case PADS_CODE: 11554cf49a43SJulian Elischer /* 11564cf49a43SJulian Elischer * We are a client: 11574cf49a43SJulian Elischer * Use the host_uniq tag to find the 11584cf49a43SJulian Elischer * hook this is in response to. 11594cf49a43SJulian Elischer * take the session ID and store it away. 11604cf49a43SJulian Elischer * Also make sure the pre-made header is 11614cf49a43SJulian Elischer * correct and set us into Session mode. 11624cf49a43SJulian Elischer */ 11631f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 11641f89d938SJulian Elischer if ((utag == NULL) 11651f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 11664cf49a43SJulian Elischer LEAVE (ENETUNREACH); 11674cf49a43SJulian Elischer break; 11684cf49a43SJulian Elischer } 11691f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 11704cf49a43SJulian Elischer if (sendhook == NULL) { 11714cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11724cf49a43SJulian Elischer } 11734cf49a43SJulian Elischer 11744cf49a43SJulian Elischer /* 11754cf49a43SJulian Elischer * Check the session is in the right state. 11764cf49a43SJulian Elischer * It needs to be in PPPOE_SREQ. 11774cf49a43SJulian Elischer */ 117830400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11794cf49a43SJulian Elischer if (sp->state != PPPOE_SREQ) { 11804cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11814cf49a43SJulian Elischer } 11824cf49a43SJulian Elischer neg = sp->neg; 11834cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 11844cf49a43SJulian Elischer neg->timeout_handle); 1185cfbcfe62SJulian Elischer neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1186b86d0a9eSJulian Elischer sp->Session_ID = ntohs(wh->ph.sid); 11874cf49a43SJulian Elischer neg->timeout = 0; 11884cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 11894cf49a43SJulian Elischer /* 11904cf49a43SJulian Elischer * Now we have gone to Connected mode, 11914cf49a43SJulian Elischer * Free all resources needed for 11924cf49a43SJulian Elischer * negotiation. 11934cf49a43SJulian Elischer * Keep a copy of the header we will be using. 11944cf49a43SJulian Elischer */ 11954cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 1196bfa7e882SJulian Elischer if (stupid_isp) 1197bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1198bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1199bfa7e882SJulian Elischer else 12004cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 12014cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 12024cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 12034cf49a43SJulian Elischer m_freem(neg->m); 12049c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 12054cf49a43SJulian Elischer sp->neg = NULL; 1206b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 12074cf49a43SJulian Elischer break; 12084cf49a43SJulian Elischer case PADT_CODE: 12094cf49a43SJulian Elischer /* 12104cf49a43SJulian Elischer * Send a 'close' message to the controlling 12114cf49a43SJulian Elischer * process (the one that set us up); 12124cf49a43SJulian Elischer * And then tear everything down. 12134cf49a43SJulian Elischer * 12144cf49a43SJulian Elischer * Find matching peer/session combination. 12154cf49a43SJulian Elischer */ 12164cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 12174cf49a43SJulian Elischer if (sendhook == NULL) { 12184cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12194cf49a43SJulian Elischer } 12204cf49a43SJulian Elischer /* send message to creator */ 12214cf49a43SJulian Elischer /* close hook */ 1222b58a8a3bSJulian Elischer if (sendhook) { 1223954c4772SJulian Elischer ng_rmhook_self(sendhook); 1224b58a8a3bSJulian Elischer } 12254cf49a43SJulian Elischer break; 12264cf49a43SJulian Elischer default: 12274cf49a43SJulian Elischer LEAVE(EPFNOSUPPORT); 12284cf49a43SJulian Elischer } 12294cf49a43SJulian Elischer break; 1230bfa7e882SJulian Elischer case ETHERTYPE_PPPOE_STUPID_SESS: 12314cf49a43SJulian Elischer case ETHERTYPE_PPPOE_SESS: 12324cf49a43SJulian Elischer /* 12334cf49a43SJulian Elischer * find matching peer/session combination. 12344cf49a43SJulian Elischer */ 12354cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 12364cf49a43SJulian Elischer if (sendhook == NULL) { 12374cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12384cf49a43SJulian Elischer break; 12394cf49a43SJulian Elischer } 124030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 12414cf49a43SJulian Elischer m_adj(m, sizeof(*wh)); 12424cf49a43SJulian Elischer if (m->m_pkthdr.len < length) { 12434cf49a43SJulian Elischer /* Packet too short, dump it */ 12444cf49a43SJulian Elischer LEAVE(EMSGSIZE); 12454cf49a43SJulian Elischer } 12469fcb3d83SJulian Elischer 12474adb13fdSJulian Elischer /* Also need to trim excess at the end */ 12489fcb3d83SJulian Elischer if (m->m_pkthdr.len > length) { 12499fcb3d83SJulian Elischer m_adj(m, -((int)(m->m_pkthdr.len - length))); 12509fcb3d83SJulian Elischer } 12514cf49a43SJulian Elischer if ( sp->state != PPPOE_CONNECTED) { 12524cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 12534cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 12544cf49a43SJulian Elischer /* 12554cf49a43SJulian Elischer * Now we have gone to Connected mode, 12564cf49a43SJulian Elischer * Free all resources needed for 1257a4ec03cfSJulian Elischer * negotiation. Be paranoid about 1258a4ec03cfSJulian Elischer * whether there may be a timeout. 12594cf49a43SJulian Elischer */ 12604cf49a43SJulian Elischer m_freem(sp->neg->m); 1261a4ec03cfSJulian Elischer untimeout(pppoe_ticker, sendhook, 1262a4ec03cfSJulian Elischer sp->neg->timeout_handle); 12639c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 12644cf49a43SJulian Elischer sp->neg = NULL; 12654cf49a43SJulian Elischer } else { 12664cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12674cf49a43SJulian Elischer break; 12684cf49a43SJulian Elischer } 12694cf49a43SJulian Elischer } 1270069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, sendhook, m); 12714cf49a43SJulian Elischer break; 12724cf49a43SJulian Elischer default: 12734b276f90SJulian Elischer LEAVE(EPFNOSUPPORT); 12744cf49a43SJulian Elischer } 12754cf49a43SJulian Elischer } else { 12764cf49a43SJulian Elischer /* 12774cf49a43SJulian Elischer * Not ethernet or debug hook.. 12784cf49a43SJulian Elischer * 12794cf49a43SJulian Elischer * The packet has come in on a normal hook. 12804cf49a43SJulian Elischer * We need to find out what kind of hook, 12814cf49a43SJulian Elischer * So we can decide how to handle it. 12824cf49a43SJulian Elischer * Check the hook's state. 12834cf49a43SJulian Elischer */ 128430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 12854cf49a43SJulian Elischer switch (sp->state) { 12864cf49a43SJulian Elischer case PPPOE_NEWCONNECTED: 12874cf49a43SJulian Elischer case PPPOE_CONNECTED: { 12887b38c4e4SArchie Cobbs static const u_char addrctrl[] = { 0xff, 0x03 }; 12894cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 12907b38c4e4SArchie Cobbs 12917b38c4e4SArchie Cobbs /* 12927b38c4e4SArchie Cobbs * Remove PPP address and control fields, if any. 12937b38c4e4SArchie Cobbs * For example, ng_ppp(4) always sends LCP packets 12947b38c4e4SArchie Cobbs * with address and control fields as required by 12957b38c4e4SArchie Cobbs * generic PPP. PPPoE is an exception to the rule. 12967b38c4e4SArchie Cobbs */ 12977b38c4e4SArchie Cobbs if (m->m_pkthdr.len >= 2) { 12987b38c4e4SArchie Cobbs if (m->m_len < 2 && !(m = m_pullup(m, 2))) 12997b38c4e4SArchie Cobbs LEAVE(ENOBUFS); 13007b38c4e4SArchie Cobbs if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 13017b38c4e4SArchie Cobbs m_adj(m, 2); 13027b38c4e4SArchie Cobbs } 13034cf49a43SJulian Elischer /* 13044cf49a43SJulian Elischer * Bang in a pre-made header, and set the length up 13054cf49a43SJulian Elischer * to be correct. Then send it to the ethernet driver. 1306d9da9cbaSJulian Elischer * But first correct the length. 13074cf49a43SJulian Elischer */ 1308d9da9cbaSJulian Elischer sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 13094cf49a43SJulian Elischer M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 13104cf49a43SJulian Elischer if (m == NULL) { 13114cf49a43SJulian Elischer LEAVE(ENOBUFS); 13124cf49a43SJulian Elischer } 13134cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 13144cf49a43SJulian Elischer bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1315069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 13164cf49a43SJulian Elischer privp->packets_out++; 13174cf49a43SJulian Elischer break; 13184cf49a43SJulian Elischer } 13194cf49a43SJulian Elischer case PPPOE_PRIMED: 13204cf49a43SJulian Elischer /* 13214cf49a43SJulian Elischer * A PADI packet is being returned by the application 13224cf49a43SJulian Elischer * that has set up this hook. This indicates that it 13234cf49a43SJulian Elischer * wants us to offer service. 13244cf49a43SJulian Elischer */ 13254cf49a43SJulian Elischer neg = sp->neg; 1326bdaf2e81SJulian Elischer if (m->m_len < sizeof(*wh)) { 1327bdaf2e81SJulian Elischer m = m_pullup(m, sizeof(*wh)); 13284cf49a43SJulian Elischer if (m == NULL) { 13294cf49a43SJulian Elischer LEAVE(ENOBUFS); 13304cf49a43SJulian Elischer } 1331bdaf2e81SJulian Elischer } 13324cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 13334cf49a43SJulian Elischer ph = &wh->ph; 13344cf49a43SJulian Elischer session = ntohs(wh->ph.sid); 13354cf49a43SJulian Elischer length = ntohs(wh->ph.length); 13364cf49a43SJulian Elischer code = wh->ph.code; 13371e2510f8SJulian Elischer if ( code != PADI_CODE) { 13381e2510f8SJulian Elischer LEAVE(EINVAL); 13391e2510f8SJulian Elischer }; 13401e2510f8SJulian Elischer untimeout(pppoe_ticker, hook, 13411e2510f8SJulian Elischer neg->timeout_handle); 13424cf49a43SJulian Elischer 13434cf49a43SJulian Elischer /* 13444cf49a43SJulian Elischer * This is the first time we hear 13454cf49a43SJulian Elischer * from the client, so note it's 13464cf49a43SJulian Elischer * unicast address, replacing the 13474cf49a43SJulian Elischer * broadcast address. 13484cf49a43SJulian Elischer */ 13494cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 13504cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 13514cf49a43SJulian Elischer ETHER_ADDR_LEN); 13524cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 13534cf49a43SJulian Elischer neg->timeout = 0; 13544cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 13554cf49a43SJulian Elischer 13564cf49a43SJulian Elischer /* 13574cf49a43SJulian Elischer * start working out the tags to respond with. 13584cf49a43SJulian Elischer */ 13594cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_AC_COOKIE; 13604cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 13614cf49a43SJulian Elischer uniqtag.data.pointer = sp; 13624cf49a43SJulian Elischer init_tags(sp); 13634cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 13641f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 13654adb13fdSJulian Elischer insert_tag(sp, tag); /* return service */ 1366859a4d16SJulian Elischer /* 1367859a4d16SJulian Elischer * If we have a NULL service request 1368859a4d16SJulian Elischer * and have an extra service defined in this hook, 1369859a4d16SJulian Elischer * then also add a tag for the extra service. 1370859a4d16SJulian Elischer * XXX this is a hack. eventually we should be able 1371859a4d16SJulian Elischer * to support advertising many services, not just one 1372859a4d16SJulian Elischer */ 1373859a4d16SJulian Elischer if (((tag == NULL) || (tag->tag_len == 0)) 1374859a4d16SJulian Elischer && (neg->service.hdr.tag_len != 0)) { 1375859a4d16SJulian Elischer insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1376859a4d16SJulian Elischer } 13771f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 13781f89d938SJulian Elischer insert_tag(sp, tag); /* returned hostunique */ 13791f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 13804cf49a43SJulian Elischer scan_tags(sp, ph); 13814cf49a43SJulian Elischer make_packet(sp); 13824cf49a43SJulian Elischer sendpacket(sp); 13834cf49a43SJulian Elischer break; 13844cf49a43SJulian Elischer 13854cf49a43SJulian Elischer /* 13864cf49a43SJulian Elischer * Packets coming from the hook make no sense 13874cf49a43SJulian Elischer * to sessions in these states. Throw them away. 13884cf49a43SJulian Elischer */ 13894cf49a43SJulian Elischer case PPPOE_SINIT: 13904cf49a43SJulian Elischer case PPPOE_SREQ: 13914cf49a43SJulian Elischer case PPPOE_SOFFER: 13924cf49a43SJulian Elischer case PPPOE_SNONE: 13934cf49a43SJulian Elischer case PPPOE_LISTENING: 13944cf49a43SJulian Elischer case PPPOE_DEAD: 13954cf49a43SJulian Elischer default: 13964cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13974cf49a43SJulian Elischer } 13984cf49a43SJulian Elischer } 13994cf49a43SJulian Elischer quit: 1400f5856029SJulian Elischer if (item) 1401069154d5SJulian Elischer NG_FREE_ITEM(item); 1402069154d5SJulian Elischer NG_FREE_M(m); 14034cf49a43SJulian Elischer return error; 14044cf49a43SJulian Elischer } 14054cf49a43SJulian Elischer 14064cf49a43SJulian Elischer /* 14074cf49a43SJulian Elischer * Do local shutdown processing.. 14084cf49a43SJulian Elischer * If we are a persistant device, we might refuse to go away, and 14094cf49a43SJulian Elischer * we'd only remove our links and reset ourself. 14104cf49a43SJulian Elischer */ 14114cf49a43SJulian Elischer static int 1412069154d5SJulian Elischer ng_pppoe_shutdown(node_p node) 14134cf49a43SJulian Elischer { 141430400f03SJulian Elischer const priv_p privdata = NG_NODE_PRIVATE(node); 14154cf49a43SJulian Elischer 14161e2510f8SJulian Elischer AAA 141730400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 141830400f03SJulian Elischer NG_NODE_UNREF(privdata->node); 14199c8c302fSJulian Elischer FREE(privdata, M_NETGRAPH_PPPOE); 14204cf49a43SJulian Elischer return (0); 14214cf49a43SJulian Elischer } 14224cf49a43SJulian Elischer 14234cf49a43SJulian Elischer /* 14244cf49a43SJulian Elischer * This is called once we've already connected a new hook to the other node. 14254cf49a43SJulian Elischer * It gives us a chance to balk at the last minute. 14264cf49a43SJulian Elischer */ 14274cf49a43SJulian Elischer static int 14288876b55dSJulian Elischer ng_pppoe_connect(hook_p hook) 14294cf49a43SJulian Elischer { 14304cf49a43SJulian Elischer /* be really amiable and just say "YUP that's OK by me! " */ 14314cf49a43SJulian Elischer return (0); 14324cf49a43SJulian Elischer } 14334cf49a43SJulian Elischer 14344cf49a43SJulian Elischer /* 14354cf49a43SJulian Elischer * Hook disconnection 14364cf49a43SJulian Elischer * 14376faf164cSJulian Elischer * Clean up all dangling links and information about the session/hook. 14384cf49a43SJulian Elischer * For this type, removal of the last link destroys the node 14394cf49a43SJulian Elischer */ 14404cf49a43SJulian Elischer static int 14418876b55dSJulian Elischer ng_pppoe_disconnect(hook_p hook) 14424cf49a43SJulian Elischer { 144330400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 144430400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 14454cf49a43SJulian Elischer sessp sp; 144604853d8aSJulian Elischer int hooks; 14474cf49a43SJulian Elischer 14481e2510f8SJulian Elischer AAA 144930400f03SJulian Elischer hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 145030400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 14514cf49a43SJulian Elischer privp->debug_hook = NULL; 145230400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 14534cf49a43SJulian Elischer privp->ethernet_hook = NULL; 145430400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) 1455069154d5SJulian Elischer ng_rmnode_self(node); 14564cf49a43SJulian Elischer } else { 145730400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 1458b58a8a3bSJulian Elischer if (sp->state != PPPOE_SNONE ) { 1459b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1460b58a8a3bSJulian Elischer } 1461a4ec03cfSJulian Elischer /* 1462a4ec03cfSJulian Elischer * According to the spec, if we are connected, 1463a4ec03cfSJulian Elischer * we should send a DISC packet if we are shutting down 1464a4ec03cfSJulian Elischer * a session. 1465a4ec03cfSJulian Elischer */ 14669fcb3d83SJulian Elischer if ((privp->ethernet_hook) 14679fcb3d83SJulian Elischer && ((sp->state == PPPOE_CONNECTED) 14689fcb3d83SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED))) { 14699fcb3d83SJulian Elischer struct mbuf *m; 14709fcb3d83SJulian Elischer struct pppoe_full_hdr *wh; 14719fcb3d83SJulian Elischer struct pppoe_tag *tag; 14729fcb3d83SJulian Elischer int msglen = strlen(SIGNOFF); 14739fcb3d83SJulian Elischer int error = 0; 14749fcb3d83SJulian Elischer 14759fcb3d83SJulian Elischer /* revert the stored header to DISC/PADT mode */ 14769fcb3d83SJulian Elischer wh = &sp->pkt_hdr; 14779fcb3d83SJulian Elischer wh->ph.code = PADT_CODE; 1478bfa7e882SJulian Elischer if (stupid_isp) 1479bfa7e882SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 1480bfa7e882SJulian Elischer else 14819fcb3d83SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 14829fcb3d83SJulian Elischer 14839fcb3d83SJulian Elischer /* generate a packet of that type */ 14849fcb3d83SJulian Elischer MGETHDR(m, M_DONTWAIT, MT_DATA); 14856faf164cSJulian Elischer if(m == NULL) 14866faf164cSJulian Elischer printf("pppoe: Session out of mbufs\n"); 14876faf164cSJulian Elischer else { 14889fcb3d83SJulian Elischer m->m_pkthdr.rcvif = NULL; 14899fcb3d83SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*wh); 14906faf164cSJulian Elischer bcopy((caddr_t)wh, mtod(m, caddr_t), 14916faf164cSJulian Elischer sizeof(*wh)); 14926faf164cSJulian Elischer /* 14936faf164cSJulian Elischer * Add a General error message and adjust 14946faf164cSJulian Elischer * sizes 14956faf164cSJulian Elischer */ 14969fcb3d83SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 14979fcb3d83SJulian Elischer tag = wh->ph.tag; 14989fcb3d83SJulian Elischer tag->tag_type = PTT_GEN_ERR; 14999fcb3d83SJulian Elischer tag->tag_len = htons((u_int16_t)msglen); 15009fcb3d83SJulian Elischer strncpy(tag->tag_data, SIGNOFF, msglen); 15016faf164cSJulian Elischer m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 15026faf164cSJulian Elischer msglen); 15039fcb3d83SJulian Elischer wh->ph.length = htons(sizeof(*tag) + msglen); 1504069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, 1505069154d5SJulian Elischer privp->ethernet_hook, m); 15066faf164cSJulian Elischer } 15079fcb3d83SJulian Elischer } 1508a4ec03cfSJulian Elischer /* 1509514baf3fSJeroen Ruigrok van der Werven * As long as we have somewhere to store the timeout handle, 1510a4ec03cfSJulian Elischer * we may have a timeout pending.. get rid of it. 1511a4ec03cfSJulian Elischer */ 15121e2510f8SJulian Elischer if (sp->neg) { 15134cf49a43SJulian Elischer untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 15141e2510f8SJulian Elischer if (sp->neg->m) 15151e2510f8SJulian Elischer m_freem(sp->neg->m); 15169c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 15171e2510f8SJulian Elischer } 15189c8c302fSJulian Elischer FREE(sp, M_NETGRAPH_PPPOE); 151930400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 1520ed52f174SJulian Elischer /* work out how many session hooks there are */ 152104853d8aSJulian Elischer /* Node goes away on last session hook removal */ 152204853d8aSJulian Elischer if (privp->ethernet_hook) hooks -= 1; 1523ed52f174SJulian Elischer if (privp->debug_hook) hooks -= 1; 15244cf49a43SJulian Elischer } 152530400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 152630400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 1527069154d5SJulian Elischer ng_rmnode_self(node); 15284cf49a43SJulian Elischer return (0); 15294cf49a43SJulian Elischer } 15304cf49a43SJulian Elischer 15314cf49a43SJulian Elischer /* 15324cf49a43SJulian Elischer * timeouts come here. 15334cf49a43SJulian Elischer */ 15344cf49a43SJulian Elischer static void 15354cf49a43SJulian Elischer pppoe_ticker(void *arg) 15364cf49a43SJulian Elischer { 15374cf49a43SJulian Elischer int s = splnet(); 15384cf49a43SJulian Elischer hook_p hook = arg; 153930400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 15404cf49a43SJulian Elischer negp neg = sp->neg; 15414cf49a43SJulian Elischer int error = 0; 15424cf49a43SJulian Elischer struct mbuf *m0 = NULL; 154330400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 15444cf49a43SJulian Elischer 15451e2510f8SJulian Elischer AAA 15464cf49a43SJulian Elischer switch(sp->state) { 15474cf49a43SJulian Elischer /* 15484cf49a43SJulian Elischer * resend the last packet, using an exponential backoff. 15494cf49a43SJulian Elischer * After a period of time, stop growing the backoff, 15504adb13fdSJulian Elischer * and either leave it, or revert to the start. 15514cf49a43SJulian Elischer */ 15524cf49a43SJulian Elischer case PPPOE_SINIT: 15534cf49a43SJulian Elischer case PPPOE_SREQ: 15544cf49a43SJulian Elischer /* timeouts on these produce resends */ 15554cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1556069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 15574cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 15584cf49a43SJulian Elischer hook, neg->timeout * hz); 15594cf49a43SJulian Elischer if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 15604cf49a43SJulian Elischer if (sp->state == PPPOE_SREQ) { 15614cf49a43SJulian Elischer /* revert to SINIT mode */ 1562b58a8a3bSJulian Elischer pppoe_start(sp); 15634cf49a43SJulian Elischer } else { 15644cf49a43SJulian Elischer neg->timeout = PPPOE_TIMEOUT_LIMIT; 15654cf49a43SJulian Elischer } 15664cf49a43SJulian Elischer } 15674cf49a43SJulian Elischer break; 15684cf49a43SJulian Elischer case PPPOE_PRIMED: 15694cf49a43SJulian Elischer case PPPOE_SOFFER: 15704cf49a43SJulian Elischer /* a timeout on these says "give up" */ 1571954c4772SJulian Elischer ng_rmhook_self(hook); 15724cf49a43SJulian Elischer break; 15734cf49a43SJulian Elischer default: 15744cf49a43SJulian Elischer /* timeouts have no meaning in other states */ 15754cf49a43SJulian Elischer printf("pppoe: unexpected timeout\n"); 15764cf49a43SJulian Elischer } 15774cf49a43SJulian Elischer splx(s); 15784cf49a43SJulian Elischer } 15794cf49a43SJulian Elischer 15804cf49a43SJulian Elischer 15814cf49a43SJulian Elischer static void 15824cf49a43SJulian Elischer sendpacket(sessp sp) 15834cf49a43SJulian Elischer { 15844cf49a43SJulian Elischer int error = 0; 15854cf49a43SJulian Elischer struct mbuf *m0 = NULL; 15864cf49a43SJulian Elischer hook_p hook = sp->hook; 15874cf49a43SJulian Elischer negp neg = sp->neg; 158830400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 15894cf49a43SJulian Elischer 15901e2510f8SJulian Elischer AAA 15914cf49a43SJulian Elischer switch(sp->state) { 15924cf49a43SJulian Elischer case PPPOE_LISTENING: 15934cf49a43SJulian Elischer case PPPOE_DEAD: 15944cf49a43SJulian Elischer case PPPOE_SNONE: 15954cf49a43SJulian Elischer case PPPOE_CONNECTED: 1596b86d0a9eSJulian Elischer printf("pppoe: sendpacket: unexpected state\n"); 15974cf49a43SJulian Elischer break; 15984cf49a43SJulian Elischer 15996faf164cSJulian Elischer case PPPOE_NEWCONNECTED: 16006faf164cSJulian Elischer /* send the PADS without a timeout - we're now connected */ 16016faf164cSJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1602069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16036faf164cSJulian Elischer break; 16046faf164cSJulian Elischer 16054cf49a43SJulian Elischer case PPPOE_PRIMED: 16064cf49a43SJulian Elischer /* No packet to send, but set up the timeout */ 16074cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16084cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 16094cf49a43SJulian Elischer break; 16104cf49a43SJulian Elischer 16114cf49a43SJulian Elischer case PPPOE_SOFFER: 16124cf49a43SJulian Elischer /* 16134cf49a43SJulian Elischer * send the offer but if they don't respond 16144cf49a43SJulian Elischer * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 16154cf49a43SJulian Elischer */ 16164cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1617069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16184cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16194cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 16204cf49a43SJulian Elischer break; 16214cf49a43SJulian Elischer 16224cf49a43SJulian Elischer case PPPOE_SINIT: 16234cf49a43SJulian Elischer case PPPOE_SREQ: 16244cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1625069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1626d0fef808SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, hook, 1627d0fef808SJulian Elischer (hz * PPPOE_INITIAL_TIMEOUT)); 1628d0fef808SJulian Elischer neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 16294cf49a43SJulian Elischer break; 16304cf49a43SJulian Elischer 16314cf49a43SJulian Elischer default: 16324cf49a43SJulian Elischer error = EINVAL; 16334cf49a43SJulian Elischer printf("pppoe: timeout: bad state\n"); 16344cf49a43SJulian Elischer } 16354cf49a43SJulian Elischer /* return (error); */ 16364cf49a43SJulian Elischer } 16374cf49a43SJulian Elischer 16384cf49a43SJulian Elischer /* 16394cf49a43SJulian Elischer * Parse an incoming packet to see if any tags should be copied to the 16404adb13fdSJulian Elischer * output packet. Don't do any tags that have been handled in the main 16414adb13fdSJulian Elischer * state machine. 16424cf49a43SJulian Elischer */ 16434cf49a43SJulian Elischer static struct pppoe_tag* 16444cf49a43SJulian Elischer scan_tags(sessp sp, struct pppoe_hdr* ph) 16454cf49a43SJulian Elischer { 16464cf49a43SJulian Elischer char *end = (char *)next_tag(ph); 16474cf49a43SJulian Elischer char *ptn; 16484cf49a43SJulian Elischer struct pppoe_tag *pt = &ph->tag[0]; 16494cf49a43SJulian Elischer /* 16504cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 16514cf49a43SJulian Elischer */ 16521e2510f8SJulian Elischer AAA 16534cf49a43SJulian Elischer while((char*)(pt + 1) <= end) { 16544cf49a43SJulian Elischer /* 16554cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 16564cf49a43SJulian Elischer */ 16574cf49a43SJulian Elischer ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 16584cf49a43SJulian Elischer if(ptn > end) 16594cf49a43SJulian Elischer return NULL; 16604cf49a43SJulian Elischer 16614cf49a43SJulian Elischer switch (pt->tag_type) { 16624cf49a43SJulian Elischer case PTT_RELAY_SID: 16634cf49a43SJulian Elischer insert_tag(sp, pt); 16644cf49a43SJulian Elischer break; 16654cf49a43SJulian Elischer case PTT_EOL: 16664cf49a43SJulian Elischer return NULL; 16674cf49a43SJulian Elischer case PTT_SRV_NAME: 16684cf49a43SJulian Elischer case PTT_AC_NAME: 16694cf49a43SJulian Elischer case PTT_HOST_UNIQ: 16704cf49a43SJulian Elischer case PTT_AC_COOKIE: 16714cf49a43SJulian Elischer case PTT_VENDOR: 16724cf49a43SJulian Elischer case PTT_SRV_ERR: 16734cf49a43SJulian Elischer case PTT_SYS_ERR: 16744cf49a43SJulian Elischer case PTT_GEN_ERR: 16754cf49a43SJulian Elischer break; 16764cf49a43SJulian Elischer } 16774cf49a43SJulian Elischer pt = (struct pppoe_tag*)ptn; 16784cf49a43SJulian Elischer } 16794cf49a43SJulian Elischer return NULL; 16804cf49a43SJulian Elischer } 16814cf49a43SJulian Elischer 1682b58a8a3bSJulian Elischer static int 1683b58a8a3bSJulian Elischer pppoe_send_event(sessp sp, enum cmd cmdid) 1684b58a8a3bSJulian Elischer { 1685b58a8a3bSJulian Elischer int error; 1686b58a8a3bSJulian Elischer struct ng_mesg *msg; 16878876b55dSJulian Elischer struct ngpppoe_sts *sts; 1688b58a8a3bSJulian Elischer 16891e2510f8SJulian Elischer AAA 169027121ab1SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 16918876b55dSJulian Elischer sizeof(struct ngpppoe_sts), M_NOWAIT); 1692859a4d16SJulian Elischer if (msg == NULL) 1693859a4d16SJulian Elischer return (ENOMEM); 16948876b55dSJulian Elischer sts = (struct ngpppoe_sts *)msg->data; 169530400f03SJulian Elischer strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKLEN + 1); 169630400f03SJulian Elischer NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 1697b58a8a3bSJulian Elischer return (error); 1698b58a8a3bSJulian Elischer } 1699