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 (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 ***********************************************************************/ 79 static void 80 parseForHostUniq(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 ***********************************************************************/ 104 static int 105 packetIsForMe(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 ***********************************************************************/ 133 static void 134 parsePADOTags(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 ***********************************************************************/ 210 static void 211 parsePADSTags(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 ***********************************************************************/ 262 static void 263 sendPADI(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 ***********************************************************************/ 344 void 345 waitForPADO(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 ***********************************************************************/ 449 static void 450 sendPADR(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 ***********************************************************************/ 537 static void 538 waitForPADS(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 ***********************************************************************/ 628 void 629 discovery(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