Example #1
0
int recvMacAddressTime(uchar* mac,unsigned int time){
        int msgbuff[ETH_ADDR_LEN];
        int index;
        int i;
	int err = 0xFFFF;
        bzero(msgbuff,ETH_ADDR_LEN);
        if(time==0){
                for(i=0;i<ETH_ADDR_LEN;i++){
                        msgbuff[i]=receive();
			if(err==msgbuff[i]){
				return SYSERR;
			}
 //                       kprintf("LOOP: RECV macFrame:%02x, pos:%u\r\n",msgbuff[i]>>(2*8),msgbuff[i]&0x00FF);
                }
        }else{
        for(i=0;i<ETH_ADDR_LEN;i++){
                        msgbuff[i]=recvtime(CLKTICKS_PER_SEC*time);
                        if(msgbuff[i]==TIMEOUT){
                                return TIMEOUT;
                        }
			if(msgbuff[i]==err){
				return SYSERR;
			}
 //                       kprintf("LOOP: RECV macFrame:%02x, pos:%u\r\n",msgbuff[i]>>(2*8),msgbuff[i]&0x00FF);
                }
}

        for(i=0;i<ETH_ADDR_LEN;i++){
                index = msgbuff[i] & 0x00FF;
                mac[index]= (uchar)(msgbuff[i]>>(2*8));
        }
        return OK;
}
Example #2
0
/**
 * Wait, with timeout, for a response from a DHCP server and update the DHCP
 * transfer data.  The exact behavior of this function depends on the current
 * DHCP client state:
 *
 * - If in DHCPC_STATE_SELECTING, the client waits until it gets a DHCPOFFER
 *   reply from any server.
 * - If in DHCPC_STATE_REQUESTING, the client waits until it gets a DHCPACK or
 *   DHCPNAK reply from the server to which it sent the DHCPREQUEST.
 *
 * @param[in] descrp
 *      Network device on which to wait for a response.
 * @param[in,out] data
 *      DHCP transfer data.
 * @param[in] timeout
 *      Milliseconds to wait before timing out.
 *
 * @return
 *      OK if successful; TIMEOUT if timed out; SYSERR if other error occurred.
 */
syscall dhcpRecvReply(int descrp, struct dhcpData *data, uint timeout)
{
    struct packet *pkt;
    tid_typ tid;
    int retval;

    pkt = netGetbuf();
    if (SYSERR == (int)pkt)
    {
        return SYSERR;
    }

    /* This somewhat of a hack to implement a timeout:  Wait for the reply in
     * another thread to avoid blocking on read().  */
    tid = create(do_dhcpRecvReply, DHCP_RECV_STKSIZE, DHCP_RECV_PRIO,
                 "dhcpRecvReply", 3, descrp, data, pkt);
    if (isbadtid(tid))
    {
        netFreebuf(pkt);
        return SYSERR;
    }

    ready(tid, RESCHED_YES);

    /* Wait at most @timeout milliseconds for the thread to terminate before
     * returning TIMEOUT.  */
    if (TIMEOUT == recvtime(timeout))
    {
        kill(tid);
        receive();
        retval = TIMEOUT;
    }
    else
    {
        retval = data->recvStatus;
    }
    netFreebuf(pkt);
    return retval;
}
Example #3
0
/*------------------------------------------------------------------------
 * udp_recvaddr - receive a UDP packet and record the sender's address
 *------------------------------------------------------------------------
 */
int32	udp_recvaddr (
    uint32	*remip,			/* loc to record remote IP addr.*/
    uint16	*remport,		/* loc to record remote port	*/
    uint16	locport,		/* local UDP protocol port	*/
    char   *buff,			/* buffer to hold UDP data	*/
    int32	len,			/* length of buffer		*/
    uint32	timeout			/* read timeout in msec		*/
)
{
    intmask	mask;			/* saved interrupt mask		*/
    int32	i;			/* index into udptab		*/
    struct	udpentry *udptr;	/* pointer to udptab entry	*/
    umsg32	msg;			/* message from recvtime()	*/
    struct	eth_packet *pkt;		/* ptr to packet being read	*/
    struct  ipv4_packet *ippkt = NULL;
    struct  udp_packet * udppkt = NULL;
    int32	msglen;			/* length of UDP data in packet	*/
    char	*udataptr;		/* pointer to UDP data		*/

    /* Insure only one process access UDP table at a time */

    mask = disable();

    for (i=0; i<UDP_SLOTS; i++) {
        udptr = &udptab[i];
        if ( (udptr->udremip == 0 ) &&
                (locport == udptr->udlocport) ) {

            /* Entry in table matches request */
            break;
        }
    }

    if (i >= UDP_SLOTS) {
        restore(mask);
        return SYSERR;
    }

    if (udptr->udcount == 0) {	/* no packet is waiting */
        udptr->udstate = UDP_RECV;
        udptr->udpid = currpid;
        msg = recvclr();
        msg = recvtime(timeout);	/* wait for packet */
        udptr->udstate = UDP_USED;
        if (msg == TIMEOUT) {
            restore(mask);
            return TIMEOUT;
        } else if (msg != OK) {
            restore(mask);
            return SYSERR;
        }
    }

    /* Packet has arrived -- dequeue it */

    pkt = udptr->udqueue[udptr->udhead++];
    ippkt = (struct ipv4_packet *)(pkt->net_ethdata);
    udppkt = (struct udp_packet *)(ippkt->net_ipdata);
    if (udptr->udhead >= UDP_SLOTS) {
        udptr->udhead = 0;
    }
    udptr->udcount--;

    /* Record sender's IP address and UDP port number */

    *remip = ippkt->net_ipsrc;
    *remport = udppkt->net_udpsport;

    /* Copy UDP data from packet into caller's buffer */

    msglen = udppkt->net_udplen - UDP_HDR_LEN;
    udataptr = (char *)udppkt->net_udpdata;
    for (i=0; i<msglen; i++) {
        if (i >= len) {
            break;
        }
        *buff++ = *udataptr++;
    }
    freebuf((char *)pkt);
    restore(mask);
    return i;
}
/*------------------------------------------------------------------------
 * udp_recvaddr  -  Receive a UDP packet and record the sender's address
 *------------------------------------------------------------------------
 */
