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