14cf49a43SJulian Elischer /* 24cf49a43SJulian Elischer * ng_pppoe.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 64cf49a43SJulian Elischer * Copyright (c) 1996-1999 Whistle Communications, Inc. 74cf49a43SJulian Elischer * All rights reserved. 84cf49a43SJulian Elischer * 94cf49a43SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 104cf49a43SJulian Elischer * redistribution of this software, in source or object code forms, with or 114cf49a43SJulian Elischer * without modifications are expressly permitted by Whistle Communications; 124cf49a43SJulian Elischer * provided, however, that: 134cf49a43SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 144cf49a43SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 154cf49a43SJulian Elischer * 2. No rights are granted, in any manner or form, to use Whistle 164cf49a43SJulian Elischer * Communications, Inc. trademarks, including the mark "WHISTLE 174cf49a43SJulian Elischer * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 184cf49a43SJulian Elischer * such appears in the above copyright notice or in the software. 194cf49a43SJulian Elischer * 204cf49a43SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 214cf49a43SJulian Elischer * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 224cf49a43SJulian Elischer * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 234cf49a43SJulian Elischer * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 244cf49a43SJulian Elischer * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 254cf49a43SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 264cf49a43SJulian Elischer * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 274cf49a43SJulian Elischer * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 284cf49a43SJulian Elischer * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 294cf49a43SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 304cf49a43SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 314cf49a43SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 324cf49a43SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 334cf49a43SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 344cf49a43SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 354cf49a43SJulian Elischer * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 364cf49a43SJulian Elischer * OF SUCH DAMAGE. 374cf49a43SJulian Elischer * 38cc3bbd68SJulian Elischer * Author: Julian Elischer <julian@freebsd.org> 394cf49a43SJulian Elischer * 404cf49a43SJulian Elischer * $FreeBSD$ 4174f5c6aaSJulian Elischer * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $ 424cf49a43SJulian Elischer */ 434cf49a43SJulian Elischer 444cf49a43SJulian Elischer #include <sys/param.h> 454cf49a43SJulian Elischer #include <sys/systm.h> 464cf49a43SJulian Elischer #include <sys/kernel.h> 477762e8c6SGleb Smirnoff #include <sys/ktr.h> 484cf49a43SJulian Elischer #include <sys/mbuf.h> 494cf49a43SJulian Elischer #include <sys/malloc.h> 504cf49a43SJulian Elischer #include <sys/errno.h> 512e87c3ccSGleb Smirnoff #include <sys/syslog.h> 524cf49a43SJulian Elischer #include <net/ethernet.h> 534cf49a43SJulian Elischer 544cf49a43SJulian Elischer #include <netgraph/ng_message.h> 554cf49a43SJulian Elischer #include <netgraph/netgraph.h> 5676a70671SBrian Somers #include <netgraph/ng_parse.h> 574cf49a43SJulian Elischer #include <netgraph/ng_pppoe.h> 58b1ba28dfSGleb Smirnoff #include <netgraph/ng_ether.h> 594cf49a43SJulian Elischer 609c8c302fSJulian Elischer #ifdef NG_SEPARATE_MALLOC 61d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 629c8c302fSJulian Elischer #else 639c8c302fSJulian Elischer #define M_NETGRAPH_PPPOE M_NETGRAPH 649c8c302fSJulian Elischer #endif 659c8c302fSJulian Elischer 66da092930SArchie Cobbs #define SIGNOFF "session closed" 67da092930SArchie Cobbs 684cf49a43SJulian Elischer /* 694cf49a43SJulian Elischer * This section contains the netgraph method declarations for the 70bfa7e882SJulian Elischer * pppoe node. These methods define the netgraph pppoe 'type'. 714cf49a43SJulian Elischer */ 724cf49a43SJulian Elischer 7374f5c6aaSJulian Elischer static ng_constructor_t ng_pppoe_constructor; 7474f5c6aaSJulian Elischer static ng_rcvmsg_t ng_pppoe_rcvmsg; 75069154d5SJulian Elischer static ng_shutdown_t ng_pppoe_shutdown; 7674f5c6aaSJulian Elischer static ng_newhook_t ng_pppoe_newhook; 77b1ba28dfSGleb Smirnoff static ng_connect_t ng_pppoe_connect; 7874f5c6aaSJulian Elischer static ng_rcvdata_t ng_pppoe_rcvdata; 7998e7b753SAlexander Motin static ng_rcvdata_t ng_pppoe_rcvdata_ether; 8098e7b753SAlexander Motin static ng_rcvdata_t ng_pppoe_rcvdata_debug; 8174f5c6aaSJulian Elischer static ng_disconnect_t ng_pppoe_disconnect; 824cf49a43SJulian Elischer 8376a70671SBrian Somers /* Parse type for struct ngpppoe_init_data */ 84f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[] 8576a70671SBrian Somers = NG_PPPOE_INIT_DATA_TYPE_INFO; 8627121ab1SBrian Somers static const struct ng_parse_type ngpppoe_init_data_state_type = { 8776a70671SBrian Somers &ng_parse_struct_type, 88f0184ff8SArchie Cobbs &ngpppoe_init_data_type_fields 8976a70671SBrian Somers }; 9076a70671SBrian Somers 9176a70671SBrian Somers /* Parse type for struct ngpppoe_sts */ 92f0184ff8SArchie Cobbs static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[] 9376a70671SBrian Somers = NG_PPPOE_STS_TYPE_INFO; 9476a70671SBrian Somers static const struct ng_parse_type ng_pppoe_sts_state_type = { 9576a70671SBrian Somers &ng_parse_struct_type, 96f0184ff8SArchie Cobbs &ng_pppoe_sts_type_fields 9776a70671SBrian Somers }; 9876a70671SBrian Somers 9976a70671SBrian Somers /* List of commands and how to convert arguments to/from ASCII */ 10076a70671SBrian Somers static const struct ng_cmdlist ng_pppoe_cmds[] = { 10176a70671SBrian Somers { 10276a70671SBrian Somers NGM_PPPOE_COOKIE, 10376a70671SBrian Somers NGM_PPPOE_CONNECT, 10476a70671SBrian Somers "pppoe_connect", 10527121ab1SBrian Somers &ngpppoe_init_data_state_type, 10676a70671SBrian Somers NULL 10776a70671SBrian Somers }, 10876a70671SBrian Somers { 10976a70671SBrian Somers NGM_PPPOE_COOKIE, 11076a70671SBrian Somers NGM_PPPOE_LISTEN, 11176a70671SBrian Somers "pppoe_listen", 11227121ab1SBrian Somers &ngpppoe_init_data_state_type, 11376a70671SBrian Somers NULL 11476a70671SBrian Somers }, 11576a70671SBrian Somers { 11676a70671SBrian Somers NGM_PPPOE_COOKIE, 11776a70671SBrian Somers NGM_PPPOE_OFFER, 11876a70671SBrian Somers "pppoe_offer", 11927121ab1SBrian Somers &ngpppoe_init_data_state_type, 12076a70671SBrian Somers NULL 12176a70671SBrian Somers }, 12276a70671SBrian Somers { 12376a70671SBrian Somers NGM_PPPOE_COOKIE, 124859a4d16SJulian Elischer NGM_PPPOE_SERVICE, 125859a4d16SJulian Elischer "pppoe_service", 126859a4d16SJulian Elischer &ngpppoe_init_data_state_type, 127859a4d16SJulian Elischer NULL 128859a4d16SJulian Elischer }, 129859a4d16SJulian Elischer { 130859a4d16SJulian Elischer NGM_PPPOE_COOKIE, 13176a70671SBrian Somers NGM_PPPOE_SUCCESS, 13276a70671SBrian Somers "pppoe_success", 13376a70671SBrian Somers &ng_pppoe_sts_state_type, 13476a70671SBrian Somers NULL 13576a70671SBrian Somers }, 13676a70671SBrian Somers { 13776a70671SBrian Somers NGM_PPPOE_COOKIE, 13876a70671SBrian Somers NGM_PPPOE_FAIL, 13976a70671SBrian Somers "pppoe_fail", 14076a70671SBrian Somers &ng_pppoe_sts_state_type, 14176a70671SBrian Somers NULL 14276a70671SBrian Somers }, 14376a70671SBrian Somers { 14476a70671SBrian Somers NGM_PPPOE_COOKIE, 14576a70671SBrian Somers NGM_PPPOE_CLOSE, 14676a70671SBrian Somers "pppoe_close", 14776a70671SBrian Somers &ng_pppoe_sts_state_type, 14876a70671SBrian Somers NULL 14976a70671SBrian Somers }, 150fdc755d1SGleb Smirnoff { 151fdc755d1SGleb Smirnoff NGM_PPPOE_COOKIE, 152fdc755d1SGleb Smirnoff NGM_PPPOE_SETMODE, 153fdc755d1SGleb Smirnoff "pppoe_setmode", 154fdc755d1SGleb Smirnoff &ng_parse_string_type, 155fdc755d1SGleb Smirnoff NULL 156fdc755d1SGleb Smirnoff }, 157fdc755d1SGleb Smirnoff { 158fdc755d1SGleb Smirnoff NGM_PPPOE_COOKIE, 159fdc755d1SGleb Smirnoff NGM_PPPOE_GETMODE, 160fdc755d1SGleb Smirnoff "pppoe_getmode", 161fdc755d1SGleb Smirnoff NULL, 162fdc755d1SGleb Smirnoff &ng_parse_string_type 163fdc755d1SGleb Smirnoff }, 164b1ba28dfSGleb Smirnoff { 165b1ba28dfSGleb Smirnoff NGM_PPPOE_COOKIE, 166b1ba28dfSGleb Smirnoff NGM_PPPOE_SETENADDR, 167b1ba28dfSGleb Smirnoff "setenaddr", 168b1ba28dfSGleb Smirnoff &ng_parse_enaddr_type, 169b1ba28dfSGleb Smirnoff NULL 170b1ba28dfSGleb Smirnoff }, 171*5b363c09SAlexander Motin { 172*5b363c09SAlexander Motin NGM_PPPOE_COOKIE, 173*5b363c09SAlexander Motin NGM_PPPOE_SETMAXP, 174*5b363c09SAlexander Motin "setmaxp", 175*5b363c09SAlexander Motin &ng_parse_uint16_type, 176*5b363c09SAlexander Motin NULL 177*5b363c09SAlexander Motin }, 17876a70671SBrian Somers { 0 } 17976a70671SBrian Somers }; 18076a70671SBrian Somers 1814cf49a43SJulian Elischer /* Netgraph node type descriptor */ 1824cf49a43SJulian Elischer static struct ng_type typestruct = { 183f8aae777SJulian Elischer .version = NG_ABI_VERSION, 184f8aae777SJulian Elischer .name = NG_PPPOE_NODE_TYPE, 185f8aae777SJulian Elischer .constructor = ng_pppoe_constructor, 186f8aae777SJulian Elischer .rcvmsg = ng_pppoe_rcvmsg, 187f8aae777SJulian Elischer .shutdown = ng_pppoe_shutdown, 188f8aae777SJulian Elischer .newhook = ng_pppoe_newhook, 189b1ba28dfSGleb Smirnoff .connect = ng_pppoe_connect, 190f8aae777SJulian Elischer .rcvdata = ng_pppoe_rcvdata, 191f8aae777SJulian Elischer .disconnect = ng_pppoe_disconnect, 192f8aae777SJulian Elischer .cmdlist = ng_pppoe_cmds, 1934cf49a43SJulian Elischer }; 1948876b55dSJulian Elischer NETGRAPH_INIT(pppoe, &typestruct); 1954cf49a43SJulian Elischer 1964cf49a43SJulian Elischer /* 1974cf49a43SJulian Elischer * States for the session state machine. 1984cf49a43SJulian Elischer * These have no meaning if there is no hook attached yet. 1994cf49a43SJulian Elischer */ 2004cf49a43SJulian Elischer enum state { 2014cf49a43SJulian Elischer PPPOE_SNONE=0, /* [both] Initial state */ 2026faf164cSJulian Elischer PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 2034cf49a43SJulian Elischer PPPOE_SINIT, /* [Client] Sent discovery initiation */ 2046faf164cSJulian Elischer PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 2056faf164cSJulian Elischer PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 2064cf49a43SJulian Elischer PPPOE_SREQ, /* [Client] Sent a Request */ 2076faf164cSJulian Elischer PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 2084cf49a43SJulian Elischer PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 2094cf49a43SJulian Elischer PPPOE_DEAD /* [Both] */ 2104cf49a43SJulian Elischer }; 2114cf49a43SJulian Elischer 2124cf49a43SJulian Elischer #define NUMTAGS 20 /* number of tags we are set up to work with */ 2134cf49a43SJulian Elischer 2144cf49a43SJulian Elischer /* 2154cf49a43SJulian Elischer * Information we store for each hook on each node for negotiating the 2164cf49a43SJulian Elischer * session. The mbuf and cluster are freed once negotiation has completed. 2174cf49a43SJulian Elischer * The whole negotiation block is then discarded. 2184cf49a43SJulian Elischer */ 2194cf49a43SJulian Elischer 2204cf49a43SJulian Elischer struct sess_neg { 2214cf49a43SJulian Elischer struct mbuf *m; /* holds cluster with last sent packet */ 2224cf49a43SJulian Elischer union packet *pkt; /* points within the above cluster */ 223ef237c7fSGleb Smirnoff struct callout handle; /* see timeout(9) */ 2244cf49a43SJulian Elischer u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 2254cf49a43SJulian Elischer u_int numtags; 226816b834fSArchie Cobbs const struct pppoe_tag *tags[NUMTAGS]; 2274cf49a43SJulian Elischer u_int service_len; 2284cf49a43SJulian Elischer u_int ac_name_len; 2294cf49a43SJulian Elischer 2304cf49a43SJulian Elischer struct datatag service; 2314cf49a43SJulian Elischer struct datatag ac_name; 2324cf49a43SJulian Elischer }; 2334cf49a43SJulian Elischer typedef struct sess_neg *negp; 2344cf49a43SJulian Elischer 2354cf49a43SJulian Elischer /* 2364cf49a43SJulian Elischer * Session information that is needed after connection. 2374cf49a43SJulian Elischer */ 2382b9cf2f7SArchie Cobbs struct sess_con { 2394cf49a43SJulian Elischer hook_p hook; 2407762e8c6SGleb Smirnoff uint16_t Session_ID; 2414cf49a43SJulian Elischer enum state state; 242069154d5SJulian Elischer ng_ID_t creator; /* who to notify */ 2434cf49a43SJulian Elischer struct pppoe_full_hdr pkt_hdr; /* used when connected */ 2444cf49a43SJulian Elischer negp neg; /* used when negotiating */ 245dda30f12SAlexander Motin LIST_ENTRY(sess_con) sessions; 2464cf49a43SJulian Elischer }; 2472b9cf2f7SArchie Cobbs typedef struct sess_con *sessp; 2484cf49a43SJulian Elischer 249b2b5279bSAlexander Motin #define SESSHASHSIZE 0x0100 250b2b5279bSAlexander Motin #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) 251b2b5279bSAlexander Motin 252b2b5279bSAlexander Motin struct sess_hash_entry { 253b2b5279bSAlexander Motin struct mtx mtx; 254dda30f12SAlexander Motin LIST_HEAD(hhead, sess_con) head; 255b2b5279bSAlexander Motin }; 256b2b5279bSAlexander Motin 2574cf49a43SJulian Elischer /* 2584cf49a43SJulian Elischer * Information we store for each node 2594cf49a43SJulian Elischer */ 2607762e8c6SGleb Smirnoff struct PPPoE { 2614cf49a43SJulian Elischer node_p node; /* back pointer to node */ 2624cf49a43SJulian Elischer hook_p ethernet_hook; 2634cf49a43SJulian Elischer hook_p debug_hook; 2644cf49a43SJulian Elischer u_int packets_in; /* packets in from ethernet */ 2654cf49a43SJulian Elischer u_int packets_out; /* packets out towards ethernet */ 2667762e8c6SGleb Smirnoff uint32_t flags; 2671c8aa594SGleb Smirnoff #define COMPAT_3COM 0x00000001 2681c8aa594SGleb Smirnoff #define COMPAT_DLINK 0x00000002 269b1ba28dfSGleb Smirnoff struct ether_header eh; 270dda30f12SAlexander Motin LIST_HEAD(, sess_con) listeners; 271b2b5279bSAlexander Motin struct sess_hash_entry sesshash[SESSHASHSIZE]; 272*5b363c09SAlexander Motin struct maxptag max_payload; /* PPP-Max-Payload (RFC4638) */ 2734cf49a43SJulian Elischer }; 2747762e8c6SGleb Smirnoff typedef struct PPPoE *priv_p; 2754cf49a43SJulian Elischer 2764cf49a43SJulian Elischer union uniq { 2774cf49a43SJulian Elischer char bytes[sizeof(void *)]; 2784cf49a43SJulian Elischer void *pointer; 2794cf49a43SJulian Elischer }; 2804cf49a43SJulian Elischer 2814cf49a43SJulian Elischer #define LEAVE(x) do { error = x; goto quit; } while(0) 2824cf49a43SJulian Elischer static void pppoe_start(sessp sp); 283ef237c7fSGleb Smirnoff static void pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2); 284816b834fSArchie Cobbs static const struct pppoe_tag *scan_tags(sessp sp, 285816b834fSArchie Cobbs const struct pppoe_hdr* ph); 286b58a8a3bSJulian Elischer static int pppoe_send_event(sessp sp, enum cmd cmdid); 2874cf49a43SJulian Elischer 2884cf49a43SJulian Elischer /************************************************************************* 2894cf49a43SJulian Elischer * Some basic utilities from the Linux version with author's permission.* 2904cf49a43SJulian Elischer * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 2914cf49a43SJulian Elischer ************************************************************************/ 2924cf49a43SJulian Elischer 2934cf49a43SJulian Elischer 2944cf49a43SJulian Elischer 2954cf49a43SJulian Elischer /* 2964cf49a43SJulian Elischer * Return the location where the next tag can be put 2974cf49a43SJulian Elischer */ 298816b834fSArchie Cobbs static __inline const struct pppoe_tag* 299816b834fSArchie Cobbs next_tag(const struct pppoe_hdr* ph) 3004cf49a43SJulian Elischer { 30160d234c5SEd Schouten return (const struct pppoe_tag*)(((const char*)(ph + 1)) 302816b834fSArchie Cobbs + ntohs(ph->length)); 3034cf49a43SJulian Elischer } 3044cf49a43SJulian Elischer 3054cf49a43SJulian Elischer /* 3067762e8c6SGleb Smirnoff * Look for a tag of a specific type. 3077762e8c6SGleb Smirnoff * Don't trust any length the other end says, 3084cf49a43SJulian Elischer * but assume we already sanity checked ph->length. 3094cf49a43SJulian Elischer */ 310816b834fSArchie Cobbs static const struct pppoe_tag* 3117762e8c6SGleb Smirnoff get_tag(const struct pppoe_hdr* ph, uint16_t idx) 3124cf49a43SJulian Elischer { 313816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 31460d234c5SEd Schouten const struct pppoe_tag *pt = (const void *)(ph + 1); 3157762e8c6SGleb Smirnoff const char *ptn; 3167762e8c6SGleb Smirnoff 3174cf49a43SJulian Elischer /* 3184cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 3194cf49a43SJulian Elischer */ 320816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 3214cf49a43SJulian Elischer /* 3224cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 3234cf49a43SJulian Elischer */ 324816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 3257762e8c6SGleb Smirnoff if (ptn > end) { 3267762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: invalid length for tag %d", 3277762e8c6SGleb Smirnoff __func__, idx); 3287762e8c6SGleb Smirnoff return (NULL); 3297762e8c6SGleb Smirnoff } 3307762e8c6SGleb Smirnoff if (pt->tag_type == idx) { 3317762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: found tag %d", __func__, idx); 3327762e8c6SGleb Smirnoff return (pt); 3337762e8c6SGleb Smirnoff } 3344cf49a43SJulian Elischer 335816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 3364cf49a43SJulian Elischer } 3377762e8c6SGleb Smirnoff 3387762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: not found tag %d", __func__, idx); 3397762e8c6SGleb Smirnoff return (NULL); 3404cf49a43SJulian Elischer } 3414cf49a43SJulian Elischer 3424cf49a43SJulian Elischer /************************************************************************** 3437762e8c6SGleb Smirnoff * Inlines to initialise or add tags to a session's tag list. 3444cf49a43SJulian Elischer **************************************************************************/ 3454cf49a43SJulian Elischer /* 3467762e8c6SGleb Smirnoff * Initialise the session's tag list. 3474cf49a43SJulian Elischer */ 3484cf49a43SJulian Elischer static void 3494cf49a43SJulian Elischer init_tags(sessp sp) 3504cf49a43SJulian Elischer { 3517762e8c6SGleb Smirnoff KASSERT(sp->neg != NULL, ("%s: no neg", __func__)); 3524cf49a43SJulian Elischer sp->neg->numtags = 0; 3534cf49a43SJulian Elischer } 3544cf49a43SJulian Elischer 3554cf49a43SJulian Elischer static void 356816b834fSArchie Cobbs insert_tag(sessp sp, const struct pppoe_tag *tp) 3574cf49a43SJulian Elischer { 3587762e8c6SGleb Smirnoff negp neg = sp->neg; 3594cf49a43SJulian Elischer int i; 3604cf49a43SJulian Elischer 3617762e8c6SGleb Smirnoff KASSERT(neg != NULL, ("%s: no neg", __func__)); 3624cf49a43SJulian Elischer if ((i = neg->numtags++) < NUMTAGS) { 3634cf49a43SJulian Elischer neg->tags[i] = tp; 3644cf49a43SJulian Elischer } else { 3652e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe: asked to add too many tags to " 3662e87c3ccSGleb Smirnoff "packet\n"); 36712f035e0SJulian Elischer neg->numtags--; 3684cf49a43SJulian Elischer } 3694cf49a43SJulian Elischer } 3704cf49a43SJulian Elischer 3714cf49a43SJulian Elischer /* 3724cf49a43SJulian Elischer * Make up a packet, using the tags filled out for the session. 3734cf49a43SJulian Elischer * 3744cf49a43SJulian Elischer * Assume that the actual pppoe header and ethernet header 3754cf49a43SJulian Elischer * are filled out externally to this routine. 3764cf49a43SJulian Elischer * Also assume that neg->wh points to the correct 3774cf49a43SJulian Elischer * location at the front of the buffer space. 3784cf49a43SJulian Elischer */ 3794cf49a43SJulian Elischer static void 3804cf49a43SJulian Elischer make_packet(sessp sp) { 3814cf49a43SJulian Elischer struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 382816b834fSArchie Cobbs const struct pppoe_tag **tag; 3834cf49a43SJulian Elischer char *dp; 3844cf49a43SJulian Elischer int count; 3854cf49a43SJulian Elischer int tlen; 3867762e8c6SGleb Smirnoff uint16_t length = 0; 3874cf49a43SJulian Elischer 3887762e8c6SGleb Smirnoff KASSERT((sp->neg != NULL) && (sp->neg->m != NULL), 3892e87c3ccSGleb Smirnoff ("%s: called from wrong state", __func__)); 3907762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 3917762e8c6SGleb Smirnoff 39260d234c5SEd Schouten dp = (char *)(&wh->ph + 1); 3934cf49a43SJulian Elischer for (count = 0, tag = sp->neg->tags; 3944cf49a43SJulian Elischer ((count < sp->neg->numtags) && (count < NUMTAGS)); 3954cf49a43SJulian Elischer tag++, count++) { 3964cf49a43SJulian Elischer tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 3974cf49a43SJulian Elischer if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 3982e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe: tags too long\n"); 3994cf49a43SJulian Elischer sp->neg->numtags = count; 4004cf49a43SJulian Elischer break; /* XXX chop off what's too long */ 4014cf49a43SJulian Elischer } 402816b834fSArchie Cobbs bcopy(*tag, (char *)dp, tlen); 4034cf49a43SJulian Elischer length += tlen; 4044cf49a43SJulian Elischer dp += tlen; 4054cf49a43SJulian Elischer } 4064cf49a43SJulian Elischer wh->ph.length = htons(length); 4074cf49a43SJulian Elischer sp->neg->m->m_len = length + sizeof(*wh); 4084cf49a43SJulian Elischer sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 4094cf49a43SJulian Elischer } 4104cf49a43SJulian Elischer 4114cf49a43SJulian Elischer /************************************************************************** 41268b789b2SGleb Smirnoff * Routines to match a service. * 4134cf49a43SJulian Elischer **************************************************************************/ 41468b789b2SGleb Smirnoff 4154cf49a43SJulian Elischer /* 4164cf49a43SJulian Elischer * Find a hook that has a service string that matches that 41768b789b2SGleb Smirnoff * we are seeking. For now use a simple string. 4184cf49a43SJulian Elischer * In the future we may need something like regexp(). 41968b789b2SGleb Smirnoff * 42068b789b2SGleb Smirnoff * Null string is a wildcard (ANY service), according to RFC2516. 42168b789b2SGleb Smirnoff * And historical FreeBSD wildcard is also "*". 4224cf49a43SJulian Elischer */ 4239088fa05SBrian Somers 4244cf49a43SJulian Elischer static hook_p 42568b789b2SGleb Smirnoff pppoe_match_svc(node_p node, const struct pppoe_tag *tag) 4264cf49a43SJulian Elischer { 427dda30f12SAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 428dda30f12SAlexander Motin sessp sp; 4294cf49a43SJulian Elischer 430dda30f12SAlexander Motin LIST_FOREACH(sp, &privp->listeners, sessions) { 431dda30f12SAlexander Motin negp neg = sp->neg; 4324cf49a43SJulian Elischer 43368b789b2SGleb Smirnoff /* Empty Service-Name matches any service. */ 43468b789b2SGleb Smirnoff if (neg->service_len == 0) 43568b789b2SGleb Smirnoff break; 43668b789b2SGleb Smirnoff 43768b789b2SGleb Smirnoff /* Special case for a blank or "*" service name (wildcard). */ 43868b789b2SGleb Smirnoff if (neg->service_len == 1 && neg->service.data[0] == '*') 43968b789b2SGleb Smirnoff break; 4404cf49a43SJulian Elischer 4414cf49a43SJulian Elischer /* If the lengths don't match, that aint it. */ 44268b789b2SGleb Smirnoff if (neg->service_len != ntohs(tag->tag_len)) 4434cf49a43SJulian Elischer continue; 4444cf49a43SJulian Elischer 44560d234c5SEd Schouten if (strncmp((const char *)(tag + 1), neg->service.data, 44668b789b2SGleb Smirnoff ntohs(tag->tag_len)) == 0) 4474cf49a43SJulian Elischer break; 4484cf49a43SJulian Elischer } 449dda30f12SAlexander Motin CTR3(KTR_NET, "%20s: matched %p for %s", __func__, 45060d234c5SEd Schouten sp?sp->hook:NULL, (const char *)(tag + 1)); 4517762e8c6SGleb Smirnoff 452dda30f12SAlexander Motin return (sp?sp->hook:NULL); 45368b789b2SGleb Smirnoff } 45468b789b2SGleb Smirnoff 45568b789b2SGleb Smirnoff /* 45668b789b2SGleb Smirnoff * Broadcast the PADI packet in m0 to all listening hooks. 45768b789b2SGleb Smirnoff * This routine is called when a PADI with empty Service-Name 45868b789b2SGleb Smirnoff * tag is received. Client should receive PADOs with all 45968b789b2SGleb Smirnoff * available services. 46068b789b2SGleb Smirnoff */ 46168b789b2SGleb Smirnoff static int 46268b789b2SGleb Smirnoff pppoe_broadcast_padi(node_p node, struct mbuf *m0) 46368b789b2SGleb Smirnoff { 464dda30f12SAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 465dda30f12SAlexander Motin sessp sp; 46668b789b2SGleb Smirnoff int error = 0; 46768b789b2SGleb Smirnoff 468dda30f12SAlexander Motin LIST_FOREACH(sp, &privp->listeners, sessions) { 46968b789b2SGleb Smirnoff struct mbuf *m; 47068b789b2SGleb Smirnoff 471eb1b1807SGleb Smirnoff m = m_dup(m0, M_NOWAIT); 47268b789b2SGleb Smirnoff if (m == NULL) 47368b789b2SGleb Smirnoff return (ENOMEM); 474dda30f12SAlexander Motin NG_SEND_DATA_ONLY(error, sp->hook, m); 47568b789b2SGleb Smirnoff if (error) 47668b789b2SGleb Smirnoff return (error); 47768b789b2SGleb Smirnoff } 47868b789b2SGleb Smirnoff 47968b789b2SGleb Smirnoff return (0); 48068b789b2SGleb Smirnoff } 48168b789b2SGleb Smirnoff 48268b789b2SGleb Smirnoff /* 48368b789b2SGleb Smirnoff * Find a hook, which name equals to given service. 48468b789b2SGleb Smirnoff */ 48568b789b2SGleb Smirnoff static hook_p 48668b789b2SGleb Smirnoff pppoe_find_svc(node_p node, const char *svc_name, int svc_len) 48768b789b2SGleb Smirnoff { 488dda30f12SAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 489dda30f12SAlexander Motin sessp sp; 49068b789b2SGleb Smirnoff 491dda30f12SAlexander Motin LIST_FOREACH(sp, &privp->listeners, sessions) { 492dda30f12SAlexander Motin negp neg = sp->neg; 49368b789b2SGleb Smirnoff 49468b789b2SGleb Smirnoff if (neg->service_len == svc_len && 49597b4f83bSAlexander Motin strncmp(svc_name, neg->service.data, svc_len) == 0) 496dda30f12SAlexander Motin return (sp->hook); 49768b789b2SGleb Smirnoff } 49868b789b2SGleb Smirnoff 49968b789b2SGleb Smirnoff return (NULL); 5004cf49a43SJulian Elischer } 5017762e8c6SGleb Smirnoff 5024cf49a43SJulian Elischer /************************************************************************** 503b2b5279bSAlexander Motin * Routines to find a particular session that matches an incoming packet. * 5044cf49a43SJulian Elischer **************************************************************************/ 505bd500dabSAlexander Motin /* Find free session and add to hash. */ 506bd500dabSAlexander Motin static uint16_t 507bd500dabSAlexander Motin pppoe_getnewsession(sessp sp) 508bd500dabSAlexander Motin { 509bd500dabSAlexander Motin const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 510bd500dabSAlexander Motin static uint16_t pppoe_sid = 1; 511bd500dabSAlexander Motin sessp tsp; 512bd500dabSAlexander Motin uint16_t val, hash; 513bd500dabSAlexander Motin 514bd500dabSAlexander Motin restart: 515bd500dabSAlexander Motin /* Atomicity is not needed here as value will be checked. */ 516bd500dabSAlexander Motin val = pppoe_sid++; 517bd500dabSAlexander Motin /* Spec says 0xFFFF is reserved, also don't use 0x0000. */ 518bd500dabSAlexander Motin if (val == 0xffff || val == 0x0000) 519bd500dabSAlexander Motin val = pppoe_sid = 1; 520bd500dabSAlexander Motin 521bd500dabSAlexander Motin /* Check it isn't already in use. */ 522bd500dabSAlexander Motin hash = SESSHASH(val); 523bd500dabSAlexander Motin mtx_lock(&privp->sesshash[hash].mtx); 524dda30f12SAlexander Motin LIST_FOREACH(tsp, &privp->sesshash[hash].head, sessions) { 525bd500dabSAlexander Motin if (tsp->Session_ID == val) 526bd500dabSAlexander Motin break; 527bd500dabSAlexander Motin } 528bd500dabSAlexander Motin if (!tsp) { 529bd500dabSAlexander Motin sp->Session_ID = val; 530dda30f12SAlexander Motin LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions); 531bd500dabSAlexander Motin } 532bd500dabSAlexander Motin mtx_unlock(&privp->sesshash[hash].mtx); 533bd500dabSAlexander Motin if (tsp) 534bd500dabSAlexander Motin goto restart; 535bd500dabSAlexander Motin 536bd500dabSAlexander Motin CTR2(KTR_NET, "%20s: new sid %d", __func__, val); 537bd500dabSAlexander Motin 538bd500dabSAlexander Motin return (val); 539bd500dabSAlexander Motin } 540bd500dabSAlexander Motin 541b2b5279bSAlexander Motin /* Add specified session to hash. */ 542b2b5279bSAlexander Motin static void 543b2b5279bSAlexander Motin pppoe_addsession(sessp sp) 5444cf49a43SJulian Elischer { 545b2b5279bSAlexander Motin const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 546b2b5279bSAlexander Motin uint16_t hash = SESSHASH(sp->Session_ID); 547b2b5279bSAlexander Motin 548b2b5279bSAlexander Motin mtx_lock(&privp->sesshash[hash].mtx); 549dda30f12SAlexander Motin LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions); 550b2b5279bSAlexander Motin mtx_unlock(&privp->sesshash[hash].mtx); 551b2b5279bSAlexander Motin } 552b2b5279bSAlexander Motin 553b2b5279bSAlexander Motin /* Delete specified session from hash. */ 554b2b5279bSAlexander Motin static void 555b2b5279bSAlexander Motin pppoe_delsession(sessp sp) 556b2b5279bSAlexander Motin { 557b2b5279bSAlexander Motin const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 558b2b5279bSAlexander Motin uint16_t hash = SESSHASH(sp->Session_ID); 559b2b5279bSAlexander Motin 560b2b5279bSAlexander Motin mtx_lock(&privp->sesshash[hash].mtx); 561dda30f12SAlexander Motin LIST_REMOVE(sp, sessions); 562b2b5279bSAlexander Motin mtx_unlock(&privp->sesshash[hash].mtx); 563b2b5279bSAlexander Motin } 564b2b5279bSAlexander Motin 565b2b5279bSAlexander Motin /* Find matching peer/session combination. */ 566b2b5279bSAlexander Motin static sessp 567b2b5279bSAlexander Motin pppoe_findsession(priv_p privp, const struct pppoe_full_hdr *wh) 568b2b5279bSAlexander Motin { 5697762e8c6SGleb Smirnoff uint16_t session = ntohs(wh->ph.sid); 570b2b5279bSAlexander Motin uint16_t hash = SESSHASH(session); 571b2b5279bSAlexander Motin sessp sp = NULL; 5724cf49a43SJulian Elischer 573b2b5279bSAlexander Motin mtx_lock(&privp->sesshash[hash].mtx); 574dda30f12SAlexander Motin LIST_FOREACH(sp, &privp->sesshash[hash].head, sessions) { 57599f4de90SAlexander Motin if (sp->Session_ID == session && 57699f4de90SAlexander Motin bcmp(sp->pkt_hdr.eh.ether_dhost, 57799f4de90SAlexander Motin wh->eh.ether_shost, ETHER_ADDR_LEN) == 0) { 5784cf49a43SJulian Elischer break; 5794cf49a43SJulian Elischer } 5804cf49a43SJulian Elischer } 581b2b5279bSAlexander Motin mtx_unlock(&privp->sesshash[hash].mtx); 582b1a3358bSAlexander Motin CTR3(KTR_NET, "%20s: matched %p for %d", __func__, sp?sp->hook:NULL, 583b1a3358bSAlexander Motin session); 5847762e8c6SGleb Smirnoff 585b2b5279bSAlexander Motin return (sp); 5864cf49a43SJulian Elischer } 5874cf49a43SJulian Elischer 5884cf49a43SJulian Elischer static hook_p 589816b834fSArchie Cobbs pppoe_finduniq(node_p node, const struct pppoe_tag *tag) 5904cf49a43SJulian Elischer { 5917762e8c6SGleb Smirnoff hook_p hook = NULL; 5924cf49a43SJulian Elischer union uniq uniq; 5934cf49a43SJulian Elischer 59460d234c5SEd Schouten bcopy(tag + 1, uniq.bytes, sizeof(void *)); 5957762e8c6SGleb Smirnoff /* Cycle through all known hooks. */ 59630400f03SJulian Elischer LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 59799f4de90SAlexander Motin /* Skip any nonsession hook. */ 59899f4de90SAlexander Motin if (NG_HOOK_PRIVATE(hook) == NULL) 5994cf49a43SJulian Elischer continue; 60030400f03SJulian Elischer if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 6014cf49a43SJulian Elischer break; 6024cf49a43SJulian Elischer } 6037762e8c6SGleb Smirnoff CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, uniq.pointer); 6047762e8c6SGleb Smirnoff 6054cf49a43SJulian Elischer return (hook); 6064cf49a43SJulian Elischer } 6074cf49a43SJulian Elischer 6084cf49a43SJulian Elischer /************************************************************************** 6097762e8c6SGleb Smirnoff * Start of Netgraph entrypoints. * 6104cf49a43SJulian Elischer **************************************************************************/ 6114cf49a43SJulian Elischer 6124cf49a43SJulian Elischer /* 6137762e8c6SGleb Smirnoff * Allocate the private data structure and link it with node. 6144cf49a43SJulian Elischer */ 6154cf49a43SJulian Elischer static int 616069154d5SJulian Elischer ng_pppoe_constructor(node_p node) 6174cf49a43SJulian Elischer { 6187762e8c6SGleb Smirnoff priv_p privp; 619b2b5279bSAlexander Motin int i; 6204cf49a43SJulian Elischer 6217762e8c6SGleb Smirnoff /* Initialize private descriptor. */ 622674d86bfSGleb Smirnoff privp = malloc(sizeof(*privp), M_NETGRAPH_PPPOE, M_WAITOK | M_ZERO); 6234cf49a43SJulian Elischer 6247762e8c6SGleb Smirnoff /* Link structs together; this counts as our one reference to *node. */ 6257762e8c6SGleb Smirnoff NG_NODE_SET_PRIVATE(node, privp); 6267762e8c6SGleb Smirnoff privp->node = node; 627fdc755d1SGleb Smirnoff 6281c8aa594SGleb Smirnoff /* Initialize to standard mode. */ 629b1ba28dfSGleb Smirnoff memset(&privp->eh.ether_dhost, 0xff, ETHER_ADDR_LEN); 630b1ba28dfSGleb Smirnoff privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 6317762e8c6SGleb Smirnoff 632dda30f12SAlexander Motin LIST_INIT(&privp->listeners); 633b2b5279bSAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) { 634b2b5279bSAlexander Motin mtx_init(&privp->sesshash[i].mtx, "PPPoE hash mutex", NULL, MTX_DEF); 635dda30f12SAlexander Motin LIST_INIT(&privp->sesshash[i].head); 636b2b5279bSAlexander Motin } 637b2b5279bSAlexander Motin 6387762e8c6SGleb Smirnoff CTR3(KTR_NET, "%20s: created node [%x] (%p)", 6397762e8c6SGleb Smirnoff __func__, node->nd_ID, node); 640fdc755d1SGleb Smirnoff 6414cf49a43SJulian Elischer return (0); 6424cf49a43SJulian Elischer } 6434cf49a43SJulian Elischer 6444cf49a43SJulian Elischer /* 6454cf49a43SJulian Elischer * Give our ok for a hook to be added... 6464cf49a43SJulian Elischer * point the hook's private info to the hook structure. 6474cf49a43SJulian Elischer * 6484cf49a43SJulian Elischer * The following hook names are special: 6497762e8c6SGleb Smirnoff * "ethernet": the hook that should be connected to a NIC. 6507762e8c6SGleb Smirnoff * "debug": copies of data sent out here (when I write the code). 651859a4d16SJulian Elischer * All other hook names need only be unique. (the framework checks this). 6524cf49a43SJulian Elischer */ 6534cf49a43SJulian Elischer static int 6548876b55dSJulian Elischer ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 6554cf49a43SJulian Elischer { 65630400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 6574cf49a43SJulian Elischer sessp sp; 6584cf49a43SJulian Elischer 6594cf49a43SJulian Elischer if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 6604cf49a43SJulian Elischer privp->ethernet_hook = hook; 66198e7b753SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_ether); 6624cf49a43SJulian Elischer } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 6634cf49a43SJulian Elischer privp->debug_hook = hook; 66498e7b753SAlexander Motin NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_debug); 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 */ 6717762e8c6SGleb Smirnoff sp = malloc(sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 6727762e8c6SGleb Smirnoff if (sp == NULL) 6734cf49a43SJulian Elischer return (ENOMEM); 6744cf49a43SJulian Elischer 67530400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, sp); 6764cf49a43SJulian Elischer sp->hook = hook; 6774cf49a43SJulian Elischer } 6787762e8c6SGleb Smirnoff CTR5(KTR_NET, "%20s: node [%x] (%p) connected hook %s (%p)", 6797762e8c6SGleb Smirnoff __func__, node->nd_ID, node, name, hook); 6807762e8c6SGleb Smirnoff 6814cf49a43SJulian Elischer return(0); 6824cf49a43SJulian Elischer } 6834cf49a43SJulian Elischer 6844cf49a43SJulian Elischer /* 685b1ba28dfSGleb Smirnoff * Hook has been added successfully. Request the MAC address of 686b1ba28dfSGleb Smirnoff * the underlying Ethernet node. 687b1ba28dfSGleb Smirnoff */ 688b1ba28dfSGleb Smirnoff static int 689b1ba28dfSGleb Smirnoff ng_pppoe_connect(hook_p hook) 690b1ba28dfSGleb Smirnoff { 691b1ba28dfSGleb Smirnoff const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 692b1ba28dfSGleb Smirnoff struct ng_mesg *msg; 693f366efa9SGleb Smirnoff int error; 694b1ba28dfSGleb Smirnoff 695b1ba28dfSGleb Smirnoff if (hook != privp->ethernet_hook) 696b1ba28dfSGleb Smirnoff return (0); 697b1ba28dfSGleb Smirnoff 698b1ba28dfSGleb Smirnoff /* 699b1ba28dfSGleb Smirnoff * If this is Ethernet hook, then request MAC address 700b1ba28dfSGleb Smirnoff * from our downstream. 701b1ba28dfSGleb Smirnoff */ 702b1ba28dfSGleb Smirnoff NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, 0, M_NOWAIT); 703b1ba28dfSGleb Smirnoff if (msg == NULL) 704b1ba28dfSGleb Smirnoff return (ENOBUFS); 705b1ba28dfSGleb Smirnoff 706b1ba28dfSGleb Smirnoff /* 707b1ba28dfSGleb Smirnoff * Our hook and peer hook have HK_INVALID flag set, 708b1ba28dfSGleb Smirnoff * so we can't use NG_SEND_MSG_HOOK() macro here. 709b1ba28dfSGleb Smirnoff */ 710b1ba28dfSGleb Smirnoff NG_SEND_MSG_ID(error, privp->node, msg, 711b1ba28dfSGleb Smirnoff NG_NODE_ID(NG_PEER_NODE(privp->ethernet_hook)), 712b1ba28dfSGleb Smirnoff NG_NODE_ID(privp->node)); 713b1ba28dfSGleb Smirnoff 714b1ba28dfSGleb Smirnoff return (error); 715b1ba28dfSGleb Smirnoff } 716b1ba28dfSGleb Smirnoff /* 7174cf49a43SJulian Elischer * Get a netgraph control message. 7184cf49a43SJulian Elischer * Check it is one we understand. If needed, send a response. 7194cf49a43SJulian Elischer * We sometimes save the address for an async action later. 7204cf49a43SJulian Elischer * Always free the message. 7214cf49a43SJulian Elischer */ 7224cf49a43SJulian Elischer static int 723069154d5SJulian Elischer ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 7244cf49a43SJulian Elischer { 72530400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 7268876b55dSJulian Elischer struct ngpppoe_init_data *ourmsg = NULL; 7274cf49a43SJulian Elischer struct ng_mesg *resp = NULL; 7284cf49a43SJulian Elischer int error = 0; 7294cf49a43SJulian Elischer hook_p hook = NULL; 7304cf49a43SJulian Elischer sessp sp = NULL; 7314cf49a43SJulian Elischer negp neg = NULL; 732069154d5SJulian Elischer struct ng_mesg *msg; 7334cf49a43SJulian Elischer 734069154d5SJulian Elischer NGI_GET_MSG(item, msg); 7357762e8c6SGleb Smirnoff CTR5(KTR_NET, "%20s: node [%x] (%p) got message %d with cookie %d", 7367762e8c6SGleb Smirnoff __func__, node->nd_ID, node, msg->header.cmd, 7377762e8c6SGleb Smirnoff msg->header.typecookie); 7387762e8c6SGleb Smirnoff 7397762e8c6SGleb Smirnoff /* Deal with message according to cookie and command. */ 7404cf49a43SJulian Elischer switch (msg->header.typecookie) { 7414cf49a43SJulian Elischer case NGM_PPPOE_COOKIE: 7424cf49a43SJulian Elischer switch (msg->header.cmd) { 7434cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 7444cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 7454cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 746859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 74727121ab1SBrian Somers ourmsg = (struct ngpppoe_init_data *)msg->data; 74827121ab1SBrian Somers if (msg->header.arglen < sizeof(*ourmsg)) { 7492e87c3ccSGleb Smirnoff log(LOG_ERR, "ng_pppoe[%x]: init data too " 7502e87c3ccSGleb Smirnoff "small\n", node->nd_ID); 7514cf49a43SJulian Elischer LEAVE(EMSGSIZE); 7524cf49a43SJulian Elischer } 75376a70671SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) > 75476a70671SBrian Somers PPPOE_SERVICE_NAME_SIZE) { 7552e87c3ccSGleb Smirnoff log(LOG_ERR, "ng_pppoe[%x]: service name " 7562e87c3ccSGleb Smirnoff "too big\n", node->nd_ID); 7574cf49a43SJulian Elischer LEAVE(EMSGSIZE); 7584cf49a43SJulian Elischer } 75927121ab1SBrian Somers if (msg->header.arglen - sizeof(*ourmsg) < 76027121ab1SBrian Somers ourmsg->data_len) { 7612e87c3ccSGleb Smirnoff log(LOG_ERR, "ng_pppoe[%x]: init data has bad " 7622e87c3ccSGleb Smirnoff "length, %d should be %zd\n", node->nd_ID, 7632e87c3ccSGleb Smirnoff ourmsg->data_len, 76427121ab1SBrian Somers msg->header.arglen - sizeof (*ourmsg)); 76576a70671SBrian Somers LEAVE(EMSGSIZE); 76676a70671SBrian Somers } 76776a70671SBrian Somers 7687762e8c6SGleb Smirnoff /* Make sure strcmp will terminate safely. */ 7694cf49a43SJulian Elischer ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 7704cf49a43SJulian Elischer 771dda30f12SAlexander Motin /* Find hook by name. */ 772dda30f12SAlexander Motin hook = ng_findhook(node, ourmsg->hook); 7737762e8c6SGleb Smirnoff if (hook == NULL) 7744cf49a43SJulian Elischer LEAVE(ENOENT); 7757762e8c6SGleb Smirnoff 77630400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 77799f4de90SAlexander Motin if (sp == NULL) 77899f4de90SAlexander Motin LEAVE(EINVAL); 77999f4de90SAlexander Motin 7809088fa05SBrian Somers if (msg->header.cmd == NGM_PPPOE_LISTEN) { 7819088fa05SBrian Somers /* 7829088fa05SBrian Somers * Ensure we aren't already listening for this 7839088fa05SBrian Somers * service. 7849088fa05SBrian Somers */ 78568b789b2SGleb Smirnoff if (pppoe_find_svc(node, ourmsg->data, 78668b789b2SGleb Smirnoff ourmsg->data_len) != NULL) 7879088fa05SBrian Somers LEAVE(EEXIST); 7889088fa05SBrian Somers } 7899088fa05SBrian Somers 790859a4d16SJulian Elischer /* 791859a4d16SJulian Elischer * PPPOE_SERVICE advertisments are set up 792859a4d16SJulian Elischer * on sessions that are in PRIMED state. 793859a4d16SJulian Elischer */ 7947762e8c6SGleb Smirnoff if (msg->header.cmd == NGM_PPPOE_SERVICE) 795859a4d16SJulian Elischer break; 7967762e8c6SGleb Smirnoff 797f795fd00SGleb Smirnoff if (sp->state != PPPOE_SNONE) { 7982e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe[%x]: Session already " 7992e87c3ccSGleb Smirnoff "active\n", node->nd_ID); 8004cf49a43SJulian Elischer LEAVE(EISCONN); 8014cf49a43SJulian Elischer } 8021e2510f8SJulian Elischer 8034cf49a43SJulian Elischer /* 8047762e8c6SGleb Smirnoff * Set up prototype header. 8054cf49a43SJulian Elischer */ 8067762e8c6SGleb Smirnoff neg = malloc(sizeof(*neg), M_NETGRAPH_PPPOE, 80799cdf4ccSDavid Malone M_NOWAIT | M_ZERO); 8084cf49a43SJulian Elischer 8097762e8c6SGleb Smirnoff if (neg == NULL) 8104cf49a43SJulian Elischer LEAVE(ENOMEM); 8117762e8c6SGleb Smirnoff 812eb1b1807SGleb Smirnoff neg->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 8134cf49a43SJulian Elischer if (neg->m == NULL) { 8147762e8c6SGleb Smirnoff free(neg, M_NETGRAPH_PPPOE); 8154cf49a43SJulian Elischer LEAVE(ENOBUFS); 8164cf49a43SJulian Elischer } 8174cf49a43SJulian Elischer neg->m->m_pkthdr.rcvif = NULL; 8184cf49a43SJulian Elischer sp->neg = neg; 819ef237c7fSGleb Smirnoff ng_callout_init(&neg->handle); 8204cf49a43SJulian Elischer neg->m->m_len = sizeof(struct pppoe_full_hdr); 8214cf49a43SJulian Elischer neg->pkt = mtod(neg->m, union packet*); 822fdc755d1SGleb Smirnoff memcpy((void *)&neg->pkt->pkt_header.eh, 823b1ba28dfSGleb Smirnoff &privp->eh, sizeof(struct ether_header)); 8244cf49a43SJulian Elischer neg->pkt->pkt_header.ph.ver = 0x1; 8254cf49a43SJulian Elischer neg->pkt->pkt_header.ph.type = 0x1; 8264cf49a43SJulian Elischer neg->pkt->pkt_header.ph.sid = 0x0000; 8274cf49a43SJulian Elischer neg->timeout = 0; 8284cf49a43SJulian Elischer 829069154d5SJulian Elischer sp->creator = NGI_RETADDR(item); 8304cf49a43SJulian Elischer } 8314cf49a43SJulian Elischer switch (msg->header.cmd) { 8324cf49a43SJulian Elischer case NGM_PPPOE_GET_STATUS: 8334cf49a43SJulian Elischer { 8348876b55dSJulian Elischer struct ngpppoestat *stats; 8354cf49a43SJulian Elischer 8364cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 8377762e8c6SGleb Smirnoff if (!resp) 8384cf49a43SJulian Elischer LEAVE(ENOMEM); 8397762e8c6SGleb Smirnoff 8408876b55dSJulian Elischer stats = (struct ngpppoestat *) resp->data; 8414cf49a43SJulian Elischer stats->packets_in = privp->packets_in; 8424cf49a43SJulian Elischer stats->packets_out = privp->packets_out; 8434cf49a43SJulian Elischer break; 8444cf49a43SJulian Elischer } 8454cf49a43SJulian Elischer case NGM_PPPOE_CONNECT: 846098ff746SAlexander Motin { 8474cf49a43SJulian Elischer /* 8484cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8494cf49a43SJulian Elischer * Send a PADI request, and start the timeout logic. 8504cf49a43SJulian Elischer * Store the originator of this message so we can send 8514cf49a43SJulian Elischer * a success of fail message to them later. 8527762e8c6SGleb Smirnoff * Move the session to SINIT. 8534cf49a43SJulian Elischer * Set up the session to the correct state and 8544cf49a43SJulian Elischer * start it. 8554cf49a43SJulian Elischer */ 856098ff746SAlexander Motin int i, acnlen = 0, acnsep = 0, srvlen; 857098ff746SAlexander Motin for (i = 0; i < ourmsg->data_len; i++) { 858098ff746SAlexander Motin if (ourmsg->data[i] == '\\') { 859098ff746SAlexander Motin acnlen = i; 860098ff746SAlexander Motin acnsep = 1; 861098ff746SAlexander Motin break; 862098ff746SAlexander Motin } 863098ff746SAlexander Motin } 864098ff746SAlexander Motin srvlen = ourmsg->data_len - acnlen - acnsep; 865098ff746SAlexander Motin 866098ff746SAlexander Motin bcopy(ourmsg->data, neg->ac_name.data, acnlen); 867098ff746SAlexander Motin neg->ac_name_len = acnlen; 868098ff746SAlexander Motin 8694cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 870098ff746SAlexander Motin neg->service.hdr.tag_len = htons((uint16_t)srvlen); 871098ff746SAlexander Motin bcopy(ourmsg->data + acnlen + acnsep, 872098ff746SAlexander Motin neg->service.data, srvlen); 873098ff746SAlexander Motin neg->service_len = srvlen; 8744cf49a43SJulian Elischer pppoe_start(sp); 8754cf49a43SJulian Elischer break; 876098ff746SAlexander Motin } 8774cf49a43SJulian Elischer case NGM_PPPOE_LISTEN: 8784cf49a43SJulian Elischer /* 8794cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 8804cf49a43SJulian Elischer * Install the service matching string. 8814cf49a43SJulian Elischer * Store the originator of this message so we can send 8824cf49a43SJulian Elischer * a success of fail message to them later. 8834cf49a43SJulian Elischer * Move the hook to 'LISTENING' 8844cf49a43SJulian Elischer */ 8854cf49a43SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 88627121ab1SBrian Somers neg->service.hdr.tag_len = 8877762e8c6SGleb Smirnoff htons((uint16_t)ourmsg->data_len); 8881e2510f8SJulian Elischer 88927121ab1SBrian Somers if (ourmsg->data_len) 89027121ab1SBrian Somers bcopy(ourmsg->data, neg->service.data, 89127121ab1SBrian Somers ourmsg->data_len); 89227121ab1SBrian Somers neg->service_len = ourmsg->data_len; 8934cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADT_CODE; 8944cf49a43SJulian Elischer /* 8957762e8c6SGleb Smirnoff * Wait for PADI packet coming from Ethernet. 8964cf49a43SJulian Elischer */ 8974cf49a43SJulian Elischer sp->state = PPPOE_LISTENING; 898dda30f12SAlexander Motin LIST_INSERT_HEAD(&privp->listeners, sp, sessions); 8994cf49a43SJulian Elischer break; 9004cf49a43SJulian Elischer case NGM_PPPOE_OFFER: 9014cf49a43SJulian Elischer /* 9024cf49a43SJulian Elischer * Check the hook exists and is Uninitialised. 9034cf49a43SJulian Elischer * Store the originator of this message so we can send 9044cf49a43SJulian Elischer * a success of fail message to them later. 9054cf49a43SJulian Elischer * Store the AC-Name given and go to PRIMED. 9064cf49a43SJulian Elischer */ 9074cf49a43SJulian Elischer neg->ac_name.hdr.tag_type = PTT_AC_NAME; 90827121ab1SBrian Somers neg->ac_name.hdr.tag_len = 9097762e8c6SGleb Smirnoff htons((uint16_t)ourmsg->data_len); 91027121ab1SBrian Somers if (ourmsg->data_len) 91127121ab1SBrian Somers bcopy(ourmsg->data, neg->ac_name.data, 91227121ab1SBrian Somers ourmsg->data_len); 91327121ab1SBrian Somers neg->ac_name_len = ourmsg->data_len; 9144cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 9154cf49a43SJulian Elischer /* 9167762e8c6SGleb Smirnoff * Wait for PADI packet coming from hook. 9174cf49a43SJulian Elischer */ 9184cf49a43SJulian Elischer sp->state = PPPOE_PRIMED; 9194cf49a43SJulian Elischer break; 920859a4d16SJulian Elischer case NGM_PPPOE_SERVICE: 921859a4d16SJulian Elischer /* 922859a4d16SJulian Elischer * Check the session is primed. 923859a4d16SJulian Elischer * for now just allow ONE service to be advertised. 924859a4d16SJulian Elischer * If you do it twice you just overwrite. 925859a4d16SJulian Elischer */ 9265078fb0bSJulian Elischer if (sp->state != PPPOE_PRIMED) { 9272e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe[%x]: session not " 9282e87c3ccSGleb Smirnoff "primed\n", node->nd_ID); 929859a4d16SJulian Elischer LEAVE(EISCONN); 930859a4d16SJulian Elischer } 9310069b9cbSJulian Elischer neg = sp->neg; 932859a4d16SJulian Elischer neg->service.hdr.tag_type = PTT_SRV_NAME; 933859a4d16SJulian Elischer neg->service.hdr.tag_len = 9347762e8c6SGleb Smirnoff htons((uint16_t)ourmsg->data_len); 935859a4d16SJulian Elischer 936859a4d16SJulian Elischer if (ourmsg->data_len) 937859a4d16SJulian Elischer bcopy(ourmsg->data, neg->service.data, 938859a4d16SJulian Elischer ourmsg->data_len); 939859a4d16SJulian Elischer neg->service_len = ourmsg->data_len; 940859a4d16SJulian Elischer break; 941fdc755d1SGleb Smirnoff case NGM_PPPOE_SETMODE: 942fdc755d1SGleb Smirnoff { 943fdc755d1SGleb Smirnoff char *s; 944fdc755d1SGleb Smirnoff size_t len; 945fdc755d1SGleb Smirnoff 946fdc755d1SGleb Smirnoff if (msg->header.arglen == 0) 947fdc755d1SGleb Smirnoff LEAVE(EINVAL); 948fdc755d1SGleb Smirnoff 949fdc755d1SGleb Smirnoff s = (char *)msg->data; 950fdc755d1SGleb Smirnoff len = msg->header.arglen - 1; 951fdc755d1SGleb Smirnoff 9527762e8c6SGleb Smirnoff /* Search for matching mode string. */ 9531c8aa594SGleb Smirnoff if (len == strlen(NG_PPPOE_STANDARD) && 9541c8aa594SGleb Smirnoff (strncmp(NG_PPPOE_STANDARD, s, len) == 0)) { 9551c8aa594SGleb Smirnoff privp->flags = 0; 956b1ba28dfSGleb Smirnoff privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 9571c8aa594SGleb Smirnoff break; 9581c8aa594SGleb Smirnoff } 9591c8aa594SGleb Smirnoff if (len == strlen(NG_PPPOE_3COM) && 9601c8aa594SGleb Smirnoff (strncmp(NG_PPPOE_3COM, s, len) == 0)) { 9611c8aa594SGleb Smirnoff privp->flags |= COMPAT_3COM; 962b1ba28dfSGleb Smirnoff privp->eh.ether_type = 963b1ba28dfSGleb Smirnoff ETHERTYPE_PPPOE_3COM_DISC; 9641c8aa594SGleb Smirnoff break; 9651c8aa594SGleb Smirnoff } 9661c8aa594SGleb Smirnoff if (len == strlen(NG_PPPOE_DLINK) && 9671c8aa594SGleb Smirnoff (strncmp(NG_PPPOE_DLINK, s, len) == 0)) { 9681c8aa594SGleb Smirnoff privp->flags |= COMPAT_DLINK; 9691c8aa594SGleb Smirnoff break; 9701c8aa594SGleb Smirnoff } 9711c8aa594SGleb Smirnoff error = EINVAL; 972fdc755d1SGleb Smirnoff break; 973fdc755d1SGleb Smirnoff } 974fdc755d1SGleb Smirnoff case NGM_PPPOE_GETMODE: 9751c8aa594SGleb Smirnoff { 9761c8aa594SGleb Smirnoff char *s; 9771c8aa594SGleb Smirnoff size_t len = 0; 9781c8aa594SGleb Smirnoff 9791c8aa594SGleb Smirnoff if (privp->flags == 0) 9801c8aa594SGleb Smirnoff len += strlen(NG_PPPOE_STANDARD) + 1; 9811c8aa594SGleb Smirnoff if (privp->flags & COMPAT_3COM) 9821c8aa594SGleb Smirnoff len += strlen(NG_PPPOE_3COM) + 1; 9831c8aa594SGleb Smirnoff if (privp->flags & COMPAT_DLINK) 9841c8aa594SGleb Smirnoff len += strlen(NG_PPPOE_DLINK) + 1; 9851c8aa594SGleb Smirnoff 9861c8aa594SGleb Smirnoff NG_MKRESPONSE(resp, msg, len, M_NOWAIT); 987fdc755d1SGleb Smirnoff if (resp == NULL) 988fdc755d1SGleb Smirnoff LEAVE(ENOMEM); 9894cf49a43SJulian Elischer 9901c8aa594SGleb Smirnoff s = (char *)resp->data; 9911c8aa594SGleb Smirnoff if (privp->flags == 0) { 9921c8aa594SGleb Smirnoff len = strlen(NG_PPPOE_STANDARD); 9931c8aa594SGleb Smirnoff strlcpy(s, NG_PPPOE_STANDARD, len + 1); 9941c8aa594SGleb Smirnoff break; 9951c8aa594SGleb Smirnoff } 9961c8aa594SGleb Smirnoff if (privp->flags & COMPAT_3COM) { 9971c8aa594SGleb Smirnoff len = strlen(NG_PPPOE_3COM); 9981c8aa594SGleb Smirnoff strlcpy(s, NG_PPPOE_3COM, len + 1); 9991c8aa594SGleb Smirnoff s += len; 10001c8aa594SGleb Smirnoff } 10011c8aa594SGleb Smirnoff if (privp->flags & COMPAT_DLINK) { 10021c8aa594SGleb Smirnoff if (s != resp->data) 10031c8aa594SGleb Smirnoff *s++ = '|'; 10041c8aa594SGleb Smirnoff len = strlen(NG_PPPOE_DLINK); 10051c8aa594SGleb Smirnoff strlcpy(s, NG_PPPOE_DLINK, len + 1); 10061c8aa594SGleb Smirnoff } 10071c8aa594SGleb Smirnoff break; 10081c8aa594SGleb Smirnoff } 1009b1ba28dfSGleb Smirnoff case NGM_PPPOE_SETENADDR: 1010b1ba28dfSGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) 1011b1ba28dfSGleb Smirnoff LEAVE(EINVAL); 1012b1ba28dfSGleb Smirnoff bcopy(msg->data, &privp->eh.ether_shost, 1013b1ba28dfSGleb Smirnoff ETHER_ADDR_LEN); 1014b1ba28dfSGleb Smirnoff break; 1015*5b363c09SAlexander Motin case NGM_PPPOE_SETMAXP: 1016*5b363c09SAlexander Motin if (msg->header.arglen != sizeof(uint16_t)) 1017*5b363c09SAlexander Motin LEAVE(EINVAL); 1018*5b363c09SAlexander Motin privp->max_payload.hdr.tag_type = PTT_MAX_PAYL; 1019*5b363c09SAlexander Motin privp->max_payload.hdr.tag_len = htons(sizeof(uint16_t)); 1020*5b363c09SAlexander Motin privp->max_payload.data = htons(*((uint16_t *)msg->data)); 1021*5b363c09SAlexander Motin break; 10221c8aa594SGleb Smirnoff default: 10231c8aa594SGleb Smirnoff LEAVE(EINVAL); 10241c8aa594SGleb Smirnoff } 10251c8aa594SGleb Smirnoff break; 1026b1ba28dfSGleb Smirnoff case NGM_ETHER_COOKIE: 1027b1ba28dfSGleb Smirnoff if (!(msg->header.flags & NGF_RESP)) 1028b1ba28dfSGleb Smirnoff LEAVE(EINVAL); 1029b1ba28dfSGleb Smirnoff switch (msg->header.cmd) { 1030b1ba28dfSGleb Smirnoff case NGM_ETHER_GET_ENADDR: 1031b1ba28dfSGleb Smirnoff if (msg->header.arglen != ETHER_ADDR_LEN) 1032b1ba28dfSGleb Smirnoff LEAVE(EINVAL); 1033b1ba28dfSGleb Smirnoff bcopy(msg->data, &privp->eh.ether_shost, 1034b1ba28dfSGleb Smirnoff ETHER_ADDR_LEN); 1035b1ba28dfSGleb Smirnoff break; 1036b1ba28dfSGleb Smirnoff default: 1037f366efa9SGleb Smirnoff LEAVE(EINVAL); 1038b1ba28dfSGleb Smirnoff } 1039b1ba28dfSGleb Smirnoff break; 10401c8aa594SGleb Smirnoff default: 10411c8aa594SGleb Smirnoff LEAVE(EINVAL); 10421c8aa594SGleb Smirnoff } 10437762e8c6SGleb Smirnoff 10447762e8c6SGleb Smirnoff /* Take care of synchronous response, if any. */ 10454cf49a43SJulian Elischer quit: 10461c8aa594SGleb Smirnoff CTR2(KTR_NET, "%20s: returning %d", __func__, error); 1047069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 10487762e8c6SGleb Smirnoff /* Free the message and return. */ 1049069154d5SJulian Elischer NG_FREE_MSG(msg); 10504cf49a43SJulian Elischer return(error); 10514cf49a43SJulian Elischer } 10524cf49a43SJulian Elischer 10531e2510f8SJulian Elischer /* 10541e2510f8SJulian Elischer * Start a client into the first state. A separate function because 10551e2510f8SJulian Elischer * it can be needed if the negotiation times out. 10561e2510f8SJulian Elischer */ 10574cf49a43SJulian Elischer static void 10584cf49a43SJulian Elischer pppoe_start(sessp sp) 10594cf49a43SJulian Elischer { 10608cfaad5fSAlexander Motin hook_p hook = sp->hook; 10618cfaad5fSAlexander Motin node_p node = NG_HOOK_NODE(hook); 10628cfaad5fSAlexander Motin priv_p privp = NG_NODE_PRIVATE(node); 10638cfaad5fSAlexander Motin negp neg = sp->neg; 10644cf49a43SJulian Elischer struct { 10654cf49a43SJulian Elischer struct pppoe_tag hdr; 10664cf49a43SJulian Elischer union uniq data; 10674f492bfaSAlfred Perlstein } __packed uniqtag; 10688cfaad5fSAlexander Motin struct mbuf *m0; 10698cfaad5fSAlexander Motin int error; 10704cf49a43SJulian Elischer 10714cf49a43SJulian Elischer /* 10727762e8c6SGleb Smirnoff * Kick the state machine into starting up. 10734cf49a43SJulian Elischer */ 10747762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 10754cf49a43SJulian Elischer sp->state = PPPOE_SINIT; 10767762e8c6SGleb Smirnoff /* 10777762e8c6SGleb Smirnoff * Reset the packet header to broadcast. Since we are 1078b1ba28dfSGleb Smirnoff * in a client mode use configured ethertype. 10797762e8c6SGleb Smirnoff */ 10808cfaad5fSAlexander Motin memcpy((void *)&neg->pkt->pkt_header.eh, &privp->eh, 1081b1ba28dfSGleb Smirnoff sizeof(struct ether_header)); 10828cfaad5fSAlexander Motin neg->pkt->pkt_header.ph.code = PADI_CODE; 10834cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 10844cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 10854cf49a43SJulian Elischer uniqtag.data.pointer = sp; 10864cf49a43SJulian Elischer init_tags(sp); 10871f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 10888cfaad5fSAlexander Motin insert_tag(sp, &neg->service.hdr); 1089*5b363c09SAlexander Motin if (privp->max_payload.data != 0) 1090*5b363c09SAlexander Motin insert_tag(sp, &privp->max_payload.hdr); 10914cf49a43SJulian Elischer make_packet(sp); 10928cfaad5fSAlexander Motin /* 10938cfaad5fSAlexander Motin * Send packet and prepare to retransmit it after timeout. 10948cfaad5fSAlexander Motin */ 10958cfaad5fSAlexander Motin ng_callout(&neg->handle, node, hook, PPPOE_INITIAL_TIMEOUT * hz, 10968cfaad5fSAlexander Motin pppoe_ticker, NULL, 0); 10978cfaad5fSAlexander Motin neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1098eb1b1807SGleb Smirnoff m0 = m_copypacket(neg->m, M_NOWAIT); 10998cfaad5fSAlexander Motin NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 11004cf49a43SJulian Elischer } 11014cf49a43SJulian Elischer 1102c48a0b5fSBrian Somers static int 1103816b834fSArchie Cobbs send_acname(sessp sp, const struct pppoe_tag *tag) 1104c48a0b5fSBrian Somers { 11059e6798e7SBrian Somers int error, tlen; 1106c48a0b5fSBrian Somers struct ng_mesg *msg; 1107c48a0b5fSBrian Somers struct ngpppoe_sts *sts; 1108c48a0b5fSBrian Somers 11097762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 11107762e8c6SGleb Smirnoff 1111c48a0b5fSBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME, 1112c48a0b5fSBrian Somers sizeof(struct ngpppoe_sts), M_NOWAIT); 1113c48a0b5fSBrian Somers if (msg == NULL) 1114c48a0b5fSBrian Somers return (ENOMEM); 1115c48a0b5fSBrian Somers 1116c48a0b5fSBrian Somers sts = (struct ngpppoe_sts *)msg->data; 111787e2c66aSHartmut Brandt tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len)); 111860d234c5SEd Schouten strncpy(sts->hook, (const char *)(tag + 1), tlen); 11199e6798e7SBrian Somers sts->hook[tlen] = '\0'; 1120facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1121c48a0b5fSBrian Somers 1122c48a0b5fSBrian Somers return (error); 1123c48a0b5fSBrian Somers } 1124c48a0b5fSBrian Somers 112587c4cce0SBrian Somers static int 112687c4cce0SBrian Somers send_sessionid(sessp sp) 112787c4cce0SBrian Somers { 112887c4cce0SBrian Somers int error; 112987c4cce0SBrian Somers struct ng_mesg *msg; 113087c4cce0SBrian Somers 11317762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 11327762e8c6SGleb Smirnoff 113387c4cce0SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID, 11347762e8c6SGleb Smirnoff sizeof(uint16_t), M_NOWAIT); 113587c4cce0SBrian Somers if (msg == NULL) 113687c4cce0SBrian Somers return (ENOMEM); 113787c4cce0SBrian Somers 11387762e8c6SGleb Smirnoff *(uint16_t *)msg->data = sp->Session_ID; 1139facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 114087c4cce0SBrian Somers 114187c4cce0SBrian Somers return (error); 114287c4cce0SBrian Somers } 114387c4cce0SBrian Somers 1144*5b363c09SAlexander Motin static int 1145*5b363c09SAlexander Motin send_maxp(sessp sp, const struct pppoe_tag *tag) 1146*5b363c09SAlexander Motin { 1147*5b363c09SAlexander Motin int error; 1148*5b363c09SAlexander Motin struct ng_mesg *msg; 1149*5b363c09SAlexander Motin struct ngpppoe_maxp *maxp; 1150*5b363c09SAlexander Motin 1151*5b363c09SAlexander Motin CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1152*5b363c09SAlexander Motin 1153*5b363c09SAlexander Motin NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SETMAXP, 1154*5b363c09SAlexander Motin sizeof(struct ngpppoe_maxp), M_NOWAIT); 1155*5b363c09SAlexander Motin if (msg == NULL) 1156*5b363c09SAlexander Motin return (ENOMEM); 1157*5b363c09SAlexander Motin 1158*5b363c09SAlexander Motin maxp = (struct ngpppoe_maxp *)msg->data; 1159*5b363c09SAlexander Motin strncpy(maxp->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 1160*5b363c09SAlexander Motin maxp->data = ntohs(((const struct maxptag *)tag)->data); 1161*5b363c09SAlexander Motin NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1162*5b363c09SAlexander Motin 1163*5b363c09SAlexander Motin return (error); 1164*5b363c09SAlexander Motin } 1165*5b363c09SAlexander Motin 11664cf49a43SJulian Elischer /* 116798e7b753SAlexander Motin * Receive data from session hook and do something with it. 11684cf49a43SJulian Elischer */ 11694cf49a43SJulian Elischer static int 1170069154d5SJulian Elischer ng_pppoe_rcvdata(hook_p hook, item_p item) 11714cf49a43SJulian Elischer { 117230400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 117330400f03SJulian Elischer const priv_p privp = NG_NODE_PRIVATE(node); 117430400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 117598e7b753SAlexander Motin struct pppoe_full_hdr *wh; 11767762e8c6SGleb Smirnoff struct mbuf *m; 117798e7b753SAlexander Motin int error; 11784cf49a43SJulian Elischer 11797762e8c6SGleb Smirnoff CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 11807762e8c6SGleb Smirnoff __func__, node->nd_ID, node, item, hook->hk_name, hook); 11817762e8c6SGleb Smirnoff 1182069154d5SJulian Elischer NGI_GET_M(item, m); 11834cf49a43SJulian Elischer switch (sp->state) { 11844cf49a43SJulian Elischer case PPPOE_NEWCONNECTED: 11854cf49a43SJulian Elischer case PPPOE_CONNECTED: { 11867b38c4e4SArchie Cobbs /* 11877b38c4e4SArchie Cobbs * Remove PPP address and control fields, if any. 11887b38c4e4SArchie Cobbs * For example, ng_ppp(4) always sends LCP packets 11897b38c4e4SArchie Cobbs * with address and control fields as required by 11907b38c4e4SArchie Cobbs * generic PPP. PPPoE is an exception to the rule. 11917b38c4e4SArchie Cobbs */ 11927b38c4e4SArchie Cobbs if (m->m_pkthdr.len >= 2) { 11937b38c4e4SArchie Cobbs if (m->m_len < 2 && !(m = m_pullup(m, 2))) 11947b38c4e4SArchie Cobbs LEAVE(ENOBUFS); 1195b4d0be22SAlexander Motin if (mtod(m, u_char *)[0] == 0xff && 1196b4d0be22SAlexander Motin mtod(m, u_char *)[1] == 0x03) 11977b38c4e4SArchie Cobbs m_adj(m, 2); 11987b38c4e4SArchie Cobbs } 11994cf49a43SJulian Elischer /* 12004cf49a43SJulian Elischer * Bang in a pre-made header, and set the length up 12014cf49a43SJulian Elischer * to be correct. Then send it to the ethernet driver. 12024cf49a43SJulian Elischer */ 1203eb1b1807SGleb Smirnoff M_PREPEND(m, sizeof(*wh), M_NOWAIT); 12047762e8c6SGleb Smirnoff if (m == NULL) 12054cf49a43SJulian Elischer LEAVE(ENOBUFS); 12067762e8c6SGleb Smirnoff 12074cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 12084cf49a43SJulian Elischer bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 12091e7d84b0SAlexander Motin wh->ph.length = htons(m->m_pkthdr.len - sizeof(*wh)); 1210069154d5SJulian Elischer NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m); 12114cf49a43SJulian Elischer privp->packets_out++; 12124cf49a43SJulian Elischer break; 12134cf49a43SJulian Elischer } 121498e7b753SAlexander Motin case PPPOE_PRIMED: { 121598e7b753SAlexander Motin struct { 121698e7b753SAlexander Motin struct pppoe_tag hdr; 121798e7b753SAlexander Motin union uniq data; 121898e7b753SAlexander Motin } __packed uniqtag; 121998e7b753SAlexander Motin const struct pppoe_tag *tag; 122098e7b753SAlexander Motin struct mbuf *m0; 122198e7b753SAlexander Motin const struct pppoe_hdr *ph; 122298e7b753SAlexander Motin negp neg = sp->neg; 122398e7b753SAlexander Motin uint16_t session; 122498e7b753SAlexander Motin uint16_t length; 122598e7b753SAlexander Motin uint8_t code; 122698e7b753SAlexander Motin 12274cf49a43SJulian Elischer /* 12284cf49a43SJulian Elischer * A PADI packet is being returned by the application 12294cf49a43SJulian Elischer * that has set up this hook. This indicates that it 12304cf49a43SJulian Elischer * wants us to offer service. 12314cf49a43SJulian Elischer */ 1232bdaf2e81SJulian Elischer if (m->m_len < sizeof(*wh)) { 1233bdaf2e81SJulian Elischer m = m_pullup(m, sizeof(*wh)); 12347762e8c6SGleb Smirnoff if (m == NULL) 12354cf49a43SJulian Elischer LEAVE(ENOBUFS); 12364cf49a43SJulian Elischer } 12374cf49a43SJulian Elischer wh = mtod(m, struct pppoe_full_hdr *); 12384cf49a43SJulian Elischer ph = &wh->ph; 12394cf49a43SJulian Elischer session = ntohs(wh->ph.sid); 12404cf49a43SJulian Elischer length = ntohs(wh->ph.length); 12414cf49a43SJulian Elischer code = wh->ph.code; 12427762e8c6SGleb Smirnoff /* Use peers mode in session. */ 1243fdc755d1SGleb Smirnoff neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type; 12447762e8c6SGleb Smirnoff if (code != PADI_CODE) 12451e2510f8SJulian Elischer LEAVE(EINVAL); 1246ef237c7fSGleb Smirnoff ng_uncallout(&neg->handle, node); 12474cf49a43SJulian Elischer 12484cf49a43SJulian Elischer /* 12494cf49a43SJulian Elischer * This is the first time we hear 12504cf49a43SJulian Elischer * from the client, so note it's 12514cf49a43SJulian Elischer * unicast address, replacing the 12524cf49a43SJulian Elischer * broadcast address. 12534cf49a43SJulian Elischer */ 12544cf49a43SJulian Elischer bcopy(wh->eh.ether_shost, 12554cf49a43SJulian Elischer neg->pkt->pkt_header.eh.ether_dhost, 12564cf49a43SJulian Elischer ETHER_ADDR_LEN); 12574cf49a43SJulian Elischer sp->state = PPPOE_SOFFER; 12584cf49a43SJulian Elischer neg->timeout = 0; 12594cf49a43SJulian Elischer neg->pkt->pkt_header.ph.code = PADO_CODE; 12604cf49a43SJulian Elischer 12614cf49a43SJulian Elischer /* 12627762e8c6SGleb Smirnoff * Start working out the tags to respond with. 12634cf49a43SJulian Elischer */ 12644cf49a43SJulian Elischer uniqtag.hdr.tag_type = PTT_AC_COOKIE; 12654cf49a43SJulian Elischer uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 12664cf49a43SJulian Elischer uniqtag.data.pointer = sp; 12674cf49a43SJulian Elischer init_tags(sp); 12684cf49a43SJulian Elischer insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 12691f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_SRV_NAME))) 12704adb13fdSJulian Elischer insert_tag(sp, tag); /* return service */ 1271859a4d16SJulian Elischer /* 1272859a4d16SJulian Elischer * If we have a NULL service request 1273859a4d16SJulian Elischer * and have an extra service defined in this hook, 1274859a4d16SJulian Elischer * then also add a tag for the extra service. 1275859a4d16SJulian Elischer * XXX this is a hack. eventually we should be able 1276859a4d16SJulian Elischer * to support advertising many services, not just one 1277859a4d16SJulian Elischer */ 12787762e8c6SGleb Smirnoff if (((tag == NULL) || (tag->tag_len == 0)) && 12797762e8c6SGleb Smirnoff (neg->service.hdr.tag_len != 0)) { 1280859a4d16SJulian Elischer insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1281859a4d16SJulian Elischer } 12821f89d938SJulian Elischer if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 12831f89d938SJulian Elischer insert_tag(sp, tag); /* returned hostunique */ 12841f89d938SJulian Elischer insert_tag(sp, &uniqtag.hdr); 12854cf49a43SJulian Elischer scan_tags(sp, ph); 12864cf49a43SJulian Elischer make_packet(sp); 12878cfaad5fSAlexander Motin /* 12888cfaad5fSAlexander Motin * Send the offer but if they don't respond 12898cfaad5fSAlexander Motin * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 12908cfaad5fSAlexander Motin */ 12918cfaad5fSAlexander Motin ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz, 12928cfaad5fSAlexander Motin pppoe_ticker, NULL, 0); 1293eb1b1807SGleb Smirnoff m0 = m_copypacket(sp->neg->m, M_NOWAIT); 129498e7b753SAlexander Motin NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 129598e7b753SAlexander Motin privp->packets_out++; 12964cf49a43SJulian Elischer break; 129798e7b753SAlexander Motin } 12984cf49a43SJulian Elischer 12994cf49a43SJulian Elischer /* 13004cf49a43SJulian Elischer * Packets coming from the hook make no sense 130198e7b753SAlexander Motin * to sessions in the rest of states. Throw them away. 13024cf49a43SJulian Elischer */ 13034cf49a43SJulian Elischer default: 13044cf49a43SJulian Elischer LEAVE(ENETUNREACH); 13054cf49a43SJulian Elischer } 13064cf49a43SJulian Elischer quit: 1307f5856029SJulian Elischer if (item) 1308069154d5SJulian Elischer NG_FREE_ITEM(item); 1309069154d5SJulian Elischer NG_FREE_M(m); 131098e7b753SAlexander Motin return (error); 131198e7b753SAlexander Motin } 131298e7b753SAlexander Motin 131398e7b753SAlexander Motin /* 131498e7b753SAlexander Motin * Receive data from ether and do something with it. 131598e7b753SAlexander Motin */ 131698e7b753SAlexander Motin static int 131798e7b753SAlexander Motin ng_pppoe_rcvdata_ether(hook_p hook, item_p item) 131898e7b753SAlexander Motin { 131998e7b753SAlexander Motin node_p node = NG_HOOK_NODE(hook); 132098e7b753SAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 13215a73d193SAlexander Motin sessp sp; 132298e7b753SAlexander Motin const struct pppoe_tag *utag = NULL, *tag = NULL; 132398e7b753SAlexander Motin const struct pppoe_full_hdr *wh; 132498e7b753SAlexander Motin const struct pppoe_hdr *ph; 132598e7b753SAlexander Motin negp neg = NULL; 132698e7b753SAlexander Motin struct mbuf *m; 132798e7b753SAlexander Motin hook_p sendhook; 132898e7b753SAlexander Motin int error = 0; 132998e7b753SAlexander Motin uint16_t session; 133098e7b753SAlexander Motin uint16_t length; 133198e7b753SAlexander Motin uint8_t code; 133298e7b753SAlexander Motin struct mbuf *m0; 133398e7b753SAlexander Motin 133498e7b753SAlexander Motin CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 133598e7b753SAlexander Motin __func__, node->nd_ID, node, item, hook->hk_name, hook); 133698e7b753SAlexander Motin 133798e7b753SAlexander Motin NGI_GET_M(item, m); 133898e7b753SAlexander Motin /* 133998e7b753SAlexander Motin * Dig out various fields from the packet. 134098e7b753SAlexander Motin * Use them to decide where to send it. 134198e7b753SAlexander Motin */ 134298e7b753SAlexander Motin privp->packets_in++; 134398e7b753SAlexander Motin if( m->m_len < sizeof(*wh)) { 134498e7b753SAlexander Motin m = m_pullup(m, sizeof(*wh)); /* Checks length */ 134598e7b753SAlexander Motin if (m == NULL) { 134698e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: couldn't " 134798e7b753SAlexander Motin "m_pullup(wh)\n", node->nd_ID); 134898e7b753SAlexander Motin LEAVE(ENOBUFS); 134998e7b753SAlexander Motin } 135098e7b753SAlexander Motin } 135198e7b753SAlexander Motin wh = mtod(m, struct pppoe_full_hdr *); 135298e7b753SAlexander Motin length = ntohs(wh->ph.length); 135398e7b753SAlexander Motin switch(wh->eh.ether_type) { 135498e7b753SAlexander Motin case ETHERTYPE_PPPOE_3COM_DISC: /* fall through */ 135598e7b753SAlexander Motin case ETHERTYPE_PPPOE_DISC: 135698e7b753SAlexander Motin /* 135798e7b753SAlexander Motin * We need to try to make sure that the tag area 135898e7b753SAlexander Motin * is contiguous, or we could wander off the end 135998e7b753SAlexander Motin * of a buffer and make a mess. 136098e7b753SAlexander Motin * (Linux wouldn't have this problem). 136198e7b753SAlexander Motin */ 136298e7b753SAlexander Motin if (m->m_pkthdr.len <= MHLEN) { 136398e7b753SAlexander Motin if( m->m_len < m->m_pkthdr.len) { 136498e7b753SAlexander Motin m = m_pullup(m, m->m_pkthdr.len); 136598e7b753SAlexander Motin if (m == NULL) { 136698e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: " 136798e7b753SAlexander Motin "couldn't m_pullup(pkthdr)\n", 136898e7b753SAlexander Motin node->nd_ID); 136998e7b753SAlexander Motin LEAVE(ENOBUFS); 137098e7b753SAlexander Motin } 137198e7b753SAlexander Motin } 137298e7b753SAlexander Motin } 137398e7b753SAlexander Motin if (m->m_len != m->m_pkthdr.len) { 137498e7b753SAlexander Motin /* 137598e7b753SAlexander Motin * It's not all in one piece. 137698e7b753SAlexander Motin * We need to do extra work. 137798e7b753SAlexander Motin * Put it into a cluster. 137898e7b753SAlexander Motin */ 137998e7b753SAlexander Motin struct mbuf *n; 1380eb1b1807SGleb Smirnoff n = m_dup(m, M_NOWAIT); 138198e7b753SAlexander Motin m_freem(m); 138298e7b753SAlexander Motin m = n; 138398e7b753SAlexander Motin if (m) { 138498e7b753SAlexander Motin /* just check we got a cluster */ 138598e7b753SAlexander Motin if (m->m_len != m->m_pkthdr.len) { 138698e7b753SAlexander Motin m_freem(m); 138798e7b753SAlexander Motin m = NULL; 138898e7b753SAlexander Motin } 138998e7b753SAlexander Motin } 139098e7b753SAlexander Motin if (m == NULL) { 139198e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: packet " 139298e7b753SAlexander Motin "fragmented\n", node->nd_ID); 139398e7b753SAlexander Motin LEAVE(EMSGSIZE); 139498e7b753SAlexander Motin } 139598e7b753SAlexander Motin } 139698e7b753SAlexander Motin wh = mtod(m, struct pppoe_full_hdr *); 139798e7b753SAlexander Motin length = ntohs(wh->ph.length); 139898e7b753SAlexander Motin ph = &wh->ph; 139998e7b753SAlexander Motin session = ntohs(wh->ph.sid); 140098e7b753SAlexander Motin code = wh->ph.code; 140198e7b753SAlexander Motin 140298e7b753SAlexander Motin switch(code) { 140398e7b753SAlexander Motin case PADI_CODE: 140498e7b753SAlexander Motin /* 140598e7b753SAlexander Motin * We are a server: 140698e7b753SAlexander Motin * Look for a hook with the required service and send 140798e7b753SAlexander Motin * the ENTIRE packet up there. It should come back to 140898e7b753SAlexander Motin * a new hook in PRIMED state. Look there for further 140998e7b753SAlexander Motin * processing. 141098e7b753SAlexander Motin */ 141198e7b753SAlexander Motin tag = get_tag(ph, PTT_SRV_NAME); 141298e7b753SAlexander Motin if (tag == NULL) { 141398e7b753SAlexander Motin CTR1(KTR_NET, "%20s: PADI w/o Service-Name", 141498e7b753SAlexander Motin __func__); 141598e7b753SAlexander Motin LEAVE(ENETUNREACH); 141698e7b753SAlexander Motin } 141798e7b753SAlexander Motin 141898e7b753SAlexander Motin /* 141998e7b753SAlexander Motin * First, try to match Service-Name against our 142098e7b753SAlexander Motin * listening hooks. If no success and we are in D-Link 142198e7b753SAlexander Motin * compat mode and Service-Name is empty, then we 142298e7b753SAlexander Motin * broadcast the PADI to all listening hooks. 142398e7b753SAlexander Motin */ 142498e7b753SAlexander Motin sendhook = pppoe_match_svc(node, tag); 142598e7b753SAlexander Motin if (sendhook != NULL) 142698e7b753SAlexander Motin NG_FWD_NEW_DATA(error, item, sendhook, m); 142798e7b753SAlexander Motin else if (privp->flags & COMPAT_DLINK && 142898e7b753SAlexander Motin ntohs(tag->tag_len) == 0) 142998e7b753SAlexander Motin error = pppoe_broadcast_padi(node, m); 143098e7b753SAlexander Motin else 143198e7b753SAlexander Motin error = ENETUNREACH; 143298e7b753SAlexander Motin break; 143398e7b753SAlexander Motin case PADO_CODE: 143498e7b753SAlexander Motin /* 143598e7b753SAlexander Motin * We are a client: 143698e7b753SAlexander Motin * Use the host_uniq tag to find the hook this is in 143798e7b753SAlexander Motin * response to. Received #2, now send #3 143898e7b753SAlexander Motin * For now simply accept the first we receive. 143998e7b753SAlexander Motin */ 144098e7b753SAlexander Motin utag = get_tag(ph, PTT_HOST_UNIQ); 144198e7b753SAlexander Motin if ((utag == NULL) || 144298e7b753SAlexander Motin (ntohs(utag->tag_len) != sizeof(sp))) { 144398e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: no host " 144498e7b753SAlexander Motin "unique field\n", node->nd_ID); 144598e7b753SAlexander Motin LEAVE(ENETUNREACH); 144698e7b753SAlexander Motin } 144798e7b753SAlexander Motin 144898e7b753SAlexander Motin sendhook = pppoe_finduniq(node, utag); 144998e7b753SAlexander Motin if (sendhook == NULL) { 145098e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: no " 145198e7b753SAlexander Motin "matching session\n", node->nd_ID); 145298e7b753SAlexander Motin LEAVE(ENETUNREACH); 145398e7b753SAlexander Motin } 145498e7b753SAlexander Motin 145598e7b753SAlexander Motin /* 145698e7b753SAlexander Motin * Check the session is in the right state. 145798e7b753SAlexander Motin * It needs to be in PPPOE_SINIT. 145898e7b753SAlexander Motin */ 145998e7b753SAlexander Motin sp = NG_HOOK_PRIVATE(sendhook); 1460098ff746SAlexander Motin if (sp->state == PPPOE_SREQ || 1461098ff746SAlexander Motin sp->state == PPPOE_CONNECTED) { 1462098ff746SAlexander Motin break; /* Multiple PADO is OK. */ 1463098ff746SAlexander Motin } 146498e7b753SAlexander Motin if (sp->state != PPPOE_SINIT) { 146598e7b753SAlexander Motin log(LOG_NOTICE, "ng_pppoe[%x]: session " 146698e7b753SAlexander Motin "in wrong state\n", node->nd_ID); 146798e7b753SAlexander Motin LEAVE(ENETUNREACH); 146898e7b753SAlexander Motin } 146998e7b753SAlexander Motin neg = sp->neg; 1470098ff746SAlexander Motin /* If requested specific AC-name, check it. */ 1471098ff746SAlexander Motin if (neg->ac_name_len) { 1472098ff746SAlexander Motin tag = get_tag(ph, PTT_AC_NAME); 1473098ff746SAlexander Motin if (!tag) { 1474098ff746SAlexander Motin /* No PTT_AC_NAME in PADO */ 1475098ff746SAlexander Motin break; 1476098ff746SAlexander Motin } 1477098ff746SAlexander Motin if (neg->ac_name_len != htons(tag->tag_len) || 147860d234c5SEd Schouten strncmp(neg->ac_name.data, 147960d234c5SEd Schouten (const char *)(tag + 1), 1480098ff746SAlexander Motin neg->ac_name_len) != 0) { 1481098ff746SAlexander Motin break; 1482098ff746SAlexander Motin } 1483098ff746SAlexander Motin } 1484098ff746SAlexander Motin sp->state = PPPOE_SREQ; 148598e7b753SAlexander Motin ng_uncallout(&neg->handle, node); 148698e7b753SAlexander Motin 148798e7b753SAlexander Motin /* 148898e7b753SAlexander Motin * This is the first time we hear 148998e7b753SAlexander Motin * from the server, so note it's 149098e7b753SAlexander Motin * unicast address, replacing the 149198e7b753SAlexander Motin * broadcast address . 149298e7b753SAlexander Motin */ 149398e7b753SAlexander Motin bcopy(wh->eh.ether_shost, 149498e7b753SAlexander Motin neg->pkt->pkt_header.eh.ether_dhost, 149598e7b753SAlexander Motin ETHER_ADDR_LEN); 149698e7b753SAlexander Motin neg->timeout = 0; 149798e7b753SAlexander Motin neg->pkt->pkt_header.ph.code = PADR_CODE; 149898e7b753SAlexander Motin init_tags(sp); 149998e7b753SAlexander Motin insert_tag(sp, utag); /* Host Unique */ 150098e7b753SAlexander Motin if ((tag = get_tag(ph, PTT_AC_COOKIE))) 150198e7b753SAlexander Motin insert_tag(sp, tag); /* return cookie */ 150298e7b753SAlexander Motin if ((tag = get_tag(ph, PTT_AC_NAME))) { 150398e7b753SAlexander Motin insert_tag(sp, tag); /* return it */ 150498e7b753SAlexander Motin send_acname(sp, tag); 150598e7b753SAlexander Motin } 1506*5b363c09SAlexander Motin if ((tag = get_tag(ph, PTT_MAX_PAYL)) && 1507*5b363c09SAlexander Motin (privp->max_payload.data != 0)) 1508*5b363c09SAlexander Motin insert_tag(sp, tag); /* return it */ 150998e7b753SAlexander Motin insert_tag(sp, &neg->service.hdr); /* Service */ 151098e7b753SAlexander Motin scan_tags(sp, ph); 151198e7b753SAlexander Motin make_packet(sp); 151298e7b753SAlexander Motin sp->state = PPPOE_SREQ; 151398e7b753SAlexander Motin ng_callout(&neg->handle, node, sp->hook, 151498e7b753SAlexander Motin PPPOE_INITIAL_TIMEOUT * hz, 151598e7b753SAlexander Motin pppoe_ticker, NULL, 0); 151698e7b753SAlexander Motin neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1517eb1b1807SGleb Smirnoff m0 = m_copypacket(neg->m, M_NOWAIT); 151898e7b753SAlexander Motin NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 151998e7b753SAlexander Motin break; 152098e7b753SAlexander Motin case PADR_CODE: 152198e7b753SAlexander Motin /* 152298e7b753SAlexander Motin * We are a server: 152398e7b753SAlexander Motin * Use the ac_cookie tag to find the 152498e7b753SAlexander Motin * hook this is in response to. 152598e7b753SAlexander Motin */ 152698e7b753SAlexander Motin utag = get_tag(ph, PTT_AC_COOKIE); 152798e7b753SAlexander Motin if ((utag == NULL) || 152898e7b753SAlexander Motin (ntohs(utag->tag_len) != sizeof(sp))) { 152998e7b753SAlexander Motin LEAVE(ENETUNREACH); 153098e7b753SAlexander Motin } 153198e7b753SAlexander Motin 153298e7b753SAlexander Motin sendhook = pppoe_finduniq(node, utag); 153398e7b753SAlexander Motin if (sendhook == NULL) 153498e7b753SAlexander Motin LEAVE(ENETUNREACH); 153598e7b753SAlexander Motin 153698e7b753SAlexander Motin /* 153798e7b753SAlexander Motin * Check the session is in the right state. 153898e7b753SAlexander Motin * It needs to be in PPPOE_SOFFER or PPPOE_NEWCONNECTED. 153998e7b753SAlexander Motin * If the latter, then this is a retry by the client, 154098e7b753SAlexander Motin * so be nice, and resend. 154198e7b753SAlexander Motin */ 154298e7b753SAlexander Motin sp = NG_HOOK_PRIVATE(sendhook); 154398e7b753SAlexander Motin if (sp->state == PPPOE_NEWCONNECTED) { 154498e7b753SAlexander Motin /* 154598e7b753SAlexander Motin * Whoa! drop back to resend that PADS packet. 154698e7b753SAlexander Motin * We should still have a copy of it. 154798e7b753SAlexander Motin */ 154898e7b753SAlexander Motin sp->state = PPPOE_SOFFER; 1549b2b5279bSAlexander Motin } else if (sp->state != PPPOE_SOFFER) 155098e7b753SAlexander Motin LEAVE (ENETUNREACH); 155198e7b753SAlexander Motin neg = sp->neg; 155298e7b753SAlexander Motin ng_uncallout(&neg->handle, node); 155398e7b753SAlexander Motin neg->pkt->pkt_header.ph.code = PADS_CODE; 1554b2b5279bSAlexander Motin if (sp->Session_ID == 0) { 155598e7b753SAlexander Motin neg->pkt->pkt_header.ph.sid = 1556bd500dabSAlexander Motin htons(pppoe_getnewsession(sp)); 1557b2b5279bSAlexander Motin } 155898e7b753SAlexander Motin send_sessionid(sp); 155998e7b753SAlexander Motin neg->timeout = 0; 156098e7b753SAlexander Motin /* 156198e7b753SAlexander Motin * start working out the tags to respond with. 156298e7b753SAlexander Motin */ 156398e7b753SAlexander Motin init_tags(sp); 156498e7b753SAlexander Motin insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 156598e7b753SAlexander Motin if ((tag = get_tag(ph, PTT_SRV_NAME))) 156698e7b753SAlexander Motin insert_tag(sp, tag);/* return service */ 156798e7b753SAlexander Motin if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 156898e7b753SAlexander Motin insert_tag(sp, tag); /* return it */ 156998e7b753SAlexander Motin insert_tag(sp, utag); /* ac_cookie */ 157098e7b753SAlexander Motin scan_tags(sp, ph); 157198e7b753SAlexander Motin make_packet(sp); 157298e7b753SAlexander Motin sp->state = PPPOE_NEWCONNECTED; 157398e7b753SAlexander Motin 157498e7b753SAlexander Motin /* Send the PADS without a timeout - we're now connected. */ 1575eb1b1807SGleb Smirnoff m0 = m_copypacket(sp->neg->m, M_NOWAIT); 157698e7b753SAlexander Motin NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 157798e7b753SAlexander Motin 157898e7b753SAlexander Motin /* 157998e7b753SAlexander Motin * Having sent the last Negotiation header, 158098e7b753SAlexander Motin * Set up the stored packet header to be correct for 158198e7b753SAlexander Motin * the actual session. But keep the negotialtion stuff 158298e7b753SAlexander Motin * around in case we need to resend this last packet. 158398e7b753SAlexander Motin * We'll discard it when we move from NEWCONNECTED 158498e7b753SAlexander Motin * to CONNECTED 158598e7b753SAlexander Motin */ 158698e7b753SAlexander Motin sp->pkt_hdr = neg->pkt->pkt_header; 158798e7b753SAlexander Motin /* Configure ethertype depending on what 158898e7b753SAlexander Motin * ethertype was used at discovery phase */ 158998e7b753SAlexander Motin if (sp->pkt_hdr.eh.ether_type == 159098e7b753SAlexander Motin ETHERTYPE_PPPOE_3COM_DISC) 159198e7b753SAlexander Motin sp->pkt_hdr.eh.ether_type 159298e7b753SAlexander Motin = ETHERTYPE_PPPOE_3COM_SESS; 159398e7b753SAlexander Motin else 159498e7b753SAlexander Motin sp->pkt_hdr.eh.ether_type 159598e7b753SAlexander Motin = ETHERTYPE_PPPOE_SESS; 159698e7b753SAlexander Motin sp->pkt_hdr.ph.code = 0; 159798e7b753SAlexander Motin pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 159898e7b753SAlexander Motin break; 159998e7b753SAlexander Motin case PADS_CODE: 160098e7b753SAlexander Motin /* 160198e7b753SAlexander Motin * We are a client: 160298e7b753SAlexander Motin * Use the host_uniq tag to find the hook this is in 160398e7b753SAlexander Motin * response to. Take the session ID and store it away. 160498e7b753SAlexander Motin * Also make sure the pre-made header is correct and 160598e7b753SAlexander Motin * set us into Session mode. 160698e7b753SAlexander Motin */ 160798e7b753SAlexander Motin utag = get_tag(ph, PTT_HOST_UNIQ); 160898e7b753SAlexander Motin if ((utag == NULL) || 160998e7b753SAlexander Motin (ntohs(utag->tag_len) != sizeof(sp))) { 161098e7b753SAlexander Motin LEAVE (ENETUNREACH); 161198e7b753SAlexander Motin } 161298e7b753SAlexander Motin sendhook = pppoe_finduniq(node, utag); 161398e7b753SAlexander Motin if (sendhook == NULL) 161498e7b753SAlexander Motin LEAVE(ENETUNREACH); 161598e7b753SAlexander Motin 161698e7b753SAlexander Motin /* 161798e7b753SAlexander Motin * Check the session is in the right state. 161898e7b753SAlexander Motin * It needs to be in PPPOE_SREQ. 161998e7b753SAlexander Motin */ 162098e7b753SAlexander Motin sp = NG_HOOK_PRIVATE(sendhook); 162198e7b753SAlexander Motin if (sp->state != PPPOE_SREQ) 162298e7b753SAlexander Motin LEAVE(ENETUNREACH); 162398e7b753SAlexander Motin neg = sp->neg; 162498e7b753SAlexander Motin ng_uncallout(&neg->handle, node); 162598e7b753SAlexander Motin neg->pkt->pkt_header.ph.sid = wh->ph.sid; 162698e7b753SAlexander Motin sp->Session_ID = ntohs(wh->ph.sid); 1627b2b5279bSAlexander Motin pppoe_addsession(sp); 162898e7b753SAlexander Motin send_sessionid(sp); 162998e7b753SAlexander Motin neg->timeout = 0; 163098e7b753SAlexander Motin sp->state = PPPOE_CONNECTED; 163198e7b753SAlexander Motin /* 163298e7b753SAlexander Motin * Now we have gone to Connected mode, 163398e7b753SAlexander Motin * Free all resources needed for negotiation. 163498e7b753SAlexander Motin * Keep a copy of the header we will be using. 163598e7b753SAlexander Motin */ 163698e7b753SAlexander Motin sp->pkt_hdr = neg->pkt->pkt_header; 163798e7b753SAlexander Motin if (privp->flags & COMPAT_3COM) 163898e7b753SAlexander Motin sp->pkt_hdr.eh.ether_type 163998e7b753SAlexander Motin = ETHERTYPE_PPPOE_3COM_SESS; 164098e7b753SAlexander Motin else 164198e7b753SAlexander Motin sp->pkt_hdr.eh.ether_type 164298e7b753SAlexander Motin = ETHERTYPE_PPPOE_SESS; 164398e7b753SAlexander Motin sp->pkt_hdr.ph.code = 0; 164498e7b753SAlexander Motin m_freem(neg->m); 164598e7b753SAlexander Motin free(sp->neg, M_NETGRAPH_PPPOE); 164698e7b753SAlexander Motin sp->neg = NULL; 1647*5b363c09SAlexander Motin if ((tag = get_tag(ph, PTT_MAX_PAYL)) && 1648*5b363c09SAlexander Motin (privp->max_payload.data != 0)) 1649*5b363c09SAlexander Motin send_maxp(sp, tag); 165098e7b753SAlexander Motin pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 165198e7b753SAlexander Motin break; 165298e7b753SAlexander Motin case PADT_CODE: 165398e7b753SAlexander Motin /* 165498e7b753SAlexander Motin * Find matching peer/session combination. 165598e7b753SAlexander Motin */ 1656b2b5279bSAlexander Motin sp = pppoe_findsession(privp, wh); 1657b2b5279bSAlexander Motin if (sp == NULL) 165898e7b753SAlexander Motin LEAVE(ENETUNREACH); 165998e7b753SAlexander Motin /* Disconnect that hook. */ 1660b2b5279bSAlexander Motin ng_rmhook_self(sp->hook); 166198e7b753SAlexander Motin break; 166298e7b753SAlexander Motin default: 166398e7b753SAlexander Motin LEAVE(EPFNOSUPPORT); 166498e7b753SAlexander Motin } 166598e7b753SAlexander Motin break; 166698e7b753SAlexander Motin case ETHERTYPE_PPPOE_3COM_SESS: 166798e7b753SAlexander Motin case ETHERTYPE_PPPOE_SESS: 166898e7b753SAlexander Motin /* 166998e7b753SAlexander Motin * Find matching peer/session combination. 167098e7b753SAlexander Motin */ 1671b2b5279bSAlexander Motin sp = pppoe_findsession(privp, wh); 1672b2b5279bSAlexander Motin if (sp == NULL) 167398e7b753SAlexander Motin LEAVE (ENETUNREACH); 167498e7b753SAlexander Motin m_adj(m, sizeof(*wh)); 167598e7b753SAlexander Motin 167698e7b753SAlexander Motin /* If packet too short, dump it. */ 167798e7b753SAlexander Motin if (m->m_pkthdr.len < length) 167898e7b753SAlexander Motin LEAVE(EMSGSIZE); 167998e7b753SAlexander Motin /* Also need to trim excess at the end */ 168098e7b753SAlexander Motin if (m->m_pkthdr.len > length) { 168198e7b753SAlexander Motin m_adj(m, -((int)(m->m_pkthdr.len - length))); 168298e7b753SAlexander Motin } 168398e7b753SAlexander Motin if ( sp->state != PPPOE_CONNECTED) { 168498e7b753SAlexander Motin if (sp->state == PPPOE_NEWCONNECTED) { 168598e7b753SAlexander Motin sp->state = PPPOE_CONNECTED; 168698e7b753SAlexander Motin /* 168798e7b753SAlexander Motin * Now we have gone to Connected mode, 168898e7b753SAlexander Motin * Free all resources needed for negotiation. 168998e7b753SAlexander Motin * Be paranoid about whether there may be 169098e7b753SAlexander Motin * a timeout. 169198e7b753SAlexander Motin */ 169298e7b753SAlexander Motin m_freem(sp->neg->m); 169398e7b753SAlexander Motin ng_uncallout(&sp->neg->handle, node); 169498e7b753SAlexander Motin free(sp->neg, M_NETGRAPH_PPPOE); 169598e7b753SAlexander Motin sp->neg = NULL; 169698e7b753SAlexander Motin } else { 169798e7b753SAlexander Motin LEAVE (ENETUNREACH); 169898e7b753SAlexander Motin } 169998e7b753SAlexander Motin } 1700b2b5279bSAlexander Motin NG_FWD_NEW_DATA(error, item, sp->hook, m); 170198e7b753SAlexander Motin break; 170298e7b753SAlexander Motin default: 170398e7b753SAlexander Motin LEAVE(EPFNOSUPPORT); 170498e7b753SAlexander Motin } 170598e7b753SAlexander Motin quit: 170698e7b753SAlexander Motin if (item) 170798e7b753SAlexander Motin NG_FREE_ITEM(item); 170898e7b753SAlexander Motin NG_FREE_M(m); 170998e7b753SAlexander Motin return (error); 171098e7b753SAlexander Motin } 171198e7b753SAlexander Motin 171298e7b753SAlexander Motin /* 171398e7b753SAlexander Motin * Receive data from debug hook and bypass it to ether. 171498e7b753SAlexander Motin */ 171598e7b753SAlexander Motin static int 171698e7b753SAlexander Motin ng_pppoe_rcvdata_debug(hook_p hook, item_p item) 171798e7b753SAlexander Motin { 171898e7b753SAlexander Motin node_p node = NG_HOOK_NODE(hook); 171998e7b753SAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 172098e7b753SAlexander Motin int error; 172198e7b753SAlexander Motin 172298e7b753SAlexander Motin CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 172398e7b753SAlexander Motin __func__, node->nd_ID, node, item, hook->hk_name, hook); 172498e7b753SAlexander Motin 172598e7b753SAlexander Motin NG_FWD_ITEM_HOOK(error, item, privp->ethernet_hook); 172698e7b753SAlexander Motin privp->packets_out++; 172798e7b753SAlexander Motin return (error); 17284cf49a43SJulian Elischer } 17294cf49a43SJulian Elischer 17304cf49a43SJulian Elischer /* 17314cf49a43SJulian Elischer * Do local shutdown processing.. 17324cf49a43SJulian Elischer * If we are a persistant device, we might refuse to go away, and 17334cf49a43SJulian Elischer * we'd only remove our links and reset ourself. 17344cf49a43SJulian Elischer */ 17354cf49a43SJulian Elischer static int 1736069154d5SJulian Elischer ng_pppoe_shutdown(node_p node) 17374cf49a43SJulian Elischer { 1738b2b5279bSAlexander Motin const priv_p privp = NG_NODE_PRIVATE(node); 1739b2b5279bSAlexander Motin int i; 17404cf49a43SJulian Elischer 1741b2b5279bSAlexander Motin for (i = 0; i < SESSHASHSIZE; i++) 1742b2b5279bSAlexander Motin mtx_destroy(&privp->sesshash[i].mtx); 174330400f03SJulian Elischer NG_NODE_SET_PRIVATE(node, NULL); 1744b2b5279bSAlexander Motin NG_NODE_UNREF(privp->node); 1745b2b5279bSAlexander Motin free(privp, M_NETGRAPH_PPPOE); 17464cf49a43SJulian Elischer return (0); 17474cf49a43SJulian Elischer } 17484cf49a43SJulian Elischer 17494cf49a43SJulian Elischer /* 17504cf49a43SJulian Elischer * Hook disconnection 17514cf49a43SJulian Elischer * 17526faf164cSJulian Elischer * Clean up all dangling links and information about the session/hook. 17537762e8c6SGleb Smirnoff * For this type, removal of the last link destroys the node. 17544cf49a43SJulian Elischer */ 17554cf49a43SJulian Elischer static int 17568876b55dSJulian Elischer ng_pppoe_disconnect(hook_p hook) 17574cf49a43SJulian Elischer { 175830400f03SJulian Elischer node_p node = NG_HOOK_NODE(hook); 175930400f03SJulian Elischer priv_p privp = NG_NODE_PRIVATE(node); 17604cf49a43SJulian Elischer sessp sp; 17614cf49a43SJulian Elischer 176299f4de90SAlexander Motin if (hook == privp->debug_hook) { 17634cf49a43SJulian Elischer privp->debug_hook = NULL; 176499f4de90SAlexander Motin } else if (hook == privp->ethernet_hook) { 17654cf49a43SJulian Elischer privp->ethernet_hook = NULL; 176630400f03SJulian Elischer if (NG_NODE_IS_VALID(node)) 1767069154d5SJulian Elischer ng_rmnode_self(node); 17684cf49a43SJulian Elischer } else { 176930400f03SJulian Elischer sp = NG_HOOK_PRIVATE(hook); 1770b58a8a3bSJulian Elischer if (sp->state != PPPOE_SNONE ) { 1771b58a8a3bSJulian Elischer pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1772b58a8a3bSJulian Elischer } 1773a4ec03cfSJulian Elischer /* 1774a4ec03cfSJulian Elischer * According to the spec, if we are connected, 1775a4ec03cfSJulian Elischer * we should send a DISC packet if we are shutting down 1776a4ec03cfSJulian Elischer * a session. 1777a4ec03cfSJulian Elischer */ 17789fcb3d83SJulian Elischer if ((privp->ethernet_hook) 17799fcb3d83SJulian Elischer && ((sp->state == PPPOE_CONNECTED) 17809fcb3d83SJulian Elischer || (sp->state == PPPOE_NEWCONNECTED))) { 17819fcb3d83SJulian Elischer struct mbuf *m; 17829fcb3d83SJulian Elischer 17837762e8c6SGleb Smirnoff /* Generate a packet of that type. */ 1784eb1b1807SGleb Smirnoff MGETHDR(m, M_NOWAIT, MT_DATA); 17856faf164cSJulian Elischer if (m == NULL) 17862e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe[%x]: session out of " 17872e87c3ccSGleb Smirnoff "mbufs\n", node->nd_ID); 17886faf164cSJulian Elischer else { 17891e7d84b0SAlexander Motin struct pppoe_full_hdr *wh; 17901e7d84b0SAlexander Motin struct pppoe_tag *tag; 17911e7d84b0SAlexander Motin int msglen = strlen(SIGNOFF); 17921e7d84b0SAlexander Motin int error = 0; 17931e7d84b0SAlexander Motin 17949fcb3d83SJulian Elischer m->m_pkthdr.rcvif = NULL; 17959fcb3d83SJulian Elischer m->m_pkthdr.len = m->m_len = sizeof(*wh); 17961e7d84b0SAlexander Motin wh = mtod(m, struct pppoe_full_hdr *); 17971e7d84b0SAlexander Motin bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 17981e7d84b0SAlexander Motin 17991e7d84b0SAlexander Motin /* Revert the stored header to DISC/PADT mode. */ 18001e7d84b0SAlexander Motin wh->ph.code = PADT_CODE; 18011e7d84b0SAlexander Motin /* 18021e7d84b0SAlexander Motin * Configure ethertype depending on what 18031e7d84b0SAlexander Motin * was used during sessions stage. 18041e7d84b0SAlexander Motin */ 18051e7d84b0SAlexander Motin if (wh->eh.ether_type == 18061e7d84b0SAlexander Motin ETHERTYPE_PPPOE_3COM_SESS) 18071e7d84b0SAlexander Motin wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC; 18081e7d84b0SAlexander Motin else 18091e7d84b0SAlexander Motin wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 18106faf164cSJulian Elischer /* 18116faf164cSJulian Elischer * Add a General error message and adjust 18127762e8c6SGleb Smirnoff * sizes. 18136faf164cSJulian Elischer */ 181460d234c5SEd Schouten tag = (void *)(&wh->ph + 1); 18159fcb3d83SJulian Elischer tag->tag_type = PTT_GEN_ERR; 18169fcb3d83SJulian Elischer tag->tag_len = htons((u_int16_t)msglen); 181760d234c5SEd Schouten strncpy((char *)(tag + 1), SIGNOFF, msglen); 18186faf164cSJulian Elischer m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 18196faf164cSJulian Elischer msglen); 18209fcb3d83SJulian Elischer wh->ph.length = htons(sizeof(*tag) + msglen); 1821069154d5SJulian Elischer NG_SEND_DATA_ONLY(error, 1822069154d5SJulian Elischer privp->ethernet_hook, m); 18236faf164cSJulian Elischer } 18249fcb3d83SJulian Elischer } 1825dda30f12SAlexander Motin if (sp->state == PPPOE_LISTENING) 1826dda30f12SAlexander Motin LIST_REMOVE(sp, sessions); 1827dda30f12SAlexander Motin else if (sp->Session_ID) 1828b2b5279bSAlexander Motin pppoe_delsession(sp); 1829a4ec03cfSJulian Elischer /* 1830514baf3fSJeroen Ruigrok van der Werven * As long as we have somewhere to store the timeout handle, 1831a4ec03cfSJulian Elischer * we may have a timeout pending.. get rid of it. 1832a4ec03cfSJulian Elischer */ 18331e2510f8SJulian Elischer if (sp->neg) { 1834ef237c7fSGleb Smirnoff ng_uncallout(&sp->neg->handle, node); 18351e2510f8SJulian Elischer if (sp->neg->m) 18361e2510f8SJulian Elischer m_freem(sp->neg->m); 18377762e8c6SGleb Smirnoff free(sp->neg, M_NETGRAPH_PPPOE); 18381e2510f8SJulian Elischer } 18397762e8c6SGleb Smirnoff free(sp, M_NETGRAPH_PPPOE); 184030400f03SJulian Elischer NG_HOOK_SET_PRIVATE(hook, NULL); 18414cf49a43SJulian Elischer } 18427762e8c6SGleb Smirnoff if ((NG_NODE_NUMHOOKS(node) == 0) && 18437762e8c6SGleb Smirnoff (NG_NODE_IS_VALID(node))) 1844069154d5SJulian Elischer ng_rmnode_self(node); 18454cf49a43SJulian Elischer return (0); 18464cf49a43SJulian Elischer } 18474cf49a43SJulian Elischer 18484cf49a43SJulian Elischer /* 18497762e8c6SGleb Smirnoff * Timeouts come here. 18504cf49a43SJulian Elischer */ 18514cf49a43SJulian Elischer static void 1852ef237c7fSGleb Smirnoff pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2) 18534cf49a43SJulian Elischer { 18547762e8c6SGleb Smirnoff priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 185530400f03SJulian Elischer sessp sp = NG_HOOK_PRIVATE(hook); 18564cf49a43SJulian Elischer negp neg = sp->neg; 18574cf49a43SJulian Elischer struct mbuf *m0 = NULL; 18587762e8c6SGleb Smirnoff int error = 0; 18594cf49a43SJulian Elischer 18607762e8c6SGleb Smirnoff CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d", 18617762e8c6SGleb Smirnoff __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID); 18624cf49a43SJulian Elischer switch(sp->state) { 18634cf49a43SJulian Elischer /* 18647762e8c6SGleb Smirnoff * Resend the last packet, using an exponential backoff. 18654cf49a43SJulian Elischer * After a period of time, stop growing the backoff, 18667762e8c6SGleb Smirnoff * And either leave it, or revert to the start. 18674cf49a43SJulian Elischer */ 18684cf49a43SJulian Elischer case PPPOE_SINIT: 18694cf49a43SJulian Elischer case PPPOE_SREQ: 18707762e8c6SGleb Smirnoff /* Timeouts on these produce resends. */ 1871eb1b1807SGleb Smirnoff m0 = m_copypacket(sp->neg->m, M_NOWAIT); 1872069154d5SJulian Elischer NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1873ef237c7fSGleb Smirnoff ng_callout(&neg->handle, node, hook, neg->timeout * hz, 1874ef237c7fSGleb Smirnoff pppoe_ticker, NULL, 0); 18754cf49a43SJulian Elischer if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 18764cf49a43SJulian Elischer if (sp->state == PPPOE_SREQ) { 18777762e8c6SGleb Smirnoff /* Revert to SINIT mode. */ 1878b58a8a3bSJulian Elischer pppoe_start(sp); 18794cf49a43SJulian Elischer } else { 18804cf49a43SJulian Elischer neg->timeout = PPPOE_TIMEOUT_LIMIT; 18814cf49a43SJulian Elischer } 18824cf49a43SJulian Elischer } 18834cf49a43SJulian Elischer break; 18844cf49a43SJulian Elischer case PPPOE_PRIMED: 18854cf49a43SJulian Elischer case PPPOE_SOFFER: 18867762e8c6SGleb Smirnoff /* A timeout on these says "give up" */ 1887954c4772SJulian Elischer ng_rmhook_self(hook); 18884cf49a43SJulian Elischer break; 18894cf49a43SJulian Elischer default: 18907762e8c6SGleb Smirnoff /* Timeouts have no meaning in other states. */ 18912e87c3ccSGleb Smirnoff log(LOG_NOTICE, "ng_pppoe[%x]: unexpected timeout\n", 18922e87c3ccSGleb Smirnoff node->nd_ID); 18934cf49a43SJulian Elischer } 18944cf49a43SJulian Elischer } 18954cf49a43SJulian Elischer 18964cf49a43SJulian Elischer /* 18974cf49a43SJulian Elischer * Parse an incoming packet to see if any tags should be copied to the 18984adb13fdSJulian Elischer * output packet. Don't do any tags that have been handled in the main 18994adb13fdSJulian Elischer * state machine. 19004cf49a43SJulian Elischer */ 1901816b834fSArchie Cobbs static const struct pppoe_tag* 1902816b834fSArchie Cobbs scan_tags(sessp sp, const struct pppoe_hdr* ph) 19034cf49a43SJulian Elischer { 1904816b834fSArchie Cobbs const char *const end = (const char *)next_tag(ph); 1905816b834fSArchie Cobbs const char *ptn; 190660d234c5SEd Schouten const struct pppoe_tag *pt = (const void *)(ph + 1); 19077762e8c6SGleb Smirnoff 19084cf49a43SJulian Elischer /* 19094cf49a43SJulian Elischer * Keep processing tags while a tag header will still fit. 19104cf49a43SJulian Elischer */ 19117762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 19127762e8c6SGleb Smirnoff 1913816b834fSArchie Cobbs while((const char*)(pt + 1) <= end) { 19144cf49a43SJulian Elischer /* 19154cf49a43SJulian Elischer * If the tag data would go past the end of the packet, abort. 19164cf49a43SJulian Elischer */ 1917816b834fSArchie Cobbs ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 19184cf49a43SJulian Elischer if(ptn > end) 19194cf49a43SJulian Elischer return NULL; 19204cf49a43SJulian Elischer 19214cf49a43SJulian Elischer switch (pt->tag_type) { 19224cf49a43SJulian Elischer case PTT_RELAY_SID: 19234cf49a43SJulian Elischer insert_tag(sp, pt); 19244cf49a43SJulian Elischer break; 19254cf49a43SJulian Elischer case PTT_EOL: 19264cf49a43SJulian Elischer return NULL; 19274cf49a43SJulian Elischer case PTT_SRV_NAME: 19284cf49a43SJulian Elischer case PTT_AC_NAME: 19294cf49a43SJulian Elischer case PTT_HOST_UNIQ: 19304cf49a43SJulian Elischer case PTT_AC_COOKIE: 19314cf49a43SJulian Elischer case PTT_VENDOR: 19324cf49a43SJulian Elischer case PTT_SRV_ERR: 19334cf49a43SJulian Elischer case PTT_SYS_ERR: 19344cf49a43SJulian Elischer case PTT_GEN_ERR: 19355c6d5d55SGleb Smirnoff case PTT_MAX_PAYL: 19364cf49a43SJulian Elischer break; 19374cf49a43SJulian Elischer } 1938816b834fSArchie Cobbs pt = (const struct pppoe_tag*)ptn; 19394cf49a43SJulian Elischer } 19404cf49a43SJulian Elischer return NULL; 19414cf49a43SJulian Elischer } 19424cf49a43SJulian Elischer 1943b58a8a3bSJulian Elischer static int 1944b58a8a3bSJulian Elischer pppoe_send_event(sessp sp, enum cmd cmdid) 1945b58a8a3bSJulian Elischer { 1946b58a8a3bSJulian Elischer int error; 1947b58a8a3bSJulian Elischer struct ng_mesg *msg; 19488876b55dSJulian Elischer struct ngpppoe_sts *sts; 1949b58a8a3bSJulian Elischer 19507762e8c6SGleb Smirnoff CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 19517762e8c6SGleb Smirnoff 195227121ab1SBrian Somers NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 19538876b55dSJulian Elischer sizeof(struct ngpppoe_sts), M_NOWAIT); 1954859a4d16SJulian Elischer if (msg == NULL) 1955859a4d16SJulian Elischer return (ENOMEM); 19568876b55dSJulian Elischer sts = (struct ngpppoe_sts *)msg->data; 195787e2c66aSHartmut Brandt strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 1958facfd889SArchie Cobbs NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1959b58a8a3bSJulian Elischer return (error); 1960b58a8a3bSJulian Elischer } 1961