ng_pppoe.c revision 172628
198944Sobrien/* 298944Sobrien * ng_pppoe.c 398944Sobrien */ 498944Sobrien 598944Sobrien/*- 698944Sobrien * Copyright (c) 1996-1999 Whistle Communications, Inc. 798944Sobrien * All rights reserved. 898944Sobrien * 998944Sobrien * Subject to the following obligations and disclaimer of warranty, use and 1098944Sobrien * redistribution of this software, in source or object code forms, with or 1198944Sobrien * without modifications are expressly permitted by Whistle Communications; 1298944Sobrien * provided, however, that: 1398944Sobrien * 1. Any and all reproductions of the source or object code must include the 1498944Sobrien * copyright notice above and the following disclaimer of warranties; and 1598944Sobrien * 2. No rights are granted, in any manner or form, to use Whistle 1698944Sobrien * Communications, Inc. trademarks, including the mark "WHISTLE 1798944Sobrien * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 1898944Sobrien * such appears in the above copyright notice or in the software. 1998944Sobrien * 2098944Sobrien * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 2198944Sobrien * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 2298944Sobrien * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 2398944Sobrien * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 2498944Sobrien * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 2598944Sobrien * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 2698944Sobrien * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27130803Smarcel * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 2898944Sobrien * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29130803Smarcel * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 3098944Sobrien * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 3198944Sobrien * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 3298944Sobrien * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 3398944Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3498944Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3598944Sobrien * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 3698944Sobrien * OF SUCH DAMAGE. 3798944Sobrien * 3898944Sobrien * Author: Julian Elischer <julian@freebsd.org> 3998944Sobrien * 4098944Sobrien * $FreeBSD: head/sys/netgraph/ng_pppoe.c 172628 2007-10-14 09:51:19Z mav $ 4198944Sobrien * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $ 4298944Sobrien */ 4398944Sobrien 4498944Sobrien#include <sys/param.h> 4598944Sobrien#include <sys/systm.h> 4698944Sobrien#include <sys/kernel.h> 4798944Sobrien#include <sys/ktr.h> 4898944Sobrien#include <sys/mbuf.h> 4998944Sobrien#include <sys/malloc.h> 5098944Sobrien#include <sys/errno.h> 5198944Sobrien#include <sys/syslog.h> 5298944Sobrien#include <net/ethernet.h> 5398944Sobrien 54130803Smarcel#include <netgraph/ng_message.h> 5598944Sobrien#include <netgraph/netgraph.h> 5698944Sobrien#include <netgraph/ng_parse.h> 5798944Sobrien#include <netgraph/ng_pppoe.h> 5898944Sobrien#include <netgraph/ng_ether.h> 5998944Sobrien 6098944Sobrien#ifdef NG_SEPARATE_MALLOC 6198944SobrienMALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 6298944Sobrien#else 6398944Sobrien#define M_NETGRAPH_PPPOE M_NETGRAPH 6498944Sobrien#endif 6598944Sobrien 6698944Sobrien#define SIGNOFF "session closed" 6798944Sobrien#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0)) 6898944Sobrien 6998944Sobrien/* 7098944Sobrien * This section contains the netgraph method declarations for the 7198944Sobrien * pppoe node. These methods define the netgraph pppoe 'type'. 7298944Sobrien */ 7398944Sobrien 7498944Sobrienstatic ng_constructor_t ng_pppoe_constructor; 7598944Sobrienstatic ng_rcvmsg_t ng_pppoe_rcvmsg; 7698944Sobrienstatic ng_shutdown_t ng_pppoe_shutdown; 7798944Sobrienstatic ng_newhook_t ng_pppoe_newhook; 7898944Sobrienstatic ng_connect_t ng_pppoe_connect; 79130803Smarcelstatic ng_rcvdata_t ng_pppoe_rcvdata; 80130803Smarcelstatic ng_disconnect_t ng_pppoe_disconnect; 81130803Smarcel 8298944Sobrien/* Parse type for struct ngpppoe_init_data */ 8398944Sobrienstatic const struct ng_parse_struct_field ngpppoe_init_data_type_fields[] 8498944Sobrien = NG_PPPOE_INIT_DATA_TYPE_INFO; 8598944Sobrienstatic const struct ng_parse_type ngpppoe_init_data_state_type = { 8698944Sobrien &ng_parse_struct_type, 8798944Sobrien &ngpppoe_init_data_type_fields 8898944Sobrien}; 8998944Sobrien 9098944Sobrien/* Parse type for struct ngpppoe_sts */ 9198944Sobrienstatic const struct ng_parse_struct_field ng_pppoe_sts_type_fields[] 9298944Sobrien = NG_PPPOE_STS_TYPE_INFO; 9398944Sobrienstatic const struct ng_parse_type ng_pppoe_sts_state_type = { 9498944Sobrien &ng_parse_struct_type, 9598944Sobrien &ng_pppoe_sts_type_fields 9698944Sobrien}; 9798944Sobrien 9898944Sobrien/* List of commands and how to convert arguments to/from ASCII */ 9998944Sobrienstatic const struct ng_cmdlist ng_pppoe_cmds[] = { 10098944Sobrien { 10198944Sobrien NGM_PPPOE_COOKIE, 10298944Sobrien NGM_PPPOE_CONNECT, 10398944Sobrien "pppoe_connect", 10498944Sobrien &ngpppoe_init_data_state_type, 10598944Sobrien NULL 10698944Sobrien }, 10798944Sobrien { 10898944Sobrien NGM_PPPOE_COOKIE, 10998944Sobrien NGM_PPPOE_LISTEN, 11098944Sobrien "pppoe_listen", 11198944Sobrien &ngpppoe_init_data_state_type, 11298944Sobrien NULL 11398944Sobrien }, 11498944Sobrien { 11598944Sobrien NGM_PPPOE_COOKIE, 11698944Sobrien NGM_PPPOE_OFFER, 11798944Sobrien "pppoe_offer", 11898944Sobrien &ngpppoe_init_data_state_type, 11998944Sobrien NULL 12098944Sobrien }, 12198944Sobrien { 12298944Sobrien NGM_PPPOE_COOKIE, 12398944Sobrien NGM_PPPOE_SERVICE, 12498944Sobrien "pppoe_service", 12598944Sobrien &ngpppoe_init_data_state_type, 12698944Sobrien NULL 12798944Sobrien }, 12898944Sobrien { 12998944Sobrien NGM_PPPOE_COOKIE, 130130803Smarcel NGM_PPPOE_SUCCESS, 131130803Smarcel "pppoe_success", 132130803Smarcel &ng_pppoe_sts_state_type, 13398944Sobrien NULL 13498944Sobrien }, 135130803Smarcel { 136130803Smarcel NGM_PPPOE_COOKIE, 13798944Sobrien NGM_PPPOE_FAIL, 13898944Sobrien "pppoe_fail", 13998944Sobrien &ng_pppoe_sts_state_type, 14098944Sobrien NULL 14198944Sobrien }, 142130803Smarcel { 14398944Sobrien NGM_PPPOE_COOKIE, 14498944Sobrien NGM_PPPOE_CLOSE, 14598944Sobrien "pppoe_close", 14698944Sobrien &ng_pppoe_sts_state_type, 14798944Sobrien NULL 14898944Sobrien }, 14998944Sobrien { 15098944Sobrien NGM_PPPOE_COOKIE, 15198944Sobrien NGM_PPPOE_SETMODE, 15298944Sobrien "pppoe_setmode", 15398944Sobrien &ng_parse_string_type, 15498944Sobrien NULL 15598944Sobrien }, 15698944Sobrien { 157130803Smarcel NGM_PPPOE_COOKIE, 158130803Smarcel NGM_PPPOE_GETMODE, 159130803Smarcel "pppoe_getmode", 160130803Smarcel NULL, 16198944Sobrien &ng_parse_string_type 16298944Sobrien }, 16398944Sobrien { 164 NGM_PPPOE_COOKIE, 165 NGM_PPPOE_SETENADDR, 166 "setenaddr", 167 &ng_parse_enaddr_type, 168 NULL 169 }, 170 { 0 } 171}; 172 173/* Netgraph node type descriptor */ 174static struct ng_type typestruct = { 175 .version = NG_ABI_VERSION, 176 .name = NG_PPPOE_NODE_TYPE, 177 .constructor = ng_pppoe_constructor, 178 .rcvmsg = ng_pppoe_rcvmsg, 179 .shutdown = ng_pppoe_shutdown, 180 .newhook = ng_pppoe_newhook, 181 .connect = ng_pppoe_connect, 182 .rcvdata = ng_pppoe_rcvdata, 183 .disconnect = ng_pppoe_disconnect, 184 .cmdlist = ng_pppoe_cmds, 185}; 186NETGRAPH_INIT(pppoe, &typestruct); 187 188/* 189 * States for the session state machine. 190 * These have no meaning if there is no hook attached yet. 191 */ 192enum state { 193 PPPOE_SNONE=0, /* [both] Initial state */ 194 PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 195 PPPOE_SINIT, /* [Client] Sent discovery initiation */ 196 PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 197 PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 198 PPPOE_SREQ, /* [Client] Sent a Request */ 199 PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 200 PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 201 PPPOE_DEAD /* [Both] */ 202}; 203 204#define NUMTAGS 20 /* number of tags we are set up to work with */ 205 206/* 207 * Information we store for each hook on each node for negotiating the 208 * session. The mbuf and cluster are freed once negotiation has completed. 209 * The whole negotiation block is then discarded. 210 */ 211 212struct sess_neg { 213 struct mbuf *m; /* holds cluster with last sent packet */ 214 union packet *pkt; /* points within the above cluster */ 215 struct callout handle; /* see timeout(9) */ 216 u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 217 u_int numtags; 218 const struct pppoe_tag *tags[NUMTAGS]; 219 u_int service_len; 220 u_int ac_name_len; 221 222 struct datatag service; 223 struct datatag ac_name; 224}; 225typedef struct sess_neg *negp; 226 227/* 228 * Session information that is needed after connection. 229 */ 230struct sess_con { 231 hook_p hook; 232 uint16_t Session_ID; 233 enum state state; 234 ng_ID_t creator; /* who to notify */ 235 struct pppoe_full_hdr pkt_hdr; /* used when connected */ 236 negp neg; /* used when negotiating */ 237}; 238typedef struct sess_con *sessp; 239 240/* 241 * Information we store for each node 242 */ 243struct PPPoE { 244 node_p node; /* back pointer to node */ 245 hook_p ethernet_hook; 246 hook_p debug_hook; 247 u_int packets_in; /* packets in from ethernet */ 248 u_int packets_out; /* packets out towards ethernet */ 249 uint32_t flags; 250#define COMPAT_3COM 0x00000001 251#define COMPAT_DLINK 0x00000002 252 struct ether_header eh; 253}; 254typedef struct PPPoE *priv_p; 255 256union uniq { 257 char bytes[sizeof(void *)]; 258 void *pointer; 259}; 260 261#define LEAVE(x) do { error = x; goto quit; } while(0) 262static void pppoe_start(sessp sp); 263static void pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2); 264static const struct pppoe_tag *scan_tags(sessp sp, 265 const struct pppoe_hdr* ph); 266static int pppoe_send_event(sessp sp, enum cmd cmdid); 267 268/************************************************************************* 269 * Some basic utilities from the Linux version with author's permission.* 270 * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 271 ************************************************************************/ 272 273/* 274 * Generate a new session id 275 * XXX find out the FreeBSD locking scheme. 276 */ 277static uint16_t 278get_new_sid(node_p node) 279{ 280 static int pppoe_sid = 10; 281 hook_p hook; 282 uint16_t val; 283 284restart: 285 val = pppoe_sid++; 286 /* 287 * Spec says 0xFFFF is reserved. 288 * Also don't use 0x0000 289 */ 290 if (val == 0xffff) { 291 pppoe_sid = 20; 292 goto restart; 293 } 294 295 /* Check it isn't already in use. */ 296 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 297 sessp sp = NG_HOOK_PRIVATE(hook); 298 299 /* Skip any nonsession hook. */ 300 if (sp == NULL) 301 continue; 302 if (sp->Session_ID == val) 303 goto restart; 304 } 305 306 CTR2(KTR_NET, "%20s: new sid %d", __func__, val); 307 308 return (val); 309} 310 311 312/* 313 * Return the location where the next tag can be put 314 */ 315static __inline const struct pppoe_tag* 316next_tag(const struct pppoe_hdr* ph) 317{ 318 return (const struct pppoe_tag*)(((const char*)&ph->tag[0]) 319 + ntohs(ph->length)); 320} 321 322/* 323 * Look for a tag of a specific type. 324 * Don't trust any length the other end says, 325 * but assume we already sanity checked ph->length. 326 */ 327static const struct pppoe_tag* 328get_tag(const struct pppoe_hdr* ph, uint16_t idx) 329{ 330 const char *const end = (const char *)next_tag(ph); 331 const struct pppoe_tag *pt = &ph->tag[0]; 332 const char *ptn; 333 334 /* 335 * Keep processing tags while a tag header will still fit. 336 */ 337 while((const char*)(pt + 1) <= end) { 338 /* 339 * If the tag data would go past the end of the packet, abort. 340 */ 341 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 342 if (ptn > end) { 343 CTR2(KTR_NET, "%20s: invalid length for tag %d", 344 __func__, idx); 345 return (NULL); 346 } 347 if (pt->tag_type == idx) { 348 CTR2(KTR_NET, "%20s: found tag %d", __func__, idx); 349 return (pt); 350 } 351 352 pt = (const struct pppoe_tag*)ptn; 353 } 354 355 CTR2(KTR_NET, "%20s: not found tag %d", __func__, idx); 356 return (NULL); 357} 358 359/************************************************************************** 360 * Inlines to initialise or add tags to a session's tag list. 361 **************************************************************************/ 362/* 363 * Initialise the session's tag list. 364 */ 365static void 366init_tags(sessp sp) 367{ 368 KASSERT(sp->neg != NULL, ("%s: no neg", __func__)); 369 sp->neg->numtags = 0; 370} 371 372static void 373insert_tag(sessp sp, const struct pppoe_tag *tp) 374{ 375 negp neg = sp->neg; 376 int i; 377 378 KASSERT(neg != NULL, ("%s: no neg", __func__)); 379 if ((i = neg->numtags++) < NUMTAGS) { 380 neg->tags[i] = tp; 381 } else { 382 log(LOG_NOTICE, "ng_pppoe: asked to add too many tags to " 383 "packet\n"); 384 neg->numtags--; 385 } 386} 387 388/* 389 * Make up a packet, using the tags filled out for the session. 390 * 391 * Assume that the actual pppoe header and ethernet header 392 * are filled out externally to this routine. 393 * Also assume that neg->wh points to the correct 394 * location at the front of the buffer space. 395 */ 396static void 397make_packet(sessp sp) { 398 struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 399 const struct pppoe_tag **tag; 400 char *dp; 401 int count; 402 int tlen; 403 uint16_t length = 0; 404 405 KASSERT((sp->neg != NULL) && (sp->neg->m != NULL), 406 ("%s: called from wrong state", __func__)); 407 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 408 409 dp = (char *)wh->ph.tag; 410 for (count = 0, tag = sp->neg->tags; 411 ((count < sp->neg->numtags) && (count < NUMTAGS)); 412 tag++, count++) { 413 tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 414 if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 415 log(LOG_NOTICE, "ng_pppoe: tags too long\n"); 416 sp->neg->numtags = count; 417 break; /* XXX chop off what's too long */ 418 } 419 bcopy(*tag, (char *)dp, tlen); 420 length += tlen; 421 dp += tlen; 422 } 423 wh->ph.length = htons(length); 424 sp->neg->m->m_len = length + sizeof(*wh); 425 sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 426} 427 428/************************************************************************** 429 * Routines to match a service. * 430 **************************************************************************/ 431 432/* 433 * Find a hook that has a service string that matches that 434 * we are seeking. For now use a simple string. 435 * In the future we may need something like regexp(). 436 * 437 * Null string is a wildcard (ANY service), according to RFC2516. 438 * And historical FreeBSD wildcard is also "*". 439 */ 440 441static hook_p 442pppoe_match_svc(node_p node, const struct pppoe_tag *tag) 443{ 444 hook_p hook; 445 446 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 447 sessp sp = NG_HOOK_PRIVATE(hook); 448 negp neg; 449 450 /* Skip any nonsession hook. */ 451 if (sp == NULL) 452 continue; 453 454 /* Skip any sessions which are not in LISTEN mode. */ 455 if (sp->state != PPPOE_LISTENING) 456 continue; 457 458 neg = sp->neg; 459 460 /* Empty Service-Name matches any service. */ 461 if (neg->service_len == 0) 462 break; 463 464 /* Special case for a blank or "*" service name (wildcard). */ 465 if (neg->service_len == 1 && neg->service.data[0] == '*') 466 break; 467 468 /* If the lengths don't match, that aint it. */ 469 if (neg->service_len != ntohs(tag->tag_len)) 470 continue; 471 472 if (strncmp(tag->tag_data, neg->service.data, 473 ntohs(tag->tag_len)) == 0) 474 break; 475 } 476 CTR3(KTR_NET, "%20s: matched %p for %s", __func__, hook, tag->tag_data); 477 478 return (hook); 479} 480 481/* 482 * Broadcast the PADI packet in m0 to all listening hooks. 483 * This routine is called when a PADI with empty Service-Name 484 * tag is received. Client should receive PADOs with all 485 * available services. 486 */ 487static int 488pppoe_broadcast_padi(node_p node, struct mbuf *m0) 489{ 490 hook_p hook; 491 int error = 0; 492 493 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 494 sessp sp = NG_HOOK_PRIVATE(hook); 495 struct mbuf *m; 496 497 /* 498 * Go through all listening hooks and 499 * broadcast the PADI packet up there 500 */ 501 if (sp == NULL) 502 continue; 503 504 if (sp->state != PPPOE_LISTENING) 505 continue; 506 507 m = m_dup(m0, M_DONTWAIT); 508 if (m == NULL) 509 return (ENOMEM); 510 NG_SEND_DATA_ONLY(error, hook, m); 511 if (error) 512 return (error); 513 } 514 515 return (0); 516} 517 518/* 519 * Find a hook, which name equals to given service. 520 */ 521static hook_p 522pppoe_find_svc(node_p node, const char *svc_name, int svc_len) 523{ 524 hook_p hook; 525 526 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 527 sessp sp = NG_HOOK_PRIVATE(hook); 528 negp neg; 529 530 /* Skip any nonsession hook. */ 531 if (sp == NULL) 532 continue; 533 534 /* Skip any sessions which are not in LISTEN mode. */ 535 if (sp->state != PPPOE_LISTENING) 536 continue; 537 538 neg = sp->neg; 539 540 if (neg->service_len == svc_len && 541 strncmp(svc_name, neg->service.data, svc_len == 0)) 542 return (hook); 543 } 544 545 return (NULL); 546} 547 548/************************************************************************** 549 * Routine to find a particular session that matches an incoming packet. * 550 **************************************************************************/ 551static hook_p 552pppoe_findsession(node_p node, const struct pppoe_full_hdr *wh) 553{ 554 hook_p hook = NULL; 555 uint16_t session = ntohs(wh->ph.sid); 556 557 /* 558 * Find matching peer/session combination. 559 */ 560 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 561 sessp sp = NG_HOOK_PRIVATE(hook); 562 563 /* Skip any nonsession hook. */ 564 if (sp == NULL) 565 continue; 566 if (sp->Session_ID == session && 567 (sp->state == PPPOE_CONNECTED || 568 sp->state == PPPOE_NEWCONNECTED) && 569 bcmp(sp->pkt_hdr.eh.ether_dhost, 570 wh->eh.ether_shost, ETHER_ADDR_LEN) == 0) { 571 break; 572 } 573 } 574 CTR3(KTR_NET, "%20s: matched %p for %d", __func__, hook, session); 575 576 return (hook); 577} 578 579static hook_p 580pppoe_finduniq(node_p node, const struct pppoe_tag *tag) 581{ 582 hook_p hook = NULL; 583 union uniq uniq; 584 585 bcopy(tag->tag_data, uniq.bytes, sizeof(void *)); 586 /* Cycle through all known hooks. */ 587 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 588 /* Skip any nonsession hook. */ 589 if (NG_HOOK_PRIVATE(hook) == NULL) 590 continue; 591 if (uniq.pointer == NG_HOOK_PRIVATE(hook)) 592 break; 593 } 594 CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, uniq.pointer); 595 596 return (hook); 597} 598 599/************************************************************************** 600 * Start of Netgraph entrypoints. * 601 **************************************************************************/ 602 603/* 604 * Allocate the private data structure and link it with node. 605 */ 606static int 607ng_pppoe_constructor(node_p node) 608{ 609 priv_p privp; 610 611 /* Initialize private descriptor. */ 612 privp = malloc(sizeof(*privp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 613 if (privp == NULL) 614 return (ENOMEM); 615 616 /* Link structs together; this counts as our one reference to *node. */ 617 NG_NODE_SET_PRIVATE(node, privp); 618 privp->node = node; 619 620 /* Initialize to standard mode. */ 621 memset(&privp->eh.ether_dhost, 0xff, ETHER_ADDR_LEN); 622 privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 623 624 CTR3(KTR_NET, "%20s: created node [%x] (%p)", 625 __func__, node->nd_ID, node); 626 627 return (0); 628} 629 630/* 631 * Give our ok for a hook to be added... 632 * point the hook's private info to the hook structure. 633 * 634 * The following hook names are special: 635 * "ethernet": the hook that should be connected to a NIC. 636 * "debug": copies of data sent out here (when I write the code). 637 * All other hook names need only be unique. (the framework checks this). 638 */ 639static int 640ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 641{ 642 const priv_p privp = NG_NODE_PRIVATE(node); 643 sessp sp; 644 645 if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 646 privp->ethernet_hook = hook; 647 } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 648 privp->debug_hook = hook; 649 } else { 650 /* 651 * Any other unique name is OK. 652 * The infrastructure has already checked that it's unique, 653 * so just allocate it and hook it in. 654 */ 655 sp = malloc(sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 656 if (sp == NULL) 657 return (ENOMEM); 658 659 NG_HOOK_SET_PRIVATE(hook, sp); 660 sp->hook = hook; 661 } 662 CTR5(KTR_NET, "%20s: node [%x] (%p) connected hook %s (%p)", 663 __func__, node->nd_ID, node, name, hook); 664 665 return(0); 666} 667 668/* 669 * Hook has been added successfully. Request the MAC address of 670 * the underlying Ethernet node. 671 */ 672static int 673ng_pppoe_connect(hook_p hook) 674{ 675 const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 676 struct ng_mesg *msg; 677 int error; 678 679 if (hook != privp->ethernet_hook) 680 return (0); 681 682 /* 683 * If this is Ethernet hook, then request MAC address 684 * from our downstream. 685 */ 686 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, 0, M_NOWAIT); 687 if (msg == NULL) 688 return (ENOBUFS); 689 690 /* 691 * Our hook and peer hook have HK_INVALID flag set, 692 * so we can't use NG_SEND_MSG_HOOK() macro here. 693 */ 694 NG_SEND_MSG_ID(error, privp->node, msg, 695 NG_NODE_ID(NG_PEER_NODE(privp->ethernet_hook)), 696 NG_NODE_ID(privp->node)); 697 698 return (error); 699} 700/* 701 * Get a netgraph control message. 702 * Check it is one we understand. If needed, send a response. 703 * We sometimes save the address for an async action later. 704 * Always free the message. 705 */ 706static int 707ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 708{ 709 priv_p privp = NG_NODE_PRIVATE(node); 710 struct ngpppoe_init_data *ourmsg = NULL; 711 struct ng_mesg *resp = NULL; 712 int error = 0; 713 hook_p hook = NULL; 714 sessp sp = NULL; 715 negp neg = NULL; 716 struct ng_mesg *msg; 717 718 NGI_GET_MSG(item, msg); 719 CTR5(KTR_NET, "%20s: node [%x] (%p) got message %d with cookie %d", 720 __func__, node->nd_ID, node, msg->header.cmd, 721 msg->header.typecookie); 722 723 /* Deal with message according to cookie and command. */ 724 switch (msg->header.typecookie) { 725 case NGM_PPPOE_COOKIE: 726 switch (msg->header.cmd) { 727 case NGM_PPPOE_CONNECT: 728 case NGM_PPPOE_LISTEN: 729 case NGM_PPPOE_OFFER: 730 case NGM_PPPOE_SERVICE: 731 ourmsg = (struct ngpppoe_init_data *)msg->data; 732 if (msg->header.arglen < sizeof(*ourmsg)) { 733 log(LOG_ERR, "ng_pppoe[%x]: init data too " 734 "small\n", node->nd_ID); 735 LEAVE(EMSGSIZE); 736 } 737 if (msg->header.arglen - sizeof(*ourmsg) > 738 PPPOE_SERVICE_NAME_SIZE) { 739 log(LOG_ERR, "ng_pppoe[%x]: service name " 740 "too big\n", node->nd_ID); 741 LEAVE(EMSGSIZE); 742 } 743 if (msg->header.arglen - sizeof(*ourmsg) < 744 ourmsg->data_len) { 745 log(LOG_ERR, "ng_pppoe[%x]: init data has bad " 746 "length, %d should be %zd\n", node->nd_ID, 747 ourmsg->data_len, 748 msg->header.arglen - sizeof (*ourmsg)); 749 LEAVE(EMSGSIZE); 750 } 751 752 /* Make sure strcmp will terminate safely. */ 753 ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 754 755 /* Cycle through all known hooks. */ 756 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 757 if (NG_HOOK_NAME(hook) && 758 strcmp(NG_HOOK_NAME(hook), ourmsg->hook) == 759 0) 760 break; 761 } 762 if (hook == NULL) 763 LEAVE(ENOENT); 764 765 sp = NG_HOOK_PRIVATE(hook); 766 767 if (sp == NULL) 768 LEAVE(EINVAL); 769 770 if (msg->header.cmd == NGM_PPPOE_LISTEN) { 771 /* 772 * Ensure we aren't already listening for this 773 * service. 774 */ 775 if (pppoe_find_svc(node, ourmsg->data, 776 ourmsg->data_len) != NULL) 777 LEAVE(EEXIST); 778 } 779 780 /* 781 * PPPOE_SERVICE advertisments are set up 782 * on sessions that are in PRIMED state. 783 */ 784 if (msg->header.cmd == NGM_PPPOE_SERVICE) 785 break; 786 787 if (sp->state != PPPOE_SNONE) { 788 log(LOG_NOTICE, "ng_pppoe[%x]: Session already " 789 "active\n", node->nd_ID); 790 LEAVE(EISCONN); 791 } 792 793 /* 794 * Set up prototype header. 795 */ 796 neg = malloc(sizeof(*neg), M_NETGRAPH_PPPOE, 797 M_NOWAIT | M_ZERO); 798 799 if (neg == NULL) 800 LEAVE(ENOMEM); 801 802 neg->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); 803 if (neg->m == NULL) { 804 free(neg, M_NETGRAPH_PPPOE); 805 LEAVE(ENOBUFS); 806 } 807 neg->m->m_pkthdr.rcvif = NULL; 808 sp->neg = neg; 809 ng_callout_init(&neg->handle); 810 neg->m->m_len = sizeof(struct pppoe_full_hdr); 811 neg->pkt = mtod(neg->m, union packet*); 812 memcpy((void *)&neg->pkt->pkt_header.eh, 813 &privp->eh, sizeof(struct ether_header)); 814 neg->pkt->pkt_header.ph.ver = 0x1; 815 neg->pkt->pkt_header.ph.type = 0x1; 816 neg->pkt->pkt_header.ph.sid = 0x0000; 817 neg->timeout = 0; 818 819 sp->creator = NGI_RETADDR(item); 820 } 821 switch (msg->header.cmd) { 822 case NGM_PPPOE_GET_STATUS: 823 { 824 struct ngpppoestat *stats; 825 826 NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 827 if (!resp) 828 LEAVE(ENOMEM); 829 830 stats = (struct ngpppoestat *) resp->data; 831 stats->packets_in = privp->packets_in; 832 stats->packets_out = privp->packets_out; 833 break; 834 } 835 case NGM_PPPOE_CONNECT: 836 /* 837 * Check the hook exists and is Uninitialised. 838 * Send a PADI request, and start the timeout logic. 839 * Store the originator of this message so we can send 840 * a success of fail message to them later. 841 * Move the session to SINIT. 842 * Set up the session to the correct state and 843 * start it. 844 */ 845 neg->service.hdr.tag_type = PTT_SRV_NAME; 846 neg->service.hdr.tag_len = 847 htons((uint16_t)ourmsg->data_len); 848 if (ourmsg->data_len) 849 bcopy(ourmsg->data, neg->service.data, 850 ourmsg->data_len); 851 neg->service_len = ourmsg->data_len; 852 pppoe_start(sp); 853 break; 854 case NGM_PPPOE_LISTEN: 855 /* 856 * Check the hook exists and is Uninitialised. 857 * Install the service matching string. 858 * Store the originator of this message so we can send 859 * a success of fail message to them later. 860 * Move the hook to 'LISTENING' 861 */ 862 neg->service.hdr.tag_type = PTT_SRV_NAME; 863 neg->service.hdr.tag_len = 864 htons((uint16_t)ourmsg->data_len); 865 866 if (ourmsg->data_len) 867 bcopy(ourmsg->data, neg->service.data, 868 ourmsg->data_len); 869 neg->service_len = ourmsg->data_len; 870 neg->pkt->pkt_header.ph.code = PADT_CODE; 871 /* 872 * Wait for PADI packet coming from Ethernet. 873 */ 874 sp->state = PPPOE_LISTENING; 875 break; 876 case NGM_PPPOE_OFFER: 877 /* 878 * Check the hook exists and is Uninitialised. 879 * Store the originator of this message so we can send 880 * a success of fail message to them later. 881 * Store the AC-Name given and go to PRIMED. 882 */ 883 neg->ac_name.hdr.tag_type = PTT_AC_NAME; 884 neg->ac_name.hdr.tag_len = 885 htons((uint16_t)ourmsg->data_len); 886 if (ourmsg->data_len) 887 bcopy(ourmsg->data, neg->ac_name.data, 888 ourmsg->data_len); 889 neg->ac_name_len = ourmsg->data_len; 890 neg->pkt->pkt_header.ph.code = PADO_CODE; 891 /* 892 * Wait for PADI packet coming from hook. 893 */ 894 sp->state = PPPOE_PRIMED; 895 break; 896 case NGM_PPPOE_SERVICE: 897 /* 898 * Check the session is primed. 899 * for now just allow ONE service to be advertised. 900 * If you do it twice you just overwrite. 901 */ 902 if (sp->state != PPPOE_PRIMED) { 903 log(LOG_NOTICE, "ng_pppoe[%x]: session not " 904 "primed\n", node->nd_ID); 905 LEAVE(EISCONN); 906 } 907 neg = sp->neg; 908 neg->service.hdr.tag_type = PTT_SRV_NAME; 909 neg->service.hdr.tag_len = 910 htons((uint16_t)ourmsg->data_len); 911 912 if (ourmsg->data_len) 913 bcopy(ourmsg->data, neg->service.data, 914 ourmsg->data_len); 915 neg->service_len = ourmsg->data_len; 916 break; 917 case NGM_PPPOE_SETMODE: 918 { 919 char *s; 920 size_t len; 921 922 if (msg->header.arglen == 0) 923 LEAVE(EINVAL); 924 925 s = (char *)msg->data; 926 len = msg->header.arglen - 1; 927 928 /* Search for matching mode string. */ 929 if (len == strlen(NG_PPPOE_STANDARD) && 930 (strncmp(NG_PPPOE_STANDARD, s, len) == 0)) { 931 privp->flags = 0; 932 privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 933 break; 934 } 935 if (len == strlen(NG_PPPOE_3COM) && 936 (strncmp(NG_PPPOE_3COM, s, len) == 0)) { 937 privp->flags |= COMPAT_3COM; 938 privp->eh.ether_type = 939 ETHERTYPE_PPPOE_3COM_DISC; 940 break; 941 } 942 if (len == strlen(NG_PPPOE_DLINK) && 943 (strncmp(NG_PPPOE_DLINK, s, len) == 0)) { 944 privp->flags |= COMPAT_DLINK; 945 break; 946 } 947 error = EINVAL; 948 break; 949 } 950 case NGM_PPPOE_GETMODE: 951 { 952 char *s; 953 size_t len = 0; 954 955 if (privp->flags == 0) 956 len += strlen(NG_PPPOE_STANDARD) + 1; 957 if (privp->flags & COMPAT_3COM) 958 len += strlen(NG_PPPOE_3COM) + 1; 959 if (privp->flags & COMPAT_DLINK) 960 len += strlen(NG_PPPOE_DLINK) + 1; 961 962 NG_MKRESPONSE(resp, msg, len, M_NOWAIT); 963 if (resp == NULL) 964 LEAVE(ENOMEM); 965 966 s = (char *)resp->data; 967 if (privp->flags == 0) { 968 len = strlen(NG_PPPOE_STANDARD); 969 strlcpy(s, NG_PPPOE_STANDARD, len + 1); 970 break; 971 } 972 if (privp->flags & COMPAT_3COM) { 973 len = strlen(NG_PPPOE_3COM); 974 strlcpy(s, NG_PPPOE_3COM, len + 1); 975 s += len; 976 } 977 if (privp->flags & COMPAT_DLINK) { 978 if (s != resp->data) 979 *s++ = '|'; 980 len = strlen(NG_PPPOE_DLINK); 981 strlcpy(s, NG_PPPOE_DLINK, len + 1); 982 } 983 break; 984 } 985 case NGM_PPPOE_SETENADDR: 986 if (msg->header.arglen != ETHER_ADDR_LEN) 987 LEAVE(EINVAL); 988 bcopy(msg->data, &privp->eh.ether_shost, 989 ETHER_ADDR_LEN); 990 break; 991 default: 992 LEAVE(EINVAL); 993 } 994 break; 995 case NGM_ETHER_COOKIE: 996 if (!(msg->header.flags & NGF_RESP)) 997 LEAVE(EINVAL); 998 switch (msg->header.cmd) { 999 case NGM_ETHER_GET_ENADDR: 1000 if (msg->header.arglen != ETHER_ADDR_LEN) 1001 LEAVE(EINVAL); 1002 bcopy(msg->data, &privp->eh.ether_shost, 1003 ETHER_ADDR_LEN); 1004 break; 1005 default: 1006 LEAVE(EINVAL); 1007 } 1008 break; 1009 default: 1010 LEAVE(EINVAL); 1011 } 1012 1013 /* Take care of synchronous response, if any. */ 1014quit: 1015 CTR2(KTR_NET, "%20s: returning %d", __func__, error); 1016 NG_RESPOND_MSG(error, node, item, resp); 1017 /* Free the message and return. */ 1018 NG_FREE_MSG(msg); 1019 return(error); 1020} 1021 1022/* 1023 * Start a client into the first state. A separate function because 1024 * it can be needed if the negotiation times out. 1025 */ 1026static void 1027pppoe_start(sessp sp) 1028{ 1029 hook_p hook = sp->hook; 1030 node_p node = NG_HOOK_NODE(hook); 1031 priv_p privp = NG_NODE_PRIVATE(node); 1032 negp neg = sp->neg; 1033 struct { 1034 struct pppoe_tag hdr; 1035 union uniq data; 1036 } __packed uniqtag; 1037 struct mbuf *m0; 1038 int error; 1039 1040 /* 1041 * Kick the state machine into starting up. 1042 */ 1043 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1044 sp->state = PPPOE_SINIT; 1045 /* 1046 * Reset the packet header to broadcast. Since we are 1047 * in a client mode use configured ethertype. 1048 */ 1049 memcpy((void *)&neg->pkt->pkt_header.eh, &privp->eh, 1050 sizeof(struct ether_header)); 1051 neg->pkt->pkt_header.ph.code = PADI_CODE; 1052 uniqtag.hdr.tag_type = PTT_HOST_UNIQ; 1053 uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(uniqtag.data)); 1054 uniqtag.data.pointer = sp; 1055 init_tags(sp); 1056 insert_tag(sp, &uniqtag.hdr); 1057 insert_tag(sp, &neg->service.hdr); 1058 make_packet(sp); 1059 /* 1060 * Send packet and prepare to retransmit it after timeout. 1061 */ 1062 ng_callout(&neg->handle, node, hook, PPPOE_INITIAL_TIMEOUT * hz, 1063 pppoe_ticker, NULL, 0); 1064 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1065 m0 = m_copypacket(neg->m, M_DONTWAIT); 1066 NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 1067} 1068 1069static int 1070send_acname(sessp sp, const struct pppoe_tag *tag) 1071{ 1072 int error, tlen; 1073 struct ng_mesg *msg; 1074 struct ngpppoe_sts *sts; 1075 1076 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1077 1078 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME, 1079 sizeof(struct ngpppoe_sts), M_NOWAIT); 1080 if (msg == NULL) 1081 return (ENOMEM); 1082 1083 sts = (struct ngpppoe_sts *)msg->data; 1084 tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len)); 1085 strncpy(sts->hook, tag->tag_data, tlen); 1086 sts->hook[tlen] = '\0'; 1087 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1088 1089 return (error); 1090} 1091 1092static int 1093send_sessionid(sessp sp) 1094{ 1095 int error; 1096 struct ng_mesg *msg; 1097 1098 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1099 1100 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID, 1101 sizeof(uint16_t), M_NOWAIT); 1102 if (msg == NULL) 1103 return (ENOMEM); 1104 1105 *(uint16_t *)msg->data = sp->Session_ID; 1106 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1107 1108 return (error); 1109} 1110 1111/* 1112 * Receive data, and do something with it. 1113 * The caller will never free m, so if we use up this data 1114 * or abort we must free it. 1115 */ 1116static int 1117ng_pppoe_rcvdata(hook_p hook, item_p item) 1118{ 1119 node_p node = NG_HOOK_NODE(hook); 1120 const priv_p privp = NG_NODE_PRIVATE(node); 1121 sessp sp = NG_HOOK_PRIVATE(hook); 1122 const struct pppoe_tag *utag = NULL, *tag = NULL; 1123 const struct pppoe_full_hdr *wh; 1124 const struct pppoe_hdr *ph; 1125 negp neg = NULL; 1126 struct mbuf *m; 1127 hook_p sendhook; 1128 int error = 0; 1129 uint16_t session; 1130 uint16_t length; 1131 uint8_t code; 1132 struct { 1133 struct pppoe_tag hdr; 1134 union uniq data; 1135 } __packed uniqtag; 1136 struct mbuf *m0; 1137 1138 CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 1139 __func__, node->nd_ID, node, item, hook->hk_name, hook); 1140 1141 NGI_GET_M(item, m); 1142 if (hook == privp->debug_hook) { 1143 /* 1144 * Data from the debug hook gets sent without modification 1145 * straight to the ethernet. 1146 */ 1147 NG_FWD_ITEM_HOOK( error, item, privp->ethernet_hook); 1148 privp->packets_out++; 1149 } else if (hook == privp->ethernet_hook) { 1150 /* 1151 * Incoming data. 1152 * Dig out various fields from the packet. 1153 * Use them to decide where to send it. 1154 */ 1155 1156 privp->packets_in++; 1157 if( m->m_len < sizeof(*wh)) { 1158 m = m_pullup(m, sizeof(*wh)); /* Checks length */ 1159 if (m == NULL) { 1160 log(LOG_NOTICE, "ng_pppoe[%x]: couldn't " 1161 "m_pullup(wh)\n", node->nd_ID); 1162 LEAVE(ENOBUFS); 1163 } 1164 } 1165 wh = mtod(m, struct pppoe_full_hdr *); 1166 length = ntohs(wh->ph.length); 1167 switch(wh->eh.ether_type) { 1168 case ETHERTYPE_PPPOE_3COM_DISC: /* fall through */ 1169 case ETHERTYPE_PPPOE_DISC: 1170 /* 1171 * We need to try to make sure that the tag area 1172 * is contiguous, or we could wander off the end 1173 * of a buffer and make a mess. 1174 * (Linux wouldn't have this problem). 1175 */ 1176 if (m->m_pkthdr.len <= MHLEN) { 1177 if( m->m_len < m->m_pkthdr.len) { 1178 m = m_pullup(m, m->m_pkthdr.len); 1179 if (m == NULL) { 1180 log(LOG_NOTICE, "ng_pppoe[%x]: " 1181 "couldn't " 1182 "m_pullup(pkthdr)\n", 1183 node->nd_ID); 1184 LEAVE(ENOBUFS); 1185 } 1186 } 1187 } 1188 if (m->m_len != m->m_pkthdr.len) { 1189 /* 1190 * It's not all in one piece. 1191 * We need to do extra work. 1192 * Put it into a cluster. 1193 */ 1194 struct mbuf *n; 1195 n = m_dup(m, M_DONTWAIT); 1196 m_freem(m); 1197 m = n; 1198 if (m) { 1199 /* just check we got a cluster */ 1200 if (m->m_len != m->m_pkthdr.len) { 1201 m_freem(m); 1202 m = NULL; 1203 } 1204 } 1205 if (m == NULL) { 1206 log(LOG_NOTICE, "ng_pppoe[%x]: packet " 1207 "fragmented\n", node->nd_ID); 1208 LEAVE(EMSGSIZE); 1209 } 1210 } 1211 wh = mtod(m, struct pppoe_full_hdr *); 1212 length = ntohs(wh->ph.length); 1213 ph = &wh->ph; 1214 session = ntohs(wh->ph.sid); 1215 code = wh->ph.code; 1216 1217 switch(code) { 1218 case PADI_CODE: 1219 /* 1220 * We are a server: 1221 * Look for a hook with the required service 1222 * and send the ENTIRE packet up there. 1223 * It should come back to a new hook in 1224 * PRIMED state. Look there for further 1225 * processing. 1226 */ 1227 tag = get_tag(ph, PTT_SRV_NAME); 1228 if (tag == NULL) { 1229 CTR1(KTR_NET, 1230 "%20s: PADI w/o Service-Name", 1231 __func__); 1232 LEAVE(ENETUNREACH); 1233 } 1234 1235 /* 1236 * First, try to match Service-Name 1237 * against our listening hooks. If 1238 * no success and we are in D-Link 1239 * compat mode and Service-Name is 1240 * empty, then we broadcast the PADI 1241 * to all listening hooks. 1242 */ 1243 sendhook = pppoe_match_svc(node, tag); 1244 if (sendhook != NULL) 1245 NG_FWD_NEW_DATA(error, item, 1246 sendhook, m); 1247 else if (privp->flags & COMPAT_DLINK && 1248 ntohs(tag->tag_len) == 0) 1249 error = pppoe_broadcast_padi(node, m); 1250 else 1251 error = ENETUNREACH; 1252 break; 1253 case PADO_CODE: 1254 /* 1255 * We are a client: 1256 * Use the host_uniq tag to find the 1257 * hook this is in response to. 1258 * Received #2, now send #3 1259 * For now simply accept the first we receive. 1260 */ 1261 utag = get_tag(ph, PTT_HOST_UNIQ); 1262 if ((utag == NULL) 1263 || (ntohs(utag->tag_len) != sizeof(sp))) { 1264 log(LOG_NOTICE, "ng_pppoe[%x]: no host " 1265 "unique field\n", node->nd_ID); 1266 LEAVE(ENETUNREACH); 1267 } 1268 1269 sendhook = pppoe_finduniq(node, utag); 1270 if (sendhook == NULL) { 1271 log(LOG_NOTICE, "ng_pppoe[%x]: no " 1272 "matching session\n", node->nd_ID); 1273 LEAVE(ENETUNREACH); 1274 } 1275 1276 /* 1277 * Check the session is in the right state. 1278 * It needs to be in PPPOE_SINIT. 1279 */ 1280 sp = NG_HOOK_PRIVATE(sendhook); 1281 if (sp->state != PPPOE_SINIT) { 1282 log(LOG_NOTICE, "ng_pppoe[%x]: session " 1283 "in wrong state\n", node->nd_ID); 1284 LEAVE(ENETUNREACH); 1285 } 1286 neg = sp->neg; 1287 ng_uncallout(&neg->handle, node); 1288 1289 /* 1290 * This is the first time we hear 1291 * from the server, so note it's 1292 * unicast address, replacing the 1293 * broadcast address . 1294 */ 1295 bcopy(wh->eh.ether_shost, 1296 neg->pkt->pkt_header.eh.ether_dhost, 1297 ETHER_ADDR_LEN); 1298 neg->timeout = 0; 1299 neg->pkt->pkt_header.ph.code = PADR_CODE; 1300 init_tags(sp); 1301 insert_tag(sp, utag); /* Host Unique */ 1302 if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1303 insert_tag(sp, tag); /* return cookie */ 1304 if ((tag = get_tag(ph, PTT_AC_NAME))) { 1305 insert_tag(sp, tag); /* return it */ 1306 send_acname(sp, tag); 1307 } 1308 insert_tag(sp, &neg->service.hdr); /* Service */ 1309 scan_tags(sp, ph); 1310 make_packet(sp); 1311 sp->state = PPPOE_SREQ; 1312 ng_callout(&neg->handle, node, sp->hook, 1313 PPPOE_INITIAL_TIMEOUT * hz, 1314 pppoe_ticker, NULL, 0); 1315 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1316 m0 = m_copypacket(neg->m, M_DONTWAIT); 1317 NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 1318 break; 1319 case PADR_CODE: 1320 1321 /* 1322 * We are a server: 1323 * Use the ac_cookie tag to find the 1324 * hook this is in response to. 1325 */ 1326 utag = get_tag(ph, PTT_AC_COOKIE); 1327 if ((utag == NULL) 1328 || (ntohs(utag->tag_len) != sizeof(sp))) { 1329 LEAVE(ENETUNREACH); 1330 } 1331 1332 sendhook = pppoe_finduniq(node, utag); 1333 if (sendhook == NULL) { 1334 LEAVE(ENETUNREACH); 1335 } 1336 1337 /* 1338 * Check the session is in the right state. 1339 * It needs to be in PPPOE_SOFFER 1340 * or PPPOE_NEWCONNECTED. If the latter, 1341 * then this is a retry by the client. 1342 * so be nice, and resend. 1343 */ 1344 sp = NG_HOOK_PRIVATE(sendhook); 1345 if (sp->state == PPPOE_NEWCONNECTED) { 1346 /* 1347 * Whoa! drop back to resend that 1348 * PADS packet. 1349 * We should still have a copy of it. 1350 */ 1351 sp->state = PPPOE_SOFFER; 1352 } 1353 if (sp->state != PPPOE_SOFFER) { 1354 LEAVE (ENETUNREACH); 1355 break; 1356 } 1357 neg = sp->neg; 1358 ng_uncallout(&neg->handle, node); 1359 neg->pkt->pkt_header.ph.code = PADS_CODE; 1360 if (sp->Session_ID == 0) 1361 neg->pkt->pkt_header.ph.sid = 1362 htons(sp->Session_ID 1363 = get_new_sid(node)); 1364 send_sessionid(sp); 1365 neg->timeout = 0; 1366 /* 1367 * start working out the tags to respond with. 1368 */ 1369 init_tags(sp); 1370 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1371 if ((tag = get_tag(ph, PTT_SRV_NAME))) 1372 insert_tag(sp, tag);/* return service */ 1373 if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 1374 insert_tag(sp, tag); /* return it */ 1375 insert_tag(sp, utag); /* ac_cookie */ 1376 scan_tags(sp, ph); 1377 make_packet(sp); 1378 sp->state = PPPOE_NEWCONNECTED; 1379 1380 /* Send the PADS without a timeout - we're now connected. */ 1381 m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1382 NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 1383 1384 /* 1385 * Having sent the last Negotiation header, 1386 * Set up the stored packet header to 1387 * be correct for the actual session. 1388 * But keep the negotialtion stuff 1389 * around in case we need to resend this last 1390 * packet. We'll discard it when we move 1391 * from NEWCONNECTED to CONNECTED 1392 */ 1393 sp->pkt_hdr = neg->pkt->pkt_header; 1394 /* Configure ethertype depending on what 1395 * ethertype was used at discovery phase */ 1396 if (sp->pkt_hdr.eh.ether_type == 1397 ETHERTYPE_PPPOE_3COM_DISC) 1398 sp->pkt_hdr.eh.ether_type 1399 = ETHERTYPE_PPPOE_3COM_SESS; 1400 else 1401 sp->pkt_hdr.eh.ether_type 1402 = ETHERTYPE_PPPOE_SESS; 1403 sp->pkt_hdr.ph.code = 0; 1404 pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 1405 break; 1406 case PADS_CODE: 1407 /* 1408 * We are a client: 1409 * Use the host_uniq tag to find the 1410 * hook this is in response to. 1411 * take the session ID and store it away. 1412 * Also make sure the pre-made header is 1413 * correct and set us into Session mode. 1414 */ 1415 utag = get_tag(ph, PTT_HOST_UNIQ); 1416 if ((utag == NULL) 1417 || (ntohs(utag->tag_len) != sizeof(sp))) { 1418 LEAVE (ENETUNREACH); 1419 break; 1420 } 1421 sendhook = pppoe_finduniq(node, utag); 1422 if (sendhook == NULL) { 1423 LEAVE(ENETUNREACH); 1424 } 1425 1426 /* 1427 * Check the session is in the right state. 1428 * It needs to be in PPPOE_SREQ. 1429 */ 1430 sp = NG_HOOK_PRIVATE(sendhook); 1431 if (sp->state != PPPOE_SREQ) { 1432 LEAVE(ENETUNREACH); 1433 } 1434 neg = sp->neg; 1435 ng_uncallout(&neg->handle, node); 1436 neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1437 sp->Session_ID = ntohs(wh->ph.sid); 1438 send_sessionid(sp); 1439 neg->timeout = 0; 1440 sp->state = PPPOE_CONNECTED; 1441 /* 1442 * Now we have gone to Connected mode, 1443 * Free all resources needed for 1444 * negotiation. 1445 * Keep a copy of the header we will be using. 1446 */ 1447 sp->pkt_hdr = neg->pkt->pkt_header; 1448 if (privp->flags & COMPAT_3COM) 1449 sp->pkt_hdr.eh.ether_type 1450 = ETHERTYPE_PPPOE_3COM_SESS; 1451 else 1452 sp->pkt_hdr.eh.ether_type 1453 = ETHERTYPE_PPPOE_SESS; 1454 sp->pkt_hdr.ph.code = 0; 1455 m_freem(neg->m); 1456 free(sp->neg, M_NETGRAPH_PPPOE); 1457 sp->neg = NULL; 1458 pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 1459 break; 1460 case PADT_CODE: 1461 /* 1462 * Find matching peer/session combination. 1463 */ 1464 sendhook = pppoe_findsession(node, wh); 1465 if (sendhook == NULL) { 1466 LEAVE(ENETUNREACH); 1467 } 1468 /* Disconnect that hook. */ 1469 ng_rmhook_self(sendhook); 1470 break; 1471 default: 1472 LEAVE(EPFNOSUPPORT); 1473 } 1474 break; 1475 case ETHERTYPE_PPPOE_3COM_SESS: 1476 case ETHERTYPE_PPPOE_SESS: 1477 /* 1478 * Find matching peer/session combination. 1479 */ 1480 sendhook = pppoe_findsession(node, wh); 1481 if (sendhook == NULL) { 1482 LEAVE (ENETUNREACH); 1483 break; 1484 } 1485 sp = NG_HOOK_PRIVATE(sendhook); 1486 m_adj(m, sizeof(*wh)); 1487 if (m->m_pkthdr.len < length) { 1488 /* Packet too short, dump it */ 1489 LEAVE(EMSGSIZE); 1490 } 1491 1492 /* Also need to trim excess at the end */ 1493 if (m->m_pkthdr.len > length) { 1494 m_adj(m, -((int)(m->m_pkthdr.len - length))); 1495 } 1496 if ( sp->state != PPPOE_CONNECTED) { 1497 if (sp->state == PPPOE_NEWCONNECTED) { 1498 sp->state = PPPOE_CONNECTED; 1499 /* 1500 * Now we have gone to Connected mode, 1501 * Free all resources needed for 1502 * negotiation. Be paranoid about 1503 * whether there may be a timeout. 1504 */ 1505 m_freem(sp->neg->m); 1506 ng_uncallout(&sp->neg->handle, node); 1507 free(sp->neg, M_NETGRAPH_PPPOE); 1508 sp->neg = NULL; 1509 } else { 1510 LEAVE (ENETUNREACH); 1511 break; 1512 } 1513 } 1514 NG_FWD_NEW_DATA( error, item, sendhook, m); 1515 break; 1516 default: 1517 LEAVE(EPFNOSUPPORT); 1518 } 1519 } else { 1520 /* 1521 * Not ethernet or debug hook.. 1522 * 1523 * The packet has come in on a normal hook. 1524 * We need to find out what kind of hook, 1525 * So we can decide how to handle it. 1526 * Check the hook's state. 1527 */ 1528 sp = NG_HOOK_PRIVATE(hook); 1529 switch (sp->state) { 1530 case PPPOE_NEWCONNECTED: 1531 case PPPOE_CONNECTED: { 1532 static const u_char addrctrl[] = { 0xff, 0x03 }; 1533 struct pppoe_full_hdr *wh; 1534 1535 /* 1536 * Remove PPP address and control fields, if any. 1537 * For example, ng_ppp(4) always sends LCP packets 1538 * with address and control fields as required by 1539 * generic PPP. PPPoE is an exception to the rule. 1540 */ 1541 if (m->m_pkthdr.len >= 2) { 1542 if (m->m_len < 2 && !(m = m_pullup(m, 2))) 1543 LEAVE(ENOBUFS); 1544 if (bcmp(mtod(m, u_char *), addrctrl, 2) == 0) 1545 m_adj(m, 2); 1546 } 1547 /* 1548 * Bang in a pre-made header, and set the length up 1549 * to be correct. Then send it to the ethernet driver. 1550 */ 1551 M_PREPEND(m, sizeof(*wh), M_DONTWAIT); 1552 if (m == NULL) 1553 LEAVE(ENOBUFS); 1554 1555 wh = mtod(m, struct pppoe_full_hdr *); 1556 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1557 wh->ph.length = htons(m->m_pkthdr.len - sizeof(*wh)); 1558 NG_FWD_NEW_DATA( error, item, privp->ethernet_hook, m); 1559 privp->packets_out++; 1560 break; 1561 } 1562 case PPPOE_PRIMED: 1563 /* 1564 * A PADI packet is being returned by the application 1565 * that has set up this hook. This indicates that it 1566 * wants us to offer service. 1567 */ 1568 neg = sp->neg; 1569 if (m->m_len < sizeof(*wh)) { 1570 m = m_pullup(m, sizeof(*wh)); 1571 if (m == NULL) 1572 LEAVE(ENOBUFS); 1573 } 1574 wh = mtod(m, struct pppoe_full_hdr *); 1575 ph = &wh->ph; 1576 session = ntohs(wh->ph.sid); 1577 length = ntohs(wh->ph.length); 1578 code = wh->ph.code; 1579 /* Use peers mode in session. */ 1580 neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type; 1581 if (code != PADI_CODE) 1582 LEAVE(EINVAL); 1583 ng_uncallout(&neg->handle, node); 1584 1585 /* 1586 * This is the first time we hear 1587 * from the client, so note it's 1588 * unicast address, replacing the 1589 * broadcast address. 1590 */ 1591 bcopy(wh->eh.ether_shost, 1592 neg->pkt->pkt_header.eh.ether_dhost, 1593 ETHER_ADDR_LEN); 1594 sp->state = PPPOE_SOFFER; 1595 neg->timeout = 0; 1596 neg->pkt->pkt_header.ph.code = PADO_CODE; 1597 1598 /* 1599 * Start working out the tags to respond with. 1600 */ 1601 uniqtag.hdr.tag_type = PTT_AC_COOKIE; 1602 uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 1603 uniqtag.data.pointer = sp; 1604 init_tags(sp); 1605 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1606 if ((tag = get_tag(ph, PTT_SRV_NAME))) 1607 insert_tag(sp, tag); /* return service */ 1608 /* 1609 * If we have a NULL service request 1610 * and have an extra service defined in this hook, 1611 * then also add a tag for the extra service. 1612 * XXX this is a hack. eventually we should be able 1613 * to support advertising many services, not just one 1614 */ 1615 if (((tag == NULL) || (tag->tag_len == 0)) && 1616 (neg->service.hdr.tag_len != 0)) { 1617 insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1618 } 1619 if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 1620 insert_tag(sp, tag); /* returned hostunique */ 1621 insert_tag(sp, &uniqtag.hdr); 1622 scan_tags(sp, ph); 1623 make_packet(sp); 1624 /* 1625 * Send the offer but if they don't respond 1626 * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 1627 */ 1628 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz, 1629 pppoe_ticker, NULL, 0); 1630 m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1631 NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 1632 break; 1633 1634 /* 1635 * Packets coming from the hook make no sense 1636 * to sessions in these states. Throw them away. 1637 */ 1638 case PPPOE_SINIT: 1639 case PPPOE_SREQ: 1640 case PPPOE_SOFFER: 1641 case PPPOE_SNONE: 1642 case PPPOE_LISTENING: 1643 case PPPOE_DEAD: 1644 default: 1645 LEAVE(ENETUNREACH); 1646 } 1647 } 1648quit: 1649 if (item) 1650 NG_FREE_ITEM(item); 1651 NG_FREE_M(m); 1652 return error; 1653} 1654 1655/* 1656 * Do local shutdown processing.. 1657 * If we are a persistant device, we might refuse to go away, and 1658 * we'd only remove our links and reset ourself. 1659 */ 1660static int 1661ng_pppoe_shutdown(node_p node) 1662{ 1663 const priv_p privdata = NG_NODE_PRIVATE(node); 1664 1665 NG_NODE_SET_PRIVATE(node, NULL); 1666 NG_NODE_UNREF(privdata->node); 1667 free(privdata, M_NETGRAPH_PPPOE); 1668 return (0); 1669} 1670 1671/* 1672 * Hook disconnection 1673 * 1674 * Clean up all dangling links and information about the session/hook. 1675 * For this type, removal of the last link destroys the node. 1676 */ 1677static int 1678ng_pppoe_disconnect(hook_p hook) 1679{ 1680 node_p node = NG_HOOK_NODE(hook); 1681 priv_p privp = NG_NODE_PRIVATE(node); 1682 sessp sp; 1683 1684 if (hook == privp->debug_hook) { 1685 privp->debug_hook = NULL; 1686 } else if (hook == privp->ethernet_hook) { 1687 privp->ethernet_hook = NULL; 1688 if (NG_NODE_IS_VALID(node)) 1689 ng_rmnode_self(node); 1690 } else { 1691 sp = NG_HOOK_PRIVATE(hook); 1692 if (sp->state != PPPOE_SNONE ) { 1693 pppoe_send_event(sp, NGM_PPPOE_CLOSE); 1694 } 1695 /* 1696 * According to the spec, if we are connected, 1697 * we should send a DISC packet if we are shutting down 1698 * a session. 1699 */ 1700 if ((privp->ethernet_hook) 1701 && ((sp->state == PPPOE_CONNECTED) 1702 || (sp->state == PPPOE_NEWCONNECTED))) { 1703 struct mbuf *m; 1704 1705 /* Generate a packet of that type. */ 1706 MGETHDR(m, M_DONTWAIT, MT_DATA); 1707 if (m == NULL) 1708 log(LOG_NOTICE, "ng_pppoe[%x]: session out of " 1709 "mbufs\n", node->nd_ID); 1710 else { 1711 struct pppoe_full_hdr *wh; 1712 struct pppoe_tag *tag; 1713 int msglen = strlen(SIGNOFF); 1714 int error = 0; 1715 1716 m->m_pkthdr.rcvif = NULL; 1717 m->m_pkthdr.len = m->m_len = sizeof(*wh); 1718 wh = mtod(m, struct pppoe_full_hdr *); 1719 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1720 1721 /* Revert the stored header to DISC/PADT mode. */ 1722 wh->ph.code = PADT_CODE; 1723 /* 1724 * Configure ethertype depending on what 1725 * was used during sessions stage. 1726 */ 1727 if (wh->eh.ether_type == 1728 ETHERTYPE_PPPOE_3COM_SESS) 1729 wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC; 1730 else 1731 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 1732 /* 1733 * Add a General error message and adjust 1734 * sizes. 1735 */ 1736 tag = wh->ph.tag; 1737 tag->tag_type = PTT_GEN_ERR; 1738 tag->tag_len = htons((u_int16_t)msglen); 1739 strncpy(tag->tag_data, SIGNOFF, msglen); 1740 m->m_pkthdr.len = (m->m_len += sizeof(*tag) + 1741 msglen); 1742 wh->ph.length = htons(sizeof(*tag) + msglen); 1743 NG_SEND_DATA_ONLY(error, 1744 privp->ethernet_hook, m); 1745 } 1746 } 1747 /* 1748 * As long as we have somewhere to store the timeout handle, 1749 * we may have a timeout pending.. get rid of it. 1750 */ 1751 if (sp->neg) { 1752 ng_uncallout(&sp->neg->handle, node); 1753 if (sp->neg->m) 1754 m_freem(sp->neg->m); 1755 free(sp->neg, M_NETGRAPH_PPPOE); 1756 } 1757 free(sp, M_NETGRAPH_PPPOE); 1758 NG_HOOK_SET_PRIVATE(hook, NULL); 1759 } 1760 if ((NG_NODE_NUMHOOKS(node) == 0) && 1761 (NG_NODE_IS_VALID(node))) 1762 ng_rmnode_self(node); 1763 return (0); 1764} 1765 1766/* 1767 * Timeouts come here. 1768 */ 1769static void 1770pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2) 1771{ 1772 priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 1773 sessp sp = NG_HOOK_PRIVATE(hook); 1774 negp neg = sp->neg; 1775 struct mbuf *m0 = NULL; 1776 int error = 0; 1777 1778 CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d", 1779 __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID); 1780 switch(sp->state) { 1781 /* 1782 * Resend the last packet, using an exponential backoff. 1783 * After a period of time, stop growing the backoff, 1784 * And either leave it, or revert to the start. 1785 */ 1786 case PPPOE_SINIT: 1787 case PPPOE_SREQ: 1788 /* Timeouts on these produce resends. */ 1789 m0 = m_copypacket(sp->neg->m, M_DONTWAIT); 1790 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 1791 ng_callout(&neg->handle, node, hook, neg->timeout * hz, 1792 pppoe_ticker, NULL, 0); 1793 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 1794 if (sp->state == PPPOE_SREQ) { 1795 /* Revert to SINIT mode. */ 1796 pppoe_start(sp); 1797 } else { 1798 neg->timeout = PPPOE_TIMEOUT_LIMIT; 1799 } 1800 } 1801 break; 1802 case PPPOE_PRIMED: 1803 case PPPOE_SOFFER: 1804 /* A timeout on these says "give up" */ 1805 ng_rmhook_self(hook); 1806 break; 1807 default: 1808 /* Timeouts have no meaning in other states. */ 1809 log(LOG_NOTICE, "ng_pppoe[%x]: unexpected timeout\n", 1810 node->nd_ID); 1811 } 1812} 1813 1814/* 1815 * Parse an incoming packet to see if any tags should be copied to the 1816 * output packet. Don't do any tags that have been handled in the main 1817 * state machine. 1818 */ 1819static const struct pppoe_tag* 1820scan_tags(sessp sp, const struct pppoe_hdr* ph) 1821{ 1822 const char *const end = (const char *)next_tag(ph); 1823 const char *ptn; 1824 const struct pppoe_tag *pt = &ph->tag[0]; 1825 1826 /* 1827 * Keep processing tags while a tag header will still fit. 1828 */ 1829 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1830 1831 while((const char*)(pt + 1) <= end) { 1832 /* 1833 * If the tag data would go past the end of the packet, abort. 1834 */ 1835 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 1836 if(ptn > end) 1837 return NULL; 1838 1839 switch (pt->tag_type) { 1840 case PTT_RELAY_SID: 1841 insert_tag(sp, pt); 1842 break; 1843 case PTT_EOL: 1844 return NULL; 1845 case PTT_SRV_NAME: 1846 case PTT_AC_NAME: 1847 case PTT_HOST_UNIQ: 1848 case PTT_AC_COOKIE: 1849 case PTT_VENDOR: 1850 case PTT_SRV_ERR: 1851 case PTT_SYS_ERR: 1852 case PTT_GEN_ERR: 1853 break; 1854 } 1855 pt = (const struct pppoe_tag*)ptn; 1856 } 1857 return NULL; 1858} 1859 1860static int 1861pppoe_send_event(sessp sp, enum cmd cmdid) 1862{ 1863 int error; 1864 struct ng_mesg *msg; 1865 struct ngpppoe_sts *sts; 1866 1867 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1868 1869 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 1870 sizeof(struct ngpppoe_sts), M_NOWAIT); 1871 if (msg == NULL) 1872 return (ENOMEM); 1873 sts = (struct ngpppoe_sts *)msg->data; 1874 strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 1875 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1876 return (error); 1877} 1878