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; }
/** * 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; }
/*------------------------------------------------------------------------ * 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; }
/** * @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; }
/** * 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; } }
/** * 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; }
/** * @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; }