int32	udp_recvaddr (
	 uid32	slot,			/* Slot in table to use		*/
	 uint32	*remip,			/* Loc for remote IP address	*/
	 uint16	*remport,		/* Loc for remote protocol port	*/
	 char   *buff,			/* Buffer to hold UDP data	*/
	 int32	len,			/* Length of buffer		*/
	 uint32	timeout			/* Read timeout in msec		*/
	)
{
	intmask	mask;			/* Saved interrupt mask		*/
	struct	udpentry *udptr;	/* Pointer to udptab entry	*/
	umsg32	msg;			/* Message from recvtime()	*/
	struct	netpacket *pkt;		/* Pointer to packet being read	*/
	int32	msglen;			/* Length of UDP data in packet	*/
	int32	i;			/* Counts bytes copied		*/
	char	*udataptr;		/* Pointer to UDP data		*/

	/* Ensure only one process can access the UDP table at a time	*/

	mask = disable();

	/* Verify that the slot is valid */

	if ((slot < 0) || (slot >= UDP_SLOTS)) {
		restore(mask);
		return SYSERR;
	}

	/* Get pointer to table entry */

	udptr = &udptab[slot];

	/* Verify that the slot has been registered and is valid */

	if (udptr->udstate != UDP_USED) {
		restore(mask);
		return SYSERR;
	}

	/* Wait for a packet to arrive */

	if (udptr->udcount == 0) {		/* No packet is waiting */
		udptr->udstate = UDP_RECV;
		udptr->udpid = currpid;
		msg = recvclr();
		msg = recvtime(timeout);	/* Wait for a packet	*/
		udptr->udstate = UDP_USED;
		if (msg == TIMEOUT) {
			restore(mask);
			return TIMEOUT;
		} else if (msg != OK) {
			restore(mask);
			return SYSERR;
		}
	}

	/* Packet has arrived -- dequeue it */

	pkt = udptr->udqueue[udptr->udhead++];
	if (udptr->udhead >= UDP_QSIZ) {
		udptr->udhead = 0;
	}

	/* Record sender's IP address and UDP port number */

	*remip = pkt->net_ipsrc;
	*remport = pkt->net_udpsport;

	udptr->udcount--;

	/* Copy UDP data from packet into caller's buffer */

	msglen = pkt->net_udplen - UDP_HDR_LEN;
	udataptr = (char *)pkt->net_udpdata;
	if (len < msglen) {
		msglen = len;
	}
	for (i=0; i<msglen; i++) {
		*buff++ = *udataptr++;
	}
	freebuf((char *)pkt);
	restore(mask);
	return msglen;
}
Example #5
0
/**
 * @ingroup arp
 *
 * Obtains a hardware address from the ARP table given a protocol address.
 * @param netptr network interface
 * @param praddr protocol address
 * @param hwaddr buffer into which hardware address should be placed
 * @return OK if hardware address was obtained, otherwise TIMEOUT or SYSERR
 */
syscall arpLookup(struct netif *netptr, const struct netaddr *praddr,
                  struct netaddr *hwaddr)
{
    struct arpEntry *entry = NULL;  /**< pointer to ARP table entry   */
    uint lookups = 0;                   /**< num of ARP lookups performed */
    int ttl;                            /**< TTL for ARP table entry      */
    irqmask im;                         /**< interrupt state              */

    /* Error check pointers */
    if ((NULL == netptr) || (NULL == praddr) || (NULL == hwaddr))
    {
        ARP_TRACE("Invalid args");
        return SYSERR;
    }

    ARP_TRACE("Looking up protocol address");

    /* Attempt to obtain destination hardware address from ARP table until:
     * 1) lookup succeeds; 2) TIMEOUT occurs; 3) SYSERR occurs; or
     * 4) maximum number of lookup attempts occrus. */
    while (lookups < ARP_MAX_LOOKUP)
    {
        lookups++;

        /* Obtain entry from ARP table */
        im = disable();
        entry = arpGetEntry(praddr);

        /* If ARP entry does not exist; create an unresolved entry */
        if (NULL == entry)
        {
            ARP_TRACE("Entry does not exist");
            entry = arpAlloc();
            if (SYSERR == (int)entry)
            {
                restore(im);
                return SYSERR;
            }

            entry->state = ARP_UNRESOLVED;
            entry->nif = netptr;
            netaddrcpy(&entry->praddr, praddr);
            entry->expires = clktime + ARP_TTL_UNRESOLVED;
            entry->count = 0;
        }

        /* Place hardware address in buffer if entry is resolved */
        if (ARP_RESOLVED == entry->state)
        {
            netaddrcpy(hwaddr, &entry->hwaddr);
            ARP_TRACE("Entry exists");
            return OK;
        }

        /* Entry is unresolved; enqueue thread to wait for resolution */
        if (entry->count >= ARP_NTHRWAIT)
        {
            restore(im);
            ARP_TRACE("Queue of waiting threads is full");
            return SYSERR;
        }
        entry->waiting[entry->count] = gettid();
        entry->count++;
        ttl = (entry->expires - clktime) * CLKTICKS_PER_SEC;
        restore(im);

        /* Send an ARP request and wait for response */
        if (SYSERR == arpSendRqst(entry))
        {
            ARP_TRACE("Failed to send request");
            return SYSERR;
        }
        recvclr();
        switch (recvtime(ttl))
        {
        case TIMEOUT:
        case SYSERR:
            return SYSERR;
        case ARP_MSG_RESOLVED:
        default:
            /* Reply received, address resolved, re-attempt lookup */
            continue;
        }
    }

    return SYSERR;
}
/*------------------------------------------------------------------------
 * icmp_recv  -  Receive an icmp echo reply packet
 *------------------------------------------------------------------------
 */
int32	icmp_recv (
	 int32	icmpid,			/* ICMP slot identifier		*/
	 char   *buff,			/* Buffer to ICMP data		*/
	 int32	len,			/* Length of buffer		*/
	 uint32	timeout			/* Time to wait in msec		*/
	)
{
	intmask	mask;			/* Saved interrupt mask		*/
	struct	icmpentry *icmptr;	/* Pointer to icmptab entry	*/
	umsg32	msg;			/* Message from recvtime()	*/
	struct	netpacket *pkt;		/* Pointer to packet being read	*/
	int32	datalen;		/* Length of ICMP data area	*/
	char	*icdataptr;		/* Pointer to icmp data		*/
	int32	i;			/* Counter for data copy	*/

	/* Verify that the ID is valid */

	if ( (icmpid < 0) || (icmpid >= ICMP_SLOTS) ) {
		return SYSERR;
	}

	/* Insure only one process touches the table at a time */

	mask = disable();

	/* Verify that the ID has been registered and is idle */

	icmptr = &icmptab[icmpid];
	if (icmptr->icstate != ICMP_USED) {
		restore(mask);
		return SYSERR;
	}

	if (icmptr->iccount == 0) {		/* No packet is waiting */
		icmptr->icstate = ICMP_RECV;
		icmptr->icpid = currpid;
		msg = recvclr();
		msg = recvtime(timeout);	/* Wait for a reply */
		icmptr->icstate = ICMP_USED;
		if (msg == TIMEOUT) {
			restore(mask);
			return TIMEOUT;
		} else if (msg != OK) {
			restore(mask);
			return SYSERR;
		}
	}

	/* Packet has arrived -- dequeue it */

	pkt = icmptr->icqueue[icmptr->ichead++];
	if (icmptr->ichead >= ICMP_SLOTS) {
		icmptr->ichead = 0;
	}
	icmptr->iccount--;

	/* Copy data from ICMP message into caller's buffer */

	datalen = pkt->net_iplen - IP_HDR_LEN - ICMP_HDR_LEN;
	icdataptr = (char *) &pkt->net_icdata;
	for (i=0; i<datalen; i++) {
		if (i >= len) {
			break;
		}
		*buff++ = *icdataptr++;
	}
	freebuf((char *)pkt);
	restore(mask);
	return i;
}
Example #7
0
/**
 * Replies to ARP requests destined for our router, and handles replies
 * from our router
 */
