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