1/*********************************************************************** 2* 3* discovery.c 4* 5* Perform PPPoE discovery 6* 7* Copyright (C) 1999 by Roaring Penguin Software Inc. 8* 9***********************************************************************/ 10 11static char const RCSID[] = 12"$Id: discovery.c,v 1.4 2005/03/22 10:22:32 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***********************************************************************/ 55void 56parseForHostUniq(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***********************************************************************/ 80int 81packetIsForMe(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***********************************************************************/ 109void 110parsePADOTags(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***********************************************************************/ 205void 206parsePADSTags(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***********************************************************************/ 243void 244sendPADI(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***********************************************************************/ 310void 311waitForPADO(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 (BROADCAST(packet.ethHdr.h_source)) { 369 printErr("Ignoring PADO packet from broadcast 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***********************************************************************/ 413void 414sendPADR(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***********************************************************************/ 494void 495waitForPADS(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***********************************************************************/ 576void 577discovery(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