summaryrefslogtreecommitdiff
path: root/sys/arch/i386/netboot/arp.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/i386/netboot/arp.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/i386/netboot/arp.c')
-rw-r--r--sys/arch/i386/netboot/arp.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/sys/arch/i386/netboot/arp.c b/sys/arch/i386/netboot/arp.c
new file mode 100644
index 00000000000..53285cb1966
--- /dev/null
+++ b/sys/arch/i386/netboot/arp.c
@@ -0,0 +1,459 @@
+/* $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);
+}