void arpDaemon(void)
{
	//arpTab = malloc(sizeof(struct arpTable));
	//arpTab.arr = malloc(sizeof(struct arp)*MAX_ARP_TABLE);
	arpTab.size = 0;
	arpSem = semcreate(1);
	bzero(packet,PKTSZ);	
	int pid;
	struct arpPkt *arpPkt;
	uchar *mac = malloc(ETH_ADDR_LEN);
	uchar *brdcast = malloc(ETH_ADDR_LEN);

	struct ethergram *eg = (struct ethergram*) packet;
	//bzero(packet, PKTSZ);
	
	control(ETH0, ETH_CTRL_GET_MAC, (long)mac, 0);

	brdcast[0] = 0xFF;
	brdcast[1] = 0xFF;
	brdcast[2] = 0xFF;
	brdcast[3] = 0xFF;
	brdcast[4] = 0xFF;
	brdcast[5] = 0xFF;

	while (1)
	{
		//printf("arpDaemon started loop\n");
		//bzero(packet, PKTSZ);
		read(ETH0, packet, PKTSZ);
//		printPacket(packet);
		if (memcmp(eg->dst, mac, sizeof(mac)) != 0 && memcmp(eg->dst, brdcast, sizeof(brdcast)) != 0) /* Not our packet, drop */
			continue;
		//printf("arpDaemon We received a packet for our mac\n");
		arpPkt = (struct arpPkt *)&eg->data[0];
		//printf("eg->type=%d arpPkt->hwtype=%d arpPkt->prtype=%08x\n",ntohs(eg->type), ntohs(arpPkt->hwtype), ntohs(arpPkt->prtype));
		if (ntohs(eg->type)!=ETYPE_ARP||ntohs(arpPkt->hwtype) != 1 || ntohs(arpPkt->prtype) != ETYPE_IPv4
			 || arpPkt->hwalen != ETH_ADDR_LEN || arpPkt->pralen != IPv4_ADDR_LEN)
			continue;
		//printf("arpDaemon Got past first check\n");
		if (memcmp(myipaddr, &arpPkt->addrs[ARP_ADDR_DPA], sizeof(myipaddr)) != 0)
			continue;
		//printf("arpDaemon We received a packet for our mac and ip\n");

		if (arpPkt->op == ntohs(ARP_OP_RQST)) /* ARP Request */
		{
			//printf("arpDaemon ARP Request recveid\n");
			memcpy(eg->dst, eg->src, sizeof(eg->src));
			memcpy(eg->src, mac, sizeof(mac));
			eg->type = htons(ETYPE_ARP);
			arpPkt->prtype = htons(ETYPE_IPv4);
			arpPkt->op = htons(ARP_OP_REPLY);
			memcpy(&arpPkt->addrs[ARP_ADDR_DHA], &arpPkt->addrs[ARP_ADDR_SHA], ETH_ADDR_LEN);
			memcpy(&arpPkt->addrs[ARP_ADDR_DPA], &arpPkt->addrs[ARP_ADDR_SPA], IPv4_ADDR_LEN);
			memcpy(&arpPkt->addrs[ARP_ADDR_SHA], mac, ETH_ADDR_LEN);
			memcpy(&arpPkt->addrs[ARP_ADDR_SPA], myipaddr, IPv4_ADDR_LEN);
			write(ETH0, packet,ETHER_SIZE+ETHER_MINPAYLOAD);//(uchar*)((struct arpPkt *)((struct ethergram *)packet)->data)-packet);
		}else if (arpPkt->op == ntohs(ARP_OP_REPLY)) /* ARP Reply */
		{
			//printf("\narpDaemon Mac address recveived thee mac address %02x:%02x:%02x:%02x:%02x:%02x\n",arpPkt->addrs[ARP_ADDR_SHA],arpPkt->addrs[ARP_ADDR_SHA+1],arpPkt->addrs[ARP_ADDR_SHA+2],arpPkt->addrs[ARP_ADDR_SHA+3],arpPkt->addrs[ARP_ADDR_SHA+4],arpPkt->addrs[ARP_ADDR_SHA+5]);
			pid = recvtime(CLKTICKS_PER_SEC*10);
			//printf("arpDaemon recved succ, sending\n");
			if (pid == TIMEOUT)
				continue;
			sendMacAddress(pid, &arpPkt->addrs[ARP_ADDR_SHA]);
		}else /* Some other op type, drop */
			continue;
	}
}
Example #8
0
/**
 * Tests ARP.
 * @return OK when testing is complete
 */
