Example #1
0
/**********************************************************************
*%FUNCTION: waitForPADS
*%ARGUMENTS:
* conn -- PPPoE connection info
* timeout -- how long to wait (in seconds)
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Waits for a PADS packet and copies useful information
***********************************************************************/
void
waitForPADS(PPPoEConnection *conn, int timeout)
{
    fd_set readable;
    int r;
    struct timeval tv;
    PPPoEPacket packet;
    int len;

    do {
	if (BPF_BUFFER_IS_EMPTY) {
	    tv.tv_sec = timeout;
	    tv.tv_usec = 0;
	    
	    FD_ZERO(&readable);
	    FD_SET(conn->discoverySocket, &readable);
	    
	    while(1) {
		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
		if (r >= 0 || errno != EINTR) break;
	    }
	    if (r < 0) {
		fatalSys("select (waitForPADS)");
	    }
	    if (r == 0) return;
	}

	/* Get the packet */
	receivePacket(conn->discoverySocket, &packet, &len);

	/* Check length */
	if (ntohs(packet.length) + HDR_SIZE > len) {
	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
		   (unsigned int) ntohs(packet.length));
	    continue;
	}

#ifdef USE_BPF
	/* If it's not a Discovery packet, loop again */
	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
#endif
	if (conn->debugFile) {
	    dumpPacket(conn->debugFile, &packet, "RCVD");
	    fprintf(conn->debugFile, "\n");
	    fflush(conn->debugFile);
	}

	/* If it's not from the AC, it's not for me */
	if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;

	/* If it's not for us, loop again */
	if (!packetIsForMe(conn, &packet)) continue;

	/* Is it PADS?  */
	if (packet.code == CODE_PADS) {
	    /* Parse for goodies */
	    parsePacket(&packet, parsePADSTags, conn);
	    conn->discoveryState = STATE_SESSION;
	    break;
	}
    } while (conn->discoveryState != STATE_SESSION);

    /* Don't bother with ntohs; we'll just end up converting it back... */
    conn->session = packet.session;

    syslog(LOG_INFO, "PPP session is %d", (int) ntohs(conn->session));

    /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
    if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
	syslog(LOG_ERR, "Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
    }
}
Example #2
0
/**********************************************************************
*%FUNCTION: waitForPADS
*%ARGUMENTS:
* conn -- PPPoE connection info
* timeout -- how long to wait (in seconds)
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Waits for a PADS packet and copies useful information
***********************************************************************/
static void
waitForPADS(PPPoEConnection *conn, int timeout)
{
    fd_set readable;
    int r;
    struct timeval tv;
    struct timeval expire_at;

    PPPoEPacket packet;
    int len;

    if (gettimeofday(&expire_at, NULL) < 0) {
	error("gettimeofday (waitForPADS): %m");
	return;
    }
    expire_at.tv_sec += timeout;

    conn->error = 0;
    do {
	if (BPF_BUFFER_IS_EMPTY) {
	    if (!time_left(&tv, &expire_at))
		return;		/* Timed out */

	    FD_ZERO(&readable);
	    FD_SET(conn->discoverySocket, &readable);

	    while(1) {
		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
		if (r >= 0 || errno != EINTR) break;
	    }
	    if (r < 0) {
		error("select (waitForPADS): %m");
		return;
	    }
	    if (r == 0)
		return;		/* Timed out */
	}

	/* Get the packet */
	receivePacket(conn->discoverySocket, &packet, &len);

	/* Check length */
	if (ntohs(packet.length) + HDR_SIZE > len) {
	    error("Bogus PPPoE length field (%u)",
		   (unsigned int) ntohs(packet.length));
	    continue;
	}

#ifdef USE_BPF
	/* If it's not a Discovery packet, loop again */
	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
#endif

	/* If it's not from the AC, it's not for me */
	if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;

	/* If it's not for us, loop again */
	if (!packetIsForMe(conn, &packet)) continue;

	/* Is it PADS?  */
	if (packet.code == CODE_PADS) {
	    /* Parse for goodies */
	    if (parsePacket(&packet, parsePADSTags, conn) < 0)
		return;
	    if (conn->error)
		return;
	    conn->discoveryState = STATE_SESSION;
	    break;
	}
    } while (conn->discoveryState != STATE_SESSION);

    /* Don't bother with ntohs; we'll just end up converting it back... */
    conn->session = packet.session;

    info("PPP session is %d", (int) ntohs(conn->session));

    /* RFC 2516 says session id MUST NOT be zero or 0xFFFF */
    if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xFFFF) {
	error("Access concentrator used a session value of %x -- the AC is violating RFC 2516", (unsigned int) ntohs(conn->session));
    }
}
Example #3
0
/**********************************************************************
*%FUNCTION: waitForPADO
*%ARGUMENTS:
* conn -- PPPoEConnection structure
* timeout -- how long to wait (in seconds)
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Waits for a PADO packet and copies useful information
***********************************************************************/
void
waitForPADO(PPPoEConnection *conn, int timeout)
{
    fd_set readable;
    int r;
    struct timeval tv;
    PPPoEPacket packet;
    int len;

    struct PacketCriteria pc;
    pc.conn          = conn;
    pc.acNameOK      = (conn->acName)      ? 0 : 1;
    pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
    pc.seenACName    = 0;
    pc.seenServiceName = 0;
	
    do {
	if (BPF_BUFFER_IS_EMPTY) {
	    tv.tv_sec = timeout;
	    tv.tv_usec = 0;
	
	    FD_ZERO(&readable);
	    FD_SET(conn->discoverySocket, &readable);

	    while(1) {
		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
		if (r >= 0 || errno != EINTR) break;
	    }
	    if (r < 0) {
		fatalSys("select (waitForPADO)");
	    }
	    if (r == 0) return;        /* Timed out */
	}
	
	/* Get the packet */
	receivePacket(conn->discoverySocket, &packet, &len);

	/* Check length */
	if (ntohs(packet.length) + HDR_SIZE > len) {
	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
		   (unsigned int) ntohs(packet.length));
	    continue;
	}

#ifdef USE_BPF
	/* If it's not a Discovery packet, loop again */
	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
#endif

	if (conn->debugFile) {
	    dumpPacket(conn->debugFile, &packet, "RCVD");
	    fprintf(conn->debugFile, "\n");
	    fflush(conn->debugFile);
	}
	/* If it's not for us, loop again */
	if (!packetIsForMe(conn, &packet)) continue;

	if (packet.code == CODE_PADO) {
	    if (NOT_UNICAST(packet.ethHdr.h_source)) {
		printErr("Ignoring PADO packet from non-unicast MAC address");
		continue;
	    }
	    parsePacket(&packet, parsePADOTags, &pc);
	    if (!pc.seenACName) {
		printErr("Ignoring PADO packet with no AC-Name tag");
		continue;
	    }
	    if (!pc.seenServiceName) {
		printErr("Ignoring PADO packet with no Service-Name tag");
		continue;
	    }
	    conn->numPADOs++;
	    if (conn->printACNames) {
		printf("--------------------------------------------------\n");
	    }
	    if (pc.acNameOK && pc.serviceNameOK) {
		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
		if (conn->printACNames) {
		    printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
			   (unsigned) conn->peerEth[0], 
			   (unsigned) conn->peerEth[1],
			   (unsigned) conn->peerEth[2],
			   (unsigned) conn->peerEth[3],
			   (unsigned) conn->peerEth[4],
			   (unsigned) conn->peerEth[5]);
		    continue;
		}
		conn->discoveryState = STATE_RECEIVED_PADO;
		break;
	    }
	}
    } while (conn->discoveryState != STATE_RECEIVED_PADO);
}
Example #4
0
/**********************************************************************
*%FUNCTION: waitForPADO
*%ARGUMENTS:
* conn -- PPPoEConnection structure
* timeout -- how long to wait (in seconds)
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Waits for a PADO packet and copies useful information
***********************************************************************/
void
waitForPADO(PPPoEConnection *conn, int timeout)
{
    fd_set readable;
    int r;
    struct timeval tv;
    struct timeval expire_at;

    PPPoEPacket packet;
    int len;

    struct PacketCriteria pc;
    pc.conn          = conn;
    pc.acNameOK      = (conn->acName)      ? 0 : 1;
    pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
    pc.seenACName    = 0;
    pc.seenServiceName = 0;
    conn->seenMaxPayload = 0;
    conn->error = 0;

    if (gettimeofday(&expire_at, NULL) < 0) {
	error("gettimeofday (waitForPADO): %m");
	return;
    }
    expire_at.tv_sec += timeout;

    do {
	if (BPF_BUFFER_IS_EMPTY) {
	    if (!time_left(&tv, &expire_at))
		return;		/* Timed out */

	    FD_ZERO(&readable);
	    FD_SET(conn->discoverySocket, &readable);

	    while(1) {
		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
		if (r >= 0 || errno != EINTR) break;
	    }
	    if (r < 0) {
		error("select (waitForPADO): %m");
		return;
	    }
	    if (r == 0)
		return;		/* Timed out */
	}

	/* Get the packet */
	receivePacket(conn->discoverySocket, &packet, &len);

	/* Check length */
	if (ntohs(packet.length) + HDR_SIZE > len) {
	    error("Bogus PPPoE length field (%u)",
		   (unsigned int) ntohs(packet.length));
	    continue;
	}

#ifdef USE_BPF
	/* If it's not a Discovery packet, loop again */
	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
#endif

	/* If it's not for us, loop again */
	if (!packetIsForMe(conn, &packet)) continue;

	if (packet.code == CODE_PADO) {
	    if (NOT_UNICAST(packet.ethHdr.h_source)) {
		error("Ignoring PADO packet from non-unicast MAC address");
		continue;
	    }
	    if (conn->req_peer
		&& memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
		warn("Ignoring PADO packet from wrong MAC address");
		continue;
	    }
	    if (parsePacket(&packet, parsePADOTags, &pc) < 0)
		return;
	    if (conn->error)
		return;
	    if (!pc.seenACName) {
		error("Ignoring PADO packet with no AC-Name tag");
		continue;
	    }
	    if (!pc.seenServiceName) {
		error("Ignoring PADO packet with no Service-Name tag");
		continue;
	    }
	    conn->numPADOs++;
	    if (pc.acNameOK && pc.serviceNameOK) {
		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
		conn->discoveryState = STATE_RECEIVED_PADO;
		break;
	    }
	}
    } while (conn->discoveryState != STATE_RECEIVED_PADO);
}
Example #5
0
bool RfidReaderMac::handleRecvdMacPacket(PacketPtr packet, 
	t_uint sendingLayerIdx)
{
	RfidTagMacDataPtr macData =
		boost::dynamic_pointer_cast<RfidTagMacData>
		(packet->getData(Packet::DataTypes_Link));

	bool wasSuccessful = true;

	// For now, we'll only handle MAC packets from tags.
	if(macData.get() != 0) {
		if(packetIsForMe(macData)) {
			switch(macData->getType()) {
			case RfidTagMacData::Types_Reply:
				// If we ended the cycle early due to too many
				// consecutive missed reads, then the timer will
				// be stopped and we shouldn't handle anymore
				// REPLY packets.
				if(m_cycleTimer->isRunning()) {
					// It could be the case that this reply is received
					// in a contention cycle after the a SELECT packet
					// was lost.  In response to the SELECT packet being
					// lost in slot i, the reader will reset in
					// slot i+2 and send a REQUEST in slot i+3.
					// If this reply is received in slot i+2, we should
					// ignore it since m_packetToTransmit already
					// has a REQUEST packet pending.
					if(m_packetToTransmit.get() == 0) {
						assert(m_currentAppReadPacket.get() != 0);
						// Send SELECT message header on the original
						// app packet.
						addSelectHeader(m_currentAppReadPacket, 
							macData->getSenderId());
						m_packetToTransmit = m_currentAppReadPacket;
						m_txSlotNumber = m_currentSlotNumber;
						assert(m_slotTimer.get() != 0 && 
							m_slotTimer->isRunning());
					}
				}
				break;
			case RfidTagMacData::Types_Generic:
				// Subtract three from the winning slot number for:
				// 1. The current slot number was incremented
				// at the beginning of the slot.
				// 2. The REPLY was sent two slots prior
				// to this packet being received (the SELECT
				// packet was sent one slot prior).
				m_winningSlotNumbers.push_back(
					make_pair(macData->getSenderId(), 
					(m_currentSlotNumber - 3)));
				// Just pass the packet to upper layers.
				wasSuccessful = sendToLinkLayer(
					CommunicationLayer::Directions_Upper, packet);
				m_packetToTransmit = createAckPacket(macData->getSenderId());
				m_txSlotNumber = m_currentSlotNumber;
				assert(m_slotTimer.get() != 0 && 
					m_slotTimer->isRunning());
				break;
			default:
				wasSuccessful = false;
			}
		}
	}

	return wasSuccessful;
}
Example #6
0
static void waitForPADS(PPPoEConnection * conn, int timeout)
{
	fd_set readable;
	int r;
	struct timeval tv;
	PPPoEPacket packet;
	int len;

	d_dbg("[%d]: waitForPADS: >>> \n", getpid());

	do
	{
		tv.tv_sec = timeout;
		tv.tv_usec = 0;

		FD_ZERO(&readable);
		FD_SET(conn->discoverySocket, &readable);

		while (1)
		{
			r = select(conn->discoverySocket + 1, &readable, NULL, NULL, &tv);
			if (r >= 0 || errno != EINTR) break;
		}
		if (r < 0)
		{
			d_error("[%d]: select (waitForPADS)\n", getpid());
			return;
		}
		if (r == 0) return;

		/* Get the packet */
		receivePacket(conn->discoverySocket, &packet, &len);
		if (packet.code == CODE_PADS) d_dbg("[%d]: GOT a PADS!\n", getpid());

		/* Check length */
		if (ntohs(packet.length) + HDR_SIZE > len)
		{
			d_error("[%d]: Bogus PPPoE length field (%u)\n", getpid(), (unsigned int)ntohs(packet.length));
			continue;
		}

		/* If it's not from the AC, it's not for me */
		if (memcmp(packet.ethHdr.h_source, conn->peerEth, ETH_ALEN)) continue;

		/* If it's not for us, loop again */
		if (!packetIsForMe(conn, &packet)) continue;

		/* Is it PADS ? */
		if (packet.code == CODE_PADS)
		{
			/* Parse for goodies */
			kpppoe_parsePacket(&packet, parsePADSTags, conn);
			conn->discoveryState = STATE_SESSION;
			break;
		}

	} while (conn->discoveryState != STATE_SESSION);

	/* Don't bother with ntohs; we'll just end up converting it back... */
	conn->session = packet.session;
	d_info("[%d]: PPP session is %d\n", getpid(), (int)ntohs(conn->session));
#ifndef LOGNUM
#ifdef ELBOX_PROGS_GPL_SYSLOGD_AP	
	syslog(ALOG_NOTICE|LOG_NOTICE,"[Notice]PPPoE: Received PADS for %s. (Session ID: %x)", linkname, (int)ntohs(conn->session));
#else
	syslog(ALOG_NOTICE|LOG_NOTICE,"PPPoE: Received PADS for %s. (Session ID: %x)", linkname, (int)ntohs(conn->session));
#endif
#else
	syslog(ALOG_NOTICE|LOG_NOTICE,"NTC:028[%s][%x]", linkname, (int)ntohs(conn->session));
#endif
	sts_session_id = conn->session;

	/* RFC 2516 says session id MUST NOT be zero of 0xffff */
	if (ntohs(conn->session) == 0 || ntohs(conn->session) == 0xffff)
		d_info("[%d]: Access concentrator used a session value of %x -- the AC is violating RFC 2516\n", getpid(), (unsigned int) ntohs(conn->session));
	d_dbg("[%d]: waitForPADS: <<< \n", getpid());
}
Example #7
0
static void waitForPADO(PPPoEConnection * conn, int timeout)
{
	fd_set readable;
	int r = -1;
	struct timeval tv;
	PPPoEPacket packet;
	int len;

	struct PacketCriteria pc;
	pc.conn = conn;
	pc.acNameOK = (conn->acName) ? 0 : 1;
	pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
	pc.seenACName = 0;
	pc.seenServiceName = 0;

	do
	{
		tv.tv_sec = timeout;
		tv.tv_usec = 0;

		FD_ZERO(&readable);
		FD_SET(conn->discoverySocket, &readable);

		while (!got_sigterm) /* Modified by Kwest Wan 20080919 to terminate pppd immediately if PPPoE server don't reponse */
		{
			d_dbg("[%d]: waitPADO select >>>>\n", getpid());
			r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
			d_dbg("[%d]: waitPADO select (%d) <<<<\n", getpid(), r);
			if (r >= 0 || errno != EINTR) break;
		}
		if (r < 0)
		{
			d_error("[%d]: select (waitForPADS)\n", getpid());
			return;
		}
		if (r == 0) return;

		/* Get the packet */
		d_dbg("[%d]: receivePacket >>>\n", getpid());
		receivePacket(conn->discoverySocket, &packet, &len);
		d_dbg("[%d]: receivePacket <<<\n", getpid());

		/* Check length */
		if (ntohs(packet.length) + HDR_SIZE > len)
		{
			d_error("[%d]: Bogus PPPoE length field (%u)\n", getpid(), (unsigned int)ntohs(packet.length));
			continue;
		}

		/* If it's not for us, loop again */
		if (!packetIsForMe(conn, &packet))
		{
			d_dbg("[%d]: packet is not for me!\n", getpid());
			continue;
		}

		/* Is it PADS ? */
		if (packet.code == CODE_PADO)
		{
			if (NOT_UNICAST(packet.ethHdr.h_source))
			{
				d_info("[%d]: Ignoring PADO packet from non-unicast MAC address\n", getpid());
#ifndef LOGNUM
#ifdef ELBOX_PROGS_GPL_SYSLOGD_AP	
				syslog(ALOG_NOTICE|LOG_NOTICE, "[Notice]PPPoE: Ignore PADO packet from non-unicast MAC address. (%s)", linkname);
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "PPPoE: Ignore PADO packet from non-unicast MAC address. (%s)", linkname);
#endif	
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "NTC:029[%s]", linkname);
#endif
				continue;
			}
			kpppoe_parsePacket(&packet, parsePADOTags, &pc);
			if (!pc.seenACName)
			{
				d_info("[%d]: Ignoring PADO packet with no AC-Name tag\n", getpid());
#ifndef LOGNUM
#ifdef ELBOX_PROGS_GPL_SYSLOGD_AP	
				syslog(ALOG_NOTICE|LOG_NOTICE, "[Notice]PPPoE: Ignore PADO packet with no AC-Name tag. (%s)", linkname);
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "PPPoE: Ignore PADO packet with no AC-Name tag. (%s)", linkname);
#endif	
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "NTC:030[%s]", linkname);
#endif
				continue;
			}
			if (!pc.seenServiceName)
			{
				d_info("[%d]: Ignoring PADO packet with no Service-Name tag\n", getpid());
#ifndef LOGNUM
#ifdef ELBOX_PROGS_GPL_SYSLOGD_AP	
				syslog(ALOG_NOTICE|LOG_NOTICE, "[Notice]PPPoE: Ignore PADO packet with no Service-Name tag. (%s)", linkname);
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "PPPoE: Ignore PADO packet with no Service-Name tag. (%s)", linkname);
#endif	
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "NTC:031[%s]", linkname);
#endif
				continue;
			}
			conn->numPADOs++;
			if (conn->printACNames) printf("------------------------------------------------------\n");
			update_statusfile("PPPoE:PADO");
			d_dbg("[%d]: acNameOK:%d, serviceNameOK:%d\n", getpid(), pc.acNameOK, pc.serviceNameOK);
			if (pc.acNameOK && pc.serviceNameOK)
			{
				memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
				if (conn->printACNames)
				{
					printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
							(unsigned)conn->peerEth[0],
							(unsigned)conn->peerEth[1],
							(unsigned)conn->peerEth[2],
							(unsigned)conn->peerEth[3],
							(unsigned)conn->peerEth[4],
							(unsigned)conn->peerEth[5]);
					continue;
				}
				conn->discoveryState = STATE_RECEIVED_PADO;
