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