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