xref: /freebsd/stand/libsa/arp.c (revision 7c43148a974877188a930e4078a164f83da8e652)
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 
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <net/if.h>
39 #include <netinet/in.h>
40 #include <netinet/if_ether.h>
41 
42 #include <netinet/in_systm.h>
43 
44 #include <string.h>
45 
46 #include "stand.h"
47 #include "net.h"
48 
49 /* Cache stuff */
50 #define ARP_NUM 8			/* need at most 3 arp entries */
51 
52 struct arp_list {
53 	struct in_addr	addr;
54 	u_char		ea[6];
55 } arp_list[ARP_NUM] = {
56 	/* XXX - net order `INADDR_BROADCAST' must be a constant */
57 	{ {0xffffffff}, BA }
58 };
59 int arp_num = 1;
60 
61 /* Local forwards */
62 static	ssize_t arpsend(struct iodesc *, void *, size_t);
63 static	ssize_t arprecv(struct iodesc *, void **, void **, time_t, void *);
64 
65 /* Broadcast an ARP packet, asking who has addr on interface d */
66 u_char *
arpwhohas(struct iodesc * d,struct in_addr addr)67 arpwhohas(struct iodesc *d, struct in_addr addr)
68 {
69 	int i;
70 	struct ether_arp *ah;
71 	struct arp_list *al;
72 	void *pkt;
73 	struct {
74 		struct ether_header eh;
75 		struct {
76 			struct ether_arp arp;
77 			u_char pad[18]; 	/* 60 - sizeof(...) */
78 		} data;
79 	} wbuf;
80 
81 	/* Try for cached answer first */
82 	for (i = 0, al = arp_list; i < arp_num; ++i, ++al)
83 		if (addr.s_addr == al->addr.s_addr)
84 			return (al->ea);
85 
86 	/* Don't overflow cache */
87 	if (arp_num > ARP_NUM - 1) {
88 		arp_num = 1;	/* recycle */
89 		printf("arpwhohas: overflowed arp_list!\n");
90 	}
91 
92 #ifdef ARP_DEBUG
93  	if (debug)
94  	    printf("arpwhohas: send request for %s\n", inet_ntoa(addr));
95 #endif
96 
97 	bzero((char*)&wbuf.data, sizeof(wbuf.data));
98 	ah = &wbuf.data.arp;
99 	ah->arp_hrd = htons(ARPHRD_ETHER);
100 	ah->arp_pro = htons(ETHERTYPE_IP);
101 	ah->arp_hln = sizeof(ah->arp_sha); /* hardware address length */
102 	ah->arp_pln = sizeof(ah->arp_spa); /* protocol address length */
103 	ah->arp_op = htons(ARPOP_REQUEST);
104 	MACPY(d->myea, ah->arp_sha);
105 	bcopy(&d->myip, ah->arp_spa, sizeof(ah->arp_spa));
106 	/* Leave zeros in arp_tha */
107 	bcopy(&addr, ah->arp_tpa, sizeof(ah->arp_tpa));
108 
109 	/* Store ip address in cache (incomplete entry). */
110 	al->addr = addr;
111 
112 	pkt = NULL;
113 	ah = NULL;
114 	i = sendrecv(d,
115 	    arpsend, &wbuf.data, sizeof(wbuf.data),
116 	    arprecv, &pkt, (void **)&ah, NULL);
117 	if (i == -1) {
118 		panic("arp: no response for %s",
119 			  inet_ntoa(addr));
120 	}
121 
122 	/* Store ethernet address in cache */
123 #ifdef ARP_DEBUG
124  	if (debug) {
125 		struct ether_header *eh;
126 
127 		eh = (struct ether_header *)((uintptr_t)pkt + ETHER_ALIGN);
128 		printf("arp: response from %s\n",
129 		    ether_sprintf(eh->ether_shost));
130 		printf("arp: cacheing %s --> %s\n",
131 		    inet_ntoa(addr), ether_sprintf(ah->arp_sha));
132 	}
133 #endif
134 	MACPY(ah->arp_sha, al->ea);
135 	++arp_num;
136 
137 	free(pkt);
138 	return (al->ea);
139 }
140 
141 static ssize_t
arpsend(struct iodesc * d,void * pkt,size_t len)142 arpsend(struct iodesc *d, void *pkt, size_t len)
143 {
144 
145 #ifdef ARP_DEBUG
146  	if (debug)
147 		printf("arpsend: called\n");
148 #endif
149 
150 	return (sendether(d, pkt, len, bcea, ETHERTYPE_ARP));
151 }
152 
153 /*
154  * Returns 0 if this is the packet we're waiting for
155  * else -1 (and errno == 0)
156  */
157 static ssize_t
arprecv(struct iodesc * d,void ** pkt,void ** payload,time_t tleft,void * extra)158 arprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft, void *extra)
159 {
160 	ssize_t n;
161 	struct ether_arp *ah;
162 	uint16_t etype;	/* host order */
163 	void *ptr;
164 
165 #ifdef ARP_DEBUG
166  	if (debug)
167 		printf("arprecv: ");
168 #endif
169 
170 	ptr = NULL;
171 	n = readether(d, &ptr, (void **)&ah, tleft, &etype);
172 	errno = 0;	/* XXX */
173 	if (n == -1 || n < sizeof(struct ether_arp)) {
174 #ifdef ARP_DEBUG
175 		if (debug)
176 			printf("bad len=%zd\n", n);
177 #endif
178 		free(ptr);
179 		return (-1);
180 	}
181 
182 	if (etype != ETHERTYPE_ARP) {
183 #ifdef ARP_DEBUG
184 		if (debug)
185 			printf("not arp type=%d\n", etype);
186 #endif
187 		free(ptr);
188 		return (-1);
189 	}
190 
191 	/* Ethernet address now checked in readether() */
192 	if (ah->arp_hrd != htons(ARPHRD_ETHER) ||
193 	    ah->arp_pro != htons(ETHERTYPE_IP) ||
194 	    ah->arp_hln != sizeof(ah->arp_sha) ||
195 	    ah->arp_pln != sizeof(ah->arp_spa) )
196 	{
197 #ifdef ARP_DEBUG
198 		if (debug)
199 			printf("bad hrd/pro/hln/pln\n");
200 #endif
201 		free(ptr);
202 		return (-1);
203 	}
204 
205 	if (ah->arp_op == htons(ARPOP_REQUEST)) {
206 #ifdef ARP_DEBUG
207 		if (debug)
208 			printf("is request\n");
209 #endif
210 		arp_reply(d, ah);
211 		free(ptr);
212 		return (-1);
213 	}
214 
215 	if (ah->arp_op != htons(ARPOP_REPLY)) {
216 #ifdef ARP_DEBUG
217 		if (debug)
218 			printf("not ARP reply\n");
219 #endif
220 		free(ptr);
221 		return (-1);
222 	}
223 
224 	/* Is the reply from the source we want? */
225 	if (bcmp(&arp_list[arp_num].addr,
226 			 ah->arp_spa, sizeof(ah->arp_spa)))
227 	{
228 #ifdef ARP_DEBUG
229 		if (debug)
230 			printf("unwanted address\n");
231 #endif
232 		free(ptr);
233 		return (-1);
234 	}
235 	/* We don't care who the reply was sent to. */
236 
237 	/* We have our answer. */
238 #ifdef ARP_DEBUG
239  	if (debug)
240 		printf("got it\n");
241 #endif
242 	*pkt = ptr;
243 	*payload = ah;
244 	return (n);
245 }
246 
247 /*
248  * Convert an ARP request into a reply and send it.
249  * Notes:  Re-uses buffer.  Pad to length = 46.
250  */
251 void
arp_reply(struct iodesc * d,void * pkt)252 arp_reply(struct iodesc *d, void *pkt)
253 {
254 	struct ether_arp *arp = pkt;
255 
256 	if (arp->arp_hrd != htons(ARPHRD_ETHER) ||
257 	    arp->arp_pro != htons(ETHERTYPE_IP) ||
258 	    arp->arp_hln != sizeof(arp->arp_sha) ||
259 	    arp->arp_pln != sizeof(arp->arp_spa) )
260 	{
261 #ifdef ARP_DEBUG
262 		if (debug)
263 			printf("arp_reply: bad hrd/pro/hln/pln\n");
264 #endif
265 		return;
266 	}
267 
268 	if (arp->arp_op != htons(ARPOP_REQUEST)) {
269 #ifdef ARP_DEBUG
270 		if (debug)
271 			printf("arp_reply: not request!\n");
272 #endif
273 		return;
274 	}
275 
276 	/* If we are not the target, ignore the request. */
277 	if (bcmp(arp->arp_tpa, &d->myip, sizeof(arp->arp_tpa)))
278 		return;
279 
280 #ifdef ARP_DEBUG
281 	if (debug) {
282 		printf("arp_reply: to %s\n", ether_sprintf(arp->arp_sha));
283 	}
284 #endif
285 
286 	arp->arp_op = htons(ARPOP_REPLY);
287 	/* source becomes target */
288 	bcopy(arp->arp_sha, arp->arp_tha, sizeof(arp->arp_tha));
289 	bcopy(arp->arp_spa, arp->arp_tpa, sizeof(arp->arp_tpa));
290 	/* here becomes source */
291 	bcopy(d->myea,  arp->arp_sha, sizeof(arp->arp_sha));
292 	bcopy(&d->myip, arp->arp_spa, sizeof(arp->arp_spa));
293 
294 	/*
295 	 * No need to get fancy here.  If the send fails, the
296 	 * requestor will just ask again.
297 	 */
298 	(void) sendether(d, pkt, sizeof(*arp) + 18,
299 	                 arp->arp_tha, ETHERTYPE_ARP);
300 }
301