/* $NetBSD: arp.c,v 1.4 1994/10/27 04:21:01 cgd Exp $ */ /* * source in this file came from * the Mach ethernet boot written by Leendert van Doorn. * * Ethernet (Reverse) Address Resolution Protocol (see RFC 903, and 826). * No doubt this code is overkill, but I had it lying around. * * Copyright (c) 1992 by Leendert van Doorn */ #include "proto.h" #include "assert.h" #include "param.h" #include "packet.h" #include "ether.h" #include "inet.h" #include "arp.h" #include "bootp.h" #include "tftp.h" static u_char bcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static arptab_t arptab[ARPTAB_SIZE]; extern u_char vendor_area[64]; ipaddr_t ip_myaddr = IP_ANYADDR; ipaddr_t ip_gateway = IP_ANYADDR; #ifdef USE_RARP /* * Broadcast a RARP request (i.e. who knows who I am) */ static void RarpWhoAmI(void) { arphdr_t *ap; packet_t *pkt; pkt = PktAlloc(sizeof(ethhdr_t)); pkt->pkt_len = sizeof(arphdr_t); ap = (arphdr_t *) pkt->pkt_offset; ap->arp_hrd = htons(ARPHRD_ETHER); ap->arp_pro = htons(ETHTYPE_IP); ap->arp_hln = ETH_ADDRSIZE; ap->arp_pln = sizeof(ipaddr_t); ap->arp_op = htons(REVARP_REQUEST); bcopy((char *)eth_myaddr, (char *)ap->arp_sha, ETH_ADDRSIZE); bcopy((char *)eth_myaddr, (char *)ap->arp_tha, ETH_ADDRSIZE); EtherSend(pkt, ETHTYPE_RARP, bcastaddr); PktRelease(pkt); } #endif #ifdef USE_BOOTP static int saved_bootp_xid; /* from last bootp req */ extern int time_zero; /* * Broadcast a BOOTP request (i.e. who knows who I am) */ static void BootpWhoAmI(void) { struct bootp *bp; packet_t *pkt; udphdr_t *up; pkt = PktAlloc(sizeof(ethhdr_t)+sizeof(iphdr_t)); pkt->pkt_len = sizeof(ethhdr_t) + sizeof(iphdr_t) + sizeof(udphdr_t) +sizeof(struct bootp); up = (udphdr_t *) pkt->pkt_offset; bp = (struct bootp *) ((char *)up + sizeof(udphdr_t)); up->uh_dport = htons(IPPORT_BOOTPS); up->uh_len = htons(sizeof(udphdr_t) + sizeof(struct bootp)); bp->bp_op = BOOTREQUEST; bp->bp_htype = 1; bp->bp_hlen = ETH_ADDRSIZE; bp->bp_xid = saved_bootp_xid = rand(); bp->bp_secs = htons(timer() - time_zero); bcopy((char *)eth_myaddr, (char *)bp->bp_chaddr, ETH_ADDRSIZE); IpSend(pkt, IP_BCASTADDR, IP_ANYADDR); PktInit(); } #endif extern ipaddr_t tftp_gateway; extern ipaddr_t tftp_server; #ifdef USE_RARP /* * Called when packet containing RARP is received */ static inline ipaddr_t RarpInput(packet_t *pkt, ipaddr_t *server) { ipaddr_t ipaddr; ethhdr_t *ep; ep = (ethhdr_t *)pkt->pkt_offset; /* is rarp? */ if (pkt->pkt_len >= sizeof(arphdr_t) && ntohs(ep->eth_proto) == ETHTYPE_RARP) { ipaddr_t ipa; arphdr_t *ap; ap = (arphdr_t *) (pkt->pkt_offset + sizeof(ethhdr_t)); if (ntohs(ap->arp_op) != REVARP_REPLY || ntohs(ap->arp_pro) != ETHTYPE_IP) return 0; if (bcmp(ap->arp_tha, eth_myaddr, ETH_ADDRSIZE)) return 0; bcopy((char *)ap->arp_tpa, (char *)&ipaddr, sizeof(ipaddr_t)); printf("From RARP server "); bcopy((char *)ap->arp_spa, (char *)&ipa, sizeof(ipaddr_t)); IpPrintAddr(ipa); printf(": using IP address "); IpPrintAddr(ipaddr); if (server) { bcopy((char *)ap->arp_spa, (char *)server, sizeof(ipaddr_t)); printf(",\n tftp server "); IpPrintAddr(*server); } printf("\n"); return ipaddr; } return 0; } #endif #ifdef USE_BOOTP static inline ipaddr_t BootpInput(packet_t *pkt, ipaddr_t *server, ipaddr_t *gateway, char *filename) { ipaddr_t ipaddr; ethhdr_t *ep; ep = (ethhdr_t *)pkt->pkt_offset; if (pkt->pkt_len < sizeof(iphdr_t)+sizeof(udphdr_t)+sizeof(struct bootp)) return 0; if (ntohs(ep->eth_proto) == ETHTYPE_IP) { iphdr_t *ip; udphdr_t *up; struct bootp *bp; ip = (iphdr_t *) ((char *)ep + sizeof(ethhdr_t)); up = (udphdr_t *) ((char *)ip + sizeof(iphdr_t)); bp = (struct bootp *) ((char *)up + sizeof(udphdr_t)); #if 0 DUMP_STRUCT("eboot", ep, 100); printf("pktlen %d of %d\n\n", pkt->pkt_len, sizeof(iphdr_t)+sizeof(udphdr_t)+sizeof(struct bootp)); #endif if (ip->ip_p != IP_PROTO_UDP) { return 0; } if (up->uh_dport != htons(IPPORT_BOOTPC)) { return 0; } if (bp->bp_xid != saved_bootp_xid) { return 0; } /* passed all checks - is the packet we expected */ ipaddr = bp->bp_yiaddr; printf("From BOOTP server "); IpPrintAddr(ip->ip_src); printf(": using IP address "); IpPrintAddr(bp->bp_yiaddr); if (server) { *server = bp->bp_siaddr; printf(",\n tftp server "); IpPrintAddr(bp->bp_siaddr); } if (bp->bp_giaddr) { *gateway = bp->bp_giaddr; printf(",\n gateway "); IpPrintAddr(bp->bp_giaddr); } if (*bp->bp_file) { bcopy((char *)bp->bp_file, filename, MAX_FILE_NAME_LEN-1); printf(",\n file '%s'", bp->bp_file); } bcopy((char *)bp->bp_vend, (char *)vendor_area, sizeof(vendor_area)); printf("\n"); PktInit(); return ipaddr; } return 0; } #endif /* * Using the BOOTP and/or RARP request/reply exchange we try to obtain our * internet address (see RFC 903). */ ipaddr_t GetIpAddress(ipaddr_t *serv_addr, ipaddr_t *myaddr, ipaddr_t *gateway, char *filename) { u_long time, current, timeout; int retry; packet_t *pkt; int spin = 0; #if TRACE > 0 printe("GetIpAddress: Requesting IP address for "); EtherPrintAddr(eth_myaddr); printe("\n"); #endif timeout = 4; /* four seconds */ for (retry = 0; retry < NRETRIES; retry++) { #ifdef USE_RARP RarpWhoAmI(); #endif #ifdef USE_BOOTP BootpWhoAmI(); #endif printf("%c\b", "-\\|/"[spin++ % 4]); time = timer() + timeout; do { pkt = EtherReceive(); if (pkt) { *myaddr = 0; #ifdef USE_RARP *myaddr = RarpInput(pkt, serv_addr); #endif #ifdef USE_BOOTP if (!*myaddr) *myaddr = BootpInput(pkt, serv_addr, gateway, filename); #endif PktRelease(pkt); if (*myaddr) { return 1; } } HandleKbdAttn(); current = timer(); } while (current < time); EtherReset(); timeout <<= 1; } printf("No response for " #ifdef USE_BOOTP "BOOTP " #endif #ifdef USE_RARP "RARP " #endif "request\n"); return IP_ANYADDR; } /* * Broadcast an ARP packet (i.e. ask who has address "addr") */ static void ArpWhoHas(ipaddr_t addr) { arphdr_t *ap; packet_t *pkt; pkt = PktAlloc(sizeof(ethhdr_t)); pkt->pkt_len = sizeof(arphdr_t); ap = (arphdr_t *) pkt->pkt_offset; ap->arp_hrd = htons(ARPHRD_ETHER); ap->arp_pro = htons(ETHTYPE_IP); ap->arp_hln = ETH_ADDRSIZE; ap->arp_pln = sizeof(ipaddr_t); ap->arp_op = htons(ARPOP_REQUEST); bcopy((char *)eth_myaddr, (char *)ap->arp_sha, ETH_ADDRSIZE); bcopy((char *)&ip_myaddr, (char *)ap->arp_spa, sizeof(ipaddr_t)); bcopy((char *)&addr, (char *)ap->arp_tpa, sizeof(ipaddr_t)); #if TRACE > 0 printe("ArpWhoHas:\n"); DUMP_STRUCT("arphdr_t", ap, sizeof(arphdr_t)); #endif EtherSend(pkt, ETHTYPE_ARP, bcastaddr); PktRelease(pkt); } /* * Free an arptab entry */ static void ArpTfree(arptab_t *at) { if (at->at_hold) PktRelease(at->at_hold); at->at_hold = (packet_t *)0; at->at_timer = at->at_flags = 0; at->at_ipaddr = 0; } /* * Enter a new address in arptab, pushing out the oldest entry * from the bucket if there is no room. */ static arptab_t * ArpTnew(ipaddr_t addr) { u_short n; u_long oldest; arptab_t *at, *ato; oldest = ~0; ato = at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) { if (at->at_flags == 0) goto out; /* found an empty entry */ if (at->at_timer < oldest) { oldest = at->at_timer; ato = at; } } at = ato; ArpTfree(at); out: at->at_ipaddr = addr; at->at_flags = ATF_INUSE; return at; } /* * Resolve an IP address into a hardware address. If success, * destha is filled in and 1 is returned. If there is no entry * in arptab, set one up and broadcast a request * for the IP address; return 0. Hold onto this packet and * resend it once the address is finally resolved. */ int ArpResolve(packet_t *pkt, ipaddr_t destip, u_char *destha) { arptab_t *at; u_long lna = ntohl(destip) & 0xFF; if (lna == 0xFF || lna == 0x0) { /* broadcast address */ bcopy((char *)bcastaddr, (char *)destha, ETH_ADDRSIZE); return 1; } ARPTAB_LOOK(at, destip); if (at == 0) { at = ArpTnew(destip); at->at_hold = pkt; ArpWhoHas(destip); return 0; } at->at_timer = timer(); /* restart the timer */ if (at->at_flags & ATF_COM) { /* entry is complete */ bcopy((char *)at->at_eaddr, (char *)destha, ETH_ADDRSIZE); return 1; } /* * There is an arptab entry, but no hardware address * response yet. Replace the held packet with this * latest one. */ if (at->at_hold) PktRelease(at->at_hold); at->at_hold = pkt; ArpWhoHas(destip); return 0; } /* * Called when packet containing ARP is received. * Algorithm is that given in RFC 826. */ void ArpInput(packet_t *pkt) { arphdr_t *ap; arptab_t *at; packet_t *phold; ipaddr_t isaddr, itaddr; #if 0 T(ArpInput); #endif if (pkt->pkt_len < sizeof(arphdr_t)) { #if 0 printf("ArpInput: bad packet size %d\n", pkt->pkt_len); #endif return; } ap = (arphdr_t *) (pkt->pkt_offset + sizeof(ethhdr_t)); #if 0 DUMP_STRUCT("arphdr_t", ap, sizeof(arphdr_t)); #endif if (ntohs(ap->arp_pro) != ETHTYPE_IP) { #if 0 printf("ArpInput: incorrect proto addr %x\n", ap->arp_pro); #endif return; } bcopy((char *)ap->arp_spa, (char *)&isaddr, sizeof(ipaddr_t)); bcopy((char *)ap->arp_tpa, (char *)&itaddr, sizeof(ipaddr_t)); if (!bcmp(ap->arp_sha, eth_myaddr, ETH_ADDRSIZE)) { #if 0 printf("ArpInput: incorrect sender h/w addr "); EtherPrintAddr(ap->arp_sha); printf("/n"); #endif return; } at = (arptab_t *)0; ARPTAB_LOOK(at, isaddr); if (at) { bcopy((char *)ap->arp_sha, (char *)at->at_eaddr, ETH_ADDRSIZE); at->at_flags |= ATF_COM; if (at->at_hold) { phold = at->at_hold; at->at_hold = (packet_t *)0; #if 0 printf("ArpInput: found addr, releasing packet\n"); #endif EtherSend(phold, ETHTYPE_IP, at->at_eaddr); PktRelease(phold); } } /* * Only answer ARP request which are for me */ if (itaddr != ip_myaddr) { #if 0 printf("ArpInput: it addr "); IpPrintAddr(itaddr); printf(" somebody else\n"); #endif return; } if (at == 0) { /* ensure we have a table entry */ at = ArpTnew(isaddr); bcopy((char *)ap->arp_sha, (char *)at->at_eaddr, ETH_ADDRSIZE); at->at_flags |= ATF_COM; } if (ntohs(ap->arp_op) != ARPOP_REQUEST) { printf("ArpInput: incorrect operation: 0x%x\n", ntohs(ap->arp_op)); return; } bcopy((char *)ap->arp_sha, (char *)ap->arp_tha, ETH_ADDRSIZE); bcopy((char *)ap->arp_spa, (char *)ap->arp_tpa, sizeof(ipaddr_t)); bcopy((char *)eth_myaddr, (char *)ap->arp_sha, ETH_ADDRSIZE); bcopy((char *)&itaddr, (char *)ap->arp_spa, sizeof(ipaddr_t)); ap->arp_op = htons(ARPOP_REPLY); #if 0 printf("ArpInput: valid request rec'd, replying\n"); #endif EtherSend(pkt, ETHTYPE_ARP, ap->arp_tha); }