thread test_arp(bool verbose)
{
    bool passed = TRUE;
    int i = 0;
    struct netaddr ip;
    struct netaddr mask;
    struct netaddr praddr;
    struct netaddr hwaddr;
    struct netaddr addrbuf;
    struct pcap_file_header pcap;
    struct pcap_pkthdr phdr;
    struct packet *pkt;
    struct netif *netptr;
    struct ethloop *pelp;
    uchar *data;
    uchar *request;
    struct arpPkt *arp;
    struct arpEntry *entry;
    uchar buf[ELOOP_BUFSIZE];
    int nproc;
    int nout;
    int wait;
    tid_typ tids[ARP_NTHRWAIT];
    tid_typ tid;
    irqmask im;

    enable();

    ip.type = NETADDR_IPv4;
    ip.len = IPv4_ADDR_LEN;
    ip.addr[0] = 192;
    ip.addr[1] = 168;
    ip.addr[2] = 1;
    ip.addr[3] = 6;

    mask.type = NETADDR_IPv4;
    mask.len = IPv4_ADDR_LEN;
    mask.addr[0] = 255;
    mask.addr[1] = 255;
    mask.addr[2] = 255;
    mask.addr[3] = 0;

    /* Initialize loopback ethernet and network interface */
    testPrint(verbose, "Test case initialization");

    data = (uchar *)(&_binary_data_testarp_pcap_start);
    memcpy(&pcap, data, sizeof(pcap));
    data += sizeof(pcap);

    if (SYSERR == open(ELOOP))
    {
        failif(TRUE, "");
    }
    else
    {
        failif((SYSERR == netUp(ELOOP, &ip, &mask, NULL)), "");
    }
    if (!passed)
    {
        testFail(TRUE, "");
        return OK;
    }

    /* Setup pointers to underlying structures */
#if NNETIF
    for (i = 0; i < NNETIF; i++)
    {
        if ((NET_ALLOC == netiftab[i].state)
            && (ELOOP == netiftab[i].dev))
        {
            break;
        }
    }
#endif
    netptr = &netiftab[i];
    pelp = &elooptab[devtab[ELOOP].minor];

    pkt = netGetbuf();
    if ((int)pkt != SYSERR)
    {
        pkt->nif = netptr;
    }

    praddr.type = NETADDR_IPv4;
    praddr.len = IPv4_ADDR_LEN;
    praddr.addr[0] = 192;
    praddr.addr[1] = 168;
    praddr.addr[2] = 1;
    praddr.addr[3] = 3;

    hwaddr.type = NETADDR_ETHERNET;
    hwaddr.len = ETH_ADDR_LEN;
    hwaddr.addr[0] = 0xAA;
    hwaddr.addr[1] = 0xBB;
    hwaddr.addr[2] = 0xCC;
    hwaddr.addr[3] = 0xDD;
    hwaddr.addr[4] = 0xEE;
    hwaddr.addr[5] = 0xFF;

    /* Test arpPkt structure */
    testPrint(verbose, "Header structure (Request)");
    /* Get 1st packet */
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    pkt = netGetbuf();
    pkt->len +=
        ARP_CONST_HDR_LEN + (2 * IPv4_ADDR_LEN) + (2 * ETH_ADDR_LEN);
    pkt->curr -= pkt->len;
    arp = (struct arpPkt *)pkt->curr;
    arp->hwtype = hs2net(ARP_HWTYPE_ETHERNET);
    arp->prtype = hs2net(ARP_PRTYPE_IPv4);
    arp->hwalen = ETH_ADDR_LEN;
    arp->pralen = IPv4_ADDR_LEN;
    arp->op = hs2net(ARP_OP_RQST);
    memcpy(&arp->addrs[ARP_ADDR_SHA(arp)], netptr->hwaddr.addr,
           arp->hwalen);
    memcpy(&arp->addrs[ARP_ADDR_SPA(arp)], netptr->ip.addr, arp->pralen);
    memcpy(&arp->addrs[ARP_ADDR_DPA(arp)], praddr.addr, arp->pralen);
    failif((0 != memcmp(data + ELOOP_LINKHDRSIZE, arp, pkt->len)), "");

    /* Test arpPkt structure */
    testPrint(verbose, "Header structure (Reply)");
    /* Get 2nd packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    arp->op = hs2net(ARP_OP_REPLY);
    memcpy(&arp->addrs[ARP_ADDR_DHA(arp)], &arp->addrs[ARP_ADDR_SHA(arp)],
           arp->hwalen);
    arp->addrs[ARP_ADDR_DHA(arp) + ETH_ADDR_LEN - 1] = 0xCC;
    memcpy(&arp->addrs[ARP_ADDR_DPA(arp)], praddr.addr, arp->pralen);
    memcpy(&arp->addrs[ARP_ADDR_SHA(arp)], netptr->hwaddr.addr,
           arp->hwalen);
    memcpy(&arp->addrs[ARP_ADDR_SPA(arp)], netptr->ip.addr, arp->pralen);
    failif((0 != memcmp(data + ELOOP_LINKHDRSIZE, arp, pkt->len)), "");

    /* Test arpAlloc, free entry exists */
    testPrint(verbose, "Allocate free entry");
    /* Make first entry used */
    arptab[0].state = ARP_USED;
    arptab[0].expires = clktime + ARP_TTL_UNRESOLVED;
    entry = arpAlloc();
    failif(((NULL == entry) || (entry == &arptab[0])
            || (0 == (entry->state & ARP_USED))), "");

    /* Test arpAlloc, free entry exists */
    testPrint(verbose, "Allocate used entry");
    arptab[1].state = ARP_USED;
    arptab[1].expires = clktime + 1;
    /* Make all entries (except the first 2) have ARP_TTL_RESOLVED */
    for (i = 2; i < ARP_NENTRY; i++)
    {
        arptab[i].state = ARP_USED;
        arptab[i].expires = clktime + ARP_TTL_RESOLVED;
    }
    entry = arpAlloc();
    failif(((NULL == entry) || (entry != &arptab[1])
            || (0 == (entry->state & ARP_USED))), "");

    /* Test arpNotify */
    testPrint(verbose, "Notify waiting threads (bad params)");
    failif((SYSERR != arpNotify(NULL, TIMEOUT)), "");

    /* Test arpNotify */
    testPrint(verbose, "Notify waiting threads");
    entry = &arptab[0];
    entry->state = ARP_UNRESOLVED;
    for (i = 0; i < ARP_NTHRWAIT; i++)
    {
        tid =
            create((void *)notifyTest, INITSTK, INITPRIO, "notifyTest",
                   0);
        tids[i] = tid;
        entry->waiting[i] = tid;
        entry->count++;
        ready(tid, RESCHED_NO);
    }
    recvclr();
    arpNotify(entry, ARP_MSG_RESOLVED);
    nout = FALSE;
    nproc = 0;
    tid = recvtime(100);
    while ((tid != TIMEOUT) && (nproc < ARP_NTHRWAIT))
    {
        for (i = 0; i < ARP_NTHRWAIT; i++)
        {
            if (tid == tids[i])
            {
                tids[i] = NULL;
            }
        }
        nproc++;
        tid = recvtime(100);
    }
    for (i = 0; i < ARP_NTHRWAIT; i++)
    {
        if (tids[i] != NULL)
        {
            kill(tids[i]);
            nout = TRUE;
        }
    }
    failif(nout || (entry->count != 0), "");

    /* Test arpFree */
    testPrint(verbose, "Free entry (bad params)");
    failif((SYSERR != arpFree(NULL)), "");

    /* Test arpFree */
    testPrint(verbose, "Free resolved entry");
    entry = &arptab[0];
    entry->state = ARP_RESOLVED;
    entry->expires = clktime;
    failif(((SYSERR == arpFree(entry)) || (entry->expires != 0)), "");

    /* Test arpFree */
    testPrint(verbose, "Free unresolved entry");
    entry = &arptab[0];
    entry->state = ARP_UNRESOLVED;
    for (i = 0; i < ARP_NTHRWAIT; i++)
    {
        tid = create((void *)freeTest, INITSTK, INITPRIO, "freeTest", 0);
        tids[i] = tid;
        entry->waiting[i] = tid;
        entry->count++;
        ready(tid, RESCHED_NO);
    }
    recvclr();
    if (SYSERR == arpFree(entry))
    {
        failif(TRUE, "Returned SYSERR");
    }
    else
    {
        nout = FALSE;
        nproc = 0;
        tid = recvtime(100);
        while ((tid != TIMEOUT) && (nproc < ARP_NTHRWAIT))
        {
            for (i = 0; i < ARP_NTHRWAIT; i++)
            {
                if (tid == tids[i])
                {
                    tids[i] = NULL;
                }
            }
            nproc++;
            tid = recvtime(100);
        }
        for (i = 0; i < ARP_NTHRWAIT; i++)
        {
            if (tids[i] != NULL)
            {
                kill(tids[i]);
                nout = TRUE;
            }
        }
        failif(nout || (entry->count != 0), "");
    }

    /* Test arpGetEntry */
    testPrint(verbose, "Get entry");
    for (i = 0; i < ARP_NENTRY; i++)
    {
        arpFree(&arptab[i]);
    }
    nout = 4;
    if (ARP_NENTRY < nout)
    {
        nout = ARP_NENTRY;
    }
    for (i = 1; i < nout; i++)
    {
        entry = &arptab[i];
        entry->state = ARP_RESOLVED;
        entry->nif = netptr;
        praddr.addr[3] = i + 1;
        hwaddr.addr[5] = ((i + 0xA) << 4) + (i + 0xA);
        netaddrcpy(&entry->hwaddr, &hwaddr);
        netaddrcpy(&entry->praddr, &praddr);
        entry->expires = clktime + ARP_TTL_RESOLVED;
    }
    for (i = 1; i < nout; i++)
    {
        praddr.addr[3] = i + 1;
        entry = arpGetEntry(&praddr);
        if (entry != &arptab[i])
        {
            break;
        }
    }
    failif((i != nout), "");

    /* Test arpGetEntry with timeout */
    testPrint(verbose, "Get entry (timeout entries)");
    arptab[i].expires = clktime - 1;
    praddr.addr[3] = 2;
    entry = arpGetEntry(&praddr);
    if (entry != &arptab[1])
    {
        failif(TRUE, "Incorrect entry");
    }
    else
    {
        failif((arptab[0].state != ARP_FREE),
               "Did not free expired entry");
    }
    for (i = 0; i < ARP_NENTRY; i++)
    {
        arpFree(&arptab[i]);
    }

    /* Test receive reply for non-existing ARP table entry */
    testPrint(verbose, "Receive ARP reply, new entry");
    /* Get 3rd packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    praddr.addr[3] = 3;
    hwaddr.addr[5] = 0xCC;
    nout = pelp->nout;
    nproc = netptr->nproc;
    write(ELOOP, data, phdr.caplen);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(10);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Check entry and ensure reply was not sent */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if (ARP_RESOLVED == arptab[i].state)
            {
                entry = &arptab[i];
                break;
            }
        }
        if (NULL == entry)
        {
            failif(TRUE, "No entry inserted");
        }
        else if ((FALSE == netaddrequal(&praddr, &entry->praddr))
                 || (FALSE == netaddrequal(&hwaddr, &entry->hwaddr))
                 || (entry->nif != netptr) || (entry->count != 0))
        {
            failif(TRUE, "Entry incorrect");
        }
        else if (pelp->nout > (nout + 1))
        {
            failif(TRUE, "Reply sent to reply");
        }
        else
        {
            failif(FALSE, "");
        }
    }

    /* Test receive request for non-existing ARP table entry */
    testPrint(verbose, "Receive ARP request, new entry");
    /* Get 4th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    praddr.addr[3] = 1;
    hwaddr.addr[5] = 0xAA;
    nout = pelp->nout;
    nproc = netptr->nproc;
    im = disable();
    write(ELOOP, data, phdr.caplen);
    control(ELOOP, ELOOP_CTRL_SETFLAG, ELOOP_FLAG_HOLDNXT, NULL);
    restore(im);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(100);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Check entry and ensure reply was not sent */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if ((ARP_RESOLVED == arptab[i].state)
                && (netaddrequal(&praddr, &arptab[i].praddr)))
            {
                entry = &arptab[i];
                break;
            }
        }
        if (NULL == entry)
        {
            failif(TRUE, "No entry inserted");
        }
        else if ((FALSE == netaddrequal(&praddr, &entry->praddr))
                 || (FALSE == netaddrequal(&hwaddr, &entry->hwaddr))
                 || (entry->nif != netptr) || (entry->count != 0))
        {
            failif(TRUE, "Entry incorrect");
        }
        else
        {
            control(ELOOP, ELOOP_CTRL_GETHOLD, (int)buf, ELOOP_BUFSIZE);
            /* Get 5th packet */
            data += phdr.caplen;
            memcpy(&phdr, data, sizeof(phdr));
            data += sizeof(phdr);
            if (PCAP_MAGIC != pcap.magic)
            {
                phdr.caplen = endswap(phdr.caplen);
            }
            failif((memcmp(data, buf, phdr.caplen) != 0),
                   "Invalid reply");
        }
    }

    /* Test receive request for non-supported hardware type */
    testPrint(verbose, "Receive ARP request, bad HW type");
    /* Get 6th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    praddr.addr[3] = 2;
    hwaddr.addr[5] = 0xBB;
    nproc = netptr->nproc;
    write(ELOOP, data, phdr.caplen);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(100);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Ensure entry was not added */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if ((ARP_RESOLVED == arptab[i].state)
                && (netaddrequal(&praddr, &arptab[i].praddr)))
            {
                entry = &arptab[i];
                break;
            }
        }
        failif((entry != NULL), "Entry inserted");
    }

    /* Test receive reply for non-supported protocol type */
    testPrint(verbose, "Receive ARP reply, bad protocol type");
    /* Get 7th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    nproc = netptr->nproc;
    write(ELOOP, data, phdr.caplen);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(100);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Ensure entry was not added */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if ((ARP_RESOLVED == arptab[i].state)
                && (netaddrequal(&praddr, &arptab[i].praddr)))
            {
                entry = &arptab[i];
                break;
            }
        }
        failif((entry != NULL), "Entry inserted");
    }

    /* Test receive reply for existing resolved ARP table entry */
    testPrint(verbose, "Receive ARP reply, resolved entry");
    /* Get 8th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    praddr.addr[3] = 3;
    hwaddr.addr[5] = 0x0C;
    nout = pelp->nout;
    nproc = netptr->nproc;
    write(ELOOP, data, phdr.caplen);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(100);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Check entry */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if ((ARP_RESOLVED == arptab[i].state)
                && (netaddrequal(&praddr, &arptab[i].praddr)))
            {
                entry = &arptab[i];
                break;
            }
        }
        failif((FALSE == netaddrequal(&hwaddr, &entry->hwaddr)),
               "Entry incorrect");
    }

    /* Test receive request not for me */
    testPrint(verbose, "Receive ARP request, not mine");
    /* Get 9th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    praddr.addr[3] = 4;
    hwaddr.addr[5] = 0xDD;
    nproc = netptr->nproc;
    write(ELOOP, data, phdr.caplen);
    /* Wait until packet is processed */
    wait = 0;
    while ((wait < MAX_WAIT) && (netptr->nproc == nproc))
    {
        wait++;
        sleep(100);
    }
    if (MAX_WAIT == wait)
    {
        failif(TRUE, "Wait time expired");
    }
    else
    {
        /* Ensure entry was not added */
        entry = NULL;
        for (i = 0; i < ARP_NENTRY; i++)
        {
            if ((ARP_RESOLVED == arptab[i].state)
                && (netaddrequal(&praddr, &arptab[i].praddr)))
            {
                entry = &arptab[i];
                break;
            }
        }
        failif((entry != NULL), "Entry inserted");
    }

    /* Test arpSendRequest */
    testPrint(verbose, "Send ARP request (bad params)");
    failif((SYSERR != arpSendRqst(NULL)), "");

    /* Test arpSendRequest */
    testPrint(verbose, "Send ARP request");
    /* Get 10th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    entry = &arptab[0];
    entry->state = ARP_UNRESOLVED;
    entry->nif = netptr;
    praddr.addr[3] = 2;
    hwaddr.addr[5] = 0xBB;
    netaddrcpy(&entry->hwaddr, &hwaddr);
    netaddrcpy(&entry->praddr, &praddr);
    entry->expires = clktime + ARP_TTL_UNRESOLVED;
    control(ELOOP, ELOOP_CTRL_SETFLAG, ELOOP_FLAG_HOLDNXT, NULL);
    if (SYSERR == arpSendRqst(entry))
    {
        failif(TRUE, "Returned SYSERR");
    }
    else
    {
        control(ELOOP, ELOOP_CTRL_GETHOLD, (int)buf, ELOOP_BUFSIZE);
        failif((memcmp(buf, data, phdr.caplen) != 0), "");
    }

    /* Test arpLookup */
    testPrint(verbose, "Lookup address (bad params)");
    failif((SYSERR != arpLookup(NULL, NULL, NULL)), "");

    /* Test arpLookup */
    testPrint(verbose, "Lookup existing resolved address");
    for (i = 0; i < ARP_NENTRY; i++)
    {
        arpFree(&arptab[i]);
    }
    entry = &arptab[0];
    entry->state = ARP_RESOLVED;
    entry->nif = netptr;
    praddr.addr[3] = 1;
    hwaddr.addr[5] = 0xAA;
    netaddrcpy(&entry->hwaddr, &hwaddr);
    netaddrcpy(&entry->praddr, &praddr);
    entry->expires = clktime + ARP_TTL_UNRESOLVED;
    i = arpLookup(netptr, &praddr, &addrbuf);
    if ((SYSERR == i) || (TIMEOUT == i))
    {
        failif(TRUE, "Returned SYSERR or TIMEOUT");
    }
    else
    {
        failif((FALSE == netaddrequal(&addrbuf, &hwaddr)),
               "Wrong address");
    }

    /* Test arpLookup */
    testPrint(verbose, "Lookup existing unresolved address");
    entry = &arptab[1];
    entry->state = ARP_UNRESOLVED;
    entry->nif = netptr;
    praddr.addr[3] = 2;
    hwaddr.addr[5] = 0xBB;
    netaddrcpy(&entry->hwaddr, &hwaddr);
    netaddrcpy(&entry->praddr, &praddr);
    entry->expires = clktime + ARP_TTL_UNRESOLVED;
    control(ELOOP, ELOOP_CTRL_SETFLAG, ELOOP_FLAG_HOLDNXT, NULL);
    request = data;
    wait = phdr.caplen;
    /* Get 11th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    tid =
        ready(create
              ((void *)lookupTest, INITSTK, INITPRIO, "lookupTest", 4,
               request, wait, data, phdr.caplen), RESCHED_NO);
    i = arpLookup(netptr, &praddr, &addrbuf);
    if ((SYSERR == i) || (TIMEOUT == i))
    {
        kill(tid);
        failif(TRUE, "Returned SYSERR or TIMEOUT");
    }
    else
    {
        failif((FALSE == netaddrequal(&addrbuf, &hwaddr)),
               "Wrong address");
    }

    /* Test arpLookup */
    testPrint(verbose, "Lookup max threads wait for resolve");
    entry->state = ARP_UNRESOLVED;
    entry->count = ARP_NTHRWAIT;
    failif((arpLookup(netptr, &praddr, &addrbuf) != SYSERR), "");

    /* Test arpLookup */
    testPrint(verbose, "Lookup non-existing unresolved addr");
    praddr.addr[3] = 3;
    hwaddr.addr[5] = 0xCC;
    control(ELOOP, ELOOP_CTRL_SETFLAG, ELOOP_FLAG_HOLDNXT, NULL);
    request = data;
    wait = phdr.caplen;
    /* Get 12th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    request = data;
    wait = phdr.caplen;
    /* Get 13th packet */
    data += phdr.caplen;
    memcpy(&phdr, data, sizeof(phdr));
    data += sizeof(phdr);
    if (PCAP_MAGIC != pcap.magic)
    {
        phdr.caplen = endswap(phdr.caplen);
    }
    tid =
        ready(create
              ((void *)lookupTest, INITSTK, INITPRIO, "lookupTest", 4,
               request, wait, data, phdr.caplen), RESCHED_NO);
    i = arpLookup(netptr, &praddr, &addrbuf);
    if ((SYSERR == i) || (TIMEOUT == i))
    {
        kill(tid);
        failif(TRUE, "Returned SYSERR or TIMEOUT");
    }
    else
    {
        failif((FALSE == netaddrequal(&addrbuf, &hwaddr)),
               "Wrong address");
    }

    /* Test arpLookup */
    testPrint(verbose, "Lookup timeout");
    praddr.addr[3] = 4;
    hwaddr.addr[5] = 0xDD;
    control(ELOOP, ELOOP_CTRL_SETFLAG, ELOOP_FLAG_DROPALL, NULL);
    failif((SYSERR != arpLookup(netptr, &praddr, &addrbuf)), "");

    /* Stop loopback ethernet and network interface */
    testPrint(verbose, "Test case cleanup");
    for (i = 0; i < ARP_NENTRY; i++)
    {
        arpFree(&arptab[i]);
    }
    failif(((SYSERR == netFreebuf(pkt)) || (SYSERR == netDown(ELOOP))
            || (SYSERR == close(ELOOP))), "");

    if (passed)
    {
        testPass(TRUE, "");
    }
    else
    {
        testFail(TRUE, "");
    }

    return OK;
}
Example #9
0
/**
 * @ingroup tftp
 *
 * Download a file from a remote server using TFTP and passes its contents,
 * block-by-block, to a callback function.  This callback function can do
 * whatever it wants with the file data, such as store it all into a buffer or
 * write it to persistent storage.
 *
 * @param[in] filename
 *      Name of the file to download.
 * @param[in] local_ip
 *      Local protocol address to use for the connection.
 * @param[in] server_ip
 *      Remote protocol address to use for the connection (address of TFTP
 *      server).
 * @param[in] recvDataFunc
 *      Callback function that will be passed the file data block-by-block.  For
 *      each call of the callback function, the @p data (first) argument will be
 *      set to a pointer to the next block of data and the @p len (second)
 *      argument will be set to the block's length.  All data blocks will be the
 *      same size, except possibly the last, which can be anywhere from 0 bytes
 *      up to the size of the previous block(s) if any.
 *      <br/>
 *      In the current implementation, the block size (other than possibly for
 *      the last block) is fixed at 512 bytes.  However, implementations of this
 *      callback SHOULD handle larger block sizes since tftpGet() could be
 *      extended to support TFTP block size negotiation.
 *      <br/>
 *      This callback is expected to return ::OK if successful.  If it does not
 *      return ::OK, the TFTP transfer is aborted and tftpGet() returns this
 *      value.
 * @param[in] recvDataCtx
 *      Extra parameter that will be passed literally to @p recvDataFunc.
 *
 * @return
 *      ::OK on success; ::SYSERR if the TFTP transfer times out or fails, or if
 *      one of several other errors occur; or the value returned by @p
 *      recvDataFunc, if it was not ::OK.
 */