#ifndef LOGNUM
#ifdef ELBOX_PROGS_GPL_SYSLOGD_AP	
				syslog(ALOG_NOTICE|LOG_NOTICE, "[Notice]PPPoE: Received PADO for %s, from: %02x:%02x:%02x:%02x:%02x:%02x",
						linkname,
						(unsigned)conn->peerEth[0],
						(unsigned)conn->peerEth[1],
						(unsigned)conn->peerEth[2],
						(unsigned)conn->peerEth[3],
						(unsigned)conn->peerEth[4],
						(unsigned)conn->peerEth[5]);
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "PPPoE: Received PADO for %s, from: %02x:%02x:%02x:%02x:%02x:%02x",
						linkname,
						(unsigned)conn->peerEth[0],
						(unsigned)conn->peerEth[1],
						(unsigned)conn->peerEth[2],
						(unsigned)conn->peerEth[3],
						(unsigned)conn->peerEth[4],
						(unsigned)conn->peerEth[5]);
#endif	
#else
				syslog(ALOG_NOTICE|LOG_NOTICE, "NTC:026[%s][%02x:%02x:%02x:%02x:%02x:%02x]",
						linkname,
						(unsigned)conn->peerEth[0],
						(unsigned)conn->peerEth[1],
						(unsigned)conn->peerEth[2],
						(unsigned)conn->peerEth[3],
						(unsigned)conn->peerEth[4],
						(unsigned)conn->peerEth[5]);
