]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 1983, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static const char copyright[] = | |
36 | "@(#) Copyright (c) 1983, 1993\n\ | |
37 | The Regents of the University of California. All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
41 | #if 0 | |
42 | static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; | |
43 | #endif | |
44 | static const char rcsid[] = | |
45 | "$Id: ifconfig.c,v 1.5 2003/07/02 01:22:29 lindak Exp $"; | |
46 | #endif /* not lint */ | |
47 | ||
48 | #include <sys/param.h> | |
49 | #include <sys/ioctl.h> | |
50 | #include <sys/socket.h> | |
51 | #include <sys/sysctl.h> | |
52 | #include <sys/time.h> | |
53 | ||
54 | #include <net/ethernet.h> | |
55 | #include <net/if.h> | |
56 | #include <net/if_var.h> | |
57 | #include <net/if_dl.h> | |
58 | #include <net/if_types.h> | |
59 | #include <net/route.h> | |
60 | ||
61 | /* IP */ | |
62 | #include <netinet/in.h> | |
63 | #include <netinet/in_var.h> | |
64 | #include <arpa/inet.h> | |
65 | #include <netdb.h> | |
66 | ||
67 | #ifdef INET6 | |
68 | #include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */ | |
69 | #endif | |
70 | ||
71 | ||
72 | /* XNS */ | |
73 | #ifdef NS | |
74 | #define NSIP | |
75 | #include <netns/ns.h> | |
76 | #include <netns/ns_if.h> | |
77 | #endif | |
78 | /* OSI */ | |
79 | ||
80 | #include <ctype.h> | |
81 | #include <err.h> | |
82 | #include <errno.h> | |
83 | #include <fcntl.h> | |
84 | #include <stdio.h> | |
85 | #include <stdlib.h> | |
86 | #include <string.h> | |
87 | #include <unistd.h> | |
88 | ||
89 | #include "ifconfig.h" | |
90 | ||
91 | /* wrapper for KAME-special getnameinfo() */ | |
92 | #ifndef NI_WITHSCOPEID | |
93 | #define NI_WITHSCOPEID 0 | |
94 | #endif | |
95 | ||
96 | struct ifreq ifr, ridreq; | |
97 | struct ifaliasreq addreq; | |
98 | #ifdef INET6 | |
99 | struct in6_ifreq in6_ridreq; | |
100 | struct in6_aliasreq in6_addreq = | |
101 | { { 0 }, | |
102 | { 0 }, | |
103 | { 0 }, | |
104 | { 0 }, | |
105 | 0, | |
106 | { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } }; | |
107 | #endif | |
108 | struct sockaddr_in netmask; | |
109 | ||
110 | ||
111 | char name[32]; | |
112 | int flags; | |
113 | int metric; | |
114 | int mtu; | |
115 | int setaddr; | |
116 | int setipdst; | |
117 | int setmask; | |
118 | int doalias; | |
119 | int clearaddr; | |
120 | int newaddr = 1; | |
121 | #ifdef INET6 | |
122 | static int ip6lifetime; | |
123 | #endif | |
124 | ||
125 | struct afswtch; | |
126 | ||
127 | int supmedia = 0; | |
128 | int listcloners = 0; | |
129 | ||
130 | #ifdef INET6 | |
131 | char addr_buf[MAXHOSTNAMELEN *2 + 1]; /*for getnameinfo()*/ | |
132 | #endif | |
133 | ||
134 | void Perror __P((const char *cmd)); | |
135 | ||
136 | int ifconfig __P((int argc, char *const *argv, const struct afswtch *afp)); | |
137 | void notealias __P((const char *, int, int, const struct afswtch *afp)); | |
138 | void printb __P((const char *s, unsigned value, const char *bits)); | |
139 | void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *)); | |
140 | void status __P((const struct afswtch *afp, int addrcount, | |
141 | struct sockaddr_dl *sdl, struct if_msghdr *ifm, | |
142 | struct ifa_msghdr *ifam)); | |
143 | void tunnel_status __P((int s)); | |
144 | void usage __P((void)); | |
145 | ||
146 | #ifdef INET6 | |
147 | void in6_fillscopeid __P((struct sockaddr_in6 *sin6)); | |
148 | int prefix __P((void *, int)); | |
149 | static char *sec2str __P((time_t)); | |
150 | int explicit_prefix = 0; | |
151 | #endif | |
152 | ||
153 | typedef void c_func __P((const char *cmd, int arg, int s, const struct afswtch *afp)); | |
154 | typedef void c_func2 __P((const char *arg, const char *arg2, int s, const struct afswtch *afp)); | |
155 | c_func setifaddr, setifbroadaddr, setifdstaddr, setifnetmask; | |
156 | c_func2 settunnel; | |
157 | c_func deletetunnel; | |
158 | #ifdef INET6 | |
159 | c_func setifprefixlen; | |
160 | c_func setip6flags; | |
161 | c_func setip6pltime; | |
162 | c_func setip6vltime; | |
163 | c_func2 setip6lifetime; | |
164 | #endif | |
165 | c_func setifipdst; | |
166 | c_func setifflags, setifmetric, setifmtu, setiflladdr; | |
167 | ||
168 | ||
169 | #define NEXTARG 0xffffff | |
170 | #define NEXTARG2 0xfffffe | |
171 | ||
172 | const | |
173 | struct cmd { | |
174 | const char *c_name; | |
175 | int c_parameter; /* NEXTARG means next argv */ | |
176 | void (*c_func) __P((const char *, int, int, const struct afswtch *afp)); | |
177 | void (*c_func2) __P((const char *, const char *, int, const struct afswtch *afp)); | |
178 | } cmds[] = { | |
179 | { "up", IFF_UP, setifflags } , | |
180 | { "down", -IFF_UP, setifflags }, | |
181 | { "arp", -IFF_NOARP, setifflags }, | |
182 | { "-arp", IFF_NOARP, setifflags }, | |
183 | { "debug", IFF_DEBUG, setifflags }, | |
184 | { "-debug", -IFF_DEBUG, setifflags }, | |
185 | { "add", IFF_UP, notealias }, | |
186 | { "alias", IFF_UP, notealias }, | |
187 | { "-alias", -IFF_UP, notealias }, | |
188 | { "delete", -IFF_UP, notealias }, | |
189 | { "remove", -IFF_UP, notealias }, | |
190 | #ifdef notdef | |
191 | #define EN_SWABIPS 0x1000 | |
192 | { "swabips", EN_SWABIPS, setifflags }, | |
193 | { "-swabips", -EN_SWABIPS, setifflags }, | |
194 | #endif | |
195 | { "netmask", NEXTARG, setifnetmask }, | |
196 | #ifdef INET6 | |
197 | { "prefixlen", NEXTARG, setifprefixlen }, | |
198 | { "anycast", IN6_IFF_ANYCAST, setip6flags }, | |
199 | { "tentative", IN6_IFF_TENTATIVE, setip6flags }, | |
200 | { "-tentative", -IN6_IFF_TENTATIVE, setip6flags }, | |
201 | { "deprecated", IN6_IFF_DEPRECATED, setip6flags }, | |
202 | { "-deprecated", -IN6_IFF_DEPRECATED, setip6flags }, | |
203 | { "autoconf", IN6_IFF_AUTOCONF, setip6flags }, | |
204 | { "-autoconf", -IN6_IFF_AUTOCONF, setip6flags }, | |
205 | { "pltime", NEXTARG, setip6pltime }, | |
206 | { "vltime", NEXTARG, setip6vltime }, | |
207 | #endif | |
208 | { "metric", NEXTARG, setifmetric }, | |
209 | { "broadcast", NEXTARG, setifbroadaddr }, | |
210 | { "ipdst", NEXTARG, setifipdst }, | |
211 | { "tunnel", NEXTARG2, NULL, settunnel }, | |
212 | { "deletetunnel", 0, deletetunnel }, | |
213 | { "link0", IFF_LINK0, setifflags }, | |
214 | { "-link0", -IFF_LINK0, setifflags }, | |
215 | { "link1", IFF_LINK1, setifflags }, | |
216 | { "-link1", -IFF_LINK1, setifflags }, | |
217 | { "link2", IFF_LINK2, setifflags }, | |
218 | { "-link2", -IFF_LINK2, setifflags }, | |
219 | #if USE_IF_MEDIA | |
220 | { "media", NEXTARG, setmedia }, | |
221 | { "mediaopt", NEXTARG, setmediaopt }, | |
222 | { "-mediaopt", NEXTARG, unsetmediaopt }, | |
223 | #endif | |
224 | #ifdef USE_VLANS | |
225 | { "vlan", NEXTARG, setvlantag }, | |
226 | { "vlandev", NEXTARG, setvlandev }, | |
227 | { "-vlandev", NEXTARG, unsetvlandev }, | |
228 | #endif | |
229 | #if 0 | |
230 | /* XXX `create' special-cased below */ | |
231 | {"create", 0, clone_create }, | |
232 | {"plumb", 0, clone_create }, | |
233 | #endif | |
234 | #ifndef __APPLE__ | |
235 | {"destroy", 0, clone_destroy }, | |
236 | {"unplumb", 0, clone_destroy }, | |
237 | #endif | |
238 | #ifdef USE_IEEE80211 | |
239 | { "ssid", NEXTARG, set80211ssid }, | |
240 | { "nwid", NEXTARG, set80211ssid }, | |
241 | { "stationname", NEXTARG, set80211stationname }, | |
242 | { "station", NEXTARG, set80211stationname }, /* BSD/OS */ | |
243 | { "channel", NEXTARG, set80211channel }, | |
244 | { "authmode", NEXTARG, set80211authmode }, | |
245 | { "powersavemode", NEXTARG, set80211powersavemode }, | |
246 | { "powersave", 1, set80211powersave }, | |
247 | { "-powersave", 0, set80211powersave }, | |
248 | { "powersavesleep", NEXTARG, set80211powersavesleep }, | |
249 | { "wepmode", NEXTARG, set80211wepmode }, | |
250 | { "wep", 1, set80211wep }, | |
251 | { "-wep", 0, set80211wep }, | |
252 | { "weptxkey", NEXTARG, set80211weptxkey }, | |
253 | { "wepkey", NEXTARG, set80211wepkey }, | |
254 | { "nwkey", NEXTARG, set80211nwkey }, /* NetBSD */ | |
255 | { "-nwkey", 0, set80211wep }, /* NetBSD */ | |
256 | #endif | |
257 | { "normal", -IFF_LINK0, setifflags }, | |
258 | { "compress", IFF_LINK0, setifflags }, | |
259 | { "noicmp", IFF_LINK1, setifflags }, | |
260 | { "mtu", NEXTARG, setifmtu }, | |
261 | { "lladdr", NEXTARG, setiflladdr }, | |
262 | { 0, 0, setifaddr }, | |
263 | { 0, 0, setifdstaddr }, | |
264 | }; | |
265 | ||
266 | /* | |
267 | * XNS support liberally adapted from code written at the University of | |
268 | * Maryland principally by James O'Toole and Chris Torek. | |
269 | */ | |
270 | typedef void af_status __P((int, struct rt_addrinfo *)); | |
271 | typedef void af_getaddr __P((const char *, int)); | |
272 | typedef void af_getprefix __P((const char *, int)); | |
273 | ||
274 | af_status in_status, at_status, ether_status; | |
275 | af_getaddr in_getaddr, at_getaddr, ether_getaddr; | |
276 | ||
277 | ||
278 | #ifdef INET6 | |
279 | af_status in6_status; | |
280 | af_getaddr in6_getaddr; | |
281 | af_getprefix in6_getprefix; | |
282 | #endif /*INET6*/ | |
283 | #ifdef NS | |
284 | af_status xns_status; | |
285 | af_getaddr xns_getaddr; | |
286 | #endif | |
287 | ||
288 | /* Known address families */ | |
289 | const | |
290 | struct afswtch { | |
291 | const char *af_name; | |
292 | short af_af; | |
293 | af_status *af_status; | |
294 | af_getaddr *af_getaddr; | |
295 | af_getprefix *af_getprefix; | |
296 | u_long af_difaddr; | |
297 | u_long af_aifaddr; | |
298 | caddr_t af_ridreq; | |
299 | caddr_t af_addreq; | |
300 | } afs[] = { | |
301 | #define C(x) ((caddr_t) &x) | |
302 | { "inet", AF_INET, in_status, in_getaddr, NULL, | |
303 | SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, | |
304 | #ifdef INET6 | |
305 | { "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix, | |
306 | SIOCDIFADDR_IN6, SIOCAIFADDR_IN6, | |
307 | C(in6_ridreq), C(in6_addreq) }, | |
308 | #endif /*INET6*/ | |
309 | #ifdef NS | |
310 | { "ns", AF_NS, xns_status, xns_getaddr, NULL, | |
311 | SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) }, | |
312 | #endif | |
313 | { "ether", AF_LINK, ether_status, ether_getaddr, NULL, | |
314 | 0, SIOCSIFLLADDR, NULL, C(ridreq) }, | |
315 | #if 0 /* XXX conflicts with the media command */ | |
316 | #ifdef USE_IF_MEDIA | |
317 | { "media", AF_UNSPEC, media_status, NULL, NULL, }, /* XXX not real!! */ | |
318 | #endif | |
319 | #ifdef USE_VLANS | |
320 | { "vlan", AF_UNSPEC, vlan_status, NULL, NULL, }, /* XXX not real!! */ | |
321 | #endif | |
322 | #ifdef USE_IEEE80211 | |
323 | { "ieee80211", AF_UNSPEC, ieee80211_status, NULL, NULL, }, /* XXX not real!! */ | |
324 | #endif | |
325 | #endif | |
326 | { 0, 0, 0, 0 } | |
327 | }; | |
328 | ||
329 | /* | |
330 | * Expand the compacted form of addresses as returned via the | |
331 | * configuration read via sysctl(). | |
332 | */ | |
333 | ||
334 | #define ROUNDUP(a) \ | |
335 | ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) | |
336 | #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) | |
337 | ||
338 | void | |
339 | rt_xaddrs(cp, cplim, rtinfo) | |
340 | caddr_t cp, cplim; | |
341 | struct rt_addrinfo *rtinfo; | |
342 | { | |
343 | struct sockaddr *sa; | |
344 | int i; | |
345 | ||
346 | memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); | |
347 | for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { | |
348 | if ((rtinfo->rti_addrs & (1 << i)) == 0) | |
349 | continue; | |
350 | rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; | |
351 | ADVANCE(cp, sa); | |
352 | } | |
353 | } | |
354 | ||
355 | ||
356 | void | |
357 | usage() | |
358 | { | |
359 | #ifndef INET6 | |
360 | fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", | |
361 | "usage: ifconfig interface address_family [address [dest_address]]", | |
362 | " [parameters]", | |
363 | " ifconfig interface create", | |
364 | " ifconfig -a [-d] [-m] [-u] [address_family]", | |
365 | " ifconfig -l [-d] [-u] [address_family]", | |
366 | " ifconfig [-d] [-m] [-u]"); | |
367 | #else | |
368 | fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", | |
369 | "usage: ifconfig [-L] interface address_family [address [dest_address]]", | |
370 | " [parameters]", | |
371 | " ifconfig interface create", | |
372 | " ifconfig -a [-L] [-d] [-m] [-u] [address_family]", | |
373 | " ifconfig -l [-d] [-u] [address_family]", | |
374 | " ifconfig [-L] [-d] [-m] [-u]"); | |
375 | #endif | |
376 | exit(1); | |
377 | } | |
378 | ||
379 | int | |
380 | main(argc, argv) | |
381 | int argc; | |
382 | char *const *argv; | |
383 | { | |
384 | int c; | |
385 | int all, namesonly, downonly, uponly; | |
386 | int foundit = 0, need_nl = 0; | |
387 | const struct afswtch *afp = 0; | |
388 | int addrcount; | |
389 | struct if_msghdr *ifm, *nextifm; | |
390 | struct ifa_msghdr *ifam; | |
391 | struct sockaddr_dl *sdl; | |
392 | char *buf, *lim, *next; | |
393 | ||
394 | ||
395 | size_t needed; | |
396 | int mib[6]; | |
397 | ||
398 | /* Parse leading line options */ | |
399 | all = downonly = uponly = namesonly = 0; | |
400 | while ((c = getopt(argc, argv, "adlmu" | |
401 | #ifdef INET6 | |
402 | "L" | |
403 | #endif | |
404 | )) != -1) { | |
405 | switch (c) { | |
406 | case 'a': /* scan all interfaces */ | |
407 | all++; | |
408 | break; | |
409 | case 'd': /* restrict scan to "down" interfaces */ | |
410 | downonly++; | |
411 | break; | |
412 | case 'l': /* scan interface names only */ | |
413 | namesonly++; | |
414 | break; | |
415 | case 'm': /* show media choices in status */ | |
416 | supmedia = 1; | |
417 | break; | |
418 | case 'u': /* restrict scan to "up" interfaces */ | |
419 | uponly++; | |
420 | break; | |
421 | #ifdef INET6 | |
422 | case 'L': | |
423 | ip6lifetime++; /* print IPv6 address lifetime */ | |
424 | break; | |
425 | #endif | |
426 | default: | |
427 | usage(); | |
428 | break; | |
429 | } | |
430 | } | |
431 | argc -= optind; | |
432 | argv += optind; | |
433 | ||
434 | /* -l cannot be used with -a or -m */ | |
435 | if (namesonly && (all || supmedia)) | |
436 | usage(); | |
437 | ||
438 | /* nonsense.. */ | |
439 | if (uponly && downonly) | |
440 | usage(); | |
441 | ||
442 | /* no arguments is equivalent to '-a' */ | |
443 | if (!namesonly && argc < 1) | |
444 | all = 1; | |
445 | ||
446 | /* -a and -l allow an address family arg to limit the output */ | |
447 | if (all || namesonly) { | |
448 | if (argc > 1) | |
449 | usage(); | |
450 | ||
451 | if (argc == 1) { | |
452 | for (afp = afs; afp->af_name; afp++) | |
453 | if (strcmp(afp->af_name, *argv) == 0) { | |
454 | argc--, argv++; | |
455 | break; | |
456 | } | |
457 | if (afp->af_name == NULL) | |
458 | usage(); | |
459 | /* leave with afp non-zero */ | |
460 | } | |
461 | } else { | |
462 | /* not listing, need an argument */ | |
463 | if (argc < 1) | |
464 | usage(); | |
465 | ||
466 | strncpy(name, *argv, sizeof(name)); | |
467 | argc--, argv++; | |
468 | ||
469 | /* | |
470 | * NOTE: We must special-case the `create' command right | |
471 | * here as we would otherwise fail when trying to find | |
472 | * the interface. | |
473 | */ | |
474 | if (argc > 0 && (strcmp(argv[0], "create") == 0 || | |
475 | strcmp(argv[0], "plumb") == 0)) { | |
476 | #ifndef __APPLE__ | |
477 | clone_create(); | |
478 | #endif | |
479 | argc--, argv++; | |
480 | if (argc == 0) | |
481 | exit(0); | |
482 | } | |
483 | } | |
484 | ||
485 | /* Check for address family */ | |
486 | if (argc > 0) { | |
487 | for (afp = afs; afp->af_name; afp++) | |
488 | if (strcmp(afp->af_name, *argv) == 0) { | |
489 | argc--, argv++; | |
490 | break; | |
491 | } | |
492 | if (afp->af_name == NULL) | |
493 | afp = NULL; /* not a family, NULL */ | |
494 | } | |
495 | ||
496 | mib[0] = CTL_NET; | |
497 | mib[1] = PF_ROUTE; | |
498 | mib[2] = 0; | |
499 | mib[3] = 0; /* address family */ | |
500 | mib[4] = NET_RT_IFLIST; | |
501 | mib[5] = 0; | |
502 | ||
503 | /* if particular family specified, only ask about it */ | |
504 | if (afp) | |
505 | mib[3] = afp->af_af; | |
506 | ||
507 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) | |
508 | errx(1, "iflist-sysctl-estimate"); | |
509 | if ((buf = malloc(needed)) == NULL) | |
510 | errx(1, "malloc"); | |
511 | if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) | |
512 | errx(1, "actual retrieval of interface table"); | |
513 | lim = buf + needed; | |
514 | ||
515 | next = buf; | |
516 | while (next < lim) { | |
517 | ||
518 | ifm = (struct if_msghdr *)next; | |
519 | ||
520 | if (ifm->ifm_type == RTM_IFINFO) { | |
521 | sdl = (struct sockaddr_dl *)(ifm + 1); | |
522 | flags = ifm->ifm_flags; | |
523 | } else { | |
524 | fprintf(stderr, "out of sync parsing NET_RT_IFLIST\n"); | |
525 | fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO, | |
526 | ifm->ifm_type); | |
527 | fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen); | |
528 | fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next, | |
529 | lim); | |
530 | exit (1); | |
531 | } | |
532 | ||
533 | next += ifm->ifm_msglen; | |
534 | ifam = NULL; | |
535 | addrcount = 0; | |
536 | while (next < lim) { | |
537 | ||
538 | nextifm = (struct if_msghdr *)next; | |
539 | ||
540 | if (nextifm->ifm_type != RTM_NEWADDR) | |
541 | break; | |
542 | ||
543 | if (ifam == NULL) | |
544 | ifam = (struct ifa_msghdr *)nextifm; | |
545 | ||
546 | addrcount++; | |
547 | next += nextifm->ifm_msglen; | |
548 | } | |
549 | ||
550 | if (all || namesonly) { | |
551 | if (uponly) | |
552 | if ((flags & IFF_UP) == 0) | |
553 | continue; /* not up */ | |
554 | if (downonly) | |
555 | if (flags & IFF_UP) | |
556 | continue; /* not down */ | |
557 | strncpy(name, sdl->sdl_data, sdl->sdl_nlen); | |
558 | name[sdl->sdl_nlen] = '\0'; | |
559 | if (namesonly) { | |
560 | if (afp == NULL || | |
561 | afp->af_status != ether_status || | |
562 | sdl->sdl_type == IFT_ETHER) { | |
563 | if (need_nl) | |
564 | putchar(' '); | |
565 | fputs(name, stdout); | |
566 | need_nl++; | |
567 | } | |
568 | continue; | |
569 | } | |
570 | } else { | |
571 | if (strlen(name) != sdl->sdl_nlen) | |
572 | continue; /* not same len */ | |
573 | if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0) | |
574 | continue; /* not same name */ | |
575 | } | |
576 | ||
577 | if (argc > 0) | |
578 | ifconfig(argc, argv, afp); | |
579 | else | |
580 | status(afp, addrcount, sdl, ifm, ifam); | |
581 | ||
582 | if (all == 0 && namesonly == 0) { | |
583 | foundit++; /* flag it as 'done' */ | |
584 | break; | |
585 | } | |
586 | } | |
587 | free(buf); | |
588 | ||
589 | if (namesonly && need_nl > 0) | |
590 | putchar('\n'); | |
591 | ||
592 | if (all == 0 && namesonly == 0 && foundit == 0) | |
593 | errx(1, "interface %s does not exist", name); | |
594 | ||
595 | ||
596 | exit (0); | |
597 | } | |
598 | ||
599 | ||
600 | int | |
601 | ifconfig(argc, argv, afp) | |
602 | int argc; | |
603 | char *const *argv; | |
604 | const struct afswtch *afp; | |
605 | { | |
606 | int s; | |
607 | ||
608 | if (afp == NULL) | |
609 | afp = &afs[0]; | |
610 | ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af; | |
611 | strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); | |
612 | ||
613 | if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) | |
614 | err(1, "socket"); | |
615 | ||
616 | while (argc > 0) { | |
617 | register const struct cmd *p; | |
618 | ||
619 | for (p = cmds; p->c_name; p++) | |
620 | if (strcmp(*argv, p->c_name) == 0) | |
621 | break; | |
622 | if (p->c_name == 0 && setaddr) | |
623 | p++; /* got src, do dst */ | |
624 | if (p->c_func || p->c_func2) { | |
625 | if (p->c_parameter == NEXTARG) { | |
626 | if (argv[1] == NULL) | |
627 | errx(1, "'%s' requires argument", | |
628 | p->c_name); | |
629 | (*p->c_func)(argv[1], 0, s, afp); | |
630 | argc--, argv++; | |
631 | } else if (p->c_parameter == NEXTARG2) { | |
632 | if (argc < 3) | |
633 | errx(1, "'%s' requires 2 arguments", | |
634 | p->c_name); | |
635 | (*p->c_func2)(argv[1], argv[2], s, afp); | |
636 | argc -= 2, argv += 2; | |
637 | } else | |
638 | (*p->c_func)(*argv, p->c_parameter, s, afp); | |
639 | } | |
640 | argc--, argv++; | |
641 | } | |
642 | #ifdef INET6 | |
643 | if (ifr.ifr_addr.sa_family == AF_INET6 && explicit_prefix == 0) { | |
644 | /* Aggregatable address architecture defines all prefixes | |
645 | are 64. So, it is convenient to set prefixlen to 64 if | |
646 | it is not specified. */ | |
647 | setifprefixlen("64", 0, s, afp); | |
648 | /* in6_getprefix("64", MASK) if MASK is available here... */ | |
649 | } | |
650 | #endif | |
651 | #ifdef NS | |
652 | if (setipdst && ifr.ifr_addr.sa_family == AF_NS) { | |
653 | struct nsip_req rq; | |
654 | int size = sizeof(rq); | |
655 | ||
656 | rq.rq_ns = addreq.ifra_addr; | |
657 | rq.rq_ip = addreq.ifra_dstaddr; | |
658 | ||
659 | if (setsockopt(s, 0, SO_NSIP_ROUTE, &rq, size) < 0) | |
660 | Perror("Encapsulation Routing"); | |
661 | } | |
662 | #endif | |
663 | if (clearaddr) { | |
664 | if (afp->af_ridreq == NULL || afp->af_difaddr == 0) { | |
665 | warnx("interface %s cannot change %s addresses!", | |
666 | name, afp->af_name); | |
667 | clearaddr = NULL; | |
668 | } | |
669 | } | |
670 | if (clearaddr) { | |
671 | int ret; | |
672 | strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name); | |
673 | if ((ret = ioctl(s, afp->af_difaddr, afp->af_ridreq)) < 0) { | |
674 | if (errno == EADDRNOTAVAIL && (doalias >= 0)) { | |
675 | /* means no previous address for interface */ | |
676 | } else | |
677 | Perror("ioctl (SIOCDIFADDR)"); | |
678 | } | |
679 | } | |
680 | if (newaddr) { | |
681 | if (afp->af_addreq == NULL || afp->af_aifaddr == 0) { | |
682 | warnx("interface %s cannot change %s addresses!", | |
683 | name, afp->af_name); | |
684 | newaddr = NULL; | |
685 | } | |
686 | } | |
687 | if (newaddr && (setaddr || setmask)) { | |
688 | strncpy(afp->af_addreq, name, sizeof ifr.ifr_name); | |
689 | if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0) | |
690 | Perror("ioctl (SIOCAIFADDR)"); | |
691 | } | |
692 | close(s); | |
693 | return(0); | |
694 | } | |
695 | #define RIDADDR 0 | |
696 | #define ADDR 1 | |
697 | #define MASK 2 | |
698 | #define DSTADDR 3 | |
699 | ||
700 | /*ARGSUSED*/ | |
701 | void | |
702 | setifaddr(addr, param, s, afp) | |
703 | const char *addr; | |
704 | int param; | |
705 | int s; | |
706 | const struct afswtch *afp; | |
707 | { | |
708 | if (*afp->af_getaddr == NULL) | |
709 | return; | |
710 | /* | |
711 | * Delay the ioctl to set the interface addr until flags are all set. | |
712 | * The address interpretation may depend on the flags, | |
713 | * and the flags may change when the address is set. | |
714 | */ | |
715 | setaddr++; | |
716 | if (doalias == 0 && afp->af_af != AF_LINK) | |
717 | clearaddr = 1; | |
718 | (*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR)); | |
719 | } | |
720 | ||
721 | void | |
722 | settunnel(src, dst, s, afp) | |
723 | const char *src, *dst; | |
724 | int s; | |
725 | const struct afswtch *afp; | |
726 | { | |
727 | struct addrinfo hints, *srcres, *dstres; | |
728 | struct ifaliasreq addreq; | |
729 | int ecode; | |
730 | #ifdef INET6 | |
731 | struct in6_aliasreq in6_addreq; | |
732 | #endif | |
733 | ||
734 | memset(&hints, 0, sizeof(hints)); | |
735 | hints.ai_family = afp->af_af; | |
736 | ||
737 | if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0) | |
738 | errx(1, "error in parsing address string: %s", | |
739 | gai_strerror(ecode)); | |
740 | ||
741 | if ((ecode = getaddrinfo(dst, NULL, NULL, &dstres)) != 0) | |
742 | errx(1, "error in parsing address string: %s", | |
743 | gai_strerror(ecode)); | |
744 | ||
745 | if (srcres->ai_addr->sa_family != dstres->ai_addr->sa_family) | |
746 | errx(1, | |
747 | "source and destination address families do not match"); | |
748 | ||
749 | switch (srcres->ai_addr->sa_family) { | |
750 | case AF_INET: | |
751 | memset(&addreq, 0, sizeof(addreq)); | |
752 | strncpy(addreq.ifra_name, name, IFNAMSIZ); | |
753 | memcpy(&addreq.ifra_addr, srcres->ai_addr, | |
754 | srcres->ai_addr->sa_len); | |
755 | memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, | |
756 | dstres->ai_addr->sa_len); | |
757 | ||
758 | if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0) | |
759 | warn("SIOCSIFPHYADDR"); | |
760 | break; | |
761 | ||
762 | #ifdef INET6 | |
763 | case AF_INET6: | |
764 | memset(&in6_addreq, 0, sizeof(in6_addreq)); | |
765 | strncpy(in6_addreq.ifra_name, name, IFNAMSIZ); | |
766 | memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, | |
767 | srcres->ai_addr->sa_len); | |
768 | memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr, | |
769 | dstres->ai_addr->sa_len); | |
770 | ||
771 | if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0) | |
772 | warn("SIOCSIFPHYADDR_IN6"); | |
773 | break; | |
774 | #endif /* INET6 */ | |
775 | ||
776 | default: | |
777 | warn("address family not supported"); | |
778 | } | |
779 | ||
780 | freeaddrinfo(srcres); | |
781 | freeaddrinfo(dstres); | |
782 | } | |
783 | ||
784 | /* ARGSUSED */ | |
785 | void | |
786 | deletetunnel(vname, param, s, afp) | |
787 | const char *vname; | |
788 | int param; | |
789 | int s; | |
790 | const struct afswtch *afp; | |
791 | { | |
792 | ||
793 | if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0) | |
794 | err(1, "SIOCDIFPHYADDR"); | |
795 | } | |
796 | ||
797 | void | |
798 | setifnetmask(addr, dummy, s, afp) | |
799 | const char *addr; | |
800 | int dummy ; | |
801 | int s; | |
802 | const struct afswtch *afp; | |
803 | { | |
804 | if (*afp->af_getaddr == NULL) | |
805 | return; | |
806 | setmask++; | |
807 | (*afp->af_getaddr)(addr, MASK); | |
808 | } | |
809 | ||
810 | #ifdef INET6 | |
811 | void | |
812 | setifprefixlen(addr, dummy, s, afp) | |
813 | const char *addr; | |
814 | int dummy ; | |
815 | int s; | |
816 | const struct afswtch *afp; | |
817 | { | |
818 | if (*afp->af_getprefix) | |
819 | (*afp->af_getprefix)(addr, MASK); | |
820 | explicit_prefix = 1; | |
821 | } | |
822 | ||
823 | void | |
824 | setip6flags(dummyaddr, flag, dummysoc, afp) | |
825 | const char *dummyaddr ; | |
826 | int flag; | |
827 | int dummysoc ; | |
828 | const struct afswtch *afp; | |
829 | { | |
830 | if (afp->af_af != AF_INET6) | |
831 | err(1, "address flags can be set only for inet6 addresses"); | |
832 | ||
833 | if (flag < 0) | |
834 | in6_addreq.ifra_flags &= ~(-flag); | |
835 | else | |
836 | in6_addreq.ifra_flags |= flag; | |
837 | } | |
838 | ||
839 | void | |
840 | setip6pltime(seconds, dummy, s, afp) | |
841 | const char *seconds; | |
842 | int dummy ; | |
843 | int s; | |
844 | const struct afswtch *afp; | |
845 | { | |
846 | setip6lifetime("pltime", seconds, s, afp); | |
847 | } | |
848 | ||
849 | void | |
850 | setip6vltime(seconds, dummy, s, afp) | |
851 | const char *seconds; | |
852 | int dummy ; | |
853 | int s; | |
854 | const struct afswtch *afp; | |
855 | { | |
856 | setip6lifetime("vltime", seconds, s, afp); | |
857 | } | |
858 | ||
859 | void | |
860 | setip6lifetime(cmd, val, s, afp) | |
861 | const char *cmd; | |
862 | const char *val; | |
863 | int s; | |
864 | const struct afswtch *afp; | |
865 | { | |
866 | time_t newval, t; | |
867 | char *ep; | |
868 | ||
869 | t = time(NULL); | |
870 | newval = (time_t)strtoul(val, &ep, 0); | |
871 | if (val == ep) | |
872 | errx(1, "invalid %s", cmd); | |
873 | if (afp->af_af != AF_INET6) | |
874 | errx(1, "%s not allowed for the AF", cmd); | |
875 | if (strcmp(cmd, "vltime") == 0) { | |
876 | in6_addreq.ifra_lifetime.ia6t_expire = t + newval; | |
877 | in6_addreq.ifra_lifetime.ia6t_vltime = newval; | |
878 | } else if (strcmp(cmd, "pltime") == 0) { | |
879 | in6_addreq.ifra_lifetime.ia6t_preferred = t + newval; | |
880 | in6_addreq.ifra_lifetime.ia6t_pltime = newval; | |
881 | } | |
882 | } | |
883 | #endif | |
884 | ||
885 | void | |
886 | setifbroadaddr(addr, dummy, s, afp) | |
887 | const char *addr; | |
888 | int dummy ; | |
889 | int s; | |
890 | const struct afswtch *afp; | |
891 | { | |
892 | if (afp->af_getaddr) | |
893 | (*afp->af_getaddr)(addr, DSTADDR); | |
894 | } | |
895 | ||
896 | void | |
897 | setifipdst(addr, dummy, s, afp) | |
898 | const char *addr; | |
899 | int dummy ; | |
900 | int s; | |
901 | const struct afswtch *afp; | |
902 | { | |
903 | in_getaddr(addr, DSTADDR); | |
904 | setipdst++; | |
905 | clearaddr = 0; | |
906 | newaddr = 0; | |
907 | } | |
908 | #define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr)) | |
909 | ||
910 | void | |
911 | notealias(addr, param, s, afp) | |
912 | const char *addr; | |
913 | int param; | |
914 | int s; | |
915 | const struct afswtch *afp; | |
916 | { | |
917 | if (setaddr && doalias == 0 && param < 0) | |
918 | bcopy((caddr_t)rqtosa(af_addreq), | |
919 | (caddr_t)rqtosa(af_ridreq), | |
920 | rqtosa(af_addreq)->sa_len); | |
921 | doalias = param; | |
922 | if (param < 0) { | |
923 | clearaddr = 1; | |
924 | newaddr = 0; | |
925 | } else | |
926 | clearaddr = 0; | |
927 | } | |
928 | ||
929 | /*ARGSUSED*/ | |
930 | void | |
931 | setifdstaddr(addr, param, s, afp) | |
932 | const char *addr; | |
933 | int param ; | |
934 | int s; | |
935 | const struct afswtch *afp; | |
936 | { | |
937 | if (*afp->af_getaddr == NULL) | |
938 | return; | |
939 | (*afp->af_getaddr)(addr, DSTADDR); | |
940 | } | |
941 | ||
942 | /* | |
943 | * Note: doing an SIOCIGIFFLAGS scribbles on the union portion | |
944 | * of the ifreq structure, which may confuse other parts of ifconfig. | |
945 | * Make a private copy so we can avoid that. | |
946 | */ | |
947 | void | |
948 | setifflags(vname, value, s, afp) | |
949 | const char *vname; | |
950 | int value; | |
951 | int s; | |
952 | const struct afswtch *afp; | |
953 | { | |
954 | struct ifreq my_ifr; | |
955 | ||
956 | bcopy((char *)&ifr, (char *)&my_ifr, sizeof(struct ifreq)); | |
957 | ||
958 | if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&my_ifr) < 0) { | |
959 | Perror("ioctl (SIOCGIFFLAGS)"); | |
960 | exit(1); | |
961 | } | |
962 | strncpy(my_ifr.ifr_name, name, sizeof (my_ifr.ifr_name)); | |
963 | flags = my_ifr.ifr_flags; | |
964 | ||
965 | if (value < 0) { | |
966 | value = -value; | |
967 | flags &= ~value; | |
968 | } else | |
969 | flags |= value; | |
970 | my_ifr.ifr_flags = flags; | |
971 | if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&my_ifr) < 0) | |
972 | Perror(vname); | |
973 | } | |
974 | ||
975 | void | |
976 | setifmetric(val, dummy, s, afp) | |
977 | const char *val; | |
978 | int dummy ; | |
979 | int s; | |
980 | const struct afswtch *afp; | |
981 | { | |
982 | strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
983 | ifr.ifr_metric = atoi(val); | |
984 | if (ioctl(s, SIOCSIFMETRIC, (caddr_t)&ifr) < 0) | |
985 | warn("ioctl (set metric)"); | |
986 | } | |
987 | ||
988 | void | |
989 | setifmtu(val, dummy, s, afp) | |
990 | const char *val; | |
991 | int dummy ; | |
992 | int s; | |
993 | const struct afswtch *afp; | |
994 | { | |
995 | strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
996 | ifr.ifr_mtu = atoi(val); | |
997 | if (ioctl(s, SIOCSIFMTU, (caddr_t)&ifr) < 0) | |
998 | warn("ioctl (set mtu)"); | |
999 | } | |
1000 | ||
1001 | void | |
1002 | setiflladdr(val, dummy, s, afp) | |
1003 | const char *val; | |
1004 | int dummy; | |
1005 | int s; | |
1006 | const struct afswtch *afp; | |
1007 | { | |
1008 | struct ether_addr *ea; | |
1009 | ||
1010 | ea = ether_aton(val); | |
1011 | if (ea == NULL) { | |
1012 | warn("malformed link-level address"); | |
1013 | return; | |
1014 | } | |
1015 | strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name)); | |
1016 | ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; | |
1017 | ifr.ifr_addr.sa_family = AF_LINK; | |
1018 | bcopy(ea, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); | |
1019 | if (ioctl(s, SIOCSIFLLADDR, (caddr_t)&ifr) < 0) | |
1020 | warn("ioctl (set lladdr)"); | |
1021 | ||
1022 | return; | |
1023 | } | |
1024 | ||
1025 | #define IFFBITS \ | |
1026 | "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \ | |
1027 | "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ | |
1028 | "\20MULTICAST" | |
1029 | ||
1030 | /* | |
1031 | * Print the status of the interface. If an address family was | |
1032 | * specified, show it and it only; otherwise, show them all. | |
1033 | */ | |
1034 | void | |
1035 | status(afp, addrcount, sdl, ifm, ifam) | |
1036 | const struct afswtch *afp; | |
1037 | int addrcount; | |
1038 | struct sockaddr_dl *sdl; | |
1039 | struct if_msghdr *ifm; | |
1040 | struct ifa_msghdr *ifam; | |
1041 | { | |
1042 | const struct afswtch *p = NULL; | |
1043 | struct rt_addrinfo info; | |
1044 | int allfamilies, s; | |
1045 | struct ifstat ifs; | |
1046 | ||
1047 | if (afp == NULL) { | |
1048 | allfamilies = 1; | |
1049 | afp = &afs[0]; | |
1050 | } else | |
1051 | allfamilies = 0; | |
1052 | ||
1053 | ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af; | |
1054 | strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); | |
1055 | ||
1056 | if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0) | |
1057 | err(1, "socket"); | |
1058 | ||
1059 | /* | |
1060 | * XXX is it we are doing a SIOCGIFMETRIC etc for one family. | |
1061 | * is it possible that the metric and mtu can be different for | |
1062 | * each family? If so, we have a format problem, because the | |
1063 | * metric and mtu is printed on the global the flags line. | |
1064 | */ | |
1065 | if (ioctl(s, SIOCGIFMETRIC, (caddr_t)&ifr) < 0) | |
1066 | warn("ioctl (SIOCGIFMETRIC)"); | |
1067 | else | |
1068 | metric = ifr.ifr_metric; | |
1069 | ||
1070 | if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) | |
1071 | warn("ioctl (SIOCGIFMTU)"); | |
1072 | else | |
1073 | mtu = ifr.ifr_mtu; | |
1074 | ||
1075 | printf("%s: ", name); | |
1076 | printb("flags", flags, IFFBITS); | |
1077 | if (metric) | |
1078 | printf(" metric %d", metric); | |
1079 | if (mtu) | |
1080 | printf(" mtu %d", mtu); | |
1081 | putchar('\n'); | |
1082 | ||
1083 | tunnel_status(s); | |
1084 | ||
1085 | while (addrcount > 0) { | |
1086 | ||
1087 | info.rti_addrs = ifam->ifam_addrs; | |
1088 | ||
1089 | /* Expand the compacted addresses */ | |
1090 | rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, | |
1091 | &info); | |
1092 | ||
1093 | if (!allfamilies) { | |
1094 | if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family) { | |
1095 | p = afp; | |
1096 | (*p->af_status)(s, &info); | |
1097 | } | |
1098 | } else for (p = afs; p->af_name; p++) { | |
1099 | if (p->af_af == info.rti_info[RTAX_IFA]->sa_family) | |
1100 | (*p->af_status)(s, &info); | |
1101 | } | |
1102 | addrcount--; | |
1103 | ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen); | |
1104 | } | |
1105 | if (allfamilies || afp->af_status == ether_status) | |
1106 | ether_status(s, (struct rt_addrinfo *)sdl); | |
1107 | #if USE_IF_MEDIA | |
1108 | if (allfamilies || afp->af_status == media_status) | |
1109 | media_status(s, NULL); | |
1110 | #endif | |
1111 | #ifdef USE_VLANS | |
1112 | if (allfamilies || afp->af_status == vlan_status) | |
1113 | vlan_status(s, NULL); | |
1114 | #endif | |
1115 | #ifdef USE_IEEE80211 | |
1116 | if (allfamilies || afp->af_status == ieee80211_status) | |
1117 | ieee80211_status(s, NULL); | |
1118 | #endif | |
1119 | strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name); | |
1120 | if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) | |
1121 | printf("%s", ifs.ascii); | |
1122 | ||
1123 | if (!allfamilies && !p && afp->af_status != media_status && | |
1124 | afp->af_status != ether_status | |
1125 | #ifdef USE_VLANS | |
1126 | && afp->af_status != vlan_status | |
1127 | #endif | |
1128 | ) | |
1129 | warnx("%s has no %s interface address!", name, afp->af_name); | |
1130 | ||
1131 | close(s); | |
1132 | return; | |
1133 | } | |
1134 | ||
1135 | void | |
1136 | tunnel_status(s) | |
1137 | int s; | |
1138 | { | |
1139 | char psrcaddr[NI_MAXHOST]; | |
1140 | char pdstaddr[NI_MAXHOST]; | |
1141 | u_long srccmd, dstcmd; | |
1142 | struct ifreq *ifrp; | |
1143 | const char *ver = ""; | |
1144 | #ifdef NI_WITHSCOPEID | |
1145 | const int niflag = NI_NUMERICHOST | NI_WITHSCOPEID; | |
1146 | #else | |
1147 | const int niflag = NI_NUMERICHOST; | |
1148 | #endif | |
1149 | #ifdef INET6 | |
1150 | struct in6_ifreq in6_ifr; | |
1151 | int s6; | |
1152 | #endif /* INET6 */ | |
1153 | ||
1154 | psrcaddr[0] = pdstaddr[0] = '\0'; | |
1155 | ||
1156 | #ifdef INET6 | |
1157 | memset(&in6_ifr, 0, sizeof(in6_ifr)); | |
1158 | strncpy(in6_ifr.ifr_name, name, IFNAMSIZ); | |
1159 | s6 = socket(AF_INET6, SOCK_DGRAM, 0); | |
1160 | if (s6 < 0) { | |
1161 | srccmd = SIOCGIFPSRCADDR; | |
1162 | dstcmd = SIOCGIFPDSTADDR; | |
1163 | ifrp = 𝔦 | |
1164 | } else { | |
1165 | close(s6); | |
1166 | srccmd = SIOCGIFPSRCADDR_IN6; | |
1167 | dstcmd = SIOCGIFPDSTADDR_IN6; | |
1168 | ifrp = (struct ifreq *)&in6_ifr; | |
1169 | } | |
1170 | #else /* INET6 */ | |
1171 | srccmd = SIOCGIFPSRCADDR; | |
1172 | dstcmd = SIOCGIFPDSTADDR; | |
1173 | ifrp = 𝔦 | |
1174 | #endif /* INET6 */ | |
1175 | ||
1176 | if (ioctl(s, srccmd, (caddr_t)ifrp) < 0) | |
1177 | return; | |
1178 | #ifdef INET6 | |
1179 | if (ifrp->ifr_addr.sa_family == AF_INET6) | |
1180 | in6_fillscopeid((struct sockaddr_in6 *)&ifrp->ifr_addr); | |
1181 | #endif | |
1182 | getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len, | |
1183 | psrcaddr, sizeof(psrcaddr), 0, 0, niflag); | |
1184 | #ifdef INET6 | |
1185 | if (ifrp->ifr_addr.sa_family == AF_INET6) | |
1186 | ver = "6"; | |
1187 | #endif | |
1188 | ||
1189 | if (ioctl(s, dstcmd, (caddr_t)ifrp) < 0) | |
1190 | return; | |
1191 | #ifdef INET6 | |
1192 | if (ifrp->ifr_addr.sa_family == AF_INET6) | |
1193 | in6_fillscopeid((struct sockaddr_in6 *)&ifrp->ifr_addr); | |
1194 | #endif | |
1195 | getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len, | |
1196 | pdstaddr, sizeof(pdstaddr), 0, 0, niflag); | |
1197 | ||
1198 | printf("\ttunnel inet%s %s --> %s\n", ver, | |
1199 | psrcaddr, pdstaddr); | |
1200 | } | |
1201 | ||
1202 | void | |
1203 | in_status(s, info) | |
1204 | int s ; | |
1205 | struct rt_addrinfo * info; | |
1206 | { | |
1207 | struct sockaddr_in *sin, null_sin; | |
1208 | ||
1209 | memset(&null_sin, 0, sizeof(null_sin)); | |
1210 | ||
1211 | sin = (struct sockaddr_in *)info->rti_info[RTAX_IFA]; | |
1212 | printf("\tinet %s ", inet_ntoa(sin->sin_addr)); | |
1213 | ||
1214 | if (flags & IFF_POINTOPOINT) { | |
1215 | /* note RTAX_BRD overlap with IFF_BROADCAST */ | |
1216 | sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD]; | |
1217 | if (!sin) | |
1218 | sin = &null_sin; | |
1219 | printf("--> %s ", inet_ntoa(sin->sin_addr)); | |
1220 | } | |
1221 | ||
1222 | sin = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK]; | |
1223 | if (!sin) | |
1224 | sin = &null_sin; | |
1225 | printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr)); | |
1226 | ||
1227 | if (flags & IFF_BROADCAST) { | |
1228 | /* note RTAX_BRD overlap with IFF_POINTOPOINT */ | |
1229 | sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD]; | |
1230 | if (sin && sin->sin_addr.s_addr != 0) | |
1231 | printf("broadcast %s", inet_ntoa(sin->sin_addr)); | |
1232 | } | |
1233 | putchar('\n'); | |
1234 | } | |
1235 | ||
1236 | #ifdef INET6 | |
1237 | void | |
1238 | in6_fillscopeid(sin6) | |
1239 | struct sockaddr_in6 *sin6; | |
1240 | { | |
1241 | #if defined(__KAME__) && defined(KAME_SCOPEID) | |
1242 | if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { | |
1243 | sin6->sin6_scope_id = | |
1244 | ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); | |
1245 | sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; | |
1246 | } | |
1247 | #endif | |
1248 | } | |
1249 | ||
1250 | void | |
1251 | in6_status(s, info) | |
1252 | int s ; | |
1253 | struct rt_addrinfo * info; | |
1254 | { | |
1255 | struct sockaddr_in6 *sin, null_sin; | |
1256 | struct in6_ifreq ifr6; | |
1257 | int s6; | |
1258 | u_int32_t flags6; | |
1259 | struct in6_addrlifetime lifetime; | |
1260 | time_t t = time(NULL); | |
1261 | int error; | |
1262 | u_int32_t scopeid; | |
1263 | ||
1264 | memset(&null_sin, 0, sizeof(null_sin)); | |
1265 | ||
1266 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_IFA]; | |
1267 | strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name)); | |
1268 | if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { | |
1269 | perror("ifconfig: socket"); | |
1270 | return; | |
1271 | } | |
1272 | ifr6.ifr_addr = *sin; | |
1273 | if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) { | |
1274 | perror("ifconfig: ioctl(SIOCGIFAFLAG_IN6)"); | |
1275 | close(s6); | |
1276 | return; | |
1277 | } | |
1278 | flags6 = ifr6.ifr_ifru.ifru_flags6; | |
1279 | memset(&lifetime, 0, sizeof(lifetime)); | |
1280 | ifr6.ifr_addr = *sin; | |
1281 | if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) { | |
1282 | perror("ifconfig: ioctl(SIOCGIFALIFETIME_IN6)"); | |
1283 | close(s6); | |
1284 | return; | |
1285 | } | |
1286 | lifetime = ifr6.ifr_ifru.ifru_lifetime; | |
1287 | close(s6); | |
1288 | ||
1289 | /* XXX: embedded link local addr check */ | |
1290 | if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && | |
1291 | *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { | |
1292 | u_short index; | |
1293 | ||
1294 | index = *(u_short *)&sin->sin6_addr.s6_addr[2]; | |
1295 | *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; | |
1296 | if (sin->sin6_scope_id == 0) | |
1297 | sin->sin6_scope_id = ntohs(index); | |
1298 | } | |
1299 | scopeid = sin->sin6_scope_id; | |
1300 | ||
1301 | error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf, | |
1302 | sizeof(addr_buf), NULL, 0, | |
1303 | NI_NUMERICHOST|NI_WITHSCOPEID); | |
1304 | if (error != 0) | |
1305 | inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, | |
1306 | sizeof(addr_buf)); | |
1307 | printf("\tinet6 %s ", addr_buf); | |
1308 | ||
1309 | if (flags & IFF_POINTOPOINT) { | |
1310 | /* note RTAX_BRD overlap with IFF_BROADCAST */ | |
1311 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_BRD]; | |
1312 | /* | |
1313 | * some of the interfaces do not have valid destination | |
1314 | * address. | |
1315 | */ | |
1316 | if (sin && sin->sin6_family == AF_INET6) { | |
1317 | int error; | |
1318 | ||
1319 | /* XXX: embedded link local addr check */ | |
1320 | if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && | |
1321 | *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) { | |
1322 | u_short index; | |
1323 | ||
1324 | index = *(u_short *)&sin->sin6_addr.s6_addr[2]; | |
1325 | *(u_short *)&sin->sin6_addr.s6_addr[2] = 0; | |
1326 | if (sin->sin6_scope_id == 0) | |
1327 | sin->sin6_scope_id = ntohs(index); | |
1328 | } | |
1329 | ||
1330 | error = getnameinfo((struct sockaddr *)sin, | |
1331 | sin->sin6_len, addr_buf, | |
1332 | sizeof(addr_buf), NULL, 0, | |
1333 | NI_NUMERICHOST|NI_WITHSCOPEID); | |
1334 | if (error != 0) | |
1335 | inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf, | |
1336 | sizeof(addr_buf)); | |
1337 | printf("--> %s ", addr_buf); | |
1338 | } | |
1339 | } | |
1340 | ||
1341 | sin = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK]; | |
1342 | if (!sin) | |
1343 | sin = &null_sin; | |
1344 | printf("prefixlen %d ", prefix(&sin->sin6_addr, | |
1345 | sizeof(struct in6_addr))); | |
1346 | ||
1347 | if ((flags6 & IN6_IFF_ANYCAST) != 0) | |
1348 | printf("anycast "); | |
1349 | if ((flags6 & IN6_IFF_TENTATIVE) != 0) | |
1350 | printf("tentative "); | |
1351 | if ((flags6 & IN6_IFF_DUPLICATED) != 0) | |
1352 | printf("duplicated "); | |
1353 | if ((flags6 & IN6_IFF_DETACHED) != 0) | |
1354 | printf("detached "); | |
1355 | if ((flags6 & IN6_IFF_DEPRECATED) != 0) | |
1356 | printf("deprecated "); | |
1357 | if ((flags6 & IN6_IFF_AUTOCONF) != 0) | |
1358 | printf("autoconf "); | |
1359 | if ((flags6 & IN6_IFF_TEMPORARY) != 0) | |
1360 | printf("temporary "); | |
1361 | ||
1362 | if (scopeid) | |
1363 | printf("scopeid 0x%x ", scopeid); | |
1364 | ||
1365 | if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) { | |
1366 | printf("pltime "); | |
1367 | if (lifetime.ia6t_preferred) { | |
1368 | printf("%s ", lifetime.ia6t_preferred < t | |
1369 | ? "0" : sec2str(lifetime.ia6t_preferred - t)); | |
1370 | } else | |
1371 | printf("infty "); | |
1372 | ||
1373 | printf("vltime "); | |
1374 | if (lifetime.ia6t_expire) { | |
1375 | printf("%s ", lifetime.ia6t_expire < t | |
1376 | ? "0" : sec2str(lifetime.ia6t_expire - t)); | |
1377 | } else | |
1378 | printf("infty "); | |
1379 | } | |
1380 | ||
1381 | putchar('\n'); | |
1382 | } | |
1383 | #endif /*INET6*/ | |
1384 | ||
1385 | #ifdef NS | |
1386 | void | |
1387 | xns_status(s, info) | |
1388 | int s ; | |
1389 | struct rt_addrinfo * info; | |
1390 | { | |
1391 | struct sockaddr_ns *sns, null_sns; | |
1392 | ||
1393 | memset(&null_sns, 0, sizeof(null_sns)); | |
1394 | ||
1395 | sns = (struct sockaddr_ns *)info->rti_info[RTAX_IFA]; | |
1396 | printf("\tns %s ", ns_ntoa(sns->sns_addr)); | |
1397 | ||
1398 | if (flags & IFF_POINTOPOINT) { | |
1399 | sns = (struct sockaddr_ns *)info->rti_info[RTAX_BRD]; | |
1400 | if (!sns) | |
1401 | sns = &null_sns; | |
1402 | printf("--> %s ", ns_ntoa(sns->sns_addr)); | |
1403 | } | |
1404 | ||
1405 | putchar('\n'); | |
1406 | close(s); | |
1407 | } | |
1408 | #endif | |
1409 | ||
1410 | ||
1411 | void | |
1412 | ether_status(s, info) | |
1413 | int s ; | |
1414 | struct rt_addrinfo *info; | |
1415 | { | |
1416 | char *cp; | |
1417 | int n; | |
1418 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)info; | |
1419 | ||
1420 | cp = (char *)LLADDR(sdl); | |
1421 | if ((n = sdl->sdl_alen) > 0) { | |
1422 | if (sdl->sdl_type == IFT_ETHER) | |
1423 | printf ("\tether "); | |
1424 | else | |
1425 | printf ("\tlladdr "); | |
1426 | while (--n >= 0) | |
1427 | printf("%02x%c",*cp++ & 0xff, n>0? ':' : ' '); | |
1428 | putchar('\n'); | |
1429 | } | |
1430 | } | |
1431 | ||
1432 | void | |
1433 | Perror(cmd) | |
1434 | const char *cmd; | |
1435 | { | |
1436 | switch (errno) { | |
1437 | ||
1438 | case ENXIO: | |
1439 | errx(1, "%s: no such interface", cmd); | |
1440 | break; | |
1441 | ||
1442 | case EPERM: | |
1443 | errx(1, "%s: permission denied", cmd); | |
1444 | break; | |
1445 | ||
1446 | default: | |
1447 | err(1, "%s", cmd); | |
1448 | } | |
1449 | } | |
1450 | ||
1451 | #define SIN(x) ((struct sockaddr_in *) &(x)) | |
1452 | struct sockaddr_in *sintab[] = { | |
1453 | SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr), | |
1454 | SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)}; | |
1455 | ||
1456 | void | |
1457 | in_getaddr(s, which) | |
1458 | const char *s; | |
1459 | int which; | |
1460 | { | |
1461 | register struct sockaddr_in *sin = sintab[which]; | |
1462 | struct hostent *hp; | |
1463 | struct netent *np; | |
1464 | ||
1465 | sin->sin_len = sizeof(*sin); | |
1466 | if (which != MASK) | |
1467 | sin->sin_family = AF_INET; | |
1468 | ||
1469 | if (which == ADDR) { | |
1470 | char *p = NULL; | |
1471 | ||
1472 | if((p = strrchr(s, '/')) != NULL) { | |
1473 | /* address is `name/masklen' */ | |
1474 | int masklen; | |
1475 | int ret; | |
1476 | struct sockaddr_in *min = sintab[MASK]; | |
1477 | *p = '\0'; | |
1478 | ret = sscanf(p+1, "%u", &masklen); | |
1479 | if(ret != 1 || (masklen < 0 || masklen > 32)) { | |
1480 | *p = '/'; | |
1481 | errx(1, "%s: bad value", s); | |
1482 | } | |
1483 | min->sin_len = sizeof(*min); | |
1484 | min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & | |
1485 | 0xffffffff); | |
1486 | } | |
1487 | } | |
1488 | ||
1489 | if (inet_aton(s, &sin->sin_addr)) | |
1490 | return; | |
1491 | if ((hp = gethostbyname(s)) != 0) | |
1492 | bcopy(hp->h_addr, (char *)&sin->sin_addr, | |
1493 | MIN(hp->h_length, sizeof(sin->sin_addr))); | |
1494 | else if ((np = getnetbyname(s)) != 0) | |
1495 | sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); | |
1496 | else | |
1497 | errx(1, "%s: bad value", s); | |
1498 | } | |
1499 | ||
1500 | #ifdef INET6 | |
1501 | #define SIN6(x) ((struct sockaddr_in6 *) &(x)) | |
1502 | struct sockaddr_in6 *sin6tab[] = { | |
1503 | SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr), | |
1504 | SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)}; | |
1505 | ||
1506 | void | |
1507 | in6_getaddr(s, which) | |
1508 | const char *s; | |
1509 | int which; | |
1510 | { | |
1511 | register struct sockaddr_in6 *sin = sin6tab[which]; | |
1512 | struct addrinfo hints, *res; | |
1513 | int error = -1; | |
1514 | ||
1515 | newaddr &= 1; | |
1516 | ||
1517 | sin->sin6_len = sizeof(*sin); | |
1518 | if (which != MASK) | |
1519 | sin->sin6_family = AF_INET6; | |
1520 | ||
1521 | if (which == ADDR) { | |
1522 | char *p = NULL; | |
1523 | if((p = strrchr(s, '/')) != NULL) { | |
1524 | *p = '\0'; | |
1525 | in6_getprefix(p + 1, MASK); | |
1526 | explicit_prefix = 1; | |
1527 | } | |
1528 | } | |
1529 | ||
1530 | if (sin->sin6_family == AF_INET6) { | |
1531 | bzero(&hints, sizeof(struct addrinfo)); | |
1532 | hints.ai_family = AF_INET6; | |
1533 | error = getaddrinfo(s, NULL, &hints, &res); | |
1534 | } | |
1535 | if (error != 0) { | |
1536 | if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1) | |
1537 | errx(1, "%s: bad value", s); | |
1538 | } else | |
1539 | bcopy(res->ai_addr, sin, res->ai_addrlen); | |
1540 | } | |
1541 | ||
1542 | void | |
1543 | in6_getprefix(plen, which) | |
1544 | const char *plen; | |
1545 | int which; | |
1546 | { | |
1547 | register struct sockaddr_in6 *sin = sin6tab[which]; | |
1548 | register u_char *cp; | |
1549 | int len = atoi(plen); | |
1550 | ||
1551 | if ((len < 0) || (len > 128)) | |
1552 | errx(1, "%s: bad value", plen); | |
1553 | sin->sin6_len = sizeof(*sin); | |
1554 | if (which != MASK) | |
1555 | sin->sin6_family = AF_INET6; | |
1556 | if ((len == 0) || (len == 128)) { | |
1557 | memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr)); | |
1558 | return; | |
1559 | } | |
1560 | memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr)); | |
1561 | for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8) | |
1562 | *cp++ = 0xff; | |
1563 | *cp = 0xff << (8 - len); | |
1564 | } | |
1565 | #endif | |
1566 | ||
1567 | /* | |
1568 | * Print a value a la the %b format of the kernel's printf | |
1569 | */ | |
1570 | void | |
1571 | printb(s, v, bits) | |
1572 | const char *s; | |
1573 | register unsigned v; | |
1574 | register const char *bits; | |
1575 | { | |
1576 | register int i, any = 0; | |
1577 | register char c; | |
1578 | ||
1579 | if (bits && *bits == 8) | |
1580 | printf("%s=%o", s, v); | |
1581 | else | |
1582 | printf("%s=%x", s, v); | |
1583 | bits++; | |
1584 | if (bits) { | |
1585 | putchar('<'); | |
1586 | while ((i = *bits++) != '\0') { | |
1587 | if (v & (1 << (i-1))) { | |
1588 | if (any) | |
1589 | putchar(','); | |
1590 | any = 1; | |
1591 | for (; (c = *bits) > 32; bits++) | |
1592 | putchar(c); | |
1593 | } else | |
1594 | for (; *bits > 32; bits++) | |
1595 | ; | |
1596 | } | |
1597 | putchar('>'); | |
1598 | } | |
1599 | } | |
1600 | ||
1601 | void | |
1602 | ether_getaddr(addr, which) | |
1603 | const char *addr; | |
1604 | int which; | |
1605 | { | |
1606 | struct ether_addr *ea; | |
1607 | struct sockaddr *sea = &ridreq.ifr_addr; | |
1608 | ||
1609 | ea = ether_aton(addr); | |
1610 | if (ea == NULL) | |
1611 | errx(1, "malformed ether address"); | |
1612 | if (which == MASK) | |
1613 | errx(1, "Ethernet does not use netmasks"); | |
1614 | sea->sa_family = AF_LINK; | |
1615 | sea->sa_len = ETHER_ADDR_LEN; | |
1616 | bcopy(ea, sea->sa_data, ETHER_ADDR_LEN); | |
1617 | } | |
1618 | ||
1619 | #ifdef INET6 | |
1620 | int | |
1621 | prefix(val, size) | |
1622 | void *val; | |
1623 | int size; | |
1624 | { | |
1625 | register u_char *name = (u_char *)val; | |
1626 | register int byte, bit, plen = 0; | |
1627 | ||
1628 | for (byte = 0; byte < size; byte++, plen += 8) | |
1629 | if (name[byte] != 0xff) | |
1630 | break; | |
1631 | if (byte == size) | |
1632 | return (plen); | |
1633 | for (bit = 7; bit != 0; bit--, plen++) | |
1634 | if (!(name[byte] & (1 << bit))) | |
1635 | break; | |
1636 | for (; bit != 0; bit--) | |
1637 | if (name[byte] & (1 << bit)) | |
1638 | return(0); | |
1639 | byte++; | |
1640 | for (; byte < size; byte++) | |
1641 | if (name[byte]) | |
1642 | return(0); | |
1643 | return (plen); | |
1644 | } | |
1645 | ||
1646 | static char * | |
1647 | sec2str(total) | |
1648 | time_t total; | |
1649 | { | |
1650 | static char result[256]; | |
1651 | int days, hours, mins, secs; | |
1652 | int first = 1; | |
1653 | char *p = result; | |
1654 | ||
1655 | if (0) { | |
1656 | days = total / 3600 / 24; | |
1657 | hours = (total / 3600) % 24; | |
1658 | mins = (total / 60) % 60; | |
1659 | secs = total % 60; | |
1660 | ||
1661 | if (days) { | |
1662 | first = 0; | |
1663 | p += sprintf(p, "%dd", days); | |
1664 | } | |
1665 | if (!first || hours) { | |
1666 | first = 0; | |
1667 | p += sprintf(p, "%dh", hours); | |
1668 | } | |
1669 | if (!first || mins) { | |
1670 | first = 0; | |
1671 | p += sprintf(p, "%dm", mins); | |
1672 | } | |
1673 | sprintf(p, "%ds", secs); | |
1674 | } else | |
1675 | sprintf(result, "%lu", (unsigned long)total); | |
1676 | ||
1677 | return(result); | |
1678 | } | |
1679 | #endif /*INET6*/ | |
1680 |