syscall tftpGet(const char *filename, const struct netaddr *local_ip,
                const struct netaddr *server_ip, tftpRecvDataFunc recvDataFunc,
                void *recvDataCtx)
{
    int udpdev;
    int udpdev2;
    int send_udpdev;
    int recv_udpdev;
    int retval;
    tid_typ recv_tid;
    uint num_rreqs_sent;
    uint block_recv_tries;
    uint next_block_number;
    ushort localpt;
    uint block_max_end_time = 0;  /* This value is not used, but
                                     gcc fails to detect it.  */
    uint block_attempt_time;
    struct tftpPkt pkt;

    /* Make sure the required parameters have been specified.  */
    if (NULL == filename || NULL == local_ip ||
        NULL == server_ip || NULL == recvDataFunc)
    {
        TFTP_TRACE("Invalid parameter.");
        return SYSERR;
    }

#ifdef ENABLE_TFTP_TRACE
    {
        char str_local_ip[20];
        char str_server_ip[20];
        netaddrsprintf(str_local_ip, local_ip);
        netaddrsprintf(str_server_ip, server_ip);
        TFTP_TRACE("Downloading %s from %s (using local_ip=%s)",
                   filename, str_server_ip, str_local_ip);
    }
#endif

    /* Allocate and open a UDP device (socket) to communicate with the TFTP
     * server on.  The local and remote protocol addresses are specified as
     * required parameters to this function.  The local port, which corresponds
     * to the client's TFTP Transfer Identifier (TID) as per RFC 1350, must be
     * allocated randomly; the UDP code handles this if 0 is passed as the local
     * port.  The remote port is always initially the well-known TFTP port (69),
     * but after receiving the first data packet it must be changed to the port
     * on which the server actually responded.
     *
     * However... the last point about the server responding on a different port
     * (which is unavoidable; it's how TFTP is designed) complicates things
     * significantly.  This is because the UDP code will *not* route the
     * server's response to the initial UDP device, as this device will be bound
     * to port 69, not the actual port the server responded on.  To work around
     * this problem without manually dealing with UDP headers, we create a
     * *second* UDP device, which initially listens on the port from which the
     * client sends the initial RRQ, but is initially *not* bound to any remote
     * port or address.  We then set UDP_FLAG_BINDFIRST on this second UDP
     * device so that the remote port and address are automatically filled in
     * when the response from the server is received.  Further packets sent from
     * the server are then received on this second UDP device, while further
     * packets sent from the client are then sent over this second UDP device
     * rather than the first since the second has the remote port correctly set.
     * */

    udpdev = udpAlloc();

    if (SYSERR == udpdev)
    {
        TFTP_TRACE("Failed to allocate first UDP device.");
        return SYSERR;
    }

    if (SYSERR == open(udpdev, local_ip, server_ip, 0, UDP_PORT_TFTP))
    {
        TFTP_TRACE("Failed to open first UDP device.");
        udptab[udpdev - UDP0].state = UDP_FREE;
        return SYSERR;
    }

    localpt = udptab[udpdev - UDP0].localpt;

    udpdev2 = udpAlloc();
    if (SYSERR == udpdev2)
    {
        TFTP_TRACE("Failed to allocate second UDP device.");
        retval = SYSERR;
        goto out_close_udpdev;
    }

    if (SYSERR == open(udpdev2, local_ip, NULL, localpt, 0))
    {
        TFTP_TRACE("Failed to open second UDP device.");
        retval = SYSERR;
        udptab[udpdev2 - UDP0].state = UDP_FREE;
        goto out_close_udpdev;
    }

    send_udpdev = udpdev;
    recv_udpdev = udpdev2;

    /* See lengthy comment above for explanation of this flag.  */
    control(recv_udpdev, UDP_CTRL_SETFLAG, UDP_FLAG_BINDFIRST, 0);

    TFTP_TRACE("Using UDP%d (for initial send) "
               "and UDP%d (for binding reply), client port %u",
               send_udpdev - UDP0, recv_udpdev - UDP0, localpt);

    /* Create receive thread.  This is a workaround to avoid having the
     * currently executing thread call read() on the UDP device, which can block
     * indefinitely.  */
    recv_tid = create(tftpRecvPackets, TFTP_RECV_THR_STK,
                      TFTP_RECV_THR_PRIO, "tftpRecvPackets", 3,
                      recv_udpdev, &pkt, gettid());
    if (isbadtid(recv_tid))
    {
        TFTP_TRACE("Failed to create TFTP receive thread.");
        retval = SYSERR;
        goto out_close_udpdev2;
    }
    ready(recv_tid, RESCHED_NO);

    /* Begin the download by requesting the file.  */
    retval = tftpSendRRQ(send_udpdev, filename);
    if (SYSERR == retval)
    {
        retval = SYSERR;
        goto out_kill_recv_thread;
    }
    num_rreqs_sent = 1;
    next_block_number = 1;

    /* Loop until file is fully downloaded or an error condition occurs.  The
     * basic idea is that the client receives DATA packets one-by-one, each of
     * which corresponds to the next block of file data, and the client ACK's
     * each one before the server sends the next.  But the actual code below is
     * a bit more complicated as it must handle timeouts, retries, invalid
     * packets, etc.  */
    block_recv_tries = 0;
    for (;;)
    {
        ushort opcode;
        ushort recv_block_number;
        struct netaddr *remote_address;
        bool wrong_source;
        ushort block_nbytes;

        /* Handle bookkeeping for timing out.  */

        block_attempt_time = clktime;
        if (block_recv_tries == 0)
        {
            uint timeout_secs;

            if (next_block_number == 1)
            {
                timeout_secs = TFTP_INIT_BLOCK_TIMEOUT;
            }
            else
            {
                timeout_secs = TFTP_BLOCK_TIMEOUT;
            }
            block_max_end_time = block_attempt_time + timeout_secs;
        }

        if (block_attempt_time <= block_max_end_time)
        {
            /* Try to receive the block using the appropriate timeout.  The
             * actual receive is done by another thread, executing
             * tftpRecvPacket(s).  */
            TFTP_TRACE("Waiting for block %u", next_block_number);
            block_recv_tries++;
            send(recv_tid, 0);
            retval = recvtime(1000 * (block_max_end_time -
                                      block_attempt_time) + 500);
        }
        else
        {
            /* Timeout was reached.  */
            retval = TIMEOUT;
        }

        /* Handle timeout.  */
        if (TIMEOUT == retval)
        {
            TFTP_TRACE("Receive timed out.");

            /* If the client is still waiting for the very first reply from the
             * server, don't fail on the first timeout; instead wait until the
             * client has had the chance to re-send the RRQ a few times.  */
            if (next_block_number == 1 &&
                num_rreqs_sent < TFTP_INIT_BLOCK_MAX_RETRIES)
            {
                TFTP_TRACE("Trying RRQ again (try %u of %u)",
                           num_rreqs_sent + 1, TFTP_INIT_BLOCK_MAX_RETRIES);
                retval = tftpSendRRQ(send_udpdev, filename);
                if (SYSERR == retval)
                {
                    break;
                }
                block_recv_tries = 0;
                num_rreqs_sent++;
                continue;
            }

            /* Timed out for real; clean up and return failure status.  */
            retval = SYSERR;
            break;
        }

        /* Return failure status if packet was not otherwise successfully
         * received for some reason.  */
        if (SYSERR == retval)
        {
            TFTP_TRACE("UDP device or message passing error; aborting.");
            break;
        }

        /* Otherwise, 'retval' is the length of the received TFTP packet.  */

        /* Begin extracting information from and validating the received packet.
         * What we're looking for is a well-formed TFTP DATA packet from the
         * correct IP address, where the block number is either that of the next
         * block or that of the previous block.  The very first block needs some
         * special handling, however; in particular, there is no previous block
         * in that case, and the remote network address needs to be checked to
         * verify the socket was actually bound to the server's network address
         * as expected.
         */
        remote_address = &udptab[recv_udpdev - UDP0].remoteip;
        opcode = net2hs(pkt.opcode);
        recv_block_number = net2hs(pkt.DATA.block_number);
        wrong_source = !netaddrequal(server_ip, remote_address);

        if (wrong_source || retval < 4 || TFTP_OPCODE_DATA != opcode ||
            (recv_block_number != (ushort)next_block_number &&
             (next_block_number == 1 ||
              recv_block_number != (ushort)next_block_number - 1)))
        {
            /* Check for TFTP ERROR packet  */
            if (!wrong_source && (retval >= 2 && TFTP_OPCODE_ERROR == opcode))
            {
                TFTP_TRACE("Received TFTP ERROR opcode packet; aborting.");
                retval = SYSERR;
                break;
            }
            TFTP_TRACE("Received invalid or unexpected packet.");

            /* If we're still waiting for the first valid reply from the server
             * but the bound connection is *not* from the server, reset the
             * BINDFIRST flag.  */
            if (wrong_source && next_block_number == 1)
            {
                irqmask im;
                TFTP_TRACE("Received packet is from wrong source; "
                           "re-setting bind flag.");
                im = disable();
                control(recv_udpdev, UDP_CTRL_BIND, 0, (long)NULL);
                control(recv_udpdev, UDP_CTRL_SETFLAG, UDP_FLAG_BINDFIRST, 0);
                restore(im);
            }

            /* Ignore the bad packet and try receiving again.  */
            continue;
        }

        /* Received packet is a valid TFTP DATA packet for either the next block
         * or the previous block.  */


    #if TFTP_DROP_PACKET_PERCENT != 0
        /* Stress testing.  */
        if (rand() % 100 < TFTP_DROP_PACKET_PERCENT)
        {
            TFTP_TRACE("WARNING: Ignoring valid TFTP packet for test purposes.");
            continue;
        }
    #endif

        /* If this is the first response from the server, set the actual port
         * that it responded on.  */
        if (next_block_number == 1)
        {
            send_udpdev = recv_udpdev;
            TFTP_TRACE("Server responded on port %u; bound socket",
                       udptab[recv_udpdev - UDP0].remotept);
        }

        /* Handle receiving the next data block.  */
        block_nbytes = TFTP_BLOCK_SIZE;
        if (recv_block_number == (ushort)next_block_number)
        {
            block_nbytes = retval - 4;
            TFTP_TRACE("Received block %u (%u bytes)",
                       recv_block_number, block_nbytes);

            /* Feed received data into the callback function.  */
            retval = (*recvDataFunc)(pkt.DATA.data, block_nbytes,
                                     recvDataCtx);
            /* Return if callback did not return OK.  */
            if (OK != retval)
            {
                break;
            }
            next_block_number++;
            block_recv_tries = 0;
        }

        /* Acknowledge the block received.  */
        retval = tftpSendACK(send_udpdev, recv_block_number);

        /* A TFTP Get transfer is complete when a short data block has been
         * received.   Note that it doesn't really matter from the client's
         * perspective whether the last data block is acknowledged or not;
         * however, the server would like to know so it doesn't keep re-sending
         * the last block.  For this reason we did send the final ACK packet but
         * will ignore failure to send it.  */
        if (block_nbytes < TFTP_BLOCK_SIZE)
        {
            retval = OK;
            break;
        }

        /* Break if sending the ACK failed.  */
        if (SYSERR == retval)
        {
            break;
        }
    }
    /* Clean up and return.  */
out_kill_recv_thread:
    kill(recv_tid);
out_close_udpdev2:
    close(udpdev2);
out_close_udpdev:
    close(udpdev);
    return retval;
}
/*------------------------------------------------------------------------
 * arp_resolve  -  Use ARP to resolve an IP address to an Ethernet address
 *------------------------------------------------------------------------
 */
