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> 564cf49a43SJulian Elischer #include <net/ethernet.h> 574cf49a43SJulian Elischer 584cf49a43SJulian Elischer #include <netgraph/ng_message.h> 594cf49a43SJulian Elischer #include <netgraph/netgraph.h> 6076a70671SBrian Somers #include <netgraph/ng_parse.h> 614cf49a43SJulian Elischer #include <netgraph/ng_pppoe.h> 624cf49a43SJulian Elischer 639c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC 649c8c302fSJulian Elischer MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 659c8c302fSJulian Elischer #else 669c8c302fSJulian Elischer #define M_NETGRAPH_PPPOE M_NETGRAPH 679c8c302fSJulian Elischer #endif 689c8c302fSJulian Elischer 69da092930SArchie Cobbs #define SIGNOFF "session closed" 7076a70671SBrian Somers #define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 71da092930SArchie Cobbs 724cf49a43SJulian Elischer /* 734cf49a43SJulian Elischer * This section contains the netgraph method declarations for the 744cf49a43SJulian Elischer * sample node. These methods define the netgraph 'type'. 754cf49a43SJulian Elischer */ 764cf49a43SJulian Elischer 7774f5c6aaSJulian Elischer static ng_constructor_t ng_pppoe_constructor; 7874f5c6aaSJulian Elischer static ng_rcvmsg_t ng_pppoe_rcvmsg; 79069154d5SJulian Elischer static ng_shutdown_t ng_pppoe_shutdown; 8074f5c6aaSJulian Elischer static ng_newhook_t ng_pppoe_newhook; 8174f5c6aaSJulian Elischer static ng_connect_t ng_pppoe_connect; 8274f5c6aaSJulian Elischer static ng_rcvdata_t ng_pppoe_rcvdata; 8374f5c6aaSJulian Elischer static ng_disconnect_t ng_pppoe_disconnect; 844cf49a43SJulian Elischer 8576a70671SBrian Somers /* Parse type for struct ngpppoe_init_data */ 8627121ab1SBrian Somers static const struct ng_parse_struct_info ngpppoe_init_data_type_info 8776a70671SBrian Somers = NG_PPPOE_INIT_DATA_TYPE_INFO; 8827121ab1SBrian Somers static const struct ng_parse_type ngpppoe_init_data_state_type = { 8976a70671SBrian Somers &ng_parse_struct_type, 9027121ab1SBrian Somers &ngpppoe_init_data_type_info 9176a70671SBrian Somers }; 9276a70671SBrian Somers 9376a70671SBrian Somers /* Parse type for struct ngpppoe_sts */ 9476a70671SBrian Somers static const struct ng_parse_struct_info ng_pppoe_sts_type_info 9576a70671SBrian Somers = NG_PPPOE_STS_TYPE_INFO; 9676a70671SBrian Somers static const struct ng_parse_type ng_pppoe_sts_state_type = { 9776a70671SBrian Somers &ng_parse_struct_type, 9876a70671SBrian Somers &ng_pppoe_sts_type_info 9976a70671SBrian Somers }; 10076a70671SBrian Somers 10176a70671SBrian Somers /* List of commands and how to convert arguments to/from ASCII */ 10276a70671SBrian Somers static const struct ng_cmdlist ng_pppoe_cmds[] = { 10376a70671SBrian Somers { 10476a70671SBrian Somers NGM_PPPOE_COOKIE, 10576a70671SBrian Somers NGM_PPPOE_CONNECT, 10676a70671SBrian Somers "pppoe_connect", 10727121ab1SBrian Somers &ngpppoe_init_data_state_type, 10876a70671SBrian Somers NULL 10976a70671SBrian Somers }, 11076a70671SBrian Somers { 11176a70671SBrian Somers NGM_PPPOE_COOKIE, 11276a70671SBrian Somers NGM_PPPOE_LISTEN, 11376a70671SBrian Somers "pppoe_listen", 11427121ab1SBrian Somers &ngpppoe_init_data_state_type, 11576a70671SBrian Somers NULL 11676a70671SBrian Somers }, 11776a70671SBrian Somers { 11876a70671SBrian Somers NGM_PPPOE_COOKIE, 11976a70671SBrian Somers NGM_PPPOE_OFFER, 12076a70671SBrian Somers "pppoe_offer", 12127121ab1SBrian Somers &ngpppoe_init_data_state_type, 12276a70671SBrian Somers NULL 12376a70671SBrian Somers }, 12476a70671SBrian Somers { 12576a70671SBrian Somers NGM_PPPOE_COOKIE, 126859a4d16SJulian Elischer NGM_PPPOE_SERVICE, 127859a4d16SJulian Elischer "pppoe_service", 128859a4d16SJulian Elischer &ngpppoe_init_data_state_type, 129859a4d16SJulian Elischer NULL 130859a4d16SJulian Elischer }, 131859a4d16SJulian Elischer { 132859a4d16SJulian Elischer NGM_PPPOE_COOKIE, 13376a70671SBrian Somers NGM_PPPOE_SUCCESS, 13476a70671SBrian Somers "pppoe_success", 13576a70671SBrian Somers &ng_pppoe_sts_state_type, 13676a70671SBrian Somers NULL 13776a70671SBrian Somers }, 13876a70671SBrian Somers { 13976a70671SBrian Somers NGM_PPPOE_COOKIE, 14076a70671SBrian Somers NGM_PPPOE_FAIL, 14176a70671SBrian Somers "pppoe_fail", 14276a70671SBrian Somers &ng_pppoe_sts_state_type, 14376a70671SBrian Somers NULL 14476a70671SBrian Somers }, 14576a70671SBrian Somers { 14676a70671SBrian Somers NGM_PPPOE_COOKIE, 14776a70671SBrian Somers NGM_PPPOE_CLOSE, 14876a70671SBrian Somers "pppoe_close", 14976a70671SBrian Somers &ng_pppoe_sts_state_type, 15076a70671SBrian Somers NULL 15176a70671SBrian Somers }, 15276a70671SBrian Somers { 0 } 15376a70671SBrian Somers }; 15476a70671SBrian Somers 1554cf49a43SJulian Elischer /* Netgraph node type descriptor */ 1564cf49a43SJulian Elischer static struct ng_type typestruct = { 157589f6ed8SJulian Elischer NG_ABI_VERSION, 1584cf49a43SJulian Elischer NG_PPPOE_NODE_TYPE, 1594cf49a43SJulian Elischer NULL, 1608876b55dSJulian Elischer ng_pppoe_constructor, 1618876b55dSJulian Elischer ng_pppoe_rcvmsg, 162069154d5SJulian Elischer ng_pppoe_shutdown, 1638876b55dSJulian Elischer ng_pppoe_newhook, 1644cf49a43SJulian Elischer NULL, 1658876b55dSJulian Elischer ng_pppoe_connect, 1668876b55dSJulian Elischer ng_pppoe_rcvdata, 167f8307e12SArchie Cobbs ng_pppoe_disconnect, 16876a70671SBrian Somers ng_pppoe_cmds 1694cf49a43SJulian Elischer }; 1708876b55dSJulian Elischer NETGRAPH_INIT(pppoe, &typestruct); 1711acb27c6SJulian Elischer /* Depend on ng_ether so we can use the Ethernet parse type */ 1721acb27c6SJulian Elischer MODULE_DEPEND(ng_pppoe, ng_ether, 1, 1, 1); 1734cf49a43SJulian Elischer 1744cf49a43SJulian Elischer /* 1754cf49a43SJulian Elischer * States for the session state machine. 1764cf49a43SJulian Elischer * These have no meaning if there is no hook attached yet. 1774cf49a43SJulian Elischer */ 1784cf49a43SJulian Elischer enum state { 1794cf49a43SJulian Elischer PPPOE_SNONE=0, /* [both] Initial state */ 1806faf164cSJulian Elischer PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 1814cf49a43SJulian Elischer PPPOE_SINIT, /* [Client] Sent discovery initiation */ 1826faf164cSJulian Elischer PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 1836faf164cSJulian Elischer PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 1844cf49a43SJulian Elischer PPPOE_SREQ, /* [Client] Sent a Request */ 1856faf164cSJulian Elischer PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 1864cf49a43SJulian Elischer PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 1874cf49a43SJulian Elischer PPPOE_DEAD /* [Both] */ 1884cf49a43SJulian Elischer }; 1894cf49a43SJulian Elischer 1904cf49a43SJulian Elischer #define NUMTAGS 20 /* number of tags we are set up to work with */ 1914cf49a43SJulian Elischer 1924cf49a43SJulian Elischer /* 1934cf49a43SJulian Elischer * Information we store for each hook on each node for negotiating the 1944cf49a43SJulian Elischer * session. The mbuf and cluster are freed once negotiation has completed. 1954cf49a43SJulian Elischer * The whole negotiation block is then discarded. 1964cf49a43SJulian Elischer */ 1974cf49a43SJulian Elischer 1984cf49a43SJulian Elischer struct sess_neg { 1994cf49a43SJulian Elischer struct mbuf *m; /* holds cluster with last sent packet */ 2004cf49a43SJulian Elischer union packet *pkt; /* points within the above cluster */ 2014cf49a43SJulian Elischer struct callout_handle timeout_handle; /* see timeout(9) */ 2024cf49a43SJulian Elischer u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 2034cf49a43SJulian Elischer u_int numtags; 2044cf49a43SJulian Elischer struct pppoe_tag *tags[NUMTAGS]; 2054cf49a43SJulian Elischer u_int service_len; 2064cf49a43SJulian Elischer u_int ac_name_len; 2074cf49a43SJulian Elischer 2084cf49a43SJulian Elischer struct datatag service; 2094cf49a43SJulian Elischer struct datatag ac_name; 2104cf49a43SJulian Elischer }; 2114cf49a43SJulian Elischer typedef struct sess_neg *negp; 2124cf49a43SJulian Elischer 2134cf49a43SJulian Elischer /* 2144cf49a43SJulian Elischer * Session information that is needed after connection. 2154cf49a43SJulian Elischer */ 2162b9cf2f7SArchie Cobbs struct sess_con { 2174cf49a43SJulian Elischer hook_p hook; 2184cf49a43SJulian Elischer u_int16_t Session_ID; 2194cf49a43SJulian Elischer enum state state; 220069154d5SJulian Elischer ng_ID_t creator; /* who to notify */ 2214cf49a43SJulian Elischer struct pppoe_full_hdr pkt_hdr; /* used when connected */ 2224cf49a43SJulian Elischer negp neg; /* used when negotiating */ 2232b9cf2f7SArchie Cobbs /*struct sess_con *hash_next;*/ /* not yet used */ 2244cf49a43SJulian Elischer }; 2252b9cf2f7SArchie Cobbs typedef struct sess_con *sessp; 2264cf49a43SJulian Elischer 2274cf49a43SJulian Elischer /* 2284cf49a43SJulian Elischer * Information we store for each node 2294cf49a43SJulian Elischer */ 2304cf49a43SJulian Elischer struct PPPOE { 2314cf49a43SJulian Elischer node_p node; /* back pointer to node */ 2324cf49a43SJulian Elischer hook_p ethernet_hook; 2334cf49a43SJulian Elischer hook_p debug_hook; 2344cf49a43SJulian Elischer u_int packets_in; /* packets in from ethernet */ 2354cf49a43SJulian Elischer u_int packets_out; /* packets out towards ethernet */ 2364cf49a43SJulian Elischer u_int32_t flags; 2372b9cf2f7SArchie Cobbs /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ 2384cf49a43SJulian Elischer }; 2394cf49a43SJulian Elischer typedef struct PPPOE *priv_p; 2404cf49a43SJulian Elischer 2414cf49a43SJulian Elischer const struct ether_header eh_prototype = 2424cf49a43SJulian Elischer {{0xff,0xff,0xff,0xff,0xff,0xff}, 2434cf49a43SJulian Elischer {0x00,0x00,0x00,0x00,0x00,0x00}, 2444cf49a43SJulian Elischer ETHERTYPE_PPPOE_DISC}; 2454cf49a43SJulian Elischer 2464cf49a43SJulian Elischer union uniq { 2474cf49a43SJulian Elischer char bytes[sizeof(void *)]; 2484cf49a43SJulian Elischer void * pointer; 2494cf49a43SJulian Elischer }; 2504cf49a43SJulian Elischer 2514cf49a43SJulian Elischer #define LEAVE(x) do { error = x; goto quit; } while(0) 2524cf49a43SJulian Elischer static void pppoe_start(sessp sp); 2534cf49a43SJulian Elischer static void sendpacket(sessp sp); 2544cf49a43SJulian Elischer static void pppoe_ticker(void *arg); 2554cf49a43SJulian Elischer static struct pppoe_tag* scan_tags(sessp sp, struct pppoe_hdr* ph); 256b58a8a3bSJulian Elischer static int pppoe_send_event(sessp sp, enum cmd cmdid); 2574cf49a43SJulian Elischer 2584cf49a43SJulian Elischer /************************************************************************* 2594cf49a43SJulian Elischer * Some basic utilities from the Linux version with author's permission.* 2604cf49a43SJulian Elischer * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 2614cf49a43SJulian Elischer ************************************************************************/ 2624cf49a43SJulian Elischer 2634cf49a43SJulian Elischer /* 2644cf49a43SJulian Elischer * Generate a new session id 2654adb13fdSJulian Elischer * XXX find out the FreeBSD locking scheme. 2664cf49a43SJulian Elischer */ 2674cf49a43SJulian Elischer static u_int16_t 2684cf49a43SJulian Elischer get_new_sid(node_p node) 2694cf49a43SJulian Elischer { 2704cf49a43SJulian Elischer static int pppoe_sid = 10; 2714cf49a43SJulian Elischer sessp sp; 2724cf49a43SJulian Elischer hook_p hook; 2734cf49a43SJulian Elischer u_int16_t val; 27430400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 2754cf49a43SJulian Elischer 2761e2510f8SJulian Elischer AAA 2774cf49a43SJulian Elischer restart: 2784cf49a43SJulian Elischer val = pppoe_sid++; 2794cf49a43SJulian Elischer /* 2804cf49a43SJulian Elischer * Spec says 0xFFFF is reserved. 2814cf49a43SJulian Elischer * Also don't use 0x0000 2824cf49a43SJulian Elischer */ 2834cf49a43SJulian Elischer if (val == 0xffff) { 2844cf49a43SJulian Elischer pppoe_sid = 20; 2854cf49a43SJulian Elischer goto restart; 2864cf49a43SJulian Elischer } 2874cf49a43SJulian Elischer 2884cf49a43SJulian Elischer /* Check it isn't already in use */ 28930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 2904cf49a43SJulian Elischer /* don't check special hooks */ 29130400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 29230400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 2934cf49a43SJulian Elischer continue; 29430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 2954cf49a43SJulian Elischer if (sp->Session_ID == val) 2964cf49a43SJulian Elischer goto restart; 2974cf49a43SJulian Elischer } 2984cf49a43SJulian Elischer 2994cf49a43SJulian Elischer return val; 3004cf49a43SJulian Elischer } 3014cf49a43SJulian Elischer 3024cf49a43SJulian Elischer 3034cf49a43SJulian Elischer /* 3044cf49a43SJulian Elischer * Return the location where the next tag can be put 3054cf49a43SJulian Elischer */ 3064cf49a43SJulian Elischer static __inline struct pppoe_tag* 3074cf49a43SJulian Elischer next_tag(struct pppoe_hdr* ph) 3084cf49a43SJulian Elischer { 3094cf49a43SJulian Elischer return (struct pppoe_tag*)(((char*)&ph->tag[0]) + ntohs(ph->length)); 3104cf49a43SJulian Elischer } 3114cf49a43SJulian Elischer 3124cf49a43SJulian Elischer /* 3134cf49a43SJulian Elischer * Look for a tag of a specific type 3144cf49a43SJulian Elischer * Don't trust any length the other end says. 3154cf49a43SJulian Elischer * but assume we already sanity checked ph->length. 3164cf49a43SJulian Elischer */ 3174cf49a43SJulian Elischer static struct pppoe_tag* 3184cf49a43SJulian Elischer get_tag(struct pppoe_hdr* ph, u_int16_t idx) 3194cf49a43SJulian Elischer { 3204cf49a43SJulian Elischer char *end = (char *)next_tag(ph); 3214cf49a43SJulian Elischer char *ptn; 3224cf49a43SJulian Elischer struct pppoe_tag *pt = &ph->tag[0]; 3234cf49a43SJulian Elischer /* 3244cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 3254cf49a43SJulian Elischer */ 3261e2510f8SJulian Elischer AAA 3274cf49a43SJulian Elischer while((char*)(pt + 1) <= end) { 3284cf49a43SJulian Elischer /* 3294cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 3304cf49a43SJulian Elischer */ 3314cf49a43SJulian Elischer ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 3324cf49a43SJulian Elischer if(ptn > end) 3334cf49a43SJulian Elischer return NULL; 3344cf49a43SJulian Elischer 3354cf49a43SJulian Elischer if(pt->tag_type == idx) 3364cf49a43SJulian Elischer return pt; 3374cf49a43SJulian Elischer 3384cf49a43SJulian Elischer pt = (struct pppoe_tag*)ptn; 3394cf49a43SJulian Elischer } 3404cf49a43SJulian Elischer return NULL; 3414cf49a43SJulian Elischer } 3424cf49a43SJulian Elischer 3434cf49a43SJulian Elischer /************************************************************************** 3444cf49a43SJulian Elischer * inlines to initialise or add tags to a session's tag list, 3454cf49a43SJulian Elischer **************************************************************************/ 3464cf49a43SJulian Elischer /* 3474cf49a43SJulian Elischer * Initialise the session's tag list 3484cf49a43SJulian Elischer */ 3494cf49a43SJulian Elischer static void 3504cf49a43SJulian Elischer init_tags(sessp sp) 3514cf49a43SJulian Elischer { 3521e2510f8SJulian Elischer AAA 3534cf49a43SJulian Elischer if(sp->neg == NULL) { 3544cf49a43SJulian Elischer printf("pppoe: asked to init NULL neg pointer\n"); 3554cf49a43SJulian Elischer return; 3564cf49a43SJulian Elischer } 3574cf49a43SJulian Elischer sp->neg->numtags = 0; 3584cf49a43SJulian Elischer } 3594cf49a43SJulian Elischer 3604cf49a43SJulian Elischer static void 3614cf49a43SJulian Elischer insert_tag(sessp sp, struct pppoe_tag *tp) 3624cf49a43SJulian Elischer { 3634cf49a43SJulian Elischer int i; 3644cf49a43SJulian Elischer negp neg; 3654cf49a43SJulian Elischer 3661e2510f8SJulian Elischer AAA 3674cf49a43SJulian Elischer if((neg = sp->neg) == NULL) { 3684cf49a43SJulian Elischer printf("pppoe: asked to use NULL neg pointer\n"); 3694cf49a43SJulian Elischer return; 3704cf49a43SJulian Elischer } 3714cf49a43SJulian Elischer if ((i = neg->numtags++) < NUMTAGS) { 3724cf49a43SJulian Elischer neg->tags[i] = tp; 3734cf49a43SJulian Elischer } else { 3744cf49a43SJulian Elischer printf("pppoe: asked to add too many tags to packet\n"); 37512f035e0SJulian Elischer neg->numtags--; 3764cf49a43SJulian Elischer } 3774cf49a43SJulian Elischer } 3784cf49a43SJulian Elischer 3794cf49a43SJulian Elischer /* 3804cf49a43SJulian Elischer * Make up a packet, using the tags filled out for the session. 3814cf49a43SJulian Elischer * 3824cf49a43SJulian Elischer * Assume that the actual pppoe header and ethernet header 3834cf49a43SJulian Elischer * are filled out externally to this routine. 3844cf49a43SJulian Elischer * Also assume that neg->wh points to the correct 3854cf49a43SJulian Elischer * location at the front of the buffer space. 3864cf49a43SJulian Elischer */ 3874cf49a43SJulian Elischer static void 3884cf49a43SJulian Elischer make_packet(sessp sp) { 3894cf49a43SJulian Elischer struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 3904cf49a43SJulian Elischer struct pppoe_tag **tag; 3914cf49a43SJulian Elischer char *dp; 3924cf49a43SJulian Elischer int count; 3934cf49a43SJulian Elischer int tlen; 3944cf49a43SJulian Elischer u_int16_t length = 0; 3954cf49a43SJulian Elischer 3961e2510f8SJulian Elischer AAA 3971e2510f8SJulian Elischer if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 3984cf49a43SJulian Elischer printf("pppoe: make_packet called from wrong state\n"); 3994cf49a43SJulian Elischer } 4004cf49a43SJulian Elischer dp = (char *)wh->ph.tag; 4014cf49a43SJulian Elischer for (count = 0, tag = sp->neg->tags; 4024cf49a43SJulian Elischer ((count < sp->neg->numtags) && (count < NUMTAGS)); 4034cf49a43SJulian Elischer tag++, count++) { 4044cf49a43SJulian Elischer tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 4054cf49a43SJulian Elischer if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 4064cf49a43SJulian Elischer printf("pppoe: tags too long\n"); 4074cf49a43SJulian Elischer sp->neg->numtags = count; 4084cf49a43SJulian Elischer break; /* XXX chop off what's too long */ 4094cf49a43SJulian Elischer } 4104cf49a43SJulian Elischer bcopy((char *)*tag, (char *)dp, tlen); 4114cf49a43SJulian Elischer length += tlen; 4124cf49a43SJulian Elischer dp += tlen; 4134cf49a43SJulian Elischer } 4144cf49a43SJulian Elischer wh->ph.length = htons(length); 4154cf49a43SJulian Elischer sp->neg->m->m_len = length + sizeof(*wh); 4164cf49a43SJulian Elischer sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 4174cf49a43SJulian Elischer } 4184cf49a43SJulian Elischer 4194cf49a43SJulian Elischer /************************************************************************** 4204cf49a43SJulian Elischer * Routine to match a service offered * 4214cf49a43SJulian Elischer **************************************************************************/ 4224cf49a43SJulian Elischer /* 4234cf49a43SJulian Elischer * Find a hook that has a service string that matches that 4244cf49a43SJulian Elischer * we are seeking. for now use a simple string. 4254cf49a43SJulian Elischer * In the future we may need something like regexp(). 4264cf49a43SJulian Elischer * for testing allow a null string to match 1st found and a null service 4274cf49a43SJulian Elischer * to match all requests. Also make '*' do the same. 4284cf49a43SJulian Elischer */ 4294cf49a43SJulian Elischer static hook_p 4304cf49a43SJulian Elischer pppoe_match_svc(node_p node, char *svc_name, int svc_len) 4314cf49a43SJulian Elischer { 4324cf49a43SJulian Elischer sessp sp = NULL; 4334cf49a43SJulian Elischer negp neg = NULL; 43430400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 4354cf49a43SJulian Elischer hook_p hook; 4364cf49a43SJulian Elischer 4371e2510f8SJulian Elischer AAA 43830400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 4394cf49a43SJulian Elischer 4404cf49a43SJulian Elischer /* skip any hook that is debug or ethernet */ 44130400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 44230400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 4434cf49a43SJulian Elischer continue; 44430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 4454cf49a43SJulian Elischer 4464cf49a43SJulian Elischer /* Skip any sessions which are not in LISTEN mode. */ 4474cf49a43SJulian Elischer if ( sp->state != PPPOE_LISTENING) 4484cf49a43SJulian Elischer continue; 4494cf49a43SJulian Elischer 4504cf49a43SJulian Elischer neg = sp->neg; 4514cf49a43SJulian Elischer /* XXX check validity of this */ 4524cf49a43SJulian Elischer /* special case, NULL request. match 1st found. */ 4534cf49a43SJulian Elischer if (svc_len == 0) 4544cf49a43SJulian Elischer break; 4554cf49a43SJulian Elischer 4564cf49a43SJulian Elischer /* XXX check validity of this */ 4574cf49a43SJulian Elischer /* Special case for a blank or "*" service name (wildcard) */ 4584cf49a43SJulian Elischer if ((neg->service_len == 0) 4594cf49a43SJulian Elischer || ((neg->service_len == 1) 4604cf49a43SJulian Elischer && (neg->service.data[0] == '*'))) { 4614cf49a43SJulian Elischer break; 4624cf49a43SJulian Elischer } 4634cf49a43SJulian Elischer 4644cf49a43SJulian Elischer /* If the lengths don't match, that aint it. */ 4654cf49a43SJulian Elischer if (neg->service_len != svc_len) 4664cf49a43SJulian Elischer continue; 4674cf49a43SJulian Elischer 4684cf49a43SJulian Elischer /* An exact match? */ 4694cf49a43SJulian Elischer if (strncmp(svc_name, neg->service.data, svc_len) == 0) 4704cf49a43SJulian Elischer break; 4714cf49a43SJulian Elischer } 4724cf49a43SJulian Elischer return (hook); 4734cf49a43SJulian Elischer } 4744cf49a43SJulian Elischer /************************************************************************** 4754cf49a43SJulian Elischer * Routine to find a particular session that matches an incoming packet * 4764cf49a43SJulian Elischer **************************************************************************/ 4774cf49a43SJulian Elischer static hook_p 4784cf49a43SJulian Elischer pppoe_findsession(node_p node, struct pppoe_full_hdr *wh) 4794cf49a43SJulian Elischer { 4804cf49a43SJulian Elischer sessp sp = NULL; 4814cf49a43SJulian Elischer hook_p hook = NULL; 48230400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 483b86d0a9eSJulian Elischer u_int16_t session = ntohs(wh->ph.sid); 4844cf49a43SJulian Elischer 4854cf49a43SJulian Elischer /* 4864cf49a43SJulian Elischer * find matching peer/session combination. 4874cf49a43SJulian Elischer */ 4881e2510f8SJulian Elischer AAA 48930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 4904cf49a43SJulian Elischer /* don't check special hooks */ 49130400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 49230400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 4934cf49a43SJulian Elischer continue; 4944cf49a43SJulian Elischer } 49530400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 4964cf49a43SJulian Elischer if ( ( (sp->state == PPPOE_CONNECTED) 4974cf49a43SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED) ) 4984cf49a43SJulian Elischer && (sp->Session_ID == session) 4994cf49a43SJulian Elischer && (bcmp(sp->pkt_hdr.eh.ether_dhost, 5004cf49a43SJulian Elischer wh->eh.ether_shost, 5014cf49a43SJulian Elischer ETHER_ADDR_LEN)) == 0) { 5024cf49a43SJulian Elischer break; 5034cf49a43SJulian Elischer } 5044cf49a43SJulian Elischer } 5054cf49a43SJulian Elischer return (hook); 5064cf49a43SJulian Elischer } 5074cf49a43SJulian Elischer 5084cf49a43SJulian Elischer static hook_p 5094cf49a43SJulian Elischer pppoe_finduniq(node_p node, struct pppoe_tag *tag) 5104cf49a43SJulian Elischer { 5114cf49a43SJulian Elischer hook_p hook = NULL; 51230400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 5134cf49a43SJulian Elischer union uniq uniq; 5144cf49a43SJulian Elischer 5151e2510f8SJulian Elischer AAA 5164cf49a43SJulian Elischer bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 5174cf49a43SJulian Elischer /* cycle through all known hooks */ 51830400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5194cf49a43SJulian Elischer /* don't check special hooks */ 52030400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 52130400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 5224cf49a43SJulian Elischer continue; 52330400f03SJulian Elischer if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 5244cf49a43SJulian Elischer break; 5254cf49a43SJulian Elischer } 5264cf49a43SJulian Elischer return (hook); 5274cf49a43SJulian Elischer } 5284cf49a43SJulian Elischer 5294cf49a43SJulian Elischer /************************************************************************** 5304cf49a43SJulian Elischer * start of Netgraph entrypoints * 5314cf49a43SJulian Elischer **************************************************************************/ 5324cf49a43SJulian Elischer 5334cf49a43SJulian Elischer /* 5344cf49a43SJulian Elischer * Allocate the private data structure and the generic node 5354cf49a43SJulian Elischer * and link them together. 5364cf49a43SJulian Elischer * 5374cf49a43SJulian Elischer * ng_make_node_common() returns with a generic node struct 5384cf49a43SJulian Elischer * with a single reference for us.. we transfer it to the 5394cf49a43SJulian Elischer * private structure.. when we free the private struct we must 5404cf49a43SJulian Elischer * unref the node so it gets freed too. 5414cf49a43SJulian Elischer */ 5424cf49a43SJulian Elischer static int 543069154d5SJulian Elischer ng_pppoe_constructor(node_p node) 5444cf49a43SJulian Elischer { 5454cf49a43SJulian Elischer priv_p privdata; 5464cf49a43SJulian Elischer 5471e2510f8SJulian Elischer AAA 5484cf49a43SJulian Elischer /* Initialize private descriptor */ 5499c8c302fSJulian Elischer MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH_PPPOE, 55099cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 5514cf49a43SJulian Elischer if (privdata == NULL) 5524cf49a43SJulian Elischer return (ENOMEM); 5534cf49a43SJulian Elischer 5544cf49a43SJulian Elischer /* Link structs together; this counts as our one reference to *nodep */ 55530400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, privdata); 556069154d5SJulian Elischer privdata->node = node; 5574cf49a43SJulian Elischer return (0); 5584cf49a43SJulian Elischer } 5594cf49a43SJulian Elischer 5604cf49a43SJulian Elischer /* 5614cf49a43SJulian Elischer * Give our ok for a hook to be added... 5624cf49a43SJulian Elischer * point the hook's private info to the hook structure. 5634cf49a43SJulian Elischer * 5644cf49a43SJulian Elischer * The following hook names are special: 5654cf49a43SJulian Elischer * Ethernet: the hook that should be connected to a NIC. 5664cf49a43SJulian Elischer * debug: copies of data sent out here (when I write the code). 567859a4d16SJulian Elischer * All other hook names need only be unique. (the framework checks this). 5684cf49a43SJulian Elischer */ 5694cf49a43SJulian Elischer static int 5708876b55dSJulian Elischer ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 5714cf49a43SJulian Elischer { 57230400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 5734cf49a43SJulian Elischer sessp sp; 5744cf49a43SJulian Elischer 5751e2510f8SJulian Elischer AAA 5764cf49a43SJulian Elischer if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 5774cf49a43SJulian Elischer privp->ethernet_hook = hook; 57830400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 5794cf49a43SJulian Elischer } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 5804cf49a43SJulian Elischer privp->debug_hook = hook; 58130400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 5824cf49a43SJulian Elischer } else { 5834cf49a43SJulian Elischer /* 5844cf49a43SJulian Elischer * Any other unique name is OK. 5854cf49a43SJulian Elischer * The infrastructure has already checked that it's unique, 5864cf49a43SJulian Elischer * so just allocate it and hook it in. 5874cf49a43SJulian Elischer */ 5889c8c302fSJulian Elischer MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 5894cf49a43SJulian Elischer if (sp == NULL) { 5904cf49a43SJulian Elischer return (ENOMEM); 5914cf49a43SJulian Elischer } 5924cf49a43SJulian Elischer 59330400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, sp); 5944cf49a43SJulian Elischer sp->hook = hook; 5954cf49a43SJulian Elischer } 5964cf49a43SJulian Elischer return(0); 5974cf49a43SJulian Elischer } 5984cf49a43SJulian Elischer 5994cf49a43SJulian Elischer /* 6004cf49a43SJulian Elischer * Get a netgraph control message. 6014cf49a43SJulian Elischer * Check it is one we understand. If needed, send a response. 6024cf49a43SJulian Elischer * We sometimes save the address for an async action later. 6034cf49a43SJulian Elischer * Always free the message. 6044cf49a43SJulian Elischer */ 6054cf49a43SJulian Elischer static int 606069154d5SJulian Elischer ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 6074cf49a43SJulian Elischer { 60830400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 6098876b55dSJulian Elischer struct ngpppoe_init_data *ourmsg = NULL; 6104cf49a43SJulian Elischer struct ng_mesg *resp = NULL; 6114cf49a43SJulian Elischer int error = 0; 6124cf49a43SJulian Elischer hook_p hook = NULL; 6134cf49a43SJulian Elischer sessp sp = NULL; 6144cf49a43SJulian Elischer negp neg = NULL; 615069154d5SJulian Elischer struct ng_mesg *msg; 6164cf49a43SJulian Elischer 6171e2510f8SJulian Elischer AAA 618069154d5SJulian Elischer NGI_GET_MSG(item, msg); 6194cf49a43SJulian Elischer /* Deal with message according to cookie and command */ 6204cf49a43SJulian Elischer switch (msg->header.typecookie) { 6214cf49a43SJulian Elischer case NGM_PPPOE_COOKIE: 6224cf49a43SJulian Elischer switch (msg->header.cmd) { 6234cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 6244cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 6254cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 626859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 62727121ab1SBrian Somers ourmsg = (struct ngpppoe_init_data *)msg->data; 62827121ab1SBrian Somers if (msg->header.arglen < sizeof(*ourmsg)) { 62927121ab1SBrian Somers printf("pppoe: init data too small\n"); 6304cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6314cf49a43SJulian Elischer } 63276a70671SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) > 63376a70671SBrian Somers PPPOE_SERVICE_NAME_SIZE) { 63476a70671SBrian Somers printf("pppoe_rcvmsg: service name too big"); 6354cf49a43SJulian Elischer LEAVE(EMSGSIZE); 6364cf49a43SJulian Elischer } 63727121ab1SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) < 63827121ab1SBrian Somers ourmsg->data_len) { 63927121ab1SBrian Somers printf("pppoe: init data has bad length," 64027121ab1SBrian Somers " %d should be %d\n", ourmsg->data_len, 64127121ab1SBrian Somers msg->header.arglen - sizeof (*ourmsg)); 64276a70671SBrian Somers LEAVE(EMSGSIZE); 64376a70671SBrian Somers } 64476a70671SBrian Somers 6454cf49a43SJulian Elischer /* make sure strcmp will terminate safely */ 6464cf49a43SJulian Elischer ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 6474cf49a43SJulian Elischer 6484cf49a43SJulian Elischer /* cycle through all known hooks */ 64930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 65030400f03SJulian Elischer if (NG_HOOK_NAME(hook) 65130400f03SJulian Elischer && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 6524cf49a43SJulian Elischer break; 6534cf49a43SJulian Elischer } 6544cf49a43SJulian Elischer if (hook == NULL) { 6554cf49a43SJulian Elischer LEAVE(ENOENT); 6564cf49a43SJulian Elischer } 65730400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 65830400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 6594cf49a43SJulian Elischer LEAVE(EINVAL); 6604cf49a43SJulian Elischer } 66130400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 662859a4d16SJulian Elischer 663859a4d16SJulian Elischer /* 664859a4d16SJulian Elischer * PPPOE_SERVICE advertisments are set up 665859a4d16SJulian Elischer * on sessions that are in PRIMED state. 666859a4d16SJulian Elischer */ 667859a4d16SJulian Elischer if (msg->header.cmd == NGM_PPPOE_SERVICE) { 668859a4d16SJulian Elischer break; 669859a4d16SJulian Elischer } 6704cf49a43SJulian Elischer if (sp->state |= PPPOE_SNONE) { 6714cf49a43SJulian Elischer printf("pppoe: Session already active\n"); 6724cf49a43SJulian Elischer LEAVE(EISCONN); 6734cf49a43SJulian Elischer } 6741e2510f8SJulian Elischer 6754cf49a43SJulian Elischer /* 6764cf49a43SJulian Elischer * set up prototype header 6774cf49a43SJulian Elischer */ 6789c8c302fSJulian Elischer MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH_PPPOE, 67999cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 6804cf49a43SJulian Elischer 6814cf49a43SJulian Elischer if (neg == NULL) { 6824cf49a43SJulian Elischer printf("pppoe: Session out of memory\n"); 6834cf49a43SJulian Elischer LEAVE(ENOMEM); 6844cf49a43SJulian Elischer } 6854cf49a43SJulian Elischer MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 6864cf49a43SJulian Elischer if(neg->m == NULL) { 6871e2510f8SJulian Elischer printf("pppoe: Session out of mbufs\n"); 6889c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 6894cf49a43SJulian Elischer LEAVE(ENOBUFS); 6904cf49a43SJulian Elischer } 6914cf49a43SJulian Elischer neg->m->m_pkthdr.rcvif = NULL; 6924cf49a43SJulian Elischer MCLGET(neg->m, M_DONTWAIT); 6934cf49a43SJulian Elischer if ((neg->m->m_flags & M_EXT) == 0) { 6941e2510f8SJulian Elischer printf("pppoe: Session out of mcls\n"); 6954cf49a43SJulian Elischer m_freem(neg->m); 6969c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 6974cf49a43SJulian Elischer LEAVE(ENOBUFS); 6984cf49a43SJulian Elischer } 6994cf49a43SJulian Elischer sp->neg = neg; 7001e2510f8SJulian Elischer callout_handle_init( &neg->timeout_handle); 7014cf49a43SJulian Elischer neg->m->m_len = sizeof(struct pppoe_full_hdr); 7024cf49a43SJulian Elischer neg->pkt = mtod(neg->m, union packet*); 7034cf49a43SJulian Elischer neg->pkt->pkt_header.eh = eh_prototype; 7044cf49a43SJulian Elischer neg->pkt->pkt_header.ph.ver = 0x1; 7054cf49a43SJulian Elischer neg->pkt->pkt_header.ph.type = 0x1; 7064cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 0x0000; 7074cf49a43SJulian Elischer neg->timeout = 0; 7084cf49a43SJulian Elischer 709069154d5SJulian Elischer sp->creator = NGI_RETADDR(item); 7104cf49a43SJulian Elischer } 7114cf49a43SJulian Elischer switch (msg->header.cmd) { 7124cf49a43SJulian Elischer case NGM_PPPOE_GET_STATUS: 7134cf49a43SJulian Elischer { 7148876b55dSJulian Elischer struct ngpppoestat *stats; 7154cf49a43SJulian Elischer 7164cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 7174cf49a43SJulian Elischer if (!resp) { 7184cf49a43SJulian Elischer LEAVE(ENOMEM); 7194cf49a43SJulian Elischer } 7208876b55dSJulian Elischer stats = (struct ngpppoestat *) resp->data; 7214cf49a43SJulian Elischer stats->packets_in = privp->packets_in; 7224cf49a43SJulian Elischer stats->packets_out = privp->packets_out; 7234cf49a43SJulian Elischer break; 7244cf49a43SJulian Elischer } 7254cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 7264cf49a43SJulian Elischer /* 7274cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7284cf49a43SJulian Elischer * Send a PADI request, and start the timeout logic. 7294cf49a43SJulian Elischer * Store the originator of this message so we can send 7304cf49a43SJulian Elischer * a success of fail message to them later. 7314cf49a43SJulian Elischer * Move the session to SINIT 7324cf49a43SJulian Elischer * Set up the session to the correct state and 7334cf49a43SJulian Elischer * start it. 7344cf49a43SJulian Elischer */ 7354cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 73627121ab1SBrian Somers neg->service.hdr.tag_len = 73727121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 73827121ab1SBrian Somers if (ourmsg->data_len) 73927121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 74027121ab1SBrian Somers ourmsg->data_len); 74127121ab1SBrian Somers neg->service_len = ourmsg->data_len; 7424cf49a43SJulian Elischer pppoe_start(sp); 7434cf49a43SJulian Elischer break; 7444cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 7454cf49a43SJulian Elischer /* 7464cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7474cf49a43SJulian Elischer * Install the service matching string. 7484cf49a43SJulian Elischer * Store the originator of this message so we can send 7494cf49a43SJulian Elischer * a success of fail message to them later. 7504cf49a43SJulian Elischer * Move the hook to 'LISTENING' 7514cf49a43SJulian Elischer */ 7524cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 75327121ab1SBrian Somers neg->service.hdr.tag_len = 75427121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 7551e2510f8SJulian Elischer 75627121ab1SBrian Somers if (ourmsg->data_len) 75727121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 75827121ab1SBrian Somers ourmsg->data_len); 75927121ab1SBrian Somers neg->service_len = ourmsg->data_len; 7604cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADT_CODE; 7614cf49a43SJulian Elischer /* 7624cf49a43SJulian Elischer * wait for PADI packet coming from ethernet 7634cf49a43SJulian Elischer */ 7644cf49a43SJulian Elischer sp->state = PPPOE_LISTENING; 7654cf49a43SJulian Elischer break; 7664cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 7674cf49a43SJulian Elischer /* 7684cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 7694cf49a43SJulian Elischer * Store the originator of this message so we can send 7704cf49a43SJulian Elischer * a success of fail message to them later. 7714cf49a43SJulian Elischer * Store the AC-Name given and go to PRIMED. 7724cf49a43SJulian Elischer */ 7734cf49a43SJulian Elischer neg->ac_name.hdr.tag_type = PTT_AC_NAME; 77427121ab1SBrian Somers neg->ac_name.hdr.tag_len = 77527121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 77627121ab1SBrian Somers if (ourmsg->data_len) 77727121ab1SBrian Somers bcopy(ourmsg->data, neg->ac_name.data, 77827121ab1SBrian Somers ourmsg->data_len); 77927121ab1SBrian Somers neg->ac_name_len = ourmsg->data_len; 7804cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 7814cf49a43SJulian Elischer /* 7824cf49a43SJulian Elischer * Wait for PADI packet coming from hook 7834cf49a43SJulian Elischer */ 7844cf49a43SJulian Elischer sp->state = PPPOE_PRIMED; 7854cf49a43SJulian Elischer break; 786859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 787859a4d16SJulian Elischer /* 788859a4d16SJulian Elischer * Check the session is primed. 789859a4d16SJulian Elischer * for now just allow ONE service to be advertised. 790859a4d16SJulian Elischer * If you do it twice you just overwrite. 791859a4d16SJulian Elischer */ 7925078fb0bSJulian Elischer if (sp->state != PPPOE_PRIMED) { 793859a4d16SJulian Elischer printf("pppoe: Session not primed\n"); 794859a4d16SJulian Elischer LEAVE(EISCONN); 795859a4d16SJulian Elischer } 7960069b9cbSJulian Elischer neg = sp->neg; 797859a4d16SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 798859a4d16SJulian Elischer neg->service.hdr.tag_len = 799859a4d16SJulian Elischer htons((u_int16_t)ourmsg->data_len); 800859a4d16SJulian Elischer 801859a4d16SJulian Elischer if (ourmsg->data_len) 802859a4d16SJulian Elischer bcopy(ourmsg->data, neg->service.data, 803859a4d16SJulian Elischer ourmsg->data_len); 804859a4d16SJulian Elischer neg->service_len = ourmsg->data_len; 805859a4d16SJulian Elischer break; 8064cf49a43SJulian Elischer default: 8074cf49a43SJulian Elischer LEAVE(EINVAL); 8084cf49a43SJulian Elischer } 8094cf49a43SJulian Elischer break; 8104cf49a43SJulian Elischer default: 8114cf49a43SJulian Elischer LEAVE(EINVAL); 8124cf49a43SJulian Elischer } 8134cf49a43SJulian Elischer 8144cf49a43SJulian Elischer /* Take care of synchronous response, if any */ 8154cf49a43SJulian Elischer quit: 816069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 817069154d5SJulian Elischer /* Free the message and return */ 818069154d5SJulian Elischer NG_FREE_MSG(msg); 8194cf49a43SJulian Elischer return(error); 8204cf49a43SJulian Elischer } 8214cf49a43SJulian Elischer 8221e2510f8SJulian Elischer /* 8231e2510f8SJulian Elischer * Start a client into the first state. A separate function because 8241e2510f8SJulian Elischer * it can be needed if the negotiation times out. 8251e2510f8SJulian Elischer */ 8264cf49a43SJulian Elischer static void 8274cf49a43SJulian Elischer pppoe_start(sessp sp) 8284cf49a43SJulian Elischer { 8294cf49a43SJulian Elischer struct { 8304cf49a43SJulian Elischer struct pppoe_tag hdr; 8314cf49a43SJulian Elischer union uniq data; 8324cf49a43SJulian Elischer } uniqtag; 8334cf49a43SJulian Elischer 8344cf49a43SJulian Elischer /* 8354cf49a43SJulian Elischer * kick the state machine into starting up 8364cf49a43SJulian Elischer */ 8371e2510f8SJulian Elischer AAA 8384cf49a43SJulian Elischer sp->state = PPPOE_SINIT; 8391e2510f8SJulian Elischer /* reset the packet header to broadcast */ 8401e2510f8SJulian Elischer sp->neg->pkt->pkt_header.eh = eh_prototype; 8411e2510f8SJulian Elischer sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 8424cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 8434cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 8444cf49a43SJulian Elischer uniqtag.data.pointer = sp; 8454cf49a43SJulian Elischer init_tags(sp); 8461f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 8477ccbb17bSJulian Elischer insert_tag(sp, &sp->neg->service.hdr); 8484cf49a43SJulian Elischer make_packet(sp); 8494cf49a43SJulian Elischer sendpacket(sp); 8504cf49a43SJulian Elischer } 8514cf49a43SJulian Elischer 8524cf49a43SJulian Elischer /* 8534cf49a43SJulian Elischer * Receive data, and do something with it. 8544cf49a43SJulian Elischer * The caller will never free m or meta, so 8554cf49a43SJulian Elischer * if we use up this data or abort we must free BOTH of these. 8564cf49a43SJulian Elischer */ 8574cf49a43SJulian Elischer static int 858069154d5SJulian Elischer ng_pppoe_rcvdata(hook_p hook, item_p item) 8594cf49a43SJulian Elischer { 86030400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 86130400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 86230400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 8634cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 8644cf49a43SJulian Elischer struct pppoe_hdr *ph; 8654cf49a43SJulian Elischer int error = 0; 8664cf49a43SJulian Elischer u_int16_t session; 8674cf49a43SJulian Elischer u_int16_t length; 8684cf49a43SJulian Elischer u_int8_t code; 8691f89d938SJulian Elischer struct pppoe_tag *utag = NULL, *tag = NULL; 8704cf49a43SJulian Elischer hook_p sendhook; 8714cf49a43SJulian Elischer struct { 8724cf49a43SJulian Elischer struct pppoe_tag hdr; 8734cf49a43SJulian Elischer union uniq data; 8744cf49a43SJulian Elischer } uniqtag; 8754cf49a43SJulian Elischer negp neg = NULL; 876069154d5SJulian Elischer struct mbuf *m; 8774cf49a43SJulian Elischer 8781e2510f8SJulian Elischer AAA 879069154d5SJulian Elischer NGI_GET_M(item, m); 88030400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 8814cf49a43SJulian Elischer /* 8824cf49a43SJulian Elischer * Data from the debug hook gets sent without modification 8834cf49a43SJulian Elischer * straight to the ethernet. 8844cf49a43SJulian Elischer */ 88530400f03SJulian Elischer NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 8864cf49a43SJulian Elischer privp->packets_out++; 88730400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 8884cf49a43SJulian Elischer /* 8894cf49a43SJulian Elischer * Incoming data. 8904cf49a43SJulian Elischer * Dig out various fields from the packet. 8914cf49a43SJulian Elischer * use them to decide where to send it. 8924cf49a43SJulian Elischer */ 8934cf49a43SJulian Elischer 8944cf49a43SJulian Elischer privp->packets_in++; 8950c65c135SJulian Elischer if( m->m_len < sizeof(*wh)) { 8960c65c135SJulian Elischer m = m_pullup(m, sizeof(*wh)); /* Checks length */ 8974cf49a43SJulian Elischer if (m == NULL) { 898b86d0a9eSJulian Elischer printf("couldn't m_pullup\n"); 8994cf49a43SJulian Elischer LEAVE(ENOBUFS); 9004cf49a43SJulian Elischer } 9010c65c135SJulian Elischer } 9024cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 9034cf49a43SJulian Elischer length = ntohs(wh->ph.length); 9040c65c135SJulian Elischer switch(wh->eh.ether_type) { 9054cf49a43SJulian Elischer case ETHERTYPE_PPPOE_DISC: 9064cf49a43SJulian Elischer /* 907bdaf2e81SJulian Elischer * We need to try to make sure that the tag area 908bdaf2e81SJulian Elischer * is contiguous, or we could wander off the end 9094cf49a43SJulian Elischer * of a buffer and make a mess. 9104cf49a43SJulian Elischer * (Linux wouldn't have this problem). 9114cf49a43SJulian Elischer */ 9120c65c135SJulian Elischer if (m->m_pkthdr.len <= MHLEN) { 9130c65c135SJulian Elischer if( m->m_len < m->m_pkthdr.len) { 9140c65c135SJulian Elischer m = m_pullup(m, m->m_pkthdr.len); 9150c65c135SJulian Elischer if (m == NULL) { 9160c65c135SJulian Elischer printf("couldn't m_pullup\n"); 9170c65c135SJulian Elischer LEAVE(ENOBUFS); 9180c65c135SJulian Elischer } 9190c65c135SJulian Elischer } 9200c65c135SJulian Elischer } 9214cf49a43SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 9224cf49a43SJulian Elischer /* 9234cf49a43SJulian Elischer * It's not all in one piece. 9244cf49a43SJulian Elischer * We need to do extra work. 925069154d5SJulian Elischer * Put it into a cluster. 9264cf49a43SJulian Elischer */ 927069154d5SJulian Elischer struct mbuf *n; 928069154d5SJulian Elischer n = m_dup(m, M_DONTWAIT); 929069154d5SJulian Elischer m_freem(m); 930069154d5SJulian Elischer m = n; 931069154d5SJulian Elischer if (m) { 932069154d5SJulian Elischer /* just check we got a cluster */ 933069154d5SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 934069154d5SJulian Elischer m_freem(m); 935069154d5SJulian Elischer m = NULL; 936069154d5SJulian Elischer } 937069154d5SJulian Elischer } 938069154d5SJulian Elischer if (m == NULL) { 9394cf49a43SJulian Elischer printf("packet fragmented\n"); 940b86d0a9eSJulian Elischer LEAVE(EMSGSIZE); 9414cf49a43SJulian Elischer } 942069154d5SJulian Elischer } 943069154d5SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 944069154d5SJulian Elischer length = ntohs(wh->ph.length); 945069154d5SJulian Elischer ph = &wh->ph; 946069154d5SJulian Elischer session = ntohs(wh->ph.sid); 947069154d5SJulian Elischer code = wh->ph.code; 9484cf49a43SJulian Elischer 9494cf49a43SJulian Elischer switch(code) { 9504cf49a43SJulian Elischer case PADI_CODE: 9514cf49a43SJulian Elischer /* 9524cf49a43SJulian Elischer * We are a server: 9534cf49a43SJulian Elischer * Look for a hook with the required service 9544cf49a43SJulian Elischer * and send the ENTIRE packet up there. 9554cf49a43SJulian Elischer * It should come back to a new hook in 9564cf49a43SJulian Elischer * PRIMED state. Look there for further 9574cf49a43SJulian Elischer * processing. 9584cf49a43SJulian Elischer */ 9594cf49a43SJulian Elischer tag = get_tag(ph, PTT_SRV_NAME); 9604cf49a43SJulian Elischer if (tag == NULL) { 961b86d0a9eSJulian Elischer printf("no service tag\n"); 9624cf49a43SJulian Elischer LEAVE(ENETUNREACH); 9634cf49a43SJulian Elischer } 96430400f03SJulian Elischer sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 9654cf49a43SJulian Elischer tag->tag_data, ntohs(tag->tag_len)); 9664cf49a43SJulian Elischer if (sendhook) { 967069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, 968069154d5SJulian Elischer sendhook, m); 9694cf49a43SJulian Elischer } else { 970b86d0a9eSJulian Elischer printf("no such service\n"); 9714cf49a43SJulian Elischer LEAVE(ENETUNREACH); 9724cf49a43SJulian Elischer } 9734cf49a43SJulian Elischer break; 9744cf49a43SJulian Elischer case PADO_CODE: 9754cf49a43SJulian Elischer /* 9764cf49a43SJulian Elischer * We are a client: 9774cf49a43SJulian Elischer * Use the host_uniq tag to find the 9784cf49a43SJulian Elischer * hook this is in response to. 979b86d0a9eSJulian Elischer * Received #2, now send #3 9804cf49a43SJulian Elischer * For now simply accept the first we receive. 9814cf49a43SJulian Elischer */ 9821f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 9831f89d938SJulian Elischer if ((utag == NULL) 9841f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 985b86d0a9eSJulian Elischer printf("no host unique field\n"); 9864cf49a43SJulian Elischer LEAVE(ENETUNREACH); 9874cf49a43SJulian Elischer } 9884cf49a43SJulian Elischer 9891f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 9904cf49a43SJulian Elischer if (sendhook == NULL) { 991b86d0a9eSJulian Elischer printf("no matching session\n"); 9924cf49a43SJulian Elischer LEAVE(ENETUNREACH); 9934cf49a43SJulian Elischer } 9944cf49a43SJulian Elischer 9954cf49a43SJulian Elischer /* 9964cf49a43SJulian Elischer * Check the session is in the right state. 9974cf49a43SJulian Elischer * It needs to be in PPPOE_SINIT. 9984cf49a43SJulian Elischer */ 99930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 10004cf49a43SJulian Elischer if (sp->state != PPPOE_SINIT) { 1001b86d0a9eSJulian Elischer printf("session in wrong state\n"); 10024cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10034cf49a43SJulian Elischer } 10044cf49a43SJulian Elischer neg = sp->neg; 10054cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 10064cf49a43SJulian Elischer neg->timeout_handle); 10074cf49a43SJulian Elischer 10084cf49a43SJulian Elischer /* 10094cf49a43SJulian Elischer * This is the first time we hear 10104cf49a43SJulian Elischer * from the server, so note it's 10114cf49a43SJulian Elischer * unicast address, replacing the 10124cf49a43SJulian Elischer * broadcast address . 10134cf49a43SJulian Elischer */ 10144cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 10154cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 10164cf49a43SJulian Elischer ETHER_ADDR_LEN); 10174cf49a43SJulian Elischer neg->timeout = 0; 10184cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADR_CODE; 10194cf49a43SJulian Elischer init_tags(sp); 10207ccbb17bSJulian Elischer insert_tag(sp, utag); /* Host Unique */ 10211f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1022b86d0a9eSJulian Elischer insert_tag(sp, tag); /* return cookie */ 10231f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_NAME))) 10241f89d938SJulian Elischer insert_tag(sp, tag); /* return it */ 10257ccbb17bSJulian Elischer insert_tag(sp, &neg->service.hdr); /* Service */ 10264cf49a43SJulian Elischer scan_tags(sp, ph); 10274cf49a43SJulian Elischer make_packet(sp); 10284cf49a43SJulian Elischer sp->state = PPPOE_SREQ; 10294cf49a43SJulian Elischer sendpacket(sp); 10304cf49a43SJulian Elischer break; 10314cf49a43SJulian Elischer case PADR_CODE: 10324cf49a43SJulian Elischer 10334cf49a43SJulian Elischer /* 10344cf49a43SJulian Elischer * We are a server: 10354cf49a43SJulian Elischer * Use the ac_cookie tag to find the 10364cf49a43SJulian Elischer * hook this is in response to. 10374cf49a43SJulian Elischer */ 10381f89d938SJulian Elischer utag = get_tag(ph, PTT_AC_COOKIE); 10391f89d938SJulian Elischer if ((utag == NULL) 10401f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 10414cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10424cf49a43SJulian Elischer } 10434cf49a43SJulian Elischer 10441f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 10454cf49a43SJulian Elischer if (sendhook == NULL) { 10464cf49a43SJulian Elischer LEAVE(ENETUNREACH); 10474cf49a43SJulian Elischer } 10484cf49a43SJulian Elischer 10494cf49a43SJulian Elischer /* 10504cf49a43SJulian Elischer * Check the session is in the right state. 10514cf49a43SJulian Elischer * It needs to be in PPPOE_SOFFER 10524cf49a43SJulian Elischer * or PPPOE_NEWCONNECTED. If the latter, 10534cf49a43SJulian Elischer * then this is a retry by the client. 10544cf49a43SJulian Elischer * so be nice, and resend. 10554cf49a43SJulian Elischer */ 105630400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 10574cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 10584cf49a43SJulian Elischer /* 10594cf49a43SJulian Elischer * Whoa! drop back to resend that 10604cf49a43SJulian Elischer * PADS packet. 10614cf49a43SJulian Elischer * We should still have a copy of it. 10624cf49a43SJulian Elischer */ 10634cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 10644cf49a43SJulian Elischer } 10654cf49a43SJulian Elischer if (sp->state != PPPOE_SOFFER) { 10664cf49a43SJulian Elischer LEAVE (ENETUNREACH); 10674cf49a43SJulian Elischer break; 10684cf49a43SJulian Elischer } 10694cf49a43SJulian Elischer neg = sp->neg; 10704cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 10714cf49a43SJulian Elischer neg->timeout_handle); 10724cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADS_CODE; 10734cf49a43SJulian Elischer if (sp->Session_ID == 0) 10744cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 1075b86d0a9eSJulian Elischer htons(sp->Session_ID 1076b86d0a9eSJulian Elischer = get_new_sid(node)); 10774cf49a43SJulian Elischer neg->timeout = 0; 10784cf49a43SJulian Elischer /* 10794cf49a43SJulian Elischer * start working out the tags to respond with. 10804cf49a43SJulian Elischer */ 10814cf49a43SJulian Elischer init_tags(sp); 10824cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1083bdaf2e81SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 10844adb13fdSJulian Elischer insert_tag(sp, tag);/* return service */ 10851f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 10864adb13fdSJulian Elischer insert_tag(sp, tag); /* return it */ 10871f89d938SJulian Elischer insert_tag(sp, utag); /* ac_cookie */ 10884cf49a43SJulian Elischer scan_tags(sp, ph); 10894cf49a43SJulian Elischer make_packet(sp); 1090bdaf2e81SJulian Elischer sp->state = PPPOE_NEWCONNECTED; 10916faf164cSJulian Elischer sendpacket(sp); 10924cf49a43SJulian Elischer /* 10934cf49a43SJulian Elischer * Having sent the last Negotiation header, 10944cf49a43SJulian Elischer * Set up the stored packet header to 10954cf49a43SJulian Elischer * be correct for the actual session. 10964cf49a43SJulian Elischer * But keep the negotialtion stuff 10974cf49a43SJulian Elischer * around in case we need to resend this last 10984cf49a43SJulian Elischer * packet. We'll discard it when we move 10994cf49a43SJulian Elischer * from NEWCONNECTED to CONNECTED 11004cf49a43SJulian Elischer */ 11014cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 11024cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 11034cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 11044cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 1105b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 11064cf49a43SJulian Elischer break; 11074cf49a43SJulian Elischer case PADS_CODE: 11084cf49a43SJulian Elischer /* 11094cf49a43SJulian Elischer * We are a client: 11104cf49a43SJulian Elischer * Use the host_uniq tag to find the 11114cf49a43SJulian Elischer * hook this is in response to. 11124cf49a43SJulian Elischer * take the session ID and store it away. 11134cf49a43SJulian Elischer * Also make sure the pre-made header is 11144cf49a43SJulian Elischer * correct and set us into Session mode. 11154cf49a43SJulian Elischer */ 11161f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 11171f89d938SJulian Elischer if ((utag == NULL) 11181f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 11194cf49a43SJulian Elischer LEAVE (ENETUNREACH); 11204cf49a43SJulian Elischer break; 11214cf49a43SJulian Elischer } 11221f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 11234cf49a43SJulian Elischer if (sendhook == NULL) { 11244cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11254cf49a43SJulian Elischer } 11264cf49a43SJulian Elischer 11274cf49a43SJulian Elischer /* 11284cf49a43SJulian Elischer * Check the session is in the right state. 11294cf49a43SJulian Elischer * It needs to be in PPPOE_SREQ. 11304cf49a43SJulian Elischer */ 113130400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11324cf49a43SJulian Elischer if (sp->state != PPPOE_SREQ) { 11334cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11344cf49a43SJulian Elischer } 11354cf49a43SJulian Elischer neg = sp->neg; 11364cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 11374cf49a43SJulian Elischer neg->timeout_handle); 1138cfbcfe62SJulian Elischer neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1139b86d0a9eSJulian Elischer sp->Session_ID = ntohs(wh->ph.sid); 11404cf49a43SJulian Elischer neg->timeout = 0; 11414cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 11424cf49a43SJulian Elischer /* 11434cf49a43SJulian Elischer * Now we have gone to Connected mode, 11444cf49a43SJulian Elischer * Free all resources needed for 11454cf49a43SJulian Elischer * negotiation. 11464cf49a43SJulian Elischer * Keep a copy of the header we will be using. 11474cf49a43SJulian Elischer */ 11484cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 11494cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 11504cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 11514cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 11524cf49a43SJulian Elischer m_freem(neg->m); 11539c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 11544cf49a43SJulian Elischer sp->neg = NULL; 1155b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 11564cf49a43SJulian Elischer break; 11574cf49a43SJulian Elischer case PADT_CODE: 11584cf49a43SJulian Elischer /* 11594cf49a43SJulian Elischer * Send a 'close' message to the controlling 11604cf49a43SJulian Elischer * process (the one that set us up); 11614cf49a43SJulian Elischer * And then tear everything down. 11624cf49a43SJulian Elischer * 11634cf49a43SJulian Elischer * Find matching peer/session combination. 11644cf49a43SJulian Elischer */ 11654cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 11664cf49a43SJulian Elischer if (sendhook == NULL) { 11674cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11684cf49a43SJulian Elischer } 11694cf49a43SJulian Elischer /* send message to creator */ 11704cf49a43SJulian Elischer /* close hook */ 1171b58a8a3bSJulian Elischer if (sendhook) { 1172954c4772SJulian Elischer ng_rmhook_self(sendhook); 1173b58a8a3bSJulian Elischer } 11744cf49a43SJulian Elischer break; 11754cf49a43SJulian Elischer default: 11764cf49a43SJulian Elischer LEAVE(EPFNOSUPPORT); 11774cf49a43SJulian Elischer } 11784cf49a43SJulian Elischer break; 11794cf49a43SJulian Elischer case ETHERTYPE_PPPOE_SESS: 11804cf49a43SJulian Elischer /* 11814cf49a43SJulian Elischer * find matching peer/session combination. 11824cf49a43SJulian Elischer */ 11834cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 11844cf49a43SJulian Elischer if (sendhook == NULL) { 11854cf49a43SJulian Elischer LEAVE (ENETUNREACH); 11864cf49a43SJulian Elischer break; 11874cf49a43SJulian Elischer } 118830400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11894cf49a43SJulian Elischer m_adj(m, sizeof(*wh)); 11904cf49a43SJulian Elischer if (m->m_pkthdr.len < length) { 11914cf49a43SJulian Elischer /* Packet too short, dump it */ 11924cf49a43SJulian Elischer LEAVE(EMSGSIZE); 11934cf49a43SJulian Elischer } 11949fcb3d83SJulian Elischer 11954adb13fdSJulian Elischer /* Also need to trim excess at the end */ 11969fcb3d83SJulian Elischer if (m->m_pkthdr.len > length) { 11979fcb3d83SJulian Elischer m_adj(m, -((int)(m->m_pkthdr.len - length))); 11989fcb3d83SJulian Elischer } 11994cf49a43SJulian Elischer if ( sp->state != PPPOE_CONNECTED) { 12004cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 12014cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 12024cf49a43SJulian Elischer /* 12034cf49a43SJulian Elischer * Now we have gone to Connected mode, 12044cf49a43SJulian Elischer * Free all resources needed for 1205a4ec03cfSJulian Elischer * negotiation. Be paranoid about 1206a4ec03cfSJulian Elischer * whether there may be a timeout. 12074cf49a43SJulian Elischer */ 12084cf49a43SJulian Elischer m_freem(sp->neg->m); 1209a4ec03cfSJulian Elischer untimeout(pppoe_ticker, sendhook, 1210a4ec03cfSJulian Elischer sp->neg->timeout_handle); 12119c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 12124cf49a43SJulian Elischer sp->neg = NULL; 12134cf49a43SJulian Elischer } else { 12144cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12154cf49a43SJulian Elischer break; 12164cf49a43SJulian Elischer } 12174cf49a43SJulian Elischer } 1218069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, sendhook, m); 12194cf49a43SJulian Elischer break; 12204cf49a43SJulian Elischer default: 12214b276f90SJulian Elischer LEAVE(EPFNOSUPPORT); 12224cf49a43SJulian Elischer } 12234cf49a43SJulian Elischer } else { 12244cf49a43SJulian Elischer /* 12254cf49a43SJulian Elischer * Not ethernet or debug hook.. 12264cf49a43SJulian Elischer * 12274cf49a43SJulian Elischer * The packet has come in on a normal hook. 12284cf49a43SJulian Elischer * We need to find out what kind of hook, 12294cf49a43SJulian Elischer * So we can decide how to handle it. 12304cf49a43SJulian Elischer * Check the hook's state. 12314cf49a43SJulian Elischer */ 123230400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 12334cf49a43SJulian Elischer switch (sp->state) { 12344cf49a43SJulian Elischer case PPPOE_NEWCONNECTED: 12354cf49a43SJulian Elischer case PPPOE_CONNECTED: { 12367b38c4e4SArchie Cobbs static const u_char addrctrl[] = { 0xff, 0x03 }; 12374cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 12387b38c4e4SArchie Cobbs 12397b38c4e4SArchie Cobbs /* 12407b38c4e4SArchie Cobbs * Remove PPP address and control fields, if any. 12417b38c4e4SArchie Cobbs * For example, ng_ppp(4) always sends LCP packets 12427b38c4e4SArchie Cobbs * with address and control fields as required by 12437b38c4e4SArchie Cobbs * generic PPP. PPPoE is an exception to the rule. 12447b38c4e4SArchie Cobbs */ 12457b38c4e4SArchie Cobbs if (m->m_pkthdr.len >= 2) { 12467b38c4e4SArchie Cobbs if (m->m_len < 2 && !(m = m_pullup(m, 2))) 12477b38c4e4SArchie Cobbs LEAVE(ENOBUFS); 12487b38c4e4SArchie Cobbs if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 12497b38c4e4SArchie Cobbs m_adj(m, 2); 12507b38c4e4SArchie Cobbs } 12514cf49a43SJulian Elischer /* 12524cf49a43SJulian Elischer * Bang in a pre-made header, and set the length up 12534cf49a43SJulian Elischer * to be correct. Then send it to the ethernet driver. 1254d9da9cbaSJulian Elischer * But first correct the length. 12554cf49a43SJulian Elischer */ 1256d9da9cbaSJulian Elischer sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 12574cf49a43SJulian Elischer M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 12584cf49a43SJulian Elischer if (m == NULL) { 12594cf49a43SJulian Elischer LEAVE(ENOBUFS); 12604cf49a43SJulian Elischer } 12614cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 12624cf49a43SJulian Elischer bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1263069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 12644cf49a43SJulian Elischer privp->packets_out++; 12654cf49a43SJulian Elischer break; 12664cf49a43SJulian Elischer } 12674cf49a43SJulian Elischer case PPPOE_PRIMED: 12684cf49a43SJulian Elischer /* 12694cf49a43SJulian Elischer * A PADI packet is being returned by the application 12704cf49a43SJulian Elischer * that has set up this hook. This indicates that it 12714cf49a43SJulian Elischer * wants us to offer service. 12724cf49a43SJulian Elischer */ 12734cf49a43SJulian Elischer neg = sp->neg; 1274bdaf2e81SJulian Elischer if (m->m_len < sizeof(*wh)) { 1275bdaf2e81SJulian Elischer m = m_pullup(m, sizeof(*wh)); 12764cf49a43SJulian Elischer if (m == NULL) { 12774cf49a43SJulian Elischer LEAVE(ENOBUFS); 12784cf49a43SJulian Elischer } 1279bdaf2e81SJulian Elischer } 12804cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 12814cf49a43SJulian Elischer ph = &wh->ph; 12824cf49a43SJulian Elischer session = ntohs(wh->ph.sid); 12834cf49a43SJulian Elischer length = ntohs(wh->ph.length); 12844cf49a43SJulian Elischer code = wh->ph.code; 12851e2510f8SJulian Elischer if ( code != PADI_CODE) { 12861e2510f8SJulian Elischer LEAVE(EINVAL); 12871e2510f8SJulian Elischer }; 12881e2510f8SJulian Elischer untimeout(pppoe_ticker, hook, 12891e2510f8SJulian Elischer neg->timeout_handle); 12904cf49a43SJulian Elischer 12914cf49a43SJulian Elischer /* 12924cf49a43SJulian Elischer * This is the first time we hear 12934cf49a43SJulian Elischer * from the client, so note it's 12944cf49a43SJulian Elischer * unicast address, replacing the 12954cf49a43SJulian Elischer * broadcast address. 12964cf49a43SJulian Elischer */ 12974cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 12984cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 12994cf49a43SJulian Elischer ETHER_ADDR_LEN); 13004cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 13014cf49a43SJulian Elischer neg->timeout = 0; 13024cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 13034cf49a43SJulian Elischer 13044cf49a43SJulian Elischer /* 13054cf49a43SJulian Elischer * start working out the tags to respond with. 13064cf49a43SJulian Elischer */ 13074cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_AC_COOKIE; 13084cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 13094cf49a43SJulian Elischer uniqtag.data.pointer = sp; 13104cf49a43SJulian Elischer init_tags(sp); 13114cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 13121f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 13134adb13fdSJulian Elischer insert_tag(sp, tag); /* return service */ 1314859a4d16SJulian Elischer /* 1315859a4d16SJulian Elischer * If we have a NULL service request 1316859a4d16SJulian Elischer * and have an extra service defined in this hook, 1317859a4d16SJulian Elischer * then also add a tag for the extra service. 1318859a4d16SJulian Elischer * XXX this is a hack. eventually we should be able 1319859a4d16SJulian Elischer * to support advertising many services, not just one 1320859a4d16SJulian Elischer */ 1321859a4d16SJulian Elischer if (((tag == NULL) || (tag->tag_len == 0)) 1322859a4d16SJulian Elischer && (neg->service.hdr.tag_len != 0)) { 1323859a4d16SJulian Elischer insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1324859a4d16SJulian Elischer } 13251f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 13261f89d938SJulian Elischer insert_tag(sp, tag); /* returned hostunique */ 13271f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 13284cf49a43SJulian Elischer scan_tags(sp, ph); 13294cf49a43SJulian Elischer make_packet(sp); 13304cf49a43SJulian Elischer sendpacket(sp); 13314cf49a43SJulian Elischer break; 13324cf49a43SJulian Elischer 13334cf49a43SJulian Elischer /* 13344cf49a43SJulian Elischer * Packets coming from the hook make no sense 13354cf49a43SJulian Elischer * to sessions in these states. Throw them away. 13364cf49a43SJulian Elischer */ 13374cf49a43SJulian Elischer case PPPOE_SINIT: 13384cf49a43SJulian Elischer case PPPOE_SREQ: 13394cf49a43SJulian Elischer case PPPOE_SOFFER: 13404cf49a43SJulian Elischer case PPPOE_SNONE: 13414cf49a43SJulian Elischer case PPPOE_LISTENING: 13424cf49a43SJulian Elischer case PPPOE_DEAD: 13434cf49a43SJulian Elischer default: 13444cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13454cf49a43SJulian Elischer } 13464cf49a43SJulian Elischer } 13474cf49a43SJulian Elischer quit: 1348f5856029SJulian Elischer if (item) 1349069154d5SJulian Elischer NG_FREE_ITEM(item); 1350069154d5SJulian Elischer NG_FREE_M(m); 13514cf49a43SJulian Elischer return error; 13524cf49a43SJulian Elischer } 13534cf49a43SJulian Elischer 13544cf49a43SJulian Elischer /* 13554cf49a43SJulian Elischer * Do local shutdown processing.. 13564cf49a43SJulian Elischer * If we are a persistant device, we might refuse to go away, and 13574cf49a43SJulian Elischer * we'd only remove our links and reset ourself. 13584cf49a43SJulian Elischer */ 13594cf49a43SJulian Elischer static int 1360069154d5SJulian Elischer ng_pppoe_shutdown(node_p node) 13614cf49a43SJulian Elischer { 136230400f03SJulian Elischer const priv_p privdata = NG_NODE_PRIVATE(node); 13634cf49a43SJulian Elischer 13641e2510f8SJulian Elischer AAA 136530400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 136630400f03SJulian Elischer NG_NODE_UNREF(privdata->node); 13679c8c302fSJulian Elischer FREE(privdata, M_NETGRAPH_PPPOE); 13684cf49a43SJulian Elischer return (0); 13694cf49a43SJulian Elischer } 13704cf49a43SJulian Elischer 13714cf49a43SJulian Elischer /* 13724cf49a43SJulian Elischer * This is called once we've already connected a new hook to the other node. 13734cf49a43SJulian Elischer * It gives us a chance to balk at the last minute. 13744cf49a43SJulian Elischer */ 13754cf49a43SJulian Elischer static int 13768876b55dSJulian Elischer ng_pppoe_connect(hook_p hook) 13774cf49a43SJulian Elischer { 13784cf49a43SJulian Elischer /* be really amiable and just say "YUP that's OK by me! " */ 13794cf49a43SJulian Elischer return (0); 13804cf49a43SJulian Elischer } 13814cf49a43SJulian Elischer 13824cf49a43SJulian Elischer /* 13834cf49a43SJulian Elischer * Hook disconnection 13844cf49a43SJulian Elischer * 13856faf164cSJulian Elischer * Clean up all dangling links and information about the session/hook. 13864cf49a43SJulian Elischer * For this type, removal of the last link destroys the node 13874cf49a43SJulian Elischer */ 13884cf49a43SJulian Elischer static int 13898876b55dSJulian Elischer ng_pppoe_disconnect(hook_p hook) 13904cf49a43SJulian Elischer { 139130400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 139230400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 13934cf49a43SJulian Elischer sessp sp; 139404853d8aSJulian Elischer int hooks; 13954cf49a43SJulian Elischer 13961e2510f8SJulian Elischer AAA 139730400f03SJulian Elischer hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 139830400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 13994cf49a43SJulian Elischer privp->debug_hook = NULL; 140030400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 14014cf49a43SJulian Elischer privp->ethernet_hook = NULL; 140230400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) 1403069154d5SJulian Elischer ng_rmnode_self(node); 14044cf49a43SJulian Elischer } else { 140530400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 1406b58a8a3bSJulian Elischer if (sp->state != PPPOE_SNONE ) { 1407b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1408b58a8a3bSJulian Elischer } 1409a4ec03cfSJulian Elischer /* 1410a4ec03cfSJulian Elischer * According to the spec, if we are connected, 1411a4ec03cfSJulian Elischer * we should send a DISC packet if we are shutting down 1412a4ec03cfSJulian Elischer * a session. 1413a4ec03cfSJulian Elischer */ 14149fcb3d83SJulian Elischer if ((privp->ethernet_hook) 14159fcb3d83SJulian Elischer && ((sp->state == PPPOE_CONNECTED) 14169fcb3d83SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED))) { 14179fcb3d83SJulian Elischer struct mbuf *m; 14189fcb3d83SJulian Elischer struct pppoe_full_hdr *wh; 14199fcb3d83SJulian Elischer struct pppoe_tag *tag; 14209fcb3d83SJulian Elischer int msglen = strlen(SIGNOFF); 14219fcb3d83SJulian Elischer int error = 0; 14229fcb3d83SJulian Elischer 14239fcb3d83SJulian Elischer /* revert the stored header to DISC/PADT mode */ 14249fcb3d83SJulian Elischer wh = &sp->pkt_hdr; 14259fcb3d83SJulian Elischer wh->ph.code = PADT_CODE; 14269fcb3d83SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 14279fcb3d83SJulian Elischer 14289fcb3d83SJulian Elischer /* generate a packet of that type */ 14299fcb3d83SJulian Elischer MGETHDR(m, M_DONTWAIT, MT_DATA); 14306faf164cSJulian Elischer if(m == NULL) 14316faf164cSJulian Elischer printf("pppoe: Session out of mbufs\n"); 14326faf164cSJulian Elischer else { 14339fcb3d83SJulian Elischer m->m_pkthdr.rcvif = NULL; 14349fcb3d83SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*wh); 14356faf164cSJulian Elischer bcopy((caddr_t)wh, mtod(m, caddr_t), 14366faf164cSJulian Elischer sizeof(*wh)); 14376faf164cSJulian Elischer /* 14386faf164cSJulian Elischer * Add a General error message and adjust 14396faf164cSJulian Elischer * sizes 14406faf164cSJulian Elischer */ 14419fcb3d83SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 14429fcb3d83SJulian Elischer tag = wh->ph.tag; 14439fcb3d83SJulian Elischer tag->tag_type = PTT_GEN_ERR; 14449fcb3d83SJulian Elischer tag->tag_len = htons((u_int16_t)msglen); 14459fcb3d83SJulian Elischer strncpy(tag->tag_data, SIGNOFF, msglen); 14466faf164cSJulian Elischer m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 14476faf164cSJulian Elischer msglen); 14489fcb3d83SJulian Elischer wh->ph.length = htons(sizeof(*tag) + msglen); 1449069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, 1450069154d5SJulian Elischer privp->ethernet_hook, m); 14516faf164cSJulian Elischer } 14529fcb3d83SJulian Elischer } 1453a4ec03cfSJulian Elischer /* 1454514baf3fSJeroen Ruigrok van der Werven * As long as we have somewhere to store the timeout handle, 1455a4ec03cfSJulian Elischer * we may have a timeout pending.. get rid of it. 1456a4ec03cfSJulian Elischer */ 14571e2510f8SJulian Elischer if (sp->neg) { 14584cf49a43SJulian Elischer untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 14591e2510f8SJulian Elischer if (sp->neg->m) 14601e2510f8SJulian Elischer m_freem(sp->neg->m); 14619c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 14621e2510f8SJulian Elischer } 14639c8c302fSJulian Elischer FREE(sp, M_NETGRAPH_PPPOE); 146430400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 1465ed52f174SJulian Elischer /* work out how many session hooks there are */ 146604853d8aSJulian Elischer /* Node goes away on last session hook removal */ 146704853d8aSJulian Elischer if (privp->ethernet_hook) hooks -= 1; 1468ed52f174SJulian Elischer if (privp->debug_hook) hooks -= 1; 14694cf49a43SJulian Elischer } 147030400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 147130400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 1472069154d5SJulian Elischer ng_rmnode_self(node); 14734cf49a43SJulian Elischer return (0); 14744cf49a43SJulian Elischer } 14754cf49a43SJulian Elischer 14764cf49a43SJulian Elischer /* 14774cf49a43SJulian Elischer * timeouts come here. 14784cf49a43SJulian Elischer */ 14794cf49a43SJulian Elischer static void 14804cf49a43SJulian Elischer pppoe_ticker(void *arg) 14814cf49a43SJulian Elischer { 14824cf49a43SJulian Elischer int s = splnet(); 14834cf49a43SJulian Elischer hook_p hook = arg; 148430400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 14854cf49a43SJulian Elischer negp neg = sp->neg; 14864cf49a43SJulian Elischer int error = 0; 14874cf49a43SJulian Elischer struct mbuf *m0 = NULL; 148830400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 14894cf49a43SJulian Elischer 14901e2510f8SJulian Elischer AAA 14914cf49a43SJulian Elischer switch(sp->state) { 14924cf49a43SJulian Elischer /* 14934cf49a43SJulian Elischer * resend the last packet, using an exponential backoff. 14944cf49a43SJulian Elischer * After a period of time, stop growing the backoff, 14954adb13fdSJulian Elischer * and either leave it, or revert to the start. 14964cf49a43SJulian Elischer */ 14974cf49a43SJulian Elischer case PPPOE_SINIT: 14984cf49a43SJulian Elischer case PPPOE_SREQ: 14994cf49a43SJulian Elischer /* timeouts on these produce resends */ 15004cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1501069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 15024cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 15034cf49a43SJulian Elischer hook, neg->timeout * hz); 15044cf49a43SJulian Elischer if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 15054cf49a43SJulian Elischer if (sp->state == PPPOE_SREQ) { 15064cf49a43SJulian Elischer /* revert to SINIT mode */ 1507b58a8a3bSJulian Elischer pppoe_start(sp); 15084cf49a43SJulian Elischer } else { 15094cf49a43SJulian Elischer neg->timeout = PPPOE_TIMEOUT_LIMIT; 15104cf49a43SJulian Elischer } 15114cf49a43SJulian Elischer } 15124cf49a43SJulian Elischer break; 15134cf49a43SJulian Elischer case PPPOE_PRIMED: 15144cf49a43SJulian Elischer case PPPOE_SOFFER: 15154cf49a43SJulian Elischer /* a timeout on these says "give up" */ 1516954c4772SJulian Elischer ng_rmhook_self(hook); 15174cf49a43SJulian Elischer break; 15184cf49a43SJulian Elischer default: 15194cf49a43SJulian Elischer /* timeouts have no meaning in other states */ 15204cf49a43SJulian Elischer printf("pppoe: unexpected timeout\n"); 15214cf49a43SJulian Elischer } 15224cf49a43SJulian Elischer splx(s); 15234cf49a43SJulian Elischer } 15244cf49a43SJulian Elischer 15254cf49a43SJulian Elischer 15264cf49a43SJulian Elischer static void 15274cf49a43SJulian Elischer sendpacket(sessp sp) 15284cf49a43SJulian Elischer { 15294cf49a43SJulian Elischer int error = 0; 15304cf49a43SJulian Elischer struct mbuf *m0 = NULL; 15314cf49a43SJulian Elischer hook_p hook = sp->hook; 15324cf49a43SJulian Elischer negp neg = sp->neg; 153330400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 15344cf49a43SJulian Elischer 15351e2510f8SJulian Elischer AAA 15364cf49a43SJulian Elischer switch(sp->state) { 15374cf49a43SJulian Elischer case PPPOE_LISTENING: 15384cf49a43SJulian Elischer case PPPOE_DEAD: 15394cf49a43SJulian Elischer case PPPOE_SNONE: 15404cf49a43SJulian Elischer case PPPOE_CONNECTED: 1541b86d0a9eSJulian Elischer printf("pppoe: sendpacket: unexpected state\n"); 15424cf49a43SJulian Elischer break; 15434cf49a43SJulian Elischer 15446faf164cSJulian Elischer case PPPOE_NEWCONNECTED: 15456faf164cSJulian Elischer /* send the PADS without a timeout - we're now connected */ 15466faf164cSJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1547069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 15486faf164cSJulian Elischer break; 15496faf164cSJulian Elischer 15504cf49a43SJulian Elischer case PPPOE_PRIMED: 15514cf49a43SJulian Elischer /* No packet to send, but set up the timeout */ 15524cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 15534cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 15544cf49a43SJulian Elischer break; 15554cf49a43SJulian Elischer 15564cf49a43SJulian Elischer case PPPOE_SOFFER: 15574cf49a43SJulian Elischer /* 15584cf49a43SJulian Elischer * send the offer but if they don't respond 15594cf49a43SJulian Elischer * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 15604cf49a43SJulian Elischer */ 15614cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1562069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 15634cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 15644cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 15654cf49a43SJulian Elischer break; 15664cf49a43SJulian Elischer 15674cf49a43SJulian Elischer case PPPOE_SINIT: 15684cf49a43SJulian Elischer case PPPOE_SREQ: 15694cf49a43SJulian Elischer m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1570069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1571d0fef808SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, hook, 1572d0fef808SJulian Elischer (hz * PPPOE_INITIAL_TIMEOUT)); 1573d0fef808SJulian Elischer neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 15744cf49a43SJulian Elischer break; 15754cf49a43SJulian Elischer 15764cf49a43SJulian Elischer default: 15774cf49a43SJulian Elischer error = EINVAL; 15784cf49a43SJulian Elischer printf("pppoe: timeout: bad state\n"); 15794cf49a43SJulian Elischer } 15804cf49a43SJulian Elischer /* return (error); */ 15814cf49a43SJulian Elischer } 15824cf49a43SJulian Elischer 15834cf49a43SJulian Elischer /* 15844cf49a43SJulian Elischer * Parse an incoming packet to see if any tags should be copied to the 15854adb13fdSJulian Elischer * output packet. Don't do any tags that have been handled in the main 15864adb13fdSJulian Elischer * state machine. 15874cf49a43SJulian Elischer */ 15884cf49a43SJulian Elischer static struct pppoe_tag* 15894cf49a43SJulian Elischer scan_tags(sessp sp, struct pppoe_hdr* ph) 15904cf49a43SJulian Elischer { 15914cf49a43SJulian Elischer char *end = (char *)next_tag(ph); 15924cf49a43SJulian Elischer char *ptn; 15934cf49a43SJulian Elischer struct pppoe_tag *pt = &ph->tag[0]; 15944cf49a43SJulian Elischer /* 15954cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 15964cf49a43SJulian Elischer */ 15971e2510f8SJulian Elischer AAA 15984cf49a43SJulian Elischer while((char*)(pt + 1) <= end) { 15994cf49a43SJulian Elischer /* 16004cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 16014cf49a43SJulian Elischer */ 16024cf49a43SJulian Elischer ptn = (((char *)(pt + 1)) + ntohs(pt->tag_len)); 16034cf49a43SJulian Elischer if(ptn > end) 16044cf49a43SJulian Elischer return NULL; 16054cf49a43SJulian Elischer 16064cf49a43SJulian Elischer switch (pt->tag_type) { 16074cf49a43SJulian Elischer case PTT_RELAY_SID: 16084cf49a43SJulian Elischer insert_tag(sp, pt); 16094cf49a43SJulian Elischer break; 16104cf49a43SJulian Elischer case PTT_EOL: 16114cf49a43SJulian Elischer return NULL; 16124cf49a43SJulian Elischer case PTT_SRV_NAME: 16134cf49a43SJulian Elischer case PTT_AC_NAME: 16144cf49a43SJulian Elischer case PTT_HOST_UNIQ: 16154cf49a43SJulian Elischer case PTT_AC_COOKIE: 16164cf49a43SJulian Elischer case PTT_VENDOR: 16174cf49a43SJulian Elischer case PTT_SRV_ERR: 16184cf49a43SJulian Elischer case PTT_SYS_ERR: 16194cf49a43SJulian Elischer case PTT_GEN_ERR: 16204cf49a43SJulian Elischer break; 16214cf49a43SJulian Elischer } 16224cf49a43SJulian Elischer pt = (struct pppoe_tag*)ptn; 16234cf49a43SJulian Elischer } 16244cf49a43SJulian Elischer return NULL; 16254cf49a43SJulian Elischer } 16264cf49a43SJulian Elischer 1627b58a8a3bSJulian Elischer static int 1628b58a8a3bSJulian Elischer pppoe_send_event(sessp sp, enum cmd cmdid) 1629b58a8a3bSJulian Elischer { 1630b58a8a3bSJulian Elischer int error; 1631b58a8a3bSJulian Elischer struct ng_mesg *msg; 16328876b55dSJulian Elischer struct ngpppoe_sts *sts; 1633b58a8a3bSJulian Elischer 16341e2510f8SJulian Elischer AAA 163527121ab1SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 16368876b55dSJulian Elischer sizeof(struct ngpppoe_sts), M_NOWAIT); 1637859a4d16SJulian Elischer if (msg == NULL) 1638859a4d16SJulian Elischer return (ENOMEM); 16398876b55dSJulian Elischer sts = (struct ngpppoe_sts *)msg->data; 164030400f03SJulian Elischer strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKLEN + 1); 164130400f03SJulian Elischer NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, NULL); 1642b58a8a3bSJulian Elischer return (error); 1643b58a8a3bSJulian Elischer } 1644