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 43ec774932SGleb Smirnoff #define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 441e2510f8SJulian Elischer #else 45ec774932SGleb Smirnoff #define DBG do {} while (0) 461e2510f8SJulian Elischer #endif 474cf49a43SJulian Elischer 484cf49a43SJulian Elischer #include <sys/param.h> 494cf49a43SJulian Elischer #include <sys/systm.h> 504cf49a43SJulian Elischer #include <sys/kernel.h> 514cf49a43SJulian Elischer #include <sys/mbuf.h> 524cf49a43SJulian Elischer #include <sys/malloc.h> 534cf49a43SJulian Elischer #include <sys/errno.h> 54bfa7e882SJulian Elischer #include <sys/sysctl.h> 55875467faSYaroslav Tykhiy #include <sys/syslog.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 74bfa7e882SJulian Elischer * pppoe node. These methods define the netgraph pppoe '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 */ 86f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[] 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, 90f0184ff8SArchie Cobbs &ngpppoe_init_data_type_fields 9176a70671SBrian Somers }; 9276a70671SBrian Somers 9376a70671SBrian Somers /* Parse type for struct ngpppoe_sts */ 94f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[] 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, 98f0184ff8SArchie Cobbs &ng_pppoe_sts_type_fields 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 }, 152fdc755d1SGleb Smirnoff { 153fdc755d1SGleb Smirnoff NGM_PPPOE_COOKIE, 154fdc755d1SGleb Smirnoff NGM_PPPOE_SETMODE, 155fdc755d1SGleb Smirnoff "pppoe_setmode", 156fdc755d1SGleb Smirnoff &ng_parse_string_type, 157fdc755d1SGleb Smirnoff NULL 158fdc755d1SGleb Smirnoff }, 159fdc755d1SGleb Smirnoff { 160fdc755d1SGleb Smirnoff NGM_PPPOE_COOKIE, 161fdc755d1SGleb Smirnoff NGM_PPPOE_GETMODE, 162fdc755d1SGleb Smirnoff "pppoe_getmode", 163fdc755d1SGleb Smirnoff NULL, 164fdc755d1SGleb Smirnoff &ng_parse_string_type 165fdc755d1SGleb Smirnoff }, 16676a70671SBrian Somers { 0 } 16776a70671SBrian Somers }; 16876a70671SBrian Somers 1694cf49a43SJulian Elischer /* Netgraph node type descriptor */ 1704cf49a43SJulian Elischer static struct ng_type typestruct = { 171f8aae777SJulian Elischer .version = NG_ABI_VERSION, 172f8aae777SJulian Elischer .name = NG_PPPOE_NODE_TYPE, 173f8aae777SJulian Elischer .constructor = ng_pppoe_constructor, 174f8aae777SJulian Elischer .rcvmsg = ng_pppoe_rcvmsg, 175f8aae777SJulian Elischer .shutdown = ng_pppoe_shutdown, 176f8aae777SJulian Elischer .newhook = ng_pppoe_newhook, 177f8aae777SJulian Elischer .connect = ng_pppoe_connect, 178f8aae777SJulian Elischer .rcvdata = ng_pppoe_rcvdata, 179f8aae777SJulian Elischer .disconnect = ng_pppoe_disconnect, 180f8aae777SJulian Elischer .cmdlist = ng_pppoe_cmds, 1814cf49a43SJulian Elischer }; 1828876b55dSJulian Elischer NETGRAPH_INIT(pppoe, &typestruct); 1831acb27c6SJulian Elischer /* Depend on ng_ether so we can use the Ethernet parse type */ 1841acb27c6SJulian Elischer MODULE_DEPEND(ng_pppoe, ng_ether, 1, 1, 1); 1854cf49a43SJulian Elischer 1864cf49a43SJulian Elischer /* 1874cf49a43SJulian Elischer * States for the session state machine. 1884cf49a43SJulian Elischer * These have no meaning if there is no hook attached yet. 1894cf49a43SJulian Elischer */ 1904cf49a43SJulian Elischer enum state { 1914cf49a43SJulian Elischer PPPOE_SNONE=0, /* [both] Initial state */ 1926faf164cSJulian Elischer PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 1934cf49a43SJulian Elischer PPPOE_SINIT, /* [Client] Sent discovery initiation */ 1946faf164cSJulian Elischer PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 1956faf164cSJulian Elischer PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 1964cf49a43SJulian Elischer PPPOE_SREQ, /* [Client] Sent a Request */ 1976faf164cSJulian Elischer PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 1984cf49a43SJulian Elischer PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 1994cf49a43SJulian Elischer PPPOE_DEAD /* [Both] */ 2004cf49a43SJulian Elischer }; 2014cf49a43SJulian Elischer 2024cf49a43SJulian Elischer #define NUMTAGS 20 /* number of tags we are set up to work with */ 2034cf49a43SJulian Elischer 2044cf49a43SJulian Elischer /* 2054cf49a43SJulian Elischer * Information we store for each hook on each node for negotiating the 2064cf49a43SJulian Elischer * session. The mbuf and cluster are freed once negotiation has completed. 2074cf49a43SJulian Elischer * The whole negotiation block is then discarded. 2084cf49a43SJulian Elischer */ 2094cf49a43SJulian Elischer 2104cf49a43SJulian Elischer struct sess_neg { 2114cf49a43SJulian Elischer struct mbuf *m; /* holds cluster with last sent packet */ 2124cf49a43SJulian Elischer union packet *pkt; /* points within the above cluster */ 2134cf49a43SJulian Elischer struct callout_handle timeout_handle; /* see timeout(9) */ 2144cf49a43SJulian Elischer u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 2154cf49a43SJulian Elischer u_int numtags; 216816b834fSArchie Cobbs const struct pppoe_tag *tags[NUMTAGS]; 2174cf49a43SJulian Elischer u_int service_len; 2184cf49a43SJulian Elischer u_int ac_name_len; 2194cf49a43SJulian Elischer 2204cf49a43SJulian Elischer struct datatag service; 2214cf49a43SJulian Elischer struct datatag ac_name; 2224cf49a43SJulian Elischer }; 2234cf49a43SJulian Elischer typedef struct sess_neg *negp; 2244cf49a43SJulian Elischer 2254cf49a43SJulian Elischer /* 2264cf49a43SJulian Elischer * Session information that is needed after connection. 2274cf49a43SJulian Elischer */ 2282b9cf2f7SArchie Cobbs struct sess_con { 2294cf49a43SJulian Elischer hook_p hook; 2304cf49a43SJulian Elischer u_int16_t Session_ID; 2314cf49a43SJulian Elischer enum state state; 232069154d5SJulian Elischer ng_ID_t creator; /* who to notify */ 2334cf49a43SJulian Elischer struct pppoe_full_hdr pkt_hdr; /* used when connected */ 2344cf49a43SJulian Elischer negp neg; /* used when negotiating */ 2352b9cf2f7SArchie Cobbs /*struct sess_con *hash_next;*/ /* not yet used */ 2364cf49a43SJulian Elischer }; 2372b9cf2f7SArchie Cobbs typedef struct sess_con *sessp; 2384cf49a43SJulian Elischer 239fdc755d1SGleb Smirnoff #define NG_PPPOE_SESSION_NODE(sp) NG_HOOK_NODE(sp->hook) 240fdc755d1SGleb Smirnoff 241fdc755d1SGleb Smirnoff enum { 242fdc755d1SGleb Smirnoff PPPOE_STANDARD = 1, /* standard RFC2516 mode */ 243fdc755d1SGleb Smirnoff PPPOE_NONSTANDARD, /* 3Com proprietary mode */ 244fdc755d1SGleb Smirnoff }; 245fdc755d1SGleb Smirnoff 246fdc755d1SGleb Smirnoff struct ng_pppoe_mode_t { 247fdc755d1SGleb Smirnoff u_int8_t id; 248fdc755d1SGleb Smirnoff const struct ether_header *eh_prototype; 249fdc755d1SGleb Smirnoff const char *name; 250fdc755d1SGleb Smirnoff }; 251fdc755d1SGleb Smirnoff 252fdc755d1SGleb Smirnoff static const struct ether_header eh_standard = 253fdc755d1SGleb Smirnoff {{0xff,0xff,0xff,0xff,0xff,0xff}, 254fdc755d1SGleb Smirnoff {0x00,0x00,0x00,0x00,0x00,0x00}, 255fdc755d1SGleb Smirnoff ETHERTYPE_PPPOE_DISC}; 256fdc755d1SGleb Smirnoff 257fdc755d1SGleb Smirnoff static const struct ether_header eh_3Com = 258fdc755d1SGleb Smirnoff {{0xff,0xff,0xff,0xff,0xff,0xff}, 259fdc755d1SGleb Smirnoff {0x00,0x00,0x00,0x00,0x00,0x00}, 260fdc755d1SGleb Smirnoff ETHERTYPE_PPPOE_STUPID_DISC}; 261fdc755d1SGleb Smirnoff 262fdc755d1SGleb Smirnoff static const struct ng_pppoe_mode_t ng_pppoe_modes[] = { 263fdc755d1SGleb Smirnoff { PPPOE_STANDARD, &eh_standard, NG_PPPOE_STANDARD }, 264fdc755d1SGleb Smirnoff { PPPOE_NONSTANDARD, &eh_3Com, NG_PPPOE_NONSTANDARD }, 265fdc755d1SGleb Smirnoff { 0, NULL}, 266fdc755d1SGleb Smirnoff }; 267fdc755d1SGleb Smirnoff 2684cf49a43SJulian Elischer /* 2694cf49a43SJulian Elischer * Information we store for each node 2704cf49a43SJulian Elischer */ 2714cf49a43SJulian Elischer struct PPPOE { 2724cf49a43SJulian Elischer node_p node; /* back pointer to node */ 2734cf49a43SJulian Elischer hook_p ethernet_hook; 2744cf49a43SJulian Elischer hook_p debug_hook; 2754cf49a43SJulian Elischer u_int packets_in; /* packets in from ethernet */ 2764cf49a43SJulian Elischer u_int packets_out; /* packets out towards ethernet */ 2774cf49a43SJulian Elischer u_int32_t flags; 278fdc755d1SGleb Smirnoff const struct ng_pppoe_mode_t *mode; /* standard PPPoE or 3Com? */ 2792b9cf2f7SArchie Cobbs /*struct sess_con *buckets[HASH_SIZE];*/ /* not yet used */ 2804cf49a43SJulian Elischer }; 2814cf49a43SJulian Elischer typedef struct PPPOE *priv_p; 2824cf49a43SJulian Elischer 283fdc755d1SGleb Smirnoff /* Deprecated sysctl, leaved here to keep compatibility for some time */ 284c0451ac3SGleb Smirnoff #define PPPOE_SYSCTL_KEEPSTANDARD -1 285c0451ac3SGleb Smirnoff #define PPPOE_SYSCTL_STANDARD 0 286c0451ac3SGleb Smirnoff #define PPPOE_SYSCTL_NONSTANDARD 1 287c0451ac3SGleb Smirnoff static int pppoe_mode = PPPOE_SYSCTL_KEEPSTANDARD; 288fdc755d1SGleb Smirnoff static const struct ng_pppoe_mode_t *sysctl_mode = ng_pppoe_modes; 289875467faSYaroslav Tykhiy 290bfa7e882SJulian Elischer static int 291bfa7e882SJulian Elischer ngpppoe_set_ethertype(SYSCTL_HANDLER_ARGS) 292bfa7e882SJulian Elischer { 293bfa7e882SJulian Elischer int error; 294bfa7e882SJulian Elischer int val; 295bfa7e882SJulian Elischer 296875467faSYaroslav Tykhiy val = pppoe_mode; 297bfa7e882SJulian Elischer error = sysctl_handle_int(oidp, &val, sizeof(int), req); 298bfa7e882SJulian Elischer if (error != 0 || req->newptr == NULL) 299bfa7e882SJulian Elischer return (error); 300875467faSYaroslav Tykhiy switch (val) { 301c0451ac3SGleb Smirnoff case PPPOE_SYSCTL_NONSTANDARD: 302fdc755d1SGleb Smirnoff sysctl_mode = ng_pppoe_modes + 1; 303875467faSYaroslav Tykhiy break; 304c0451ac3SGleb Smirnoff case PPPOE_SYSCTL_STANDARD: 305c0451ac3SGleb Smirnoff case PPPOE_SYSCTL_KEEPSTANDARD: 306fdc755d1SGleb Smirnoff sysctl_mode = ng_pppoe_modes; 307875467faSYaroslav Tykhiy break; 308875467faSYaroslav Tykhiy default: 309875467faSYaroslav Tykhiy return (EINVAL); 310bfa7e882SJulian Elischer } 311fdc755d1SGleb Smirnoff 312fdc755d1SGleb Smirnoff pppoe_mode = val; 313fdc755d1SGleb Smirnoff printf("net.graph.nonstandard_pppoe is deprecated. See ng_pppoe(4), ppp(8).\n"); 314bfa7e882SJulian Elischer return (0); 315bfa7e882SJulian Elischer } 316bfa7e882SJulian Elischer 31794142c49SJulian Elischer SYSCTL_PROC(_net_graph, OID_AUTO, nonstandard_pppoe, CTLTYPE_INT | CTLFLAG_RW, 318bfa7e882SJulian Elischer 0, sizeof(int), ngpppoe_set_ethertype, "I", "select normal or stupid ISP"); 319bfa7e882SJulian Elischer 3204cf49a43SJulian Elischer union uniq { 3214cf49a43SJulian Elischer char bytes[sizeof(void *)]; 3224cf49a43SJulian Elischer void * pointer; 3234cf49a43SJulian Elischer }; 3244cf49a43SJulian Elischer 3254cf49a43SJulian Elischer #define LEAVE(x) do { error = x; goto quit; } while(0) 3264cf49a43SJulian Elischer static void pppoe_start(sessp sp); 3274cf49a43SJulian Elischer static void sendpacket(sessp sp); 3284cf49a43SJulian Elischer static void pppoe_ticker(void *arg); 329816b834fSArchie Cobbs static const struct pppoe_tag *scan_tags(sessp sp, 330816b834fSArchie Cobbs const struct pppoe_hdr* ph); 331b58a8a3bSJulian Elischer static int pppoe_send_event(sessp sp, enum cmd cmdid); 3324cf49a43SJulian Elischer 3334cf49a43SJulian Elischer /************************************************************************* 3344cf49a43SJulian Elischer * Some basic utilities from the Linux version with author's permission.* 3354cf49a43SJulian Elischer * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 3364cf49a43SJulian Elischer ************************************************************************/ 3374cf49a43SJulian Elischer 3384cf49a43SJulian Elischer /* 3394cf49a43SJulian Elischer * Generate a new session id 3404adb13fdSJulian Elischer * XXX find out the FreeBSD locking scheme. 3414cf49a43SJulian Elischer */ 3424cf49a43SJulian Elischer static u_int16_t 3434cf49a43SJulian Elischer get_new_sid(node_p node) 3444cf49a43SJulian Elischer { 3454cf49a43SJulian Elischer static int pppoe_sid = 10; 3464cf49a43SJulian Elischer sessp sp; 3474cf49a43SJulian Elischer hook_p hook; 3484cf49a43SJulian Elischer u_int16_t val; 34930400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 3504cf49a43SJulian Elischer 351f2b9562cSGleb Smirnoff DBG; 3524cf49a43SJulian Elischer restart: 3534cf49a43SJulian Elischer val = pppoe_sid++; 3544cf49a43SJulian Elischer /* 3554cf49a43SJulian Elischer * Spec says 0xFFFF is reserved. 3564cf49a43SJulian Elischer * Also don't use 0x0000 3574cf49a43SJulian Elischer */ 3584cf49a43SJulian Elischer if (val == 0xffff) { 3594cf49a43SJulian Elischer pppoe_sid = 20; 3604cf49a43SJulian Elischer goto restart; 3614cf49a43SJulian Elischer } 3624cf49a43SJulian Elischer 3634cf49a43SJulian Elischer /* Check it isn't already in use */ 36430400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 3654cf49a43SJulian Elischer /* don't check special hooks */ 36630400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 36730400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 3684cf49a43SJulian Elischer continue; 36930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 3704cf49a43SJulian Elischer if (sp->Session_ID == val) 3714cf49a43SJulian Elischer goto restart; 3724cf49a43SJulian Elischer } 3734cf49a43SJulian Elischer 3744cf49a43SJulian Elischer return val; 3754cf49a43SJulian Elischer } 3764cf49a43SJulian Elischer 3774cf49a43SJulian Elischer 3784cf49a43SJulian Elischer /* 3794cf49a43SJulian Elischer * Return the location where the next tag can be put 3804cf49a43SJulian Elischer */ 381816b834fSArchie Cobbs static __inline const struct pppoe_tag* 382816b834fSArchie Cobbs next_tag(const struct pppoe_hdr* ph) 3834cf49a43SJulian Elischer { 384816b834fSArchie Cobbs return (const struct pppoe_tag*)(((const char*)&ph->tag[0]) 385816b834fSArchie Cobbs + ntohs(ph->length)); 3864cf49a43SJulian Elischer } 3874cf49a43SJulian Elischer 3884cf49a43SJulian Elischer /* 3894cf49a43SJulian Elischer * Look for a tag of a specific type 3904cf49a43SJulian Elischer * Don't trust any length the other end says. 3914cf49a43SJulian Elischer * but assume we already sanity checked ph->length. 3924cf49a43SJulian Elischer */ 393816b834fSArchie Cobbs static const struct pppoe_tag* 394816b834fSArchie Cobbs get_tag(const struct pppoe_hdr* ph, u_int16_t idx) 3954cf49a43SJulian Elischer { 396816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 397816b834fSArchie Cobbs const char *ptn; 398816b834fSArchie Cobbs const struct pppoe_tag *pt = &ph->tag[0]; 3994cf49a43SJulian Elischer /* 4004cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 4014cf49a43SJulian Elischer */ 402f2b9562cSGleb Smirnoff DBG; 403816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 4044cf49a43SJulian Elischer /* 4054cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 4064cf49a43SJulian Elischer */ 407816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 4084cf49a43SJulian Elischer if(ptn > end) 4094cf49a43SJulian Elischer return NULL; 4104cf49a43SJulian Elischer 4114cf49a43SJulian Elischer if(pt->tag_type == idx) 4124cf49a43SJulian Elischer return pt; 4134cf49a43SJulian Elischer 414816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 4154cf49a43SJulian Elischer } 4164cf49a43SJulian Elischer return NULL; 4174cf49a43SJulian Elischer } 4184cf49a43SJulian Elischer 4194cf49a43SJulian Elischer /************************************************************************** 4204cf49a43SJulian Elischer * inlines to initialise or add tags to a session's tag list, 4214cf49a43SJulian Elischer **************************************************************************/ 4224cf49a43SJulian Elischer /* 4234cf49a43SJulian Elischer * Initialise the session's tag list 4244cf49a43SJulian Elischer */ 4254cf49a43SJulian Elischer static void 4264cf49a43SJulian Elischer init_tags(sessp sp) 4274cf49a43SJulian Elischer { 428f2b9562cSGleb Smirnoff DBG; 4294cf49a43SJulian Elischer if(sp->neg == NULL) { 4304cf49a43SJulian Elischer printf("pppoe: asked to init NULL neg pointer\n"); 4314cf49a43SJulian Elischer return; 4324cf49a43SJulian Elischer } 4334cf49a43SJulian Elischer sp->neg->numtags = 0; 4344cf49a43SJulian Elischer } 4354cf49a43SJulian Elischer 4364cf49a43SJulian Elischer static void 437816b834fSArchie Cobbs insert_tag(sessp sp, const struct pppoe_tag *tp) 4384cf49a43SJulian Elischer { 4394cf49a43SJulian Elischer int i; 4404cf49a43SJulian Elischer negp neg; 4414cf49a43SJulian Elischer 442f2b9562cSGleb Smirnoff DBG; 4434cf49a43SJulian Elischer if((neg = sp->neg) == NULL) { 4444cf49a43SJulian Elischer printf("pppoe: asked to use NULL neg pointer\n"); 4454cf49a43SJulian Elischer return; 4464cf49a43SJulian Elischer } 4474cf49a43SJulian Elischer if ((i = neg->numtags++) < NUMTAGS) { 4484cf49a43SJulian Elischer neg->tags[i] = tp; 4494cf49a43SJulian Elischer } else { 4504cf49a43SJulian Elischer printf("pppoe: asked to add too many tags to packet\n"); 45112f035e0SJulian Elischer neg->numtags--; 4524cf49a43SJulian Elischer } 4534cf49a43SJulian Elischer } 4544cf49a43SJulian Elischer 4554cf49a43SJulian Elischer /* 4564cf49a43SJulian Elischer * Make up a packet, using the tags filled out for the session. 4574cf49a43SJulian Elischer * 4584cf49a43SJulian Elischer * Assume that the actual pppoe header and ethernet header 4594cf49a43SJulian Elischer * are filled out externally to this routine. 4604cf49a43SJulian Elischer * Also assume that neg->wh points to the correct 4614cf49a43SJulian Elischer * location at the front of the buffer space. 4624cf49a43SJulian Elischer */ 4634cf49a43SJulian Elischer static void 4644cf49a43SJulian Elischer make_packet(sessp sp) { 4654cf49a43SJulian Elischer struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 466816b834fSArchie Cobbs const struct pppoe_tag **tag; 4674cf49a43SJulian Elischer char *dp; 4684cf49a43SJulian Elischer int count; 4694cf49a43SJulian Elischer int tlen; 4704cf49a43SJulian Elischer u_int16_t length = 0; 4714cf49a43SJulian Elischer 472f2b9562cSGleb Smirnoff DBG; 4731e2510f8SJulian Elischer if ((sp->neg == NULL) || (sp->neg->m == NULL)) { 4744cf49a43SJulian Elischer printf("pppoe: make_packet called from wrong state\n"); 4754cf49a43SJulian Elischer } 4764cf49a43SJulian Elischer dp = (char *)wh->ph.tag; 4774cf49a43SJulian Elischer for (count = 0, tag = sp->neg->tags; 4784cf49a43SJulian Elischer ((count < sp->neg->numtags) && (count < NUMTAGS)); 4794cf49a43SJulian Elischer tag++, count++) { 4804cf49a43SJulian Elischer tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 4814cf49a43SJulian Elischer if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 4824cf49a43SJulian Elischer printf("pppoe: tags too long\n"); 4834cf49a43SJulian Elischer sp->neg->numtags = count; 4844cf49a43SJulian Elischer break; /* XXX chop off what's too long */ 4854cf49a43SJulian Elischer } 486816b834fSArchie Cobbs bcopy(*tag, (char *)dp, tlen); 4874cf49a43SJulian Elischer length += tlen; 4884cf49a43SJulian Elischer dp += tlen; 4894cf49a43SJulian Elischer } 4904cf49a43SJulian Elischer wh->ph.length = htons(length); 4914cf49a43SJulian Elischer sp->neg->m->m_len = length + sizeof(*wh); 4924cf49a43SJulian Elischer sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 4934cf49a43SJulian Elischer } 4944cf49a43SJulian Elischer 4954cf49a43SJulian Elischer /************************************************************************** 4964cf49a43SJulian Elischer * Routine to match a service offered * 4974cf49a43SJulian Elischer **************************************************************************/ 4984cf49a43SJulian Elischer /* 4994cf49a43SJulian Elischer * Find a hook that has a service string that matches that 5004cf49a43SJulian Elischer * we are seeking. for now use a simple string. 5014cf49a43SJulian Elischer * In the future we may need something like regexp(). 5024cf49a43SJulian Elischer * for testing allow a null string to match 1st found and a null service 5034cf49a43SJulian Elischer * to match all requests. Also make '*' do the same. 5044cf49a43SJulian Elischer */ 5059088fa05SBrian Somers 5069088fa05SBrian Somers #define NG_MATCH_EXACT 1 5079088fa05SBrian Somers #define NG_MATCH_ANY 2 5089088fa05SBrian Somers 5094cf49a43SJulian Elischer static hook_p 510816b834fSArchie Cobbs pppoe_match_svc(node_p node, const char *svc_name, int svc_len, int match) 5114cf49a43SJulian Elischer { 5124cf49a43SJulian Elischer sessp sp = NULL; 5134cf49a43SJulian Elischer negp neg = NULL; 51430400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 5159088fa05SBrian Somers hook_p allhook = NULL; 5164cf49a43SJulian Elischer hook_p hook; 5174cf49a43SJulian Elischer 518f2b9562cSGleb Smirnoff DBG; 51930400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5204cf49a43SJulian Elischer 5214cf49a43SJulian Elischer /* skip any hook that is debug or ethernet */ 52230400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 52330400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 5244cf49a43SJulian Elischer continue; 52530400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 5264cf49a43SJulian Elischer 5274cf49a43SJulian Elischer /* Skip any sessions which are not in LISTEN mode. */ 5284cf49a43SJulian Elischer if ( sp->state != PPPOE_LISTENING) 5294cf49a43SJulian Elischer continue; 5304cf49a43SJulian Elischer 5314cf49a43SJulian Elischer neg = sp->neg; 5324cf49a43SJulian Elischer 5334cf49a43SJulian Elischer /* Special case for a blank or "*" service name (wildcard) */ 5349088fa05SBrian Somers if (match == NG_MATCH_ANY && neg->service_len == 1 && 5359088fa05SBrian Somers neg->service.data[0] == '*') { 5369088fa05SBrian Somers allhook = hook; 5379088fa05SBrian Somers continue; 5384cf49a43SJulian Elischer } 5394cf49a43SJulian Elischer 5404cf49a43SJulian Elischer /* If the lengths don't match, that aint it. */ 5414cf49a43SJulian Elischer if (neg->service_len != svc_len) 5424cf49a43SJulian Elischer continue; 5434cf49a43SJulian Elischer 5444cf49a43SJulian Elischer /* An exact match? */ 5459088fa05SBrian Somers if (svc_len == 0) 5469088fa05SBrian Somers break; 5479088fa05SBrian Somers 5484cf49a43SJulian Elischer if (strncmp(svc_name, neg->service.data, svc_len) == 0) 5494cf49a43SJulian Elischer break; 5504cf49a43SJulian Elischer } 5519088fa05SBrian Somers return (hook ? hook : allhook); 5524cf49a43SJulian Elischer } 5534cf49a43SJulian Elischer /************************************************************************** 5544cf49a43SJulian Elischer * Routine to find a particular session that matches an incoming packet * 5554cf49a43SJulian Elischer **************************************************************************/ 5564cf49a43SJulian Elischer static hook_p 557816b834fSArchie Cobbs pppoe_findsession(node_p node, const struct pppoe_full_hdr *wh) 5584cf49a43SJulian Elischer { 5594cf49a43SJulian Elischer sessp sp = NULL; 5604cf49a43SJulian Elischer hook_p hook = NULL; 56130400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 562b86d0a9eSJulian Elischer u_int16_t session = ntohs(wh->ph.sid); 5634cf49a43SJulian Elischer 5644cf49a43SJulian Elischer /* 5654cf49a43SJulian Elischer * find matching peer/session combination. 5664cf49a43SJulian Elischer */ 567f2b9562cSGleb Smirnoff DBG; 56830400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5694cf49a43SJulian Elischer /* don't check special hooks */ 57030400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 57130400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 5724cf49a43SJulian Elischer continue; 5734cf49a43SJulian Elischer } 57430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 5754cf49a43SJulian Elischer if ( ( (sp->state == PPPOE_CONNECTED) 5764cf49a43SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED) ) 5774cf49a43SJulian Elischer && (sp->Session_ID == session) 5784cf49a43SJulian Elischer && (bcmp(sp->pkt_hdr.eh.ether_dhost, 5794cf49a43SJulian Elischer wh->eh.ether_shost, 5804cf49a43SJulian Elischer ETHER_ADDR_LEN)) == 0) { 5814cf49a43SJulian Elischer break; 5824cf49a43SJulian Elischer } 5834cf49a43SJulian Elischer } 5844cf49a43SJulian Elischer return (hook); 5854cf49a43SJulian Elischer } 5864cf49a43SJulian Elischer 5874cf49a43SJulian Elischer static hook_p 588816b834fSArchie Cobbs pppoe_finduniq(node_p node, const struct pppoe_tag *tag) 5894cf49a43SJulian Elischer { 5904cf49a43SJulian Elischer hook_p hook = NULL; 59130400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 5924cf49a43SJulian Elischer union uniq uniq; 5934cf49a43SJulian Elischer 594f2b9562cSGleb Smirnoff DBG; 5954cf49a43SJulian Elischer bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 5964cf49a43SJulian Elischer /* cycle through all known hooks */ 59730400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 5984cf49a43SJulian Elischer /* don't check special hooks */ 59930400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 60030400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) 6014cf49a43SJulian Elischer continue; 60230400f03SJulian Elischer if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 6034cf49a43SJulian Elischer break; 6044cf49a43SJulian Elischer } 6054cf49a43SJulian Elischer return (hook); 6064cf49a43SJulian Elischer } 6074cf49a43SJulian Elischer 6084cf49a43SJulian Elischer /************************************************************************** 6094cf49a43SJulian Elischer * start of Netgraph entrypoints * 6104cf49a43SJulian Elischer **************************************************************************/ 6114cf49a43SJulian Elischer 6124cf49a43SJulian Elischer /* 6134cf49a43SJulian Elischer * Allocate the private data structure and the generic node 6144cf49a43SJulian Elischer * and link them together. 6154cf49a43SJulian Elischer * 6164cf49a43SJulian Elischer * ng_make_node_common() returns with a generic node struct 6174cf49a43SJulian Elischer * with a single reference for us.. we transfer it to the 6184cf49a43SJulian Elischer * private structure.. when we free the private struct we must 6194cf49a43SJulian Elischer * unref the node so it gets freed too. 6204cf49a43SJulian Elischer */ 6214cf49a43SJulian Elischer static int 622069154d5SJulian Elischer ng_pppoe_constructor(node_p node) 6234cf49a43SJulian Elischer { 6244cf49a43SJulian Elischer priv_p privdata; 6254cf49a43SJulian Elischer 626f2b9562cSGleb Smirnoff DBG; 6274cf49a43SJulian Elischer /* Initialize private descriptor */ 6289c8c302fSJulian Elischer MALLOC(privdata, priv_p, sizeof(*privdata), M_NETGRAPH_PPPOE, 62999cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 6304cf49a43SJulian Elischer if (privdata == NULL) 6314cf49a43SJulian Elischer return (ENOMEM); 6324cf49a43SJulian Elischer 6334cf49a43SJulian Elischer /* Link structs together; this counts as our one reference to *nodep */ 63430400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, privdata); 635069154d5SJulian Elischer privdata->node = node; 636fdc755d1SGleb Smirnoff 637fdc755d1SGleb Smirnoff /* Initialize to standard mode (the first one in ng_pppoe_modes[]). */ 638fdc755d1SGleb Smirnoff privdata->mode = sysctl_mode; 639fdc755d1SGleb Smirnoff 6404cf49a43SJulian Elischer return (0); 6414cf49a43SJulian Elischer } 6424cf49a43SJulian Elischer 6434cf49a43SJulian Elischer /* 6444cf49a43SJulian Elischer * Give our ok for a hook to be added... 6454cf49a43SJulian Elischer * point the hook's private info to the hook structure. 6464cf49a43SJulian Elischer * 6474cf49a43SJulian Elischer * The following hook names are special: 6484cf49a43SJulian Elischer * Ethernet: the hook that should be connected to a NIC. 6494cf49a43SJulian Elischer * debug: copies of data sent out here (when I write the code). 650859a4d16SJulian Elischer * All other hook names need only be unique. (the framework checks this). 6514cf49a43SJulian Elischer */ 6524cf49a43SJulian Elischer static int 6538876b55dSJulian Elischer ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 6544cf49a43SJulian Elischer { 65530400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 6564cf49a43SJulian Elischer sessp sp; 6574cf49a43SJulian Elischer 658f2b9562cSGleb Smirnoff DBG; 6594cf49a43SJulian Elischer if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 6604cf49a43SJulian Elischer privp->ethernet_hook = hook; 66130400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->ethernet_hook); 6624cf49a43SJulian Elischer } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 6634cf49a43SJulian Elischer privp->debug_hook = hook; 66430400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, &privp->debug_hook); 6654cf49a43SJulian Elischer } else { 6664cf49a43SJulian Elischer /* 6674cf49a43SJulian Elischer * Any other unique name is OK. 6684cf49a43SJulian Elischer * The infrastructure has already checked that it's unique, 6694cf49a43SJulian Elischer * so just allocate it and hook it in. 6704cf49a43SJulian Elischer */ 6719c8c302fSJulian Elischer MALLOC(sp, sessp, sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 6724cf49a43SJulian Elischer if (sp == NULL) { 6734cf49a43SJulian Elischer return (ENOMEM); 6744cf49a43SJulian Elischer } 6754cf49a43SJulian Elischer 67630400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, sp); 6774cf49a43SJulian Elischer sp->hook = hook; 6784cf49a43SJulian Elischer } 6794cf49a43SJulian Elischer return(0); 6804cf49a43SJulian Elischer } 6814cf49a43SJulian Elischer 6824cf49a43SJulian Elischer /* 6834cf49a43SJulian Elischer * Get a netgraph control message. 6844cf49a43SJulian Elischer * Check it is one we understand. If needed, send a response. 6854cf49a43SJulian Elischer * We sometimes save the address for an async action later. 6864cf49a43SJulian Elischer * Always free the message. 6874cf49a43SJulian Elischer */ 6884cf49a43SJulian Elischer static int 689069154d5SJulian Elischer ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 6904cf49a43SJulian Elischer { 69130400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 6928876b55dSJulian Elischer struct ngpppoe_init_data *ourmsg = NULL; 6934cf49a43SJulian Elischer struct ng_mesg *resp = NULL; 6944cf49a43SJulian Elischer int error = 0; 6954cf49a43SJulian Elischer hook_p hook = NULL; 6964cf49a43SJulian Elischer sessp sp = NULL; 6974cf49a43SJulian Elischer negp neg = NULL; 698069154d5SJulian Elischer struct ng_mesg *msg; 6994cf49a43SJulian Elischer 700f2b9562cSGleb Smirnoff DBG; 701069154d5SJulian Elischer NGI_GET_MSG(item, msg); 7024cf49a43SJulian Elischer /* Deal with message according to cookie and command */ 7034cf49a43SJulian Elischer switch (msg->header.typecookie) { 7044cf49a43SJulian Elischer case NGM_PPPOE_COOKIE: 7054cf49a43SJulian Elischer switch (msg->header.cmd) { 7064cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 7074cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 7084cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 709859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 71027121ab1SBrian Somers ourmsg = (struct ngpppoe_init_data *)msg->data; 71127121ab1SBrian Somers if (msg->header.arglen < sizeof(*ourmsg)) { 71227121ab1SBrian Somers printf("pppoe: init data too small\n"); 7134cf49a43SJulian Elischer LEAVE(EMSGSIZE); 7144cf49a43SJulian Elischer } 71576a70671SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) > 71676a70671SBrian Somers PPPOE_SERVICE_NAME_SIZE) { 71776a70671SBrian Somers printf("pppoe_rcvmsg: service name too big"); 7184cf49a43SJulian Elischer LEAVE(EMSGSIZE); 7194cf49a43SJulian Elischer } 72027121ab1SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) < 72127121ab1SBrian Somers ourmsg->data_len) { 72227121ab1SBrian Somers printf("pppoe: init data has bad length," 723a2a6abd5SJohn Baldwin " %d should be %zd\n", ourmsg->data_len, 72427121ab1SBrian Somers msg->header.arglen - sizeof (*ourmsg)); 72576a70671SBrian Somers LEAVE(EMSGSIZE); 72676a70671SBrian Somers } 72776a70671SBrian Somers 7284cf49a43SJulian Elischer /* make sure strcmp will terminate safely */ 7294cf49a43SJulian Elischer ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 7304cf49a43SJulian Elischer 7314cf49a43SJulian Elischer /* cycle through all known hooks */ 73230400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 73330400f03SJulian Elischer if (NG_HOOK_NAME(hook) 73430400f03SJulian Elischer && strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 0) 7354cf49a43SJulian Elischer break; 7364cf49a43SJulian Elischer } 7374cf49a43SJulian Elischer if (hook == NULL) { 7384cf49a43SJulian Elischer LEAVE(ENOENT); 7394cf49a43SJulian Elischer } 74030400f03SJulian Elischer if ((NG_HOOK_PRIVATE(hook) == &privp->debug_hook) 74130400f03SJulian Elischer || (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook)) { 7424cf49a43SJulian Elischer LEAVE(EINVAL); 7434cf49a43SJulian Elischer } 74430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 745859a4d16SJulian Elischer 7469088fa05SBrian Somers if (msg->header.cmd == NGM_PPPOE_LISTEN) { 7479088fa05SBrian Somers /* 7489088fa05SBrian Somers * Ensure we aren't already listening for this 7499088fa05SBrian Somers * service. 7509088fa05SBrian Somers */ 7519088fa05SBrian Somers if (pppoe_match_svc(node, ourmsg->data, 7529088fa05SBrian Somers ourmsg->data_len, NG_MATCH_EXACT) != NULL) { 7539088fa05SBrian Somers LEAVE(EEXIST); 7549088fa05SBrian Somers } 7559088fa05SBrian Somers } 7569088fa05SBrian Somers 757859a4d16SJulian Elischer /* 758859a4d16SJulian Elischer * PPPOE_SERVICE advertisments are set up 759859a4d16SJulian Elischer * on sessions that are in PRIMED state. 760859a4d16SJulian Elischer */ 761859a4d16SJulian Elischer if (msg->header.cmd == NGM_PPPOE_SERVICE) { 762859a4d16SJulian Elischer break; 763859a4d16SJulian Elischer } 7644cf49a43SJulian Elischer if (sp->state |= PPPOE_SNONE) { 7654cf49a43SJulian Elischer printf("pppoe: Session already active\n"); 7664cf49a43SJulian Elischer LEAVE(EISCONN); 7674cf49a43SJulian Elischer } 7681e2510f8SJulian Elischer 7694cf49a43SJulian Elischer /* 7704cf49a43SJulian Elischer * set up prototype header 7714cf49a43SJulian Elischer */ 7729c8c302fSJulian Elischer MALLOC(neg, negp, sizeof(*neg), M_NETGRAPH_PPPOE, 77399cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 7744cf49a43SJulian Elischer 7754cf49a43SJulian Elischer if (neg == NULL) { 7764cf49a43SJulian Elischer printf("pppoe: Session out of memory\n"); 7774cf49a43SJulian Elischer LEAVE(ENOMEM); 7784cf49a43SJulian Elischer } 779a163d034SWarner Losh MGETHDR(neg->m, M_DONTWAIT, MT_DATA); 7804cf49a43SJulian Elischer if(neg->m == NULL) { 7811e2510f8SJulian Elischer printf("pppoe: Session out of mbufs\n"); 7829c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7834cf49a43SJulian Elischer LEAVE(ENOBUFS); 7844cf49a43SJulian Elischer } 7854cf49a43SJulian Elischer neg->m->m_pkthdr.rcvif = NULL; 786a163d034SWarner Losh MCLGET(neg->m, M_DONTWAIT); 7874cf49a43SJulian Elischer if ((neg->m->m_flags & M_EXT) == 0) { 7881e2510f8SJulian Elischer printf("pppoe: Session out of mcls\n"); 7894cf49a43SJulian Elischer m_freem(neg->m); 7909c8c302fSJulian Elischer FREE(neg, M_NETGRAPH_PPPOE); 7914cf49a43SJulian Elischer LEAVE(ENOBUFS); 7924cf49a43SJulian Elischer } 7934cf49a43SJulian Elischer sp->neg = neg; 7941e2510f8SJulian Elischer callout_handle_init( &neg->timeout_handle); 7954cf49a43SJulian Elischer neg->m->m_len = sizeof(struct pppoe_full_hdr); 7964cf49a43SJulian Elischer neg->pkt = mtod(neg->m, union packet*); 797fdc755d1SGleb Smirnoff memcpy((void *)&neg->pkt->pkt_header.eh, 798fdc755d1SGleb Smirnoff (const void *)privp->mode->eh_prototype, 799fdc755d1SGleb Smirnoff sizeof(struct ether_header)); 8004cf49a43SJulian Elischer neg->pkt->pkt_header.ph.ver = 0x1; 8014cf49a43SJulian Elischer neg->pkt->pkt_header.ph.type = 0x1; 8024cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 0x0000; 8034cf49a43SJulian Elischer neg->timeout = 0; 8044cf49a43SJulian Elischer 805069154d5SJulian Elischer sp->creator = NGI_RETADDR(item); 8064cf49a43SJulian Elischer } 8074cf49a43SJulian Elischer switch (msg->header.cmd) { 8084cf49a43SJulian Elischer case NGM_PPPOE_GET_STATUS: 8094cf49a43SJulian Elischer { 8108876b55dSJulian Elischer struct ngpppoestat *stats; 8114cf49a43SJulian Elischer 8124cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 8134cf49a43SJulian Elischer if (!resp) { 8144cf49a43SJulian Elischer LEAVE(ENOMEM); 8154cf49a43SJulian Elischer } 8168876b55dSJulian Elischer stats = (struct ngpppoestat *) resp->data; 8174cf49a43SJulian Elischer stats->packets_in = privp->packets_in; 8184cf49a43SJulian Elischer stats->packets_out = privp->packets_out; 8194cf49a43SJulian Elischer break; 8204cf49a43SJulian Elischer } 8214cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 8224cf49a43SJulian Elischer /* 8234cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8244cf49a43SJulian Elischer * Send a PADI request, and start the timeout logic. 8254cf49a43SJulian Elischer * Store the originator of this message so we can send 8264cf49a43SJulian Elischer * a success of fail message to them later. 8274cf49a43SJulian Elischer * Move the session to SINIT 8284cf49a43SJulian Elischer * Set up the session to the correct state and 8294cf49a43SJulian Elischer * start it. 8304cf49a43SJulian Elischer */ 8314cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 83227121ab1SBrian Somers neg->service.hdr.tag_len = 83327121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 83427121ab1SBrian Somers if (ourmsg->data_len) 83527121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 83627121ab1SBrian Somers ourmsg->data_len); 83727121ab1SBrian Somers neg->service_len = ourmsg->data_len; 8384cf49a43SJulian Elischer pppoe_start(sp); 8394cf49a43SJulian Elischer break; 8404cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 8414cf49a43SJulian Elischer /* 8424cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8434cf49a43SJulian Elischer * Install the service matching string. 8444cf49a43SJulian Elischer * Store the originator of this message so we can send 8454cf49a43SJulian Elischer * a success of fail message to them later. 8464cf49a43SJulian Elischer * Move the hook to 'LISTENING' 8474cf49a43SJulian Elischer */ 8484cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 84927121ab1SBrian Somers neg->service.hdr.tag_len = 85027121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 8511e2510f8SJulian Elischer 85227121ab1SBrian Somers if (ourmsg->data_len) 85327121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 85427121ab1SBrian Somers ourmsg->data_len); 85527121ab1SBrian Somers neg->service_len = ourmsg->data_len; 8564cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADT_CODE; 8574cf49a43SJulian Elischer /* 8584cf49a43SJulian Elischer * wait for PADI packet coming from ethernet 8594cf49a43SJulian Elischer */ 8604cf49a43SJulian Elischer sp->state = PPPOE_LISTENING; 8614cf49a43SJulian Elischer break; 8624cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 8634cf49a43SJulian Elischer /* 8644cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8654cf49a43SJulian Elischer * Store the originator of this message so we can send 8664cf49a43SJulian Elischer * a success of fail message to them later. 8674cf49a43SJulian Elischer * Store the AC-Name given and go to PRIMED. 8684cf49a43SJulian Elischer */ 8694cf49a43SJulian Elischer neg->ac_name.hdr.tag_type = PTT_AC_NAME; 87027121ab1SBrian Somers neg->ac_name.hdr.tag_len = 87127121ab1SBrian Somers htons((u_int16_t)ourmsg->data_len); 87227121ab1SBrian Somers if (ourmsg->data_len) 87327121ab1SBrian Somers bcopy(ourmsg->data, neg->ac_name.data, 87427121ab1SBrian Somers ourmsg->data_len); 87527121ab1SBrian Somers neg->ac_name_len = ourmsg->data_len; 8764cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 8774cf49a43SJulian Elischer /* 8784cf49a43SJulian Elischer * Wait for PADI packet coming from hook 8794cf49a43SJulian Elischer */ 8804cf49a43SJulian Elischer sp->state = PPPOE_PRIMED; 8814cf49a43SJulian Elischer break; 882859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 883859a4d16SJulian Elischer /* 884859a4d16SJulian Elischer * Check the session is primed. 885859a4d16SJulian Elischer * for now just allow ONE service to be advertised. 886859a4d16SJulian Elischer * If you do it twice you just overwrite. 887859a4d16SJulian Elischer */ 8885078fb0bSJulian Elischer if (sp->state != PPPOE_PRIMED) { 889859a4d16SJulian Elischer printf("pppoe: Session not primed\n"); 890859a4d16SJulian Elischer LEAVE(EISCONN); 891859a4d16SJulian Elischer } 8920069b9cbSJulian Elischer neg = sp->neg; 893859a4d16SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 894859a4d16SJulian Elischer neg->service.hdr.tag_len = 895859a4d16SJulian Elischer htons((u_int16_t)ourmsg->data_len); 896859a4d16SJulian Elischer 897859a4d16SJulian Elischer if (ourmsg->data_len) 898859a4d16SJulian Elischer bcopy(ourmsg->data, neg->service.data, 899859a4d16SJulian Elischer ourmsg->data_len); 900859a4d16SJulian Elischer neg->service_len = ourmsg->data_len; 901859a4d16SJulian Elischer break; 902fdc755d1SGleb Smirnoff case NGM_PPPOE_SETMODE: 903fdc755d1SGleb Smirnoff { 904fdc755d1SGleb Smirnoff const struct ng_pppoe_mode_t *mode; 905fdc755d1SGleb Smirnoff char *s; 906fdc755d1SGleb Smirnoff size_t len; 907fdc755d1SGleb Smirnoff 908fdc755d1SGleb Smirnoff if (msg->header.arglen == 0) 909fdc755d1SGleb Smirnoff LEAVE(EINVAL); 910fdc755d1SGleb Smirnoff 911fdc755d1SGleb Smirnoff s = (char *)msg->data; 912fdc755d1SGleb Smirnoff len = msg->header.arglen - 1; 913fdc755d1SGleb Smirnoff 914fdc755d1SGleb Smirnoff /* Search for matching mode string */ 915fdc755d1SGleb Smirnoff for (mode = ng_pppoe_modes; mode->id != 0; mode++ ) 916fdc755d1SGleb Smirnoff if ((strlen(mode->name) == len) && 917fdc755d1SGleb Smirnoff !strncmp(mode->name, s, len)) 918fdc755d1SGleb Smirnoff break; /* found */ 919fdc755d1SGleb Smirnoff 920fdc755d1SGleb Smirnoff if (mode->id != 0) 921fdc755d1SGleb Smirnoff privp->mode = mode; 922fdc755d1SGleb Smirnoff else 923fdc755d1SGleb Smirnoff LEAVE(EINVAL); 924fdc755d1SGleb Smirnoff break; 925fdc755d1SGleb Smirnoff } 926fdc755d1SGleb Smirnoff case NGM_PPPOE_GETMODE: 927fdc755d1SGleb Smirnoff NG_MKRESPONSE(resp, msg, strlen(privp->mode->name) + 1, 928fdc755d1SGleb Smirnoff M_NOWAIT); 929fdc755d1SGleb Smirnoff if (resp == NULL) 930fdc755d1SGleb Smirnoff LEAVE(ENOMEM); 931fdc755d1SGleb Smirnoff strlcpy((char *)resp->data, privp->mode->name, 932fdc755d1SGleb Smirnoff strlen(privp->mode->name) + 1); 933fdc755d1SGleb Smirnoff break; 9344cf49a43SJulian Elischer default: 9354cf49a43SJulian Elischer LEAVE(EINVAL); 9364cf49a43SJulian Elischer } 9374cf49a43SJulian Elischer break; 9384cf49a43SJulian Elischer default: 9394cf49a43SJulian Elischer LEAVE(EINVAL); 9404cf49a43SJulian Elischer } 9414cf49a43SJulian Elischer 9424cf49a43SJulian Elischer /* Take care of synchronous response, if any */ 9434cf49a43SJulian Elischer quit: 944069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 945069154d5SJulian Elischer /* Free the message and return */ 946069154d5SJulian Elischer NG_FREE_MSG(msg); 9474cf49a43SJulian Elischer return(error); 9484cf49a43SJulian Elischer } 9494cf49a43SJulian Elischer 9501e2510f8SJulian Elischer /* 9511e2510f8SJulian Elischer * Start a client into the first state. A separate function because 9521e2510f8SJulian Elischer * it can be needed if the negotiation times out. 9531e2510f8SJulian Elischer */ 9544cf49a43SJulian Elischer static void 9554cf49a43SJulian Elischer pppoe_start(sessp sp) 9564cf49a43SJulian Elischer { 957fdc755d1SGleb Smirnoff priv_p privp = NG_NODE_PRIVATE(NG_PPPOE_SESSION_NODE(sp)); 9584cf49a43SJulian Elischer struct { 9594cf49a43SJulian Elischer struct pppoe_tag hdr; 9604cf49a43SJulian Elischer union uniq data; 9614f492bfaSAlfred Perlstein } __packed uniqtag; 9624cf49a43SJulian Elischer 9634cf49a43SJulian Elischer /* 9644cf49a43SJulian Elischer * kick the state machine into starting up 9654cf49a43SJulian Elischer */ 966f2b9562cSGleb Smirnoff DBG; 9674cf49a43SJulian Elischer sp->state = PPPOE_SINIT; 968fdc755d1SGleb Smirnoff /* Reset the packet header to broadcast. Since we are in a client 969fdc755d1SGleb Smirnoff * mode use configured ethertype. */ 970fdc755d1SGleb Smirnoff memcpy((void *)&sp->neg->pkt->pkt_header.eh, 971fdc755d1SGleb Smirnoff (const void *)privp->mode->eh_prototype, 972fdc755d1SGleb Smirnoff sizeof(struct ether_header)); 9731e2510f8SJulian Elischer sp->neg->pkt->pkt_header.ph.code = PADI_CODE; 9744cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 9754cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 9764cf49a43SJulian Elischer uniqtag.data.pointer = sp; 9774cf49a43SJulian Elischer init_tags(sp); 9781f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 9797ccbb17bSJulian Elischer insert_tag(sp, &sp->neg->service.hdr); 9804cf49a43SJulian Elischer make_packet(sp); 9814cf49a43SJulian Elischer sendpacket(sp); 9824cf49a43SJulian Elischer } 9834cf49a43SJulian Elischer 984c48a0b5fSBrian Somers static int 985816b834fSArchie Cobbs send_acname(sessp sp, const struct pppoe_tag *tag) 986c48a0b5fSBrian Somers { 9879e6798e7SBrian Somers int error, tlen; 988c48a0b5fSBrian Somers struct ng_mesg *msg; 989c48a0b5fSBrian Somers struct ngpppoe_sts *sts; 990c48a0b5fSBrian Somers 991c48a0b5fSBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME, 992c48a0b5fSBrian Somers sizeof(struct ngpppoe_sts), M_NOWAIT); 993c48a0b5fSBrian Somers if (msg == NULL) 994c48a0b5fSBrian Somers return (ENOMEM); 995c48a0b5fSBrian Somers 996c48a0b5fSBrian Somers sts = (struct ngpppoe_sts *)msg->data; 99787e2c66aSHartmut Brandt tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len)); 9989e6798e7SBrian Somers strncpy(sts->hook, tag->tag_data, tlen); 9999e6798e7SBrian Somers sts->hook[tlen] = '\0'; 1000facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1001c48a0b5fSBrian Somers 1002c48a0b5fSBrian Somers return (error); 1003c48a0b5fSBrian Somers } 1004c48a0b5fSBrian Somers 100587c4cce0SBrian Somers static int 100687c4cce0SBrian Somers send_sessionid(sessp sp) 100787c4cce0SBrian Somers { 100887c4cce0SBrian Somers int error; 100987c4cce0SBrian Somers struct ng_mesg *msg; 101087c4cce0SBrian Somers 101187c4cce0SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID, 101287c4cce0SBrian Somers sizeof(u_int16_t), M_NOWAIT); 101387c4cce0SBrian Somers if (msg == NULL) 101487c4cce0SBrian Somers return (ENOMEM); 101587c4cce0SBrian Somers 101687c4cce0SBrian Somers *(u_int16_t *)msg->data = sp->Session_ID; 1017facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 101887c4cce0SBrian Somers 101987c4cce0SBrian Somers return (error); 102087c4cce0SBrian Somers } 102187c4cce0SBrian Somers 10224cf49a43SJulian Elischer /* 10234cf49a43SJulian Elischer * Receive data, and do something with it. 10243ca24c28SJulian Elischer * The caller will never free m, so if we use up this data 10253ca24c28SJulian Elischer * or abort we must free it. 10264cf49a43SJulian Elischer */ 10274cf49a43SJulian Elischer static int 1028069154d5SJulian Elischer ng_pppoe_rcvdata(hook_p hook, item_p item) 10294cf49a43SJulian Elischer { 103030400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 103130400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 103230400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 1033816b834fSArchie Cobbs const struct pppoe_full_hdr *wh; 1034816b834fSArchie Cobbs const struct pppoe_hdr *ph; 10354cf49a43SJulian Elischer int error = 0; 10364cf49a43SJulian Elischer u_int16_t session; 10374cf49a43SJulian Elischer u_int16_t length; 10384cf49a43SJulian Elischer u_int8_t code; 1039816b834fSArchie Cobbs const struct pppoe_tag *utag = NULL, *tag = NULL; 10404cf49a43SJulian Elischer hook_p sendhook; 10414cf49a43SJulian Elischer struct { 10424cf49a43SJulian Elischer struct pppoe_tag hdr; 10434cf49a43SJulian Elischer union uniq data; 10444f492bfaSAlfred Perlstein } __packed uniqtag; 10454cf49a43SJulian Elischer negp neg = NULL; 1046069154d5SJulian Elischer struct mbuf *m; 10474cf49a43SJulian Elischer 1048f2b9562cSGleb Smirnoff DBG; 1049069154d5SJulian Elischer NGI_GET_M(item, m); 105030400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 10514cf49a43SJulian Elischer /* 10524cf49a43SJulian Elischer * Data from the debug hook gets sent without modification 10534cf49a43SJulian Elischer * straight to the ethernet. 10544cf49a43SJulian Elischer */ 105530400f03SJulian Elischer NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 10564cf49a43SJulian Elischer privp->packets_out++; 105730400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 10584cf49a43SJulian Elischer /* 10594cf49a43SJulian Elischer * Incoming data. 10604cf49a43SJulian Elischer * Dig out various fields from the packet. 10614cf49a43SJulian Elischer * use them to decide where to send it. 10624cf49a43SJulian Elischer */ 10634cf49a43SJulian Elischer 10644cf49a43SJulian Elischer privp->packets_in++; 10650c65c135SJulian Elischer if( m->m_len < sizeof(*wh)) { 10660c65c135SJulian Elischer m = m_pullup(m, sizeof(*wh)); /* Checks length */ 10674cf49a43SJulian Elischer if (m == NULL) { 1068b86d0a9eSJulian Elischer printf("couldn't m_pullup\n"); 10694cf49a43SJulian Elischer LEAVE(ENOBUFS); 10704cf49a43SJulian Elischer } 10710c65c135SJulian Elischer } 10724cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 10734cf49a43SJulian Elischer length = ntohs(wh->ph.length); 10740c65c135SJulian Elischer switch(wh->eh.ether_type) { 1075fdc755d1SGleb Smirnoff case ETHERTYPE_PPPOE_STUPID_DISC: /* fall through */ 10764cf49a43SJulian Elischer case ETHERTYPE_PPPOE_DISC: 10774cf49a43SJulian Elischer /* 1078bdaf2e81SJulian Elischer * We need to try to make sure that the tag area 1079bdaf2e81SJulian Elischer * is contiguous, or we could wander off the end 10804cf49a43SJulian Elischer * of a buffer and make a mess. 10814cf49a43SJulian Elischer * (Linux wouldn't have this problem). 10824cf49a43SJulian Elischer */ 10830c65c135SJulian Elischer if (m->m_pkthdr.len <= MHLEN) { 10840c65c135SJulian Elischer if( m->m_len < m->m_pkthdr.len) { 10850c65c135SJulian Elischer m = m_pullup(m, m->m_pkthdr.len); 10860c65c135SJulian Elischer if (m == NULL) { 10870c65c135SJulian Elischer printf("couldn't m_pullup\n"); 10880c65c135SJulian Elischer LEAVE(ENOBUFS); 10890c65c135SJulian Elischer } 10900c65c135SJulian Elischer } 10910c65c135SJulian Elischer } 10924cf49a43SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 10934cf49a43SJulian Elischer /* 10944cf49a43SJulian Elischer * It's not all in one piece. 10954cf49a43SJulian Elischer * We need to do extra work. 1096069154d5SJulian Elischer * Put it into a cluster. 10974cf49a43SJulian Elischer */ 1098069154d5SJulian Elischer struct mbuf *n; 1099a163d034SWarner Losh n = m_dup(m, M_DONTWAIT); 1100069154d5SJulian Elischer m_freem(m); 1101069154d5SJulian Elischer m = n; 1102069154d5SJulian Elischer if (m) { 1103069154d5SJulian Elischer /* just check we got a cluster */ 1104069154d5SJulian Elischer if (m->m_len != m->m_pkthdr.len) { 1105069154d5SJulian Elischer m_freem(m); 1106069154d5SJulian Elischer m = NULL; 1107069154d5SJulian Elischer } 1108069154d5SJulian Elischer } 1109069154d5SJulian Elischer if (m == NULL) { 11104cf49a43SJulian Elischer printf("packet fragmented\n"); 1111b86d0a9eSJulian Elischer LEAVE(EMSGSIZE); 11124cf49a43SJulian Elischer } 1113069154d5SJulian Elischer } 1114069154d5SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 1115069154d5SJulian Elischer length = ntohs(wh->ph.length); 1116069154d5SJulian Elischer ph = &wh->ph; 1117069154d5SJulian Elischer session = ntohs(wh->ph.sid); 1118069154d5SJulian Elischer code = wh->ph.code; 11194cf49a43SJulian Elischer 11204cf49a43SJulian Elischer switch(code) { 11214cf49a43SJulian Elischer case PADI_CODE: 11224cf49a43SJulian Elischer /* 11234cf49a43SJulian Elischer * We are a server: 11244cf49a43SJulian Elischer * Look for a hook with the required service 11254cf49a43SJulian Elischer * and send the ENTIRE packet up there. 11264cf49a43SJulian Elischer * It should come back to a new hook in 11274cf49a43SJulian Elischer * PRIMED state. Look there for further 11284cf49a43SJulian Elischer * processing. 11294cf49a43SJulian Elischer */ 11304cf49a43SJulian Elischer tag = get_tag(ph, PTT_SRV_NAME); 11314cf49a43SJulian Elischer if (tag == NULL) { 1132b86d0a9eSJulian Elischer printf("no service tag\n"); 11334cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11344cf49a43SJulian Elischer } 113530400f03SJulian Elischer sendhook = pppoe_match_svc(NG_HOOK_NODE(hook), 11369088fa05SBrian Somers tag->tag_data, ntohs(tag->tag_len), 11379088fa05SBrian Somers NG_MATCH_ANY); 11384cf49a43SJulian Elischer if (sendhook) { 1139069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, 1140069154d5SJulian Elischer sendhook, m); 11414cf49a43SJulian Elischer } else { 11424cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11434cf49a43SJulian Elischer } 11444cf49a43SJulian Elischer break; 11454cf49a43SJulian Elischer case PADO_CODE: 11464cf49a43SJulian Elischer /* 11474cf49a43SJulian Elischer * We are a client: 11484cf49a43SJulian Elischer * Use the host_uniq tag to find the 11494cf49a43SJulian Elischer * hook this is in response to. 1150b86d0a9eSJulian Elischer * Received #2, now send #3 11514cf49a43SJulian Elischer * For now simply accept the first we receive. 11524cf49a43SJulian Elischer */ 11531f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 11541f89d938SJulian Elischer if ((utag == NULL) 11551f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 1156b86d0a9eSJulian Elischer printf("no host unique field\n"); 11574cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11584cf49a43SJulian Elischer } 11594cf49a43SJulian Elischer 11601f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 11614cf49a43SJulian Elischer if (sendhook == NULL) { 1162b86d0a9eSJulian Elischer printf("no matching session\n"); 11634cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11644cf49a43SJulian Elischer } 11654cf49a43SJulian Elischer 11664cf49a43SJulian Elischer /* 11674cf49a43SJulian Elischer * Check the session is in the right state. 11684cf49a43SJulian Elischer * It needs to be in PPPOE_SINIT. 11694cf49a43SJulian Elischer */ 117030400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 11714cf49a43SJulian Elischer if (sp->state != PPPOE_SINIT) { 1172b86d0a9eSJulian Elischer printf("session in wrong state\n"); 11734cf49a43SJulian Elischer LEAVE(ENETUNREACH); 11744cf49a43SJulian Elischer } 11754cf49a43SJulian Elischer neg = sp->neg; 11764cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 11774cf49a43SJulian Elischer neg->timeout_handle); 11784cf49a43SJulian Elischer 11794cf49a43SJulian Elischer /* 11804cf49a43SJulian Elischer * This is the first time we hear 11814cf49a43SJulian Elischer * from the server, so note it's 11824cf49a43SJulian Elischer * unicast address, replacing the 11834cf49a43SJulian Elischer * broadcast address . 11844cf49a43SJulian Elischer */ 11854cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 11864cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 11874cf49a43SJulian Elischer ETHER_ADDR_LEN); 11884cf49a43SJulian Elischer neg->timeout = 0; 11894cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADR_CODE; 11904cf49a43SJulian Elischer init_tags(sp); 11917ccbb17bSJulian Elischer insert_tag(sp, utag); /* Host Unique */ 11921f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1193b86d0a9eSJulian Elischer insert_tag(sp, tag); /* return cookie */ 1194c48a0b5fSBrian Somers if ((tag = get_tag(ph, PTT_AC_NAME))) { 11951f89d938SJulian Elischer insert_tag(sp, tag); /* return it */ 1196c48a0b5fSBrian Somers send_acname(sp, tag); 1197c48a0b5fSBrian Somers } 11987ccbb17bSJulian Elischer insert_tag(sp, &neg->service.hdr); /* Service */ 11994cf49a43SJulian Elischer scan_tags(sp, ph); 12004cf49a43SJulian Elischer make_packet(sp); 12014cf49a43SJulian Elischer sp->state = PPPOE_SREQ; 12024cf49a43SJulian Elischer sendpacket(sp); 12034cf49a43SJulian Elischer break; 12044cf49a43SJulian Elischer case PADR_CODE: 12054cf49a43SJulian Elischer 12064cf49a43SJulian Elischer /* 12074cf49a43SJulian Elischer * We are a server: 12084cf49a43SJulian Elischer * Use the ac_cookie tag to find the 12094cf49a43SJulian Elischer * hook this is in response to. 12104cf49a43SJulian Elischer */ 12111f89d938SJulian Elischer utag = get_tag(ph, PTT_AC_COOKIE); 12121f89d938SJulian Elischer if ((utag == NULL) 12131f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 12144cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12154cf49a43SJulian Elischer } 12164cf49a43SJulian Elischer 12171f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 12184cf49a43SJulian Elischer if (sendhook == NULL) { 12194cf49a43SJulian Elischer LEAVE(ENETUNREACH); 12204cf49a43SJulian Elischer } 12214cf49a43SJulian Elischer 12224cf49a43SJulian Elischer /* 12234cf49a43SJulian Elischer * Check the session is in the right state. 12244cf49a43SJulian Elischer * It needs to be in PPPOE_SOFFER 12254cf49a43SJulian Elischer * or PPPOE_NEWCONNECTED. If the latter, 12264cf49a43SJulian Elischer * then this is a retry by the client. 12274cf49a43SJulian Elischer * so be nice, and resend. 12284cf49a43SJulian Elischer */ 122930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 12304cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 12314cf49a43SJulian Elischer /* 12324cf49a43SJulian Elischer * Whoa! drop back to resend that 12334cf49a43SJulian Elischer * PADS packet. 12344cf49a43SJulian Elischer * We should still have a copy of it. 12354cf49a43SJulian Elischer */ 12364cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 12374cf49a43SJulian Elischer } 12384cf49a43SJulian Elischer if (sp->state != PPPOE_SOFFER) { 12394cf49a43SJulian Elischer LEAVE (ENETUNREACH); 12404cf49a43SJulian Elischer break; 12414cf49a43SJulian Elischer } 12424cf49a43SJulian Elischer neg = sp->neg; 12434cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 12444cf49a43SJulian Elischer neg->timeout_handle); 12454cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADS_CODE; 12464cf49a43SJulian Elischer if (sp->Session_ID == 0) 12474cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 1248b86d0a9eSJulian Elischer htons(sp->Session_ID 1249b86d0a9eSJulian Elischer = get_new_sid(node)); 125087c4cce0SBrian Somers send_sessionid(sp); 12514cf49a43SJulian Elischer neg->timeout = 0; 12524cf49a43SJulian Elischer /* 12534cf49a43SJulian Elischer * start working out the tags to respond with. 12544cf49a43SJulian Elischer */ 12554cf49a43SJulian Elischer init_tags(sp); 12564cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1257bdaf2e81SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 12584adb13fdSJulian Elischer insert_tag(sp, tag);/* return service */ 12591f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 12604adb13fdSJulian Elischer insert_tag(sp, tag); /* return it */ 12611f89d938SJulian Elischer insert_tag(sp, utag); /* ac_cookie */ 12624cf49a43SJulian Elischer scan_tags(sp, ph); 12634cf49a43SJulian Elischer make_packet(sp); 1264bdaf2e81SJulian Elischer sp->state = PPPOE_NEWCONNECTED; 12656faf164cSJulian Elischer sendpacket(sp); 12664cf49a43SJulian Elischer /* 12674cf49a43SJulian Elischer * Having sent the last Negotiation header, 12684cf49a43SJulian Elischer * Set up the stored packet header to 12694cf49a43SJulian Elischer * be correct for the actual session. 12704cf49a43SJulian Elischer * But keep the negotialtion stuff 12714cf49a43SJulian Elischer * around in case we need to resend this last 12724cf49a43SJulian Elischer * packet. We'll discard it when we move 12734cf49a43SJulian Elischer * from NEWCONNECTED to CONNECTED 12744cf49a43SJulian Elischer */ 12754cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 1276fdc755d1SGleb Smirnoff /* Configure ethertype depending on what 1277fdc755d1SGleb Smirnoff * ethertype was used at discovery phase */ 1278fdc755d1SGleb Smirnoff if (sp->pkt_hdr.eh.ether_type == 1279fdc755d1SGleb Smirnoff ETHERTYPE_PPPOE_STUPID_DISC) 1280bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1281bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1282bfa7e882SJulian Elischer else 12834cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 12844cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 12854cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 1286b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 12874cf49a43SJulian Elischer break; 12884cf49a43SJulian Elischer case PADS_CODE: 12894cf49a43SJulian Elischer /* 12904cf49a43SJulian Elischer * We are a client: 12914cf49a43SJulian Elischer * Use the host_uniq tag to find the 12924cf49a43SJulian Elischer * hook this is in response to. 12934cf49a43SJulian Elischer * take the session ID and store it away. 12944cf49a43SJulian Elischer * Also make sure the pre-made header is 12954cf49a43SJulian Elischer * correct and set us into Session mode. 12964cf49a43SJulian Elischer */ 12971f89d938SJulian Elischer utag = get_tag(ph, PTT_HOST_UNIQ); 12981f89d938SJulian Elischer if ((utag == NULL) 12991f89d938SJulian Elischer || (ntohs(utag->tag_len) != sizeof(sp))) { 13004cf49a43SJulian Elischer LEAVE (ENETUNREACH); 13014cf49a43SJulian Elischer break; 13024cf49a43SJulian Elischer } 13031f89d938SJulian Elischer sendhook = pppoe_finduniq(node, utag); 13044cf49a43SJulian Elischer if (sendhook == NULL) { 13054cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13064cf49a43SJulian Elischer } 13074cf49a43SJulian Elischer 13084cf49a43SJulian Elischer /* 13094cf49a43SJulian Elischer * Check the session is in the right state. 13104cf49a43SJulian Elischer * It needs to be in PPPOE_SREQ. 13114cf49a43SJulian Elischer */ 131230400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 13134cf49a43SJulian Elischer if (sp->state != PPPOE_SREQ) { 13144cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13154cf49a43SJulian Elischer } 13164cf49a43SJulian Elischer neg = sp->neg; 13174cf49a43SJulian Elischer untimeout(pppoe_ticker, sendhook, 13184cf49a43SJulian Elischer neg->timeout_handle); 1319cfbcfe62SJulian Elischer neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1320b86d0a9eSJulian Elischer sp->Session_ID = ntohs(wh->ph.sid); 132187c4cce0SBrian Somers send_sessionid(sp); 13224cf49a43SJulian Elischer neg->timeout = 0; 13234cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 13244cf49a43SJulian Elischer /* 13254cf49a43SJulian Elischer * Now we have gone to Connected mode, 13264cf49a43SJulian Elischer * Free all resources needed for 13274cf49a43SJulian Elischer * negotiation. 13284cf49a43SJulian Elischer * Keep a copy of the header we will be using. 13294cf49a43SJulian Elischer */ 13304cf49a43SJulian Elischer sp->pkt_hdr = neg->pkt->pkt_header; 1331fdc755d1SGleb Smirnoff if (privp->mode->id == PPPOE_NONSTANDARD) 1332bfa7e882SJulian Elischer sp->pkt_hdr.eh.ether_type 1333bfa7e882SJulian Elischer = ETHERTYPE_PPPOE_STUPID_SESS; 1334bfa7e882SJulian Elischer else 13354cf49a43SJulian Elischer sp->pkt_hdr.eh.ether_type 13364cf49a43SJulian Elischer = ETHERTYPE_PPPOE_SESS; 13374cf49a43SJulian Elischer sp->pkt_hdr.ph.code = 0; 13384cf49a43SJulian Elischer m_freem(neg->m); 13399c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 13404cf49a43SJulian Elischer sp->neg = NULL; 1341b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 13424cf49a43SJulian Elischer break; 13434cf49a43SJulian Elischer case PADT_CODE: 13444cf49a43SJulian Elischer /* 13454cf49a43SJulian Elischer * Send a 'close' message to the controlling 13464cf49a43SJulian Elischer * process (the one that set us up); 13474cf49a43SJulian Elischer * And then tear everything down. 13484cf49a43SJulian Elischer * 13494cf49a43SJulian Elischer * Find matching peer/session combination. 13504cf49a43SJulian Elischer */ 13514cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 13524cf49a43SJulian Elischer if (sendhook == NULL) { 13534cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13544cf49a43SJulian Elischer } 13554cf49a43SJulian Elischer /* send message to creator */ 13564cf49a43SJulian Elischer /* close hook */ 1357b58a8a3bSJulian Elischer if (sendhook) { 1358954c4772SJulian Elischer ng_rmhook_self(sendhook); 1359b58a8a3bSJulian Elischer } 13604cf49a43SJulian Elischer break; 13614cf49a43SJulian Elischer default: 13624cf49a43SJulian Elischer LEAVE(EPFNOSUPPORT); 13634cf49a43SJulian Elischer } 13644cf49a43SJulian Elischer break; 1365bfa7e882SJulian Elischer case ETHERTYPE_PPPOE_STUPID_SESS: 13664cf49a43SJulian Elischer case ETHERTYPE_PPPOE_SESS: 13674cf49a43SJulian Elischer /* 13684cf49a43SJulian Elischer * find matching peer/session combination. 13694cf49a43SJulian Elischer */ 13704cf49a43SJulian Elischer sendhook = pppoe_findsession(node, wh); 13714cf49a43SJulian Elischer if (sendhook == NULL) { 13724cf49a43SJulian Elischer LEAVE (ENETUNREACH); 13734cf49a43SJulian Elischer break; 13744cf49a43SJulian Elischer } 137530400f03SJulian Elischer sp = NG_HOOK_PRIVATE(sendhook); 13764cf49a43SJulian Elischer m_adj(m, sizeof(*wh)); 13774cf49a43SJulian Elischer if (m->m_pkthdr.len < length) { 13784cf49a43SJulian Elischer /* Packet too short, dump it */ 13794cf49a43SJulian Elischer LEAVE(EMSGSIZE); 13804cf49a43SJulian Elischer } 13819fcb3d83SJulian Elischer 13824adb13fdSJulian Elischer /* Also need to trim excess at the end */ 13839fcb3d83SJulian Elischer if (m->m_pkthdr.len > length) { 13849fcb3d83SJulian Elischer m_adj(m, -((int)(m->m_pkthdr.len - length))); 13859fcb3d83SJulian Elischer } 13864cf49a43SJulian Elischer if ( sp->state != PPPOE_CONNECTED) { 13874cf49a43SJulian Elischer if (sp->state == PPPOE_NEWCONNECTED) { 13884cf49a43SJulian Elischer sp->state = PPPOE_CONNECTED; 13894cf49a43SJulian Elischer /* 13904cf49a43SJulian Elischer * Now we have gone to Connected mode, 13914cf49a43SJulian Elischer * Free all resources needed for 1392a4ec03cfSJulian Elischer * negotiation. Be paranoid about 1393a4ec03cfSJulian Elischer * whether there may be a timeout. 13944cf49a43SJulian Elischer */ 13954cf49a43SJulian Elischer m_freem(sp->neg->m); 1396a4ec03cfSJulian Elischer untimeout(pppoe_ticker, sendhook, 1397a4ec03cfSJulian Elischer sp->neg->timeout_handle); 13989c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 13994cf49a43SJulian Elischer sp->neg = NULL; 14004cf49a43SJulian Elischer } else { 14014cf49a43SJulian Elischer LEAVE (ENETUNREACH); 14024cf49a43SJulian Elischer break; 14034cf49a43SJulian Elischer } 14044cf49a43SJulian Elischer } 1405069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, sendhook, m); 14064cf49a43SJulian Elischer break; 14074cf49a43SJulian Elischer default: 14084b276f90SJulian Elischer LEAVE(EPFNOSUPPORT); 14094cf49a43SJulian Elischer } 14104cf49a43SJulian Elischer } else { 14114cf49a43SJulian Elischer /* 14124cf49a43SJulian Elischer * Not ethernet or debug hook.. 14134cf49a43SJulian Elischer * 14144cf49a43SJulian Elischer * The packet has come in on a normal hook. 14154cf49a43SJulian Elischer * We need to find out what kind of hook, 14164cf49a43SJulian Elischer * So we can decide how to handle it. 14174cf49a43SJulian Elischer * Check the hook's state. 14184cf49a43SJulian Elischer */ 141930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 14204cf49a43SJulian Elischer switch (sp->state) { 14214cf49a43SJulian Elischer case PPPOE_NEWCONNECTED: 14224cf49a43SJulian Elischer case PPPOE_CONNECTED: { 14237b38c4e4SArchie Cobbs static const u_char addrctrl[] = { 0xff, 0x03 }; 14244cf49a43SJulian Elischer struct pppoe_full_hdr *wh; 14257b38c4e4SArchie Cobbs 14267b38c4e4SArchie Cobbs /* 14277b38c4e4SArchie Cobbs * Remove PPP address and control fields, if any. 14287b38c4e4SArchie Cobbs * For example, ng_ppp(4) always sends LCP packets 14297b38c4e4SArchie Cobbs * with address and control fields as required by 14307b38c4e4SArchie Cobbs * generic PPP. PPPoE is an exception to the rule. 14317b38c4e4SArchie Cobbs */ 14327b38c4e4SArchie Cobbs if (m->m_pkthdr.len >= 2) { 14337b38c4e4SArchie Cobbs if (m->m_len < 2 && !(m = m_pullup(m, 2))) 14347b38c4e4SArchie Cobbs LEAVE(ENOBUFS); 14357b38c4e4SArchie Cobbs if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 14367b38c4e4SArchie Cobbs m_adj(m, 2); 14377b38c4e4SArchie Cobbs } 14384cf49a43SJulian Elischer /* 14394cf49a43SJulian Elischer * Bang in a pre-made header, and set the length up 14404cf49a43SJulian Elischer * to be correct. Then send it to the ethernet driver. 1441d9da9cbaSJulian Elischer * But first correct the length. 14424cf49a43SJulian Elischer */ 1443d9da9cbaSJulian Elischer sp->pkt_hdr.ph.length = htons((short)(m->m_pkthdr.len)); 1444a163d034SWarner Losh M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 14454cf49a43SJulian Elischer if (m == NULL) { 14464cf49a43SJulian Elischer LEAVE(ENOBUFS); 14474cf49a43SJulian Elischer } 14484cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 14494cf49a43SJulian Elischer bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1450069154d5SJulian Elischer NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 14514cf49a43SJulian Elischer privp->packets_out++; 14524cf49a43SJulian Elischer break; 14534cf49a43SJulian Elischer } 14544cf49a43SJulian Elischer case PPPOE_PRIMED: 14554cf49a43SJulian Elischer /* 14564cf49a43SJulian Elischer * A PADI packet is being returned by the application 14574cf49a43SJulian Elischer * that has set up this hook. This indicates that it 14584cf49a43SJulian Elischer * wants us to offer service. 14594cf49a43SJulian Elischer */ 14604cf49a43SJulian Elischer neg = sp->neg; 1461bdaf2e81SJulian Elischer if (m->m_len < sizeof(*wh)) { 1462bdaf2e81SJulian Elischer m = m_pullup(m, sizeof(*wh)); 14634cf49a43SJulian Elischer if (m == NULL) { 14644cf49a43SJulian Elischer LEAVE(ENOBUFS); 14654cf49a43SJulian Elischer } 1466bdaf2e81SJulian Elischer } 14674cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 14684cf49a43SJulian Elischer ph = &wh->ph; 14694cf49a43SJulian Elischer session = ntohs(wh->ph.sid); 14704cf49a43SJulian Elischer length = ntohs(wh->ph.length); 14714cf49a43SJulian Elischer code = wh->ph.code; 1472fdc755d1SGleb Smirnoff /* Use peers mode in session */ 1473fdc755d1SGleb Smirnoff neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type; 14741e2510f8SJulian Elischer if ( code != PADI_CODE) { 14751e2510f8SJulian Elischer LEAVE(EINVAL); 14761e2510f8SJulian Elischer }; 14771e2510f8SJulian Elischer untimeout(pppoe_ticker, hook, 14781e2510f8SJulian Elischer neg->timeout_handle); 14794cf49a43SJulian Elischer 14804cf49a43SJulian Elischer /* 14814cf49a43SJulian Elischer * This is the first time we hear 14824cf49a43SJulian Elischer * from the client, so note it's 14834cf49a43SJulian Elischer * unicast address, replacing the 14844cf49a43SJulian Elischer * broadcast address. 14854cf49a43SJulian Elischer */ 14864cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 14874cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 14884cf49a43SJulian Elischer ETHER_ADDR_LEN); 14894cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 14904cf49a43SJulian Elischer neg->timeout = 0; 14914cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 14924cf49a43SJulian Elischer 14934cf49a43SJulian Elischer /* 14944cf49a43SJulian Elischer * start working out the tags to respond with. 14954cf49a43SJulian Elischer */ 14964cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_AC_COOKIE; 14974cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 14984cf49a43SJulian Elischer uniqtag.data.pointer = sp; 14994cf49a43SJulian Elischer init_tags(sp); 15004cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 15011f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 15024adb13fdSJulian Elischer insert_tag(sp, tag); /* return service */ 1503859a4d16SJulian Elischer /* 1504859a4d16SJulian Elischer * If we have a NULL service request 1505859a4d16SJulian Elischer * and have an extra service defined in this hook, 1506859a4d16SJulian Elischer * then also add a tag for the extra service. 1507859a4d16SJulian Elischer * XXX this is a hack. eventually we should be able 1508859a4d16SJulian Elischer * to support advertising many services, not just one 1509859a4d16SJulian Elischer */ 1510859a4d16SJulian Elischer if (((tag == NULL) || (tag->tag_len == 0)) 1511859a4d16SJulian Elischer && (neg->service.hdr.tag_len != 0)) { 1512859a4d16SJulian Elischer insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1513859a4d16SJulian Elischer } 15141f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 15151f89d938SJulian Elischer insert_tag(sp, tag); /* returned hostunique */ 15161f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 15174cf49a43SJulian Elischer scan_tags(sp, ph); 15184cf49a43SJulian Elischer make_packet(sp); 15194cf49a43SJulian Elischer sendpacket(sp); 15204cf49a43SJulian Elischer break; 15214cf49a43SJulian Elischer 15224cf49a43SJulian Elischer /* 15234cf49a43SJulian Elischer * Packets coming from the hook make no sense 15244cf49a43SJulian Elischer * to sessions in these states. Throw them away. 15254cf49a43SJulian Elischer */ 15264cf49a43SJulian Elischer case PPPOE_SINIT: 15274cf49a43SJulian Elischer case PPPOE_SREQ: 15284cf49a43SJulian Elischer case PPPOE_SOFFER: 15294cf49a43SJulian Elischer case PPPOE_SNONE: 15304cf49a43SJulian Elischer case PPPOE_LISTENING: 15314cf49a43SJulian Elischer case PPPOE_DEAD: 15324cf49a43SJulian Elischer default: 15334cf49a43SJulian Elischer LEAVE(ENETUNREACH); 15344cf49a43SJulian Elischer } 15354cf49a43SJulian Elischer } 15364cf49a43SJulian Elischer quit: 1537f5856029SJulian Elischer if (item) 1538069154d5SJulian Elischer NG_FREE_ITEM(item); 1539069154d5SJulian Elischer NG_FREE_M(m); 15404cf49a43SJulian Elischer return error; 15414cf49a43SJulian Elischer } 15424cf49a43SJulian Elischer 15434cf49a43SJulian Elischer /* 15444cf49a43SJulian Elischer * Do local shutdown processing.. 15454cf49a43SJulian Elischer * If we are a persistant device, we might refuse to go away, and 15464cf49a43SJulian Elischer * we'd only remove our links and reset ourself. 15474cf49a43SJulian Elischer */ 15484cf49a43SJulian Elischer static int 1549069154d5SJulian Elischer ng_pppoe_shutdown(node_p node) 15504cf49a43SJulian Elischer { 155130400f03SJulian Elischer const priv_p privdata = NG_NODE_PRIVATE(node); 15524cf49a43SJulian Elischer 1553f2b9562cSGleb Smirnoff DBG; 155430400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 155530400f03SJulian Elischer NG_NODE_UNREF(privdata->node); 15569c8c302fSJulian Elischer FREE(privdata, M_NETGRAPH_PPPOE); 15574cf49a43SJulian Elischer return (0); 15584cf49a43SJulian Elischer } 15594cf49a43SJulian Elischer 15604cf49a43SJulian Elischer /* 15614cf49a43SJulian Elischer * This is called once we've already connected a new hook to the other node. 15624cf49a43SJulian Elischer * It gives us a chance to balk at the last minute. 15634cf49a43SJulian Elischer */ 15644cf49a43SJulian Elischer static int 15658876b55dSJulian Elischer ng_pppoe_connect(hook_p hook) 15664cf49a43SJulian Elischer { 15674cf49a43SJulian Elischer /* be really amiable and just say "YUP that's OK by me! " */ 15684cf49a43SJulian Elischer return (0); 15694cf49a43SJulian Elischer } 15704cf49a43SJulian Elischer 15714cf49a43SJulian Elischer /* 15724cf49a43SJulian Elischer * Hook disconnection 15734cf49a43SJulian Elischer * 15746faf164cSJulian Elischer * Clean up all dangling links and information about the session/hook. 15754cf49a43SJulian Elischer * For this type, removal of the last link destroys the node 15764cf49a43SJulian Elischer */ 15774cf49a43SJulian Elischer static int 15788876b55dSJulian Elischer ng_pppoe_disconnect(hook_p hook) 15794cf49a43SJulian Elischer { 158030400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 158130400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 15824cf49a43SJulian Elischer sessp sp; 158304853d8aSJulian Elischer int hooks; 15844cf49a43SJulian Elischer 1585f2b9562cSGleb Smirnoff DBG; 158630400f03SJulian Elischer hooks = NG_NODE_NUMHOOKS(node); /* this one already not counted */ 158730400f03SJulian Elischer if (NG_HOOK_PRIVATE(hook) == &privp->debug_hook) { 15884cf49a43SJulian Elischer privp->debug_hook = NULL; 158930400f03SJulian Elischer } else if (NG_HOOK_PRIVATE(hook) == &privp->ethernet_hook) { 15904cf49a43SJulian Elischer privp->ethernet_hook = NULL; 159130400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) 1592069154d5SJulian Elischer ng_rmnode_self(node); 15934cf49a43SJulian Elischer } else { 159430400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 1595b58a8a3bSJulian Elischer if (sp->state != PPPOE_SNONE ) { 1596b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1597b58a8a3bSJulian Elischer } 1598a4ec03cfSJulian Elischer /* 1599a4ec03cfSJulian Elischer * According to the spec, if we are connected, 1600a4ec03cfSJulian Elischer * we should send a DISC packet if we are shutting down 1601a4ec03cfSJulian Elischer * a session. 1602a4ec03cfSJulian Elischer */ 16039fcb3d83SJulian Elischer if ((privp->ethernet_hook) 16049fcb3d83SJulian Elischer && ((sp->state == PPPOE_CONNECTED) 16059fcb3d83SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED))) { 16069fcb3d83SJulian Elischer struct mbuf *m; 16079fcb3d83SJulian Elischer struct pppoe_full_hdr *wh; 16089fcb3d83SJulian Elischer struct pppoe_tag *tag; 16099fcb3d83SJulian Elischer int msglen = strlen(SIGNOFF); 16109fcb3d83SJulian Elischer int error = 0; 16119fcb3d83SJulian Elischer 16129fcb3d83SJulian Elischer /* revert the stored header to DISC/PADT mode */ 16139fcb3d83SJulian Elischer wh = &sp->pkt_hdr; 16149fcb3d83SJulian Elischer wh->ph.code = PADT_CODE; 1615fdc755d1SGleb Smirnoff /* Configure ethertype depending on what was used during 1616fdc755d1SGleb Smirnoff * sessions stage. */ 1617fdc755d1SGleb Smirnoff if (sp->pkt_hdr.eh.ether_type == 1618fdc755d1SGleb Smirnoff ETHERTYPE_PPPOE_STUPID_SESS) 1619bfa7e882SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_STUPID_DISC; 1620bfa7e882SJulian Elischer else 16219fcb3d83SJulian Elischer wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 16229fcb3d83SJulian Elischer 16239fcb3d83SJulian Elischer /* generate a packet of that type */ 1624a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 16256faf164cSJulian Elischer if(m == NULL) 16266faf164cSJulian Elischer printf("pppoe: Session out of mbufs\n"); 16276faf164cSJulian Elischer else { 16289fcb3d83SJulian Elischer m->m_pkthdr.rcvif = NULL; 16299fcb3d83SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*wh); 16306faf164cSJulian Elischer bcopy((caddr_t)wh, mtod(m, caddr_t), 16316faf164cSJulian Elischer sizeof(*wh)); 16326faf164cSJulian Elischer /* 16336faf164cSJulian Elischer * Add a General error message and adjust 16346faf164cSJulian Elischer * sizes 16356faf164cSJulian Elischer */ 16369fcb3d83SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 16379fcb3d83SJulian Elischer tag = wh->ph.tag; 16389fcb3d83SJulian Elischer tag->tag_type = PTT_GEN_ERR; 16399fcb3d83SJulian Elischer tag->tag_len = htons((u_int16_t)msglen); 16409fcb3d83SJulian Elischer strncpy(tag->tag_data, SIGNOFF, msglen); 16416faf164cSJulian Elischer m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 16426faf164cSJulian Elischer msglen); 16439fcb3d83SJulian Elischer wh->ph.length = htons(sizeof(*tag) + msglen); 1644069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, 1645069154d5SJulian Elischer privp->ethernet_hook, m); 16466faf164cSJulian Elischer } 16479fcb3d83SJulian Elischer } 1648a4ec03cfSJulian Elischer /* 1649514baf3fSJeroen Ruigrok van der Werven * As long as we have somewhere to store the timeout handle, 1650a4ec03cfSJulian Elischer * we may have a timeout pending.. get rid of it. 1651a4ec03cfSJulian Elischer */ 16521e2510f8SJulian Elischer if (sp->neg) { 16534cf49a43SJulian Elischer untimeout(pppoe_ticker, hook, sp->neg->timeout_handle); 16541e2510f8SJulian Elischer if (sp->neg->m) 16551e2510f8SJulian Elischer m_freem(sp->neg->m); 16569c8c302fSJulian Elischer FREE(sp->neg, M_NETGRAPH_PPPOE); 16571e2510f8SJulian Elischer } 16589c8c302fSJulian Elischer FREE(sp, M_NETGRAPH_PPPOE); 165930400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 1660ed52f174SJulian Elischer /* work out how many session hooks there are */ 166104853d8aSJulian Elischer /* Node goes away on last session hook removal */ 166204853d8aSJulian Elischer if (privp->ethernet_hook) hooks -= 1; 1663ed52f174SJulian Elischer if (privp->debug_hook) hooks -= 1; 16644cf49a43SJulian Elischer } 166530400f03SJulian Elischer if ((NG_NODE_NUMHOOKS(node) == 0) 166630400f03SJulian Elischer && (NG_NODE_IS_VALID(node))) 1667069154d5SJulian Elischer ng_rmnode_self(node); 16684cf49a43SJulian Elischer return (0); 16694cf49a43SJulian Elischer } 16704cf49a43SJulian Elischer 16714cf49a43SJulian Elischer /* 16724cf49a43SJulian Elischer * timeouts come here. 16734cf49a43SJulian Elischer */ 16744cf49a43SJulian Elischer static void 16754cf49a43SJulian Elischer pppoe_ticker(void *arg) 16764cf49a43SJulian Elischer { 16774cf49a43SJulian Elischer int s = splnet(); 16784cf49a43SJulian Elischer hook_p hook = arg; 167930400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 16804cf49a43SJulian Elischer negp neg = sp->neg; 16814cf49a43SJulian Elischer int error = 0; 16824cf49a43SJulian Elischer struct mbuf *m0 = NULL; 168330400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 16844cf49a43SJulian Elischer 1685f2b9562cSGleb Smirnoff DBG; 16864cf49a43SJulian Elischer switch(sp->state) { 16874cf49a43SJulian Elischer /* 16884cf49a43SJulian Elischer * resend the last packet, using an exponential backoff. 16894cf49a43SJulian Elischer * After a period of time, stop growing the backoff, 16904adb13fdSJulian Elischer * and either leave it, or revert to the start. 16914cf49a43SJulian Elischer */ 16924cf49a43SJulian Elischer case PPPOE_SINIT: 16934cf49a43SJulian Elischer case PPPOE_SREQ: 16944cf49a43SJulian Elischer /* timeouts on these produce resends */ 1695a163d034SWarner Losh m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1696069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 16974cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 16984cf49a43SJulian Elischer hook, neg->timeout * hz); 16994cf49a43SJulian Elischer if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 17004cf49a43SJulian Elischer if (sp->state == PPPOE_SREQ) { 17014cf49a43SJulian Elischer /* revert to SINIT mode */ 1702b58a8a3bSJulian Elischer pppoe_start(sp); 17034cf49a43SJulian Elischer } else { 17044cf49a43SJulian Elischer neg->timeout = PPPOE_TIMEOUT_LIMIT; 17054cf49a43SJulian Elischer } 17064cf49a43SJulian Elischer } 17074cf49a43SJulian Elischer break; 17084cf49a43SJulian Elischer case PPPOE_PRIMED: 17094cf49a43SJulian Elischer case PPPOE_SOFFER: 17104cf49a43SJulian Elischer /* a timeout on these says "give up" */ 1711954c4772SJulian Elischer ng_rmhook_self(hook); 17124cf49a43SJulian Elischer break; 17134cf49a43SJulian Elischer default: 17144cf49a43SJulian Elischer /* timeouts have no meaning in other states */ 17154cf49a43SJulian Elischer printf("pppoe: unexpected timeout\n"); 17164cf49a43SJulian Elischer } 17174cf49a43SJulian Elischer splx(s); 17184cf49a43SJulian Elischer } 17194cf49a43SJulian Elischer 17204cf49a43SJulian Elischer 17214cf49a43SJulian Elischer static void 17224cf49a43SJulian Elischer sendpacket(sessp sp) 17234cf49a43SJulian Elischer { 17244cf49a43SJulian Elischer int error = 0; 17254cf49a43SJulian Elischer struct mbuf *m0 = NULL; 17264cf49a43SJulian Elischer hook_p hook = sp->hook; 17274cf49a43SJulian Elischer negp neg = sp->neg; 172830400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 17294cf49a43SJulian Elischer 1730f2b9562cSGleb Smirnoff DBG; 17314cf49a43SJulian Elischer switch(sp->state) { 17324cf49a43SJulian Elischer case PPPOE_LISTENING: 17334cf49a43SJulian Elischer case PPPOE_DEAD: 17344cf49a43SJulian Elischer case PPPOE_SNONE: 17354cf49a43SJulian Elischer case PPPOE_CONNECTED: 1736b86d0a9eSJulian Elischer printf("pppoe: sendpacket: unexpected state\n"); 17374cf49a43SJulian Elischer break; 17384cf49a43SJulian Elischer 17396faf164cSJulian Elischer case PPPOE_NEWCONNECTED: 17406faf164cSJulian Elischer /* send the PADS without a timeout - we're now connected */ 1741a163d034SWarner Losh m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1742069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 17436faf164cSJulian Elischer break; 17446faf164cSJulian Elischer 17454cf49a43SJulian Elischer case PPPOE_PRIMED: 17464cf49a43SJulian Elischer /* No packet to send, but set up the timeout */ 17474cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 17484cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 17494cf49a43SJulian Elischer break; 17504cf49a43SJulian Elischer 17514cf49a43SJulian Elischer case PPPOE_SOFFER: 17524cf49a43SJulian Elischer /* 17534cf49a43SJulian Elischer * send the offer but if they don't respond 17544cf49a43SJulian Elischer * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 17554cf49a43SJulian Elischer */ 1756a163d034SWarner Losh m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1757069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 17584cf49a43SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, 17594cf49a43SJulian Elischer hook, PPPOE_OFFER_TIMEOUT * hz); 17604cf49a43SJulian Elischer break; 17614cf49a43SJulian Elischer 17624cf49a43SJulian Elischer case PPPOE_SINIT: 17634cf49a43SJulian Elischer case PPPOE_SREQ: 1764a163d034SWarner Losh m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1765069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1766d0fef808SJulian Elischer neg->timeout_handle = timeout(pppoe_ticker, hook, 1767d0fef808SJulian Elischer (hz * PPPOE_INITIAL_TIMEOUT)); 1768d0fef808SJulian Elischer neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 17694cf49a43SJulian Elischer break; 17704cf49a43SJulian Elischer 17714cf49a43SJulian Elischer default: 17724cf49a43SJulian Elischer error = EINVAL; 17734cf49a43SJulian Elischer printf("pppoe: timeout: bad state\n"); 17744cf49a43SJulian Elischer } 17754cf49a43SJulian Elischer /* return (error); */ 17764cf49a43SJulian Elischer } 17774cf49a43SJulian Elischer 17784cf49a43SJulian Elischer /* 17794cf49a43SJulian Elischer * Parse an incoming packet to see if any tags should be copied to the 17804adb13fdSJulian Elischer * output packet. Don't do any tags that have been handled in the main 17814adb13fdSJulian Elischer * state machine. 17824cf49a43SJulian Elischer */ 1783816b834fSArchie Cobbs static const struct pppoe_tag* 1784816b834fSArchie Cobbs scan_tags(sessp sp, const struct pppoe_hdr* ph) 17854cf49a43SJulian Elischer { 1786816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 1787816b834fSArchie Cobbs const char *ptn; 1788816b834fSArchie Cobbs const struct pppoe_tag *pt = &ph->tag[0]; 17894cf49a43SJulian Elischer /* 17904cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 17914cf49a43SJulian Elischer */ 1792f2b9562cSGleb Smirnoff DBG; 1793816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 17944cf49a43SJulian Elischer /* 17954cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 17964cf49a43SJulian Elischer */ 1797816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 17984cf49a43SJulian Elischer if(ptn > end) 17994cf49a43SJulian Elischer return NULL; 18004cf49a43SJulian Elischer 18014cf49a43SJulian Elischer switch (pt->tag_type) { 18024cf49a43SJulian Elischer case PTT_RELAY_SID: 18034cf49a43SJulian Elischer insert_tag(sp, pt); 18044cf49a43SJulian Elischer break; 18054cf49a43SJulian Elischer case PTT_EOL: 18064cf49a43SJulian Elischer return NULL; 18074cf49a43SJulian Elischer case PTT_SRV_NAME: 18084cf49a43SJulian Elischer case PTT_AC_NAME: 18094cf49a43SJulian Elischer case PTT_HOST_UNIQ: 18104cf49a43SJulian Elischer case PTT_AC_COOKIE: 18114cf49a43SJulian Elischer case PTT_VENDOR: 18124cf49a43SJulian Elischer case PTT_SRV_ERR: 18134cf49a43SJulian Elischer case PTT_SYS_ERR: 18144cf49a43SJulian Elischer case PTT_GEN_ERR: 18154cf49a43SJulian Elischer break; 18164cf49a43SJulian Elischer } 1817816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 18184cf49a43SJulian Elischer } 18194cf49a43SJulian Elischer return NULL; 18204cf49a43SJulian Elischer } 18214cf49a43SJulian Elischer 1822b58a8a3bSJulian Elischer static int 1823b58a8a3bSJulian Elischer pppoe_send_event(sessp sp, enum cmd cmdid) 1824b58a8a3bSJulian Elischer { 1825b58a8a3bSJulian Elischer int error; 1826b58a8a3bSJulian Elischer struct ng_mesg *msg; 18278876b55dSJulian Elischer struct ngpppoe_sts *sts; 1828b58a8a3bSJulian Elischer 1829f2b9562cSGleb Smirnoff DBG; 183027121ab1SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 18318876b55dSJulian Elischer sizeof(struct ngpppoe_sts), M_NOWAIT); 1832859a4d16SJulian Elischer if (msg == NULL) 1833859a4d16SJulian Elischer return (ENOMEM); 18348876b55dSJulian Elischer sts = (struct ngpppoe_sts *)msg->data; 183587e2c66aSHartmut Brandt strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 1836facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1837b58a8a3bSJulian Elischer return (error); 1838b58a8a3bSJulian Elischer } 1839