status	arp_resolve (
    uint32	nxthop,			/* Next-hop address to resolve	*/
    byte	mac[ETH_ADDR_LEN]	/* Array into which Ethernet	*/
)				/*   address should be placed	*/
{
    intmask	mask;			/* Saved interrupt mask		*/
    struct	arppacket apkt;		/* Local packet buffer		*/
    int32	i;			/* Index into arpcache		*/
    int32	slot;			/* ARP table slot to use	*/
    struct	arpentry  *arptr;	/* Ptr to ARP cache entry	*/
    int32	msg;			/* Message returned by recvtime	*/

    /* Use MAC broadcast address for IP limited broadcast */

    if (nxthop == IP_BCAST) {
        memcpy(mac, NetData.ethbcast, ETH_ADDR_LEN);
        return OK;
    }

    /* Use MAC broadcast address for IP network broadcast */

    if (nxthop == NetData.ipbcast) {
        memcpy(mac, NetData.ethbcast, ETH_ADDR_LEN);
        return OK;
    }

    /* Ensure only one process uses ARP at a time */

    mask = disable();

    /* See if next hop address is already present in ARP cache */

    for (i=0; i<ARP_SIZ; i++) {
        arptr = &arpcache[i];
        if (arptr->arstate == AR_FREE) {
            continue;
        }
        if (arptr->arpaddr == nxthop) { /* Adddress is in cache	*/
            break;
        }
    }

    if (i < ARP_SIZ) {	/* Entry was found */

        /* If entry is resolved - handle and return */

        if (arptr->arstate == AR_RESOLVED) {
            memcpy(mac, arptr->arhaddr, ARP_HALEN);
            restore(mask);
            return OK;
        }

        /* Entry is already pending -  return error because	*/
        /*	only one process can be	waiting at a time	*/

        if (arptr->arstate == AR_PENDING) {
            restore(mask);
            return SYSERR;
        }
    }

    /* IP address not in cache -  allocate a new cache entry and	*/
    /*	send an ARP request to obtain the answer		*/

    slot = arp_alloc();
    if (slot == SYSERR) {
        restore(mask);
        return SYSERR;
    }

    arptr = &arpcache[slot];
    arptr->arstate = AR_PENDING;
    arptr->arpaddr = nxthop;
    arptr->arpid = currpid;

    /* Hand-craft an ARP Request packet */

    memcpy(apkt.arp_ethdst, NetData.ethbcast, ETH_ADDR_LEN);
    memcpy(apkt.arp_ethsrc, NetData.ethucast, ETH_ADDR_LEN);
    apkt.arp_ethtype = ETH_ARP;	  /* Packet type is ARP		*/
    apkt.arp_htype = ARP_HTYPE;	  /* Hardware type is Ethernet	*/
    apkt.arp_ptype = ARP_PTYPE;	  /* Protocol type is IP	*/
    apkt.arp_hlen = 0xff & ARP_HALEN; /* Ethernet MAC size in bytes	*/
    apkt.arp_plen = 0xff & ARP_PALEN; /* IP address size in bytes	*/
    apkt.arp_op = 0xffff & ARP_OP_REQ;/* ARP type is Request	*/
    memcpy(apkt.arp_sndha, NetData.ethucast, ARP_HALEN);
    apkt.arp_sndpa = NetData.ipucast; /* IP address of interface	*/
    memset(apkt.arp_tarha, '\0', ARP_HALEN); /* Target HA is unknown*/
    apkt.arp_tarpa = nxthop;	  /* Target protocol address	*/

    /* Convert ARP packet from host to net byte order */

    arp_hton(&apkt);

    /* Convert Ethernet header from host to net byte order */

    eth_hton((struct netpacket *)&apkt);

    /* Send the packet ARP_RETRY times and await response */

    msg = recvclr();
    for (i=0; i<ARP_RETRY; i++) {
        write(ETHER0, (char *)&apkt, sizeof(struct arppacket));
        msg = recvtime(ARP_TIMEOUT);
        if (msg == TIMEOUT) {
            continue;
        } else if (msg == SYSERR) {
            restore(mask);
            return SYSERR;
        } else {	/* entry is resolved */
            break;
        }
    }

    /* If no response, return TIMEOUT */

    if (msg == TIMEOUT) {
        arptr->arstate = AR_FREE;   /* Invalidate cache entry */
        restore(mask);
        return TIMEOUT;
    }

    /* Return hardware address */

    memcpy(mac, arptr->arhaddr, ARP_HALEN);
    restore(mask);
    return OK;
}