Home | History | Annotate | Download | only in rp-pppoe
      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