1 /* 2 * ng_pppoe.c 3 */ 4 5 /*- 6 * Copyright (c) 1996-1999 Whistle Communications, Inc. 7 * All rights reserved. 8 * 9 * Subject to the following obligations and disclaimer of warranty, use and 10 * redistribution of this software, in source or object code forms, with or 11 * without modifications are expressly permitted by Whistle Communications; 12 * provided, however, that: 13 * 1. Any and all reproductions of the source or object code must include the 14 * copyright notice above and the following disclaimer of warranties; and 15 * 2. No rights are granted, in any manner or form, to use Whistle 16 * Communications, Inc. trademarks, including the mark "WHISTLE 17 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18 * such appears in the above copyright notice or in the software. 19 * 20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36 * OF SUCH DAMAGE. 37 * 38 * Author: Julian Elischer <julian@freebsd.org> 39 * 40 * $FreeBSD$ 41 * $Whistle: ng_pppoe.c,v 1.10 1999/11/01 09:24:52 julian Exp $ 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/ktr.h> 48 #include <sys/mbuf.h> 49 #include <sys/malloc.h> 50 #include <sys/errno.h> 51 #include <sys/syslog.h> 52 #include <net/ethernet.h> 53 54 #include <netgraph/ng_message.h> 55 #include <netgraph/netgraph.h> 56 #include <netgraph/ng_parse.h> 57 #include <netgraph/ng_pppoe.h> 58 #include <netgraph/ng_ether.h> 59 60 #ifdef NG_SEPARATE_MALLOC 61 static MALLOC_DEFINE(M_NETGRAPH_PPPOE, "netgraph_pppoe", "netgraph pppoe node"); 62 #else 63 #define M_NETGRAPH_PPPOE M_NETGRAPH 64 #endif 65 66 #define SIGNOFF "session closed" 67 68 /* 69 * This section contains the netgraph method declarations for the 70 * pppoe node. These methods define the netgraph pppoe 'type'. 71 */ 72 73 static ng_constructor_t ng_pppoe_constructor; 74 static ng_rcvmsg_t ng_pppoe_rcvmsg; 75 static ng_shutdown_t ng_pppoe_shutdown; 76 static ng_newhook_t ng_pppoe_newhook; 77 static ng_connect_t ng_pppoe_connect; 78 static ng_rcvdata_t ng_pppoe_rcvdata; 79 static ng_rcvdata_t ng_pppoe_rcvdata_ether; 80 static ng_rcvdata_t ng_pppoe_rcvdata_debug; 81 static ng_disconnect_t ng_pppoe_disconnect; 82 83 /* Parse type for struct ngpppoe_init_data */ 84 static const struct ng_parse_struct_field ngpppoe_init_data_type_fields[] 85 = NG_PPPOE_INIT_DATA_TYPE_INFO; 86 static const struct ng_parse_type ngpppoe_init_data_state_type = { 87 &ng_parse_struct_type, 88 &ngpppoe_init_data_type_fields 89 }; 90 91 /* Parse type for struct ngpppoe_sts */ 92 static const struct ng_parse_struct_field ng_pppoe_sts_type_fields[] 93 = NG_PPPOE_STS_TYPE_INFO; 94 static const struct ng_parse_type ng_pppoe_sts_state_type = { 95 &ng_parse_struct_type, 96 &ng_pppoe_sts_type_fields 97 }; 98 99 /* List of commands and how to convert arguments to/from ASCII */ 100 static const struct ng_cmdlist ng_pppoe_cmds[] = { 101 { 102 NGM_PPPOE_COOKIE, 103 NGM_PPPOE_CONNECT, 104 "pppoe_connect", 105 &ngpppoe_init_data_state_type, 106 NULL 107 }, 108 { 109 NGM_PPPOE_COOKIE, 110 NGM_PPPOE_LISTEN, 111 "pppoe_listen", 112 &ngpppoe_init_data_state_type, 113 NULL 114 }, 115 { 116 NGM_PPPOE_COOKIE, 117 NGM_PPPOE_OFFER, 118 "pppoe_offer", 119 &ngpppoe_init_data_state_type, 120 NULL 121 }, 122 { 123 NGM_PPPOE_COOKIE, 124 NGM_PPPOE_SERVICE, 125 "pppoe_service", 126 &ngpppoe_init_data_state_type, 127 NULL 128 }, 129 { 130 NGM_PPPOE_COOKIE, 131 NGM_PPPOE_SUCCESS, 132 "pppoe_success", 133 &ng_pppoe_sts_state_type, 134 NULL 135 }, 136 { 137 NGM_PPPOE_COOKIE, 138 NGM_PPPOE_FAIL, 139 "pppoe_fail", 140 &ng_pppoe_sts_state_type, 141 NULL 142 }, 143 { 144 NGM_PPPOE_COOKIE, 145 NGM_PPPOE_CLOSE, 146 "pppoe_close", 147 &ng_pppoe_sts_state_type, 148 NULL 149 }, 150 { 151 NGM_PPPOE_COOKIE, 152 NGM_PPPOE_SETMODE, 153 "pppoe_setmode", 154 &ng_parse_string_type, 155 NULL 156 }, 157 { 158 NGM_PPPOE_COOKIE, 159 NGM_PPPOE_GETMODE, 160 "pppoe_getmode", 161 NULL, 162 &ng_parse_string_type 163 }, 164 { 165 NGM_PPPOE_COOKIE, 166 NGM_PPPOE_SETENADDR, 167 "setenaddr", 168 &ng_parse_enaddr_type, 169 NULL 170 }, 171 { 172 NGM_PPPOE_COOKIE, 173 NGM_PPPOE_SETMAXP, 174 "setmaxp", 175 &ng_parse_uint16_type, 176 NULL 177 }, 178 { 179 NGM_PPPOE_COOKIE, 180 NGM_PPPOE_SEND_HURL, 181 "send_hurl", 182 &ngpppoe_init_data_state_type, 183 NULL 184 }, 185 { 186 NGM_PPPOE_COOKIE, 187 NGM_PPPOE_SEND_MOTM, 188 "send_motm", 189 &ngpppoe_init_data_state_type, 190 NULL 191 }, 192 { 0 } 193 }; 194 195 /* Netgraph node type descriptor */ 196 static struct ng_type typestruct = { 197 .version = NG_ABI_VERSION, 198 .name = NG_PPPOE_NODE_TYPE, 199 .constructor = ng_pppoe_constructor, 200 .rcvmsg = ng_pppoe_rcvmsg, 201 .shutdown = ng_pppoe_shutdown, 202 .newhook = ng_pppoe_newhook, 203 .connect = ng_pppoe_connect, 204 .rcvdata = ng_pppoe_rcvdata, 205 .disconnect = ng_pppoe_disconnect, 206 .cmdlist = ng_pppoe_cmds, 207 }; 208 NETGRAPH_INIT(pppoe, &typestruct); 209 210 /* 211 * States for the session state machine. 212 * These have no meaning if there is no hook attached yet. 213 */ 214 enum state { 215 PPPOE_SNONE=0, /* [both] Initial state */ 216 PPPOE_LISTENING, /* [Daemon] Listening for discover initiation pkt */ 217 PPPOE_SINIT, /* [Client] Sent discovery initiation */ 218 PPPOE_PRIMED, /* [Server] Awaiting PADI from daemon */ 219 PPPOE_SOFFER, /* [Server] Sent offer message (got PADI)*/ 220 PPPOE_SREQ, /* [Client] Sent a Request */ 221 PPPOE_NEWCONNECTED, /* [Server] Connection established, No data received */ 222 PPPOE_CONNECTED, /* [Both] Connection established, Data received */ 223 PPPOE_DEAD /* [Both] */ 224 }; 225 226 #define NUMTAGS 20 /* number of tags we are set up to work with */ 227 228 /* 229 * Information we store for each hook on each node for negotiating the 230 * session. The mbuf and cluster are freed once negotiation has completed. 231 * The whole negotiation block is then discarded. 232 */ 233 234 struct sess_neg { 235 struct mbuf *m; /* holds cluster with last sent packet */ 236 union packet *pkt; /* points within the above cluster */ 237 struct callout handle; /* see timeout(9) */ 238 u_int timeout; /* 0,1,2,4,8,16 etc. seconds */ 239 u_int numtags; 240 const struct pppoe_tag *tags[NUMTAGS]; 241 u_int service_len; 242 u_int ac_name_len; 243 u_int host_uniq_len; 244 245 struct datatag service; 246 struct datatag ac_name; 247 struct datatag host_uniq; 248 }; 249 typedef struct sess_neg *negp; 250 251 /* 252 * Session information that is needed after connection. 253 */ 254 struct sess_con { 255 hook_p hook; 256 uint16_t Session_ID; 257 enum state state; 258 ng_ID_t creator; /* who to notify */ 259 struct pppoe_full_hdr pkt_hdr; /* used when connected */ 260 negp neg; /* used when negotiating */ 261 LIST_ENTRY(sess_con) sessions; 262 }; 263 typedef struct sess_con *sessp; 264 265 #define SESSHASHSIZE 0x0100 266 #define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) 267 268 struct sess_hash_entry { 269 struct mtx mtx; 270 LIST_HEAD(hhead, sess_con) head; 271 }; 272 273 /* 274 * Information we store for each node 275 */ 276 struct PPPoE { 277 node_p node; /* back pointer to node */ 278 hook_p ethernet_hook; 279 hook_p debug_hook; 280 u_int packets_in; /* packets in from ethernet */ 281 u_int packets_out; /* packets out towards ethernet */ 282 uint32_t flags; 283 #define COMPAT_3COM 0x00000001 284 #define COMPAT_DLINK 0x00000002 285 struct ether_header eh; 286 LIST_HEAD(, sess_con) listeners; 287 struct sess_hash_entry sesshash[SESSHASHSIZE]; 288 struct maxptag max_payload; /* PPP-Max-Payload (RFC4638) */ 289 }; 290 typedef struct PPPoE *priv_p; 291 292 union uniq { 293 char bytes[sizeof(void *)]; 294 void *pointer; 295 }; 296 297 #define LEAVE(x) do { error = x; goto quit; } while(0) 298 static void pppoe_start(sessp sp); 299 static void pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2); 300 static const struct pppoe_tag *scan_tags(sessp sp, 301 const struct pppoe_hdr* ph); 302 static int pppoe_send_event(sessp sp, enum cmd cmdid); 303 304 /************************************************************************* 305 * Some basic utilities from the Linux version with author's permission.* 306 * Author: Michal Ostrowski <mostrows@styx.uwaterloo.ca> * 307 ************************************************************************/ 308 309 310 311 /* 312 * Return the location where the next tag can be put 313 */ 314 static __inline const struct pppoe_tag* 315 next_tag(const struct pppoe_hdr* ph) 316 { 317 return (const struct pppoe_tag*)(((const char*)(ph + 1)) 318 + ntohs(ph->length)); 319 } 320 321 /* 322 * Look for a tag of a specific type. 323 * Don't trust any length the other end says, 324 * but assume we already sanity checked ph->length. 325 */ 326 static const struct pppoe_tag* 327 get_tag(const struct pppoe_hdr* ph, uint16_t idx) 328 { 329 const char *const end = (const char *)next_tag(ph); 330 const struct pppoe_tag *pt = (const void *)(ph + 1); 331 const char *ptn; 332 333 /* 334 * Keep processing tags while a tag header will still fit. 335 */ 336 while((const char*)(pt + 1) <= end) { 337 /* 338 * If the tag data would go past the end of the packet, abort. 339 */ 340 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 341 if (ptn > end) { 342 CTR2(KTR_NET, "%20s: invalid length for tag %d", 343 __func__, idx); 344 return (NULL); 345 } 346 if (pt->tag_type == idx) { 347 CTR2(KTR_NET, "%20s: found tag %d", __func__, idx); 348 return (pt); 349 } 350 351 pt = (const struct pppoe_tag*)ptn; 352 } 353 354 CTR2(KTR_NET, "%20s: not found tag %d", __func__, idx); 355 return (NULL); 356 } 357 358 /************************************************************************** 359 * Inlines to initialise or add tags to a session's tag list. 360 **************************************************************************/ 361 /* 362 * Initialise the session's tag list. 363 */ 364 static void 365 init_tags(sessp sp) 366 { 367 KASSERT(sp->neg != NULL, ("%s: no neg", __func__)); 368 sp->neg->numtags = 0; 369 } 370 371 static void 372 insert_tag(sessp sp, const struct pppoe_tag *tp) 373 { 374 negp neg = sp->neg; 375 int i; 376 377 KASSERT(neg != NULL, ("%s: no neg", __func__)); 378 if ((i = neg->numtags++) < NUMTAGS) { 379 neg->tags[i] = tp; 380 } else { 381 log(LOG_NOTICE, "ng_pppoe: asked to add too many tags to " 382 "packet\n"); 383 neg->numtags--; 384 } 385 } 386 387 /* 388 * Make up a packet, using the tags filled out for the session. 389 * 390 * Assume that the actual pppoe header and ethernet header 391 * are filled out externally to this routine. 392 * Also assume that neg->wh points to the correct 393 * location at the front of the buffer space. 394 */ 395 static void 396 make_packet(sessp sp) { 397 struct pppoe_full_hdr *wh = &sp->neg->pkt->pkt_header; 398 const struct pppoe_tag **tag; 399 char *dp; 400 int count; 401 int tlen; 402 uint16_t length = 0; 403 404 KASSERT((sp->neg != NULL) && (sp->neg->m != NULL), 405 ("%s: called from wrong state", __func__)); 406 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 407 408 dp = (char *)(&wh->ph + 1); 409 for (count = 0, tag = sp->neg->tags; 410 ((count < sp->neg->numtags) && (count < NUMTAGS)); 411 tag++, count++) { 412 tlen = ntohs((*tag)->tag_len) + sizeof(**tag); 413 if ((length + tlen) > (ETHER_MAX_LEN - 4 - sizeof(*wh))) { 414 log(LOG_NOTICE, "ng_pppoe: tags too long\n"); 415 sp->neg->numtags = count; 416 break; /* XXX chop off what's too long */ 417 } 418 bcopy(*tag, (char *)dp, tlen); 419 length += tlen; 420 dp += tlen; 421 } 422 wh->ph.length = htons(length); 423 sp->neg->m->m_len = length + sizeof(*wh); 424 sp->neg->m->m_pkthdr.len = length + sizeof(*wh); 425 } 426 427 /************************************************************************** 428 * Routines to match a service. * 429 **************************************************************************/ 430 431 /* 432 * Find a hook that has a service string that matches that 433 * we are seeking. For now use a simple string. 434 * In the future we may need something like regexp(). 435 * 436 * Null string is a wildcard (ANY service), according to RFC2516. 437 * And historical FreeBSD wildcard is also "*". 438 */ 439 440 static hook_p 441 pppoe_match_svc(node_p node, const struct pppoe_tag *tag) 442 { 443 const priv_p privp = NG_NODE_PRIVATE(node); 444 sessp sp; 445 446 LIST_FOREACH(sp, &privp->listeners, sessions) { 447 negp neg = sp->neg; 448 449 /* Empty Service-Name matches any service. */ 450 if (neg->service_len == 0) 451 break; 452 453 /* Special case for a blank or "*" service name (wildcard). */ 454 if (neg->service_len == 1 && neg->service.data[0] == '*') 455 break; 456 457 /* If the lengths don't match, that aint it. */ 458 if (neg->service_len != ntohs(tag->tag_len)) 459 continue; 460 461 if (strncmp((const char *)(tag + 1), neg->service.data, 462 ntohs(tag->tag_len)) == 0) 463 break; 464 } 465 CTR3(KTR_NET, "%20s: matched %p for %s", __func__, 466 sp?sp->hook:NULL, (const char *)(tag + 1)); 467 468 return (sp?sp->hook:NULL); 469 } 470 471 /* 472 * Broadcast the PADI packet in m0 to all listening hooks. 473 * This routine is called when a PADI with empty Service-Name 474 * tag is received. Client should receive PADOs with all 475 * available services. 476 */ 477 static int 478 pppoe_broadcast_padi(node_p node, struct mbuf *m0) 479 { 480 const priv_p privp = NG_NODE_PRIVATE(node); 481 sessp sp; 482 int error = 0; 483 484 LIST_FOREACH(sp, &privp->listeners, sessions) { 485 struct mbuf *m; 486 487 m = m_dup(m0, M_NOWAIT); 488 if (m == NULL) 489 return (ENOMEM); 490 NG_SEND_DATA_ONLY(error, sp->hook, m); 491 if (error) 492 return (error); 493 } 494 495 return (0); 496 } 497 498 /* 499 * Find a hook, which name equals to given service. 500 */ 501 static hook_p 502 pppoe_find_svc(node_p node, const char *svc_name, int svc_len) 503 { 504 const priv_p privp = NG_NODE_PRIVATE(node); 505 sessp sp; 506 507 LIST_FOREACH(sp, &privp->listeners, sessions) { 508 negp neg = sp->neg; 509 510 if (neg->service_len == svc_len && 511 strncmp(svc_name, neg->service.data, svc_len) == 0) 512 return (sp->hook); 513 } 514 515 return (NULL); 516 } 517 518 /************************************************************************** 519 * Routines to find a particular session that matches an incoming packet. * 520 **************************************************************************/ 521 /* Find free session and add to hash. */ 522 static uint16_t 523 pppoe_getnewsession(sessp sp) 524 { 525 const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 526 static uint16_t pppoe_sid = 1; 527 sessp tsp; 528 uint16_t val, hash; 529 530 restart: 531 /* Atomicity is not needed here as value will be checked. */ 532 val = pppoe_sid++; 533 /* Spec says 0xFFFF is reserved, also don't use 0x0000. */ 534 if (val == 0xffff || val == 0x0000) 535 val = pppoe_sid = 1; 536 537 /* Check it isn't already in use. */ 538 hash = SESSHASH(val); 539 mtx_lock(&privp->sesshash[hash].mtx); 540 LIST_FOREACH(tsp, &privp->sesshash[hash].head, sessions) { 541 if (tsp->Session_ID == val) 542 break; 543 } 544 if (!tsp) { 545 sp->Session_ID = val; 546 LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions); 547 } 548 mtx_unlock(&privp->sesshash[hash].mtx); 549 if (tsp) 550 goto restart; 551 552 CTR2(KTR_NET, "%20s: new sid %d", __func__, val); 553 554 return (val); 555 } 556 557 /* Add specified session to hash. */ 558 static void 559 pppoe_addsession(sessp sp) 560 { 561 const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 562 uint16_t hash = SESSHASH(sp->Session_ID); 563 564 mtx_lock(&privp->sesshash[hash].mtx); 565 LIST_INSERT_HEAD(&privp->sesshash[hash].head, sp, sessions); 566 mtx_unlock(&privp->sesshash[hash].mtx); 567 } 568 569 /* Delete specified session from hash. */ 570 static void 571 pppoe_delsession(sessp sp) 572 { 573 const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(sp->hook)); 574 uint16_t hash = SESSHASH(sp->Session_ID); 575 576 mtx_lock(&privp->sesshash[hash].mtx); 577 LIST_REMOVE(sp, sessions); 578 mtx_unlock(&privp->sesshash[hash].mtx); 579 } 580 581 /* Find matching peer/session combination. */ 582 static sessp 583 pppoe_findsession(priv_p privp, const struct pppoe_full_hdr *wh) 584 { 585 uint16_t session = ntohs(wh->ph.sid); 586 uint16_t hash = SESSHASH(session); 587 sessp sp = NULL; 588 589 mtx_lock(&privp->sesshash[hash].mtx); 590 LIST_FOREACH(sp, &privp->sesshash[hash].head, sessions) { 591 if (sp->Session_ID == session && 592 bcmp(sp->pkt_hdr.eh.ether_dhost, 593 wh->eh.ether_shost, ETHER_ADDR_LEN) == 0) { 594 break; 595 } 596 } 597 mtx_unlock(&privp->sesshash[hash].mtx); 598 CTR3(KTR_NET, "%20s: matched %p for %d", __func__, sp?sp->hook:NULL, 599 session); 600 601 return (sp); 602 } 603 604 static hook_p 605 pppoe_finduniq(node_p node, const struct pppoe_tag *tag) 606 { 607 hook_p hook = NULL; 608 sessp sp; 609 610 /* Cycle through all known hooks. */ 611 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 612 /* Skip any nonsession hook. */ 613 if (NG_HOOK_PRIVATE(hook) == NULL) 614 continue; 615 sp = NG_HOOK_PRIVATE(hook); 616 /* Skip already connected sessions. */ 617 if (sp->neg == NULL) 618 continue; 619 if (sp->neg->host_uniq_len == ntohs(tag->tag_len) && 620 bcmp(sp->neg->host_uniq.data, (const char *)(tag + 1), 621 sp->neg->host_uniq_len) == 0) 622 break; 623 } 624 CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, sp); 625 626 return (hook); 627 } 628 629 static hook_p 630 pppoe_findcookie(node_p node, const struct pppoe_tag *tag) 631 { 632 hook_p hook = NULL; 633 union uniq cookie; 634 635 bcopy(tag + 1, cookie.bytes, sizeof(void *)); 636 /* Cycle through all known hooks. */ 637 LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { 638 /* Skip any nonsession hook. */ 639 if (NG_HOOK_PRIVATE(hook) == NULL) 640 continue; 641 if (cookie.pointer == NG_HOOK_PRIVATE(hook)) 642 break; 643 } 644 CTR3(KTR_NET, "%20s: matched %p for %p", __func__, hook, cookie.pointer); 645 646 return (hook); 647 } 648 649 /************************************************************************** 650 * Start of Netgraph entrypoints. * 651 **************************************************************************/ 652 653 /* 654 * Allocate the private data structure and link it with node. 655 */ 656 static int 657 ng_pppoe_constructor(node_p node) 658 { 659 priv_p privp; 660 int i; 661 662 /* Initialize private descriptor. */ 663 privp = malloc(sizeof(*privp), M_NETGRAPH_PPPOE, M_WAITOK | M_ZERO); 664 665 /* Link structs together; this counts as our one reference to *node. */ 666 NG_NODE_SET_PRIVATE(node, privp); 667 privp->node = node; 668 669 /* Initialize to standard mode. */ 670 memset(&privp->eh.ether_dhost, 0xff, ETHER_ADDR_LEN); 671 privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 672 673 LIST_INIT(&privp->listeners); 674 for (i = 0; i < SESSHASHSIZE; i++) { 675 mtx_init(&privp->sesshash[i].mtx, "PPPoE hash mutex", NULL, MTX_DEF); 676 LIST_INIT(&privp->sesshash[i].head); 677 } 678 679 CTR3(KTR_NET, "%20s: created node [%x] (%p)", 680 __func__, node->nd_ID, node); 681 682 return (0); 683 } 684 685 /* 686 * Give our ok for a hook to be added... 687 * point the hook's private info to the hook structure. 688 * 689 * The following hook names are special: 690 * "ethernet": the hook that should be connected to a NIC. 691 * "debug": copies of data sent out here (when I write the code). 692 * All other hook names need only be unique. (the framework checks this). 693 */ 694 static int 695 ng_pppoe_newhook(node_p node, hook_p hook, const char *name) 696 { 697 const priv_p privp = NG_NODE_PRIVATE(node); 698 sessp sp; 699 700 if (strcmp(name, NG_PPPOE_HOOK_ETHERNET) == 0) { 701 privp->ethernet_hook = hook; 702 NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_ether); 703 } else if (strcmp(name, NG_PPPOE_HOOK_DEBUG) == 0) { 704 privp->debug_hook = hook; 705 NG_HOOK_SET_RCVDATA(hook, ng_pppoe_rcvdata_debug); 706 } else { 707 /* 708 * Any other unique name is OK. 709 * The infrastructure has already checked that it's unique, 710 * so just allocate it and hook it in. 711 */ 712 sp = malloc(sizeof(*sp), M_NETGRAPH_PPPOE, M_NOWAIT | M_ZERO); 713 if (sp == NULL) 714 return (ENOMEM); 715 716 NG_HOOK_SET_PRIVATE(hook, sp); 717 sp->hook = hook; 718 } 719 CTR5(KTR_NET, "%20s: node [%x] (%p) connected hook %s (%p)", 720 __func__, node->nd_ID, node, name, hook); 721 722 return(0); 723 } 724 725 /* 726 * Hook has been added successfully. Request the MAC address of 727 * the underlying Ethernet node. 728 */ 729 static int 730 ng_pppoe_connect(hook_p hook) 731 { 732 const priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 733 struct ng_mesg *msg; 734 int error; 735 736 if (hook != privp->ethernet_hook) 737 return (0); 738 739 /* 740 * If this is Ethernet hook, then request MAC address 741 * from our downstream. 742 */ 743 NG_MKMESSAGE(msg, NGM_ETHER_COOKIE, NGM_ETHER_GET_ENADDR, 0, M_NOWAIT); 744 if (msg == NULL) 745 return (ENOBUFS); 746 747 /* 748 * Our hook and peer hook have HK_INVALID flag set, 749 * so we can't use NG_SEND_MSG_HOOK() macro here. 750 */ 751 NG_SEND_MSG_ID(error, privp->node, msg, 752 NG_NODE_ID(NG_PEER_NODE(privp->ethernet_hook)), 753 NG_NODE_ID(privp->node)); 754 755 return (error); 756 } 757 /* 758 * Get a netgraph control message. 759 * Check it is one we understand. If needed, send a response. 760 * We sometimes save the address for an async action later. 761 * Always free the message. 762 */ 763 static int 764 ng_pppoe_rcvmsg(node_p node, item_p item, hook_p lasthook) 765 { 766 priv_p privp = NG_NODE_PRIVATE(node); 767 struct ngpppoe_init_data *ourmsg = NULL; 768 struct ng_mesg *resp = NULL; 769 int error = 0; 770 hook_p hook = NULL; 771 sessp sp = NULL; 772 negp neg = NULL; 773 struct ng_mesg *msg; 774 775 NGI_GET_MSG(item, msg); 776 CTR5(KTR_NET, "%20s: node [%x] (%p) got message %d with cookie %d", 777 __func__, node->nd_ID, node, msg->header.cmd, 778 msg->header.typecookie); 779 780 /* Deal with message according to cookie and command. */ 781 switch (msg->header.typecookie) { 782 case NGM_PPPOE_COOKIE: 783 switch (msg->header.cmd) { 784 case NGM_PPPOE_CONNECT: 785 case NGM_PPPOE_LISTEN: 786 case NGM_PPPOE_OFFER: 787 case NGM_PPPOE_SERVICE: 788 case NGM_PPPOE_SEND_HURL: 789 case NGM_PPPOE_SEND_MOTM: 790 ourmsg = (struct ngpppoe_init_data *)msg->data; 791 if (msg->header.arglen < sizeof(*ourmsg)) { 792 log(LOG_ERR, "ng_pppoe[%x]: init data too " 793 "small\n", node->nd_ID); 794 LEAVE(EMSGSIZE); 795 } 796 if (msg->header.cmd == NGM_PPPOE_SEND_HURL || 797 msg->header.cmd == NGM_PPPOE_SEND_MOTM) { 798 if (msg->header.arglen - sizeof(*ourmsg) > 799 PPPOE_PADM_VALUE_SIZE) { 800 log(LOG_ERR, "ng_pppoe[%x]: message " 801 "too big\n", node->nd_ID); 802 LEAVE(EMSGSIZE); 803 } 804 } else { 805 if (msg->header.arglen - sizeof(*ourmsg) > 806 PPPOE_SERVICE_NAME_SIZE) { 807 log(LOG_ERR, "ng_pppoe[%x]: service name " 808 "too big\n", node->nd_ID); 809 LEAVE(EMSGSIZE); 810 } 811 } 812 if (msg->header.arglen - sizeof(*ourmsg) < 813 ourmsg->data_len) { 814 log(LOG_ERR, "ng_pppoe[%x]: init data has bad " 815 "length, %d should be %zd\n", node->nd_ID, 816 ourmsg->data_len, 817 msg->header.arglen - sizeof (*ourmsg)); 818 LEAVE(EMSGSIZE); 819 } 820 821 /* Make sure strcmp will terminate safely. */ 822 ourmsg->hook[sizeof(ourmsg->hook) - 1] = '\0'; 823 824 /* Find hook by name. */ 825 hook = ng_findhook(node, ourmsg->hook); 826 if (hook == NULL) 827 LEAVE(ENOENT); 828 829 sp = NG_HOOK_PRIVATE(hook); 830 if (sp == NULL) 831 LEAVE(EINVAL); 832 833 if (msg->header.cmd == NGM_PPPOE_LISTEN) { 834 /* 835 * Ensure we aren't already listening for this 836 * service. 837 */ 838 if (pppoe_find_svc(node, ourmsg->data, 839 ourmsg->data_len) != NULL) 840 LEAVE(EEXIST); 841 } 842 843 /* 844 * PPPOE_SERVICE advertisements are set up 845 * on sessions that are in PRIMED state. 846 */ 847 if (msg->header.cmd == NGM_PPPOE_SERVICE) 848 break; 849 850 /* 851 * PADM messages are set up on active sessions. 852 */ 853 if (msg->header.cmd == NGM_PPPOE_SEND_HURL || 854 msg->header.cmd == NGM_PPPOE_SEND_MOTM) { 855 if (sp->state != PPPOE_NEWCONNECTED && 856 sp->state != PPPOE_CONNECTED) { 857 log(LOG_NOTICE, "ng_pppoe[%x]: session is not " 858 "active\n", node->nd_ID); 859 LEAVE(EISCONN); 860 } 861 break; 862 } 863 864 if (sp->state != PPPOE_SNONE) { 865 log(LOG_NOTICE, "ng_pppoe[%x]: Session already " 866 "active\n", node->nd_ID); 867 LEAVE(EISCONN); 868 } 869 870 /* 871 * Set up prototype header. 872 */ 873 neg = malloc(sizeof(*neg), M_NETGRAPH_PPPOE, 874 M_NOWAIT | M_ZERO); 875 876 if (neg == NULL) 877 LEAVE(ENOMEM); 878 879 neg->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 880 if (neg->m == NULL) { 881 free(neg, M_NETGRAPH_PPPOE); 882 LEAVE(ENOBUFS); 883 } 884 neg->m->m_pkthdr.rcvif = NULL; 885 sp->neg = neg; 886 ng_callout_init(&neg->handle); 887 neg->m->m_len = sizeof(struct pppoe_full_hdr); 888 neg->pkt = mtod(neg->m, union packet*); 889 memcpy((void *)&neg->pkt->pkt_header.eh, 890 &privp->eh, sizeof(struct ether_header)); 891 neg->pkt->pkt_header.ph.ver = 0x1; 892 neg->pkt->pkt_header.ph.type = 0x1; 893 neg->pkt->pkt_header.ph.sid = 0x0000; 894 neg->timeout = 0; 895 896 sp->creator = NGI_RETADDR(item); 897 } 898 switch (msg->header.cmd) { 899 case NGM_PPPOE_GET_STATUS: 900 { 901 struct ngpppoestat *stats; 902 903 NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT); 904 if (!resp) 905 LEAVE(ENOMEM); 906 907 stats = (struct ngpppoestat *) resp->data; 908 stats->packets_in = privp->packets_in; 909 stats->packets_out = privp->packets_out; 910 break; 911 } 912 case NGM_PPPOE_CONNECT: 913 { 914 /* 915 * Check the hook exists and is Uninitialised. 916 * Send a PADI request, and start the timeout logic. 917 * Store the originator of this message so we can send 918 * a success or fail message to them later. 919 * Move the session to SINIT. 920 * Set up the session to the correct state and 921 * start it. 922 */ 923 int acnpos, acnlen = 0, acnsep = 0; 924 int hupos, hulen = 0, husep = 0; 925 int i, srvpos, srvlen; 926 acnpos = 0; 927 for (i = 0; i < ourmsg->data_len; i++) { 928 if (ourmsg->data[i] == '\\') { 929 acnlen = i; 930 acnsep = 1; 931 break; 932 } 933 } 934 hupos = acnlen + acnsep; 935 for (i = hupos; i < ourmsg->data_len; i++) { 936 if (ourmsg->data[i] == '|') { 937 hulen = i - hupos; 938 husep = 1; 939 break; 940 } 941 } 942 srvpos = hupos + hulen + husep; 943 srvlen = ourmsg->data_len - srvpos; 944 945 bcopy(ourmsg->data + acnpos, neg->ac_name.data, acnlen); 946 neg->ac_name_len = acnlen; 947 948 neg->host_uniq.hdr.tag_type = PTT_HOST_UNIQ; 949 if (hulen == 0) { 950 /* Not provided, generate one */ 951 neg->host_uniq.hdr.tag_len = htons(sizeof(sp)); 952 bcopy(&sp, neg->host_uniq.data, sizeof(sp)); 953 neg->host_uniq_len = sizeof(sp); 954 } else if (hulen > 2 && ourmsg->data[hupos] == '0' && 955 ourmsg->data[hupos + 1] == 'x' && hulen % 2 == 0) { 956 /* Hex encoded */ 957 static const char hexdig[16] = "0123456789abcdef"; 958 int j; 959 960 neg->host_uniq.hdr.tag_len = htons((uint16_t)(hulen / 2 - 1)); 961 for (i = 0; i < hulen - 2; i++) { 962 for (j = 0; 963 j < 16 && 964 ourmsg->data[hupos + 2 + i] != hexdig[j]; 965 j++); 966 if (j == 16) 967 LEAVE(EINVAL); 968 if (i % 2 == 0) 969 neg->host_uniq.data[i / 2] = j << 4; 970 else 971 neg->host_uniq.data[i / 2] |= j; 972 } 973 neg->host_uniq_len = hulen / 2 - 1; 974 } else { 975 /* Plain string */ 976 neg->host_uniq.hdr.tag_len = htons((uint16_t)hulen); 977 bcopy(ourmsg->data + hupos, neg->host_uniq.data, hulen); 978 neg->host_uniq_len = hulen; 979 } 980 981 neg->service.hdr.tag_type = PTT_SRV_NAME; 982 neg->service.hdr.tag_len = htons((uint16_t)srvlen); 983 bcopy(ourmsg->data + srvpos, neg->service.data, srvlen); 984 neg->service_len = srvlen; 985 pppoe_start(sp); 986 break; 987 } 988 case NGM_PPPOE_LISTEN: 989 /* 990 * Check the hook exists and is Uninitialised. 991 * Install the service matching string. 992 * Store the originator of this message so we can send 993 * a success or fail message to them later. 994 * Move the hook to 'LISTENING' 995 */ 996 neg->service.hdr.tag_type = PTT_SRV_NAME; 997 neg->service.hdr.tag_len = 998 htons((uint16_t)ourmsg->data_len); 999 1000 if (ourmsg->data_len) 1001 bcopy(ourmsg->data, neg->service.data, 1002 ourmsg->data_len); 1003 neg->service_len = ourmsg->data_len; 1004 neg->pkt->pkt_header.ph.code = PADT_CODE; 1005 /* 1006 * Wait for PADI packet coming from Ethernet. 1007 */ 1008 sp->state = PPPOE_LISTENING; 1009 LIST_INSERT_HEAD(&privp->listeners, sp, sessions); 1010 break; 1011 case NGM_PPPOE_OFFER: 1012 /* 1013 * Check the hook exists and is Uninitialised. 1014 * Store the originator of this message so we can send 1015 * a success of fail message to them later. 1016 * Store the AC-Name given and go to PRIMED. 1017 */ 1018 neg->ac_name.hdr.tag_type = PTT_AC_NAME; 1019 neg->ac_name.hdr.tag_len = 1020 htons((uint16_t)ourmsg->data_len); 1021 if (ourmsg->data_len) 1022 bcopy(ourmsg->data, neg->ac_name.data, 1023 ourmsg->data_len); 1024 neg->ac_name_len = ourmsg->data_len; 1025 neg->pkt->pkt_header.ph.code = PADO_CODE; 1026 /* 1027 * Wait for PADI packet coming from hook. 1028 */ 1029 sp->state = PPPOE_PRIMED; 1030 break; 1031 case NGM_PPPOE_SERVICE: 1032 /* 1033 * Check the session is primed. 1034 * for now just allow ONE service to be advertised. 1035 * If you do it twice you just overwrite. 1036 */ 1037 if (sp->state != PPPOE_PRIMED) { 1038 log(LOG_NOTICE, "ng_pppoe[%x]: session not " 1039 "primed\n", node->nd_ID); 1040 LEAVE(EISCONN); 1041 } 1042 neg = sp->neg; 1043 neg->service.hdr.tag_type = PTT_SRV_NAME; 1044 neg->service.hdr.tag_len = 1045 htons((uint16_t)ourmsg->data_len); 1046 1047 if (ourmsg->data_len) 1048 bcopy(ourmsg->data, neg->service.data, 1049 ourmsg->data_len); 1050 neg->service_len = ourmsg->data_len; 1051 break; 1052 case NGM_PPPOE_SETMODE: 1053 { 1054 char *s; 1055 size_t len; 1056 1057 if (msg->header.arglen == 0) 1058 LEAVE(EINVAL); 1059 1060 s = (char *)msg->data; 1061 len = msg->header.arglen - 1; 1062 1063 /* Search for matching mode string. */ 1064 if (len == strlen(NG_PPPOE_STANDARD) && 1065 (strncmp(NG_PPPOE_STANDARD, s, len) == 0)) { 1066 privp->flags = 0; 1067 privp->eh.ether_type = ETHERTYPE_PPPOE_DISC; 1068 break; 1069 } 1070 if (len == strlen(NG_PPPOE_3COM) && 1071 (strncmp(NG_PPPOE_3COM, s, len) == 0)) { 1072 privp->flags |= COMPAT_3COM; 1073 privp->eh.ether_type = 1074 ETHERTYPE_PPPOE_3COM_DISC; 1075 break; 1076 } 1077 if (len == strlen(NG_PPPOE_DLINK) && 1078 (strncmp(NG_PPPOE_DLINK, s, len) == 0)) { 1079 privp->flags |= COMPAT_DLINK; 1080 break; 1081 } 1082 error = EINVAL; 1083 break; 1084 } 1085 case NGM_PPPOE_GETMODE: 1086 { 1087 char *s; 1088 size_t len = 0; 1089 1090 if (privp->flags == 0) 1091 len += strlen(NG_PPPOE_STANDARD) + 1; 1092 if (privp->flags & COMPAT_3COM) 1093 len += strlen(NG_PPPOE_3COM) + 1; 1094 if (privp->flags & COMPAT_DLINK) 1095 len += strlen(NG_PPPOE_DLINK) + 1; 1096 1097 NG_MKRESPONSE(resp, msg, len, M_NOWAIT); 1098 if (resp == NULL) 1099 LEAVE(ENOMEM); 1100 1101 s = (char *)resp->data; 1102 if (privp->flags == 0) { 1103 len = strlen(NG_PPPOE_STANDARD); 1104 strlcpy(s, NG_PPPOE_STANDARD, len + 1); 1105 break; 1106 } 1107 if (privp->flags & COMPAT_3COM) { 1108 len = strlen(NG_PPPOE_3COM); 1109 strlcpy(s, NG_PPPOE_3COM, len + 1); 1110 s += len; 1111 } 1112 if (privp->flags & COMPAT_DLINK) { 1113 if (s != resp->data) 1114 *s++ = '|'; 1115 len = strlen(NG_PPPOE_DLINK); 1116 strlcpy(s, NG_PPPOE_DLINK, len + 1); 1117 } 1118 break; 1119 } 1120 case NGM_PPPOE_SETENADDR: 1121 if (msg->header.arglen != ETHER_ADDR_LEN) 1122 LEAVE(EINVAL); 1123 bcopy(msg->data, &privp->eh.ether_shost, 1124 ETHER_ADDR_LEN); 1125 break; 1126 case NGM_PPPOE_SETMAXP: 1127 if (msg->header.arglen != sizeof(uint16_t)) 1128 LEAVE(EINVAL); 1129 privp->max_payload.hdr.tag_type = PTT_MAX_PAYL; 1130 privp->max_payload.hdr.tag_len = htons(sizeof(uint16_t)); 1131 privp->max_payload.data = htons(*((uint16_t *)msg->data)); 1132 break; 1133 case NGM_PPPOE_SEND_HURL: 1134 { 1135 struct mbuf *m; 1136 1137 /* Generate a packet of that type. */ 1138 m = m_gethdr(M_NOWAIT, MT_DATA); 1139 if (m == NULL) 1140 log(LOG_NOTICE, "ng_pppoe[%x]: session out of " 1141 "mbufs\n", node->nd_ID); 1142 else { 1143 struct pppoe_full_hdr *wh; 1144 struct pppoe_tag *tag; 1145 int error = 0; 1146 1147 wh = mtod(m, struct pppoe_full_hdr *); 1148 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1149 1150 /* Revert the stored header to DISC/PADM mode. */ 1151 wh->ph.code = PADM_CODE; 1152 /* 1153 * Configure ethertype depending on what 1154 * was used during sessions stage. 1155 */ 1156 if (wh->eh.ether_type == 1157 ETHERTYPE_PPPOE_3COM_SESS) 1158 wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC; 1159 else 1160 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 1161 /* 1162 * Add PADM message and adjust sizes. 1163 */ 1164 tag = (void *)(&wh->ph + 1); 1165 tag->tag_type = PTT_HURL; 1166 tag->tag_len = htons(ourmsg->data_len); 1167 strncpy((char *)(tag + 1), ourmsg->data, ourmsg->data_len); 1168 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) + 1169 ourmsg->data_len; 1170 wh->ph.length = htons(sizeof(*tag) + ourmsg->data_len); 1171 NG_SEND_DATA_ONLY(error, 1172 privp->ethernet_hook, m); 1173 } 1174 break; 1175 } 1176 case NGM_PPPOE_SEND_MOTM: 1177 { 1178 struct mbuf *m; 1179 1180 /* Generate a packet of that type. */ 1181 m = m_gethdr(M_NOWAIT, MT_DATA); 1182 if (m == NULL) 1183 log(LOG_NOTICE, "ng_pppoe[%x]: session out of " 1184 "mbufs\n", node->nd_ID); 1185 else { 1186 struct pppoe_full_hdr *wh; 1187 struct pppoe_tag *tag; 1188 int error = 0; 1189 1190 wh = mtod(m, struct pppoe_full_hdr *); 1191 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1192 1193 /* Revert the stored header to DISC/PADM mode. */ 1194 wh->ph.code = PADM_CODE; 1195 /* 1196 * Configure ethertype depending on what 1197 * was used during sessions stage. 1198 */ 1199 if (wh->eh.ether_type == 1200 ETHERTYPE_PPPOE_3COM_SESS) 1201 wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC; 1202 else 1203 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 1204 /* 1205 * Add PADM message and adjust sizes. 1206 */ 1207 tag = (void *)(&wh->ph + 1); 1208 tag->tag_type = PTT_MOTM; 1209 tag->tag_len = htons(ourmsg->data_len); 1210 strncpy((char *)(tag + 1), ourmsg->data, ourmsg->data_len); 1211 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) + 1212 ourmsg->data_len; 1213 wh->ph.length = htons(sizeof(*tag) + ourmsg->data_len); 1214 NG_SEND_DATA_ONLY(error, 1215 privp->ethernet_hook, m); 1216 } 1217 break; 1218 } 1219 default: 1220 LEAVE(EINVAL); 1221 } 1222 break; 1223 case NGM_ETHER_COOKIE: 1224 if (!(msg->header.flags & NGF_RESP)) 1225 LEAVE(EINVAL); 1226 switch (msg->header.cmd) { 1227 case NGM_ETHER_GET_ENADDR: 1228 if (msg->header.arglen != ETHER_ADDR_LEN) 1229 LEAVE(EINVAL); 1230 bcopy(msg->data, &privp->eh.ether_shost, 1231 ETHER_ADDR_LEN); 1232 break; 1233 default: 1234 LEAVE(EINVAL); 1235 } 1236 break; 1237 default: 1238 LEAVE(EINVAL); 1239 } 1240 1241 /* Take care of synchronous response, if any. */ 1242 quit: 1243 CTR2(KTR_NET, "%20s: returning %d", __func__, error); 1244 NG_RESPOND_MSG(error, node, item, resp); 1245 /* Free the message and return. */ 1246 NG_FREE_MSG(msg); 1247 return(error); 1248 } 1249 1250 /* 1251 * Start a client into the first state. A separate function because 1252 * it can be needed if the negotiation times out. 1253 */ 1254 static void 1255 pppoe_start(sessp sp) 1256 { 1257 hook_p hook = sp->hook; 1258 node_p node = NG_HOOK_NODE(hook); 1259 priv_p privp = NG_NODE_PRIVATE(node); 1260 negp neg = sp->neg; 1261 struct mbuf *m0; 1262 int error; 1263 1264 /* 1265 * Kick the state machine into starting up. 1266 */ 1267 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1268 sp->state = PPPOE_SINIT; 1269 /* 1270 * Reset the packet header to broadcast. Since we are 1271 * in a client mode use configured ethertype. 1272 */ 1273 memcpy((void *)&neg->pkt->pkt_header.eh, &privp->eh, 1274 sizeof(struct ether_header)); 1275 neg->pkt->pkt_header.ph.code = PADI_CODE; 1276 init_tags(sp); 1277 insert_tag(sp, &neg->host_uniq.hdr); 1278 insert_tag(sp, &neg->service.hdr); 1279 if (privp->max_payload.data != 0) 1280 insert_tag(sp, &privp->max_payload.hdr); 1281 make_packet(sp); 1282 /* 1283 * Send packet and prepare to retransmit it after timeout. 1284 */ 1285 ng_callout(&neg->handle, node, hook, PPPOE_INITIAL_TIMEOUT * hz, 1286 pppoe_ticker, NULL, 0); 1287 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1288 m0 = m_copypacket(neg->m, M_NOWAIT); 1289 NG_SEND_DATA_ONLY(error, privp->ethernet_hook, m0); 1290 } 1291 1292 static int 1293 send_acname(sessp sp, const struct pppoe_tag *tag) 1294 { 1295 int error, tlen; 1296 struct ng_mesg *msg; 1297 struct ngpppoe_sts *sts; 1298 1299 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1300 1301 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_ACNAME, 1302 sizeof(struct ngpppoe_sts), M_NOWAIT); 1303 if (msg == NULL) 1304 return (ENOMEM); 1305 1306 sts = (struct ngpppoe_sts *)msg->data; 1307 tlen = min(NG_HOOKSIZ - 1, ntohs(tag->tag_len)); 1308 strncpy(sts->hook, (const char *)(tag + 1), tlen); 1309 sts->hook[tlen] = '\0'; 1310 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1311 1312 return (error); 1313 } 1314 1315 static int 1316 send_sessionid(sessp sp) 1317 { 1318 int error; 1319 struct ng_mesg *msg; 1320 1321 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1322 1323 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SESSIONID, 1324 sizeof(uint16_t), M_NOWAIT); 1325 if (msg == NULL) 1326 return (ENOMEM); 1327 1328 *(uint16_t *)msg->data = sp->Session_ID; 1329 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1330 1331 return (error); 1332 } 1333 1334 static int 1335 send_maxp(sessp sp, const struct pppoe_tag *tag) 1336 { 1337 int error; 1338 struct ng_mesg *msg; 1339 struct ngpppoe_maxp *maxp; 1340 1341 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1342 1343 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_SETMAXP, 1344 sizeof(struct ngpppoe_maxp), M_NOWAIT); 1345 if (msg == NULL) 1346 return (ENOMEM); 1347 1348 maxp = (struct ngpppoe_maxp *)msg->data; 1349 strncpy(maxp->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 1350 maxp->data = ntohs(((const struct maxptag *)tag)->data); 1351 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1352 1353 return (error); 1354 } 1355 1356 static int 1357 send_hurl(sessp sp, const struct pppoe_tag *tag) 1358 { 1359 int error, tlen; 1360 struct ng_mesg *msg; 1361 struct ngpppoe_padm *padm; 1362 1363 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1364 1365 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_HURL, 1366 sizeof(struct ngpppoe_padm), M_NOWAIT); 1367 if (msg == NULL) 1368 return (ENOMEM); 1369 1370 padm = (struct ngpppoe_padm *)msg->data; 1371 tlen = min(PPPOE_PADM_VALUE_SIZE - 1, ntohs(tag->tag_len)); 1372 strncpy(padm->msg, (const char *)(tag + 1), tlen); 1373 padm->msg[tlen] = '\0'; 1374 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1375 1376 return (error); 1377 } 1378 1379 static int 1380 send_motm(sessp sp, const struct pppoe_tag *tag) 1381 { 1382 int error, tlen; 1383 struct ng_mesg *msg; 1384 struct ngpppoe_padm *padm; 1385 1386 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 1387 1388 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, NGM_PPPOE_MOTM, 1389 sizeof(struct ngpppoe_padm), M_NOWAIT); 1390 if (msg == NULL) 1391 return (ENOMEM); 1392 1393 padm = (struct ngpppoe_padm *)msg->data; 1394 tlen = min(PPPOE_PADM_VALUE_SIZE - 1, ntohs(tag->tag_len)); 1395 strncpy(padm->msg, (const char *)(tag + 1), tlen); 1396 padm->msg[tlen] = '\0'; 1397 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 1398 1399 return (error); 1400 } 1401 1402 /* 1403 * Receive data from session hook and do something with it. 1404 */ 1405 static int 1406 ng_pppoe_rcvdata(hook_p hook, item_p item) 1407 { 1408 node_p node = NG_HOOK_NODE(hook); 1409 const priv_p privp = NG_NODE_PRIVATE(node); 1410 sessp sp = NG_HOOK_PRIVATE(hook); 1411 struct pppoe_full_hdr *wh; 1412 struct mbuf *m; 1413 int error; 1414 1415 CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 1416 __func__, node->nd_ID, node, item, hook->hk_name, hook); 1417 1418 NGI_GET_M(item, m); 1419 switch (sp->state) { 1420 case PPPOE_NEWCONNECTED: 1421 case PPPOE_CONNECTED: { 1422 /* 1423 * Remove PPP address and control fields, if any. 1424 * For example, ng_ppp(4) always sends LCP packets 1425 * with address and control fields as required by 1426 * generic PPP. PPPoE is an exception to the rule. 1427 */ 1428 if (m->m_pkthdr.len >= 2) { 1429 if (m->m_len < 2 && !(m = m_pullup(m, 2))) 1430 LEAVE(ENOBUFS); 1431 if (mtod(m, u_char *)[0] == 0xff && 1432 mtod(m, u_char *)[1] == 0x03) 1433 m_adj(m, 2); 1434 } 1435 /* 1436 * Bang in a pre-made header, and set the length up 1437 * to be correct. Then send it to the ethernet driver. 1438 */ 1439 M_PREPEND(m, sizeof(*wh), M_NOWAIT); 1440 if (m == NULL) 1441 LEAVE(ENOBUFS); 1442 1443 wh = mtod(m, struct pppoe_full_hdr *); 1444 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 1445 wh->ph.length = htons(m->m_pkthdr.len - sizeof(*wh)); 1446 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m); 1447 privp->packets_out++; 1448 break; 1449 } 1450 case PPPOE_PRIMED: { 1451 struct { 1452 struct pppoe_tag hdr; 1453 union uniq data; 1454 } __packed uniqtag; 1455 const struct pppoe_tag *tag; 1456 struct mbuf *m0; 1457 const struct pppoe_hdr *ph; 1458 negp neg = sp->neg; 1459 uint16_t session; 1460 uint16_t length; 1461 uint8_t code; 1462 1463 /* 1464 * A PADI packet is being returned by the application 1465 * that has set up this hook. This indicates that it 1466 * wants us to offer service. 1467 */ 1468 if (m->m_len < sizeof(*wh)) { 1469 m = m_pullup(m, sizeof(*wh)); 1470 if (m == NULL) 1471 LEAVE(ENOBUFS); 1472 } 1473 wh = mtod(m, struct pppoe_full_hdr *); 1474 ph = &wh->ph; 1475 session = ntohs(wh->ph.sid); 1476 length = ntohs(wh->ph.length); 1477 code = wh->ph.code; 1478 /* Use peers mode in session. */ 1479 neg->pkt->pkt_header.eh.ether_type = wh->eh.ether_type; 1480 if (code != PADI_CODE) 1481 LEAVE(EINVAL); 1482 ng_uncallout(&neg->handle, node); 1483 1484 /* 1485 * This is the first time we hear 1486 * from the client, so note it's 1487 * unicast address, replacing the 1488 * broadcast address. 1489 */ 1490 bcopy(wh->eh.ether_shost, 1491 neg->pkt->pkt_header.eh.ether_dhost, 1492 ETHER_ADDR_LEN); 1493 sp->state = PPPOE_SOFFER; 1494 neg->timeout = 0; 1495 neg->pkt->pkt_header.ph.code = PADO_CODE; 1496 1497 /* 1498 * Start working out the tags to respond with. 1499 */ 1500 uniqtag.hdr.tag_type = PTT_AC_COOKIE; 1501 uniqtag.hdr.tag_len = htons((u_int16_t)sizeof(sp)); 1502 uniqtag.data.pointer = sp; 1503 init_tags(sp); 1504 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1505 if ((tag = get_tag(ph, PTT_SRV_NAME))) 1506 insert_tag(sp, tag); /* return service */ 1507 /* 1508 * If we have a NULL service request 1509 * and have an extra service defined in this hook, 1510 * then also add a tag for the extra service. 1511 * XXX this is a hack. eventually we should be able 1512 * to support advertising many services, not just one 1513 */ 1514 if (((tag == NULL) || (tag->tag_len == 0)) && 1515 (neg->service.hdr.tag_len != 0)) { 1516 insert_tag(sp, &neg->service.hdr); /* SERVICE */ 1517 } 1518 if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 1519 insert_tag(sp, tag); /* returned hostunique */ 1520 insert_tag(sp, &uniqtag.hdr); 1521 scan_tags(sp, ph); 1522 make_packet(sp); 1523 /* 1524 * Send the offer but if they don't respond 1525 * in PPPOE_OFFER_TIMEOUT seconds, forget about it. 1526 */ 1527 ng_callout(&neg->handle, node, hook, PPPOE_OFFER_TIMEOUT * hz, 1528 pppoe_ticker, NULL, 0); 1529 m0 = m_copypacket(sp->neg->m, M_NOWAIT); 1530 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 1531 privp->packets_out++; 1532 break; 1533 } 1534 1535 /* 1536 * Packets coming from the hook make no sense 1537 * to sessions in the rest of states. Throw them away. 1538 */ 1539 default: 1540 LEAVE(ENETUNREACH); 1541 } 1542 quit: 1543 if (item) 1544 NG_FREE_ITEM(item); 1545 NG_FREE_M(m); 1546 return (error); 1547 } 1548 1549 /* 1550 * Receive data from ether and do something with it. 1551 */ 1552 static int 1553 ng_pppoe_rcvdata_ether(hook_p hook, item_p item) 1554 { 1555 node_p node = NG_HOOK_NODE(hook); 1556 const priv_p privp = NG_NODE_PRIVATE(node); 1557 sessp sp; 1558 const struct pppoe_tag *utag = NULL, *tag = NULL; 1559 const struct pppoe_tag sntag = { PTT_SRV_NAME, 0 }; 1560 const struct pppoe_full_hdr *wh; 1561 const struct pppoe_hdr *ph; 1562 negp neg = NULL; 1563 struct mbuf *m; 1564 hook_p sendhook; 1565 int error = 0; 1566 uint16_t session; 1567 uint16_t length; 1568 uint8_t code; 1569 struct mbuf *m0; 1570 1571 CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 1572 __func__, node->nd_ID, node, item, hook->hk_name, hook); 1573 1574 NGI_GET_M(item, m); 1575 /* 1576 * Dig out various fields from the packet. 1577 * Use them to decide where to send it. 1578 */ 1579 privp->packets_in++; 1580 if( m->m_len < sizeof(*wh)) { 1581 m = m_pullup(m, sizeof(*wh)); /* Checks length */ 1582 if (m == NULL) { 1583 log(LOG_NOTICE, "ng_pppoe[%x]: couldn't " 1584 "m_pullup(wh)\n", node->nd_ID); 1585 LEAVE(ENOBUFS); 1586 } 1587 } 1588 wh = mtod(m, struct pppoe_full_hdr *); 1589 length = ntohs(wh->ph.length); 1590 switch(wh->eh.ether_type) { 1591 case ETHERTYPE_PPPOE_3COM_DISC: /* fall through */ 1592 case ETHERTYPE_PPPOE_DISC: 1593 /* 1594 * We need to try to make sure that the tag area 1595 * is contiguous, or we could wander off the end 1596 * of a buffer and make a mess. 1597 * (Linux wouldn't have this problem). 1598 */ 1599 if (m->m_pkthdr.len <= MHLEN) { 1600 if( m->m_len < m->m_pkthdr.len) { 1601 m = m_pullup(m, m->m_pkthdr.len); 1602 if (m == NULL) { 1603 log(LOG_NOTICE, "ng_pppoe[%x]: " 1604 "couldn't m_pullup(pkthdr)\n", 1605 node->nd_ID); 1606 LEAVE(ENOBUFS); 1607 } 1608 } 1609 } 1610 if (m->m_len != m->m_pkthdr.len) { 1611 /* 1612 * It's not all in one piece. 1613 * We need to do extra work. 1614 * Put it into a cluster. 1615 */ 1616 struct mbuf *n; 1617 n = m_dup(m, M_NOWAIT); 1618 m_freem(m); 1619 m = n; 1620 if (m) { 1621 /* just check we got a cluster */ 1622 if (m->m_len != m->m_pkthdr.len) { 1623 m_freem(m); 1624 m = NULL; 1625 } 1626 } 1627 if (m == NULL) { 1628 log(LOG_NOTICE, "ng_pppoe[%x]: packet " 1629 "fragmented\n", node->nd_ID); 1630 LEAVE(EMSGSIZE); 1631 } 1632 } 1633 wh = mtod(m, struct pppoe_full_hdr *); 1634 length = ntohs(wh->ph.length); 1635 ph = &wh->ph; 1636 session = ntohs(wh->ph.sid); 1637 code = wh->ph.code; 1638 1639 switch(code) { 1640 case PADI_CODE: 1641 /* 1642 * We are a server: 1643 * Look for a hook with the required service and send 1644 * the ENTIRE packet up there. It should come back to 1645 * a new hook in PRIMED state. Look there for further 1646 * processing. 1647 */ 1648 tag = get_tag(ph, PTT_SRV_NAME); 1649 if (tag == NULL) 1650 tag = &sntag; 1651 1652 /* 1653 * First, try to match Service-Name against our 1654 * listening hooks. If no success and we are in D-Link 1655 * compat mode and Service-Name is empty, then we 1656 * broadcast the PADI to all listening hooks. 1657 */ 1658 sendhook = pppoe_match_svc(node, tag); 1659 if (sendhook != NULL) 1660 NG_FWD_NEW_DATA(error, item, sendhook, m); 1661 else if (privp->flags & COMPAT_DLINK && 1662 ntohs(tag->tag_len) == 0) 1663 error = pppoe_broadcast_padi(node, m); 1664 else 1665 error = ENETUNREACH; 1666 break; 1667 case PADO_CODE: 1668 /* 1669 * We are a client: 1670 * Use the host_uniq tag to find the hook this is in 1671 * response to. Received #2, now send #3 1672 * For now simply accept the first we receive. 1673 */ 1674 utag = get_tag(ph, PTT_HOST_UNIQ); 1675 if (utag == NULL) { 1676 log(LOG_NOTICE, "ng_pppoe[%x]: no host " 1677 "unique field\n", node->nd_ID); 1678 LEAVE(ENETUNREACH); 1679 } 1680 1681 sendhook = pppoe_finduniq(node, utag); 1682 if (sendhook == NULL) { 1683 log(LOG_NOTICE, "ng_pppoe[%x]: no " 1684 "matching session\n", node->nd_ID); 1685 LEAVE(ENETUNREACH); 1686 } 1687 1688 /* 1689 * Check the session is in the right state. 1690 * It needs to be in PPPOE_SINIT. 1691 */ 1692 sp = NG_HOOK_PRIVATE(sendhook); 1693 if (sp->state == PPPOE_SREQ || 1694 sp->state == PPPOE_CONNECTED) { 1695 break; /* Multiple PADO is OK. */ 1696 } 1697 if (sp->state != PPPOE_SINIT) { 1698 log(LOG_NOTICE, "ng_pppoe[%x]: session " 1699 "in wrong state\n", node->nd_ID); 1700 LEAVE(ENETUNREACH); 1701 } 1702 neg = sp->neg; 1703 /* If requested specific AC-name, check it. */ 1704 if (neg->ac_name_len) { 1705 tag = get_tag(ph, PTT_AC_NAME); 1706 if (!tag) { 1707 /* No PTT_AC_NAME in PADO */ 1708 break; 1709 } 1710 if (neg->ac_name_len != htons(tag->tag_len) || 1711 strncmp(neg->ac_name.data, 1712 (const char *)(tag + 1), 1713 neg->ac_name_len) != 0) { 1714 break; 1715 } 1716 } 1717 sp->state = PPPOE_SREQ; 1718 ng_uncallout(&neg->handle, node); 1719 1720 /* 1721 * This is the first time we hear 1722 * from the server, so note it's 1723 * unicast address, replacing the 1724 * broadcast address . 1725 */ 1726 bcopy(wh->eh.ether_shost, 1727 neg->pkt->pkt_header.eh.ether_dhost, 1728 ETHER_ADDR_LEN); 1729 neg->timeout = 0; 1730 neg->pkt->pkt_header.ph.code = PADR_CODE; 1731 init_tags(sp); 1732 insert_tag(sp, utag); /* Host Unique */ 1733 if ((tag = get_tag(ph, PTT_AC_COOKIE))) 1734 insert_tag(sp, tag); /* return cookie */ 1735 if ((tag = get_tag(ph, PTT_AC_NAME))) { 1736 insert_tag(sp, tag); /* return it */ 1737 send_acname(sp, tag); 1738 } 1739 if ((tag = get_tag(ph, PTT_MAX_PAYL)) && 1740 (privp->max_payload.data != 0)) 1741 insert_tag(sp, tag); /* return it */ 1742 insert_tag(sp, &neg->service.hdr); /* Service */ 1743 scan_tags(sp, ph); 1744 make_packet(sp); 1745 sp->state = PPPOE_SREQ; 1746 ng_callout(&neg->handle, node, sp->hook, 1747 PPPOE_INITIAL_TIMEOUT * hz, 1748 pppoe_ticker, NULL, 0); 1749 neg->timeout = PPPOE_INITIAL_TIMEOUT * 2; 1750 m0 = m_copypacket(neg->m, M_NOWAIT); 1751 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 1752 break; 1753 case PADR_CODE: 1754 /* 1755 * We are a server: 1756 * Use the ac_cookie tag to find the 1757 * hook this is in response to. 1758 */ 1759 utag = get_tag(ph, PTT_AC_COOKIE); 1760 if ((utag == NULL) || 1761 (ntohs(utag->tag_len) != sizeof(sp))) { 1762 LEAVE(ENETUNREACH); 1763 } 1764 1765 sendhook = pppoe_findcookie(node, utag); 1766 if (sendhook == NULL) 1767 LEAVE(ENETUNREACH); 1768 1769 /* 1770 * Check the session is in the right state. 1771 * It needs to be in PPPOE_SOFFER or PPPOE_NEWCONNECTED. 1772 * If the latter, then this is a retry by the client, 1773 * so be nice, and resend. 1774 */ 1775 sp = NG_HOOK_PRIVATE(sendhook); 1776 if (sp->state == PPPOE_NEWCONNECTED) { 1777 /* 1778 * Whoa! drop back to resend that PADS packet. 1779 * We should still have a copy of it. 1780 */ 1781 sp->state = PPPOE_SOFFER; 1782 } else if (sp->state != PPPOE_SOFFER) 1783 LEAVE (ENETUNREACH); 1784 neg = sp->neg; 1785 ng_uncallout(&neg->handle, node); 1786 neg->pkt->pkt_header.ph.code = PADS_CODE; 1787 if (sp->Session_ID == 0) { 1788 neg->pkt->pkt_header.ph.sid = 1789 htons(pppoe_getnewsession(sp)); 1790 } 1791 send_sessionid(sp); 1792 neg->timeout = 0; 1793 /* 1794 * start working out the tags to respond with. 1795 */ 1796 init_tags(sp); 1797 insert_tag(sp, &neg->ac_name.hdr); /* AC_NAME */ 1798 if ((tag = get_tag(ph, PTT_SRV_NAME))) 1799 insert_tag(sp, tag);/* return service */ 1800 if ((tag = get_tag(ph, PTT_HOST_UNIQ))) 1801 insert_tag(sp, tag); /* return it */ 1802 insert_tag(sp, utag); /* ac_cookie */ 1803 scan_tags(sp, ph); 1804 make_packet(sp); 1805 sp->state = PPPOE_NEWCONNECTED; 1806 1807 /* Send the PADS without a timeout - we're now connected. */ 1808 m0 = m_copypacket(sp->neg->m, M_NOWAIT); 1809 NG_FWD_NEW_DATA(error, item, privp->ethernet_hook, m0); 1810 1811 /* 1812 * Having sent the last Negotiation header, 1813 * Set up the stored packet header to be correct for 1814 * the actual session. But keep the negotialtion stuff 1815 * around in case we need to resend this last packet. 1816 * We'll discard it when we move from NEWCONNECTED 1817 * to CONNECTED 1818 */ 1819 sp->pkt_hdr = neg->pkt->pkt_header; 1820 /* Configure ethertype depending on what 1821 * ethertype was used at discovery phase */ 1822 if (sp->pkt_hdr.eh.ether_type == 1823 ETHERTYPE_PPPOE_3COM_DISC) 1824 sp->pkt_hdr.eh.ether_type 1825 = ETHERTYPE_PPPOE_3COM_SESS; 1826 else 1827 sp->pkt_hdr.eh.ether_type 1828 = ETHERTYPE_PPPOE_SESS; 1829 sp->pkt_hdr.ph.code = 0; 1830 pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 1831 break; 1832 case PADS_CODE: 1833 /* 1834 * We are a client: 1835 * Use the host_uniq tag to find the hook this is in 1836 * response to. Take the session ID and store it away. 1837 * Also make sure the pre-made header is correct and 1838 * set us into Session mode. 1839 */ 1840 utag = get_tag(ph, PTT_HOST_UNIQ); 1841 if (utag == NULL) { 1842 LEAVE (ENETUNREACH); 1843 } 1844 sendhook = pppoe_finduniq(node, utag); 1845 if (sendhook == NULL) 1846 LEAVE(ENETUNREACH); 1847 1848 /* 1849 * Check the session is in the right state. 1850 * It needs to be in PPPOE_SREQ. 1851 */ 1852 sp = NG_HOOK_PRIVATE(sendhook); 1853 if (sp->state != PPPOE_SREQ) 1854 LEAVE(ENETUNREACH); 1855 neg = sp->neg; 1856 ng_uncallout(&neg->handle, node); 1857 neg->pkt->pkt_header.ph.sid = wh->ph.sid; 1858 sp->Session_ID = ntohs(wh->ph.sid); 1859 pppoe_addsession(sp); 1860 send_sessionid(sp); 1861 neg->timeout = 0; 1862 sp->state = PPPOE_CONNECTED; 1863 /* 1864 * Now we have gone to Connected mode, 1865 * Free all resources needed for negotiation. 1866 * Keep a copy of the header we will be using. 1867 */ 1868 sp->pkt_hdr = neg->pkt->pkt_header; 1869 if (privp->flags & COMPAT_3COM) 1870 sp->pkt_hdr.eh.ether_type 1871 = ETHERTYPE_PPPOE_3COM_SESS; 1872 else 1873 sp->pkt_hdr.eh.ether_type 1874 = ETHERTYPE_PPPOE_SESS; 1875 sp->pkt_hdr.ph.code = 0; 1876 m_freem(neg->m); 1877 free(sp->neg, M_NETGRAPH_PPPOE); 1878 sp->neg = NULL; 1879 if ((tag = get_tag(ph, PTT_MAX_PAYL)) && 1880 (privp->max_payload.data != 0)) 1881 send_maxp(sp, tag); 1882 pppoe_send_event(sp, NGM_PPPOE_SUCCESS); 1883 break; 1884 case PADT_CODE: 1885 /* 1886 * Find matching peer/session combination. 1887 */ 1888 sp = pppoe_findsession(privp, wh); 1889 if (sp == NULL) 1890 LEAVE(ENETUNREACH); 1891 /* Disconnect that hook. */ 1892 ng_rmhook_self(sp->hook); 1893 break; 1894 case PADM_CODE: 1895 /* 1896 * We are a client: 1897 * find matching peer/session combination. 1898 */ 1899 sp = pppoe_findsession(privp, wh); 1900 if (sp == NULL) 1901 LEAVE (ENETUNREACH); 1902 if ((tag = get_tag(ph, PTT_HURL))) 1903 send_hurl(sp, tag); 1904 if ((tag = get_tag(ph, PTT_MOTM))) 1905 send_motm(sp, tag); 1906 break; 1907 default: 1908 LEAVE(EPFNOSUPPORT); 1909 } 1910 break; 1911 case ETHERTYPE_PPPOE_3COM_SESS: 1912 case ETHERTYPE_PPPOE_SESS: 1913 /* 1914 * Find matching peer/session combination. 1915 */ 1916 sp = pppoe_findsession(privp, wh); 1917 if (sp == NULL) 1918 LEAVE (ENETUNREACH); 1919 m_adj(m, sizeof(*wh)); 1920 1921 /* If packet too short, dump it. */ 1922 if (m->m_pkthdr.len < length) 1923 LEAVE(EMSGSIZE); 1924 /* Also need to trim excess at the end */ 1925 if (m->m_pkthdr.len > length) { 1926 m_adj(m, -((int)(m->m_pkthdr.len - length))); 1927 } 1928 if ( sp->state != PPPOE_CONNECTED) { 1929 if (sp->state == PPPOE_NEWCONNECTED) { 1930 sp->state = PPPOE_CONNECTED; 1931 /* 1932 * Now we have gone to Connected mode, 1933 * Free all resources needed for negotiation. 1934 * Be paranoid about whether there may be 1935 * a timeout. 1936 */ 1937 m_freem(sp->neg->m); 1938 ng_uncallout(&sp->neg->handle, node); 1939 free(sp->neg, M_NETGRAPH_PPPOE); 1940 sp->neg = NULL; 1941 } else { 1942 LEAVE (ENETUNREACH); 1943 } 1944 } 1945 NG_FWD_NEW_DATA(error, item, sp->hook, m); 1946 break; 1947 default: 1948 LEAVE(EPFNOSUPPORT); 1949 } 1950 quit: 1951 if (item) 1952 NG_FREE_ITEM(item); 1953 NG_FREE_M(m); 1954 return (error); 1955 } 1956 1957 /* 1958 * Receive data from debug hook and bypass it to ether. 1959 */ 1960 static int 1961 ng_pppoe_rcvdata_debug(hook_p hook, item_p item) 1962 { 1963 node_p node = NG_HOOK_NODE(hook); 1964 const priv_p privp = NG_NODE_PRIVATE(node); 1965 int error; 1966 1967 CTR6(KTR_NET, "%20s: node [%x] (%p) received %p on \"%s\" (%p)", 1968 __func__, node->nd_ID, node, item, hook->hk_name, hook); 1969 1970 NG_FWD_ITEM_HOOK(error, item, privp->ethernet_hook); 1971 privp->packets_out++; 1972 return (error); 1973 } 1974 1975 /* 1976 * Do local shutdown processing.. 1977 * If we are a persistent device, we might refuse to go away, and 1978 * we'd only remove our links and reset ourself. 1979 */ 1980 static int 1981 ng_pppoe_shutdown(node_p node) 1982 { 1983 const priv_p privp = NG_NODE_PRIVATE(node); 1984 int i; 1985 1986 for (i = 0; i < SESSHASHSIZE; i++) 1987 mtx_destroy(&privp->sesshash[i].mtx); 1988 NG_NODE_SET_PRIVATE(node, NULL); 1989 NG_NODE_UNREF(privp->node); 1990 free(privp, M_NETGRAPH_PPPOE); 1991 return (0); 1992 } 1993 1994 /* 1995 * Hook disconnection 1996 * 1997 * Clean up all dangling links and information about the session/hook. 1998 * For this type, removal of the last link destroys the node. 1999 */ 2000 static int 2001 ng_pppoe_disconnect(hook_p hook) 2002 { 2003 node_p node = NG_HOOK_NODE(hook); 2004 priv_p privp = NG_NODE_PRIVATE(node); 2005 sessp sp; 2006 2007 if (hook == privp->debug_hook) { 2008 privp->debug_hook = NULL; 2009 } else if (hook == privp->ethernet_hook) { 2010 privp->ethernet_hook = NULL; 2011 if (NG_NODE_IS_VALID(node)) 2012 ng_rmnode_self(node); 2013 } else { 2014 sp = NG_HOOK_PRIVATE(hook); 2015 if (sp->state != PPPOE_SNONE ) { 2016 pppoe_send_event(sp, NGM_PPPOE_CLOSE); 2017 } 2018 /* 2019 * According to the spec, if we are connected, 2020 * we should send a DISC packet if we are shutting down 2021 * a session. 2022 */ 2023 if ((privp->ethernet_hook) 2024 && ((sp->state == PPPOE_CONNECTED) 2025 || (sp->state == PPPOE_NEWCONNECTED))) { 2026 struct mbuf *m; 2027 2028 /* Generate a packet of that type. */ 2029 m = m_gethdr(M_NOWAIT, MT_DATA); 2030 if (m == NULL) 2031 log(LOG_NOTICE, "ng_pppoe[%x]: session out of " 2032 "mbufs\n", node->nd_ID); 2033 else { 2034 struct pppoe_full_hdr *wh; 2035 struct pppoe_tag *tag; 2036 int msglen = strlen(SIGNOFF); 2037 int error = 0; 2038 2039 wh = mtod(m, struct pppoe_full_hdr *); 2040 bcopy(&sp->pkt_hdr, wh, sizeof(*wh)); 2041 2042 /* Revert the stored header to DISC/PADT mode. */ 2043 wh->ph.code = PADT_CODE; 2044 /* 2045 * Configure ethertype depending on what 2046 * was used during sessions stage. 2047 */ 2048 if (wh->eh.ether_type == 2049 ETHERTYPE_PPPOE_3COM_SESS) 2050 wh->eh.ether_type = ETHERTYPE_PPPOE_3COM_DISC; 2051 else 2052 wh->eh.ether_type = ETHERTYPE_PPPOE_DISC; 2053 /* 2054 * Add a General error message and adjust 2055 * sizes. 2056 */ 2057 tag = (void *)(&wh->ph + 1); 2058 tag->tag_type = PTT_GEN_ERR; 2059 tag->tag_len = htons((u_int16_t)msglen); 2060 strncpy((char *)(tag + 1), SIGNOFF, msglen); 2061 m->m_pkthdr.len = m->m_len = sizeof(*wh) + sizeof(*tag) + 2062 msglen; 2063 wh->ph.length = htons(sizeof(*tag) + msglen); 2064 NG_SEND_DATA_ONLY(error, 2065 privp->ethernet_hook, m); 2066 } 2067 } 2068 if (sp->state == PPPOE_LISTENING) 2069 LIST_REMOVE(sp, sessions); 2070 else if (sp->Session_ID) 2071 pppoe_delsession(sp); 2072 /* 2073 * As long as we have somewhere to store the timeout handle, 2074 * we may have a timeout pending.. get rid of it. 2075 */ 2076 if (sp->neg) { 2077 ng_uncallout(&sp->neg->handle, node); 2078 if (sp->neg->m) 2079 m_freem(sp->neg->m); 2080 free(sp->neg, M_NETGRAPH_PPPOE); 2081 } 2082 free(sp, M_NETGRAPH_PPPOE); 2083 NG_HOOK_SET_PRIVATE(hook, NULL); 2084 } 2085 if ((NG_NODE_NUMHOOKS(node) == 0) && 2086 (NG_NODE_IS_VALID(node))) 2087 ng_rmnode_self(node); 2088 return (0); 2089 } 2090 2091 /* 2092 * Timeouts come here. 2093 */ 2094 static void 2095 pppoe_ticker(node_p node, hook_p hook, void *arg1, int arg2) 2096 { 2097 priv_p privp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 2098 sessp sp = NG_HOOK_PRIVATE(hook); 2099 negp neg = sp->neg; 2100 struct mbuf *m0 = NULL; 2101 int error = 0; 2102 2103 CTR6(KTR_NET, "%20s: node [%x] (%p) hook \"%s\" (%p) session %d", 2104 __func__, node->nd_ID, node, hook->hk_name, hook, sp->Session_ID); 2105 switch(sp->state) { 2106 /* 2107 * Resend the last packet, using an exponential backoff. 2108 * After a period of time, stop growing the backoff, 2109 * And either leave it, or revert to the start. 2110 */ 2111 case PPPOE_SINIT: 2112 case PPPOE_SREQ: 2113 /* Timeouts on these produce resends. */ 2114 m0 = m_copypacket(sp->neg->m, M_NOWAIT); 2115 NG_SEND_DATA_ONLY( error, privp->ethernet_hook, m0); 2116 ng_callout(&neg->handle, node, hook, neg->timeout * hz, 2117 pppoe_ticker, NULL, 0); 2118 if ((neg->timeout <<= 1) > PPPOE_TIMEOUT_LIMIT) { 2119 if (sp->state == PPPOE_SREQ) { 2120 /* Revert to SINIT mode. */ 2121 pppoe_start(sp); 2122 } else { 2123 neg->timeout = PPPOE_TIMEOUT_LIMIT; 2124 } 2125 } 2126 break; 2127 case PPPOE_PRIMED: 2128 case PPPOE_SOFFER: 2129 /* A timeout on these says "give up" */ 2130 ng_rmhook_self(hook); 2131 break; 2132 default: 2133 /* Timeouts have no meaning in other states. */ 2134 log(LOG_NOTICE, "ng_pppoe[%x]: unexpected timeout\n", 2135 node->nd_ID); 2136 } 2137 } 2138 2139 /* 2140 * Parse an incoming packet to see if any tags should be copied to the 2141 * output packet. Don't do any tags that have been handled in the main 2142 * state machine. 2143 */ 2144 static const struct pppoe_tag* 2145 scan_tags(sessp sp, const struct pppoe_hdr* ph) 2146 { 2147 const char *const end = (const char *)next_tag(ph); 2148 const char *ptn; 2149 const struct pppoe_tag *pt = (const void *)(ph + 1); 2150 2151 /* 2152 * Keep processing tags while a tag header will still fit. 2153 */ 2154 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 2155 2156 while((const char*)(pt + 1) <= end) { 2157 /* 2158 * If the tag data would go past the end of the packet, abort. 2159 */ 2160 ptn = (((const char *)(pt + 1)) + ntohs(pt->tag_len)); 2161 if(ptn > end) 2162 return NULL; 2163 2164 switch (pt->tag_type) { 2165 case PTT_RELAY_SID: 2166 insert_tag(sp, pt); 2167 break; 2168 case PTT_EOL: 2169 return NULL; 2170 case PTT_SRV_NAME: 2171 case PTT_AC_NAME: 2172 case PTT_HOST_UNIQ: 2173 case PTT_AC_COOKIE: 2174 case PTT_VENDOR: 2175 case PTT_SRV_ERR: 2176 case PTT_SYS_ERR: 2177 case PTT_GEN_ERR: 2178 case PTT_MAX_PAYL: 2179 case PTT_HURL: 2180 case PTT_MOTM: 2181 break; 2182 } 2183 pt = (const struct pppoe_tag*)ptn; 2184 } 2185 return NULL; 2186 } 2187 2188 static int 2189 pppoe_send_event(sessp sp, enum cmd cmdid) 2190 { 2191 int error; 2192 struct ng_mesg *msg; 2193 struct ngpppoe_sts *sts; 2194 2195 CTR2(KTR_NET, "%20s: called %d", __func__, sp->Session_ID); 2196 2197 NG_MKMESSAGE(msg, NGM_PPPOE_COOKIE, cmdid, 2198 sizeof(struct ngpppoe_sts), M_NOWAIT); 2199 if (msg == NULL) 2200 return (ENOMEM); 2201 sts = (struct ngpppoe_sts *)msg->data; 2202 strncpy(sts->hook, NG_HOOK_NAME(sp->hook), NG_HOOKSIZ); 2203 NG_SEND_MSG_ID(error, NG_HOOK_NODE(sp->hook), msg, sp->creator, 0); 2204 return (error); 2205 } 2206