1 /* 2 * Perform PPPoE discovery 3 * 4 * Copyright (C) 2000-2001 by Roaring Penguin Software Inc. 5 * Copyright (C) 2004 Marco d'Itri <md@linux.it> 6 * 7 * This program may be distributed according to the terms of the GNU 8 * General Public License, version 2 or (at your option) any later version. 9 * 10 */ 11 12 #include <stdarg.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 #include <errno.h> 17 #include <string.h> 18 #include <time.h> 19 20 #include "pppoe.h" 21 22 #ifdef HAVE_UNISTD_H 23 #include <unistd.h> 24 #endif 25 26 #ifdef HAVE_NETPACKET_PACKET_H 27 #include <netpacket/packet.h> 28 #elif defined(HAVE_LINUX_IF_PACKET_H) 29 #include <linux/if_packet.h> 30 #endif 31 32 #ifdef HAVE_ASM_TYPES_H 33 #include <asm/types.h> 34 #endif 35 36 #ifdef HAVE_SYS_IOCTL_H 37 #include <sys/ioctl.h> 38 #endif 39 40 #include <errno.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #ifdef HAVE_NET_IF_ARP_H 45 #include <net/if_arp.h> 46 #endif 47 48 char *xstrdup(const char *s); 49 void usage(void); 50 51 void die(int status) 52 { 53 exit(status); 54 } 55 56 void error(char *fmt, ...) 57 { 58 va_list pvar; 59 va_start(pvar, fmt); 60 vfprintf(stderr, fmt, pvar); 61 va_end(pvar); 62 } 63 64 /* Initialize frame types to RFC 2516 values. Some broken peers apparently 65 use different frame types... sigh... */ 66 67 UINT16_t Eth_PPPOE_Discovery = ETH_PPPOE_DISCOVERY; 68 UINT16_t Eth_PPPOE_Session = ETH_PPPOE_SESSION; 69 70 /********************************************************************** 71 *%FUNCTION: etherType 72 *%ARGUMENTS: 73 * packet -- a received PPPoE packet 74 *%RETURNS: 75 * ethernet packet type (see /usr/include/net/ethertypes.h) 76 *%DESCRIPTION: 77 * Checks the ethernet packet header to determine its type. 78 * We should only be receveing DISCOVERY and SESSION types if the BPF 79 * is set up correctly. Logs an error if an unexpected type is received. 80 * Note that the ethernet type names come from "pppoe.h" and the packet 81 * packet structure names use the LINUX dialect to maintain consistency 82 * with the rest of this file. See the BSD section of "pppoe.h" for 83 * translations of the data structure names. 84 ***********************************************************************/ 85 UINT16_t 86 etherType(PPPoEPacket *packet) 87 { 88 UINT16_t type = (UINT16_t) ntohs(packet->ethHdr.h_proto); 89 if (type != Eth_PPPOE_Discovery && type != Eth_PPPOE_Session) { 90 fprintf(stderr, "Invalid ether type 0x%x\n", type); 91 } 92 return type; 93 } 94 95 /********************************************************************** 96 *%FUNCTION: openInterface 97 *%ARGUMENTS: 98 * ifname -- name of interface 99 * type -- Ethernet frame type 100 * hwaddr -- if non-NULL, set to the hardware address 101 *%RETURNS: 102 * A raw socket for talking to the Ethernet card. Exits on error. 103 *%DESCRIPTION: 104 * Opens a raw Ethernet socket 105 ***********************************************************************/ 106 int 107 openInterface(char const *ifname, UINT16_t type, unsigned char *hwaddr) 108 { 109 int optval=1; 110 int fd; 111 struct ifreq ifr; 112 int domain, stype; 113 114 #ifdef HAVE_STRUCT_SOCKADDR_LL 115 struct sockaddr_ll sa; 116 #else 117 struct sockaddr sa; 118 #endif 119 120 memset(&sa, 0, sizeof(sa)); 121 122 #ifdef HAVE_STRUCT_SOCKADDR_LL 123 domain = PF_PACKET; 124 stype = SOCK_RAW; 125 #else 126 domain = PF_INET; 127 stype = SOCK_PACKET; 128 #endif 129 130 if ((fd = socket(domain, stype, htons(type))) < 0) { 131 /* Give a more helpful message for the common error case */ 132 if (errno == EPERM) { 133 fatal("Cannot create raw socket -- pppoe must be run as root."); 134 } 135 fatalSys("socket"); 136 } 137 138 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) < 0) { 139 fatalSys("setsockopt"); 140 } 141 142 /* Fill in hardware address */ 143 if (hwaddr) { 144 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 145 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 146 fatalSys("ioctl(SIOCGIFHWADDR)"); 147 } 148 memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 149 #ifdef ARPHRD_ETHER 150 if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { 151 fatal("Interface %.16s is not Ethernet", ifname); 152 } 153 #endif 154 if (NOT_UNICAST(hwaddr)) { 155 fatal("Interface %.16s has broadcast/multicast MAC address??", ifname); 156 } 157 } 158 159 /* Sanity check on MTU */ 160 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 161 if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { 162 fatalSys("ioctl(SIOCGIFMTU)"); 163 } 164 if (ifr.ifr_mtu < ETH_DATA_LEN) { 165 fprintf(stderr, "Interface %.16s has MTU of %d -- should be %d.\n", 166 ifname, ifr.ifr_mtu, ETH_DATA_LEN); 167 fprintf(stderr, "You may have serious connection problems.\n"); 168 } 169 170 #ifdef HAVE_STRUCT_SOCKADDR_LL 171 /* Get interface index */ 172 sa.sll_family = AF_PACKET; 173 sa.sll_protocol = htons(type); 174 175 strncpy(ifr.ifr_name, ifname, IFNAMSIZ); 176 ifr.ifr_name[IFNAMSIZ - 1] = 0; 177 if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 178 fatalSys("ioctl(SIOCFIGINDEX): Could not get interface index"); 179 } 180 sa.sll_ifindex = ifr.ifr_ifindex; 181 182 #else 183 strcpy(sa.sa_data, ifname); 184 #endif 185 186 /* We're only interested in packets on specified interface */ 187 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 188 fatalSys("bind"); 189 } 190 191 return fd; 192 } 193 194 195 /*********************************************************************** 196 *%FUNCTION: sendPacket 197 *%ARGUMENTS: 198 * sock -- socket to send to 199 * pkt -- the packet to transmit 200 * size -- size of packet (in bytes) 201 *%RETURNS: 202 * 0 on success; -1 on failure 203 *%DESCRIPTION: 204 * Transmits a packet 205 ***********************************************************************/ 206 int 207 sendPacket(PPPoEConnection *conn, int sock, PPPoEPacket *pkt, int size) 208 { 209 #if defined(HAVE_STRUCT_SOCKADDR_LL) 210 if (send(sock, pkt, size, 0) < 0) { 211 fatalSys("send (sendPacket)"); 212 return -1; 213 } 214 #else 215 struct sockaddr sa; 216 217 if (!conn) { 218 fatal("relay and server not supported on Linux 2.0 kernels"); 219 } 220 strcpy(sa.sa_data, conn->ifName); 221 if (sendto(sock, pkt, size, 0, &sa, sizeof(sa)) < 0) { 222 fatalSys("sendto (sendPacket)"); 223 return -1; 224 } 225 #endif 226 return 0; 227 } 228 229 /*********************************************************************** 230 *%FUNCTION: receivePacket 231 *%ARGUMENTS: 232 * sock -- socket to read from 233 * pkt -- place to store the received packet 234 * size -- set to size of packet in bytes 235 *%RETURNS: 236 * >= 0 if all OK; < 0 if error 237 *%DESCRIPTION: 238 * Receives a packet 239 ***********************************************************************/ 240 int 241 receivePacket(int sock, PPPoEPacket *pkt, int *size) 242 { 243 if ((*size = recv(sock, pkt, sizeof(PPPoEPacket), 0)) < 0) { 244 fatalSys("recv (receivePacket)"); 245 return -1; 246 } 247 return 0; 248 } 249 250 /********************************************************************** 251 *%FUNCTION: parsePacket 252 *%ARGUMENTS: 253 * packet -- the PPPoE discovery packet to parse 254 * func -- function called for each tag in the packet 255 * extra -- an opaque data pointer supplied to parsing function 256 *%RETURNS: 257 * 0 if everything went well; -1 if there was an error 258 *%DESCRIPTION: 259 * Parses a PPPoE discovery packet, calling "func" for each tag in the packet. 260 * "func" is passed the additional argument "extra". 261 ***********************************************************************/ 262 int 263 parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra) 264 { 265 UINT16_t len = ntohs(packet->length); 266 unsigned char *curTag; 267 UINT16_t tagType, tagLen; 268 269 if (PPPOE_VER(packet->vertype) != 1) { 270 fprintf(stderr, "Invalid PPPoE version (%d)\n", 271 PPPOE_VER(packet->vertype)); 272 return -1; 273 } 274 if (PPPOE_TYPE(packet->vertype) != 1) { 275 fprintf(stderr, "Invalid PPPoE type (%d)\n", 276 PPPOE_TYPE(packet->vertype)); 277 return -1; 278 } 279 280 /* Do some sanity checks on packet */ 281 if (len > ETH_JUMBO_LEN - PPPOE_OVERHEAD) { /* 6-byte overhead for PPPoE header */ 282 fprintf(stderr, "Invalid PPPoE packet length (%u)\n", len); 283 return -1; 284 } 285 286 /* Step through the tags */ 287 curTag = packet->payload; 288 while(curTag - packet->payload < len) { 289 /* Alignment is not guaranteed, so do this by hand... */ 290 tagType = (curTag[0] << 8) + curTag[1]; 291 tagLen = (curTag[2] << 8) + curTag[3]; 292 if (tagType == TAG_END_OF_LIST) { 293 return 0; 294 } 295 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) { 296 fprintf(stderr, "Invalid PPPoE tag length (%u)\n", tagLen); 297 return -1; 298 } 299 func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra); 300 curTag = curTag + TAG_HDR_SIZE + tagLen; 301 } 302 return 0; 303 } 304 305 /********************************************************************** 306 *%FUNCTION: parseForHostUniq 307 *%ARGUMENTS: 308 * type -- tag type 309 * len -- tag length 310 * data -- tag data. 311 * extra -- user-supplied pointer. This is assumed to be a pointer to int. 312 *%RETURNS: 313 * Nothing 314 *%DESCRIPTION: 315 * If a HostUnique tag is found which matches our PID, sets *extra to 1. 316 ***********************************************************************/ 317 void 318 parseForHostUniq(UINT16_t type, UINT16_t len, unsigned char *data, 319 void *extra) 320 { 321 PPPoETag *tag = extra; 322 323 if (type == TAG_HOST_UNIQ && len == ntohs(tag->length)) 324 tag->length = memcmp(data, tag->payload, len); 325 } 326 327 /********************************************************************** 328 *%FUNCTION: packetIsForMe 329 *%ARGUMENTS: 330 * conn -- PPPoE connection info 331 * packet -- a received PPPoE packet 332 *%RETURNS: 333 * 1 if packet is for this PPPoE daemon; 0 otherwise. 334 *%DESCRIPTION: 335 * If we are using the Host-Unique tag, verifies that packet contains 336 * our unique identifier. 337 ***********************************************************************/ 338 int 339 packetIsForMe(PPPoEConnection *conn, PPPoEPacket *packet) 340 { 341 PPPoETag hostUniq = conn->hostUniq; 342 343 /* If packet is not directed to our MAC address, forget it */ 344 if (memcmp(packet->ethHdr.h_dest, conn->myEth, ETH_ALEN)) return 0; 345 346 /* If we're not using the Host-Unique tag, then accept the packet */ 347 if (!conn->hostUniq.length) return 1; 348 349 parsePacket(packet, parseForHostUniq, &hostUniq); 350 return !hostUniq.length; 351 } 352 353 /********************************************************************** 354 *%FUNCTION: parsePADOTags 355 *%ARGUMENTS: 356 * type -- tag type 357 * len -- tag length 358 * data -- tag data 359 * extra -- extra user data. Should point to a PacketCriteria structure 360 * which gets filled in according to selected AC name and service 361 * name. 362 *%RETURNS: 363 * Nothing 364 *%DESCRIPTION: 365 * Picks interesting tags out of a PADO packet 366 ***********************************************************************/ 367 void 368 parsePADOTags(UINT16_t type, UINT16_t len, unsigned char *data, 369 void *extra) 370 { 371 struct PacketCriteria *pc = (struct PacketCriteria *) extra; 372 PPPoEConnection *conn = pc->conn; 373 int i; 374 375 switch(type) { 376 case TAG_AC_NAME: 377 pc->seenACName = 1; 378 if (conn->printACNames) { 379 printf("Access-Concentrator: %.*s\n", (int) len, data); 380 } 381 if (conn->acName && len == strlen(conn->acName) && 382 !strncmp((char *) data, conn->acName, len)) { 383 pc->acNameOK = 1; 384 } 385 break; 386 case TAG_SERVICE_NAME: 387 pc->seenServiceName = 1; 388 if (conn->printACNames && len > 0) { 389 printf(" Service-Name: %.*s\n", (int) len, data); 390 } 391 if (conn->serviceName && len == strlen(conn->serviceName) && 392 !strncmp((char *) data, conn->serviceName, len)) { 393 pc->serviceNameOK = 1; 394 } 395 break; 396 case TAG_AC_COOKIE: 397 if (conn->printACNames) { 398 printf("Got a cookie:"); 399 /* Print first 20 bytes of cookie */ 400 for (i=0; i<len && i < 20; i++) { 401 printf(" %02x", (unsigned) data[i]); 402 } 403 if (i < len) printf("..."); 404 printf("\n"); 405 } 406 conn->cookie.type = htons(type); 407 conn->cookie.length = htons(len); 408 memcpy(conn->cookie.payload, data, len); 409 break; 410 case TAG_RELAY_SESSION_ID: 411 if (conn->printACNames) { 412 printf("Got a Relay-ID:"); 413 /* Print first 20 bytes of relay ID */ 414 for (i=0; i<len && i < 20; i++) { 415 printf(" %02x", (unsigned) data[i]); 416 } 417 if (i < len) printf("..."); 418 printf("\n"); 419 } 420 conn->relayId.type = htons(type); 421 conn->relayId.length = htons(len); 422 memcpy(conn->relayId.payload, data, len); 423 break; 424 case TAG_SERVICE_NAME_ERROR: 425 if (conn->printACNames) { 426 printf("Got a Service-Name-Error tag: %.*s\n", (int) len, data); 427 } 428 break; 429 case TAG_AC_SYSTEM_ERROR: 430 if (conn->printACNames) { 431 printf("Got a System-Error tag: %.*s\n", (int) len, data); 432 } 433 break; 434 case TAG_GENERIC_ERROR: 435 if (conn->printACNames) { 436 printf("Got a Generic-Error tag: %.*s\n", (int) len, data); 437 } 438 break; 439 } 440 } 441 442 /*********************************************************************** 443 *%FUNCTION: sendPADI 444 *%ARGUMENTS: 445 * conn -- PPPoEConnection structure 446 *%RETURNS: 447 * Nothing 448 *%DESCRIPTION: 449 * Sends a PADI packet 450 ***********************************************************************/ 451 void 452 sendPADI(PPPoEConnection *conn) 453 { 454 PPPoEPacket packet; 455 unsigned char *cursor = packet.payload; 456 PPPoETag *svc = (PPPoETag *) (&packet.payload); 457 UINT16_t namelen = 0; 458 UINT16_t plen; 459 460 if (conn->serviceName) { 461 namelen = (UINT16_t) strlen(conn->serviceName); 462 } 463 plen = TAG_HDR_SIZE + namelen; 464 CHECK_ROOM(cursor, packet.payload, plen); 465 466 /* Set destination to Ethernet broadcast address */ 467 memset(packet.ethHdr.h_dest, 0xFF, ETH_ALEN); 468 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN); 469 470 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery); 471 packet.vertype = PPPOE_VER_TYPE(1, 1); 472 packet.code = CODE_PADI; 473 packet.session = 0; 474 475 svc->type = TAG_SERVICE_NAME; 476 svc->length = htons(namelen); 477 CHECK_ROOM(cursor, packet.payload, namelen+TAG_HDR_SIZE); 478 479 if (conn->serviceName) { 480 memcpy(svc->payload, conn->serviceName, strlen(conn->serviceName)); 481 } 482 cursor += namelen + TAG_HDR_SIZE; 483 484 /* If we're using Host-Uniq, copy it over */ 485 if (conn->hostUniq.length) { 486 int len = ntohs(conn->hostUniq.length); 487 CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE); 488 memcpy(cursor, &conn->hostUniq, len + TAG_HDR_SIZE); 489 cursor += len + TAG_HDR_SIZE; 490 plen += len + TAG_HDR_SIZE; 491 } 492 493 packet.length = htons(plen); 494 495 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE)); 496 if (conn->debugFile) { 497 dumpPacket(conn->debugFile, &packet, "SENT"); 498 fprintf(conn->debugFile, "\n"); 499 fflush(conn->debugFile); 500 } 501 } 502 503 /********************************************************************** 504 *%FUNCTION: waitForPADO 505 *%ARGUMENTS: 506 * conn -- PPPoEConnection structure 507 * timeout -- how long to wait (in seconds) 508 *%RETURNS: 509 * Nothing 510 *%DESCRIPTION: 511 * Waits for a PADO packet and copies useful information 512 ***********************************************************************/ 513 void 514 waitForPADO(PPPoEConnection *conn, int timeout) 515 { 516 fd_set readable; 517 int r; 518 struct timeval tv; 519 PPPoEPacket packet; 520 int len; 521 522 struct PacketCriteria pc; 523 pc.conn = conn; 524 pc.acNameOK = (conn->acName) ? 0 : 1; 525 pc.serviceNameOK = (conn->serviceName) ? 0 : 1; 526 pc.seenACName = 0; 527 pc.seenServiceName = 0; 528 conn->error = 0; 529 530 do { 531 if (BPF_BUFFER_IS_EMPTY) { 532 tv.tv_sec = timeout; 533 tv.tv_usec = 0; 534 535 FD_ZERO(&readable); 536 FD_SET(conn->discoverySocket, &readable); 537 538 while(1) { 539 r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv); 540 if (r >= 0 || errno != EINTR) break; 541 } 542 if (r < 0) { 543 perror("select (waitForPADO)"); 544 return; 545 } 546 if (r == 0) return; /* Timed out */ 547 } 548 549 /* Get the packet */ 550 receivePacket(conn->discoverySocket, &packet, &len); 551 552 /* Check length */ 553 if (ntohs(packet.length) + HDR_SIZE > len) { 554 fprintf(stderr, "Bogus PPPoE length field (%u)\n", 555 (unsigned int) ntohs(packet.length)); 556 continue; 557 } 558 559 #ifdef USE_BPF 560 /* If it's not a Discovery packet, loop again */ 561 if (etherType(&packet) != Eth_PPPOE_Discovery) continue; 562 #endif 563 564 if (conn->debugFile) { 565 dumpPacket(conn->debugFile, &packet, "RCVD"); 566 fprintf(conn->debugFile, "\n"); 567 fflush(conn->debugFile); 568 } 569 /* If it's not for us, loop again */ 570 if (!packetIsForMe(conn, &packet)) continue; 571 572 if (packet.code == CODE_PADO) { 573 if (BROADCAST(packet.ethHdr.h_source)) { 574 fprintf(stderr, "Ignoring PADO packet from broadcast MAC address\n"); 575 continue; 576 } 577 parsePacket(&packet, parsePADOTags, &pc); 578 if (conn->error) 579 return; 580 if (!pc.seenACName) { 581 fprintf(stderr, "Ignoring PADO packet with no AC-Name tag\n"); 582 continue; 583 } 584 if (!pc.seenServiceName) { 585 fprintf(stderr, "Ignoring PADO packet with no Service-Name tag\n"); 586 continue; 587 } 588 conn->numPADOs++; 589 if (pc.acNameOK && pc.serviceNameOK) { 590 memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN); 591 if (conn->printACNames) { 592 printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n", 593 (unsigned) conn->peerEth[0], 594 (unsigned) conn->peerEth[1], 595 (unsigned) conn->peerEth[2], 596 (unsigned) conn->peerEth[3], 597 (unsigned) conn->peerEth[4], 598 (unsigned) conn->peerEth[5]); 599 printf("--------------------------------------------------\n"); 600 continue; 601 } 602 conn->discoveryState = STATE_RECEIVED_PADO; 603 break; 604 } 605 } 606 } while (conn->discoveryState != STATE_RECEIVED_PADO); 607 } 608 609 /********************************************************************** 610 *%FUNCTION: discovery 611 *%ARGUMENTS: 612 * conn -- PPPoE connection info structure 613 *%RETURNS: 614 * Nothing 615 *%DESCRIPTION: 616 * Performs the PPPoE discovery phase 617 ***********************************************************************/ 618 void 619 discovery(PPPoEConnection *conn) 620 { 621 int padiAttempts = 0; 622 int timeout = conn->discoveryTimeout; 623 624 conn->discoverySocket = 625 openInterface(conn->ifName, Eth_PPPOE_Discovery, conn->myEth); 626 627 do { 628 padiAttempts++; 629 if (padiAttempts > conn->discoveryAttempts) { 630 fprintf(stderr, "Timeout waiting for PADO packets\n"); 631 close(conn->discoverySocket); 632 conn->discoverySocket = -1; 633 return; 634 } 635 sendPADI(conn); 636 conn->discoveryState = STATE_SENT_PADI; 637 waitForPADO(conn, timeout); 638 } while (!conn->numPADOs); 639 } 640 641 int main(int argc, char *argv[]) 642 { 643 int opt; 644 PPPoEConnection *conn; 645 646 conn = malloc(sizeof(PPPoEConnection)); 647 if (!conn) 648 fatalSys("malloc"); 649 650 memset(conn, 0, sizeof(PPPoEConnection)); 651 652 conn->printACNames = 1; 653 conn->discoveryTimeout = PADI_TIMEOUT; 654 conn->discoveryAttempts = MAX_PADI_ATTEMPTS; 655 656 while ((opt = getopt(argc, argv, "I:D:VUQS:C:W:t:a:h")) > 0) { 657 switch(opt) { 658 case 'S': 659 conn->serviceName = xstrdup(optarg); 660 break; 661 case 'C': 662 conn->acName = xstrdup(optarg); 663 break; 664 case 't': 665 if (sscanf(optarg, "%d", &conn->discoveryTimeout) != 1) { 666 fprintf(stderr, "Illegal argument to -t: Should be -t timeout\n"); 667 exit(EXIT_FAILURE); 668 } 669 if (conn->discoveryTimeout < 1) { 670 conn->discoveryTimeout = 1; 671 } 672 break; 673 case 'a': 674 if (sscanf(optarg, "%d", &conn->discoveryAttempts) != 1) { 675 fprintf(stderr, "Illegal argument to -a: Should be -a attempts\n"); 676 exit(EXIT_FAILURE); 677 } 678 if (conn->discoveryAttempts < 1) { 679 conn->discoveryAttempts = 1; 680 } 681 break; 682 case 'U': 683 if(conn->hostUniq.length) { 684 fprintf(stderr, "-U and -W are mutually exclusive\n"); 685 exit(EXIT_FAILURE); 686 } else { 687 pid_t pid = getpid(); 688 conn->hostUniq.type = htons(TAG_HOST_UNIQ); 689 conn->hostUniq.length = htons(sizeof(pid)); 690 memcpy(conn->hostUniq.payload, &pid, sizeof(pid)); 691 } 692 break; 693 case 'W': 694 if(conn->hostUniq.length) { 695 fprintf(stderr, "-U and -W are mutually exclusive\n"); 696 exit(EXIT_FAILURE); 697 } 698 if (!parseHostUniq(optarg, &conn->hostUniq)) { 699 fprintf(stderr, "Invalid host-uniq argument: %s\n", optarg); 700 exit(EXIT_FAILURE); 701 } 702 break; 703 case 'D': 704 conn->debugFile = fopen(optarg, "w"); 705 if (!conn->debugFile) { 706 fprintf(stderr, "Could not open %s: %s\n", 707 optarg, strerror(errno)); 708 exit(1); 709 } 710 fprintf(conn->debugFile, "pppoe-discovery from pppd %s\n", VERSION); 711 break; 712 case 'I': 713 conn->ifName = xstrdup(optarg); 714 break; 715 case 'Q': 716 conn->printACNames = 0; 717 break; 718 case 'V': 719 case 'h': 720 usage(); 721 exit(0); 722 default: 723 usage(); 724 exit(1); 725 } 726 } 727 728 /* default interface name */ 729 if (!conn->ifName) 730 conn->ifName = strdup("eth0"); 731 732 conn->discoverySocket = -1; 733 conn->sessionSocket = -1; 734 735 discovery(conn); 736 737 if (!conn->numPADOs) 738 exit(1); 739 else 740 exit(0); 741 } 742 743 void fatal(char * fmt, ...) 744 { 745 va_list ap; 746 va_start(ap, fmt); 747 vfprintf(stderr, fmt, ap); 748 va_end(ap); 749 fputc('\n', stderr); 750 exit(1); 751 } 752 753 void fatalSys(char const *str) 754 { 755 perror(str); 756 exit(1); 757 } 758 759 char *xstrdup(const char *s) 760 { 761 register char *ret = strdup(s); 762 if (!ret) 763 fatalSys("strdup"); 764 return ret; 765 } 766 767 void usage(void) 768 { 769 fprintf(stderr, "Usage: pppoe-discovery [options]\n"); 770 fprintf(stderr, "Options:\n"); 771 fprintf(stderr, " -I if_name -- Specify interface (default eth0)\n"); 772 fprintf(stderr, " -D filename -- Log debugging information in filename.\n"); 773 fprintf(stderr, 774 " -t timeout -- Initial timeout for discovery packets in seconds\n" 775 " -a attempts -- Number of discovery attempts\n" 776 " -V -- Print version and exit.\n" 777 " -Q -- Quit Mode: Do not print access concentrator names\n" 778 " -S name -- Set desired service name.\n" 779 " -C name -- Set desired access concentrator name.\n" 780 " -U -- Use Host-Unique to allow multiple PPPoE sessions.\n" 781 " -W hexvalue -- Set the Host-Unique to the supplied hex string.\n" 782 " -h -- Print usage information.\n"); 783 fprintf(stderr, "\npppoe-discovery from pppd " VERSION "\n"); 784 } 785