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 436e551fb6SDavid E. O'Brien #define AAA printf("pppoe: %s\n", __func__ ); 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 */ 87f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[] 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, 91f0184ff8SArchie Cobbs &ngpppoe_init_data_type_fields 9276a70671SBrian Somers }; 9376a70671SBrian Somers 9476a70671SBrian Somers /* Parse type for struct ngpppoe_sts */ 95f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[] 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, 99f0184ff8SArchie Cobbs &ng_pppoe_sts_type_fields 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; 205816b834fSArchie Cobbs const 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 24794142c49SJulian Elischer static int nonstandard; 248bfa7e882SJulian Elischer static int 249bfa7e882SJulian Elischer ngpppoe_set_ethertype(SYSCTL_HANDLER_ARGS) 250bfa7e882SJulian Elischer { 251bfa7e882SJulian Elischer int error; 252bfa7e882SJulian Elischer int val; 253bfa7e882SJulian Elischer 25494142c49SJulian Elischer val = nonstandard; 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) { 25994142c49SJulian Elischer nonstandard = 1; 260bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 261bfa7e882SJulian Elischer } else { 26294142c49SJulian Elischer nonstandard = 0; 263bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_DISC; 264bfa7e882SJulian Elischer } 265bfa7e882SJulian Elischer return (0); 266bfa7e882SJulian Elischer } 267bfa7e882SJulian Elischer 26894142c49SJulian Elischer SYSCTL_PROC(_net_graph, OID_AUTO, nonstandard_pppoe, 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); 280816b834fSArchie Cobbs static const struct pppoe_tag *scan_tags(sessp sp, 281816b834fSArchie Cobbs const struct pppoe_hdr* ph); 282b58a8a3bSJulian Elischer static int pppoe_send_event(sessp sp, enum cmd cmdid); 2834cf49a43SJulian Elischer 2844cf49a43SJulian Elischer /************************************************************************* 2854cf49a43SJulian Elischer * Some basic utilities from the Linux version with author's permission.* 2864cf49a43SJulian Elischer * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 2874cf49a43SJulian Elischer ************************************************************************/ 2884cf49a43SJulian Elischer 2894cf49a43SJulian Elischer /* 2904cf49a43SJulian Elischer * Generate a new session id 2914adb13fdSJulian Elischer * XXX find out the FreeBSD locking scheme. 2924cf49a43SJulian Elischer */ 2934cf49a43SJulian Elischer static u_int16_t 2944cf49a43SJulian Elischer get_new_sid(node_p node) 2954cf49a43SJulian Elischer { 2964cf49a43SJulian Elischer static int pppoe_sid = 10; 2974cf49a43SJulian Elischer sessp sp; 2984cf49a43SJulian Elischer hook_p hook; 2994cf49a43SJulian Elischer u_int16_t val; 30030400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 3014cf49a43SJulian Elischer 3021e2510f8SJulian Elischer AAA 3034cf49a43SJulian Elischer restart: 3044cf49a43SJulian Elischer val = pppoe_sid++; 3054cf49a43SJulian Elischer /* 3064cf49a43SJulian Elischer * Spec says 0xFFFF is reserved. 3074cf49a43SJulian Elischer * Also don't use 0x0000 3084cf49a43SJulian Elischer */ 3094cf49a43SJulian Elischer if (val == 0xffff) { 3104cf49a43SJulian Elischer pppoe_sid = 20; 3114cf49a43SJulian Elischer goto restart; 3124cf49a43SJulian Elischer } 3134cf49a43SJulian Elischer 3144cf49a43SJulian Elischer /* Check it isn't already in use */ 31530400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 3164cf49a43SJulian Elischer /* don't check special hooks */ 31730400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 31830400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 3194cf49a43SJulian Elischer continue; 32030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 3214cf49a43SJulian Elischer if (sp->Session_ID == val) 3224cf49a43SJulian Elischer goto restart; 3234cf49a43SJulian Elischer } 3244cf49a43SJulian Elischer 3254cf49a43SJulian Elischer return val; 3264cf49a43SJulian Elischer } 3274cf49a43SJulian Elischer 3284cf49a43SJulian Elischer 3294cf49a43SJulian Elischer /* 3304cf49a43SJulian Elischer * Return the location where the next tag can be put 3314cf49a43SJulian Elischer */ 332816b834fSArchie Cobbs static __inline const struct pppoe_tag* 333816b834fSArchie Cobbs next_tag(const struct pppoe_hdr* ph) 3344cf49a43SJulian Elischer { 335816b834fSArchie Cobbs return (const struct pppoe_tag*)(((const char*)&ph->tag[0]) 336816b834fSArchie Cobbs + ntohs(ph->length)); 3374cf49a43SJulian Elischer } 3384cf49a43SJulian Elischer 3394cf49a43SJulian Elischer /* 3404cf49a43SJulian Elischer * Look for a tag of a specific type 3414cf49a43SJulian Elischer * Don't trust any length the other end says. 3424cf49a43SJulian Elischer * but assume we already sanity checked ph->length. 3434cf49a43SJulian Elischer */ 344816b834fSArchie Cobbs static const struct pppoe_tag* 345816b834fSArchie Cobbs get_tag(const struct pppoe_hdr* ph, u_int16_t idx) 3464cf49a43SJulian Elischer { 347816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 348816b834fSArchie Cobbs const char *ptn; 349816b834fSArchie Cobbs const struct pppoe_tag *pt = &ph->tag[0]; 3504cf49a43SJulian Elischer /* 3514cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 3524cf49a43SJulian Elischer */ 3531e2510f8SJulian Elischer AAA 354816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 3554cf49a43SJulian Elischer /* 3564cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 3574cf49a43SJulian Elischer */ 358816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 3594cf49a43SJulian Elischer if(ptn > end) 3604cf49a43SJulian Elischer return NULL; 3614cf49a43SJulian Elischer 3624cf49a43SJulian Elischer if(pt->tag_type == idx) 3634cf49a43SJulian Elischer return pt; 3644cf49a43SJulian Elischer 365816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 3664cf49a43SJulian Elischer } 3674cf49a43SJulian Elischer return NULL; 3684cf49a43SJulian Elischer } 3694cf49a43SJulian Elischer 3704cf49a43SJulian Elischer /************************************************************************** 3714cf49a43SJulian Elischer * inlines to initialise or add tags to a session's tag list, 3724cf49a43SJulian Elischer **************************************************************************/ 3734cf49a43SJulian Elischer /* 3744cf49a43SJulian Elischer * Initialise the session's tag list 3754cf49a43SJulian Elischer */ 3764cf49a43SJulian Elischer static void 3774cf49a43SJulian Elischer init_tags(sessp sp) 3784cf49a43SJulian Elischer { 3791e2510f8SJulian Elischer AAA 3804cf49a43SJulian Elischer if(sp->neg == NULL) { 3814cf49a43SJulian Elischer printf("pppoe: asked to init NULL neg pointer\n"); 3824cf49a43SJulian Elischer return; 3834cf49a43SJulian Elischer } 3844cf49a43SJulian Elischer sp->neg->numtags = 0; 3854cf49a43SJulian Elischer } 3864cf49a43SJulian Elischer 3874cf49a43SJulian Elischer static void 388816b834fSArchie Cobbs insert_tag(sessp sp, const struct pppoe_tag *tp) 3894cf49a43SJulian Elischer { 3904cf49a43SJulian Elischer int i; 3914cf49a43SJulian Elischer negp neg; 3924cf49a43SJulian Elischer 3931e2510f8SJulian Elischer AAA 3944cf49a43SJulian Elischer if((neg = sp->neg) == NULL) { 3954cf49a43SJulian Elischer printf("pppoe: asked to use NULL neg pointer\n"); 3964cf49a43SJulian Elischer return; 3974cf49a43SJulian Elischer } 3984cf49a43SJulian Elischer if ((i = neg->numtags++) < NUMTAGS) { 3994cf49a43SJulian Elischer neg->tags[i] = tp; 4004cf49a43SJulian Elischer } else { 4014cf49a43SJulian Elischer printf("pppoe: asked to add too many tags to packet\n"); 40212f035e0SJulian Elischer neg->numtags--; 4034cf49a43SJulian Elischer } 4044cf49a43SJulian Elischer } 4054cf49a43SJulian Elischer 4064cf49a43SJulian Elischer /* 4074cf49a43SJulian Elischer * Make up a packet, using the tags filled out for the session. 4084cf49a43SJulian Elischer * 4094cf49a43SJulian Elischer * Assume that the actual pppoe header and ethernet header 4104cf49a43SJulian Elischer * are filled out externally to this routine. 4114cf49a43SJulian Elischer * Also assume that neg->wh points to the correct 4124cf49a43SJulian Elischer * location at the front of the buffer space. 4134cf49a43SJulian Elischer */ 4144cf49a43SJulian Elischer static void 4154cf49a43SJulian Elischer make_packet(sessp sp) { 4164cf49a43SJulian Elischer struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 417816b834fSArchie Cobbs const struct pppoe_tag **tag; 4184cf49a43SJulian Elischer char *dp; 4194cf49a43SJulian Elischer int count; 4204cf49a43SJulian Elischer int tlen; 4214cf49a43SJulian Elischer u_int16_t length = 0; 4224cf49a43SJulian Elischer 4231e2510f8SJulian Elischer AAA 4241e2510f8SJulian Elischer if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 4254cf49a43SJulian Elischer printf("pppoe: make_packet called from wrong state\n"); 4264cf49a43SJulian Elischer } 4274cf49a43SJulian Elischer dp = (char *)wh->ph.tag; 4284cf49a43SJulian Elischer for (count = 0, tag = sp->neg->tags; 4294cf49a43SJulian Elischer ((count < sp->neg->numtags) && (count < NUMTAGS)); 4304cf49a43SJulian Elischer tag++, count++) { 4314cf49a43SJulian Elischer tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 4324cf49a43SJulian Elischer if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 4334cf49a43SJulian Elischer printf("pppoe: tags too long\n"); 4344cf49a43SJulian Elischer sp->neg->numtags = count; 4354cf49a43SJulian Elischer break; /* XXX chop off what's too long */ 4364cf49a43SJulian Elischer } 437816b834fSArchie Cobbs bcopy(*tag, (char *)dp, tlen); 4384cf49a43SJulian Elischer length += tlen; 4394cf49a43SJulian Elischer dp += tlen; 4404cf49a43SJulian Elischer } 4414cf49a43SJulian Elischer wh->ph.length = htons(length); 4424cf49a43SJulian Elischer sp->neg->m->m_len = length + sizeof(*wh); 4434cf49a43SJulian Elischer sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 4444cf49a43SJulian Elischer } 4454cf49a43SJulian Elischer 4464cf49a43SJulian Elischer /************************************************************************** 4474cf49a43SJulian Elischer * Routine to match a service offered * 4484cf49a43SJulian Elischer **************************************************************************/ 4494cf49a43SJulian Elischer /* 4504cf49a43SJulian Elischer * Find a hook that has a service string that matches that 4514cf49a43SJulian Elischer * we are seeking. for now use a simple string. 4524cf49a43SJulian Elischer * In the future we may need something like regexp(). 4534cf49a43SJulian Elischer * for testing allow a null string to match 1st found and a null service 4544cf49a43SJulian Elischer * to match all requests. Also make '*' do the same. 4554cf49a43SJulian Elischer */ 4569088fa05SBrian Somers 4579088fa05SBrian Somers #define NG_MATCH_EXACT 1 4589088fa05SBrian Somers #define NG_MATCH_ANY 2 4599088fa05SBrian Somers 4604cf49a43SJulian Elischer static hook_p 461816b834fSArchie Cobbs pppoe_match_svc(node_p node, const char *svc_name, int svc_len, int match) 4624cf49a43SJulian Elischer { 4634cf49a43SJulian Elischer sessp sp = NULL; 4644cf49a43SJulian Elischer negp neg = NULL; 46530400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 4669088fa05SBrian Somers hook_p allhook = NULL; 4674cf49a43SJulian Elischer hook_p hook; 4684cf49a43SJulian Elischer 4691e2510f8SJulian Elischer AAA 47030400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 4714cf49a43SJulian Elischer 4724cf49a43SJulian Elischer /* skip any hook that is debug or ethernet */ 47330400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 47430400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 4754cf49a43SJulian Elischer continue; 47630400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 4774cf49a43SJulian Elischer 4784cf49a43SJulian Elischer /* Skip any sessions which are not in LISTEN mode. */ 4794cf49a43SJulian Elischer if ( sp->state != PPPOE_LISTENING) 4804cf49a43SJulian Elischer continue; 4814cf49a43SJulian Elischer 4824cf49a43SJulian Elischer neg = sp->neg; 4834cf49a43SJulian Elischer 4844cf49a43SJulian Elischer /* Special case for a blank or "*" service name (wildcard) */ 4859088fa05SBrian Somers if (match == NG_MATCH_ANY && neg->service_len == 1 && 4869088fa05SBrian Somers neg->service.data[0] == '*') { 4879088fa05SBrian Somers allhook = hook; 4889088fa05SBrian Somers continue; 4894cf49a43SJulian Elischer } 4904cf49a43SJulian Elischer 4914cf49a43SJulian Elischer /* If the lengths don't match, that aint it. */ 4924cf49a43SJulian Elischer if (neg->service_len != svc_len) 4934cf49a43SJulian Elischer continue; 4944cf49a43SJulian Elischer 4954cf49a43SJulian Elischer /* An exact match? */ 4969088fa05SBrian Somers if (svc_len == 0) 4979088fa05SBrian Somers break; 4989088fa05SBrian Somers 4994cf49a43SJulian Elischer if (strncmp(svc_name, neg->service.data, svc_len) == 0) 5004cf49a43SJulian Elischer break; 5014cf49a43SJulian Elischer } 5029088fa05SBrian Somers return (hook ? hook : allhook); 5034cf49a43SJulian Elischer } 5044cf49a43SJulian Elischer /************************************************************************** 5054cf49a43SJulian Elischer * Routine to find a particular session that matches an incoming packet * 5064cf49a43SJulian Elischer **************************************************************************/ 5074cf49a43SJulian Elischer static hook_p 508816b834fSArchie Cobbs pppoe_findsession(node_p node, const struct pppoe_full_hdr *wh) 5094cf49a43SJulian Elischer { 5104cf49a43SJulian Elischer sessp sp = NULL; 5114cf49a43SJulian Elischer hook_p hook = NULL; 51230400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 513b86d0a9eSJulian Elischer u_int16_t session = ntohs(wh->ph.sid); 5144cf49a43SJulian Elischer 5154cf49a43SJulian Elischer /* 5164cf49a43SJulian Elischer * find matching peer/session combination. 5174cf49a43SJulian Elischer */ 5181e2510f8SJulian Elischer AAA 51930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5204cf49a43SJulian Elischer /* don't check special hooks */ 52130400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 52230400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 5234cf49a43SJulian Elischer continue; 5244cf49a43SJulian Elischer } 52530400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 5264cf49a43SJulian Elischer if ( ( (sp->state == PPPOE_CONNECTED) 5274cf49a43SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED) ) 5284cf49a43SJulian Elischer && (sp->Session_ID == session) 5294cf49a43SJulian Elischer && (bcmp(sp->pkt_hdr.eh.ether_dhost, 5304cf49a43SJulian Elischer wh->eh.ether_shost, 5314cf49a43SJulian Elischer ETHER_ADDR_LEN)) == 0) { 5324cf49a43SJulian Elischer break; 5334cf49a43SJulian Elischer } 5344cf49a43SJulian Elischer } 5354cf49a43SJulian Elischer return (hook); 5364cf49a43SJulian Elischer } 5374cf49a43SJulian Elischer 5384cf49a43SJulian Elischer static hook_p 539816b834fSArchie Cobbs pppoe_finduniq(node_p node, const struct pppoe_tag *tag) 5404cf49a43SJulian Elischer { 5414cf49a43SJulian Elischer hook_p hook = NULL; 54230400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 5434cf49a43SJulian Elischer union uniq uniq; 5444cf49a43SJulian Elischer 5451e2510f8SJulian Elischer AAA 5464cf49a43SJulian Elischer bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 5474cf49a43SJulian Elischer /* cycle through all known hooks */ 54830400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5494cf49a43SJulian Elischer /* don't check special hooks */ 55030400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 55130400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 5524cf49a43SJulian Elischer continue; 55330400f03SJulian Elischer if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 5544cf49a43SJulian Elischer break; 5554cf49a43SJulian Elischer } 5564cf49a43SJulian Elischer return (hook); 5574cf49a43SJulian Elischer } 5584cf49a43SJulian Elischer 5594cf49a43SJulian Elischer /************************************************************************** 5604cf49a43SJulian Elischer * start of Netgraph entrypoints * 5614cf49a43SJulian Elischer **************************************************************************/ 5624cf49a43SJulian Elischer 5634cf49a43SJulian Elischer /* 5644cf49a43SJulian Elischer * Allocate the private data structure and the generic node 5654cf49a43SJulian Elischer * and link them together. 5664cf49a43SJulian Elischer * 5674cf49a43SJulian Elischer * ng_make_node_common() returns with a generic node struct 5684cf49a43SJulian Elischer * with a single reference for us.. we transfer it to the 5694cf49a43SJulian Elischer * private structure.. when we free the private struct we must 5704cf49a43SJulian Elischer * unref the node so it gets freed too. 5714cf49a43SJulian Elischer */ 5724cf49a43SJulian Elischer static int 573069154d5SJulian Elischer ng_pppoe_constructor(node_p node) 5744cf49a43SJulian Elischer { 5754cf49a43SJulian Elischer priv_p privdata; 5764cf49a43SJulian Elischer 5771e2510f8SJulian Elischer AAA 5784cf49a43SJulian Elischer /* Initialize private descriptor */ 5799c8c302fSJulian Elischer MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH_PPPOE, 58099cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 5814cf49a43SJulian Elischer if (privdata == NULL) 5824cf49a43SJulian Elischer return (ENOMEM); 5834cf49a43SJulian Elischer 5844cf49a43SJulian Elischer /* Link structs together; this counts as our one reference to *nodep */ 58530400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, privdata); 586069154d5SJulian Elischer privdata->node = node; 5874cf49a43SJulian Elischer return (0); 5884cf49a43SJulian Elischer } 5894cf49a43SJulian Elischer 5904cf49a43SJulian Elischer /* 5914cf49a43SJulian Elischer * Give our ok for a hook to be added... 5924cf49a43SJulian Elischer * point the hook's private info to the hook structure. 5934cf49a43SJulian Elischer * 5944cf49a43SJulian Elischer * The following hook names are special: 5954cf49a43SJulian Elischer * Ethernet: the hook that should be connected to a NIC. 5964cf49a43SJulian Elischer * debug: copies of data sent out here (when I write the code). 597859a4d16SJulian Elischer * All other hook names need only be unique. (the framework checks this). 5984cf49a43SJulian Elischer */ 5994cf49a43SJulian Elischer static int 6008876b55dSJulian Elischer ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 6014cf49a43SJulian Elischer { 60230400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 6034cf49a43SJulian Elischer sessp sp; 6044cf49a43SJulian Elischer 6051e2510f8SJulian Elischer AAA 6064cf49a43SJulian Elischer if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 6074cf49a43SJulian Elischer privp->ethernet_hook = hook; 60830400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 6094cf49a43SJulian Elischer } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 6104cf49a43SJulian Elischer privp->debug_hook = hook; 61130400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 6124cf49a43SJulian Elischer } else { 6134cf49a43SJulian Elischer /* 6144cf49a43SJulian Elischer * Any other unique name is OK. 6154cf49a43SJulian Elischer * The infrastructure has already checked that it's unique, 6164cf49a43SJulian Elischer * so just allocate it and hook it in. 6174cf49a43SJulian Elischer */ 6189c8c302fSJulian Elischer MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 6194cf49a43SJulian Elischer if (sp == NULL) { 6204cf49a43SJulian Elischer return (ENOMEM); 6214cf49a43SJulian Elischer } 6224cf49a43SJulian Elischer 62330400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, sp); 6244cf49a43SJulian Elischer sp->hook = hook; 6254cf49a43SJulian Elischer } 6264cf49a43SJulian Elischer return(0); 6274cf49a43SJulian Elischer } 6284cf49a43SJulian Elischer 6294cf49a43SJulian Elischer /* 6304cf49a43SJulian Elischer * Get a netgraph control message. 6314cf49a43SJulian Elischer * Check it is one we understand. If needed, send a response. 6324cf49a43SJulian Elischer * We sometimes save the address for an async action later. 6334cf49a43SJulian Elischer * Always free the message. 6344cf49a43SJulian Elischer */ 6354cf49a43SJulian Elischer static int 636069154d5SJulian Elischer ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 6374cf49a43SJulian Elischer { 63830400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 6398876b55dSJulian Elischer struct ngpppoe_init_data *ourmsg = NULL; 6404cf49a43SJulian Elischer struct ng_mesg *resp = NULL; 6414cf49a43SJulian Elischer int error = 0; 6424cf49a43SJulian Elischer hook_p hook = NULL; 6434cf49a43SJulian Elischer sessp sp = NULL; 6444cf49a43SJulian Elischer negp neg = NULL; 645069154d5SJulian Elischer struct ng_mesg *msg; 6464cf49a43SJulian Elischer 6471e2510f8SJulian Elischer AAA 648069154d5SJulian Elischer NGI_GET_MSG(item, msg); 6494cf49a43SJulian Elischer /* Deal with message according to cookie and command */ 6504cf49a43SJulian Elischer switch (msg->header.typecookie) { 6514cf49a43SJulian Elischer case NGM_PPPOE_COOKIE: 6524cf49a43SJulian Elischer switch (msg->header.cmd) { 6534cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 6544cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 6554cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 656859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 65727121ab1SBrian Somers ourmsg = (struct ngpppoe_init_data *)msg->data; 65827121ab1SBrian Somers if (msg->header.arglen < sizeof(*ourmsg)) { 65927121ab1SBrian Somers printf("pppoe: init data too small\n"); 6604cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6614cf49a43SJulian Elischer } 66276a70671SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) > 66376a70671SBrian Somers PPPOE_SERVICE_NAME_SIZE) { 66476a70671SBrian Somers printf("pppoe_rcvmsg: service name too big"); 6654cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6664cf49a43SJulian Elischer } 66727121ab1SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) < 66827121ab1SBrian Somers ourmsg->data_len) { 66927121ab1SBrian Somers printf("pppoe: init data has bad length," 67027121ab1SBrian Somers " %d should be %d\n", ourmsg->data_len, 67127121ab1SBrian Somers msg->header.arglen - sizeof (*ourmsg)); 67276a70671SBrian Somers LEAVE(EMSGSIZE); 67376a70671SBrian Somers } 67476a70671SBrian Somers 6754cf49a43SJulian Elischer /* make sure strcmp will terminate safely */ 6764cf49a43SJulian Elischer ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 6774cf49a43SJulian Elischer 6784cf49a43SJulian Elischer /* cycle through all known hooks */ 67930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 68030400f03SJulian Elischer if (NG_HOOK_NAME(hook) 68130400f03SJulian Elischer && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 6824cf49a43SJulian Elischer break; 6834cf49a43SJulian Elischer } 6844cf49a43SJulian Elischer if (hook == NULL) { 6854cf49a43SJulian Elischer LEAVE(ENOENT); 6864cf49a43SJulian Elischer } 68730400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 68830400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 6894cf49a43SJulian Elischer LEAVE(EINVAL); 6904cf49a43SJulian Elischer } 69130400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 692859a4d16SJulian Elischer 6939088fa05SBrian Somers if (msg->header.cmd == NGM_PPPOE_LISTEN) { 6949088fa05SBrian Somers /* 6959088fa05SBrian Somers * Ensure we aren't already listening for this 6969088fa05SBrian Somers * service. 6979088fa05SBrian Somers */ 6989088fa05SBrian Somers if (pppoe_match_svc(node, ourmsg->data, 6999088fa05SBrian Somers ourmsg->data_len, NG_MATCH_EXACT) != NULL) { 7009088fa05SBrian Somers LEAVE(EEXIST); 7019088fa05SBrian Somers } 7029088fa05SBrian Somers } 7039088fa05SBrian Somers 704859a4d16SJulian Elischer /* 705859a4d16SJulian Elischer * PPPOE_SERVICE advertisments are set up 706859a4d16SJulian Elischer * on sessions that are in PRIMED state. 707859a4d16SJulian Elischer */ 708859a4d16SJulian Elischer if (msg->header.cmd == NGM_PPPOE_SERVICE) { 709859a4d16SJulian Elischer break; 710859a4d16SJulian Elischer } 7114cf49a43SJulian Elischer if (sp->state |= PPPOE_SNONE) { 7124cf49a43SJulian Elischer printf("pppoe: Session already active\n"); 7134cf49a43SJulian Elischer LEAVE(EISCONN); 7144cf49a43SJulian Elischer } 7151e2510f8SJulian Elischer 7164cf49a43SJulian Elischer /* 7174cf49a43SJulian Elischer * set up prototype header 7184cf49a43SJulian Elischer */ 7199c8c302fSJulian Elischer MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH_PPPOE, 72099cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 7214cf49a43SJulian Elischer 7224cf49a43SJulian Elischer if (neg == NULL) { 7234cf49a43SJulian Elischer printf("pppoe: Session out of memory\n"); 7244cf49a43SJulian Elischer LEAVE(ENOMEM); 7254cf49a43SJulian Elischer } 7264cf49a43SJulian Elischer MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 7274cf49a43SJulian Elischer if(neg->m == NULL) { 7281e2510f8SJulian Elischer printf("pppoe: Session out of mbufs\n"); 7299c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7304cf49a43SJulian Elischer LEAVE(ENOBUFS); 7314cf49a43SJulian Elischer } 7324cf49a43SJulian Elischer neg->m->m_pkthdr.rcvif = NULL; 7334cf49a43SJulian Elischer MCLGET(neg->m, M_DONTWAIT); 7344cf49a43SJulian Elischer if ((neg->m->m_flags & M_EXT) == 0) { 7351e2510f8SJulian Elischer printf("pppoe: Session out of mcls\n"); 7364cf49a43SJulian Elischer m_freem(neg->m); 7379c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7384cf49a43SJulian Elischer LEAVE(ENOBUFS); 7394cf49a43SJulian Elischer } 7404cf49a43SJulian Elischer sp->neg = neg; 7411e2510f8SJulian Elischer callout_handle_init( &neg->timeout_handle); 7424cf49a43SJulian Elischer neg->m->m_len = sizeof(struct pppoe_full_hdr); 7434cf49a43SJulian Elischer neg->pkt = mtod(neg->m, union packet*); 7444cf49a43SJulian Elischer neg->pkt->pkt_header.eh = eh_prototype; 7454cf49a43SJulian Elischer neg->pkt->pkt_header.ph.ver = 0x1; 7464cf49a43SJulian Elischer neg->pkt->pkt_header.ph.type = 0x1; 7474cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 0x0000; 7484cf49a43SJulian Elischer neg->timeout = 0; 7494cf49a43SJulian Elischer 750069154d5SJulian Elischer sp->creator = NGI_RETADDR(item); 7514cf49a43SJulian Elischer } 7524cf49a43SJulian Elischer switch (msg->header.cmd) { 7534cf49a43SJulian Elischer case NGM_PPPOE_GET_STATUS: 7544cf49a43SJulian Elischer { 7558876b55dSJulian Elischer struct ngpppoestat *stats; 7564cf49a43SJulian Elischer 7574cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 7584cf49a43SJulian Elischer if (!resp) { 7594cf49a43SJulian Elischer LEAVE(ENOMEM); 7604cf49a43SJulian Elischer } 7618876b55dSJulian Elischer stats = (struct ngpppoestat *) resp->data; 7624cf49a43SJulian Elischer stats->packets_in = privp->packets_in; 7634cf49a43SJulian Elischer stats->packets_out = privp->packets_out; 7644cf49a43SJulian Elischer break; 7654cf49a43SJulian Elischer } 7664cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 7674cf49a43SJulian Elischer /* 7684cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7694cf49a43SJulian Elischer * Send a PADI request, and start the timeout logic. 7704cf49a43SJulian Elischer * Store the originator of this message so we can send 7714cf49a43SJulian Elischer * a success of fail message to them later. 7724cf49a43SJulian Elischer * Move the session to SINIT 7734cf49a43SJulian Elischer * Set up the session to the correct state and 7744cf49a43SJulian Elischer * start it. 7754cf49a43SJulian Elischer */ 7764cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 77727121ab1SBrian Somers neg->service.hdr.tag_len = 77827121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 77927121ab1SBrian Somers if (ourmsg->data_len) 78027121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 78127121ab1SBrian Somers ourmsg->data_len); 78227121ab1SBrian Somers neg->service_len = ourmsg->data_len; 7834cf49a43SJulian Elischer pppoe_start(sp); 7844cf49a43SJulian Elischer break; 7854cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 7864cf49a43SJulian Elischer /* 7874cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7884cf49a43SJulian Elischer * Install the service matching string. 7894cf49a43SJulian Elischer * Store the originator of this message so we can send 7904cf49a43SJulian Elischer * a success of fail message to them later. 7914cf49a43SJulian Elischer * Move the hook to 'LISTENING' 7924cf49a43SJulian Elischer */ 7934cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 79427121ab1SBrian Somers neg->service.hdr.tag_len = 79527121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 7961e2510f8SJulian Elischer 79727121ab1SBrian Somers if (ourmsg->data_len) 79827121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 79927121ab1SBrian Somers ourmsg->data_len); 80027121ab1SBrian Somers neg->service_len = ourmsg->data_len; 8014cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADT_CODE; 8024cf49a43SJulian Elischer /* 8034cf49a43SJulian Elischer * wait for PADI packet coming from ethernet 8044cf49a43SJulian Elischer */ 8054cf49a43SJulian Elischer sp->state = PPPOE_LISTENING; 8064cf49a43SJulian Elischer break; 8074cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 8084cf49a43SJulian Elischer /* 8094cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8104cf49a43SJulian Elischer * Store the originator of this message so we can send 8114cf49a43SJulian Elischer * a success of fail message to them later. 8124cf49a43SJulian Elischer * Store the AC-Name given and go to PRIMED. 8134cf49a43SJulian Elischer */ 8144cf49a43SJulian Elischer neg->ac_name.hdr.tag_type = PTT_AC_NAME; 81527121ab1SBrian Somers neg->ac_name.hdr.tag_len = 81627121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 81727121ab1SBrian Somers if (ourmsg->data_len) 81827121ab1SBrian Somers bcopy(ourmsg->data, neg->ac_name.data, 81927121ab1SBrian Somers ourmsg->data_len); 82027121ab1SBrian Somers neg->ac_name_len = ourmsg->data_len; 8214cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 8224cf49a43SJulian Elischer /* 8234cf49a43SJulian Elischer * Wait for PADI packet coming from hook 8244cf49a43SJulian Elischer */ 8254cf49a43SJulian Elischer sp->state = PPPOE_PRIMED; 8264cf49a43SJulian Elischer break; 827859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 828859a4d16SJulian Elischer /* 829859a4d16SJulian Elischer * Check the session is primed. 830859a4d16SJulian Elischer * for now just allow ONE service to be advertised. 831859a4d16SJulian Elischer * If you do it twice you just overwrite. 832859a4d16SJulian Elischer */ 8335078fb0bSJulian Elischer if (sp->state != PPPOE_PRIMED) { 834859a4d16SJulian Elischer printf("pppoe: Session not primed\n"); 835859a4d16SJulian Elischer LEAVE(EISCONN); 836859a4d16SJulian Elischer } 8370069b9cbSJulian Elischer neg = sp->neg; 838859a4d16SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 839859a4d16SJulian Elischer neg->service.hdr.tag_len = 840859a4d16SJulian Elischer htons((u_int16_t)ourmsg->data_len); 841859a4d16SJulian Elischer 842859a4d16SJulian Elischer if (ourmsg->data_len) 843859a4d16SJulian Elischer bcopy(ourmsg->data, neg->service.data, 844859a4d16SJulian Elischer ourmsg->data_len); 845859a4d16SJulian Elischer neg->service_len = ourmsg->data_len; 846859a4d16SJulian Elischer break; 8474cf49a43SJulian Elischer default: 8484cf49a43SJulian Elischer LEAVE(EINVAL); 8494cf49a43SJulian Elischer } 8504cf49a43SJulian Elischer break; 8514cf49a43SJulian Elischer default: 8524cf49a43SJulian Elischer LEAVE(EINVAL); 8534cf49a43SJulian Elischer } 8544cf49a43SJulian Elischer 8554cf49a43SJulian Elischer /* Take care of synchronous response, if any */ 8564cf49a43SJulian Elischer quit: 857069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 858069154d5SJulian Elischer /* Free the message and return */ 859069154d5SJulian Elischer NG_FREE_MSG(msg); 8604cf49a43SJulian Elischer return(error); 8614cf49a43SJulian Elischer } 8624cf49a43SJulian Elischer 8631e2510f8SJulian Elischer /* 8641e2510f8SJulian Elischer * Start a client into the first state. A separate function because 8651e2510f8SJulian Elischer * it can be needed if the negotiation times out. 8661e2510f8SJulian Elischer */ 8674cf49a43SJulian Elischer static void 8684cf49a43SJulian Elischer pppoe_start(sessp sp) 8694cf49a43SJulian Elischer { 8704cf49a43SJulian Elischer struct { 8714cf49a43SJulian Elischer struct pppoe_tag hdr; 8724cf49a43SJulian Elischer union uniq data; 8732b5dcd2fSBrian Somers } __attribute ((packed)) uniqtag; 8744cf49a43SJulian Elischer 8754cf49a43SJulian Elischer /* 8764cf49a43SJulian Elischer * kick the state machine into starting up 8774cf49a43SJulian Elischer */ 8781e2510f8SJulian Elischer AAA 8794cf49a43SJulian Elischer sp->state = PPPOE_SINIT; 8801e2510f8SJulian Elischer /* reset the packet header to broadcast */ 8811e2510f8SJulian Elischer sp->neg->pkt->pkt_header.eh = eh_prototype; 8821e2510f8SJulian Elischer sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 8834cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 8844cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 8854cf49a43SJulian Elischer uniqtag.data.pointer = sp; 8864cf49a43SJulian Elischer init_tags(sp); 8871f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 8887ccbb17bSJulian Elischer insert_tag(sp, &sp->neg->service.hdr); 8894cf49a43SJulian Elischer make_packet(sp); 8904cf49a43SJulian Elischer sendpacket(sp); 8914cf49a43SJulian Elischer } 8924cf49a43SJulian Elischer 893c48a0b5fSBrian Somers static int 894816b834fSArchie Cobbs send_acname(sessp sp, const struct pppoe_tag *tag) 895c48a0b5fSBrian Somers { 896c48a0b5fSBrian Somers int error; 897c48a0b5fSBrian Somers struct ng_mesg *msg; 898c48a0b5fSBrian Somers struct ngpppoe_sts *sts; 899c48a0b5fSBrian Somers 900c48a0b5fSBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME, 901c48a0b5fSBrian Somers sizeof(struct ngpppoe_sts), M_NOWAIT); 902c48a0b5fSBrian Somers if (msg == NULL) 903c48a0b5fSBrian Somers return (ENOMEM); 904c48a0b5fSBrian Somers 905c48a0b5fSBrian Somers sts = (struct ngpppoe_sts *)msg->data; 906c48a0b5fSBrian Somers strncpy(sts->hook, tag->tag_data, 907c48a0b5fSBrian Somers min(NG_HOOKLEN + 1, ntohs(tag->tag_len))); 908c48a0b5fSBrian Somers NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 909c48a0b5fSBrian Somers 910c48a0b5fSBrian Somers return (error); 911c48a0b5fSBrian Somers } 912c48a0b5fSBrian Somers 91387c4cce0SBrian Somers static int 91487c4cce0SBrian Somers send_sessionid(sessp sp) 91587c4cce0SBrian Somers { 91687c4cce0SBrian Somers int error; 91787c4cce0SBrian Somers struct ng_mesg *msg; 91887c4cce0SBrian Somers 91987c4cce0SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID, 92087c4cce0SBrian Somers sizeof(u_int16_t), M_NOWAIT); 92187c4cce0SBrian Somers if (msg == NULL) 92287c4cce0SBrian Somers return (ENOMEM); 92387c4cce0SBrian Somers 92487c4cce0SBrian Somers *(u_int16_t *)msg->data = sp->Session_ID; 92587c4cce0SBrian Somers NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 92687c4cce0SBrian Somers 92787c4cce0SBrian Somers return (error); 92887c4cce0SBrian Somers } 92987c4cce0SBrian Somers 9304cf49a43SJulian Elischer /* 9314cf49a43SJulian Elischer * Receive data, and do something with it. 9324cf49a43SJulian Elischer * The caller will never free m or meta, so 9334cf49a43SJulian Elischer * if we use up this data or abort we must free BOTH of these. 9344cf49a43SJulian Elischer */ 9354cf49a43SJulian Elischer static int 936069154d5SJulian Elischer ng_pppoe_rcvdata(hook_p hook, item_p item) 9374cf49a43SJulian Elischer { 93830400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 93930400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 94030400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 941816b834fSArchie Cobbs const struct pppoe_full_hdr *wh; 942816b834fSArchie Cobbs const struct pppoe_hdr *ph; 9434cf49a43SJulian Elischer int error = 0; 9444cf49a43SJulian Elischer u_int16_t session; 9454cf49a43SJulian Elischer u_int16_t length; 9464cf49a43SJulian Elischer u_int8_t code; 947816b834fSArchie Cobbs const struct pppoe_tag *utag = NULL, *tag = NULL; 9484cf49a43SJulian Elischer hook_p sendhook; 9494cf49a43SJulian Elischer struct { 9504cf49a43SJulian Elischer struct pppoe_tag hdr; 9514cf49a43SJulian Elischer union uniq data; 9522b5dcd2fSBrian Somers } __attribute ((packed)) uniqtag; 9534cf49a43SJulian Elischer negp neg = NULL; 954069154d5SJulian Elischer struct mbuf *m; 9554cf49a43SJulian Elischer 9561e2510f8SJulian Elischer AAA 957069154d5SJulian Elischer NGI_GET_M(item, m); 95830400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 9594cf49a43SJulian Elischer /* 9604cf49a43SJulian Elischer * Data from the debug hook gets sent without modification 9614cf49a43SJulian Elischer * straight to the ethernet. 9624cf49a43SJulian Elischer */ 96330400f03SJulian Elischer NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 9644cf49a43SJulian Elischer privp->packets_out++; 96530400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 9664cf49a43SJulian Elischer /* 9674cf49a43SJulian Elischer * Incoming data. 9684cf49a43SJulian Elischer * Dig out various fields from the packet. 9694cf49a43SJulian Elischer * use them to decide where to send it. 9704cf49a43SJulian Elischer */ 9714cf49a43SJulian Elischer 9724cf49a43SJulian Elischer privp->packets_in++; 9730c65c135SJulian Elischer if( m->m_len < sizeof(*wh)) { 9740c65c135SJulian Elischer m = m_pullup(m, sizeof(*wh)); /* Checks length */ 9754cf49a43SJulian Elischer if (m == NULL) { 976b86d0a9eSJulian Elischer printf("couldn't m_pullup\n"); 9774cf49a43SJulian Elischer LEAVE(ENOBUFS); 9784cf49a43SJulian Elischer } 9790c65c135SJulian Elischer } 9804cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 9814cf49a43SJulian Elischer length = ntohs(wh->ph.length); 9820c65c135SJulian Elischer switch(wh->eh.ether_type) { 983bfa7e882SJulian Elischer case ETHERTYPE_PPPOE_STUPID_DISC: 98494142c49SJulian Elischer nonstandard = 1; 985bfa7e882SJulian Elischer eh_prototype.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 986bfa7e882SJulian Elischer /* fall through */ 9874cf49a43SJulian Elischer case ETHERTYPE_PPPOE_DISC: 9884cf49a43SJulian Elischer /* 989bdaf2e81SJulian Elischer * We need to try to make sure that the tag area 990bdaf2e81SJulian Elischer * is contiguous, or we could wander off the end 9914cf49a43SJulian Elischer * of a buffer and make a mess. 9924cf49a43SJulian Elischer * (Linux wouldn't have this problem). 9934cf49a43SJulian Elischer */ 9940c65c135SJulian Elischer if (m->m_pkthdr.len <= MHLEN) { 9950c65c135SJulian Elischer if( m->m_len < m->m_pkthdr.len) { 9960c65c135SJulian Elischer m = m_pullup(m, m->m_pkthdr.len); 9970c65c135SJulian Elischer if (m == NULL) { 9980c65c135SJulian Elischer printf("couldn't m_pullup\n"); 9990c65c135SJulian Elischer LEAVE(ENOBUFS); 10000c65c135SJulian Elischer } 10010c65c135SJulian Elischer } 10020c65c135SJulian Elischer } 10034cf49a43SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 10044cf49a43SJulian Elischer /* 10054cf49a43SJulian Elischer * It's not all in one piece. 10064cf49a43SJulian Elischer * We need to do extra work. 1007069154d5SJulian Elischer * Put it into a cluster. 10084cf49a43SJulian Elischer */ 1009069154d5SJulian Elischer struct mbuf *n; 1010069154d5SJulian Elischer n = m_dup(m, M_DONTWAIT); 1011069154d5SJulian Elischer m_freem(m); 1012069154d5SJulian Elischer m = n; 1013069154d5SJulian Elischer if (m) { 1014069154d5SJulian Elischer /* just check we got a cluster */ 1015069154d5SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 1016069154d5SJulian Elischer m_freem(m); 1017069154d5SJulian Elischer m = NULL; 1018069154d5SJulian Elischer } 1019069154d5SJulian Elischer } 1020069154d5SJulian Elischer if (m == NULL) { 10214cf49a43SJulian Elischer printf("packet fragmented\n"); 1022b86d0a9eSJulian Elischer LEAVE(EMSGSIZE); 10234cf49a43SJulian Elischer } 1024069154d5SJulian Elischer } 1025069154d5SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 1026069154d5SJulian Elischer length = ntohs(wh->ph.length); 1027069154d5SJulian Elischer ph = &wh->ph; 1028069154d5SJulian Elischer session = ntohs(wh->ph.sid); 1029069154d5SJulian Elischer code = wh->ph.code; 10304cf49a43SJulian Elischer 10314cf49a43SJulian Elischer switch(code) { 10324cf49a43SJulian Elischer case PADI_CODE: 10334cf49a43SJulian Elischer /* 10344cf49a43SJulian Elischer * We are a server: 10354cf49a43SJulian Elischer * Look for a hook with the required service 10364cf49a43SJulian Elischer * and send the ENTIRE packet up there. 10374cf49a43SJulian Elischer * It should come back to a new hook in 10384cf49a43SJulian Elischer * PRIMED state. Look there for further 10394cf49a43SJulian Elischer * processing. 10404cf49a43SJulian Elischer */ 10414cf49a43SJulian Elischer tag = get_tag(ph, PTT_SRV_NAME); 10424cf49a43SJulian Elischer if (tag == NULL) { 1043b86d0a9eSJulian Elischer printf("no service tag\n"); 10444cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10454cf49a43SJulian Elischer } 104630400f03SJulian Elischer sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 10479088fa05SBrian Somers tag->tag_data, ntohs(tag->tag_len), 10489088fa05SBrian Somers NG_MATCH_ANY); 10494cf49a43SJulian Elischer if (sendhook) { 1050069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, 1051069154d5SJulian Elischer sendhook, m); 10524cf49a43SJulian Elischer } else { 10534cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10544cf49a43SJulian Elischer } 10554cf49a43SJulian Elischer break; 10564cf49a43SJulian Elischer case PADO_CODE: 10574cf49a43SJulian Elischer /* 10584cf49a43SJulian Elischer * We are a client: 10594cf49a43SJulian Elischer * Use the host_uniq tag to find the 10604cf49a43SJulian Elischer * hook this is in response to. 1061b86d0a9eSJulian Elischer * Received #2, now send #3 10624cf49a43SJulian Elischer * For now simply accept the first we receive. 10634cf49a43SJulian Elischer */ 10641f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 10651f89d938SJulian Elischer if ((utag == NULL) 10661f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 1067b86d0a9eSJulian Elischer printf("no host unique field\n"); 10684cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10694cf49a43SJulian Elischer } 10704cf49a43SJulian Elischer 10711f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 10724cf49a43SJulian Elischer if (sendhook == NULL) { 1073b86d0a9eSJulian Elischer printf("no matching session\n"); 10744cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10754cf49a43SJulian Elischer } 10764cf49a43SJulian Elischer 10774cf49a43SJulian Elischer /* 10784cf49a43SJulian Elischer * Check the session is in the right state. 10794cf49a43SJulian Elischer * It needs to be in PPPOE_SINIT. 10804cf49a43SJulian Elischer */ 108130400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 10824cf49a43SJulian Elischer if (sp->state != PPPOE_SINIT) { 1083b86d0a9eSJulian Elischer printf("session in wrong state\n"); 10844cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10854cf49a43SJulian Elischer } 10864cf49a43SJulian Elischer neg = sp->neg; 10874cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 10884cf49a43SJulian Elischer neg->timeout_handle); 10894cf49a43SJulian Elischer 10904cf49a43SJulian Elischer /* 10914cf49a43SJulian Elischer * This is the first time we hear 10924cf49a43SJulian Elischer * from the server, so note it's 10934cf49a43SJulian Elischer * unicast address, replacing the 10944cf49a43SJulian Elischer * broadcast address . 10954cf49a43SJulian Elischer */ 10964cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 10974cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 10984cf49a43SJulian Elischer ETHER_ADDR_LEN); 10994cf49a43SJulian Elischer neg->timeout = 0; 11004cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADR_CODE; 11014cf49a43SJulian Elischer init_tags(sp); 11027ccbb17bSJulian Elischer insert_tag(sp, utag); /* Host Unique */ 11031f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1104b86d0a9eSJulian Elischer insert_tag(sp, tag); /* return cookie */ 1105c48a0b5fSBrian Somers if ((tag = get_tag(ph, PTT_AC_NAME))) { 11061f89d938SJulian Elischer insert_tag(sp, tag); /* return it */ 1107c48a0b5fSBrian Somers send_acname(sp, tag); 1108c48a0b5fSBrian Somers } 11097ccbb17bSJulian Elischer insert_tag(sp, &neg->service.hdr); /* Service */ 11104cf49a43SJulian Elischer scan_tags(sp, ph); 11114cf49a43SJulian Elischer make_packet(sp); 11124cf49a43SJulian Elischer sp->state = PPPOE_SREQ; 11134cf49a43SJulian Elischer sendpacket(sp); 11144cf49a43SJulian Elischer break; 11154cf49a43SJulian Elischer case PADR_CODE: 11164cf49a43SJulian Elischer 11174cf49a43SJulian Elischer /* 11184cf49a43SJulian Elischer * We are a server: 11194cf49a43SJulian Elischer * Use the ac_cookie tag to find the 11204cf49a43SJulian Elischer * hook this is in response to. 11214cf49a43SJulian Elischer */ 11221f89d938SJulian Elischer utag = get_tag(ph, PTT_AC_COOKIE); 11231f89d938SJulian Elischer if ((utag == NULL) 11241f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 11254cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11264cf49a43SJulian Elischer } 11274cf49a43SJulian Elischer 11281f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 11294cf49a43SJulian Elischer if (sendhook == NULL) { 11304cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11314cf49a43SJulian Elischer } 11324cf49a43SJulian Elischer 11334cf49a43SJulian Elischer /* 11344cf49a43SJulian Elischer * Check the session is in the right state. 11354cf49a43SJulian Elischer * It needs to be in PPPOE_SOFFER 11364cf49a43SJulian Elischer * or PPPOE_NEWCONNECTED. If the latter, 11374cf49a43SJulian Elischer * then this is a retry by the client. 11384cf49a43SJulian Elischer * so be nice, and resend. 11394cf49a43SJulian Elischer */ 114030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11414cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 11424cf49a43SJulian Elischer /* 11434cf49a43SJulian Elischer * Whoa! drop back to resend that 11444cf49a43SJulian Elischer * PADS packet. 11454cf49a43SJulian Elischer * We should still have a copy of it. 11464cf49a43SJulian Elischer */ 11474cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 11484cf49a43SJulian Elischer } 11494cf49a43SJulian Elischer if (sp->state != PPPOE_SOFFER) { 11504cf49a43SJulian Elischer LEAVE (ENETUNREACH); 11514cf49a43SJulian Elischer break; 11524cf49a43SJulian Elischer } 11534cf49a43SJulian Elischer neg = sp->neg; 11544cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 11554cf49a43SJulian Elischer neg->timeout_handle); 11564cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADS_CODE; 11574cf49a43SJulian Elischer if (sp->Session_ID == 0) 11584cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 1159b86d0a9eSJulian Elischer htons(sp->Session_ID 1160b86d0a9eSJulian Elischer = get_new_sid(node)); 116187c4cce0SBrian Somers send_sessionid(sp); 11624cf49a43SJulian Elischer neg->timeout = 0; 11634cf49a43SJulian Elischer /* 11644cf49a43SJulian Elischer * start working out the tags to respond with. 11654cf49a43SJulian Elischer */ 11664cf49a43SJulian Elischer init_tags(sp); 11674cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1168bdaf2e81SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 11694adb13fdSJulian Elischer insert_tag(sp, tag);/* return service */ 11701f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 11714adb13fdSJulian Elischer insert_tag(sp, tag); /* return it */ 11721f89d938SJulian Elischer insert_tag(sp, utag); /* ac_cookie */ 11734cf49a43SJulian Elischer scan_tags(sp, ph); 11744cf49a43SJulian Elischer make_packet(sp); 1175bdaf2e81SJulian Elischer sp->state = PPPOE_NEWCONNECTED; 11766faf164cSJulian Elischer sendpacket(sp); 11774cf49a43SJulian Elischer /* 11784cf49a43SJulian Elischer * Having sent the last Negotiation header, 11794cf49a43SJulian Elischer * Set up the stored packet header to 11804cf49a43SJulian Elischer * be correct for the actual session. 11814cf49a43SJulian Elischer * But keep the negotialtion stuff 11824cf49a43SJulian Elischer * around in case we need to resend this last 11834cf49a43SJulian Elischer * packet. We'll discard it when we move 11844cf49a43SJulian Elischer * from NEWCONNECTED to CONNECTED 11854cf49a43SJulian Elischer */ 11864cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 118794142c49SJulian Elischer if (nonstandard) 1188bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1189bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1190bfa7e882SJulian Elischer else 11914cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 11924cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 11934cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 1194b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 11954cf49a43SJulian Elischer break; 11964cf49a43SJulian Elischer case PADS_CODE: 11974cf49a43SJulian Elischer /* 11984cf49a43SJulian Elischer * We are a client: 11994cf49a43SJulian Elischer * Use the host_uniq tag to find the 12004cf49a43SJulian Elischer * hook this is in response to. 12014cf49a43SJulian Elischer * take the session ID and store it away. 12024cf49a43SJulian Elischer * Also make sure the pre-made header is 12034cf49a43SJulian Elischer * correct and set us into Session mode. 12044cf49a43SJulian Elischer */ 12051f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 12061f89d938SJulian Elischer if ((utag == NULL) 12071f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 12084cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12094cf49a43SJulian Elischer break; 12104cf49a43SJulian Elischer } 12111f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 12124cf49a43SJulian Elischer if (sendhook == NULL) { 12134cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12144cf49a43SJulian Elischer } 12154cf49a43SJulian Elischer 12164cf49a43SJulian Elischer /* 12174cf49a43SJulian Elischer * Check the session is in the right state. 12184cf49a43SJulian Elischer * It needs to be in PPPOE_SREQ. 12194cf49a43SJulian Elischer */ 122030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 12214cf49a43SJulian Elischer if (sp->state != PPPOE_SREQ) { 12224cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12234cf49a43SJulian Elischer } 12244cf49a43SJulian Elischer neg = sp->neg; 12254cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 12264cf49a43SJulian Elischer neg->timeout_handle); 1227cfbcfe62SJulian Elischer neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1228b86d0a9eSJulian Elischer sp->Session_ID = ntohs(wh->ph.sid); 122987c4cce0SBrian Somers send_sessionid(sp); 12304cf49a43SJulian Elischer neg->timeout = 0; 12314cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 12324cf49a43SJulian Elischer /* 12334cf49a43SJulian Elischer * Now we have gone to Connected mode, 12344cf49a43SJulian Elischer * Free all resources needed for 12354cf49a43SJulian Elischer * negotiation. 12364cf49a43SJulian Elischer * Keep a copy of the header we will be using. 12374cf49a43SJulian Elischer */ 12384cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 123994142c49SJulian Elischer if (nonstandard) 1240bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1241bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1242bfa7e882SJulian Elischer else 12434cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 12444cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 12454cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 12464cf49a43SJulian Elischer m_freem(neg->m); 12479c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 12484cf49a43SJulian Elischer sp->neg = NULL; 1249b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 12504cf49a43SJulian Elischer break; 12514cf49a43SJulian Elischer case PADT_CODE: 12524cf49a43SJulian Elischer /* 12534cf49a43SJulian Elischer * Send a 'close' message to the controlling 12544cf49a43SJulian Elischer * process (the one that set us up); 12554cf49a43SJulian Elischer * And then tear everything down. 12564cf49a43SJulian Elischer * 12574cf49a43SJulian Elischer * Find matching peer/session combination. 12584cf49a43SJulian Elischer */ 12594cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 12604cf49a43SJulian Elischer if (sendhook == NULL) { 12614cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12624cf49a43SJulian Elischer } 12634cf49a43SJulian Elischer /* send message to creator */ 12644cf49a43SJulian Elischer /* close hook */ 1265b58a8a3bSJulian Elischer if (sendhook) { 1266954c4772SJulian Elischer ng_rmhook_self(sendhook); 1267b58a8a3bSJulian Elischer } 12684cf49a43SJulian Elischer break; 12694cf49a43SJulian Elischer default: 12704cf49a43SJulian Elischer LEAVE(EPFNOSUPPORT); 12714cf49a43SJulian Elischer } 12724cf49a43SJulian Elischer break; 1273bfa7e882SJulian Elischer case ETHERTYPE_PPPOE_STUPID_SESS: 12744cf49a43SJulian Elischer case ETHERTYPE_PPPOE_SESS: 12754cf49a43SJulian Elischer /* 12764cf49a43SJulian Elischer * find matching peer/session combination. 12774cf49a43SJulian Elischer */ 12784cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 12794cf49a43SJulian Elischer if (sendhook == NULL) { 12804cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12814cf49a43SJulian Elischer break; 12824cf49a43SJulian Elischer } 128330400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 12844cf49a43SJulian Elischer m_adj(m, sizeof(*wh)); 12854cf49a43SJulian Elischer if (m->m_pkthdr.len < length) { 12864cf49a43SJulian Elischer /* Packet too short, dump it */ 12874cf49a43SJulian Elischer LEAVE(EMSGSIZE); 12884cf49a43SJulian Elischer } 12899fcb3d83SJulian Elischer 12904adb13fdSJulian Elischer /* Also need to trim excess at the end */ 12919fcb3d83SJulian Elischer if (m->m_pkthdr.len > length) { 12929fcb3d83SJulian Elischer m_adj(m, -((int)(m->m_pkthdr.len - length))); 12939fcb3d83SJulian Elischer } 12944cf49a43SJulian Elischer if ( sp->state != PPPOE_CONNECTED) { 12954cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 12964cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 12974cf49a43SJulian Elischer /* 12984cf49a43SJulian Elischer * Now we have gone to Connected mode, 12994cf49a43SJulian Elischer * Free all resources needed for 1300a4ec03cfSJulian Elischer * negotiation. Be paranoid about 1301a4ec03cfSJulian Elischer * whether there may be a timeout. 13024cf49a43SJulian Elischer */ 13034cf49a43SJulian Elischer m_freem(sp->neg->m); 1304a4ec03cfSJulian Elischer untimeout(pppoe_ticker, sendhook, 1305a4ec03cfSJulian Elischer sp->neg->timeout_handle); 13069c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 13074cf49a43SJulian Elischer sp->neg = NULL; 13084cf49a43SJulian Elischer } else { 13094cf49a43SJulian Elischer LEAVE (ENETUNREACH); 13104cf49a43SJulian Elischer break; 13114cf49a43SJulian Elischer } 13124cf49a43SJulian Elischer } 1313069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, sendhook, m); 13144cf49a43SJulian Elischer break; 13154cf49a43SJulian Elischer default: 13164b276f90SJulian Elischer LEAVE(EPFNOSUPPORT); 13174cf49a43SJulian Elischer } 13184cf49a43SJulian Elischer } else { 13194cf49a43SJulian Elischer /* 13204cf49a43SJulian Elischer * Not ethernet or debug hook.. 13214cf49a43SJulian Elischer * 13224cf49a43SJulian Elischer * The packet has come in on a normal hook. 13234cf49a43SJulian Elischer * We need to find out what kind of hook, 13244cf49a43SJulian Elischer * So we can decide how to handle it. 13254cf49a43SJulian Elischer * Check the hook's state. 13264cf49a43SJulian Elischer */ 132730400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 13284cf49a43SJulian Elischer switch (sp->state) { 13294cf49a43SJulian Elischer case PPPOE_NEWCONNECTED: 13304cf49a43SJulian Elischer case PPPOE_CONNECTED: { 13317b38c4e4SArchie Cobbs static const u_char addrctrl[] = { 0xff, 0x03 }; 13324cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 13337b38c4e4SArchie Cobbs 13347b38c4e4SArchie Cobbs /* 13357b38c4e4SArchie Cobbs * Remove PPP address and control fields, if any. 13367b38c4e4SArchie Cobbs * For example, ng_ppp(4) always sends LCP packets 13377b38c4e4SArchie Cobbs * with address and control fields as required by 13387b38c4e4SArchie Cobbs * generic PPP. PPPoE is an exception to the rule. 13397b38c4e4SArchie Cobbs */ 13407b38c4e4SArchie Cobbs if (m->m_pkthdr.len >= 2) { 13417b38c4e4SArchie Cobbs if (m->m_len < 2 && !(m = m_pullup(m, 2))) 13427b38c4e4SArchie Cobbs LEAVE(ENOBUFS); 13437b38c4e4SArchie Cobbs if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 13447b38c4e4SArchie Cobbs m_adj(m, 2); 13457b38c4e4SArchie Cobbs } 13464cf49a43SJulian Elischer /* 13474cf49a43SJulian Elischer * Bang in a pre-made header, and set the length up 13484cf49a43SJulian Elischer * to be correct. Then send it to the ethernet driver. 1349d9da9cbaSJulian Elischer * But first correct the length. 13504cf49a43SJulian Elischer */ 1351d9da9cbaSJulian Elischer sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 13524cf49a43SJulian Elischer M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 13534cf49a43SJulian Elischer if (m == NULL) { 13544cf49a43SJulian Elischer LEAVE(ENOBUFS); 13554cf49a43SJulian Elischer } 13564cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 13574cf49a43SJulian Elischer bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1358069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 13594cf49a43SJulian Elischer privp->packets_out++; 13604cf49a43SJulian Elischer break; 13614cf49a43SJulian Elischer } 13624cf49a43SJulian Elischer case PPPOE_PRIMED: 13634cf49a43SJulian Elischer /* 13644cf49a43SJulian Elischer * A PADI packet is being returned by the application 13654cf49a43SJulian Elischer * that has set up this hook. This indicates that it 13664cf49a43SJulian Elischer * wants us to offer service. 13674cf49a43SJulian Elischer */ 13684cf49a43SJulian Elischer neg = sp->neg; 1369bdaf2e81SJulian Elischer if (m->m_len < sizeof(*wh)) { 1370bdaf2e81SJulian Elischer m = m_pullup(m, sizeof(*wh)); 13714cf49a43SJulian Elischer if (m == NULL) { 13724cf49a43SJulian Elischer LEAVE(ENOBUFS); 13734cf49a43SJulian Elischer } 1374bdaf2e81SJulian Elischer } 13754cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 13764cf49a43SJulian Elischer ph = &wh->ph; 13774cf49a43SJulian Elischer session = ntohs(wh->ph.sid); 13784cf49a43SJulian Elischer length = ntohs(wh->ph.length); 13794cf49a43SJulian Elischer code = wh->ph.code; 13801e2510f8SJulian Elischer if ( code != PADI_CODE) { 13811e2510f8SJulian Elischer LEAVE(EINVAL); 13821e2510f8SJulian Elischer }; 13831e2510f8SJulian Elischer untimeout(pppoe_ticker, hook, 13841e2510f8SJulian Elischer neg->timeout_handle); 13854cf49a43SJulian Elischer 13864cf49a43SJulian Elischer /* 13874cf49a43SJulian Elischer * This is the first time we hear 13884cf49a43SJulian Elischer * from the client, so note it's 13894cf49a43SJulian Elischer * unicast address, replacing the 13904cf49a43SJulian Elischer * broadcast address. 13914cf49a43SJulian Elischer */ 13924cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 13934cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 13944cf49a43SJulian Elischer ETHER_ADDR_LEN); 13954cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 13964cf49a43SJulian Elischer neg->timeout = 0; 13974cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 13984cf49a43SJulian Elischer 13994cf49a43SJulian Elischer /* 14004cf49a43SJulian Elischer * start working out the tags to respond with. 14014cf49a43SJulian Elischer */ 14024cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_AC_COOKIE; 14034cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 14044cf49a43SJulian Elischer uniqtag.data.pointer = sp; 14054cf49a43SJulian Elischer init_tags(sp); 14064cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 14071f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 14084adb13fdSJulian Elischer insert_tag(sp, tag); /* return service */ 1409859a4d16SJulian Elischer /* 1410859a4d16SJulian Elischer * If we have a NULL service request 1411859a4d16SJulian Elischer * and have an extra service defined in this hook, 1412859a4d16SJulian Elischer * then also add a tag for the extra service. 1413859a4d16SJulian Elischer * XXX this is a hack. eventually we should be able 1414859a4d16SJulian Elischer * to support advertising many services, not just one 1415859a4d16SJulian Elischer */ 1416859a4d16SJulian Elischer if (((tag == NULL) || (tag->tag_len == 0)) 1417859a4d16SJulian Elischer && (neg->service.hdr.tag_len != 0)) { 1418859a4d16SJulian Elischer insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1419859a4d16SJulian Elischer } 14201f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 14211f89d938SJulian Elischer insert_tag(sp, tag); /* returned hostunique */ 14221f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 14234cf49a43SJulian Elischer scan_tags(sp, ph); 14244cf49a43SJulian Elischer make_packet(sp); 14254cf49a43SJulian Elischer sendpacket(sp); 14264cf49a43SJulian Elischer break; 14274cf49a43SJulian Elischer 14284cf49a43SJulian Elischer /* 14294cf49a43SJulian Elischer * Packets coming from the hook make no sense 14304cf49a43SJulian Elischer * to sessions in these states. Throw them away. 14314cf49a43SJulian Elischer */ 14324cf49a43SJulian Elischer case PPPOE_SINIT: 14334cf49a43SJulian Elischer case PPPOE_SREQ: 14344cf49a43SJulian Elischer case PPPOE_SOFFER: 14354cf49a43SJulian Elischer case PPPOE_SNONE: 14364cf49a43SJulian Elischer case PPPOE_LISTENING: 14374cf49a43SJulian Elischer case PPPOE_DEAD: 14384cf49a43SJulian Elischer default: 14394cf49a43SJulian Elischer LEAVE(ENETUNREACH); 14404cf49a43SJulian Elischer } 14414cf49a43SJulian Elischer } 14424cf49a43SJulian Elischer quit: 1443f5856029SJulian Elischer if (item) 1444069154d5SJulian Elischer NG_FREE_ITEM(item); 1445069154d5SJulian Elischer NG_FREE_M(m); 14464cf49a43SJulian Elischer return error; 14474cf49a43SJulian Elischer } 14484cf49a43SJulian Elischer 14494cf49a43SJulian Elischer /* 14504cf49a43SJulian Elischer * Do local shutdown processing.. 14514cf49a43SJulian Elischer * If we are a persistant device, we might refuse to go away, and 14524cf49a43SJulian Elischer * we'd only remove our links and reset ourself. 14534cf49a43SJulian Elischer */ 14544cf49a43SJulian Elischer static int 1455069154d5SJulian Elischer ng_pppoe_shutdown(node_p node) 14564cf49a43SJulian Elischer { 145730400f03SJulian Elischer const priv_p privdata = NG_NODE_PRIVATE(node); 14584cf49a43SJulian Elischer 14591e2510f8SJulian Elischer AAA 146030400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 146130400f03SJulian Elischer NG_NODE_UNREF(privdata->node); 14629c8c302fSJulian Elischer FREE(privdata, M_NETGRAPH_PPPOE); 14634cf49a43SJulian Elischer return (0); 14644cf49a43SJulian Elischer } 14654cf49a43SJulian Elischer 14664cf49a43SJulian Elischer /* 14674cf49a43SJulian Elischer * This is called once we've already connected a new hook to the other node. 14684cf49a43SJulian Elischer * It gives us a chance to balk at the last minute. 14694cf49a43SJulian Elischer */ 14704cf49a43SJulian Elischer static int 14718876b55dSJulian Elischer ng_pppoe_connect(hook_p hook) 14724cf49a43SJulian Elischer { 14734cf49a43SJulian Elischer /* be really amiable and just say "YUP that's OK by me! " */ 14744cf49a43SJulian Elischer return (0); 14754cf49a43SJulian Elischer } 14764cf49a43SJulian Elischer 14774cf49a43SJulian Elischer /* 14784cf49a43SJulian Elischer * Hook disconnection 14794cf49a43SJulian Elischer * 14806faf164cSJulian Elischer * Clean up all dangling links and information about the session/hook. 14814cf49a43SJulian Elischer * For this type, removal of the last link destroys the node 14824cf49a43SJulian Elischer */ 14834cf49a43SJulian Elischer static int 14848876b55dSJulian Elischer ng_pppoe_disconnect(hook_p hook) 14854cf49a43SJulian Elischer { 148630400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 148730400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 14884cf49a43SJulian Elischer sessp sp; 148904853d8aSJulian Elischer int hooks; 14904cf49a43SJulian Elischer 14911e2510f8SJulian Elischer AAA 149230400f03SJulian Elischer hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 149330400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 14944cf49a43SJulian Elischer privp->debug_hook = NULL; 149530400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 14964cf49a43SJulian Elischer privp->ethernet_hook = NULL; 149730400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) 1498069154d5SJulian Elischer ng_rmnode_self(node); 14994cf49a43SJulian Elischer } else { 150030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 1501b58a8a3bSJulian Elischer if (sp->state != PPPOE_SNONE ) { 1502b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1503b58a8a3bSJulian Elischer } 1504a4ec03cfSJulian Elischer /* 1505a4ec03cfSJulian Elischer * According to the spec, if we are connected, 1506a4ec03cfSJulian Elischer * we should send a DISC packet if we are shutting down 1507a4ec03cfSJulian Elischer * a session. 1508a4ec03cfSJulian Elischer */ 15099fcb3d83SJulian Elischer if ((privp->ethernet_hook) 15109fcb3d83SJulian Elischer && ((sp->state == PPPOE_CONNECTED) 15119fcb3d83SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED))) { 15129fcb3d83SJulian Elischer struct mbuf *m; 15139fcb3d83SJulian Elischer struct pppoe_full_hdr *wh; 15149fcb3d83SJulian Elischer struct pppoe_tag *tag; 15159fcb3d83SJulian Elischer int msglen = strlen(SIGNOFF); 15169fcb3d83SJulian Elischer int error = 0; 15179fcb3d83SJulian Elischer 15189fcb3d83SJulian Elischer /* revert the stored header to DISC/PADT mode */ 15199fcb3d83SJulian Elischer wh = &sp->pkt_hdr; 15209fcb3d83SJulian Elischer wh->ph.code = PADT_CODE; 152194142c49SJulian Elischer if (nonstandard) 1522bfa7e882SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 1523bfa7e882SJulian Elischer else 15249fcb3d83SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 15259fcb3d83SJulian Elischer 15269fcb3d83SJulian Elischer /* generate a packet of that type */ 15279fcb3d83SJulian Elischer MGETHDR(m, M_DONTWAIT, MT_DATA); 15286faf164cSJulian Elischer if(m == NULL) 15296faf164cSJulian Elischer printf("pppoe: Session out of mbufs\n"); 15306faf164cSJulian Elischer else { 15319fcb3d83SJulian Elischer m->m_pkthdr.rcvif = NULL; 15329fcb3d83SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*wh); 15336faf164cSJulian Elischer bcopy((caddr_t)wh, mtod(m, caddr_t), 15346faf164cSJulian Elischer sizeof(*wh)); 15356faf164cSJulian Elischer /* 15366faf164cSJulian Elischer * Add a General error message and adjust 15376faf164cSJulian Elischer * sizes 15386faf164cSJulian Elischer */ 15399fcb3d83SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 15409fcb3d83SJulian Elischer tag = wh->ph.tag; 15419fcb3d83SJulian Elischer tag->tag_type = PTT_GEN_ERR; 15429fcb3d83SJulian Elischer tag->tag_len = htons((u_int16_t)msglen); 15439fcb3d83SJulian Elischer strncpy(tag->tag_data, SIGNOFF, msglen); 15446faf164cSJulian Elischer m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 15456faf164cSJulian Elischer msglen); 15469fcb3d83SJulian Elischer wh->ph.length = htons(sizeof(*tag) + msglen); 1547069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, 1548069154d5SJulian Elischer privp->ethernet_hook, m); 15496faf164cSJulian Elischer } 15509fcb3d83SJulian Elischer } 1551a4ec03cfSJulian Elischer /* 1552514baf3fSJeroen Ruigrok van der Werven * As long as we have somewhere to store the timeout handle, 1553a4ec03cfSJulian Elischer * we may have a timeout pending.. get rid of it. 1554a4ec03cfSJulian Elischer */ 15551e2510f8SJulian Elischer if (sp->neg) { 15564cf49a43SJulian Elischer untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 15571e2510f8SJulian Elischer if (sp->neg->m) 15581e2510f8SJulian Elischer m_freem(sp->neg->m); 15599c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 15601e2510f8SJulian Elischer } 15619c8c302fSJulian Elischer FREE(sp, M_NETGRAPH_PPPOE); 156230400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 1563ed52f174SJulian Elischer /* work out how many session hooks there are */ 156404853d8aSJulian Elischer /* Node goes away on last session hook removal */ 156504853d8aSJulian Elischer if (privp->ethernet_hook) hooks -= 1; 1566ed52f174SJulian Elischer if (privp->debug_hook) hooks -= 1; 15674cf49a43SJulian Elischer } 156830400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 156930400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 1570069154d5SJulian Elischer ng_rmnode_self(node); 15714cf49a43SJulian Elischer return (0); 15724cf49a43SJulian Elischer } 15734cf49a43SJulian Elischer 15744cf49a43SJulian Elischer /* 15754cf49a43SJulian Elischer * timeouts come here. 15764cf49a43SJulian Elischer */ 15774cf49a43SJulian Elischer static void 15784cf49a43SJulian Elischer pppoe_ticker(void *arg) 15794cf49a43SJulian Elischer { 15804cf49a43SJulian Elischer int s = splnet(); 15814cf49a43SJulian Elischer hook_p hook = arg; 158230400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 15834cf49a43SJulian Elischer negp neg = sp->neg; 15844cf49a43SJulian Elischer int error = 0; 15854cf49a43SJulian Elischer struct mbuf *m0 = NULL; 158630400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 15874cf49a43SJulian Elischer 15881e2510f8SJulian Elischer AAA 15894cf49a43SJulian Elischer switch(sp->state) { 15904cf49a43SJulian Elischer /* 15914cf49a43SJulian Elischer * resend the last packet, using an exponential backoff. 15924cf49a43SJulian Elischer * After a period of time, stop growing the backoff, 15934adb13fdSJulian Elischer * and either leave it, or revert to the start. 15944cf49a43SJulian Elischer */ 15954cf49a43SJulian Elischer case PPPOE_SINIT: 15964cf49a43SJulian Elischer case PPPOE_SREQ: 15974cf49a43SJulian Elischer /* timeouts on these produce resends */ 15984cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1599069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16004cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16014cf49a43SJulian Elischer hook, neg->timeout * hz); 16024cf49a43SJulian Elischer if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 16034cf49a43SJulian Elischer if (sp->state == PPPOE_SREQ) { 16044cf49a43SJulian Elischer /* revert to SINIT mode */ 1605b58a8a3bSJulian Elischer pppoe_start(sp); 16064cf49a43SJulian Elischer } else { 16074cf49a43SJulian Elischer neg->timeout = PPPOE_TIMEOUT_LIMIT; 16084cf49a43SJulian Elischer } 16094cf49a43SJulian Elischer } 16104cf49a43SJulian Elischer break; 16114cf49a43SJulian Elischer case PPPOE_PRIMED: 16124cf49a43SJulian Elischer case PPPOE_SOFFER: 16134cf49a43SJulian Elischer /* a timeout on these says "give up" */ 1614954c4772SJulian Elischer ng_rmhook_self(hook); 16154cf49a43SJulian Elischer break; 16164cf49a43SJulian Elischer default: 16174cf49a43SJulian Elischer /* timeouts have no meaning in other states */ 16184cf49a43SJulian Elischer printf("pppoe: unexpected timeout\n"); 16194cf49a43SJulian Elischer } 16204cf49a43SJulian Elischer splx(s); 16214cf49a43SJulian Elischer } 16224cf49a43SJulian Elischer 16234cf49a43SJulian Elischer 16244cf49a43SJulian Elischer static void 16254cf49a43SJulian Elischer sendpacket(sessp sp) 16264cf49a43SJulian Elischer { 16274cf49a43SJulian Elischer int error = 0; 16284cf49a43SJulian Elischer struct mbuf *m0 = NULL; 16294cf49a43SJulian Elischer hook_p hook = sp->hook; 16304cf49a43SJulian Elischer negp neg = sp->neg; 163130400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 16324cf49a43SJulian Elischer 16331e2510f8SJulian Elischer AAA 16344cf49a43SJulian Elischer switch(sp->state) { 16354cf49a43SJulian Elischer case PPPOE_LISTENING: 16364cf49a43SJulian Elischer case PPPOE_DEAD: 16374cf49a43SJulian Elischer case PPPOE_SNONE: 16384cf49a43SJulian Elischer case PPPOE_CONNECTED: 1639b86d0a9eSJulian Elischer printf("pppoe: sendpacket: unexpected state\n"); 16404cf49a43SJulian Elischer break; 16414cf49a43SJulian Elischer 16426faf164cSJulian Elischer case PPPOE_NEWCONNECTED: 16436faf164cSJulian Elischer /* send the PADS without a timeout - we're now connected */ 16446faf164cSJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1645069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16466faf164cSJulian Elischer break; 16476faf164cSJulian Elischer 16484cf49a43SJulian Elischer case PPPOE_PRIMED: 16494cf49a43SJulian Elischer /* No packet to send, but set up the timeout */ 16504cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16514cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 16524cf49a43SJulian Elischer break; 16534cf49a43SJulian Elischer 16544cf49a43SJulian Elischer case PPPOE_SOFFER: 16554cf49a43SJulian Elischer /* 16564cf49a43SJulian Elischer * send the offer but if they don't respond 16574cf49a43SJulian Elischer * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 16584cf49a43SJulian Elischer */ 16594cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1660069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16614cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16624cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 16634cf49a43SJulian Elischer break; 16644cf49a43SJulian Elischer 16654cf49a43SJulian Elischer case PPPOE_SINIT: 16664cf49a43SJulian Elischer case PPPOE_SREQ: 16674cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1668069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1669d0fef808SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, hook, 1670d0fef808SJulian Elischer (hz * PPPOE_INITIAL_TIMEOUT)); 1671d0fef808SJulian Elischer neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 16724cf49a43SJulian Elischer break; 16734cf49a43SJulian Elischer 16744cf49a43SJulian Elischer default: 16754cf49a43SJulian Elischer error = EINVAL; 16764cf49a43SJulian Elischer printf("pppoe: timeout: bad state\n"); 16774cf49a43SJulian Elischer } 16784cf49a43SJulian Elischer /* return (error); */ 16794cf49a43SJulian Elischer } 16804cf49a43SJulian Elischer 16814cf49a43SJulian Elischer /* 16824cf49a43SJulian Elischer * Parse an incoming packet to see if any tags should be copied to the 16834adb13fdSJulian Elischer * output packet. Don't do any tags that have been handled in the main 16844adb13fdSJulian Elischer * state machine. 16854cf49a43SJulian Elischer */ 1686816b834fSArchie Cobbs static const struct pppoe_tag* 1687816b834fSArchie Cobbs scan_tags(sessp sp, const struct pppoe_hdr* ph) 16884cf49a43SJulian Elischer { 1689816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 1690816b834fSArchie Cobbs const char *ptn; 1691816b834fSArchie Cobbs const struct pppoe_tag *pt = &ph->tag[0]; 16924cf49a43SJulian Elischer /* 16934cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 16944cf49a43SJulian Elischer */ 16951e2510f8SJulian Elischer AAA 1696816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 16974cf49a43SJulian Elischer /* 16984cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 16994cf49a43SJulian Elischer */ 1700816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 17014cf49a43SJulian Elischer if(ptn > end) 17024cf49a43SJulian Elischer return NULL; 17034cf49a43SJulian Elischer 17044cf49a43SJulian Elischer switch (pt->tag_type) { 17054cf49a43SJulian Elischer case PTT_RELAY_SID: 17064cf49a43SJulian Elischer insert_tag(sp, pt); 17074cf49a43SJulian Elischer break; 17084cf49a43SJulian Elischer case PTT_EOL: 17094cf49a43SJulian Elischer return NULL; 17104cf49a43SJulian Elischer case PTT_SRV_NAME: 17114cf49a43SJulian Elischer case PTT_AC_NAME: 17124cf49a43SJulian Elischer case PTT_HOST_UNIQ: 17134cf49a43SJulian Elischer case PTT_AC_COOKIE: 17144cf49a43SJulian Elischer case PTT_VENDOR: 17154cf49a43SJulian Elischer case PTT_SRV_ERR: 17164cf49a43SJulian Elischer case PTT_SYS_ERR: 17174cf49a43SJulian Elischer case PTT_GEN_ERR: 17184cf49a43SJulian Elischer break; 17194cf49a43SJulian Elischer } 1720816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 17214cf49a43SJulian Elischer } 17224cf49a43SJulian Elischer return NULL; 17234cf49a43SJulian Elischer } 17244cf49a43SJulian Elischer 1725b58a8a3bSJulian Elischer static int 1726b58a8a3bSJulian Elischer pppoe_send_event(sessp sp, enum cmd cmdid) 1727b58a8a3bSJulian Elischer { 1728b58a8a3bSJulian Elischer int error; 1729b58a8a3bSJulian Elischer struct ng_mesg *msg; 17308876b55dSJulian Elischer struct ngpppoe_sts *sts; 1731b58a8a3bSJulian Elischer 17321e2510f8SJulian Elischer AAA 173327121ab1SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 17348876b55dSJulian Elischer sizeof(struct ngpppoe_sts), M_NOWAIT); 1735859a4d16SJulian Elischer if (msg == NULL) 1736859a4d16SJulian Elischer return (ENOMEM); 17378876b55dSJulian Elischer sts = (struct ngpppoe_sts *)msg->data; 173830400f03SJulian Elischer strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKLEN + 1); 173930400f03SJulian Elischer NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 1740b58a8a3bSJulian Elischer return (error); 1741b58a8a3bSJulian Elischer } 1742