1 /* $OpenBSD: ifaddr.c,v 1.8 2023/03/08 04:43:06 guenther Exp $ */
2
3 /*
4 * This file has been copied from ifconfig and adapted to test
5 * SIOCSIFADDR, SIOCSIFNETMASK, SIOCSIFDSTADDR, SIOCSIFBRDADDR
6 * ioctls. Usually ifconfig uses SIOCAIFADDR and SIOCDIFADDR, but
7 * the old kernel interface has to be tested, too.
8 */
9
10 /*
11 * Copyright (c) 1983, 1993
12 * The Regents of the University of California. All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 /*-
40 * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc.
41 * All rights reserved.
42 *
43 * This code is derived from software contributed to The NetBSD Foundation
44 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
45 * NASA Ames Research Center.
46 *
47 * Redistribution and use in source and binary forms, with or without
48 * modification, are permitted provided that the following conditions
49 * are met:
50 * 1. Redistributions of source code must retain the above copyright
51 * notice, this list of conditions and the following disclaimer.
52 * 2. Redistributions in binary form must reproduce the above copyright
53 * notice, this list of conditions and the following disclaimer in the
54 * documentation and/or other materials provided with the distribution.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
57 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
58 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
59 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
60 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
61 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
62 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
63 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
64 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
65 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
66 * POSSIBILITY OF SUCH DAMAGE.
67 */
68
69 /*
70 * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
71 *
72 * Permission to use, copy, modify, and distribute this software for any
73 * purpose with or without fee is hereby granted, provided that the above
74 * copyright notice and this permission notice appear in all copies.
75 *
76 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
77 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
78 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
79 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
80 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
81 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
82 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
83 */
84
85 #include <sys/socket.h>
86 #include <sys/ioctl.h>
87 #include <sys/time.h>
88
89 #include <net/if.h>
90 #include <net/if_dl.h>
91 #include <net/if_media.h>
92 #include <net/if_types.h>
93 #include <netinet/in.h>
94 #include <netinet/in_var.h>
95 #include <netinet6/in6_var.h>
96 #include <netinet6/nd6.h>
97 #include <arpa/inet.h>
98 #include <netinet/ip_ipsp.h>
99 #include <netinet/if_ether.h>
100
101 #include <netdb.h>
102
103 #include <net/if_vlan_var.h>
104
105 #include <ctype.h>
106 #include <err.h>
107 #include <errno.h>
108 #include <stdio.h>
109 #include <stdint.h>
110 #include <stdlib.h>
111 #include <string.h>
112 #include <unistd.h>
113 #include <limits.h>
114 #include <resolv.h>
115 #include <util.h>
116 #include <ifaddrs.h>
117
118 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
119 #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
120
121 #define HWFEATURESBITS \
122 "\024\1CSUM_IPv4\2CSUM_TCPv4\3CSUM_UDPv4" \
123 "\5VLAN_MTU\6VLAN_HWTAGGING\10CSUM_TCPv6" \
124 "\11CSUM_UDPv6\20WOL"
125
126 struct ifreq ifr, ridreq;
127 struct in_aliasreq in_addreq;
128 struct in6_ifreq ifr6;
129 struct in6_ifreq in6_ridreq;
130 struct in6_aliasreq in6_addreq;
131 struct sockaddr_in netmask;
132
133 char ifname[IFNAMSIZ];
134 int flags, xflags, setaddr, setmask, setipdst, setbroad, doalias;
135 u_long metric, mtu;
136 int rdomainid;
137 int llprio;
138 int clearaddr, sock;
139 int newaddr = 0;
140 int af = AF_INET;
141 int explicit_prefix = 0;
142 int Lflag = 1;
143
144 int showcapsflag;
145
146 void notealias(const char *, int);
147 void setifaddr(const char *, int);
148 void setifrtlabel(const char *, int);
149 void setifdstaddr(const char *, int);
150 void addaf(const char *, int);
151 void removeaf(const char *, int);
152 void setifbroadaddr(const char *, int);
153 void setifnetmask(const char *, int);
154 void setifprefixlen(const char *, int);
155 void settunnel(const char *, const char *);
156 void settunneladdr(const char *, int);
157 void deletetunnel(const char *, int);
158 void settunnelinst(const char *, int);
159 void unsettunnelinst(const char *, int);
160 void settunnelttl(const char *, int);
161 void setia6flags(const char *, int);
162 void setia6pltime(const char *, int);
163 void setia6vltime(const char *, int);
164 void setia6lifetime(const char *, const char *);
165 void setia6eui64(const char *, int);
166 void setrdomain(const char *, int);
167 void unsetrdomain(const char *, int);
168 int prefix(void *val, int);
169 int printgroup(char *, int);
170 void setifipdst(const char *, int);
171 void setignore(const char *, int);
172
173 int actions; /* Actions performed */
174
175 #define A_SILENT 0x8000000 /* doing operation, do not print */
176
177 #define NEXTARG0 0xffffff
178 #define NEXTARG 0xfffffe
179 #define NEXTARG2 0xfffffd
180
181 const struct cmd {
182 char *c_name;
183 int c_parameter; /* NEXTARG means next argv */
184 int c_action; /* defered action */
185 void (*c_func)(const char *, int);
186 void (*c_func2)(const char *, const char *);
187 } cmds[] = {
188 { "alias", IFF_UP, 0, notealias },
189 { "-alias", -IFF_UP, 0, notealias },
190 { "delete", -IFF_UP, 0, notealias },
191 { "netmask", NEXTARG, 0, setifnetmask },
192 { "broadcast", NEXTARG, 0, setifbroadaddr },
193 { "prefixlen", NEXTARG, 0, setifprefixlen},
194 { "anycast", IN6_IFF_ANYCAST, 0, setia6flags },
195 { "-anycast", -IN6_IFF_ANYCAST, 0, setia6flags },
196 { "tentative", IN6_IFF_TENTATIVE, 0, setia6flags },
197 { "-tentative", -IN6_IFF_TENTATIVE, 0, setia6flags },
198 { "pltime", NEXTARG, 0, setia6pltime },
199 { "vltime", NEXTARG, 0, setia6vltime },
200 { "eui64", 0, 0, setia6eui64 },
201 #ifndef SMALL
202 { "rtlabel", NEXTARG, 0, setifrtlabel },
203 { "-rtlabel", -1, 0, setifrtlabel },
204 { "rdomain", NEXTARG, 0, setrdomain },
205 { "-rdomain", 0, 0, unsetrdomain },
206 { "tunnel", NEXTARG2, 0, NULL, settunnel },
207 { "tunneladdr", NEXTARG, 0, settunneladdr },
208 { "-tunnel", 0, 0, deletetunnel },
209 { "tunneldomain", NEXTARG, 0, settunnelinst },
210 { "-tunneldomain", 0, 0, unsettunnelinst },
211 { "tunnelttl", NEXTARG, 0, settunnelttl },
212 { "-inet", AF_INET, 0, removeaf },
213 { "-inet6", AF_INET6, 0, removeaf },
214 { "ipdst", NEXTARG, 0, setifipdst },
215 #endif /* SMALL */
216 { NULL, /*src*/ 0, 0, setifaddr },
217 { NULL, /*dst*/ 0, 0, setifdstaddr },
218 { NULL, /*illegal*/0, 0, NULL },
219 };
220
221 #define IFFBITS \
222 "\024\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6STATICARP" \
223 "\7RUNNING\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX" \
224 "\15LINK0\16LINK1\17LINK2\20MULTICAST" \
225 "\23AUTOCONF6TEMP\24MPLS\25WOL\26AUTOCONF6\27INET6_NOSOII" \
226 "\30AUTOCONF4"
227
228 int getinfo(struct ifreq *, int);
229 void getsock(int);
230 void printif(char *, int);
231 void printb(char *, unsigned int, unsigned char *);
232 void printb_status(unsigned short, unsigned char *);
233 const char *get_linkstate(int, int);
234 void status(int, struct sockaddr_dl *, int);
235 __dead void usage(void);
236 const char *get_string(const char *, const char *, u_int8_t *, int *);
237 int len_string(const u_int8_t *, int);
238 int print_string(const u_int8_t *, int);
239 char *sec2str(time_t);
240
241 unsigned long get_ts_map(int, int, int);
242
243 void in_status(int);
244 void in_getaddr(const char *, int);
245 void in_getprefix(const char *, int);
246 void in6_fillscopeid(struct sockaddr_in6 *);
247 void in6_alias(struct in6_ifreq *);
248 void in6_status(int);
249 void in6_getaddr(const char *, int);
250 void in6_getprefix(const char *, int);
251
252 /* Known address families */
253 const struct afswtch {
254 char *af_name;
255 short af_af;
256 void (*af_status)(int);
257 void (*af_getaddr)(const char *, int);
258 void (*af_getprefix)(const char *, int);
259 u_long af_difaddr;
260 u_long af_aifaddr;
261 caddr_t af_ridreq;
262 caddr_t af_addreq;
263 } afs[] = {
264 #define C(x) ((caddr_t) &x)
265 { "inet", AF_INET, in_status, in_getaddr, in_getprefix,
266 SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(in_addreq) },
267 { "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
268 SIOCDIFADDR_IN6, SIOCAIFADDR_IN6, C(in6_ridreq), C(in6_addreq) },
269 { 0, 0, 0, 0 }
270 };
271
272 const struct afswtch *afp; /*the address family being set or asked about*/
273
274 int ifaliases = 0;
275 int aflag = 0;
276
277 int
main(int argc,char * argv[])278 main(int argc, char *argv[])
279 {
280 const struct afswtch *rafp = NULL;
281 int create = 0;
282 int i;
283
284 /* If no args at all, print all interfaces. */
285 if (argc < 2) {
286 /* no filesystem visibility */
287 if (unveil("/", "") == -1)
288 err(1, "unveil /");
289 if (unveil(NULL, NULL) == -1)
290 err(1, "unveil");
291 aflag = 1;
292 printif(NULL, 0);
293 return (0);
294 }
295 argc--, argv++;
296 if (*argv[0] == '-') {
297 int nomore = 0;
298
299 for (i = 1; argv[0][i]; i++) {
300 switch (argv[0][i]) {
301 case 'a':
302 aflag = 1;
303 nomore = 1;
304 break;
305 case 'A':
306 aflag = 1;
307 ifaliases = 1;
308 nomore = 1;
309 break;
310 default:
311 usage();
312 break;
313 }
314 }
315 if (nomore == 0) {
316 argc--, argv++;
317 if (argc < 1)
318 usage();
319 if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
320 errx(1, "interface name '%s' too long", *argv);
321 }
322 } else if (strlcpy(ifname, *argv, sizeof(ifname)) >= IFNAMSIZ)
323 errx(1, "interface name '%s' too long", *argv);
324 argc--, argv++;
325
326 if (unveil(_PATH_RESCONF, "r") == -1)
327 err(1, "unveil %s", _PATH_RESCONF);
328 if (unveil(_PATH_HOSTS, "r") == -1)
329 err(1, "unveil %s", _PATH_HOSTS);
330 if (unveil(_PATH_SERVICES, "r") == -1)
331 err(1, "unveil %s", _PATH_SERVICES);
332 if (unveil(NULL, NULL) == -1)
333 err(1, "unveil");
334
335 if (argc > 0) {
336 for (afp = rafp = afs; rafp->af_name; rafp++)
337 if (strcmp(rafp->af_name, *argv) == 0) {
338 afp = rafp;
339 argc--;
340 argv++;
341 break;
342 }
343 rafp = afp;
344 af = ifr.ifr_addr.sa_family = rafp->af_af;
345 }
346 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
347
348 /* initialization */
349 in6_addreq.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
350 in6_addreq.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
351
352 if (aflag == 0) {
353 create = (argc > 0) && strcmp(argv[0], "destroy") != 0;
354 (void)getinfo(&ifr, create);
355 }
356
357 if (argc != 0 && af == AF_INET6)
358 addaf(ifname, AF_INET6);
359
360 while (argc > 0) {
361 const struct cmd *p;
362
363 for (p = cmds; p->c_name; p++)
364 if (strcmp(*argv, p->c_name) == 0)
365 break;
366 if (p->c_name == 0 && setaddr)
367 for (i = setaddr; i > 0; i--) {
368 p++;
369 if (p->c_func == NULL)
370 errx(1, "%s: bad value", *argv);
371 }
372 if (p->c_func || p->c_func2) {
373 if (p->c_parameter == NEXTARG0) {
374 const struct cmd *p0;
375 int noarg = 1;
376
377 if (argv[1]) {
378 for (p0 = cmds; p0->c_name; p0++)
379 if (strcmp(argv[1],
380 p0->c_name) == 0) {
381 noarg = 0;
382 break;
383 }
384 } else
385 noarg = 0;
386
387 if (noarg == 0)
388 (*p->c_func)(NULL, 0);
389 else
390 goto nextarg;
391 } else if (p->c_parameter == NEXTARG) {
392 nextarg:
393 if (argv[1] == NULL)
394 errx(1, "'%s' requires argument",
395 p->c_name);
396 (*p->c_func)(argv[1], 0);
397 argc--, argv++;
398 actions = actions | A_SILENT | p->c_action;
399 } else if (p->c_parameter == NEXTARG2) {
400 if ((argv[1] == NULL) ||
401 (argv[2] == NULL))
402 errx(1, "'%s' requires 2 arguments",
403 p->c_name);
404 (*p->c_func2)(argv[1], argv[2]);
405 argc -= 2;
406 argv += 2;
407 actions = actions | A_SILENT | p->c_action;
408 } else {
409 (*p->c_func)(*argv, p->c_parameter);
410 actions = actions | A_SILENT | p->c_action;
411 }
412 }
413 argc--, argv++;
414 }
415
416 if (argc == 0 && actions == 0) {
417 printif(ifr.ifr_name, aflag ? ifaliases : 1);
418 return (0);
419 }
420
421 if (af == AF_INET6 && explicit_prefix == 0) {
422 /*
423 * Aggregatable address architecture defines all prefixes
424 * are 64. So, it is convenient to set prefixlen to 64 if
425 * it is not specified. If we are setting a destination
426 * address on a point-to-point interface, 128 is required.
427 */
428 if (setipdst && (flags & IFF_POINTOPOINT))
429 setifprefixlen("128", 0);
430 else
431 setifprefixlen("64", 0);
432 /* in6_getprefix("64", MASK) if MASK is available here... */
433 }
434
435 if (doalias == 0 || (newaddr && clearaddr)) {
436 (void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
437 /* IPv4 only, inet6 does not have such ioctls */
438 if (setaddr) {
439 memcpy(&ridreq.ifr_addr, &in_addreq.ifra_addr,
440 in_addreq.ifra_addr.sin_len);
441 if (ioctl(sock, SIOCSIFADDR, rafp->af_ridreq) == -1)
442 err(1, "SIOCSIFADDR");
443 }
444 if (setmask) {
445 memcpy(&ridreq.ifr_addr, &in_addreq.ifra_mask,
446 in_addreq.ifra_mask.sin_len);
447 if (ioctl(sock, SIOCSIFNETMASK, rafp->af_ridreq) == -1)
448 err(1, "SIOCSIFNETMASK");
449 }
450 if (setipdst) {
451 memcpy(&ridreq.ifr_addr, &in_addreq.ifra_dstaddr,
452 in_addreq.ifra_dstaddr.sin_len);
453 if (ioctl(sock, SIOCSIFDSTADDR, rafp->af_ridreq) == -1)
454 err(1, "SIOCSIFDSTADDR");
455 }
456 if (setbroad) {
457 memcpy(&ridreq.ifr_addr, &in_addreq.ifra_broadaddr,
458 in_addreq.ifra_broadaddr.sin_len);
459 if (ioctl(sock, SIOCSIFBRDADDR, rafp->af_ridreq) == -1)
460 err(1, "SIOCSIFBRDADDR");
461 }
462 return (0);
463 }
464 if (clearaddr) {
465 (void) strlcpy(rafp->af_ridreq, ifname, sizeof(ifr.ifr_name));
466 if (ioctl(sock, rafp->af_difaddr, rafp->af_ridreq) == -1) {
467 if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
468 /* means no previous address for interface */
469 } else
470 err(1, "SIOCDIFADDR");
471 }
472 }
473 if (newaddr) {
474 (void) strlcpy(rafp->af_addreq, ifname, sizeof(ifr.ifr_name));
475 if (ioctl(sock, rafp->af_aifaddr, rafp->af_addreq) == -1)
476 err(1, "SIOCAIFADDR");
477 }
478 return (0);
479 }
480
481 void
getsock(int naf)482 getsock(int naf)
483 {
484 static int oaf = -1;
485
486 if (oaf == naf)
487 return;
488 if (oaf != -1)
489 close(sock);
490 sock = socket(naf, SOCK_DGRAM, 0);
491 if (sock == -1)
492 oaf = -1;
493 else
494 oaf = naf;
495 }
496
497 int
getinfo(struct ifreq * ifr,int create)498 getinfo(struct ifreq *ifr, int create)
499 {
500
501 getsock(af);
502 if (sock == -1)
503 err(1, "socket");
504 if (!isdigit((unsigned char)ifname[strlen(ifname) - 1]))
505 return (-1); /* ignore groups here */
506 if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1) {
507 int oerrno = errno;
508
509 if (!create)
510 return (-1);
511 if (ioctl(sock, SIOCIFCREATE, (caddr_t)ifr) == -1) {
512 errno = oerrno;
513 return (-1);
514 }
515 if (ioctl(sock, SIOCGIFFLAGS, (caddr_t)ifr) == -1)
516 return (-1);
517 }
518 flags = ifr->ifr_flags & 0xffff;
519 if (ioctl(sock, SIOCGIFXFLAGS, (caddr_t)ifr) == -1)
520 ifr->ifr_flags = 0;
521 xflags = ifr->ifr_flags;
522 if (ioctl(sock, SIOCGIFMETRIC, (caddr_t)ifr) == -1)
523 metric = 0;
524 else
525 metric = ifr->ifr_metric;
526 if (ioctl(sock, SIOCGIFMTU, (caddr_t)ifr) == -1)
527 mtu = 0;
528 else
529 mtu = ifr->ifr_mtu;
530 #ifndef SMALL
531 if (ioctl(sock, SIOCGIFRDOMAIN, (caddr_t)ifr) == -1)
532 rdomainid = 0;
533 else
534 rdomainid = ifr->ifr_rdomainid;
535 #endif
536 if (ioctl(sock, SIOCGIFLLPRIO, (caddr_t)ifr) == -1)
537 llprio = 0;
538 else
539 llprio = ifr->ifr_llprio;
540
541 return (0);
542 }
543
544 int
printgroup(char * groupname,int ifaliases)545 printgroup(char *groupname, int ifaliases)
546 {
547 struct ifgroupreq ifgr;
548 struct ifg_req *ifg;
549 int len, cnt = 0;
550
551 getsock(AF_INET);
552 bzero(&ifgr, sizeof(ifgr));
553 strlcpy(ifgr.ifgr_name, groupname, sizeof(ifgr.ifgr_name));
554 if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
555 if (errno == EINVAL || errno == ENOTTY ||
556 errno == ENOENT)
557 return (-1);
558 else
559 err(1, "SIOCGIFGMEMB");
560 }
561
562 len = ifgr.ifgr_len;
563 if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
564 err(1, "printgroup");
565 if (ioctl(sock, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
566 err(1, "SIOCGIFGMEMB");
567
568 for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
569 ifg++) {
570 len -= sizeof(struct ifg_req);
571 printif(ifg->ifgrq_member, ifaliases);
572 cnt++;
573 }
574 free(ifgr.ifgr_groups);
575
576 return (cnt);
577 }
578
579 void
printif(char * name,int ifaliases)580 printif(char *name, int ifaliases)
581 {
582 struct ifaddrs *ifap, *ifa;
583 struct if_data *ifdata;
584 const char *namep;
585 char *oname = NULL;
586 struct ifreq *ifrp;
587 int count = 0, noinet = 1;
588 size_t nlen = 0;
589
590 if (aflag)
591 name = NULL;
592 if (name) {
593 if ((oname = strdup(name)) == NULL)
594 err(1, "strdup");
595 nlen = strlen(oname);
596 /* is it a group? */
597 if (nlen && !isdigit((unsigned char)oname[nlen - 1]))
598 if (printgroup(oname, ifaliases) != -1) {
599 free(oname);
600 return;
601 }
602 }
603
604 if (getifaddrs(&ifap) != 0)
605 err(1, "getifaddrs");
606
607 namep = NULL;
608 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
609 if (oname) {
610 if (nlen && isdigit((unsigned char)oname[nlen - 1])) {
611 /* must have exact match */
612 if (strcmp(oname, ifa->ifa_name) != 0)
613 continue;
614 } else {
615 /* partial match OK if it ends w/ digit */
616 if (strncmp(oname, ifa->ifa_name, nlen) != 0 ||
617 !isdigit((unsigned char)ifa->ifa_name[nlen]))
618 continue;
619 }
620 }
621 /* quickhack: sizeof(ifr) < sizeof(ifr6) */
622 if (ifa->ifa_addr->sa_family == AF_INET6) {
623 memset(&ifr6, 0, sizeof(ifr6));
624 memcpy(&ifr6.ifr_addr, ifa->ifa_addr,
625 MINIMUM(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
626 ifrp = (struct ifreq *)&ifr6;
627 } else {
628 memset(&ifr, 0, sizeof(ifr));
629 memcpy(&ifr.ifr_addr, ifa->ifa_addr,
630 MINIMUM(sizeof(ifr.ifr_addr), ifa->ifa_addr->sa_len));
631 ifrp = 𝔦
632 }
633 strlcpy(ifname, ifa->ifa_name, sizeof(ifname));
634 strlcpy(ifrp->ifr_name, ifa->ifa_name, sizeof(ifrp->ifr_name));
635
636 if (ifa->ifa_addr->sa_family == AF_LINK) {
637 namep = ifa->ifa_name;
638 if (getinfo(ifrp, 0) < 0)
639 continue;
640 ifdata = ifa->ifa_data;
641 status(1, (struct sockaddr_dl *)ifa->ifa_addr,
642 ifdata->ifi_link_state);
643 count++;
644 noinet = 1;
645 continue;
646 }
647
648 if (!namep || !strcmp(namep, ifa->ifa_name)) {
649 const struct afswtch *p;
650
651 if (ifa->ifa_addr->sa_family == AF_INET &&
652 ifaliases == 0 && noinet == 0)
653 continue;
654 if ((p = afp) != NULL) {
655 if (ifa->ifa_addr->sa_family == p->af_af)
656 p->af_status(1);
657 } else {
658 for (p = afs; p->af_name; p++) {
659 if (ifa->ifa_addr->sa_family ==
660 p->af_af)
661 p->af_status(0);
662 }
663 }
664 count++;
665 if (ifa->ifa_addr->sa_family == AF_INET)
666 noinet = 0;
667 continue;
668 }
669 }
670 freeifaddrs(ifap);
671 free(oname);
672 if (count == 0) {
673 fprintf(stderr, "%s: no such interface\n", ifname);
674 exit(1);
675 }
676 }
677
678 #define RIDADDR 0
679 #define ADDR 1
680 #define MASK 2
681 #define DSTADDR 3
682
683 void
setifaddr(const char * addr,int param)684 setifaddr(const char *addr, int param)
685 {
686 /*
687 * Delay the ioctl to set the interface addr until flags are all set.
688 * The address interpretation may depend on the flags,
689 * and the flags may change when the address is set.
690 */
691 setaddr++;
692 if (doalias >= 0)
693 newaddr = 1;
694 if (doalias == 0)
695 clearaddr = 1;
696 afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
697 }
698
699 #ifndef SMALL
700 void
setifrtlabel(const char * label,int d)701 setifrtlabel(const char *label, int d)
702 {
703 if (d != 0)
704 ifr.ifr_data = (caddr_t)(const char *)"";
705 else
706 ifr.ifr_data = (caddr_t)label;
707 if (ioctl(sock, SIOCSIFRTLABEL, &ifr) == -1)
708 warn("SIOCSIFRTLABEL");
709 }
710 #endif
711
712 void
setifnetmask(const char * addr,int ignored)713 setifnetmask(const char *addr, int ignored)
714 {
715 setmask++;
716 afp->af_getaddr(addr, MASK);
717 explicit_prefix = 1;
718 }
719
720 void
setifbroadaddr(const char * addr,int ignored)721 setifbroadaddr(const char *addr, int ignored)
722 {
723 setbroad++;
724 afp->af_getaddr(addr, DSTADDR);
725 }
726
727 void
setifipdst(const char * addr,int ignored)728 setifipdst(const char *addr, int ignored)
729 {
730 in_getaddr(addr, DSTADDR);
731 setipdst++;
732 clearaddr = 0;
733 newaddr = 0;
734 }
735
736 #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
737 void
notealias(const char * addr,int param)738 notealias(const char *addr, int param)
739 {
740 if (setaddr && doalias == 0 && param < 0)
741 memcpy(rqtosa(af_ridreq), rqtosa(af_addreq),
742 rqtosa(af_addreq)->sa_len);
743 doalias = param;
744 if (param < 0) {
745 clearaddr = 1;
746 newaddr = 0;
747 } else
748 clearaddr = 0;
749 }
750
751 void
setifdstaddr(const char * addr,int param)752 setifdstaddr(const char *addr, int param)
753 {
754 setaddr++;
755 setipdst++;
756 afp->af_getaddr(addr, DSTADDR);
757 }
758
759 void
addaf(const char * vname,int value)760 addaf(const char *vname, int value)
761 {
762 struct if_afreq ifar;
763
764 strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
765 ifar.ifar_af = value;
766 if (ioctl(sock, SIOCIFAFATTACH, (caddr_t)&ifar) == -1)
767 warn("SIOCIFAFATTACH");
768 }
769
770 void
removeaf(const char * vname,int value)771 removeaf(const char *vname, int value)
772 {
773 struct if_afreq ifar;
774
775 strlcpy(ifar.ifar_name, ifname, sizeof(ifar.ifar_name));
776 ifar.ifar_af = value;
777 if (ioctl(sock, SIOCIFAFDETACH, (caddr_t)&ifar) == -1)
778 warn("SIOCIFAFDETACH");
779 }
780
781 void
setia6flags(const char * vname,int value)782 setia6flags(const char *vname, int value)
783 {
784
785 if (value < 0) {
786 value = -value;
787 in6_addreq.ifra_flags &= ~value;
788 } else
789 in6_addreq.ifra_flags |= value;
790 }
791
792 void
setia6pltime(const char * val,int d)793 setia6pltime(const char *val, int d)
794 {
795
796 setia6lifetime("pltime", val);
797 }
798
799 void
setia6vltime(const char * val,int d)800 setia6vltime(const char *val, int d)
801 {
802
803 setia6lifetime("vltime", val);
804 }
805
806 void
setia6lifetime(const char * cmd,const char * val)807 setia6lifetime(const char *cmd, const char *val)
808 {
809 const char *errmsg = NULL;
810 time_t newval, t;
811
812 newval = strtonum(val, 0, 1000000, &errmsg);
813 if (errmsg)
814 errx(1, "invalid %s %s: %s", cmd, val, errmsg);
815
816 t = time(NULL);
817
818 if (afp->af_af != AF_INET6)
819 errx(1, "%s not allowed for this address family", cmd);
820 if (strcmp(cmd, "vltime") == 0) {
821 in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
822 in6_addreq.ifra_lifetime.ia6t_vltime = newval;
823 } else if (strcmp(cmd, "pltime") == 0) {
824 in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
825 in6_addreq.ifra_lifetime.ia6t_pltime = newval;
826 }
827 }
828
829 void
setia6eui64(const char * cmd,int val)830 setia6eui64(const char *cmd, int val)
831 {
832 struct ifaddrs *ifap, *ifa;
833 const struct sockaddr_in6 *sin6 = NULL;
834 const struct in6_addr *lladdr = NULL;
835 struct in6_addr *in6;
836
837 if (afp->af_af != AF_INET6)
838 errx(1, "%s not allowed for this address family", cmd);
839
840 addaf(ifname, AF_INET6);
841
842 in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
843 if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
844 errx(1, "interface index is already filled");
845 if (getifaddrs(&ifap) != 0)
846 err(1, "getifaddrs");
847 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
848 if (ifa->ifa_addr->sa_family == AF_INET6 &&
849 strcmp(ifa->ifa_name, ifname) == 0) {
850 sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
851 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
852 lladdr = &sin6->sin6_addr;
853 break;
854 }
855 }
856 }
857 if (!lladdr)
858 errx(1, "could not determine link local address");
859
860 memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
861
862 freeifaddrs(ifap);
863 }
864
865 const char *
get_string(const char * val,const char * sep,u_int8_t * buf,int * lenp)866 get_string(const char *val, const char *sep, u_int8_t *buf, int *lenp)
867 {
868 int len = *lenp, hexstr;
869 u_int8_t *p = buf;
870
871 hexstr = (val[0] == '0' && tolower((u_char)val[1]) == 'x');
872 if (hexstr)
873 val += 2;
874 for (;;) {
875 if (*val == '\0')
876 break;
877 if (sep != NULL && strchr(sep, *val) != NULL) {
878 val++;
879 break;
880 }
881 if (hexstr) {
882 if (!isxdigit((u_char)val[0]) ||
883 !isxdigit((u_char)val[1])) {
884 warnx("bad hexadecimal digits");
885 return NULL;
886 }
887 }
888 if (p > buf + len) {
889 if (hexstr)
890 warnx("hexadecimal digits too long");
891 else
892 warnx("strings too long");
893 return NULL;
894 }
895 if (hexstr) {
896 #define tohex(x) (isdigit(x) ? (x) - '0' : tolower(x) - 'a' + 10)
897 *p++ = (tohex((u_char)val[0]) << 4) |
898 tohex((u_char)val[1]);
899 #undef tohex
900 val += 2;
901 } else {
902 if (*val == '\\' &&
903 sep != NULL && strchr(sep, *(val + 1)) != NULL)
904 val++;
905 *p++ = *val++;
906 }
907 }
908 len = p - buf;
909 if (len < *lenp)
910 memset(p, 0, *lenp - len);
911 *lenp = len;
912 return val;
913 }
914
915 int
len_string(const u_int8_t * buf,int len)916 len_string(const u_int8_t *buf, int len)
917 {
918 int i = 0, hasspc = 0;
919
920 if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
921 for (; i < len; i++) {
922 /* Only print 7-bit ASCII keys */
923 if (buf[i] & 0x80 || !isprint(buf[i]))
924 break;
925 if (isspace(buf[i]))
926 hasspc++;
927 }
928 }
929 if (i == len) {
930 if (hasspc || len == 0)
931 return len + 2;
932 else
933 return len;
934 } else
935 return (len * 2) + 2;
936 }
937
938 int
print_string(const u_int8_t * buf,int len)939 print_string(const u_int8_t *buf, int len)
940 {
941 int i = 0, hasspc = 0;
942
943 if (len < 2 || buf[0] != '0' || tolower(buf[1]) != 'x') {
944 for (; i < len; i++) {
945 /* Only print 7-bit ASCII keys */
946 if (buf[i] & 0x80 || !isprint(buf[i]))
947 break;
948 if (isspace(buf[i]))
949 hasspc++;
950 }
951 }
952 if (i == len) {
953 if (hasspc || len == 0) {
954 printf("\"%.*s\"", len, buf);
955 return len + 2;
956 } else {
957 printf("%.*s", len, buf);
958 return len;
959 }
960 } else {
961 printf("0x");
962 for (i = 0; i < len; i++)
963 printf("%02x", buf[i]);
964 return (len * 2) + 2;
965 }
966 }
967
968 static void
print_tunnel(const struct if_laddrreq * req)969 print_tunnel(const struct if_laddrreq *req)
970 {
971 char psrcaddr[NI_MAXHOST];
972 char pdstaddr[NI_MAXHOST];
973 const char *ver = "";
974 const int niflag = NI_NUMERICHOST;
975
976 if (req == NULL) {
977 printf("(unset)");
978 return;
979 }
980
981 psrcaddr[0] = pdstaddr[0] = '\0';
982
983 if (getnameinfo((struct sockaddr *)&req->addr, req->addr.ss_len,
984 psrcaddr, sizeof(psrcaddr), 0, 0, niflag) != 0)
985 strlcpy(psrcaddr, "<error>", sizeof(psrcaddr));
986 if (req->addr.ss_family == AF_INET6)
987 ver = "6";
988
989 printf("inet%s %s", ver, psrcaddr);
990
991 if (req->dstaddr.ss_family != AF_UNSPEC) {
992 in_port_t dstport = 0;
993 const struct sockaddr_in *sin;
994 const struct sockaddr_in6 *sin6;
995
996 if (getnameinfo((struct sockaddr *)&req->dstaddr,
997 req->dstaddr.ss_len, pdstaddr, sizeof(pdstaddr),
998 0, 0, niflag) != 0)
999 strlcpy(pdstaddr, "<error>", sizeof(pdstaddr));
1000
1001 printf(" -> %s", pdstaddr);
1002
1003 switch (req->dstaddr.ss_family) {
1004 case AF_INET:
1005 sin = (const struct sockaddr_in *)&req->dstaddr;
1006 dstport = sin->sin_port;
1007 break;
1008 case AF_INET6:
1009 sin6 = (const struct sockaddr_in6 *)&req->dstaddr;
1010 dstport = sin6->sin6_port;
1011 break;
1012 }
1013
1014 if (dstport)
1015 printf(":%u", ntohs(dstport));
1016 }
1017 }
1018
1019 static void
phys_status(int force)1020 phys_status(int force)
1021 {
1022 struct if_laddrreq req;
1023 struct if_laddrreq *r = &req;
1024
1025 memset(&req, 0, sizeof(req));
1026 (void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
1027 if (ioctl(sock, SIOCGLIFPHYADDR, (caddr_t)&req) == -1) {
1028 if (errno != EADDRNOTAVAIL)
1029 return;
1030
1031 r = NULL;
1032 }
1033
1034 printf("\ttunnel: ");
1035 print_tunnel(r);
1036
1037 if (ioctl(sock, SIOCGLIFPHYTTL, (caddr_t)&ifr) == 0) {
1038 if (ifr.ifr_ttl == -1)
1039 printf(" ttl copy");
1040 else if (ifr.ifr_ttl > 0)
1041 printf(" ttl %d", ifr.ifr_ttl);
1042 }
1043
1044 if (ioctl(sock, SIOCGLIFPHYDF, (caddr_t)&ifr) == 0)
1045 printf(" %s", ifr.ifr_df ? "df" : "nodf");
1046
1047 #ifndef SMALL
1048 if (ioctl(sock, SIOCGLIFPHYECN, (caddr_t)&ifr) == 0)
1049 printf(" %s", ifr.ifr_metric ? "ecn" : "noecn");
1050
1051 if (ioctl(sock, SIOCGLIFPHYRTABLE, (caddr_t)&ifr) == 0 &&
1052 (rdomainid != 0 || ifr.ifr_rdomainid != 0))
1053 printf(" rdomain %d", ifr.ifr_rdomainid);
1054 #endif
1055 printf("\n");
1056 }
1057
1058 #ifndef SMALL
1059 const uint64_t ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
1060
1061 const struct ifmedia_status_description ifm_status_descriptions[] =
1062 IFM_STATUS_DESCRIPTIONS;
1063 #endif
1064
1065 const struct if_status_description if_status_descriptions[] =
1066 LINK_STATE_DESCRIPTIONS;
1067
1068 const char *
get_linkstate(int mt,int link_state)1069 get_linkstate(int mt, int link_state)
1070 {
1071 const struct if_status_description *p;
1072 static char buf[8];
1073
1074 for (p = if_status_descriptions; p->ifs_string != NULL; p++) {
1075 if (LINK_STATE_DESC_MATCH(p, mt, link_state))
1076 return (p->ifs_string);
1077 }
1078 snprintf(buf, sizeof(buf), "[#%d]", link_state);
1079 return buf;
1080 }
1081
1082 /*
1083 * Print the status of the interface. If an address family was
1084 * specified, show it and it only; otherwise, show them all.
1085 */
1086 void
status(int link,struct sockaddr_dl * sdl,int ls)1087 status(int link, struct sockaddr_dl *sdl, int ls)
1088 {
1089 const struct afswtch *p = afp;
1090 struct ifmediareq ifmr;
1091 #ifndef SMALL
1092 struct ifreq ifrdesc;
1093 char ifdescr[IFDESCRSIZE];
1094 #endif
1095 uint64_t *media_list;
1096 char sep;
1097
1098
1099 printf("%s: ", ifname);
1100 printb("flags", flags | (xflags << 16), IFFBITS);
1101 if (rdomainid)
1102 printf(" rdomain %d", rdomainid);
1103 if (metric)
1104 printf(" metric %lu", metric);
1105 if (mtu)
1106 printf(" mtu %lu", mtu);
1107 putchar('\n');
1108 if (sdl != NULL && sdl->sdl_alen &&
1109 (sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_CARP))
1110 (void)printf("\tlladdr %s\n", ether_ntoa(
1111 (struct ether_addr *)LLADDR(sdl)));
1112
1113 sep = '\t';
1114 #ifndef SMALL
1115 (void) memset(&ifrdesc, 0, sizeof(ifrdesc));
1116 (void) strlcpy(ifrdesc.ifr_name, ifname, sizeof(ifrdesc.ifr_name));
1117 ifrdesc.ifr_data = (caddr_t)&ifdescr;
1118 if (ioctl(sock, SIOCGIFDESCR, &ifrdesc) == 0 &&
1119 strlen(ifrdesc.ifr_data))
1120 printf("\tdescription: %s\n", ifrdesc.ifr_data);
1121
1122 if (sdl != NULL) {
1123 printf("%cindex %u", sep, sdl->sdl_index);
1124 sep = ' ';
1125 }
1126 if (ioctl(sock, SIOCGIFPRIORITY, &ifrdesc) == 0) {
1127 printf("%cpriority %d", sep, ifrdesc.ifr_metric);
1128 sep = ' ';
1129 }
1130 #endif
1131 printf("%cllprio %d\n", sep, llprio);
1132
1133 (void) memset(&ifmr, 0, sizeof(ifmr));
1134 (void) strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
1135
1136 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
1137 /*
1138 * Interface doesn't support SIOC{G,S}IFMEDIA.
1139 */
1140 if (ls != LINK_STATE_UNKNOWN)
1141 printf("\tstatus: %s\n",
1142 get_linkstate(sdl->sdl_type, ls));
1143 goto proto_status;
1144 }
1145
1146 if (ifmr.ifm_count == 0) {
1147 warnx("%s: no media types?", ifname);
1148 goto proto_status;
1149 }
1150
1151 media_list = calloc(ifmr.ifm_count, sizeof(*media_list));
1152 if (media_list == NULL)
1153 err(1, "calloc");
1154 ifmr.ifm_ulist = media_list;
1155
1156 if (ioctl(sock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1)
1157 err(1, "SIOCGIFMEDIA");
1158
1159 #ifdef SMALL
1160 printf("\tstatus: %s\n", get_linkstate(sdl->sdl_type, ls));
1161 #else
1162 if (ifmr.ifm_status & IFM_AVALID) {
1163 const struct ifmedia_status_description *ifms;
1164 int bitno, found = 0;
1165
1166 printf("\tstatus: ");
1167 for (bitno = 0; ifm_status_valid_list[bitno] != 0; bitno++) {
1168 for (ifms = ifm_status_descriptions;
1169 ifms->ifms_valid != 0; ifms++) {
1170 if (ifms->ifms_type !=
1171 IFM_TYPE(ifmr.ifm_current) ||
1172 ifms->ifms_valid !=
1173 ifm_status_valid_list[bitno])
1174 continue;
1175 printf("%s%s", found ? ", " : "",
1176 IFM_STATUS_DESC(ifms, ifmr.ifm_status));
1177 found = 1;
1178
1179 /*
1180 * For each valid indicator bit, there's
1181 * only one entry for each media type, so
1182 * terminate the inner loop now.
1183 */
1184 break;
1185 }
1186 }
1187
1188 if (found == 0)
1189 printf("unknown");
1190 putchar('\n');
1191 }
1192
1193 #endif
1194 free(media_list);
1195
1196 proto_status:
1197 if (link == 0) {
1198 if ((p = afp) != NULL) {
1199 p->af_status(1);
1200 } else for (p = afs; p->af_name; p++) {
1201 ifr.ifr_addr.sa_family = p->af_af;
1202 p->af_status(0);
1203 }
1204 }
1205
1206 phys_status(0);
1207 }
1208
1209 void
in_status(int force)1210 in_status(int force)
1211 {
1212 struct sockaddr_in *sin, sin2;
1213
1214 getsock(AF_INET);
1215 if (sock == -1) {
1216 if (errno == EPROTONOSUPPORT)
1217 return;
1218 err(1, "socket");
1219 }
1220 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1221 sin = (struct sockaddr_in *)&ifr.ifr_addr;
1222
1223 /*
1224 * We keep the interface address and reset it before each
1225 * ioctl() so we can get ifaliases information (as opposed
1226 * to the primary interface netmask/dstaddr/broadaddr, if
1227 * the ifr_addr field is zero).
1228 */
1229 memcpy(&sin2, &ifr.ifr_addr, sizeof(sin2));
1230
1231 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1232 if (ioctl(sock, SIOCGIFADDR, (caddr_t)&ifr) == -1) {
1233 warn("SIOCGIFADDR");
1234 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
1235 }
1236 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1237 sin = (struct sockaddr_in *)&ifr.ifr_addr;
1238 printf("\tinet %s", inet_ntoa(sin->sin_addr));
1239 memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
1240 if (ioctl(sock, SIOCGIFNETMASK, (caddr_t)&ifr) == -1) {
1241 if (errno != EADDRNOTAVAIL)
1242 warn("SIOCGIFNETMASK");
1243 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
1244 } else
1245 netmask.sin_addr =
1246 ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
1247 if (flags & IFF_POINTOPOINT) {
1248 memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
1249 if (ioctl(sock, SIOCGIFDSTADDR, (caddr_t)&ifr) == -1) {
1250 if (errno == EADDRNOTAVAIL)
1251 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
1252 else
1253 warn("SIOCGIFDSTADDR");
1254 }
1255 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1256 sin = (struct sockaddr_in *)&ifr.ifr_dstaddr;
1257 printf(" --> %s", inet_ntoa(sin->sin_addr));
1258 }
1259 printf(" netmask 0x%x", ntohl(netmask.sin_addr.s_addr));
1260 if (flags & IFF_BROADCAST) {
1261 memcpy(&ifr.ifr_addr, &sin2, sizeof(sin2));
1262 if (ioctl(sock, SIOCGIFBRDADDR, (caddr_t)&ifr) == -1) {
1263 if (errno == EADDRNOTAVAIL)
1264 memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
1265 else
1266 warn("SIOCGIFBRDADDR");
1267 }
1268 (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1269 sin = (struct sockaddr_in *)&ifr.ifr_addr;
1270 if (sin->sin_addr.s_addr != 0)
1271 printf(" broadcast %s", inet_ntoa(sin->sin_addr));
1272 }
1273 putchar('\n');
1274 }
1275
1276 void
setifprefixlen(const char * addr,int d)1277 setifprefixlen(const char *addr, int d)
1278 {
1279 setmask++;
1280 if (afp->af_getprefix)
1281 afp->af_getprefix(addr, MASK);
1282 explicit_prefix = 1;
1283 }
1284
1285 void
in6_fillscopeid(struct sockaddr_in6 * sin6)1286 in6_fillscopeid(struct sockaddr_in6 *sin6)
1287 {
1288 #ifdef __KAME__
1289 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
1290 sin6->sin6_scope_id =
1291 ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
1292 sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
1293 }
1294 #endif /* __KAME__ */
1295 }
1296
1297 /* XXX not really an alias */
1298 void
in6_alias(struct in6_ifreq * creq)1299 in6_alias(struct in6_ifreq *creq)
1300 {
1301 struct sockaddr_in6 *sin6;
1302 struct in6_ifreq ifr6; /* shadows file static variable */
1303 u_int32_t scopeid;
1304 char hbuf[NI_MAXHOST];
1305 const int niflag = NI_NUMERICHOST;
1306
1307 /* Get the non-alias address for this interface. */
1308 getsock(AF_INET6);
1309 if (sock == -1) {
1310 if (errno == EPROTONOSUPPORT)
1311 return;
1312 err(1, "socket");
1313 }
1314
1315 sin6 = (struct sockaddr_in6 *)&creq->ifr_addr;
1316
1317 in6_fillscopeid(sin6);
1318 scopeid = sin6->sin6_scope_id;
1319 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
1320 hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
1321 strlcpy(hbuf, "", sizeof hbuf);
1322 printf("\tinet6 %s", hbuf);
1323
1324 if (flags & IFF_POINTOPOINT) {
1325 (void) memset(&ifr6, 0, sizeof(ifr6));
1326 (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
1327 ifr6.ifr_addr = creq->ifr_addr;
1328 if (ioctl(sock, SIOCGIFDSTADDR_IN6, (caddr_t)&ifr6) == -1) {
1329 if (errno != EADDRNOTAVAIL)
1330 warn("SIOCGIFDSTADDR_IN6");
1331 (void) memset(&ifr6.ifr_addr, 0, sizeof(ifr6.ifr_addr));
1332 ifr6.ifr_addr.sin6_family = AF_INET6;
1333 ifr6.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
1334 }
1335 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
1336 in6_fillscopeid(sin6);
1337 if (getnameinfo((struct sockaddr *)sin6, sin6->sin6_len,
1338 hbuf, sizeof(hbuf), NULL, 0, niflag) != 0)
1339 strlcpy(hbuf, "", sizeof hbuf);
1340 printf(" -> %s", hbuf);
1341 }
1342
1343 (void) memset(&ifr6, 0, sizeof(ifr6));
1344 (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
1345 ifr6.ifr_addr = creq->ifr_addr;
1346 if (ioctl(sock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) == -1) {
1347 if (errno != EADDRNOTAVAIL)
1348 warn("SIOCGIFNETMASK_IN6");
1349 } else {
1350 sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
1351 printf(" prefixlen %d", prefix(&sin6->sin6_addr,
1352 sizeof(struct in6_addr)));
1353 }
1354
1355 (void) memset(&ifr6, 0, sizeof(ifr6));
1356 (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
1357 ifr6.ifr_addr = creq->ifr_addr;
1358 if (ioctl(sock, SIOCGIFAFLAG_IN6, (caddr_t)&ifr6) == -1) {
1359 if (errno != EADDRNOTAVAIL)
1360 warn("SIOCGIFAFLAG_IN6");
1361 } else {
1362 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_ANYCAST)
1363 printf(" anycast");
1364 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE)
1365 printf(" tentative");
1366 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DUPLICATED)
1367 printf(" duplicated");
1368 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DETACHED)
1369 printf(" detached");
1370 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_DEPRECATED)
1371 printf(" deprecated");
1372 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_AUTOCONF)
1373 printf(" autoconf");
1374 if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TEMPORARY)
1375 printf(" autoconfprivacy");
1376 }
1377
1378 if (scopeid)
1379 printf(" scopeid 0x%x", scopeid);
1380
1381 if (Lflag) {
1382 struct in6_addrlifetime *lifetime;
1383
1384 (void) memset(&ifr6, 0, sizeof(ifr6));
1385 (void) strlcpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name));
1386 ifr6.ifr_addr = creq->ifr_addr;
1387 lifetime = &ifr6.ifr_ifru.ifru_lifetime;
1388 if (ioctl(sock, SIOCGIFALIFETIME_IN6, (caddr_t)&ifr6) == -1) {
1389 if (errno != EADDRNOTAVAIL)
1390 warn("SIOCGIFALIFETIME_IN6");
1391 } else if (lifetime->ia6t_preferred || lifetime->ia6t_expire) {
1392 time_t t = time(NULL);
1393
1394 printf(" pltime ");
1395 if (lifetime->ia6t_preferred) {
1396 printf("%s", lifetime->ia6t_preferred < t
1397 ? "0" :
1398 sec2str(lifetime->ia6t_preferred - t));
1399 } else
1400 printf("infty");
1401
1402 printf(" vltime ");
1403 if (lifetime->ia6t_expire) {
1404 printf("%s", lifetime->ia6t_expire < t
1405 ? "0"
1406 : sec2str(lifetime->ia6t_expire - t));
1407 } else
1408 printf("infty");
1409 }
1410 }
1411
1412 printf("\n");
1413 }
1414
1415 void
in6_status(int force)1416 in6_status(int force)
1417 {
1418 in6_alias((struct in6_ifreq *)&ifr6);
1419 }
1420
1421 #ifndef SMALL
1422 void
settunnel(const char * src,const char * dst)1423 settunnel(const char *src, const char *dst)
1424 {
1425 char buf[HOST_NAME_MAX+1 + sizeof (":65535")], *dstport;
1426 const char *dstip;
1427 struct addrinfo *srcres, *dstres;
1428 int ecode;
1429 struct if_laddrreq req;
1430
1431 if (strchr(dst, ':') == NULL || strchr(dst, ':') != strrchr(dst, ':')) {
1432 /* no port or IPv6 */
1433 dstip = dst;
1434 dstport = NULL;
1435 } else {
1436 if (strlcpy(buf, dst, sizeof(buf)) >= sizeof(buf))
1437 errx(1, "%s bad value", dst);
1438 dstport = strchr(buf, ':');
1439 *dstport++ = '\0';
1440 dstip = buf;
1441 }
1442
1443 if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
1444 errx(1, "error in parsing address string: %s",
1445 gai_strerror(ecode));
1446
1447 if ((ecode = getaddrinfo(dstip, dstport, NULL, &dstres)) != 0)
1448 errx(1, "error in parsing address string: %s",
1449 gai_strerror(ecode));
1450
1451 if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family)
1452 errx(1,
1453 "source and destination address families do not match");
1454
1455 memset(&req, 0, sizeof(req));
1456 (void) strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
1457 memcpy(&req.addr, srcres->ai_addr, srcres->ai_addrlen);
1458 memcpy(&req.dstaddr, dstres->ai_addr, dstres->ai_addrlen);
1459 if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
1460 warn("SIOCSLIFPHYADDR");
1461
1462 freeaddrinfo(srcres);
1463 freeaddrinfo(dstres);
1464 }
1465
1466 void
settunneladdr(const char * addr,int ignored)1467 settunneladdr(const char *addr, int ignored)
1468 {
1469 struct addrinfo hints, *res;
1470 struct if_laddrreq req;
1471 ssize_t len;
1472 int rv;
1473
1474 memset(&hints, 0, sizeof(hints));
1475 hints.ai_family = AF_UNSPEC;
1476 hints.ai_socktype = SOCK_DGRAM;
1477 hints.ai_protocol = 0;
1478 hints.ai_flags = AI_PASSIVE;
1479
1480 rv = getaddrinfo(addr, NULL, &hints, &res);
1481 if (rv != 0)
1482 errx(1, "tunneladdr %s: %s", addr, gai_strerror(rv));
1483
1484 memset(&req, 0, sizeof(req));
1485 len = strlcpy(req.iflr_name, ifname, sizeof(req.iflr_name));
1486 if (len >= sizeof(req.iflr_name))
1487 errx(1, "%s: Interface name too long", ifname);
1488
1489 memcpy(&req.addr, res->ai_addr, res->ai_addrlen);
1490
1491 req.dstaddr.ss_len = 2;
1492 req.dstaddr.ss_family = AF_UNSPEC;
1493
1494 if (ioctl(sock, SIOCSLIFPHYADDR, &req) == -1)
1495 warn("tunneladdr %s", addr);
1496
1497 freeaddrinfo(res);
1498 }
1499
1500 void
deletetunnel(const char * ignored,int alsoignored)1501 deletetunnel(const char *ignored, int alsoignored)
1502 {
1503 if (ioctl(sock, SIOCDIFPHYADDR, &ifr) == -1)
1504 warn("SIOCDIFPHYADDR");
1505 }
1506
1507 void
settunnelinst(const char * id,int param)1508 settunnelinst(const char *id, int param)
1509 {
1510 const char *errmsg = NULL;
1511 int rdomainid;
1512
1513 rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
1514 if (errmsg)
1515 errx(1, "rdomain %s: %s", id, errmsg);
1516
1517 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1518 ifr.ifr_rdomainid = rdomainid;
1519 if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
1520 warn("SIOCSLIFPHYRTABLE");
1521 }
1522
1523 void
unsettunnelinst(const char * ignored,int alsoignored)1524 unsettunnelinst(const char *ignored, int alsoignored)
1525 {
1526 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1527 ifr.ifr_rdomainid = 0;
1528 if (ioctl(sock, SIOCSLIFPHYRTABLE, (caddr_t)&ifr) == -1)
1529 warn("SIOCSLIFPHYRTABLE");
1530 }
1531
1532 void
settunnelttl(const char * id,int param)1533 settunnelttl(const char *id, int param)
1534 {
1535 const char *errmsg = NULL;
1536 int ttl;
1537
1538 if (strcmp(id, "copy") == 0)
1539 ttl = -1;
1540 else {
1541 ttl = strtonum(id, 0, 0xff, &errmsg);
1542 if (errmsg)
1543 errx(1, "tunnelttl %s: %s", id, errmsg);
1544 }
1545
1546 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1547 ifr.ifr_ttl = ttl;
1548 if (ioctl(sock, SIOCSLIFPHYTTL, (caddr_t)&ifr) == -1)
1549 warn("SIOCSLIFPHYTTL");
1550 }
1551
1552 void
utf16_to_char(uint16_t * in,int inlen,char * out,size_t outlen)1553 utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
1554 {
1555 uint16_t c;
1556
1557 while (outlen > 0) {
1558 c = inlen > 0 ? letoh16(*in) : 0;
1559 if (c == 0 || --outlen == 0) {
1560 /* always NUL terminate result */
1561 *out = '\0';
1562 break;
1563 }
1564 *out++ = isascii(c) ? (char)c : '?';
1565 in++;
1566 inlen--;
1567 }
1568 }
1569
1570 int
char_to_utf16(const char * in,uint16_t * out,size_t outlen)1571 char_to_utf16(const char *in, uint16_t *out, size_t outlen)
1572 {
1573 int n = 0;
1574 uint16_t c;
1575
1576 for (;;) {
1577 c = *in++;
1578
1579 if (c == '\0') {
1580 /*
1581 * NUL termination is not required, but zero out the
1582 * residual buffer
1583 */
1584 memset(out, 0, outlen);
1585 return n;
1586 }
1587 if (outlen < sizeof (*out))
1588 return -1;
1589
1590 *out++ = htole16(c);
1591 n += sizeof (*out);
1592 outlen -= sizeof (*out);
1593 }
1594 }
1595
1596 #endif
1597
1598 #define SIN(x) ((struct sockaddr_in *) &(x))
1599 struct sockaddr_in *sintab[] = {
1600 SIN(ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
1601 SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)};
1602
1603 void
in_getaddr(const char * s,int which)1604 in_getaddr(const char *s, int which)
1605 {
1606 struct sockaddr_in *sin = sintab[which], tsin;
1607 struct hostent *hp;
1608 int bits, l;
1609 char p[3];
1610
1611 bzero(&tsin, sizeof(tsin));
1612 sin->sin_len = sizeof(*sin);
1613 if (which != MASK)
1614 sin->sin_family = AF_INET;
1615
1616 if (which == ADDR && strrchr(s, '/') != NULL &&
1617 (bits = inet_net_pton(AF_INET, s, &tsin.sin_addr,
1618 sizeof(tsin.sin_addr))) != -1) {
1619 l = snprintf(p, sizeof(p), "%d", bits);
1620 if (l < 0 || l >= sizeof(p))
1621 errx(1, "%d: bad prefixlen", bits);
1622 setmask++;
1623 in_getprefix(p, MASK);
1624 memcpy(&sin->sin_addr, &tsin.sin_addr, sizeof(sin->sin_addr));
1625 } else if (inet_aton(s, &sin->sin_addr) == 0) {
1626 if ((hp = gethostbyname(s)))
1627 memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
1628 else
1629 errx(1, "%s: bad value", s);
1630 }
1631 }
1632
1633 void
in_getprefix(const char * plen,int which)1634 in_getprefix(const char *plen, int which)
1635 {
1636 struct sockaddr_in *sin = sintab[which];
1637 const char *errmsg = NULL;
1638 u_char *cp;
1639 int len;
1640
1641 len = strtonum(plen, 0, 32, &errmsg);
1642 if (errmsg)
1643 errx(1, "prefix %s: %s", plen, errmsg);
1644
1645 sin->sin_len = sizeof(*sin);
1646 if (which != MASK)
1647 sin->sin_family = AF_INET;
1648 if ((len == 0) || (len == 32)) {
1649 memset(&sin->sin_addr, 0xff, sizeof(struct in_addr));
1650 return;
1651 }
1652 memset((void *)&sin->sin_addr, 0x00, sizeof(sin->sin_addr));
1653 for (cp = (u_char *)&sin->sin_addr; len > 7; len -= 8)
1654 *cp++ = 0xff;
1655 if (len)
1656 *cp = 0xff << (8 - len);
1657 }
1658
1659 /*
1660 * Print a value a la the %b format of the kernel's printf
1661 */
1662 void
printb(char * s,unsigned int v,unsigned char * bits)1663 printb(char *s, unsigned int v, unsigned char *bits)
1664 {
1665 int i, any = 0;
1666 unsigned char c;
1667
1668 if (bits && *bits == 8)
1669 printf("%s=%o", s, v);
1670 else
1671 printf("%s=%x", s, v);
1672
1673 if (bits) {
1674 bits++;
1675 putchar('<');
1676 while ((i = *bits++)) {
1677 if (v & (1 << (i-1))) {
1678 if (any)
1679 putchar(',');
1680 any = 1;
1681 for (; (c = *bits) > 32; bits++)
1682 putchar(c);
1683 } else
1684 for (; *bits > 32; bits++)
1685 ;
1686 }
1687 putchar('>');
1688 }
1689 }
1690
1691 /*
1692 * A simple version of printb for status output
1693 */
1694 void
printb_status(unsigned short v,unsigned char * bits)1695 printb_status(unsigned short v, unsigned char *bits)
1696 {
1697 int i, any = 0;
1698 unsigned char c;
1699
1700 if (bits) {
1701 bits++;
1702 while ((i = *bits++)) {
1703 if (v & (1 << (i-1))) {
1704 if (any)
1705 putchar(',');
1706 any = 1;
1707 for (; (c = *bits) > 32; bits++)
1708 putchar(tolower(c));
1709 } else
1710 for (; *bits > 32; bits++)
1711 ;
1712 }
1713 }
1714 }
1715
1716 #define SIN6(x) ((struct sockaddr_in6 *) &(x))
1717 struct sockaddr_in6 *sin6tab[] = {
1718 SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
1719 SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
1720
1721 void
in6_getaddr(const char * s,int which)1722 in6_getaddr(const char *s, int which)
1723 {
1724 struct sockaddr_in6 *sin6 = sin6tab[which];
1725 struct addrinfo hints, *res;
1726 char buf[HOST_NAME_MAX+1 + sizeof("/128")], *pfxlen;
1727 int error;
1728
1729 memset(&hints, 0, sizeof(hints));
1730 hints.ai_family = AF_INET6;
1731 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1732
1733 if (which == ADDR && strchr(s, '/') != NULL) {
1734 if (strlcpy(buf, s, sizeof(buf)) >= sizeof(buf))
1735 errx(1, "%s: bad value", s);
1736 pfxlen = strchr(buf, '/');
1737 *pfxlen++ = '\0';
1738 s = buf;
1739 setmask++;
1740 in6_getprefix(pfxlen, MASK);
1741 explicit_prefix = 1;
1742 }
1743
1744 error = getaddrinfo(s, "0", &hints, &res);
1745 if (error)
1746 errx(1, "%s: %s", s, gai_strerror(error));
1747 memcpy(sin6, res->ai_addr, res->ai_addrlen);
1748 #ifdef __KAME__
1749 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) &&
1750 *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] == 0 &&
1751 sin6->sin6_scope_id) {
1752 *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] =
1753 htons(sin6->sin6_scope_id & 0xffff);
1754 sin6->sin6_scope_id = 0;
1755 }
1756 #endif /* __KAME__ */
1757 freeaddrinfo(res);
1758 }
1759
1760 void
in6_getprefix(const char * plen,int which)1761 in6_getprefix(const char *plen, int which)
1762 {
1763 struct sockaddr_in6 *sin6 = sin6tab[which];
1764 const char *errmsg = NULL;
1765 u_char *cp;
1766 int len;
1767
1768 len = strtonum(plen, 0, 128, &errmsg);
1769 if (errmsg)
1770 errx(1, "prefix %s: %s", plen, errmsg);
1771
1772 sin6->sin6_len = sizeof(*sin6);
1773 if (which != MASK)
1774 sin6->sin6_family = AF_INET6;
1775 if ((len == 0) || (len == 128)) {
1776 memset(&sin6->sin6_addr, 0xff, sizeof(struct in6_addr));
1777 return;
1778 }
1779 memset((void *)&sin6->sin6_addr, 0x00, sizeof(sin6->sin6_addr));
1780 for (cp = (u_char *)&sin6->sin6_addr; len > 7; len -= 8)
1781 *cp++ = 0xff;
1782 if (len)
1783 *cp = 0xff << (8 - len);
1784 }
1785
1786 int
prefix(void * val,int size)1787 prefix(void *val, int size)
1788 {
1789 u_char *nam = (u_char *)val;
1790 int byte, bit, plen = 0;
1791
1792 for (byte = 0; byte < size; byte++, plen += 8)
1793 if (nam[byte] != 0xff)
1794 break;
1795 if (byte == size)
1796 return (plen);
1797 for (bit = 7; bit != 0; bit--, plen++)
1798 if (!(nam[byte] & (1 << bit)))
1799 break;
1800 for (; bit != 0; bit--)
1801 if (nam[byte] & (1 << bit))
1802 return (0);
1803 byte++;
1804 for (; byte < size; byte++)
1805 if (nam[byte])
1806 return (0);
1807 return (plen);
1808 }
1809
1810 /* Print usage and exit */
1811 __dead void
usage(void)1812 usage(void)
1813 {
1814 fprintf(stderr,
1815 "usage: ifaddr interface [address_family] "
1816 "[address [dest_address]]\n"
1817 "\t\t[parameters]\n");
1818 exit(1);
1819 }
1820
1821 #ifndef SMALL
1822 void
printifhwfeatures(const char * unused,int show)1823 printifhwfeatures(const char *unused, int show)
1824 {
1825 struct if_data ifrdat;
1826
1827 if (!show) {
1828 if (showcapsflag)
1829 usage();
1830 showcapsflag = 1;
1831 return;
1832 }
1833 bzero(&ifrdat, sizeof(ifrdat));
1834 ifr.ifr_data = (caddr_t)&ifrdat;
1835 if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
1836 err(1, "SIOCGIFDATA");
1837 printb("\thwfeatures", (u_int)ifrdat.ifi_capabilities, HWFEATURESBITS);
1838
1839 if (ioctl(sock, SIOCGIFHARDMTU, (caddr_t)&ifr) != -1) {
1840 if (ifr.ifr_hardmtu)
1841 printf(" hardmtu %u", ifr.ifr_hardmtu);
1842 }
1843 putchar('\n');
1844 }
1845 #endif
1846
1847 char *
sec2str(time_t total)1848 sec2str(time_t total)
1849 {
1850 static char result[256];
1851 char *p = result;
1852 char *end = &result[sizeof(result)];
1853
1854 snprintf(p, end - p, "%lld", (long long)total);
1855 return (result);
1856 }
1857
1858 #ifndef SMALL
1859 void
setrdomain(const char * id,int param)1860 setrdomain(const char *id, int param)
1861 {
1862 const char *errmsg = NULL;
1863 int rdomainid;
1864
1865 rdomainid = strtonum(id, 0, RT_TABLEID_MAX, &errmsg);
1866 if (errmsg)
1867 errx(1, "rdomain %s: %s", id, errmsg);
1868
1869 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1870 ifr.ifr_rdomainid = rdomainid;
1871 if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
1872 warn("SIOCSIFRDOMAIN");
1873 }
1874
1875 void
unsetrdomain(const char * ignored,int alsoignored)1876 unsetrdomain(const char *ignored, int alsoignored)
1877 {
1878 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
1879 ifr.ifr_rdomainid = 0;
1880 if (ioctl(sock, SIOCSIFRDOMAIN, (caddr_t)&ifr) == -1)
1881 warn("SIOCSIFRDOMAIN");
1882 }
1883 #endif
1884
1885 #ifdef SMALL
1886 void
setignore(const char * id,int param)1887 setignore(const char *id, int param)
1888 {
1889 /* just digest the command */
1890 }
1891 #endif
1892