#endif
				break;
			}
		}
		else
		{
			d_dbg("[%d]: this is not PADO!\n", getpid());
		}
	} while (conn->discoveryState != STATE_RECEIVED_PADO);
}
Example #8
0
/**********************************************************************
*%FUNCTION: waitForPADO
*%ARGUMENTS:
* conn -- PPPoEConnection structure
* timeout -- how long to wait (in seconds)
*%RETURNS:
* Nothing
*%DESCRIPTION:
* Waits for a PADO packet and copies useful information
***********************************************************************/
static void
waitForPADO(PPPoEConnection *conn, int timeout)
{
    fd_set readable;
    int r;
    struct timeval tv;
    struct timeval expire_at;
    struct timeval now;

    PPPoEPacket packet;
    int len;

    struct PacketCriteria pc;
    pc.conn          = conn;
#ifdef PLUGIN
    conn->seenMaxPayload = 0;
#endif

    if (gettimeofday(&expire_at, NULL) < 0) {
	fatalSys("gettimeofday (waitForPADO)");
    }
    expire_at.tv_sec += timeout;

    do {
#ifdef PLUGIN
	if (got_sigterm || got_sighup) return;
#endif
	if (BPF_BUFFER_IS_EMPTY) {
	    if (gettimeofday(&now, NULL) < 0) {
		fatalSys("gettimeofday (waitForPADO)");
	    }
	    tv.tv_sec = expire_at.tv_sec - now.tv_sec;
	    tv.tv_usec = expire_at.tv_usec - now.tv_usec;
	    if (tv.tv_usec < 0) {
		tv.tv_usec += 1000000;
		if (tv.tv_sec) {
		    tv.tv_sec--;
		} else {
		    /* Timed out */
		    return;
		}
	    }
	    if (tv.tv_sec <= 0 && tv.tv_usec <= 0) {
		/* Timed out */
		return;
	    }

	    FD_ZERO(&readable);
	    FD_SET(conn->discoverySocket, &readable);

	    while(1) {
		r = select(conn->discoverySocket+1, &readable, NULL, NULL, &tv);
		if (r >= 0 || errno != EINTR) break;
#ifdef PLUGIN
		if (got_sigterm || got_sighup) return;
#endif
	    }
	    if (r < 0) {
		fatalSys("select (waitForPADO)");
	    }
	    if (r == 0) {
		/* Timed out */
		return;
	    }
	}

	/* Get the packet */
	receivePacket(conn->discoverySocket, &packet, &len);

	/* Check length */
	if (ntohs(packet.length) + HDR_SIZE > len) {
	    syslog(LOG_ERR, "Bogus PPPoE length field (%u)",
		   (unsigned int) ntohs(packet.length));
	    continue;
	}

#ifdef USE_BPF
	/* If it's not a Discovery packet, loop again */
	if (etherType(&packet) != Eth_PPPOE_Discovery) continue;
#endif

#ifdef DEBUGGING_ENABLED
	if (conn->debugFile) {
	    dumpPacket(conn->debugFile, &packet, "RCVD");
	    fprintf(conn->debugFile, "\n");
	    fflush(conn->debugFile);
	}
#endif
	/* If it's not for us, loop again */
	if (!packetIsForMe(conn, &packet)) continue;

	if (packet.code == CODE_PADO) {
	    if (BROADCAST(packet.ethHdr.h_source)) {
		printErr("Ignoring PADO packet from broadcast MAC address");
		continue;
	    }
#ifdef PLUGIN
	    if (conn->req_peer
		&& memcmp(packet.ethHdr.h_source, conn->req_peer_mac, ETH_ALEN) != 0) {
		warn("Ignoring PADO packet from wrong MAC address");
		continue;
	    }
#endif
	    pc.gotError = 0;
	    pc.seenACName    = 0;
	    pc.seenServiceName = 0;
	    pc.acNameOK      = (conn->acName)      ? 0 : 1;
	    pc.serviceNameOK = (conn->serviceName) ? 0 : 1;
	    parsePacket(&packet, parsePADOTags, &pc);
	    if (pc.gotError) {
		printErr("Error in PADO packet");
		continue;
	    }

	    if (!pc.seenACName) {
		printErr("Ignoring PADO packet with no AC-Name tag");
		continue;
	    }
	    if (!pc.seenServiceName) {
		printErr("Ignoring PADO packet with no Service-Name tag");
		continue;
	    }
	    conn->numPADOs++;
	    if (pc.acNameOK && pc.serviceNameOK) {
		memcpy(conn->peerEth, packet.ethHdr.h_source, ETH_ALEN);
		if (conn->printACNames) {
		    printf("AC-Ethernet-Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
			   (unsigned) conn->peerEth[0],
			   (unsigned) conn->peerEth[1],
			   (unsigned) conn->peerEth[2],
			   (unsigned) conn->peerEth[3],
			   (unsigned) conn->peerEth[4],
			   (unsigned) conn->peerEth[5]);
		    printf("--------------------------------------------------\n");
		    continue;
		}
		conn->discoveryState = STATE_RECEIVED_PADO;
		break;
	    }
	}
    } while (conn->discoveryState != STATE_RECEIVED_PADO);
}