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.3 2004/11/04 10:07:37 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 ***********************************************************************/
     55 void
     56 parseForHostUniq(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 ***********************************************************************/
     80 int
     81 packetIsForMe(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 ***********************************************************************/
    109 void
    110 parsePADOTags(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 ***********************************************************************/
    205 void
    206 parsePADSTags(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 ***********************************************************************/
    243 void
    244 sendPADI(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 ***********************************************************************/
    310 void
    311 waitForPADO(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 (NOT_UNICAST(packet.ethHdr.h_source)) {
    369 		printErr("Ignoring PADO packet from non-unicast 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 ***********************************************************************/
    413 void
    414 sendPADR(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 ***********************************************************************/
    494 void
    495 waitForPADS(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 ***********************************************************************/
    576 void
    577 discovery(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