Deleted Added
full compact
arp.c (233773) arp.c (246143)
1/*
2 * Copyright (c) 1984, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Sun Microsystems, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1984, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
42#endif /* not lint */
43#endif
44#include <sys/cdefs.h>
1/*
2 * Copyright (c) 1984, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Sun Microsystems, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1984, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
42#endif /* not lint */
43#endif
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 233773 2012-04-02 10:44:25Z glebius $");
45__FBSDID("$FreeBSD: head/usr.sbin/arp/arp.c 246143 2013-01-31 08:55:21Z glebius $");
46
47/*
48 * arp - display, set, and delete arp table entries
49 */
50
51
52#include <sys/param.h>
53#include <sys/file.h>
54#include <sys/socket.h>
55#include <sys/sockio.h>
56#include <sys/sysctl.h>
57#include <sys/ioctl.h>
58#include <sys/time.h>
59
60#include <net/if.h>
61#include <net/if_dl.h>
62#include <net/if_types.h>
63#include <net/route.h>
64#include <net/iso88025.h>
65
66#include <netinet/in.h>
67#include <netinet/if_ether.h>
68
69#include <arpa/inet.h>
70
71#include <ctype.h>
72#include <err.h>
73#include <errno.h>
74#include <netdb.h>
75#include <nlist.h>
76#include <paths.h>
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <strings.h>
81#include <unistd.h>
82
83typedef void (action_fn)(struct sockaddr_dl *sdl,
46
47/*
48 * arp - display, set, and delete arp table entries
49 */
50
51
52#include <sys/param.h>
53#include <sys/file.h>
54#include <sys/socket.h>
55#include <sys/sockio.h>
56#include <sys/sysctl.h>
57#include <sys/ioctl.h>
58#include <sys/time.h>
59
60#include <net/if.h>
61#include <net/if_dl.h>
62#include <net/if_types.h>
63#include <net/route.h>
64#include <net/iso88025.h>
65
66#include <netinet/in.h>
67#include <netinet/if_ether.h>
68
69#include <arpa/inet.h>
70
71#include <ctype.h>
72#include <err.h>
73#include <errno.h>
74#include <netdb.h>
75#include <nlist.h>
76#include <paths.h>
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <strings.h>
81#include <unistd.h>
82
83typedef void (action_fn)(struct sockaddr_dl *sdl,
84 struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
84 struct sockaddr_in *s_in, struct rt_msghdr *rtm);
85
86static int search(u_long addr, action_fn *action);
87static action_fn print_entry;
88static action_fn nuke_entry;
89
85
86static int search(u_long addr, action_fn *action);
87static action_fn print_entry;
88static action_fn nuke_entry;
89
90static int delete(char *host, int do_proxy);
90static int delete(char *host);
91static void usage(void);
92static int set(int argc, char **argv);
93static int get(char *host);
94static int file(char *name);
95static struct rt_msghdr *rtmsg(int cmd,
91static void usage(void);
92static int set(int argc, char **argv);
93static int get(char *host);
94static int file(char *name);
95static struct rt_msghdr *rtmsg(int cmd,
96 struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
96 struct sockaddr_in *dst, struct sockaddr_dl *sdl);
97static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
97static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
98static struct sockaddr_inarp *getaddr(char *host);
98static struct sockaddr_in *getaddr(char *host);
99static int valid_type(int type);
100
101static int nflag; /* no reverse dns lookups */
102static char *rifname;
103
104static time_t expire_time;
99static int valid_type(int type);
100
101static int nflag; /* no reverse dns lookups */
102static char *rifname;
103
104static time_t expire_time;
105static int flags, doing_proxy, proxy_only;
105static int flags, doing_proxy;
106
107/* which function we're supposed to do */
108#define F_GET 1
109#define F_SET 2
110#define F_FILESET 3
111#define F_REPLACE 4
112#define F_DELETE 5
113
114#define SETFUNC(f) { if (func) usage(); func = (f); }
115
116int
117main(int argc, char *argv[])
118{
119 int ch, func = 0;
120 int rtn = 0;
121 int aflag = 0; /* do it for all entries */
122
123 while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
124 switch(ch) {
125 case 'a':
126 aflag = 1;
127 break;
128 case 'd':
129 SETFUNC(F_DELETE);
130 break;
131 case 'n':
132 nflag = 1;
133 break;
134 case 'S':
135 SETFUNC(F_REPLACE);
136 break;
137 case 's':
138 SETFUNC(F_SET);
139 break;
140 case 'f' :
141 SETFUNC(F_FILESET);
142 break;
143 case 'i':
144 rifname = optarg;
145 break;
146 case '?':
147 default:
148 usage();
149 }
150 argc -= optind;
151 argv += optind;
152
153 if (!func)
154 func = F_GET;
155 if (rifname) {
156 if (func != F_GET && !(func == F_DELETE && aflag))
157 errx(1, "-i not applicable to this operation");
158 if (if_nametoindex(rifname) == 0) {
159 if (errno == ENXIO)
160 errx(1, "interface %s does not exist", rifname);
161 else
162 err(1, "if_nametoindex(%s)", rifname);
163 }
164 }
165 switch (func) {
166 case F_GET:
167 if (aflag) {
168 if (argc != 0)
169 usage();
170 search(0, print_entry);
171 } else {
172 if (argc != 1)
173 usage();
174 rtn = get(argv[0]);
175 }
176 break;
177 case F_SET:
178 case F_REPLACE:
179 if (argc < 2 || argc > 6)
180 usage();
181 if (func == F_REPLACE)
106
107/* which function we're supposed to do */
108#define F_GET 1
109#define F_SET 2
110#define F_FILESET 3
111#define F_REPLACE 4
112#define F_DELETE 5
113
114#define SETFUNC(f) { if (func) usage(); func = (f); }
115
116int
117main(int argc, char *argv[])
118{
119 int ch, func = 0;
120 int rtn = 0;
121 int aflag = 0; /* do it for all entries */
122
123 while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
124 switch(ch) {
125 case 'a':
126 aflag = 1;
127 break;
128 case 'd':
129 SETFUNC(F_DELETE);
130 break;
131 case 'n':
132 nflag = 1;
133 break;
134 case 'S':
135 SETFUNC(F_REPLACE);
136 break;
137 case 's':
138 SETFUNC(F_SET);
139 break;
140 case 'f' :
141 SETFUNC(F_FILESET);
142 break;
143 case 'i':
144 rifname = optarg;
145 break;
146 case '?':
147 default:
148 usage();
149 }
150 argc -= optind;
151 argv += optind;
152
153 if (!func)
154 func = F_GET;
155 if (rifname) {
156 if (func != F_GET && !(func == F_DELETE && aflag))
157 errx(1, "-i not applicable to this operation");
158 if (if_nametoindex(rifname) == 0) {
159 if (errno == ENXIO)
160 errx(1, "interface %s does not exist", rifname);
161 else
162 err(1, "if_nametoindex(%s)", rifname);
163 }
164 }
165 switch (func) {
166 case F_GET:
167 if (aflag) {
168 if (argc != 0)
169 usage();
170 search(0, print_entry);
171 } else {
172 if (argc != 1)
173 usage();
174 rtn = get(argv[0]);
175 }
176 break;
177 case F_SET:
178 case F_REPLACE:
179 if (argc < 2 || argc > 6)
180 usage();
181 if (func == F_REPLACE)
182 (void)delete(argv[0], 0);
182 (void)delete(argv[0]);
183 rtn = set(argc, argv) ? 1 : 0;
184 break;
185 case F_DELETE:
186 if (aflag) {
187 if (argc != 0)
188 usage();
189 search(0, nuke_entry);
183 rtn = set(argc, argv) ? 1 : 0;
184 break;
185 case F_DELETE:
186 if (aflag) {
187 if (argc != 0)
188 usage();
189 search(0, nuke_entry);
190 } else {
191 if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
192 ch = SIN_PROXY;
193 else if (argc == 1)
194 ch = 0;
195 else
196 usage();
197 rtn = delete(argv[0], ch);
198 }
190 } else
191 rtn = delete(argv[0]);
199 break;
200 case F_FILESET:
201 if (argc != 1)
202 usage();
203 rtn = file(argv[0]);
204 break;
205 }
206
207 return (rtn);
208}
209
210/*
211 * Process a file to set standard arp entries
212 */
213static int
214file(char *name)
215{
216 FILE *fp;
217 int i, retval;
218 char line[100], arg[5][50], *args[5], *p;
219
220 if ((fp = fopen(name, "r")) == NULL)
221 err(1, "cannot open %s", name);
222 args[0] = &arg[0][0];
223 args[1] = &arg[1][0];
224 args[2] = &arg[2][0];
225 args[3] = &arg[3][0];
226 args[4] = &arg[4][0];
227 retval = 0;
228 while(fgets(line, sizeof(line), fp) != NULL) {
229 if ((p = strchr(line, '#')) != NULL)
230 *p = '\0';
231 for (p = line; isblank(*p); p++);
232 if (*p == '\n' || *p == '\0')
233 continue;
234 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
235 arg[2], arg[3], arg[4]);
236 if (i < 2) {
237 warnx("bad line: %s", line);
238 retval = 1;
239 continue;
240 }
241 if (set(i, args))
242 retval = 1;
243 }
244 fclose(fp);
245 return (retval);
246}
247
248/*
192 break;
193 case F_FILESET:
194 if (argc != 1)
195 usage();
196 rtn = file(argv[0]);
197 break;
198 }
199
200 return (rtn);
201}
202
203/*
204 * Process a file to set standard arp entries
205 */
206static int
207file(char *name)
208{
209 FILE *fp;
210 int i, retval;
211 char line[100], arg[5][50], *args[5], *p;
212
213 if ((fp = fopen(name, "r")) == NULL)
214 err(1, "cannot open %s", name);
215 args[0] = &arg[0][0];
216 args[1] = &arg[1][0];
217 args[2] = &arg[2][0];
218 args[3] = &arg[3][0];
219 args[4] = &arg[4][0];
220 retval = 0;
221 while(fgets(line, sizeof(line), fp) != NULL) {
222 if ((p = strchr(line, '#')) != NULL)
223 *p = '\0';
224 for (p = line; isblank(*p); p++);
225 if (*p == '\n' || *p == '\0')
226 continue;
227 i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
228 arg[2], arg[3], arg[4]);
229 if (i < 2) {
230 warnx("bad line: %s", line);
231 retval = 1;
232 continue;
233 }
234 if (set(i, args))
235 retval = 1;
236 }
237 fclose(fp);
238 return (retval);
239}
240
241/*
249 * Given a hostname, fills up a (static) struct sockaddr_inarp with
242 * Given a hostname, fills up a (static) struct sockaddr_in with
250 * the address of the host and returns a pointer to the
251 * structure.
252 */
243 * the address of the host and returns a pointer to the
244 * structure.
245 */
253static struct sockaddr_inarp *
246static struct sockaddr_in *
254getaddr(char *host)
255{
256 struct hostent *hp;
247getaddr(char *host)
248{
249 struct hostent *hp;
257 static struct sockaddr_inarp reply;
250 static struct sockaddr_in reply;
258
259 bzero(&reply, sizeof(reply));
260 reply.sin_len = sizeof(reply);
261 reply.sin_family = AF_INET;
262 reply.sin_addr.s_addr = inet_addr(host);
263 if (reply.sin_addr.s_addr == INADDR_NONE) {
264 if (!(hp = gethostbyname(host))) {
265 warnx("%s: %s", host, hstrerror(h_errno));
266 return (NULL);
267 }
268 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
269 sizeof reply.sin_addr);
270 }
271 return (&reply);
272}
273
274/*
275 * Returns true if the type is a valid one for ARP.
276 */
277static int
278valid_type(int type)
279{
280
281 switch (type) {
282 case IFT_ETHER:
283 case IFT_FDDI:
284 case IFT_ISO88023:
285 case IFT_ISO88024:
286 case IFT_ISO88025:
287 case IFT_L2VLAN:
288 case IFT_BRIDGE:
289 return (1);
290 default:
291 return (0);
292 }
293}
294
295/*
296 * Set an individual arp entry
297 */
298static int
299set(int argc, char **argv)
300{
251
252 bzero(&reply, sizeof(reply));
253 reply.sin_len = sizeof(reply);
254 reply.sin_family = AF_INET;
255 reply.sin_addr.s_addr = inet_addr(host);
256 if (reply.sin_addr.s_addr == INADDR_NONE) {
257 if (!(hp = gethostbyname(host))) {
258 warnx("%s: %s", host, hstrerror(h_errno));
259 return (NULL);
260 }
261 bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
262 sizeof reply.sin_addr);
263 }
264 return (&reply);
265}
266
267/*
268 * Returns true if the type is a valid one for ARP.
269 */
270static int
271valid_type(int type)
272{
273
274 switch (type) {
275 case IFT_ETHER:
276 case IFT_FDDI:
277 case IFT_ISO88023:
278 case IFT_ISO88024:
279 case IFT_ISO88025:
280 case IFT_L2VLAN:
281 case IFT_BRIDGE:
282 return (1);
283 default:
284 return (0);
285 }
286}
287
288/*
289 * Set an individual arp entry
290 */
291static int
292set(int argc, char **argv)
293{
301 struct sockaddr_inarp *addr;
302 struct sockaddr_inarp *dst; /* what are we looking for */
294 struct sockaddr_in *addr;
295 struct sockaddr_in *dst; /* what are we looking for */
303 struct sockaddr_dl *sdl;
304 struct rt_msghdr *rtm;
305 struct ether_addr *ea;
306 char *host = argv[0], *eaddr = argv[1];
307 struct sockaddr_dl sdl_m;
308
309 argc -= 2;
310 argv += 2;
311
312 bzero(&sdl_m, sizeof(sdl_m));
313 sdl_m.sdl_len = sizeof(sdl_m);
314 sdl_m.sdl_family = AF_LINK;
315
316 dst = getaddr(host);
317 if (dst == NULL)
318 return (1);
296 struct sockaddr_dl *sdl;
297 struct rt_msghdr *rtm;
298 struct ether_addr *ea;
299 char *host = argv[0], *eaddr = argv[1];
300 struct sockaddr_dl sdl_m;
301
302 argc -= 2;
303 argv += 2;
304
305 bzero(&sdl_m, sizeof(sdl_m));
306 sdl_m.sdl_len = sizeof(sdl_m);
307 sdl_m.sdl_family = AF_LINK;
308
309 dst = getaddr(host);
310 if (dst == NULL)
311 return (1);
319 doing_proxy = flags = proxy_only = expire_time = 0;
312 doing_proxy = flags = expire_time = 0;
320 while (argc-- > 0) {
321 if (strncmp(argv[0], "temp", 4) == 0) {
322 struct timespec tp;
323 int max_age;
324 size_t len = sizeof(max_age);
325
326 clock_gettime(CLOCK_MONOTONIC, &tp);
327 if (sysctlbyname("net.link.ether.inet.max_age",
328 &max_age, &len, NULL, 0) != 0)
329 err(1, "sysctlbyname");
330 expire_time = tp.tv_sec + max_age;
331 } else if (strncmp(argv[0], "pub", 3) == 0) {
332 flags |= RTF_ANNOUNCE;
333 doing_proxy = 1;
334 if (argc && strncmp(argv[1], "only", 3) == 0) {
313 while (argc-- > 0) {
314 if (strncmp(argv[0], "temp", 4) == 0) {
315 struct timespec tp;
316 int max_age;
317 size_t len = sizeof(max_age);
318
319 clock_gettime(CLOCK_MONOTONIC, &tp);
320 if (sysctlbyname("net.link.ether.inet.max_age",
321 &max_age, &len, NULL, 0) != 0)
322 err(1, "sysctlbyname");
323 expire_time = tp.tv_sec + max_age;
324 } else if (strncmp(argv[0], "pub", 3) == 0) {
325 flags |= RTF_ANNOUNCE;
326 doing_proxy = 1;
327 if (argc && strncmp(argv[1], "only", 3) == 0) {
335 proxy_only = 1;
328 /*
329 * Compatibility: in pre FreeBSD 8 times
330 * the "only" keyword used to mean that
331 * an ARP entry should be announced, but
332 * not installed into routing table.
333 */
336 argc--; argv++;
337 }
338 } else if (strncmp(argv[0], "blackhole", 9) == 0) {
339 if (flags & RTF_REJECT) {
340 printf("Choose one of blackhole or reject, not both.\n");
341 }
342 flags |= RTF_BLACKHOLE;
343 } else if (strncmp(argv[0], "reject", 6) == 0) {
344 if (flags & RTF_BLACKHOLE) {
345 printf("Choose one of blackhole or reject, not both.\n");
346 }
347 flags |= RTF_REJECT;
348 } else if (strncmp(argv[0], "trail", 5) == 0) {
349 /* XXX deprecated and undocumented feature */
350 printf("%s: Sending trailers is no longer supported\n",
351 host);
352 }
353 argv++;
354 }
355 ea = (struct ether_addr *)LLADDR(&sdl_m);
356 if (doing_proxy && !strcmp(eaddr, "auto")) {
357 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
358 printf("no interface found for %s\n",
359 inet_ntoa(dst->sin_addr));
360 return (1);
361 }
362 sdl_m.sdl_alen = ETHER_ADDR_LEN;
363 } else {
364 struct ether_addr *ea1 = ether_aton(eaddr);
365
366 if (ea1 == NULL) {
367 warnx("invalid Ethernet address '%s'", eaddr);
368 return (1);
369 } else {
370 *ea = *ea1;
371 sdl_m.sdl_alen = ETHER_ADDR_LEN;
372 }
373 }
374
375 /*
376 * In the case a proxy-arp entry is being added for
377 * a remote end point, the RTF_ANNOUNCE flag in the
378 * RTM_GET command is an indication to the kernel
379 * routing code that the interface associated with
380 * the prefix route covering the local end of the
381 * PPP link should be returned, on which ARP applies.
382 */
383 rtm = rtmsg(RTM_GET, dst, &sdl_m);
384 if (rtm == NULL) {
385 warn("%s", host);
386 return (1);
387 }
334 argc--; argv++;
335 }
336 } else if (strncmp(argv[0], "blackhole", 9) == 0) {
337 if (flags & RTF_REJECT) {
338 printf("Choose one of blackhole or reject, not both.\n");
339 }
340 flags |= RTF_BLACKHOLE;
341 } else if (strncmp(argv[0], "reject", 6) == 0) {
342 if (flags & RTF_BLACKHOLE) {
343 printf("Choose one of blackhole or reject, not both.\n");
344 }
345 flags |= RTF_REJECT;
346 } else if (strncmp(argv[0], "trail", 5) == 0) {
347 /* XXX deprecated and undocumented feature */
348 printf("%s: Sending trailers is no longer supported\n",
349 host);
350 }
351 argv++;
352 }
353 ea = (struct ether_addr *)LLADDR(&sdl_m);
354 if (doing_proxy && !strcmp(eaddr, "auto")) {
355 if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
356 printf("no interface found for %s\n",
357 inet_ntoa(dst->sin_addr));
358 return (1);
359 }
360 sdl_m.sdl_alen = ETHER_ADDR_LEN;
361 } else {
362 struct ether_addr *ea1 = ether_aton(eaddr);
363
364 if (ea1 == NULL) {
365 warnx("invalid Ethernet address '%s'", eaddr);
366 return (1);
367 } else {
368 *ea = *ea1;
369 sdl_m.sdl_alen = ETHER_ADDR_LEN;
370 }
371 }
372
373 /*
374 * In the case a proxy-arp entry is being added for
375 * a remote end point, the RTF_ANNOUNCE flag in the
376 * RTM_GET command is an indication to the kernel
377 * routing code that the interface associated with
378 * the prefix route covering the local end of the
379 * PPP link should be returned, on which ARP applies.
380 */
381 rtm = rtmsg(RTM_GET, dst, &sdl_m);
382 if (rtm == NULL) {
383 warn("%s", host);
384 return (1);
385 }
388 addr = (struct sockaddr_inarp *)(rtm + 1);
386 addr = (struct sockaddr_in *)(rtm + 1);
389 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
390
391 if ((sdl->sdl_family != AF_LINK) ||
392 (rtm->rtm_flags & RTF_GATEWAY) ||
393 !valid_type(sdl->sdl_type)) {
394 printf("cannot intuit interface index and type for %s\n", host);
395 return (1);
396 }
397 sdl_m.sdl_type = sdl->sdl_type;
398 sdl_m.sdl_index = sdl->sdl_index;
399 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
400}
401
402/*
403 * Display an individual arp entry
404 */
405static int
406get(char *host)
407{
387 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
388
389 if ((sdl->sdl_family != AF_LINK) ||
390 (rtm->rtm_flags & RTF_GATEWAY) ||
391 !valid_type(sdl->sdl_type)) {
392 printf("cannot intuit interface index and type for %s\n", host);
393 return (1);
394 }
395 sdl_m.sdl_type = sdl->sdl_type;
396 sdl_m.sdl_index = sdl->sdl_index;
397 return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
398}
399
400/*
401 * Display an individual arp entry
402 */
403static int
404get(char *host)
405{
408 struct sockaddr_inarp *addr;
406 struct sockaddr_in *addr;
409
410 addr = getaddr(host);
411 if (addr == NULL)
412 return (1);
413 if (0 == search(addr->sin_addr.s_addr, print_entry)) {
414 printf("%s (%s) -- no entry",
415 host, inet_ntoa(addr->sin_addr));
416 if (rifname)
417 printf(" on %s", rifname);
418 printf("\n");
419 return (1);
420 }
421 return (0);
422}
423
424/*
425 * Delete an arp entry
426 */
427static int
407
408 addr = getaddr(host);
409 if (addr == NULL)
410 return (1);
411 if (0 == search(addr->sin_addr.s_addr, print_entry)) {
412 printf("%s (%s) -- no entry",
413 host, inet_ntoa(addr->sin_addr));
414 if (rifname)
415 printf(" on %s", rifname);
416 printf("\n");
417 return (1);
418 }
419 return (0);
420}
421
422/*
423 * Delete an arp entry
424 */
425static int
428delete(char *host, int do_proxy)
426delete(char *host)
429{
427{
430 struct sockaddr_inarp *addr, *dst;
428 struct sockaddr_in *addr, *dst;
431 struct rt_msghdr *rtm;
432 struct sockaddr_dl *sdl;
433 struct sockaddr_dl sdl_m;
434
435 dst = getaddr(host);
436 if (dst == NULL)
437 return (1);
438
439 /*
440 * Perform a regular entry delete first.
441 */
442 flags &= ~RTF_ANNOUNCE;
443
444 /*
445 * setup the data structure to notify the kernel
446 * it is the ARP entry the RTM_GET is interested
447 * in
448 */
449 bzero(&sdl_m, sizeof(sdl_m));
450 sdl_m.sdl_len = sizeof(sdl_m);
451 sdl_m.sdl_family = AF_LINK;
452
453 for (;;) { /* try twice */
454 rtm = rtmsg(RTM_GET, dst, &sdl_m);
455 if (rtm == NULL) {
456 warn("%s", host);
457 return (1);
458 }
429 struct rt_msghdr *rtm;
430 struct sockaddr_dl *sdl;
431 struct sockaddr_dl sdl_m;
432
433 dst = getaddr(host);
434 if (dst == NULL)
435 return (1);
436
437 /*
438 * Perform a regular entry delete first.
439 */
440 flags &= ~RTF_ANNOUNCE;
441
442 /*
443 * setup the data structure to notify the kernel
444 * it is the ARP entry the RTM_GET is interested
445 * in
446 */
447 bzero(&sdl_m, sizeof(sdl_m));
448 sdl_m.sdl_len = sizeof(sdl_m);
449 sdl_m.sdl_family = AF_LINK;
450
451 for (;;) { /* try twice */
452 rtm = rtmsg(RTM_GET, dst, &sdl_m);
453 if (rtm == NULL) {
454 warn("%s", host);
455 return (1);
456 }
459 addr = (struct sockaddr_inarp *)(rtm + 1);
457 addr = (struct sockaddr_in *)(rtm + 1);
460 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
461
462 /*
463 * With the new L2/L3 restructure, the route
464 * returned is a prefix route. The important
465 * piece of information from the previous
466 * RTM_GET is the interface index. In the
467 * case of ECMP, the kernel will traverse
468 * the route group for the given entry.
469 */
470 if (sdl->sdl_family == AF_LINK &&
471 !(rtm->rtm_flags & RTF_GATEWAY) &&
472 valid_type(sdl->sdl_type) ) {
473 addr->sin_addr.s_addr = dst->sin_addr.s_addr;
474 break;
475 }
476
477 /*
478 * Regualar entry delete failed, now check if there
479 * is a proxy-arp entry to remove.
480 */
481 if (flags & RTF_ANNOUNCE) {
482 fprintf(stderr, "delete: cannot locate %s\n",host);
483 return (1);
484 }
485
486 flags |= RTF_ANNOUNCE;
487 }
488 rtm->rtm_flags |= RTF_LLDATA;
489 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
490 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
491 return (0);
492 }
493 return (1);
494}
495
496
497/*
498 * Search the arp table and do some action on matching entries
499 */
500static int
501search(u_long addr, action_fn *action)
502{
503 int mib[6];
504 size_t needed;
505 char *lim, *buf, *next;
506 struct rt_msghdr *rtm;
458 sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
459
460 /*
461 * With the new L2/L3 restructure, the route
462 * returned is a prefix route. The important
463 * piece of information from the previous
464 * RTM_GET is the interface index. In the
465 * case of ECMP, the kernel will traverse
466 * the route group for the given entry.
467 */
468 if (sdl->sdl_family == AF_LINK &&
469 !(rtm->rtm_flags & RTF_GATEWAY) &&
470 valid_type(sdl->sdl_type) ) {
471 addr->sin_addr.s_addr = dst->sin_addr.s_addr;
472 break;
473 }
474
475 /*
476 * Regualar entry delete failed, now check if there
477 * is a proxy-arp entry to remove.
478 */
479 if (flags & RTF_ANNOUNCE) {
480 fprintf(stderr, "delete: cannot locate %s\n",host);
481 return (1);
482 }
483
484 flags |= RTF_ANNOUNCE;
485 }
486 rtm->rtm_flags |= RTF_LLDATA;
487 if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
488 printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
489 return (0);
490 }
491 return (1);
492}
493
494
495/*
496 * Search the arp table and do some action on matching entries
497 */
498static int
499search(u_long addr, action_fn *action)
500{
501 int mib[6];
502 size_t needed;
503 char *lim, *buf, *next;
504 struct rt_msghdr *rtm;
507 struct sockaddr_inarp *sin2;
505 struct sockaddr_in *sin2;
508 struct sockaddr_dl *sdl;
509 char ifname[IF_NAMESIZE];
510 int st, found_entry = 0;
511
512 mib[0] = CTL_NET;
513 mib[1] = PF_ROUTE;
514 mib[2] = 0;
515 mib[3] = AF_INET;
516 mib[4] = NET_RT_FLAGS;
517#ifdef RTF_LLINFO
518 mib[5] = RTF_LLINFO;
519#else
520 mib[5] = 0;
521#endif
522 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
523 err(1, "route-sysctl-estimate");
524 if (needed == 0) /* empty table */
525 return 0;
526 buf = NULL;
527 for (;;) {
528 buf = reallocf(buf, needed);
529 if (buf == NULL)
530 errx(1, "could not reallocate memory");
531 st = sysctl(mib, 6, buf, &needed, NULL, 0);
532 if (st == 0 || errno != ENOMEM)
533 break;
534 needed += needed / 8;
535 }
536 if (st == -1)
537 err(1, "actual retrieval of routing table");
538 lim = buf + needed;
539 for (next = buf; next < lim; next += rtm->rtm_msglen) {
540 rtm = (struct rt_msghdr *)next;
506 struct sockaddr_dl *sdl;
507 char ifname[IF_NAMESIZE];
508 int st, found_entry = 0;
509
510 mib[0] = CTL_NET;
511 mib[1] = PF_ROUTE;
512 mib[2] = 0;
513 mib[3] = AF_INET;
514 mib[4] = NET_RT_FLAGS;
515#ifdef RTF_LLINFO
516 mib[5] = RTF_LLINFO;
517#else
518 mib[5] = 0;
519#endif
520 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
521 err(1, "route-sysctl-estimate");
522 if (needed == 0) /* empty table */
523 return 0;
524 buf = NULL;
525 for (;;) {
526 buf = reallocf(buf, needed);
527 if (buf == NULL)
528 errx(1, "could not reallocate memory");
529 st = sysctl(mib, 6, buf, &needed, NULL, 0);
530 if (st == 0 || errno != ENOMEM)
531 break;
532 needed += needed / 8;
533 }
534 if (st == -1)
535 err(1, "actual retrieval of routing table");
536 lim = buf + needed;
537 for (next = buf; next < lim; next += rtm->rtm_msglen) {
538 rtm = (struct rt_msghdr *)next;
541 sin2 = (struct sockaddr_inarp *)(rtm + 1);
539 sin2 = (struct sockaddr_in *)(rtm + 1);
542 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
543 if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
544 strcmp(ifname, rifname))
545 continue;
546 if (addr) {
547 if (addr != sin2->sin_addr.s_addr)
548 continue;
549 found_entry = 1;
550 }
551 (*action)(sdl, sin2, rtm);
552 }
553 free(buf);
554 return (found_entry);
555}
556
557/*
558 * Display an arp entry
559 */
560static char lifname[IF_NAMESIZE];
561static int64_t lifindex = -1;
562
563static void
564print_entry(struct sockaddr_dl *sdl,
540 sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
541 if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
542 strcmp(ifname, rifname))
543 continue;
544 if (addr) {
545 if (addr != sin2->sin_addr.s_addr)
546 continue;
547 found_entry = 1;
548 }
549 (*action)(sdl, sin2, rtm);
550 }
551 free(buf);
552 return (found_entry);
553}
554
555/*
556 * Display an arp entry
557 */
558static char lifname[IF_NAMESIZE];
559static int64_t lifindex = -1;
560
561static void
562print_entry(struct sockaddr_dl *sdl,
565 struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
563 struct sockaddr_in *addr, struct rt_msghdr *rtm)
566{
567 const char *host;
568 struct hostent *hp;
569 struct iso88025_sockaddr_dl_data *trld;
570 int seg;
571
572 if (nflag == 0)
573 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
574 sizeof addr->sin_addr, AF_INET);
575 else
576 hp = 0;
577 if (hp)
578 host = hp->h_name;
579 else {
580 host = "?";
581 if (h_errno == TRY_AGAIN)
582 nflag = 1;
583 }
584 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
585 if (sdl->sdl_alen) {
586 if ((sdl->sdl_type == IFT_ETHER ||
587 sdl->sdl_type == IFT_L2VLAN ||
588 sdl->sdl_type == IFT_BRIDGE) &&
589 sdl->sdl_alen == ETHER_ADDR_LEN)
590 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
591 else {
592 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
593
594 printf("%s", link_ntoa(sdl) + n);
595 }
596 } else
597 printf("(incomplete)");
598 if (sdl->sdl_index != lifindex &&
599 if_indextoname(sdl->sdl_index, lifname) != NULL) {
600 lifindex = sdl->sdl_index;
601 printf(" on %s", lifname);
602 } else if (sdl->sdl_index == lifindex)
603 printf(" on %s", lifname);
604 if (rtm->rtm_rmx.rmx_expire == 0)
605 printf(" permanent");
606 else {
607 static struct timespec tp;
608 if (tp.tv_sec == 0)
609 clock_gettime(CLOCK_MONOTONIC, &tp);
610 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
611 printf(" expires in %d seconds", (int)expire_time);
612 else
613 printf(" expired");
614 }
564{
565 const char *host;
566 struct hostent *hp;
567 struct iso88025_sockaddr_dl_data *trld;
568 int seg;
569
570 if (nflag == 0)
571 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
572 sizeof addr->sin_addr, AF_INET);
573 else
574 hp = 0;
575 if (hp)
576 host = hp->h_name;
577 else {
578 host = "?";
579 if (h_errno == TRY_AGAIN)
580 nflag = 1;
581 }
582 printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
583 if (sdl->sdl_alen) {
584 if ((sdl->sdl_type == IFT_ETHER ||
585 sdl->sdl_type == IFT_L2VLAN ||
586 sdl->sdl_type == IFT_BRIDGE) &&
587 sdl->sdl_alen == ETHER_ADDR_LEN)
588 printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
589 else {
590 int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
591
592 printf("%s", link_ntoa(sdl) + n);
593 }
594 } else
595 printf("(incomplete)");
596 if (sdl->sdl_index != lifindex &&
597 if_indextoname(sdl->sdl_index, lifname) != NULL) {
598 lifindex = sdl->sdl_index;
599 printf(" on %s", lifname);
600 } else if (sdl->sdl_index == lifindex)
601 printf(" on %s", lifname);
602 if (rtm->rtm_rmx.rmx_expire == 0)
603 printf(" permanent");
604 else {
605 static struct timespec tp;
606 if (tp.tv_sec == 0)
607 clock_gettime(CLOCK_MONOTONIC, &tp);
608 if ((expire_time = rtm->rtm_rmx.rmx_expire - tp.tv_sec) > 0)
609 printf(" expires in %d seconds", (int)expire_time);
610 else
611 printf(" expired");
612 }
615 if (addr->sin_other & SIN_PROXY)
616 printf(" published (proxy only)");
617 if (rtm->rtm_flags & RTF_ANNOUNCE)
618 printf(" published");
619 switch(sdl->sdl_type) {
620 case IFT_ETHER:
621 printf(" [ethernet]");
622 break;
623 case IFT_ISO88025:
624 printf(" [token-ring]");
625 trld = SDL_ISO88025(sdl);
626 if (trld->trld_rcf != 0) {
627 printf(" rt=%x", ntohs(trld->trld_rcf));
628 for (seg = 0;
629 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
630 seg++)
631 printf(":%x", ntohs(*(trld->trld_route[seg])));
632 }
633 break;
634 case IFT_FDDI:
635 printf(" [fddi]");
636 break;
637 case IFT_ATM:
638 printf(" [atm]");
639 break;
640 case IFT_L2VLAN:
641 printf(" [vlan]");
642 break;
643 case IFT_IEEE1394:
644 printf(" [firewire]");
645 break;
646 case IFT_BRIDGE:
647 printf(" [bridge]");
648 break;
649 default:
650 break;
651 }
652
653 printf("\n");
654
655}
656
657/*
658 * Nuke an arp entry
659 */
660static void
661nuke_entry(struct sockaddr_dl *sdl __unused,
613 if (rtm->rtm_flags & RTF_ANNOUNCE)
614 printf(" published");
615 switch(sdl->sdl_type) {
616 case IFT_ETHER:
617 printf(" [ethernet]");
618 break;
619 case IFT_ISO88025:
620 printf(" [token-ring]");
621 trld = SDL_ISO88025(sdl);
622 if (trld->trld_rcf != 0) {
623 printf(" rt=%x", ntohs(trld->trld_rcf));
624 for (seg = 0;
625 seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
626 seg++)
627 printf(":%x", ntohs(*(trld->trld_route[seg])));
628 }
629 break;
630 case IFT_FDDI:
631 printf(" [fddi]");
632 break;
633 case IFT_ATM:
634 printf(" [atm]");
635 break;
636 case IFT_L2VLAN:
637 printf(" [vlan]");
638 break;
639 case IFT_IEEE1394:
640 printf(" [firewire]");
641 break;
642 case IFT_BRIDGE:
643 printf(" [bridge]");
644 break;
645 default:
646 break;
647 }
648
649 printf("\n");
650
651}
652
653/*
654 * Nuke an arp entry
655 */
656static void
657nuke_entry(struct sockaddr_dl *sdl __unused,
662 struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
658 struct sockaddr_in *addr, struct rt_msghdr *rtm __unused)
663{
664 char ip[20];
665
666 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
659{
660 char ip[20];
661
662 snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
667 (void)delete(ip, 0);
663 delete(ip);
668}
669
670static void
671usage(void)
672{
673 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
674 "usage: arp [-n] [-i interface] hostname",
675 " arp [-n] [-i interface] -a",
676 " arp -d hostname [pub]",
677 " arp -d [-i interface] -a",
678 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
679 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
680 " arp -f filename");
681 exit(1);
682}
683
684static struct rt_msghdr *
664}
665
666static void
667usage(void)
668{
669 fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
670 "usage: arp [-n] [-i interface] hostname",
671 " arp [-n] [-i interface] -a",
672 " arp -d hostname [pub]",
673 " arp -d [-i interface] -a",
674 " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
675 " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
676 " arp -f filename");
677 exit(1);
678}
679
680static struct rt_msghdr *
685rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
681rtmsg(int cmd, struct sockaddr_in *dst, struct sockaddr_dl *sdl)
686{
687 static int seq;
688 int rlen;
689 int l;
690 struct sockaddr_in so_mask, *som = &so_mask;
691 static int s = -1;
692 static pid_t pid;
693
694 static struct {
695 struct rt_msghdr m_rtm;
696 char m_space[512];
697 } m_rtmsg;
698
699 struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
700 char *cp = m_rtmsg.m_space;
701
702 if (s < 0) { /* first time: open socket, get pid */
703 s = socket(PF_ROUTE, SOCK_RAW, 0);
704 if (s < 0)
705 err(1, "socket");
706 pid = getpid();
707 }
708 bzero(&so_mask, sizeof(so_mask));
709 so_mask.sin_len = 8;
710 so_mask.sin_addr.s_addr = 0xffffffff;
711
712 errno = 0;
713 /*
714 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
715 * appropriately.
716 */
717 if (cmd == RTM_DELETE)
718 goto doit;
719 bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
720 rtm->rtm_flags = flags;
721 rtm->rtm_version = RTM_VERSION;
722
723 switch (cmd) {
724 default:
725 errx(1, "internal wrong cmd");
726 case RTM_ADD:
727 rtm->rtm_addrs |= RTA_GATEWAY;
728 rtm->rtm_rmx.rmx_expire = expire_time;
729 rtm->rtm_inits = RTV_EXPIRE;
730 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
682{
683 static int seq;
684 int rlen;
685 int l;
686 struct sockaddr_in so_mask, *som = &so_mask;
687 static int s = -1;
688 static pid_t pid;
689
690 static struct {
691 struct rt_msghdr m_rtm;
692 char m_space[512];
693 } m_rtmsg;
694
695 struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
696 char *cp = m_rtmsg.m_space;
697
698 if (s < 0) { /* first time: open socket, get pid */
699 s = socket(PF_ROUTE, SOCK_RAW, 0);
700 if (s < 0)
701 err(1, "socket");
702 pid = getpid();
703 }
704 bzero(&so_mask, sizeof(so_mask));
705 so_mask.sin_len = 8;
706 so_mask.sin_addr.s_addr = 0xffffffff;
707
708 errno = 0;
709 /*
710 * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
711 * appropriately.
712 */
713 if (cmd == RTM_DELETE)
714 goto doit;
715 bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
716 rtm->rtm_flags = flags;
717 rtm->rtm_version = RTM_VERSION;
718
719 switch (cmd) {
720 default:
721 errx(1, "internal wrong cmd");
722 case RTM_ADD:
723 rtm->rtm_addrs |= RTA_GATEWAY;
724 rtm->rtm_rmx.rmx_expire = expire_time;
725 rtm->rtm_inits = RTV_EXPIRE;
726 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
731 dst->sin_other = 0;
732 if (doing_proxy) {
727 if (doing_proxy) {
733 if (proxy_only)
734 dst->sin_other = SIN_PROXY;
735 else {
736 rtm->rtm_addrs |= RTA_NETMASK;
737 rtm->rtm_flags &= ~RTF_HOST;
738 }
728 rtm->rtm_addrs |= RTA_NETMASK;
729 rtm->rtm_flags &= ~RTF_HOST;
739 }
740 /* FALLTHROUGH */
741 case RTM_GET:
742 rtm->rtm_addrs |= RTA_DST;
743 }
744#define NEXTADDR(w, s) \
745 do { \
746 if ((s) != NULL && rtm->rtm_addrs & (w)) { \
747 bcopy((s), cp, sizeof(*(s))); \
748 cp += SA_SIZE(s); \
749 } \
750 } while (0)
751
752 NEXTADDR(RTA_DST, dst);
753 NEXTADDR(RTA_GATEWAY, sdl);
754 NEXTADDR(RTA_NETMASK, som);
755
756 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
757doit:
758 l = rtm->rtm_msglen;
759 rtm->rtm_seq = ++seq;
760 rtm->rtm_type = cmd;
761 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
762 if (errno != ESRCH || cmd != RTM_DELETE) {
763 warn("writing to routing socket");
764 return (NULL);
765 }
766 }
767 do {
768 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
769 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
770 if (l < 0)
771 warn("read from routing socket");
772 return (rtm);
773}
774
775/*
776 * get_ether_addr - get the hardware address of an interface on the
777 * the same subnet as ipaddr.
778 */
779#define MAX_IFS 32
780
781static int
782get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
783{
784 struct ifreq *ifr, *ifend, *ifp;
785 in_addr_t ina, mask;
786 struct sockaddr_dl *dla;
787 struct ifreq ifreq;
788 struct ifconf ifc;
789 struct ifreq ifs[MAX_IFS];
790 int sock;
791 int retval = 0;
792
793 sock = socket(AF_INET, SOCK_DGRAM, 0);
794 if (sock < 0)
795 err(1, "socket");
796
797 ifc.ifc_len = sizeof(ifs);
798 ifc.ifc_req = ifs;
799 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
800 warnx("ioctl(SIOCGIFCONF)");
801 goto done;
802 }
803
804#define NEXTIFR(i) \
805 ((struct ifreq *)((char *)&(i)->ifr_addr \
806 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
807
808 /*
809 * Scan through looking for an interface with an Internet
810 * address on the same subnet as `ipaddr'.
811 */
812 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
813 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
814 if (ifr->ifr_addr.sa_family != AF_INET)
815 continue;
816 strncpy(ifreq.ifr_name, ifr->ifr_name,
817 sizeof(ifreq.ifr_name));
818 ifreq.ifr_addr = ifr->ifr_addr;
819 /*
820 * Check that the interface is up,
821 * and not point-to-point or loopback.
822 */
823 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
824 continue;
825 if ((ifreq.ifr_flags &
826 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
827 IFF_LOOPBACK|IFF_NOARP))
828 != (IFF_UP|IFF_BROADCAST))
829 continue;
830 /*
831 * Get its netmask and check that it's on
832 * the right subnet.
833 */
834 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
835 continue;
836 mask = ((struct sockaddr_in *)
837 &ifreq.ifr_addr)->sin_addr.s_addr;
838 ina = ((struct sockaddr_in *)
839 &ifr->ifr_addr)->sin_addr.s_addr;
840 if ((ipaddr & mask) == (ina & mask))
841 break; /* ok, we got it! */
842 }
843
844 if (ifr >= ifend)
845 goto done;
846
847 /*
848 * Now scan through again looking for a link-level address
849 * for this interface.
850 */
851 ifp = ifr;
852 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
853 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
854 ifr->ifr_addr.sa_family == AF_LINK)
855 break;
856 if (ifr >= ifend)
857 goto done;
858 /*
859 * Found the link-level address - copy it out
860 */
861 dla = (struct sockaddr_dl *) &ifr->ifr_addr;
862 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
863 printf("using interface %s for proxy with address ",
864 ifp->ifr_name);
865 printf("%s\n", ether_ntoa(hwaddr));
866 retval = dla->sdl_alen;
867done:
868 close(sock);
869 return (retval);
870}
730 }
731 /* FALLTHROUGH */
732 case RTM_GET:
733 rtm->rtm_addrs |= RTA_DST;
734 }
735#define NEXTADDR(w, s) \
736 do { \
737 if ((s) != NULL && rtm->rtm_addrs & (w)) { \
738 bcopy((s), cp, sizeof(*(s))); \
739 cp += SA_SIZE(s); \
740 } \
741 } while (0)
742
743 NEXTADDR(RTA_DST, dst);
744 NEXTADDR(RTA_GATEWAY, sdl);
745 NEXTADDR(RTA_NETMASK, som);
746
747 rtm->rtm_msglen = cp - (char *)&m_rtmsg;
748doit:
749 l = rtm->rtm_msglen;
750 rtm->rtm_seq = ++seq;
751 rtm->rtm_type = cmd;
752 if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
753 if (errno != ESRCH || cmd != RTM_DELETE) {
754 warn("writing to routing socket");
755 return (NULL);
756 }
757 }
758 do {
759 l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
760 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
761 if (l < 0)
762 warn("read from routing socket");
763 return (rtm);
764}
765
766/*
767 * get_ether_addr - get the hardware address of an interface on the
768 * the same subnet as ipaddr.
769 */
770#define MAX_IFS 32
771
772static int
773get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
774{
775 struct ifreq *ifr, *ifend, *ifp;
776 in_addr_t ina, mask;
777 struct sockaddr_dl *dla;
778 struct ifreq ifreq;
779 struct ifconf ifc;
780 struct ifreq ifs[MAX_IFS];
781 int sock;
782 int retval = 0;
783
784 sock = socket(AF_INET, SOCK_DGRAM, 0);
785 if (sock < 0)
786 err(1, "socket");
787
788 ifc.ifc_len = sizeof(ifs);
789 ifc.ifc_req = ifs;
790 if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
791 warnx("ioctl(SIOCGIFCONF)");
792 goto done;
793 }
794
795#define NEXTIFR(i) \
796 ((struct ifreq *)((char *)&(i)->ifr_addr \
797 + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
798
799 /*
800 * Scan through looking for an interface with an Internet
801 * address on the same subnet as `ipaddr'.
802 */
803 ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
804 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
805 if (ifr->ifr_addr.sa_family != AF_INET)
806 continue;
807 strncpy(ifreq.ifr_name, ifr->ifr_name,
808 sizeof(ifreq.ifr_name));
809 ifreq.ifr_addr = ifr->ifr_addr;
810 /*
811 * Check that the interface is up,
812 * and not point-to-point or loopback.
813 */
814 if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
815 continue;
816 if ((ifreq.ifr_flags &
817 (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
818 IFF_LOOPBACK|IFF_NOARP))
819 != (IFF_UP|IFF_BROADCAST))
820 continue;
821 /*
822 * Get its netmask and check that it's on
823 * the right subnet.
824 */
825 if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
826 continue;
827 mask = ((struct sockaddr_in *)
828 &ifreq.ifr_addr)->sin_addr.s_addr;
829 ina = ((struct sockaddr_in *)
830 &ifr->ifr_addr)->sin_addr.s_addr;
831 if ((ipaddr & mask) == (ina & mask))
832 break; /* ok, we got it! */
833 }
834
835 if (ifr >= ifend)
836 goto done;
837
838 /*
839 * Now scan through again looking for a link-level address
840 * for this interface.
841 */
842 ifp = ifr;
843 for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
844 if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
845 ifr->ifr_addr.sa_family == AF_LINK)
846 break;
847 if (ifr >= ifend)
848 goto done;
849 /*
850 * Found the link-level address - copy it out
851 */
852 dla = (struct sockaddr_dl *) &ifr->ifr_addr;
853 memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
854 printf("using interface %s for proxy with address ",
855 ifp->ifr_name);
856 printf("%s\n", ether_ntoa(hwaddr));
857 retval = dla->sdl_alen;
858done:
859 close(sock);
860 return (retval);
861}