xref: /freebsd/stand/libsa/arp.c (revision d0b2dbfa)
1 /*	$NetBSD: arp.c,v 1.18 1997/07/07 15:52:49 drochner Exp $	*/
2 
3 /*
4  * Copyright (c) 1992 Regents of the University of California.
5  * All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * @(#) Header: arp.c,v 1.5 93/07/15 05:52:26 leres Exp  (LBL)
36  */
37 
38 #include <sys/cdefs.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netinet/if_ether.h>
44 
45 #include <netinet/in_systm.h>
46 
47 #include <string.h>
48 
49 #include "stand.h"
50 #include "net.h"
51 
52 /* Cache stuff */
53 #define ARP_NUM 8			/* need at most 3 arp entries */
54 
55 struct arp_list {
56 	struct in_addr	addr;
57 	u_char		ea[6];
58 } arp_list[ARP_NUM] = {
59 	/* XXX - net order `INADDR_BROADCAST' must be a constant */
60 	{ {0xffffffff}, BA }
61 };
62 int arp_num = 1;
63 
64 /* Local forwards */
65 static	ssize_t arpsend(struct iodesc *, void *, size_t);
66 static	ssize_t arprecv(struct iodesc *, void **, void **, time_t, void *);
67 
68 /* Broadcast an ARP packet, asking who has addr on interface d */
69 u_char *
70 arpwhohas(struct iodesc *d, struct in_addr addr)
71 {
72 	int i;
73 	struct ether_arp *ah;
74 	struct arp_list *al;
75 	void *pkt;
76 	struct {
77 		struct ether_header eh;
78 		struct {
79 			struct ether_arp arp;
80 			u_char pad[18]; 	/* 60 - sizeof(...) */
81 		} data;
82 	} wbuf;
83 
84 	/* Try for cached answer first */
85 	for (i = 0, al = arp_list; i < arp_num; ++i, ++al)
86 		if (addr.s_addr == al->addr.s_addr)
87 			return (al->ea);
88 
89 	/* Don't overflow cache */
90 	if (arp_num > ARP_NUM - 1) {
91 		arp_num = 1;	/* recycle */
92 		printf("arpwhohas: overflowed arp_list!\n");
93 	}
94 
95 #ifdef ARP_DEBUG
96  	if (debug)
97  	    printf("arpwhohas: send request for %s\n", inet_ntoa(addr));
98 #endif
99 
100 	bzero((char*)&wbuf.data, sizeof(wbuf.data));
101 	ah = &wbuf.data.arp;
102 	ah->arp_hrd = htons(ARPHRD_ETHER);
103 	ah->arp_pro = htons(ETHERTYPE_IP);
104 	ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */
105 	ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */
106 	ah->arp_op = htons(ARPOP_REQUEST);
107 	MACPY(d->myea, ah->arp_sha);
108 	bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa));
109 	/* Leave zeros in arp_tha */
110 	bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa));
111 
112 	/* Store ip address in cache (incomplete entry). */
113 	al->addr = addr;
114 
115 	pkt = NULL;
116 	ah = NULL;
117 	i = sendrecv(d,
118 	    arpsend, &wbuf.data, sizeof(wbuf.data),
119 	    arprecv, &pkt, (void **)&ah, NULL);
120 	if (i == -1) {
121 		panic("arp: no response for %s",
122 			  inet_ntoa(addr));
123 	}
124 
125 	/* Store ethernet address in cache */
126 #ifdef ARP_DEBUG
127  	if (debug) {
128 		struct ether_header *eh;
129 
130 		eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN);
131 		printf("arp: response from %s\n",
132 		    ether_sprintf(eh->ether_shost));
133 		printf("arp: cacheing %s --> %s\n",
134 		    inet_ntoa(addr), ether_sprintf(ah->arp_sha));
135 	}
136 #endif
137 	MACPY(ah->arp_sha, al->ea);
138 	++arp_num;
139 
140 	free(pkt);
141 	return (al->ea);
142 }
143 
144 static ssize_t
145 arpsend(struct iodesc *d, void *pkt, size_t len)
146 {
147 
148 #ifdef ARP_DEBUG
149  	if (debug)
150 		printf("arpsend: called\n");
151 #endif
152 
153 	return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP));
154 }
155 
156 /*
157  * Returns 0 if this is the packet we're waiting for
158  * else -1 (and errno == 0)
159  */
160 static ssize_t
161 arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra)
162 {
163 	ssize_t n;
164 	struct ether_arp *ah;
165 	uint16_t etype;	/* host order */
166 	void *ptr;
167 
168 #ifdef ARP_DEBUG
169  	if (debug)
170 		printf("arprecv: ");
171 #endif
172 
173 	ptr = NULL;
174 	n = readether(d, &ptr, (void **)&ah, tleft, &etype);
175 	errno = 0;	/* XXX */
176 	if (n == -1 || n < sizeof(struct ether_arp)) {
177 #ifdef ARP_DEBUG
178 		if (debug)
179 			printf("bad len=%zd\n", n);
180 #endif
181 		free(ptr);
182 		return (-1);
183 	}
184 
185 	if (etype != ETHERTYPE_ARP) {
186 #ifdef ARP_DEBUG
187 		if (debug)
188 			printf("not arp type=%d\n", etype);
189 #endif
190 		free(ptr);
191 		return (-1);
192 	}
193 
194 	/* Ethernet address now checked in readether() */
195 	if (ah->arp_hrd != htons(ARPHRD_ETHER) ||
196 	    ah->arp_pro != htons(ETHERTYPE_IP) ||
197 	    ah->arp_hln != sizeof(ah->arp_sha) ||
198 	    ah->arp_pln != sizeof(ah->arp_spa) )
199 	{
200 #ifdef ARP_DEBUG
201 		if (debug)
202 			printf("bad hrd/pro/hln/pln\n");
203 #endif
204 		free(ptr);
205 		return (-1);
206 	}
207 
208 	if (ah->arp_op == htons(ARPOP_REQUEST)) {
209 #ifdef ARP_DEBUG
210 		if (debug)
211 			printf("is request\n");
212 #endif
213 		arp_reply(d, ah);
214 		free(ptr);
215 		return (-1);
216 	}
217 
218 	if (ah->arp_op != htons(ARPOP_REPLY)) {
219 #ifdef ARP_DEBUG
220 		if (debug)
221 			printf("not ARP reply\n");
222 #endif
223 		free(ptr);
224 		return (-1);
225 	}
226 
227 	/* Is the reply from the source we want? */
228 	if (bcmp(&arp_list[arp_num].addr,
229 			 ah->arp_spa, sizeof(ah->arp_spa)))
230 	{
231 #ifdef ARP_DEBUG
232 		if (debug)
233 			printf("unwanted address\n");
234 #endif
235 		free(ptr);
236 		return (-1);
237 	}
238 	/* We don't care who the reply was sent to. */
239 
240 	/* We have our answer. */
241 #ifdef ARP_DEBUG
242  	if (debug)
243 		printf("got it\n");
244 #endif
245 	*pkt = ptr;
246 	*payload = ah;
247 	return (n);
248 }
249 
250 /*
251  * Convert an ARP request into a reply and send it.
252  * Notes:  Re-uses buffer.  Pad to length = 46.
253  */
254 void
255 arp_reply(struct iodesc *d, void *pkt)
256 {
257 	struct ether_arp *arp = pkt;
258 
259 	if (arp->arp_hrd != htons(ARPHRD_ETHER) ||
260 	    arp->arp_pro != htons(ETHERTYPE_IP) ||
261 	    arp->arp_hln != sizeof(arp->arp_sha) ||
262 	    arp->arp_pln != sizeof(arp->arp_spa) )
263 	{
264 #ifdef ARP_DEBUG
265 		if (debug)
266 			printf("arp_reply: bad hrd/pro/hln/pln\n");
267 #endif
268 		return;
269 	}
270 
271 	if (arp->arp_op != htons(ARPOP_REQUEST)) {
272 #ifdef ARP_DEBUG
273 		if (debug)
274 			printf("arp_reply: not request!\n");
275 #endif
276 		return;
277 	}
278 
279 	/* If we are not the target, ignore the request. */
280 	if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa)))
281 		return;
282 
283 #ifdef ARP_DEBUG
284 	if (debug) {
285 		printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha));
286 	}
287 #endif
288 
289 	arp->arp_op = htons(ARPOP_REPLY);
290 	/* source becomes target */
291 	bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha));
292 	bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa));
293 	/* here becomes source */
294 	bcopy(d->myea,  arp->arp_sha, sizeof(arp->arp_sha));
295 	bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa));
296 
297 	/*
298 	 * No need to get fancy here.  If the send fails, the
299 	 * requestor will just ask again.
300 	 */
301 	(void) sendether(d, pkt, sizeof(*arp) + 18,
302 	                 arp->arp_tha, ETHERTYPE_ARP);
303 }
304