1 /*********************************************************************** 2 * 3 * discovery.c 4 * 5 * Perform PPPoE discovery 6 * 7 * Copyright (C) 1999 by Roaring Penguin Software Inc. 8 * 9 ***********************************************************************/ 10 11 static char const RCSID[] = 12 "Id: discovery.c,v 1.6 2008/06/15 04:35:50 paulus Exp "; 13 14 #define _GNU_SOURCE 1 15 #include "pppoe.h" 16 #include "pppd/pppd.h" 17 #include "pppd/fsm.h" 18 #include "pppd/lcp.h" 19 20 #include <string.h> 21 #include <stdlib.h> 22 #include <errno.h> 23 24 #ifdef HAVE_SYS_TIME_H 25 #include <sys/time.h> 26 #endif 27 28 #ifdef HAVE_SYS_UIO_H 29 #include <sys/uio.h> 30 #endif 31 32 #ifdef HAVE_UNISTD_H 33 #include <unistd.h> 34 #endif 35 36 #ifdef USE_LINUX_PACKET 37 #include <sys/ioctl.h> 38 #include <fcntl.h> 39 #endif 40 41 #include <signal.h> 42 43 /* Calculate time remaining until *exp, return 0 if now >= *exp */ 44 static int time_left(struct timeval *diff, struct timeval *exp) 45 { 46 struct timeval now; 47 48 if (get_time(&now) < 0) { 49 error("get_time: %m"); 50 return 0; 51 } 52 53 if (now.tv_sec > exp->tv_sec 54 || (now.tv_sec == exp->tv_sec && now.tv_usec >= exp->tv_usec)) 55 return 0; 56 57 diff->tv_sec = exp->tv_sec - now.tv_sec; 58 diff->tv_usec = exp->tv_usec - now.tv_usec; 59 if (diff->tv_usec < 0) { 60 diff->tv_usec += 1000000; 61 --diff->tv_sec; 62 } 63 64 return 1; 65 } 66 67 /********************************************************************** 68 *%FUNCTION: parseForHostUniq 69 *%ARGUMENTS: 70 * type -- tag type 71 * len -- tag length 72 * data -- tag data. 73 * extra -- user-supplied pointer. This is assumed to be a pointer to int. 74 *%RETURNS: 75 * Nothing 76 *%DESCRIPTION: 77 * If a HostUnique tag is found which matches our PID, sets *extra to 1. 78 ***********************************************************************/ 79 static void 80 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, 81 void *extra) 82 { 83 PPPoETag *tag = extra; 84 85 if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) 86 tag->length = memcmp(data, tag->payload, len); 87 } 88 89 /********************************************************************** 90 *%FUNCTION: packetIsForMe 91 *%ARGUMENTS: 92 * conn -- PPPoE connection info 93 * packet -- a received PPPoE packet 94 *%RETURNS: 95 * 1 if packet is for this PPPoE daemon; 0 otherwise. 96 *%DESCRIPTION: 97 * If we are using the Host-Unique tag, verifies that packet contains 98 * our unique identifier. 99 ***********************************************************************/ 100 static int 101 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) 102 { 103 PPPoETag hostUniq = conn->hostUniq; 104 105 /* If packet is not directed to our MAC address, forget it */ 106 if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; 107 108 /* If we're not using the Host-Unique tag, then accept the packet */ 109 if (!conn->hostUniq.length) return 1; 110 111 parsePacket(packet, parseForHostUniq, &hostUniq); 112 return !hostUniq.length; 113 } 114 115 /********************************************************************** 116 *%FUNCTION: parsePADOTags 117 *%ARGUMENTS: 118 * type -- tag type 119 * len -- tag length 120 * data -- tag data 121 * extra -- extra user data. Should point to a PacketCriteria structure 122 * which gets filled in according to selected AC name and service 123 * name. 124 *%RETURNS: 125 * Nothing 126 *%DESCRIPTION: 127 * Picks interesting tags out of a PADO packet 128 ***********************************************************************/ 129 static void 130 parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, 131 void *extra) 132 { 133 struct PacketCriteria *pc = (struct PacketCriteria *) extra; 134 PPPoEConnection *conn = pc->conn; 135 UINT16_t mru; 136 int i; 137 138 switch(type) { 139 case TAG_AC_NAME: 140 pc->seenACName = 1; 141 if (conn->printACNames) { 142 info("Access-Concentrator: %.*s", (int) len, data); 143 } 144 if (conn->acName && len == strlen(conn->acName) && 145 !strncmp((char *) data, conn->acName, len)) { 146 pc->acNameOK = 1; 147 } 148 break; 149 case TAG_SERVICE_NAME: 150 pc->seenServiceName = 1; 151 if (conn->serviceName && len == strlen(conn->serviceName) && 152 !strncmp((char *) data, conn->serviceName, len)) { 153 pc->serviceNameOK = 1; 154 } 155 break; 156 case TAG_AC_COOKIE: 157 conn->cookie.type = htons(type); 158 conn->cookie.length = htons(len); 159 memcpy(conn->cookie.payload, data, len); 160 break; 161 case TAG_RELAY_SESSION_ID: 162 conn->relayId.type = htons(type); 163 conn->relayId.length = htons(len); 164 memcpy(conn->relayId.payload, data, len); 165 break; 166 case TAG_PPP_MAX_PAYLOAD: 167 if (len == sizeof(mru)) { 168 memcpy(&mru, data, sizeof(mru)); 169 mru = ntohs(mru); 170 if (mru >= ETH_PPPOE_MTU) { 171 if (lcp_allowoptions[0].mru > mru) 172 lcp_allowoptions[0].mru = mru; 173 if (lcp_wantoptions[0].mru > mru) 174 lcp_wantoptions[0].mru = mru; 175 conn->seenMaxPayload = 1; 176 } 177 } 178 break; 179 case TAG_SERVICE_NAME_ERROR: 180 error("PADO: Service-Name-Error: %.*s", (int) len, data); 181 conn->error = 1; 182 break; 183 case TAG_AC_SYSTEM_ERROR: 184 error("PADO: System-Error: %.*s", (int) len, data); 185 conn->error = 1; 186 break; 187 case TAG_GENERIC_ERROR: 188 error("PADO: Generic-Error: %.*s", (int) len, data); 189 conn->error = 1; 190 break; 191 } 192 } 193 194 /********************************************************************** 195 *%FUNCTION: parsePADSTags 196 *%ARGUMENTS: 197 * type -- tag type 198 * len -- tag length 199 * data -- tag data 200 * extra -- extra user data (pointer to PPPoEConnection structure) 201 *%RETURNS: 202 * Nothing 203 *%DESCRIPTION: 204 * Picks interesting tags out of a PADS packet 205 ***********************************************************************/ 206 static void 207 parsePADSTags(UINT16_t type, UINT16_t len, unsigned char *data, 208 void *extra) 209 { 210 PPPoEConnection *conn = (PPPoEConnection *) extra; 211 UINT16_t mru; 212 switch(type) { 213 case TAG_SERVICE_NAME: 214 dbglog("PADS: Service-Name: '%.*s'", (int) len, data); 215 break; 216 case TAG_PPP_MAX_PAYLOAD: 217 if (len == sizeof(mru)) { 218 memcpy(&mru, data, sizeof(mru)); 219 mru = ntohs(mru); 220 if (mru >= ETH_PPPOE_MTU) { 221 if (lcp_allowoptions[0].mru > mru) 222 lcp_allowoptions[0].mru = mru; 223 if (lcp_wantoptions[0].mru > mru) 224 lcp_wantoptions[0].mru = mru; 225 conn->seenMaxPayload = 1; 226 } 227 } 228 break; 229 case TAG_SERVICE_NAME_ERROR: 230 error("PADS: Service-Name-Error: %.*s", (int) len, data); 231 conn->error = 1; 232 break; 233 case TAG_AC_SYSTEM_ERROR: 234 error("PADS: System-Error: %.*s", (int) len, data); 235 conn->error = 1; 236 break; 237 case TAG_GENERIC_ERROR: 238 error("PADS: Generic-Error: %.*s", (int) len, data); 239 conn->error = 1; 240 break; 241 case TAG_RELAY_SESSION_ID: 242 conn->relayId.type = htons(type); 243 conn->relayId.length = htons(len); 244 memcpy(conn->relayId.payload, data, len); 245 break; 246 } 247 } 248 249 /*********************************************************************** 250 *%FUNCTION: sendPADI 251 *%ARGUMENTS: 252 * conn -- PPPoEConnection structure 253 *%RETURNS: 254 * Nothing 255 *%DESCRIPTION: 256 * Sends a PADI packet 257 ***********************************************************************/ 258 static void 259 sendPADI(PPPoEConnection *conn) 260 { 261 PPPoEPacket packet; 262 unsigned char *cursor = packet.payload; 263 PPPoETag *svc = (PPPoETag *) (&packet.payload); 264 UINT16_t namelen = 0; 265 UINT16_t plen; 266 int omit_service_name = 0; 267 268 if (conn->serviceName) { 269 namelen = (UINT16_t) strlen(conn->serviceName); 270 if (!strcmp(conn->serviceName, "NO-SERVICE-NAME-NON-RFC-COMPLIANT")) { 271 omit_service_name = 1; 272 } 273 } 274 275 /* Set destination to Ethernet broadcast address */ 276 memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN); 277 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 278 279 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 280 packet.vertype = PPPOE_VER_TYPE(1, 1); 281 packet.code = CODE_PADI; 282 packet.session = 0; 283 284 if (!omit_service_name) { 285 plen = TAG_HDR_SIZE + namelen; 286 CHECK_ROOM(cursor, packet.payload, plen); 287 288 svc->type = TAG_SERVICE_NAME; 289 svc->length = htons(namelen); 290 291 if (conn->serviceName) { 292 memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); 293 } 294 cursor += namelen + TAG_HDR_SIZE; 295 } else { 296 plen = 0; 297 } 298 299 /* If we're using Host-Uniq, copy it over */ 300 if (conn->hostUniq.length) { 301 int len = ntohs(conn->hostUniq.length); 302 CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); 303 memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); 304 cursor += len + TAG_HDR_SIZE; 305 plen += len + TAG_HDR_SIZE; 306 } 307 308 /* Add our maximum MTU/MRU */ 309 if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { 310 PPPoETag maxPayload; 311 UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); 312 maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); 313 maxPayload.length = htons(sizeof(mru)); 314 memcpy(maxPayload.payload, &mru, sizeof(mru)); 315 CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); 316 memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); 317 cursor += sizeof(mru) + TAG_HDR_SIZE; 318 plen += sizeof(mru) + TAG_HDR_SIZE; 319 } 320 321 packet.length = htons(plen); 322 323 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 324 } 325 326 /********************************************************************** 327 *%FUNCTION: waitForPADO 328 *%ARGUMENTS: 329 * conn -- PPPoEConnection structure 330 * timeout -- how long to wait (in seconds) 331 *%RETURNS: 332 * Nothing 333 *%DESCRIPTION: 334 * Waits for a PADO packet and copies useful information 335 ***********************************************************************/ 336 void 337 waitForPADO(PPPoEConnection *conn, int timeout) 338 { 339 fd_set readable; 340 int r; 341 struct timeval tv; 342 struct timeval expire_at; 343 344 PPPoEPacket packet; 345 int len; 346 347 struct PacketCriteria pc; 348 pc.conn = conn; 349 pc.acNameOK = (conn->acName) ? 0 : 1; 350 pc.serviceNameOK = (conn->serviceName) ? 0 : 1; 351 pc.seenACName = 0; 352 pc.seenServiceName = 0; 353 conn->seenMaxPayload = 0; 354 conn->error = 0; 355 356 if (get_time(&expire_at) < 0) { 357 error("get_time (waitForPADO): %m"); 358 return; 359 } 360 expire_at.tv_sec += timeout; 361 362 do { 363 if (BPF_BUFFER_IS_EMPTY) { 364 if (!time_left(&tv, &expire_at)) 365 return; /* Timed out */ 366 367 FD_ZERO(&readable); 368 FD_SET(conn->discoverySocket, &readable); 369 370 while(1) { 371 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); 372 if (r >= 0 || errno != EINTR || got_sigterm) break; 373 } 374 if (r < 0) { 375 error("select (waitForPADO): %m"); 376 return; 377 } 378 if (r == 0) 379 return; /* Timed out */ 380 } 381 382 /* Get the packet */ 383 receivePacket(conn->discoverySocket, &packet, &len); 384 385 /* Check length */ 386 if (ntohs(packet.length) + HDR_SIZE > len) { 387 error("Bogus PPPoE length field (%u)", 388 (unsigned int) ntohs(packet.length)); 389 continue; 390 } 391 392 #ifdef USE_BPF 393 /* If it's not a Discovery packet, loop again */ 394 if (etherType(&packet) != Eth_PPPOE_Discovery) continue; 395 #endif 396 397 /* If it's not for us, loop again */ 398 if (!packetIsForMe(conn, &packet)) continue; 399 400 if (packet.code == CODE_PADO) { 401 if (NOT_UNICAST(packet.ethHdr.h_source)) { 402 error("Ignoring PADO packet from non-unicast MAC address"); 403 continue; 404 } 405 if (conn->req_peer 406 && memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) { 407 warn("Ignoring PADO packet from wrong MAC address"); 408 continue; 409 } 410 if (parsePacket(&packet, parsePADOTags, &pc) < 0) 411 return; 412 if (conn->error) 413 return; 414 if (!pc.seenACName) { 415 error("Ignoring PADO packet with no AC-Name tag"); 416 continue; 417 } 418 if (!pc.seenServiceName) { 419 error("Ignoring PADO packet with no Service-Name tag"); 420 continue; 421 } 422 conn->numPADOs++; 423 if (pc.acNameOK && pc.serviceNameOK) { 424 memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); 425 conn->discoveryState = STATE_RECEIVED_PADO; 426 break; 427 } 428 } 429 } while (conn->discoveryState != STATE_RECEIVED_PADO); 430 } 431 432 /*********************************************************************** 433 *%FUNCTION: sendPADR 434 *%ARGUMENTS: 435 * conn -- PPPoE connection structur 436 *%RETURNS: 437 * Nothing 438 *%DESCRIPTION: 439 * Sends a PADR packet 440 ***********************************************************************/ 441 static void 442 sendPADR(PPPoEConnection *conn) 443 { 444 PPPoEPacket packet; 445 PPPoETag *svc = (PPPoETag *) packet.payload; 446 unsigned char *cursor = packet.payload; 447 448 UINT16_t namelen = 0; 449 UINT16_t plen; 450 451 if (conn->serviceName) { 452 namelen = (UINT16_t) strlen(conn->serviceName); 453 } 454 plen = TAG_HDR_SIZE + namelen; 455 CHECK_ROOM(cursor, packet.payload, plen); 456 457 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN); 458 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 459 460 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 461 packet.vertype = PPPOE_VER_TYPE(1, 1); 462 packet.code = CODE_PADR; 463 packet.session = 0; 464 465 svc->type = TAG_SERVICE_NAME; 466 svc->length = htons(namelen); 467 if (conn->serviceName) { 468 memcpy(svc->payload, conn->serviceName, namelen); 469 } 470 cursor += namelen + TAG_HDR_SIZE; 471 472 /* If we're using Host-Uniq, copy it over */ 473 if (conn->hostUniq.length) { 474 int len = ntohs(conn->hostUniq.length); 475 CHECK_ROOM(cursor, packet.payload, len+TAG_HDR_SIZE); 476 memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); 477 cursor += len + TAG_HDR_SIZE; 478 plen += len + TAG_HDR_SIZE; 479 } 480 481 /* Add our maximum MTU/MRU */ 482 if (MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru) > ETH_PPPOE_MTU) { 483 PPPoETag maxPayload; 484 UINT16_t mru = htons(MIN(lcp_allowoptions[0].mru, lcp_wantoptions[0].mru)); 485 maxPayload.type = htons(TAG_PPP_MAX_PAYLOAD); 486 maxPayload.length = htons(sizeof(mru)); 487 memcpy(maxPayload.payload, &mru, sizeof(mru)); 488 CHECK_ROOM(cursor, packet.payload, sizeof(mru) + TAG_HDR_SIZE); 489 memcpy(cursor, &maxPayload, sizeof(mru) + TAG_HDR_SIZE); 490 cursor += sizeof(mru) + TAG_HDR_SIZE; 491 plen += sizeof(mru) + TAG_HDR_SIZE; 492 } 493 494 /* Copy cookie and relay-ID if needed */ 495 if (conn->cookie.type) { 496 CHECK_ROOM(cursor, packet.payload, 497 ntohs(conn->cookie.length) + TAG_HDR_SIZE); 498 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE); 499 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 500 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE; 501 } 502 503 if (conn->relayId.type) { 504 CHECK_ROOM(cursor, packet.payload, 505 ntohs(conn->relayId.length) + TAG_HDR_SIZE); 506 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE); 507 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 508 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE; 509 } 510 511 packet.length = htons(plen); 512 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 513 } 514 515 /********************************************************************** 516 *%FUNCTION: waitForPADS 517 *%ARGUMENTS: 518 * conn -- PPPoE connection info 519 * timeout -- how long to wait (in seconds) 520 *%RETURNS: 521 * Nothing 522 *%DESCRIPTION: 523 * Waits for a PADS packet and copies useful information 524 ***********************************************************************/ 525 static void 526 waitForPADS(PPPoEConnection *conn, int timeout) 527 { 528 fd_set readable; 529 int r; 530 struct timeval tv; 531 struct timeval expire_at; 532 533 PPPoEPacket packet; 534 int len; 535 536 if (get_time(&expire_at) < 0) { 537 error("get_time (waitForPADS): %m"); 538 return; 539 } 540 expire_at.tv_sec += timeout; 541 542 conn->error = 0; 543 do { 544 if (BPF_BUFFER_IS_EMPTY) { 545 if (!time_left(&tv, &expire_at)) 546 return; /* Timed out */ 547 548 FD_ZERO(&readable); 549 FD_SET(conn->discoverySocket, &readable); 550 551 while(1) { 552 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); 553 if (r >= 0 || errno != EINTR || got_sigterm) break; 554 } 555 if (r < 0) { 556 error("select (waitForPADS): %m"); 557 return; 558 } 559 if (r == 0) 560 return; /* Timed out */ 561 } 562 563 /* Get the packet */ 564 receivePacket(conn->discoverySocket, &packet, &len); 565 566 /* Check length */ 567 if (ntohs(packet.length) + HDR_SIZE > len) { 568 error("Bogus PPPoE length field (%u)", 569 (unsigned int) ntohs(packet.length)); 570 continue; 571 } 572 573 #ifdef USE_BPF 574 /* If it's not a Discovery packet, loop again */ 575 if (etherType(&packet) != Eth_PPPOE_Discovery) continue; 576 #endif 577 578 /* If it's not from the AC, it's not for me */ 579 if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue; 580 581 /* If it's not for us, loop again */ 582 if (!packetIsForMe(conn, &packet)) continue; 583 584 /* Is it PADS? */ 585 if (packet.code == CODE_PADS) { 586 /* Parse for goodies */ 587 if (parsePacket(&packet, parsePADSTags, conn) < 0) 588 return; 589 if (conn->error) 590 return; 591 conn->discoveryState = STATE_SESSION; 592 break; 593 } 594 } while (conn->discoveryState != STATE_SESSION); 595 596 /* Don't bother with ntohs; we'll just end up converting it back... */ 597 conn->session = packet.session; 598 599 info("PPP session is %d", (int) ntohs(conn->session)); 600 601 /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */ 602 if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) { 603 error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session)); 604 } 605 } 606 607 /********************************************************************** 608 *%FUNCTION: discovery 609 *%ARGUMENTS: 610 * conn -- PPPoE connection info structure 611 *%RETURNS: 612 * Nothing 613 *%DESCRIPTION: 614 * Performs the PPPoE discovery phase 615 ***********************************************************************/ 616 void 617 discovery(PPPoEConnection *conn) 618 { 619 int padiAttempts = 0; 620 int padrAttempts = 0; 621 int timeout = conn->discoveryTimeout; 622 623 do { 624 padiAttempts++; 625 if (got_sigterm || padiAttempts > conn->discoveryAttempts) { 626 warn("Timeout waiting for PADO packets"); 627 close(conn->discoverySocket); 628 conn->discoverySocket = -1; 629 return; 630 } 631 sendPADI(conn); 632 conn->discoveryState = STATE_SENT_PADI; 633 waitForPADO(conn, timeout); 634 635 timeout *= 2; 636 } while (conn->discoveryState == STATE_SENT_PADI); 637 638 timeout = conn->discoveryTimeout; 639 do { 640 padrAttempts++; 641 if (got_sigterm || padrAttempts > conn->discoveryAttempts) { 642 warn("Timeout waiting for PADS packets"); 643 close(conn->discoverySocket); 644 conn->discoverySocket = -1; 645 return; 646 } 647 sendPADR(conn); 648 conn->discoveryState = STATE_SENT_PADR; 649 waitForPADS(conn, timeout); 650 timeout *= 2; 651 } while (conn->discoveryState == STATE_SENT_PADR); 652 653 if (!conn->seenMaxPayload) { 654 /* RFC 4638: MUST limit MTU/MRU to 1492 */ 655 if (lcp_allowoptions[0].mru > ETH_PPPOE_MTU) 656 lcp_allowoptions[0].mru = ETH_PPPOE_MTU; 657 if (lcp_wantoptions[0].mru > ETH_PPPOE_MTU) 658 lcp_wantoptions[0].mru = ETH_PPPOE_MTU; 659 } 660 661 /* We're done. */ 662 close(conn->discoverySocket); 663 conn->discoverySocket = -1; 664 conn->discoveryState = STATE_SESSION; 665 return; 666 } 667