6769230 ifconfig gives tunnel security warnings when it shouldn't
[unleashed.git] / usr / src / cmd / cmd-inet / usr.sbin / ifconfig / ifconfig.c
blobb33fc6c1b6cced8a9f241b47137626978430d458
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 /*
6 * Copyright (c) 1983 Regents of the University of California.
7 * All rights reserved. The Berkeley software License Agreement
8 * specifies the terms and conditions for redistribution.
9 */
11 #include "defs.h"
12 #include "strings.h"
13 #include "ifconfig.h"
14 #include <compat.h>
15 #include <libdlpi.h>
16 #include <inet/ip.h>
17 #include <inet/ipsec_impl.h>
19 #define LOOPBACK_IF "lo0"
20 #define NONE_STR "none"
21 #define ARP_MOD_NAME "arp"
22 #define TUN_NAME "tun"
23 #define ATUN_NAME "atun"
24 #define TUN6TO4_NAME "6to4tun"
26 typedef struct if_flags {
27 uint64_t iff_value;
28 char *iff_name;
29 } if_flags_t;
31 static if_flags_t if_flags_tbl[] = {
32 { IFF_UP, "UP" },
33 { IFF_BROADCAST, "BROADCAST" },
34 { IFF_DEBUG, "DEBUG" },
35 { IFF_LOOPBACK, "LOOPBACK" },
36 { IFF_POINTOPOINT, "POINTOPOINT" },
37 { IFF_NOTRAILERS, "NOTRAILERS" },
38 { IFF_RUNNING, "RUNNING" },
39 { IFF_NOARP, "NOARP" },
40 { IFF_PROMISC, "PROMISC" },
41 { IFF_ALLMULTI, "ALLMULTI" },
42 { IFF_INTELLIGENT, "INTELLIGENT" },
43 { IFF_MULTICAST, "MULTICAST" },
44 { IFF_MULTI_BCAST, "MULTI_BCAST" },
45 { IFF_UNNUMBERED, "UNNUMBERED" },
46 { IFF_DHCPRUNNING, "DHCP" },
47 { IFF_PRIVATE, "PRIVATE" },
48 { IFF_NOXMIT, "NOXMIT" },
49 { IFF_NOLOCAL, "NOLOCAL" },
50 { IFF_DEPRECATED, "DEPRECATED" },
51 { IFF_ADDRCONF, "ADDRCONF" },
52 { IFF_ROUTER, "ROUTER" },
53 { IFF_NONUD, "NONUD" },
54 { IFF_ANYCAST, "ANYCAST" },
55 { IFF_NORTEXCH, "NORTEXCH" },
56 { IFF_IPV4, "IPv4" },
57 { IFF_IPV6, "IPv6" },
58 { IFF_NOFAILOVER, "NOFAILOVER" },
59 { IFF_FAILED, "FAILED" },
60 { IFF_STANDBY, "STANDBY" },
61 { IFF_INACTIVE, "INACTIVE" },
62 { IFF_OFFLINE, "OFFLINE" },
63 { IFF_XRESOLV, "XRESOLV" },
64 { IFF_COS_ENABLED, "CoS" },
65 { IFF_PREFERRED, "PREFERRED" },
66 { IFF_TEMPORARY, "TEMPORARY" },
67 { IFF_FIXEDMTU, "FIXEDMTU" },
68 { IFF_VIRTUAL, "VIRTUAL" },
69 { IFF_DUPLICATE, "DUPLICATE" }
72 static struct lifreq lifr;
73 /* current interface name a particular function is accessing */
74 static char name[LIFNAMSIZ];
75 /* foreach interface saved name */
76 static char origname[LIFNAMSIZ];
77 static char savedname[LIFNAMSIZ]; /* For addif */
78 static int setaddr;
81 * Make sure the algorithm variables hold more than the sizeof an algorithm
82 * in PF_KEY. (For now, more than a uint8_t.) The NO_***_?ALG indicates that
83 * there was no algorithm requested, and in the ipsec_req that service should
84 * be disabled. (E.g. if ah_aalg remains NO_AH_AALG, then AH will be
85 * disabled on that tunnel.)
87 #define NO_AH_AALG 256
88 #define NO_ESP_AALG 256
89 #define NO_ESP_EALG 256
92 * iface_t
93 * used by setifether to create a list of interfaces to mark
94 * down-up when changing the ethernet address of an interface
96 typedef struct iface {
97 struct lifreq lifr;
98 struct iface *next; /* pointer to the next list element */
99 } iface_t;
101 static iface_t *logifs = NULL; /* list of logical interfaces */
102 static iface_t *phyif = NULL; /* physical interface */
104 int s;
105 int af = AF_INET; /* default address family */
106 int debug = 0;
107 int all = 0; /* setifdhcp() needs to know this */
108 int verbose = 0;
109 int v4compat = 0; /* Compatible printing format */
112 * Function prototypes for command functions.
114 static int addif(char *arg, int64_t param);
115 static int inetplumb(char *arg, int64_t param);
116 static int inetunplumb(char *arg, int64_t param);
117 static int removeif(char *arg, int64_t param);
118 static int setdebugflag(char *arg, int64_t param);
119 static int setifaddr(char *arg, int64_t param);
120 static int setifbroadaddr(char *arg, int64_t param);
121 static int setifdstaddr(char *arg, int64_t param);
122 static int setifether(char *arg, int64_t param);
123 static int setifflags(char *arg, int64_t param);
124 static int setifindex(char *arg, int64_t param);
125 static int setifmetric(char *arg, int64_t param);
126 static int setifmtu(char *arg, int64_t param);
127 static int setifnetmask(char *arg, int64_t param);
128 static int setifprefixlen(char *arg, int64_t param);
129 static int setifrevarp(char *arg, int64_t param);
130 static int setifsubnet(char *arg, int64_t param);
131 static int setiftdst(char *arg, int64_t param);
132 static int setiftoken(char *arg, int64_t param);
133 static int setiftsrc(char *arg, int64_t param);
134 static int setverboseflag(char *arg, int64_t param);
135 static int set_tun_ah_alg(char *arg, int64_t param);
136 static int set_tun_esp_auth_alg(char *arg, int64_t param);
137 static int set_tun_esp_encr_alg(char *arg, int64_t param);
138 static int modlist(char *arg, int64_t param);
139 static int modinsert(char *arg, int64_t param);
140 static int modremove(char *arg, int64_t param);
141 static int setifgroupname(char *arg, int64_t param);
142 static int configinfo(char *arg, int64_t param);
143 static void print_config_flags(uint64_t flags);
144 static void print_flags(uint64_t flags);
145 static void print_ifether(char *ifname);
146 static int set_tun_encap_limit(char *arg, int64_t param);
147 static int clr_tun_encap_limit(char *arg, int64_t param);
148 static int set_tun_hop_limit(char *arg, int64_t param);
149 static int setzone(char *arg, int64_t param);
150 static int setallzones(char *arg, int64_t param);
151 static int setifsrc(char *arg, int64_t param);
154 * Address family specific function prototypes.
156 static void in_getaddr(char *s, struct sockaddr *saddr, int *plenp);
157 static void in_status(int force, uint64_t flags);
158 static void in_configinfo(int force, uint64_t flags);
159 static void in6_getaddr(char *s, struct sockaddr *saddr, int *plenp);
160 static void in6_status(int force, uint64_t flags);
161 static void in6_configinfo(int force, uint64_t flags);
164 * Misc support functions
166 static boolean_t ni_entry(const char *, void *);
167 static void foreachinterface(void (*func)(), int argc, char *argv[],
168 int af, int64_t onflags, int64_t offflags,
169 int64_t lifc_flags);
170 static void ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp);
171 static boolean_t in_getmask(struct sockaddr_in *saddr,
172 boolean_t addr_set);
173 static int in_getprefixlen(char *addr, boolean_t slash, int plen);
174 static boolean_t in_prefixlentomask(int prefixlen, int maxlen,
175 uchar_t *mask);
176 static int settaddr(char *, int (*)(icfg_handle_t,
177 const struct sockaddr *, socklen_t));
178 static void status(void);
179 static void ifstatus(const char *);
180 static void usage(void);
181 static int strioctl(int s, int cmd, char *buf, int buflen);
182 static int setifdhcp(const char *caller, const char *ifname,
183 int argc, char *argv[]);
184 static int ip_domux2fd(int *, int *, int *, int *, int *);
185 static int ip_plink(int, int, int, int, int);
186 static int modop(char *arg, char op);
187 static void selectifs(int argc, char *argv[], int af,
188 struct lifreq *lifrp);
189 static int updownifs(iface_t *ifs, int up);
190 static int find_all_global_interfaces(struct lifconf *lifcp, char **buf,
191 int64_t lifc_flags);
192 static int find_all_zone_interfaces(struct lifconf *lifcp, char **buf,
193 int64_t lifc_flags);
195 #define max(a, b) ((a) < (b) ? (b) : (a))
198 * DHCP_EXIT_IF_FAILURE indicates that the operation failed, but if there
199 * are more interfaces to act on (i.e., ifconfig was invoked with -a), keep
200 * on going rather than exit with an error.
203 #define DHCP_EXIT_IF_FAILURE -1
205 #define NEXTARG 0xffffff /* command takes an argument */
206 #define OPTARG 0xfffffe /* command takes an optional argument */
207 #define AF_ANY (-1)
209 /* Refer to the comments in ifconfig() on the netmask "hack" */
210 #define NETMASK_CMD "netmask"
211 struct sockaddr_storage g_netmask;
212 enum { G_NETMASK_NIL, G_NETMASK_PENDING, G_NETMASK_SET }
213 g_netmask_set = G_NETMASK_NIL;
215 struct cmd {
216 char *c_name;
217 int64_t c_parameter; /* NEXTARG means next argv */
218 int (*c_func)(char *, int64_t);
219 int c_abortonfail; /* don't continue parsing args */
220 /* for the current interface */
221 int c_af; /* address family restrictions */
222 } cmds[] = {
223 { "up", IFF_UP, setifflags, 0, AF_ANY },
224 { "down", -IFF_UP, setifflags, 0, AF_ANY },
225 { "trailers", -IFF_NOTRAILERS, setifflags, 0, AF_ANY },
226 { "-trailers", IFF_NOTRAILERS, setifflags, 0, AF_ANY },
227 { "arp", -IFF_NOARP, setifflags, 0, AF_INET },
228 { "-arp", IFF_NOARP, setifflags, 0, AF_INET },
229 { "router", IFF_ROUTER, setifflags, 0, AF_ANY },
230 { "-router", -IFF_ROUTER, setifflags, 0, AF_ANY },
231 { "private", IFF_PRIVATE, setifflags, 0, AF_ANY },
232 { "-private", -IFF_PRIVATE, setifflags, 0, AF_ANY },
233 { "xmit", -IFF_NOXMIT, setifflags, 0, AF_ANY },
234 { "-xmit", IFF_NOXMIT, setifflags, 0, AF_ANY },
235 { "-nud", IFF_NONUD, setifflags, 0, AF_INET6 },
236 { "nud", -IFF_NONUD, setifflags, 0, AF_INET6 },
237 { "anycast", IFF_ANYCAST, setifflags, 0, AF_ANY },
238 { "-anycast", -IFF_ANYCAST, setifflags, 0, AF_ANY },
239 { "local", -IFF_NOLOCAL, setifflags, 0, AF_ANY },
240 { "-local", IFF_NOLOCAL, setifflags, 0, AF_ANY },
241 { "deprecated", IFF_DEPRECATED, setifflags, 0, AF_ANY },
242 { "-deprecated", -IFF_DEPRECATED, setifflags, 0, AF_ANY },
243 { "preferred", IFF_PREFERRED, setifflags, 0, AF_INET6 },
244 { "-preferred", -IFF_PREFERRED, setifflags, 0, AF_INET6 },
245 { "debug", 0, setdebugflag, 0, AF_ANY },
246 { "verbose", 0, setverboseflag, 0, AF_ANY },
247 { NETMASK_CMD, NEXTARG, setifnetmask, 0, AF_INET },
248 { "metric", NEXTARG, setifmetric, 0, AF_ANY },
249 { "mtu", NEXTARG, setifmtu, 0, AF_ANY },
250 { "index", NEXTARG, setifindex, 0, AF_ANY },
251 { "broadcast", NEXTARG, setifbroadaddr, 0, AF_INET },
252 { "auto-revarp", 0, setifrevarp, 1, AF_INET },
253 { "plumb", 0, inetplumb, 1, AF_ANY },
254 { "unplumb", 0, inetunplumb, 0, AF_ANY },
255 { "subnet", NEXTARG, setifsubnet, 0, AF_ANY },
256 { "token", NEXTARG, setiftoken, 0, AF_INET6 },
257 { "tsrc", NEXTARG, setiftsrc, 0, AF_ANY },
258 { "tdst", NEXTARG, setiftdst, 0, AF_ANY },
259 { "encr_auth_algs", NEXTARG, set_tun_esp_auth_alg, 0, AF_ANY },
260 { "encr_algs", NEXTARG, set_tun_esp_encr_alg, 0, AF_ANY },
261 { "auth_algs", NEXTARG, set_tun_ah_alg, 0, AF_ANY },
262 { "addif", NEXTARG, addif, 1, AF_ANY },
263 { "removeif", NEXTARG, removeif, 1, AF_ANY },
264 { "modlist", 0, modlist, 1, AF_ANY },
265 { "modinsert", NEXTARG, modinsert, 1, AF_ANY },
266 { "modremove", NEXTARG, modremove, 1, AF_ANY },
267 { "failover", -IFF_NOFAILOVER, setifflags, 1, AF_ANY },
268 { "-failover", IFF_NOFAILOVER, setifflags, 1, AF_ANY },
269 { "standby", IFF_STANDBY, setifflags, 1, AF_ANY },
270 { "-standby", -IFF_STANDBY, setifflags, 1, AF_ANY },
271 { "failed", IFF_FAILED, setifflags, 1, AF_ANY },
272 { "-failed", -IFF_FAILED, setifflags, 1, AF_ANY },
273 { "group", NEXTARG, setifgroupname, 1, AF_ANY },
274 { "configinfo", 0, configinfo, 1, AF_ANY },
275 { "encaplimit", NEXTARG, set_tun_encap_limit, 0, AF_ANY },
276 { "-encaplimit", 0, clr_tun_encap_limit, 0, AF_ANY },
277 { "thoplimit", NEXTARG, set_tun_hop_limit, 0, AF_ANY },
278 { "set", NEXTARG, setifaddr, 0, AF_ANY },
279 { "destination", NEXTARG, setifdstaddr, 0, AF_ANY },
280 { "zone", NEXTARG, setzone, 0, AF_ANY },
281 { "-zone", 0, setzone, 0, AF_ANY },
282 { "all-zones", 0, setallzones, 0, AF_ANY },
283 { "ether", OPTARG, setifether, 0, AF_ANY },
284 { "usesrc", NEXTARG, setifsrc, 0, AF_ANY },
287 * NOTE: any additions to this table must also be applied to ifparse
288 * (usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c)
291 { 0, 0, setifaddr, 0, AF_ANY },
292 { 0, 0, setifdstaddr, 0, AF_ANY },
293 { 0, 0, 0, 0, 0 },
297 typedef struct if_config_cmd {
298 uint64_t iff_flag;
299 char *iff_name;
300 } if_config_cmd_t;
302 static if_config_cmd_t if_config_cmd_tbl[] = {
303 { IFF_UP, "up" },
304 { IFF_NOTRAILERS, "-trailers" },
305 { IFF_PRIVATE, "private" },
306 { IFF_NOXMIT, "-xmit" },
307 { IFF_ANYCAST, "anycast" },
308 { IFF_NOLOCAL, "-local" },
309 { IFF_DEPRECATED, "deprecated" },
310 { IFF_NOFAILOVER, "-failover" },
311 { IFF_STANDBY, "standby" },
312 { IFF_FAILED, "failed" },
313 { IFF_PREFERRED, "preferred" },
314 { 0, 0 },
317 typedef struct ni {
318 char ni_name[LIFNAMSIZ];
319 struct ni *ni_next;
320 } ni_t;
322 static ni_t *ni_list = NULL;
323 static int num_ni = 0;
325 /* End defines and structure definitions for ifconfig -a plumb */
327 /* Known address families */
328 struct afswtch {
329 char *af_name;
330 short af_af;
331 void (*af_status)();
332 void (*af_getaddr)();
333 void (*af_configinfo)();
334 } afs[] = {
335 { "inet", AF_INET, in_status, in_getaddr, in_configinfo },
336 { "inet6", AF_INET6, in6_status, in6_getaddr, in6_configinfo },
337 { 0, 0, 0, 0, 0 }
340 #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af))
342 struct afswtch *afp; /* the address family being set or asked about */
345 main(int argc, char *argv[])
347 /* Include IFF_NOXMIT, IFF_TEMPORARY and all zone interfaces */
348 int64_t lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
349 char *default_ip_str;
351 if (argc < 2) {
352 usage();
353 exit(1);
355 argc--, argv++;
356 if (strlen(*argv) > sizeof (name) - 1) {
357 (void) fprintf(stderr, "%s: interface name too long\n", *argv);
358 exit(1);
360 (void) strncpy(name, *argv, sizeof (name));
361 name[sizeof (name) - 1] = '\0';
362 (void) strncpy(origname, name, sizeof (origname)); /* For addif */
363 default_ip_str = NULL;
364 v4compat = get_compat_flag(&default_ip_str);
365 if (v4compat == DEFAULT_PROT_BAD_VALUE) {
366 (void) fprintf(stderr,
367 "ifconfig: %s: Bad value for %s in %s\n", default_ip_str,
368 DEFAULT_IP, INET_DEFAULT_FILE);
369 free(default_ip_str);
370 exit(2);
372 free(default_ip_str);
373 argc--, argv++;
374 if (argc > 0) {
375 struct afswtch *myafp;
377 for (myafp = afp = afs; myafp->af_name; myafp++) {
378 if (strcmp(myafp->af_name, *argv) == 0) {
379 afp = myafp; argc--; argv++;
380 break;
383 af = lifr.lifr_addr.ss_family = afp->af_af;
384 if (af == AF_INET6) {
385 v4compat = 0;
389 s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
390 if (s < 0) {
391 Perror0_exit("socket");
395 * Special interface names is any combination of these flags.
396 * Note that due to the ifconfig syntax they have to be combined
397 * as a single '-' option.
398 * -a All interfaces
399 * -u "up" interfaces
400 * -d "down" interfaces
401 * -D Interfaces not controlled by DHCP
402 * -4 IPv4 interfaces
403 * -6 IPv6 interfaces
404 * -X Turn on debug (not documented)
405 * -v Turn on verbose
406 * -Z Only interfaces in caller's zone
409 if (name[0] == '-') {
410 /* One or more options */
411 int64_t onflags = 0;
412 int64_t offflags = 0;
413 int c;
414 char *av[2] = { "ifconfig", name };
416 while ((c = getopt(2, av, "audDXZ46v")) != -1) {
417 switch ((char)c) {
418 case 'a':
419 all = 1;
420 break;
421 case 'u':
422 onflags |= IFF_UP;
423 break;
424 case 'd':
425 offflags |= IFF_UP;
426 break;
427 case 'D':
428 offflags |= IFF_DHCPRUNNING;
429 break;
430 case 'X':
431 debug += 3;
432 break;
433 case 'Z':
434 lifc_flags &= ~LIFC_ALLZONES;
435 break;
436 case '4':
438 * -4 is not a compatable flag, therefore
439 * we assume they want v4compat turned off
441 v4compat = 0;
442 onflags |= IFF_IPV4;
443 break;
444 case '6':
446 * If they want IPv6, well then we'll assume
447 * they don't want IPv4 compat
449 v4compat = 0;
450 onflags |= IFF_IPV6;
451 break;
452 case 'v':
453 verbose = 1;
454 break;
455 case '?':
456 usage();
457 exit(1);
460 if (!all) {
461 (void) fprintf(stderr,
462 "ifconfig: %s: no such interface\n", name);
463 exit(1);
465 foreachinterface(ifconfig, argc, argv, af, onflags, offflags,
466 lifc_flags);
467 } else {
468 ifconfig(argc, argv, af, (struct lifreq *)NULL);
470 return (0);
474 * For each interface, call (*func)(argc, argv, af, lifrp).
475 * Only call function if onflags and offflags are set or clear, respectively,
476 * in the interfaces flags field.
478 static void
479 foreachinterface(void (*func)(), int argc, char *argv[], int af,
480 int64_t onflags, int64_t offflags, int64_t lifc_flags)
482 int n;
483 char *buf;
484 struct lifnum lifn;
485 struct lifconf lifc;
486 struct lifreq *lifrp;
487 struct lifreq lifrl; /* Local lifreq struct */
488 int numifs;
489 unsigned bufsize;
490 int plumball = 0;
491 int save_af = af;
493 buf = NULL;
495 * Special case:
496 * ifconfig -a plumb should find all network interfaces
497 * in the machine for the global zone.
498 * For non-global zones, only find the assigned interfaces.
499 * Also, there is no need to SIOCGLIF* ioctls, since
500 * those interfaces have already been plumbed
502 if (argc > 0 && (strcmp(*argv, "plumb") == 0)) {
503 if (getzoneid() == GLOBAL_ZONEID) {
504 if (find_all_global_interfaces(&lifc, &buf,
505 lifc_flags) != 0)
506 return;
507 } else {
508 if (find_all_zone_interfaces(&lifc, &buf,
509 lifc_flags) != 0)
510 return;
512 if (lifc.lifc_len == 0)
513 return;
514 plumball = 1;
515 } else {
516 lifn.lifn_family = AF_UNSPEC;
517 lifn.lifn_flags = lifc_flags;
518 if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
519 Perror0_exit("Could not determine number"
520 " of interfaces");
522 numifs = lifn.lifn_count;
523 if (debug)
524 (void) printf("ifconfig: %d interfaces\n", numifs);
526 bufsize = numifs * sizeof (struct lifreq);
527 if ((buf = malloc(bufsize)) == NULL) {
528 Perror0("out of memory\n");
529 (void) close(s);
530 return;
533 lifc.lifc_family = AF_UNSPEC;
534 lifc.lifc_flags = lifc_flags;
535 lifc.lifc_len = bufsize;
536 lifc.lifc_buf = buf;
538 if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
539 Perror0("SIOCGLIFCONF");
540 (void) close(s);
541 free(buf);
542 return;
546 lifrp = lifc.lifc_req;
547 for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
549 if (!plumball) {
551 * We must close and recreate the socket each time
552 * since we don't know what type of socket it is now
553 * (each status function may change it).
556 (void) close(s);
558 af = lifrp->lifr_addr.ss_family;
559 s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
560 if (s == -1) {
562 * Perror0() assumes the name to be in the
563 * globally defined lifreq structure.
565 (void) strncpy(lifr.lifr_name,
566 lifrp->lifr_name, sizeof (lifr.lifr_name));
567 Perror0_exit("socket");
572 * Only service interfaces that match the on and off
573 * flags masks.
575 if (onflags || offflags) {
576 (void) memset(&lifrl, 0, sizeof (lifrl));
577 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
578 sizeof (lifrl.lifr_name));
579 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) {
581 * Perror0() assumes the name to be in the
582 * globally defined lifreq structure.
584 (void) strncpy(lifr.lifr_name,
585 lifrp->lifr_name, sizeof (lifr.lifr_name));
586 Perror0_exit("foreachinterface: SIOCGLIFFLAGS");
588 if ((lifrl.lifr_flags & onflags) != onflags)
589 continue;
590 if ((~lifrl.lifr_flags & offflags) != offflags)
591 continue;
594 if (!plumball) {
595 (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
596 sizeof (lifrl.lifr_name));
597 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifrl) < 0) {
599 * Perror0() assumes the name to be in the
600 * globally defined lifreq structure.
602 (void) strncpy(lifr.lifr_name,
603 lifrp->lifr_name, sizeof (lifr.lifr_name));
604 Perror0("foreachinterface: SIOCGLIFADDR");
605 continue;
607 if (lifrl.lifr_addr.ss_family != af) {
608 /* Switch address family */
609 af = lifrl.lifr_addr.ss_family;
610 (void) close(s);
612 s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
613 if (s == -1) {
615 * Perror0() assumes the name to be in
616 * the globally defined lifreq
617 * structure.
619 (void) strncpy(lifr.lifr_name,
620 lifrp->lifr_name,
621 sizeof (lifr.lifr_name));
622 Perror0_exit("socket");
628 * Reset global state
629 * setaddr: Used by parser to tear apart source and dest
630 * name and origname contain the name of the 'current'
631 * interface.
633 setaddr = 0;
634 (void) strncpy(name, lifrp->lifr_name, sizeof (name));
635 (void) strncpy(origname, name, sizeof (origname));
637 (*func)(argc, argv, save_af, lifrp);
638 /* the func could have overwritten origname, so restore */
639 (void) strncpy(name, origname, sizeof (name));
641 if (buf != NULL)
642 free(buf);
645 static void
646 tun_reality_check(void)
648 struct iftun_req treq;
649 ipsec_req_t *ipsr;
651 (void) strncpy(treq.ifta_lifr_name, name, sizeof (treq.ifta_lifr_name));
652 if (strchr(name, ':') != NULL) {
653 /* Return, we don't need to check. */
654 return;
656 if (ioctl(s, SIOCGTUNPARAM, (caddr_t)&treq) < 0 ||
657 !(treq.ifta_flags & IFTUN_SECURITY) ||
658 (treq.ifta_flags & IFTUN_COMPLEX_SECURITY)) {
660 * Either not a tunnel (the SIOCGTUNPARAM fails on
661 * non-tunnels), the security flag is not set, or
662 * this is a tunnel with ipsecconf(1M)-set policy.
663 * Regardless, return.
665 return;
668 ipsr = (ipsec_req_t *)&treq.ifta_secinfo;
670 if (ipsr->ipsr_esp_req != 0 &&
671 ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE &&
672 ipsr->ipsr_ah_req == 0)
673 (void) fprintf(stderr, "ifconfig: WARNING - tunnel with "
674 "only ESP and no authentication.\n");
678 * for the specified interface call (*func)(argc, argv, af, lifrp).
681 static void
682 ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp)
684 static boolean_t scan_netmask = _B_FALSE;
685 int ret;
687 if (argc == 0) {
688 status();
689 return;
692 if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
694 * Some errors are ignored in the case where more than one
695 * interface is being operated on.
697 ret = setifdhcp("ifconfig", name, argc, argv);
698 if (ret == DHCP_EXIT_IF_FAILURE) {
699 if (!all)
700 exit(DHCP_EXIT_FAILURE);
701 } else if (ret != DHCP_EXIT_SUCCESS) {
702 exit(ret);
704 return;
708 * The following is a "hack" to get around the existing interface
709 * setting mechanism. Currently, each interface attribute,
710 * such as address, netmask, broadcast, ... is set separately. But
711 * sometimes two or more attributes must be set together. For
712 * example, setting an address without a netmask does not make sense.
713 * Yet they can be set separately for IPv4 address using the current
714 * ifconfig(1M) syntax. The kernel then "infers" the correct netmask
715 * using the deprecated "IP address classes." This is simply not
716 * correct.
718 * The "hack" below is to go thru the whole command list looking for
719 * the netmask command first. Then use this netmask to set the
720 * address. This does not provide an extensible way to accommodate
721 * future need for setting more than one attributes together.
723 * Note that if the "netmask" command argument is a "+", we need
724 * to save this info and do the query after we know the address to
725 * be set. The reason is that if "addif" is used, the working
726 * interface name will be changed later when the logical interface
727 * is created. In in_getmask(), if an address is not provided,
728 * it will use the working interface's address to do the query.
729 * It will be wrong now as we don't know the logical interface's name.
731 * ifconfig(1M) is too overloaded and the code is so convoluted
732 * that it is "safer" not to re-architect the code to fix the above
733 * issue, hence this "hack." We may be better off to have a new
734 * command with better syntax for configuring network interface
735 * parameters...
737 if (!scan_netmask && afp->af_af == AF_INET) {
738 int largc;
739 char **largv;
741 /* Only go thru the command list once to find the netmask. */
742 scan_netmask = _B_TRUE;
745 * Currently, if multiple netmask commands are specified, the
746 * last one will be used as the final netmask. So we need
747 * to scan the whole list to preserve this behavior.
749 for (largc = argc, largv = argv; largc > 0; largc--, largv++) {
750 if (strcmp(*largv, NETMASK_CMD) == 0) {
751 if (--largc == 0)
752 break;
753 largv++;
754 if (strcmp(*largv, "+") == 0) {
755 g_netmask_set = G_NETMASK_PENDING;
756 } else {
757 in_getaddr(*largv, (struct sockaddr *)
758 &g_netmask, NULL);
759 g_netmask_set = G_NETMASK_SET;
761 /* Continue the scan. */
766 while (argc > 0) {
767 struct cmd *p;
768 boolean_t found_cmd;
770 if (debug)
771 (void) printf("ifconfig: argv %s\n", *argv);
773 found_cmd = _B_FALSE;
774 for (p = cmds; p->c_func; p++) {
775 if (p->c_name) {
776 if (strcmp(*argv, p->c_name) == 0) {
778 * indicate that the command was
779 * found and check to see if
780 * the address family is valid
782 found_cmd = _B_TRUE;
783 if (p->c_af == AF_ANY ||
784 af == p->c_af)
785 break;
787 } else {
788 if (p->c_af == AF_ANY ||
789 af == p->c_af)
790 break;
794 * If we found the keyword, but the address family
795 * did not match spit out an error
797 if (found_cmd && p->c_name == 0) {
798 (void) fprintf(stderr, "ifconfig: Operation %s not"
799 " supported for %s\n", *argv, afp->af_name);
800 exit(1);
803 * else (no keyword found), we assume it's an address
804 * of some sort
806 if (p->c_name == 0 && setaddr)
807 p++; /* got src, do dst */
808 if (p->c_func) {
809 if (p->c_af == AF_INET6) {
810 v4compat = 0;
812 if (p->c_parameter == NEXTARG ||
813 p->c_parameter == OPTARG) {
814 argc--, argv++;
815 if (argc == 0 && p->c_parameter == NEXTARG) {
816 (void) fprintf(stderr,
817 "ifconfig: no argument for %s\n",
818 p->c_name);
819 exit(1);
823 * Call the function if:
825 * there's no address family
826 * restriction
827 * OR
828 * we don't know the address yet
829 * (because we were called from
830 * main)
831 * OR
832 * there is a restriction AND
833 * the address families match
835 if ((p->c_af == AF_ANY) ||
836 (lifrp == (struct lifreq *)NULL) ||
837 (lifrp->lifr_addr.ss_family == p->c_af)) {
838 ret = (*p->c_func)(*argv, p->c_parameter);
840 * If c_func failed and we should
841 * abort processing for this
842 * interface on failure, return
843 * now rather than going on to
844 * process other commands for
845 * the same interface.
847 if (ret != 0 && p->c_abortonfail)
848 return;
851 argc--, argv++;
854 /* Check to see if there's a security hole in the tunnel setup. */
855 tun_reality_check();
858 /* ARGSUSED */
859 static int
860 setdebugflag(char *val, int64_t arg)
862 debug++;
863 return (0);
866 /* ARGSUSED */
867 static int
868 setverboseflag(char *val, int64_t arg)
870 verbose++;
871 return (0);
875 * This function fills in the given lifreq's lifr_addr field based on
876 * g_netmask_set.
878 static void
879 set_mask_lifreq(struct lifreq *lifr, struct sockaddr_storage *addr,
880 struct sockaddr_storage *mask)
882 assert(addr != NULL);
883 assert(mask != NULL);
885 switch (g_netmask_set) {
886 case G_NETMASK_SET:
887 lifr->lifr_addr = g_netmask;
888 break;
890 case G_NETMASK_PENDING:
892 * "+" is used as the argument to "netmask" command. Query
893 * the database on the correct netmask based on the address to
894 * be set.
896 assert(afp->af_af == AF_INET);
897 g_netmask = *addr;
898 if (!in_getmask((struct sockaddr_in *)&g_netmask, _B_TRUE)) {
899 lifr->lifr_addr = *mask;
900 g_netmask_set = G_NETMASK_NIL;
901 } else {
902 lifr->lifr_addr = g_netmask;
903 g_netmask_set = G_NETMASK_SET;
905 break;
907 case G_NETMASK_NIL:
908 default:
909 lifr->lifr_addr = *mask;
910 break;
915 * Set the interface address. Handles <addr>, <addr>/<n> as well as /<n>
916 * syntax for setting the address, the address plus netmask, and just
917 * the netmask respectively.
919 /* ARGSUSED */
920 static int
921 setifaddr(char *addr, int64_t param)
923 int prefixlen = 0;
924 struct sockaddr_storage laddr;
925 struct sockaddr_storage netmask;
926 struct sockaddr_in6 *sin6;
927 struct sockaddr_in *sin;
928 struct sockaddr_storage sav_netmask;
930 if (addr[0] == '/')
931 return (setifprefixlen(addr, 0));
933 (*afp->af_getaddr)(addr, (struct sockaddr *)&laddr, &prefixlen);
935 (void) memset(&netmask, 0, sizeof (netmask));
936 netmask.ss_family = afp->af_af;
937 switch (prefixlen) {
938 case NO_PREFIX:
939 /* Nothing there - ok */
940 break;
941 case BAD_ADDR:
942 (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n",
943 addr);
944 exit(1);
945 default:
946 if (afp->af_af == AF_INET6) {
947 sin6 = (struct sockaddr_in6 *)&netmask;
948 if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
949 (uchar_t *)&sin6->sin6_addr)) {
950 (void) fprintf(stderr, "ifconfig: "
951 "Bad prefix length: %d\n",
952 prefixlen);
953 exit(1);
955 } else {
956 sin = (struct sockaddr_in *)&netmask;
957 if (!in_prefixlentomask(prefixlen, IP_ABITS,
958 (uchar_t *)&sin->sin_addr)) {
959 (void) fprintf(stderr, "ifconfig: "
960 "Bad prefix length: %d\n",
961 prefixlen);
962 exit(1);
966 * Just in case of funny setting of both prefix and netmask,
967 * prefix should override the netmask command.
969 g_netmask_set = G_NETMASK_NIL;
970 break;
972 /* Tell parser that an address was set */
973 setaddr++;
974 /* save copy of netmask to restore in case of error */
975 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
976 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0)
977 Perror0_exit("SIOCGLIFNETMASK");
978 sav_netmask = lifr.lifr_addr;
981 * If setting the address and not the mask, clear any existing mask
982 * and the kernel will then assign the default (netmask has been set
983 * to 0 in this case). If setting both (either by using a prefix or
984 * using the netmask command), set the mask first, so the address will
985 * be interpreted correctly.
987 set_mask_lifreq(&lifr, &laddr, &netmask);
988 if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
989 Perror0_exit("SIOCSLIFNETMASK");
991 if (debug) {
992 char abuf[INET6_ADDRSTRLEN];
993 void *addr = (afp->af_af == AF_INET) ?
994 (void *)&((struct sockaddr_in *)&laddr)->sin_addr :
995 (void *)&((struct sockaddr_in6 *)&laddr)->sin6_addr;
997 (void) printf("Setting %s af %d addr %s\n",
998 lifr.lifr_name, afp->af_af,
999 inet_ntop(afp->af_af, addr, abuf, sizeof (abuf)));
1001 lifr.lifr_addr = laddr;
1002 lifr.lifr_addr.ss_family = afp->af_af;
1003 if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
1005 * Restore the netmask
1007 int saverr = errno;
1009 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1010 lifr.lifr_addr = sav_netmask;
1011 (void) ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr);
1012 errno = saverr;
1013 Perror0_exit("SIOCSLIFADDR");
1016 return (0);
1020 * The following functions are stolen from the ipseckey(1m) program.
1021 * Perhaps they should be somewhere common, but for now, we just maintain
1022 * two versions. We do this because of the different semantics for which
1023 * algorithms we select ("requested" for ifconfig vs. "actual" for key).
1026 static ulong_t
1027 parsenum(char *num)
1029 ulong_t rc;
1030 char *end = NULL;
1032 errno = 0;
1033 rc = strtoul(num, &end, 0);
1034 if (errno != 0 || end == num || *end != '\0') {
1035 rc = (ulong_t)-1;
1038 return (rc);
1042 * Parse and reverse parse possible algorithm values, include numbers.
1043 * Mostly stolen from ipseckey.c. See the comments above parsenum() for why
1044 * this isn't common to ipseckey.c.
1046 * NOTE: Static buffer in this function for the return value. Since ifconfig
1047 * isn't multithreaded, this isn't a huge problem.
1050 #define NBUF_SIZE 20 /* Enough to print a large integer. */
1052 static char *
1053 rparsealg(uint8_t alg_value, int proto_num)
1055 struct ipsecalgent *alg;
1056 static char numprint[128]; /* Enough to hold an algorithm name. */
1059 * Special cases for "any" and "none"
1060 * The kernel needs to be able to distinguish between "any"
1061 * and "none" and the APIs are underdefined in this area for auth.
1063 if (proto_num == IPSEC_PROTO_AH) {
1064 if (alg_value == SADB_AALG_NONE)
1065 return ("none");
1066 if (alg_value == SADB_AALG_ANY)
1067 return ("any");
1070 alg = getipsecalgbynum(alg_value, proto_num, NULL);
1071 if (alg != NULL) {
1072 (void) strlcpy(numprint, alg->a_names[0], sizeof (numprint));
1073 freeipsecalgent(alg);
1074 } else {
1075 (void) snprintf(numprint, sizeof (numprint), "%d", alg_value);
1078 return (numprint);
1081 static uint_t
1082 parsealg(char *algname, int proto_num)
1084 struct ipsecalgent *alg;
1085 ulong_t invalue;
1087 if (algname == NULL) {
1088 (void) fprintf(stderr, "ifconfig: Unexpected end of command "
1089 "line.\n");
1090 exit(1);
1094 * Special-case "none" and "any".
1095 * Use strcasecmp because its length is bounded.
1097 if (strcasecmp("none", algname) == 0) {
1098 return ((proto_num == IPSEC_PROTO_ESP) ?
1099 NO_ESP_EALG : NO_ESP_AALG);
1101 if ((strcasecmp("any", algname) == 0) && (proto_num == IPSEC_PROTO_AH))
1102 return (SADB_AALG_ANY);
1104 alg = getipsecalgbyname(algname, proto_num, NULL);
1105 if (alg != NULL) {
1106 invalue = alg->a_alg_num;
1107 freeipsecalgent(alg);
1108 return ((uint_t)invalue);
1112 * Since algorithms can be loaded during kernel run-time, check for
1113 * numeric algorithm values too.
1115 invalue = parsenum(algname);
1116 if ((invalue & (ulong_t)0xff) == invalue)
1117 return ((uint_t)invalue);
1119 (void) fprintf(stderr, "ifconfig: %s algorithm type %s unknown.\n",
1120 (proto_num == IPSEC_PROTO_ESP) ?
1121 "Encryption" : "Authentication", algname);
1122 exit(1);
1123 /* NOTREACHED */
1127 * Actual ifconfig functions to set tunnel security properties.
1130 enum ipsec_alg_type { ESP_ENCR_ALG = 1, ESP_AUTH_ALG, AH_AUTH_ALG };
1132 boolean_t first_set_tun = _B_TRUE;
1133 boolean_t encr_alg_set = _B_FALSE;
1136 * Need global for multiple calls to set_tun_algs
1137 * because we accumulate algorithm selections over
1138 * the lifetime of this ifconfig(1M) invocation.
1140 static struct iftun_req treq_tun;
1142 static int
1143 set_tun_algs(int which_alg, int alg)
1145 ipsec_req_t *ipsr;
1147 (void) strncpy(treq_tun.ifta_lifr_name, name,
1148 sizeof (treq_tun.ifta_lifr_name));
1149 if (strchr(name, ':') != NULL) {
1150 errno = EPERM;
1151 Perror0_exit("Tunnel params on logical interfaces");
1153 if (ioctl(s, SIOCGTUNPARAM, (caddr_t)&treq_tun) < 0) {
1154 if (errno == EOPNOTSUPP || errno == EINVAL)
1155 Perror0_exit("Not a tunnel");
1156 else Perror0_exit("SIOCGTUNPARAM");
1159 ipsr = (ipsec_req_t *)&treq_tun.ifta_secinfo;
1161 if (treq_tun.ifta_vers != IFTUN_VERSION) {
1162 (void) fprintf(stderr,
1163 "Kernel tunnel secinfo version mismatch.\n");
1164 exit(1);
1168 * If I'm just starting off this ifconfig, I want a clean slate,
1169 * otherwise, I've captured the current tunnel security settings.
1170 * In the case of continuation, I merely add to the settings.
1172 if (first_set_tun) {
1173 first_set_tun = _B_FALSE;
1174 (void) memset(ipsr, 0, sizeof (*ipsr));
1177 treq_tun.ifta_flags = IFTUN_SECURITY;
1179 switch (which_alg) {
1180 case ESP_ENCR_ALG:
1181 if (alg == NO_ESP_EALG) {
1182 if (ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE)
1183 ipsr->ipsr_esp_req = 0;
1184 ipsr->ipsr_esp_alg = SADB_EALG_NONE;
1186 /* Let the user specify NULL encryption implicitly. */
1187 if (ipsr->ipsr_esp_auth_alg != SADB_AALG_NONE) {
1188 encr_alg_set = _B_TRUE;
1189 ipsr->ipsr_esp_alg = SADB_EALG_NULL;
1191 } else {
1192 encr_alg_set = _B_TRUE;
1193 ipsr->ipsr_esp_req =
1194 IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1195 ipsr->ipsr_esp_alg = alg;
1197 break;
1198 case ESP_AUTH_ALG:
1199 if (alg == NO_ESP_AALG) {
1200 if ((ipsr->ipsr_esp_alg == SADB_EALG_NONE ||
1201 ipsr->ipsr_esp_alg == SADB_EALG_NULL) &&
1202 !encr_alg_set)
1203 ipsr->ipsr_esp_req = 0;
1204 ipsr->ipsr_esp_auth_alg = SADB_AALG_NONE;
1205 } else {
1206 ipsr->ipsr_esp_req =
1207 IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1208 ipsr->ipsr_esp_auth_alg = alg;
1210 /* Let the user specify NULL encryption implicitly. */
1211 if (ipsr->ipsr_esp_alg == SADB_EALG_NONE &&
1212 !encr_alg_set)
1213 ipsr->ipsr_esp_alg = SADB_EALG_NULL;
1215 break;
1216 case AH_AUTH_ALG:
1217 if (alg == NO_AH_AALG) {
1218 ipsr->ipsr_ah_req = 0;
1219 ipsr->ipsr_auth_alg = SADB_AALG_NONE;
1220 } else {
1221 ipsr->ipsr_ah_req =
1222 IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1223 ipsr->ipsr_auth_alg = alg;
1225 break;
1226 /* Will never hit DEFAULT */
1229 if (ioctl(s, SIOCSTUNPARAM, (caddr_t)&treq_tun) < 0) {
1230 Perror2_exit("set tunnel security properties",
1231 treq_tun.ifta_lifr_name);
1234 return (0);
1237 /* ARGSUSED */
1238 static int
1239 set_tun_esp_encr_alg(char *addr, int64_t param)
1241 return (set_tun_algs(ESP_ENCR_ALG,
1242 parsealg(addr, IPSEC_PROTO_ESP)));
1245 /* ARGSUSED */
1246 static int
1247 set_tun_esp_auth_alg(char *addr, int64_t param)
1249 return (set_tun_algs(ESP_AUTH_ALG,
1250 parsealg(addr, IPSEC_PROTO_AH)));
1253 /* ARGSUSED */
1254 static int
1255 set_tun_ah_alg(char *addr, int64_t param)
1257 return (set_tun_algs(AH_AUTH_ALG,
1258 parsealg(addr, IPSEC_PROTO_AH)));
1261 /* ARGSUSED */
1262 static int
1263 setifrevarp(char *arg, int64_t param)
1265 struct sockaddr_in laddr;
1267 if (afp->af_af == AF_INET6) {
1268 (void) fprintf(stderr,
1269 "ifconfig: revarp not possible on IPv6 interface %s\n",
1270 name);
1271 exit(1);
1273 if (doifrevarp(name, &laddr)) {
1274 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1275 laddr.sin_family = AF_INET;
1276 (void) memcpy(&lifr.lifr_addr, &laddr, sizeof (laddr));
1277 if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
1278 Perror0_exit("SIOCSLIFADDR");
1280 return (0);
1283 /* ARGSUSED */
1284 static int
1285 setifsubnet(char *addr, int64_t param)
1287 int prefixlen = 0;
1288 struct sockaddr_storage subnet;
1290 (*afp->af_getaddr)(addr, &subnet, &prefixlen);
1292 switch (prefixlen) {
1293 case NO_PREFIX:
1294 (void) fprintf(stderr,
1295 "ifconfig: Missing prefix length in subnet %s\n", addr);
1296 exit(1);
1297 /* NOTREACHED */
1298 case BAD_ADDR:
1299 (void) fprintf(stderr,
1300 "ifconfig: Bad prefix length in %s\n", addr);
1301 exit(1);
1302 default:
1303 break;
1306 lifr.lifr_addr = subnet;
1307 lifr.lifr_addrlen = prefixlen;
1308 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1309 if (ioctl(s, SIOCSLIFSUBNET, (caddr_t)&lifr) < 0)
1310 Perror0_exit("SIOCSLIFSUBNET");
1312 return (0);
1315 /* ARGSUSED */
1316 static int
1317 setifnetmask(char *addr, int64_t param)
1319 struct sockaddr_in netmask;
1321 assert(afp->af_af != AF_INET6);
1323 if (strcmp(addr, "+") == 0) {
1324 if (!in_getmask(&netmask, _B_FALSE))
1325 return (0);
1326 (void) printf("Setting netmask of %s to %s\n", name,
1327 inet_ntoa(netmask.sin_addr));
1328 } else {
1329 in_getaddr(addr, (struct sockaddr *)&netmask, NULL);
1331 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1332 (void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask));
1333 if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1334 Perror0_exit("SIOCSLIFNETMASK");
1335 return (0);
1339 * Parse '/<n>' as a netmask.
1341 /* ARGSUSED */
1342 static int
1343 setifprefixlen(char *addr, int64_t param)
1345 int prefixlen;
1346 int af = afp->af_af;
1348 prefixlen = in_getprefixlen(addr, _B_TRUE,
1349 (af == AF_INET) ? IP_ABITS : IPV6_ABITS);
1350 if (prefixlen < 0) {
1351 (void) fprintf(stderr,
1352 "ifconfig: Bad prefix length in %s\n", addr);
1353 exit(1);
1355 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
1356 lifr.lifr_addr.ss_family = af;
1357 if (af == AF_INET6) {
1358 struct sockaddr_in6 *sin6;
1360 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
1361 if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
1362 (uchar_t *)&sin6->sin6_addr)) {
1363 (void) fprintf(stderr, "ifconfig: "
1364 "Bad prefix length: %d\n",
1365 prefixlen);
1366 exit(1);
1368 } else if (af == AF_INET) {
1369 struct sockaddr_in *sin;
1371 sin = (struct sockaddr_in *)&lifr.lifr_addr;
1372 if (!in_prefixlentomask(prefixlen, IP_ABITS,
1373 (uchar_t *)&sin->sin_addr)) {
1374 (void) fprintf(stderr, "ifconfig: "
1375 "Bad prefix length: %d\n",
1376 prefixlen);
1377 exit(1);
1379 } else {
1380 (void) fprintf(stderr, "ifconfig: setting prefix only supported"
1381 " for address family inet or inet6\n");
1382 exit(1);
1384 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1385 if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1386 Perror0_exit("SIOCSLIFNETMASK");
1387 return (0);
1390 /* ARGSUSED */
1391 static int
1392 setifbroadaddr(char *addr, int64_t param)
1394 struct sockaddr_in broadaddr;
1396 assert(afp->af_af != AF_INET6);
1398 if (strcmp(addr, "+") == 0) {
1400 * This doesn't set the broadcast address at all. Rather, it
1401 * gets, then sets the interface's address, relying on the fact
1402 * that resetting the address will reset the broadcast address.
1404 (void) strncpy(lifr.lifr_name, name,
1405 sizeof (lifr.lifr_name));
1406 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
1407 if (errno != EADDRNOTAVAIL)
1408 Perror0_exit("SIOCGLIFADDR");
1409 return (0);
1411 if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
1412 Perror0_exit("SIOCGLIFADDR");
1414 return (0);
1416 in_getaddr(addr, (struct sockaddr *)&broadaddr, NULL);
1418 (void) memcpy(&lifr.lifr_addr, &broadaddr, sizeof (broadaddr));
1419 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1420 if (ioctl(s, SIOCSLIFBRDADDR, (caddr_t)&lifr) < 0)
1421 Perror0_exit("SIOCSLIFBRDADDR");
1422 return (0);
1426 * set interface destination address
1428 /* ARGSUSED */
1429 static int
1430 setifdstaddr(char *addr, int64_t param)
1432 (*afp->af_getaddr)(addr, (struct sockaddr *)&lifr.lifr_addr, NULL);
1433 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1434 if (ioctl(s, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0)
1435 Perror0_exit("setifdstaddr: SIOCSLIFDSTADDR");
1436 return (0);
1439 /* ARGSUSED */
1440 static int
1441 setifflags(char *val, int64_t value)
1443 int phyintlen, origphyintlen;
1445 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1446 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
1447 Perror0_exit("setifflags: SIOCGLIFFLAGS");
1449 if (value == IFF_NOFAILOVER) {
1451 * Fail if '-failover' is set after a prior addif created the
1452 * alias on a different interface. This can happen when the
1453 * interface is part of an IPMP group.
1455 phyintlen = strcspn(name, ":");
1456 origphyintlen = strcspn(origname, ":");
1457 if (phyintlen != origphyintlen ||
1458 strncmp(name, origname, phyintlen) != 0) {
1459 (void) fprintf(stderr, "ifconfig: can't set -failover "
1460 "on failed/standby/offlined interface %s\n",
1461 origname);
1462 exit(1);
1466 if (value < 0) {
1467 value = -value;
1468 lifr.lifr_flags &= ~value;
1469 if ((value & IFF_UP) && (lifr.lifr_flags & IFF_DUPLICATE)) {
1471 * If the user is trying to mark an interface with a
1472 * duplicate address as "down," then fetch the address
1473 * and set it. This will cause IP to clear the
1474 * IFF_DUPLICATE flag and stop the automatic recovery
1475 * timer.
1477 value = lifr.lifr_flags;
1478 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
1479 (void) ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr);
1480 lifr.lifr_flags = value;
1482 } else {
1483 lifr.lifr_flags |= value;
1485 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1486 if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
1487 Perror0_exit("setifflags: SIOCSLIFFLAGS");
1489 return (0);
1492 /* ARGSUSED */
1493 static int
1494 setifmetric(char *val, int64_t param)
1496 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1497 lifr.lifr_metric = atoi(val);
1498 if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
1499 Perror0_exit("setifmetric: SIOCSLIFMETRIC");
1500 return (0);
1503 /* ARGSUSED */
1504 static int
1505 setifmtu(char *val, int64_t param)
1507 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1508 lifr.lifr_mtu = atoi(val);
1509 if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
1510 Perror0_exit("setifmtu: SIOCSLIFMTU");
1511 return (0);
1514 /* ARGSUSED */
1515 static int
1516 setifindex(char *val, int64_t param)
1518 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1519 lifr.lifr_index = atoi(val);
1520 if (ioctl(s, SIOCSLIFINDEX, (caddr_t)&lifr) < 0)
1521 Perror0_exit("setifindex: SIOCSLIFINDEX");
1522 return (0);
1525 /* ARGSUSED */
1526 static int
1527 setifether(char *addr, int64_t param)
1529 uchar_t *ea;
1530 iface_t *current;
1531 int maclen;
1533 if (addr == NULL) {
1534 ifstatus(name);
1535 print_ifether(name);
1536 return (0);
1539 phyif = NULL;
1540 logifs = NULL;
1543 * if the IP interface in the arguments is a logical
1544 * interface, exit with an error now.
1546 if (strchr(name, ':') != NULL) {
1547 (void) fprintf(stderr, "ifconfig: cannot change"
1548 " ethernet address of a logical interface\n");
1549 exit(1);
1552 ea = _link_aton(addr, &maclen);
1553 if (ea == NULL) {
1554 if (maclen == -1)
1555 (void) fprintf(stderr,
1556 "ifconfig: %s: bad address\n", addr);
1557 else
1558 (void) fprintf(stderr, "ifconfig: malloc() failed\n");
1559 exit(1);
1562 (void) strncpy(savedname, name, sizeof (savedname));
1565 * Call selectifs only for the IP interfaces that are ipv4.
1566 * offflags == IFF_IPV6 because you should not change the
1567 * Ethernet address of an ipv6 interface
1569 foreachinterface(selectifs, 0, (char **)NULL, 0, 0, IFF_IPV6, 0);
1571 /* If physical interface not found, exit now */
1572 if (phyif == NULL) {
1573 (void) fprintf(stderr,
1574 "ifconfig: interface %s not found\n", savedname);
1575 exit(1);
1578 /* Restore */
1579 (void) strncpy(name, savedname, sizeof (name));
1580 (void) strncpy(origname, savedname, sizeof (origname));
1581 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1584 * close and reopen the socket
1585 * we don't know which type of socket we have now
1587 (void) close(s);
1588 s = socket(SOCKET_AF(AF_UNSPEC), SOCK_DGRAM, 0);
1589 if (s < 0) {
1590 Perror0_exit("socket");
1594 * mark down the logical interfaces first,
1595 * and then the physical interface
1597 if (updownifs(logifs, 0) < 0 || updownifs(phyif, 0) < 0) {
1598 Perror0_exit("mark down interface failed");
1602 * Change the physical address
1604 if (dlpi_set_address(savedname, ea, maclen) == -1) {
1605 (void) fprintf(stderr,
1606 "ifconfig: failed setting mac address on %s\n",
1607 savedname);
1611 * if any interfaces were marked down before changing the
1612 * ethernet address, put them up again.
1613 * First the physical interface, then the logical ones.
1615 if (updownifs(phyif, 1) < 0 || updownifs(logifs, 1) < 0) {
1616 Perror0_exit("mark down interface failed");
1619 /* Free the memory allocated by selectifs */
1620 free(phyif);
1621 for (current = logifs; current != NULL; current = logifs) {
1622 logifs = logifs->next;
1623 free(current);
1626 return (0);
1630 * Print an interface's Ethernet address, if it has one.
1632 static void
1633 print_ifether(char *ifname)
1635 int protocol;
1636 icfg_if_t interface;
1637 icfg_handle_t handle;
1638 int fd;
1640 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1642 fd = socket(AF_INET, SOCK_DGRAM, 0);
1643 if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1645 * It's possible the interface is only configured for
1646 * IPv6; check again with AF_INET6.
1648 (void) close(fd);
1649 fd = socket(AF_INET6, SOCK_DGRAM, 0);
1650 if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1651 (void) close(fd);
1652 return;
1655 (void) close(fd);
1657 /* Virtual interfaces don't have MAC addresses */
1658 if (lifr.lifr_flags & IFF_VIRTUAL)
1659 return;
1662 * We must be careful to set if_protocol based on the current
1663 * properties of the interface. For instance, if "ip.tun0" is
1664 * configured only as an IPv6 tunnel, then if_protocol must be
1665 * set to AF_INET6 or icfg_get_tunnel_lower() will fail and
1666 * we will falsely conclude that it's not a tunnel.
1668 interface.if_protocol = AF_INET;
1669 if (lifr.lifr_flags & IFF_IPV6)
1670 interface.if_protocol = AF_INET6;
1672 (void) strncpy(interface.if_name, ifname, sizeof (interface.if_name));
1674 if (icfg_open(&handle, &interface) == ICFG_SUCCESS) {
1675 if (icfg_get_tunnel_lower(handle, &protocol) == ICFG_SUCCESS) {
1676 /* Tunnel op succeeded -- it's a tunnel so skip */
1677 icfg_close(handle);
1678 return;
1680 icfg_close(handle);
1683 dlpi_print_address(ifname);
1687 * static void selectifs(int argc, char *argv[], int af, struct lifreq *rp)
1689 * Called inside setifether() to create a list of interfaces to
1690 * mark down/up when changing the Ethernet address.
1691 * If the current interface is the physical interface passed
1692 * as an argument to ifconfig, update phyif.
1693 * If the current interface is a logical interface associated
1694 * to the physical interface, add it to the logifs list.
1696 /* ARGSUSED */
1697 static void
1698 selectifs(int argc, char *argv[], int af, struct lifreq *rp)
1700 char *colonp;
1701 int length;
1702 iface_t *current;
1705 * savedname= name of the IP interface to which you want to
1706 * change ethernet address
1707 * name= name of the current IP interface
1709 colonp = strchr(name, ':');
1710 if (colonp == NULL)
1711 length = max(strlen(savedname), strlen(name));
1712 else
1713 length = max(strlen(savedname), colonp - name);
1714 if (strncmp(savedname, name, length) == 0) {
1715 (void) strcpy(lifr.lifr_name, name);
1716 if (ioctl(s, SIOCGLIFFLAGS, &lifr) < 0) {
1717 Perror0("selectifs: SIOCGLIFFLAGS");
1718 return;
1721 if ((current = malloc(sizeof (iface_t))) == NULL) {
1722 Perror0_exit("selectifs: malloc failed\n");
1725 if (colonp == NULL) {
1726 /* this is the physical interface */
1727 phyif = current;
1728 bcopy(&lifr, &phyif->lifr, sizeof (struct lifreq));
1729 phyif->next = NULL;
1730 } else {
1731 /* this is a logical interface */
1732 bcopy(&lifr, &current->lifr, sizeof (struct lifreq));
1733 current->next = logifs;
1734 logifs = current;
1740 * static int updownifs(iface_t *ifs, int up)
1742 * It takes in input a list of IP interfaces (ifs)
1743 * and a flag (up).
1744 * It marks each interface in the list down (up = 0)
1745 * or up (up > 0). This is done ONLY if the IP
1746 * interface was originally up.
1748 * Return values:
1749 * 0 = everything OK
1750 * -1 = problem
1752 static int
1753 updownifs(iface_t *ifs, int up)
1755 iface_t *current;
1756 int ret = 0;
1757 int save_errno;
1758 char savename[LIFNAMSIZ];
1759 uint64_t orig_flags;
1761 for (current = ifs; current != NULL; current = current->next) {
1762 if (current->lifr.lifr_flags & IFF_UP) {
1763 orig_flags = current->lifr.lifr_flags;
1764 if (!up)
1765 current->lifr.lifr_flags &= ~IFF_UP;
1766 if (ioctl(s, SIOCSLIFFLAGS, &current->lifr) < 0) {
1767 save_errno = errno;
1768 (void) strcpy(savename,
1769 current->lifr.lifr_name);
1770 ret = -1;
1772 if (!up) /* restore the original flags */
1773 current->lifr.lifr_flags = orig_flags;
1777 if (ret == -1) {
1778 (void) strcpy(lifr.lifr_name, savename);
1779 errno = save_errno;
1781 return (ret);
1785 * static int find_all_global_interfaces(struct lifconf *lifcp, char **buf,
1786 * int64_t lifc_flags)
1788 * It finds all data links for the global zone.
1790 * It takes in input a pointer to struct lifconf to receive interfaces
1791 * informations, a **char to hold allocated buffer, and a lifc_flags.
1793 * Return values:
1794 * 0 = everything OK
1795 * -1 = problem
1797 static int
1798 find_all_global_interfaces(struct lifconf *lifcp, char **buf,
1799 int64_t lifc_flags)
1801 unsigned bufsize;
1802 int n;
1803 ni_t *nip;
1804 struct lifreq *lifrp;
1806 (void) dlpi_walk(ni_entry, NULL, 0);
1809 * Now, translate the linked list into
1810 * a struct lifreq buffer
1812 if (num_ni == 0) {
1813 lifcp->lifc_family = AF_UNSPEC;
1814 lifcp->lifc_flags = lifc_flags;
1815 lifcp->lifc_len = 0;
1816 lifcp->lifc_buf = NULL;
1817 return (0);
1820 bufsize = num_ni * sizeof (struct lifreq);
1821 if ((*buf = malloc(bufsize)) == NULL)
1822 Perror0_exit("find_all_interfaces: malloc failed");
1824 lifcp->lifc_family = AF_UNSPEC;
1825 lifcp->lifc_flags = lifc_flags;
1826 lifcp->lifc_len = bufsize;
1827 lifcp->lifc_buf = *buf;
1829 for (n = 0, lifrp = lifcp->lifc_req; n < num_ni; n++, lifrp++) {
1830 nip = ni_list;
1831 (void) strncpy(lifrp->lifr_name, nip->ni_name,
1832 sizeof (lifr.lifr_name));
1833 ni_list = nip->ni_next;
1834 free(nip);
1836 return (0);
1840 * static int find_all_zone_interfaces(struct lifconf *lifcp, char **buf,
1841 * int64_t lifc_flags)
1843 * It finds all interfaces for an exclusive-IP zone, that is all the interfaces
1844 * assigned to it.
1846 * It takes in input a pointer to struct lifconf to receive interfaces
1847 * informations, a **char to hold allocated buffer, and a lifc_flags.
1849 * Return values:
1850 * 0 = everything OK
1851 * -1 = problem
1853 static int
1854 find_all_zone_interfaces(struct lifconf *lifcp, char **buf, int64_t lifc_flags)
1856 zoneid_t zoneid;
1857 unsigned bufsize;
1858 char *dlnames, *ptr;
1859 struct lifreq *lifrp;
1860 int num_ni_saved, i;
1862 zoneid = getzoneid();
1864 num_ni = 0;
1865 if (zone_list_datalink(zoneid, &num_ni, NULL) != 0)
1866 Perror0_exit("find_all_interfaces: list interfaces failed");
1867 again:
1868 /* this zone doesn't have any data-links */
1869 if (num_ni == 0) {
1870 lifcp->lifc_family = AF_UNSPEC;
1871 lifcp->lifc_flags = lifc_flags;
1872 lifcp->lifc_len = 0;
1873 lifcp->lifc_buf = NULL;
1874 return (0);
1877 dlnames = malloc(num_ni * LIFNAMSIZ);
1878 if (dlnames == NULL)
1879 Perror0_exit("find_all_interfaces: out of memory");
1880 num_ni_saved = num_ni;
1882 if (zone_list_datalink(zoneid, &num_ni, dlnames) != 0)
1883 Perror0_exit("find_all_interfaces: list interfaces failed");
1885 if (num_ni_saved < num_ni) {
1886 /* list increased, try again */
1887 free(dlnames);
1888 goto again;
1891 /* this zone doesn't have any data-links now */
1892 if (num_ni == 0) {
1893 free(dlnames);
1894 lifcp->lifc_family = AF_UNSPEC;
1895 lifcp->lifc_flags = lifc_flags;
1896 lifcp->lifc_len = 0;
1897 lifcp->lifc_buf = NULL;
1898 return (0);
1901 bufsize = num_ni * sizeof (struct lifreq);
1902 if ((*buf = malloc(bufsize)) == NULL) {
1903 free(dlnames);
1904 Perror0_exit("find_all_interfaces: malloc failed");
1907 lifrp = (struct lifreq *)*buf;
1908 ptr = dlnames;
1909 for (i = 0; i < num_ni; i++) {
1910 if (strlcpy(lifrp->lifr_name, ptr, LIFNAMSIZ) >=
1911 LIFNAMSIZ)
1912 Perror0_exit("find_all_interfaces: overflow");
1913 ptr += LIFNAMSIZ;
1914 lifrp++;
1917 free(dlnames);
1918 lifcp->lifc_family = AF_UNSPEC;
1919 lifcp->lifc_flags = lifc_flags;
1920 lifcp->lifc_len = bufsize;
1921 lifcp->lifc_buf = *buf;
1922 return (0);
1926 * Create the next unused logical interface using the original name
1927 * and assign the address (and mask if '/<n>' is part of the address).
1928 * Use the new logical interface for subsequent subcommands by updating
1929 * the name variable.
1931 * This allows syntax like:
1932 * ifconfig le0 addif 109.106.86.130 netmask + up \
1933 * addif 109.106.86.131 netmask + up
1935 /* ARGSUSED */
1936 static int
1937 addif(char *str, int64_t param)
1939 int prefixlen = 0;
1940 struct sockaddr_storage laddr;
1941 struct sockaddr_storage mask;
1943 (void) strncpy(name, origname, sizeof (name));
1945 if (strchr(name, ':') != NULL) {
1946 (void) fprintf(stderr,
1947 "ifconfig: addif: bad physical interface name %s\n",
1948 name);
1949 exit(1);
1953 * clear so parser will interpret next address as source followed
1954 * by possible dest
1956 setaddr = 0;
1957 (*afp->af_getaddr)(str, (struct sockaddr *)&laddr, &prefixlen);
1959 switch (prefixlen) {
1960 case NO_PREFIX:
1961 /* Nothing there - ok */
1962 break;
1963 case BAD_ADDR:
1964 (void) fprintf(stderr,
1965 "ifconfig: Bad prefix length in %s\n", str);
1966 exit(1);
1967 default:
1968 (void) memset(&mask, 0, sizeof (mask));
1969 mask.ss_family = afp->af_af;
1970 if (afp->af_af == AF_INET6) {
1971 struct sockaddr_in6 *sin6;
1972 sin6 = (struct sockaddr_in6 *)&mask;
1973 if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
1974 (uchar_t *)&sin6->sin6_addr)) {
1975 (void) fprintf(stderr, "ifconfig: "
1976 "Bad prefix length: %d\n",
1977 prefixlen);
1978 exit(1);
1980 } else {
1981 struct sockaddr_in *sin;
1983 sin = (struct sockaddr_in *)&mask;
1984 if (!in_prefixlentomask(prefixlen, IP_ABITS,
1985 (uchar_t *)&sin->sin_addr)) {
1986 (void) fprintf(stderr, "ifconfig: "
1987 "Bad prefix length: %d\n",
1988 prefixlen);
1989 exit(1);
1992 g_netmask_set = G_NETMASK_NIL;
1993 break;
1997 * This is a "hack" to get around the problem of SIOCLIFADDIF. The
1998 * problem is that this ioctl does not include the netmask when
1999 * adding a logical interface. This is the same problem described
2000 * in the ifconfig() comments. To get around this problem, we first
2001 * add the logical interface with a 0 address. After that, we set
2002 * the netmask if provided. Finally we set the interface address.
2004 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2005 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
2007 /* Note: no need to do DAD here since the interface isn't up yet. */
2009 if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
2010 Perror0_exit("addif: SIOCLIFADDIF");
2012 (void) printf("Created new logical interface %s\n",
2013 lifr.lifr_name);
2014 (void) strncpy(name, lifr.lifr_name, sizeof (name));
2017 * Check and see if any "netmask" command is used and perform the
2018 * necessary operation.
2020 set_mask_lifreq(&lifr, &laddr, &mask);
2022 * Only set the netmask if "netmask" command is used or a prefix is
2023 * provided.
2025 if (g_netmask_set == G_NETMASK_SET || prefixlen >= 0) {
2026 if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
2027 Perror0_exit("addif: SIOCSLIFNETMASK");
2030 /* Finally, we set the interface address. */
2031 lifr.lifr_addr = laddr;
2032 if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
2033 Perror0_exit("SIOCSLIFADDR");
2036 * let parser know we got a source.
2037 * Next address, if given, should be dest
2039 setaddr++;
2040 return (0);
2044 * Remove a logical interface based on its IP address. Unlike addif
2045 * there is no '/<n>' here.
2046 * Verifies that the interface is down before it is removed.
2048 /* ARGSUSED */
2049 static int
2050 removeif(char *str, int64_t param)
2052 struct sockaddr_storage laddr;
2054 if (strchr(name, ':') != NULL) {
2055 (void) fprintf(stderr,
2056 "ifconfig: removeif: bad physical interface name %s\n",
2057 name);
2058 exit(1);
2061 (*afp->af_getaddr)(str, &laddr, NULL);
2062 lifr.lifr_addr = laddr;
2064 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2065 if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
2066 if (errno == EBUSY) {
2067 /* This can only happen if ipif_id = 0 */
2068 (void) fprintf(stderr,
2069 "ifconfig: removeif: can't remove interface: %s\n",
2070 name);
2071 exit(1);
2073 Perror0_exit("removeif: SIOCLIFREMOVEIF");
2075 return (0);
2079 * Set the address token for IPv6.
2081 /* ARGSUSED */
2082 static int
2083 setiftoken(char *addr, int64_t param)
2085 int prefixlen = 0;
2086 struct sockaddr_in6 token;
2088 in6_getaddr(addr, (struct sockaddr *)&token, &prefixlen);
2089 switch (prefixlen) {
2090 case NO_PREFIX:
2091 (void) fprintf(stderr,
2092 "ifconfig: Missing prefix length in subnet %s\n", addr);
2093 exit(1);
2094 /* NOTREACHED */
2095 case BAD_ADDR:
2096 (void) fprintf(stderr,
2097 "ifconfig: Bad prefix length in %s\n", addr);
2098 exit(1);
2099 default:
2100 break;
2102 (void) memcpy(&lifr.lifr_addr, &token, sizeof (token));
2103 lifr.lifr_addrlen = prefixlen;
2104 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2105 if (ioctl(s, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0) {
2106 Perror0_exit("setiftoken: SIOCSLIFTOKEN");
2108 return (0);
2112 * Return value: 0 on success, -1 on failure.
2114 static int
2115 connect_to_mpathd(int family)
2117 int s;
2118 struct sockaddr_storage ss;
2119 struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
2120 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
2121 struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
2122 int addrlen;
2123 int ret;
2124 int on;
2126 s = socket(family, SOCK_STREAM, 0);
2127 if (s < 0) {
2128 Perror0_exit("connect_to_mpathd: socket");
2130 (void) bzero((char *)&ss, sizeof (ss));
2131 ss.ss_family = family;
2133 * Need to bind to a privileged port. For non-root, this
2134 * will fail. in.mpathd verifies that only commands coming
2135 * from privileged ports succeed so that ordinary users
2136 * can't connect and start talking to in.mpathd
2138 on = 1;
2139 if (setsockopt(s, IPPROTO_TCP, TCP_ANONPRIVBIND, &on,
2140 sizeof (on)) < 0) {
2141 Perror0_exit("connect_to_mpathd: setsockopt");
2143 switch (family) {
2144 case AF_INET:
2145 sin->sin_port = 0;
2146 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2147 addrlen = sizeof (struct sockaddr_in);
2148 break;
2149 case AF_INET6:
2150 sin6->sin6_port = 0;
2151 sin6->sin6_addr = loopback_addr;
2152 addrlen = sizeof (struct sockaddr_in6);
2153 break;
2155 ret = bind(s, (struct sockaddr *)&ss, addrlen);
2156 if (ret != 0) {
2157 (void) close(s);
2158 return (-1);
2161 switch (family) {
2162 case AF_INET:
2163 sin->sin_port = htons(MPATHD_PORT);
2164 break;
2165 case AF_INET6:
2166 sin6->sin6_port = htons(MPATHD_PORT);
2167 break;
2169 ret = connect(s, (struct sockaddr *)&ss, addrlen);
2170 (void) close(s);
2171 return (ret);
2174 /* ARGSUSED */
2175 static int
2176 setifgroupname(char *grpname, int64_t param)
2178 if (debug) {
2179 (void) printf("Setting groupname %s on interface %s\n",
2180 grpname, name);
2182 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2183 (void) strncpy(lifr.lifr_groupname, grpname,
2184 sizeof (lifr.lifr_groupname));
2185 if (ioctl(s, SIOCSLIFGROUPNAME, (caddr_t)&lifr) < 0) {
2186 Perror0_exit("setifgroupname: SIOCSLIFGROUPNAME");
2190 * If the SUNW_NO_MPATHD environment variable is set then don't
2191 * bother starting up in.mpathd. See PSARC/2002/249 for the
2192 * depressing details on this bit of stupidity.
2194 if (getenv("SUNW_NO_MPATHD") != NULL) {
2195 return (0);
2199 * Try to connect to in.mpathd using IPv4. If we succeed,
2200 * we conclude that in.mpathd is running, and quit.
2202 if (connect_to_mpathd(AF_INET) == 0) {
2203 /* connect succeeded, mpathd is already running */
2204 return (0);
2207 * Try to connect to in.mpathd using IPv6. If we succeed,
2208 * we conclude that in.mpathd is running, and quit.
2210 if (connect_to_mpathd(AF_INET6) == 0) {
2211 /* connect succeeded, mpathd is already running */
2212 return (0);
2216 * in.mpathd may not be running. Start it now. If it is already
2217 * running, in.mpathd will take care of handling multiple incarnations
2218 * of itself. ifconfig only tries to optimize performance by not
2219 * starting another incarnation of in.mpathd.
2221 switch (fork()) {
2223 case -1:
2224 Perror0_exit("setifgroupname: fork");
2225 /* NOTREACHED */
2226 case 0:
2227 (void) execl(MPATHD_PATH, MPATHD_PATH, NULL);
2228 _exit(1);
2229 /* NOTREACHED */
2230 default:
2231 return (0);
2237 * To list all the modules above a given network interface.
2239 /* ARGSUSED */
2240 static int
2241 modlist(char *null, int64_t param)
2243 int muxid_fd;
2244 int muxfd;
2245 int ipfd_lowstr;
2246 int arpfd_lowstr;
2247 int num_mods;
2248 int i;
2249 struct str_list strlist;
2250 int orig_arpid;
2252 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2253 if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr,
2254 &orig_arpid) < 0) {
2255 return (-1);
2257 if ((num_mods = ioctl(ipfd_lowstr, I_LIST, NULL)) < 0) {
2258 Perror0("cannot I_LIST to get the number of modules");
2259 } else {
2260 if (debug > 0) {
2261 (void) printf("Listing (%d) modules above %s\n",
2262 num_mods, name);
2265 strlist.sl_nmods = num_mods;
2266 strlist.sl_modlist = malloc(sizeof (struct str_mlist) *
2267 num_mods);
2268 if (strlist.sl_modlist == NULL) {
2269 Perror0("cannot malloc");
2270 } else {
2271 if (ioctl(ipfd_lowstr, I_LIST, (caddr_t)&strlist) < 0) {
2272 Perror0("cannot I_LIST for module names");
2273 } else {
2274 for (i = 0; i < strlist.sl_nmods; i++) {
2275 (void) printf("%d %s\n", i,
2276 strlist.sl_modlist[i].l_name);
2279 free(strlist.sl_modlist);
2282 return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr,
2283 orig_arpid));
2286 #define MODINSERT_OP 'i'
2287 #define MODREMOVE_OP 'r'
2290 * To insert a module to the stream of the interface. It is just a
2291 * wrapper. The real function is modop().
2293 /* ARGSUSED */
2294 static int
2295 modinsert(char *arg, int64_t param)
2297 return (modop(arg, MODINSERT_OP));
2301 * To remove a module from the stream of the interface. It is just a
2302 * wrapper. The real function is modop().
2304 /* ARGSUSED */
2305 static int
2306 modremove(char *arg, int64_t param)
2308 return (modop(arg, MODREMOVE_OP));
2312 * Open a stream on /dev/udp{,6}, pop off all undesired modules (note that
2313 * the user may have configured autopush to add modules above
2314 * udp), and push the arp module onto the resulting stream.
2315 * This is used to make IP+ARP be able to atomically track the muxid
2316 * for the I_PLINKed STREAMS, thus it isn't related to ARP running the ARP
2317 * protocol.
2319 static int
2320 open_arp_on_udp(char *udp_dev_name)
2322 int fd;
2324 if ((fd = open(udp_dev_name, O_RDWR)) == -1) {
2325 Perror2("open", udp_dev_name);
2326 return (-1);
2328 errno = 0;
2329 while (ioctl(fd, I_POP, 0) != -1)
2331 if (errno != EINVAL) {
2332 Perror2("pop", udp_dev_name);
2333 } else if (ioctl(fd, I_PUSH, ARP_MOD_NAME) == -1) {
2334 Perror2("arp PUSH", udp_dev_name);
2335 } else {
2336 return (fd);
2338 (void) close(fd);
2339 return (-1);
2343 * Helper function for mod*() functions. It gets a fd to the lower IP
2344 * stream and I_PUNLINK's the lower stream. It also initializes the
2345 * global variable lifr.
2347 * Param:
2348 * int *muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK
2349 * int *muxid_fd: fd to /dev/udp{,6} for LIFMUXID
2350 * int *ipfd_lowstr: fd to the lower IP stream.
2351 * int *arpfd_lowstr: fd to the lower ARP stream.
2353 * Return:
2354 * -1 if operation fails, 0 otherwise.
2356 * Please see the big block comment above plumb_one_device()
2357 * for the logic of the PLINK/PUNLINK
2359 static int
2360 ip_domux2fd(int *muxfd, int *muxid_fd, int *ipfd_lowstr, int *arpfd_lowstr,
2361 int *orig_arpid)
2363 uint64_t flags;
2364 char *udp_dev_name;
2366 *orig_arpid = 0;
2367 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2368 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2369 Perror0_exit("status: SIOCGLIFFLAGS");
2371 flags = lifr.lifr_flags;
2372 if (flags & IFF_IPV4) {
2373 udp_dev_name = UDP_DEV_NAME;
2374 } else if (flags & IFF_IPV6) {
2375 udp_dev_name = UDP6_DEV_NAME;
2376 } else {
2377 return (-1);
2380 if ((*muxid_fd = open(udp_dev_name, O_RDWR)) < 0) {
2381 Perror2("open", udp_dev_name);
2382 return (-1);
2384 if (ioctl(*muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
2385 Perror2("SIOCGLIFMUXID", udp_dev_name);
2386 return (-1);
2388 if (debug > 0) {
2389 (void) printf("ARP_muxid %d IP_muxid %d\n",
2390 lifr.lifr_arp_muxid, lifr.lifr_ip_muxid);
2394 * Use /dev/udp{,6} as the mux to avoid linkcycles.
2396 if ((*muxfd = open_arp_on_udp(udp_dev_name)) == -1)
2397 return (-1);
2399 if (lifr.lifr_arp_muxid != 0) {
2400 if ((*arpfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
2401 lifr.lifr_arp_muxid)) < 0) {
2402 if ((errno == EINVAL) &&
2403 (flags & (IFF_NOARP | IFF_IPV6))) {
2405 * Some plumbing utilities set the muxid to
2406 * -1 or some invalid value to signify that
2407 * there is no arp stream. Set the muxid to 0
2408 * before trying to unplumb the IP stream.
2409 * IP does not allow the IP stream to be
2410 * unplumbed if it sees a non-null arp muxid,
2411 * for consistency of IP-ARP streams.
2413 *orig_arpid = lifr.lifr_arp_muxid;
2414 lifr.lifr_arp_muxid = 0;
2415 (void) ioctl(*muxid_fd, SIOCSLIFMUXID,
2416 (caddr_t)&lifr);
2417 *arpfd_lowstr = -1;
2418 } else {
2419 Perror0("_I_MUXID2FD");
2420 return (-1);
2422 } else if (ioctl(*muxfd, I_PUNLINK,
2423 lifr.lifr_arp_muxid) < 0) {
2424 Perror2("I_PUNLINK", udp_dev_name);
2425 return (-1);
2427 } else {
2428 *arpfd_lowstr = -1;
2431 if ((*ipfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
2432 lifr.lifr_ip_muxid)) < 0) {
2433 Perror0("_I_MUXID2FD");
2434 /* Undo any changes we made */
2435 if (*orig_arpid != 0) {
2436 lifr.lifr_arp_muxid = *orig_arpid;
2437 (void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2439 return (-1);
2441 if (ioctl(*muxfd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) {
2442 Perror2("I_PUNLINK", udp_dev_name);
2443 /* Undo any changes we made */
2444 if (*orig_arpid != 0) {
2445 lifr.lifr_arp_muxid = *orig_arpid;
2446 (void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2448 return (-1);
2450 return (0);
2454 * Helper function for mod*() functions. It I_PLINK's back the upper and
2455 * lower IP streams. Note that this function must be called after
2456 * ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized
2457 * and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink()
2458 * must be called in pairs.
2460 * Param:
2461 * int muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK
2462 * int muxid_fd: fd to /dev/udp{,6} for LIFMUXID
2463 * int ipfd_lowstr: fd to the lower IP stream.
2464 * int arpfd_lowstr: fd to the lower ARP stream.
2466 * Return:
2467 * -1 if operation fails, 0 otherwise.
2469 * Please see the big block comment above plumb_one_device()
2470 * for the logic of the PLINK/PUNLINK
2472 static int
2473 ip_plink(int muxfd, int muxid_fd, int ipfd_lowstr, int arpfd_lowstr,
2474 int orig_arpid)
2476 int ip_muxid;
2478 ip_muxid = ioctl(muxfd, I_PLINK, ipfd_lowstr);
2479 if (ip_muxid < 0) {
2480 Perror2("I_PLINK", UDP_DEV_NAME);
2481 return (-1);
2485 * If there is an arp stream, plink it. If there is no
2486 * arp stream, then it is possible that the plumbing
2487 * utility could have stored any value in the arp_muxid.
2488 * If so, restore it from orig_arpid.
2490 if (arpfd_lowstr != -1) {
2491 if (ioctl(muxfd, I_PLINK, arpfd_lowstr) < 0) {
2492 Perror2("I_PLINK", UDP_DEV_NAME);
2493 return (-1);
2495 } else if (orig_arpid != 0) {
2496 /* Undo the changes we did in ip_domux2fd */
2497 lifr.lifr_arp_muxid = orig_arpid;
2498 lifr.lifr_ip_muxid = ip_muxid;
2499 (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2502 (void) close(muxfd);
2503 (void) close(muxid_fd);
2504 return (0);
2508 * The real function to perform module insertion/removal.
2510 * Param:
2511 * char *arg: the argument string module_name@position
2512 * char op: operation, either MODINSERT_OP or MODREMOVE_OP.
2514 * Return:
2515 * Before doing ip_domux2fd(), this function calls exit(1) in case of
2516 * error. After ip_domux2fd() is done, it returns -1 for error, 0
2517 * otherwise.
2519 static int
2520 modop(char *arg, char op)
2522 char *pos_p;
2523 int muxfd;
2524 int muxid_fd;
2525 int ipfd_lowstr; /* IP stream (lower stream of mux) to be plinked */
2526 int arpfd_lowstr; /* ARP stream (lower stream of mux) to be plinked */
2527 struct strmodconf mod;
2528 char *at_char = "@";
2529 char *arg_str;
2530 int orig_arpid;
2532 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2534 /* Need to save the original string for -a option. */
2535 if ((arg_str = malloc(strlen(arg) + 1)) == NULL) {
2536 Perror0("cannot malloc");
2537 return (-1);
2539 (void) strcpy(arg_str, arg);
2541 if (*arg_str == *at_char) {
2542 (void) fprintf(stderr,
2543 "ifconfig: must supply a module name\n");
2544 exit(1);
2546 mod.mod_name = strtok(arg_str, at_char);
2547 if (strlen(mod.mod_name) > FMNAMESZ) {
2548 (void) fprintf(stderr, "ifconfig: module name too long: %s\n",
2549 mod.mod_name);
2550 exit(1);
2554 * Need to make sure that the core TCP/IP stack modules are not
2555 * removed. Otherwise, "bad" things can happen. If a module
2556 * is removed and inserted back, it loses its old state. But
2557 * the modules above it still have the old state. E.g. IP assumes
2558 * fast data path while tunnel after re-inserted assumes that it can
2559 * receive M_DATA only in fast data path for which it does not have
2560 * any state. This is a general caveat of _I_REMOVE/_I_INSERT.
2562 if (op == MODREMOVE_OP &&
2563 (strcmp(mod.mod_name, ARP_MOD_NAME) == 0 ||
2564 strcmp(mod.mod_name, IP_MOD_NAME) == 0 ||
2565 strcmp(mod.mod_name, TUN_NAME) == 0 ||
2566 strcmp(mod.mod_name, ATUN_NAME) == 0 ||
2567 strcmp(mod.mod_name, TUN6TO4_NAME) == 0)) {
2568 (void) fprintf(stderr, "ifconfig: cannot remove %s\n",
2569 mod.mod_name);
2570 exit(1);
2573 if ((pos_p = strtok(NULL, at_char)) == NULL) {
2574 (void) fprintf(stderr, "ifconfig: must supply a position\n");
2575 exit(1);
2577 mod.pos = atoi(pos_p);
2579 if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr,
2580 &orig_arpid) < 0) {
2581 free(arg_str);
2582 return (-1);
2584 switch (op) {
2585 case MODINSERT_OP:
2586 if (debug > 0) {
2587 (void) printf("Inserting module %s at %d\n",
2588 mod.mod_name, mod.pos);
2590 if (ioctl(ipfd_lowstr, _I_INSERT, (caddr_t)&mod) < 0) {
2591 Perror2("fail to insert module", mod.mod_name);
2593 break;
2594 case MODREMOVE_OP:
2595 if (debug > 0) {
2596 (void) printf("Removing module %s at %d\n",
2597 mod.mod_name, mod.pos);
2599 if (ioctl(ipfd_lowstr, _I_REMOVE, (caddr_t)&mod) < 0) {
2600 Perror2("fail to remove module", mod.mod_name);
2602 break;
2603 default:
2604 /* Should never get to here. */
2605 (void) fprintf(stderr, "Unknown operation\n");
2606 break;
2608 free(arg_str);
2609 return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr,
2610 orig_arpid));
2614 * Set tunnel source address
2616 /* ARGSUSED */
2617 static int
2618 setiftsrc(char *addr, int64_t param)
2620 return (settaddr(addr, icfg_set_tunnel_src));
2624 * Set tunnel destination address
2626 /* ARGSUSED */
2627 static int
2628 setiftdst(char *addr, int64_t param)
2630 return (settaddr(addr, icfg_set_tunnel_dest));
2634 * sets tunnels src|dst address. settaddr() expects the following:
2635 * addr: Points to a printable string containing the address to be
2636 * set, e.g. 129.153.128.110.
2637 * fn: Pointer to a libinetcfg routine that will do the actual work.
2638 * The only valid functions are icfg_set_tunnel_src and
2639 * icfg_set_tunnel_dest.
2641 static int
2642 settaddr(char *addr,
2643 int (*fn)(icfg_handle_t, const struct sockaddr *, socklen_t))
2645 icfg_handle_t handle;
2646 icfg_if_t interface;
2647 struct sockaddr_storage laddr;
2648 int lower;
2649 int rc;
2651 if (strchr(name, ':') != NULL) {
2652 errno = EPERM;
2653 Perror0_exit("Tunnel params on logical interfaces");
2655 (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
2656 interface.if_protocol = SOCKET_AF(af);
2658 /* Open interface. */
2659 if ((rc = icfg_open(&handle, &interface)) != ICFG_SUCCESS)
2660 Perror0_exit((char *)icfg_errmsg(rc));
2662 rc = icfg_get_tunnel_lower(handle, &lower);
2663 if (rc != ICFG_SUCCESS)
2664 Perror0_exit((char *)icfg_errmsg(rc));
2666 if (lower == AF_INET) {
2667 in_getaddr(addr, (struct sockaddr *)&laddr, NULL);
2668 } else {
2669 in6_getaddr(addr, (struct sockaddr *)&laddr, NULL);
2672 /* Call fn to do the real work, and close the interface. */
2673 rc = (*fn)(handle, (struct sockaddr *)&laddr,
2674 sizeof (struct sockaddr_storage));
2675 icfg_close(handle);
2677 if (rc != ICFG_SUCCESS)
2678 Perror0_exit((char *)icfg_errmsg(rc));
2680 return (0);
2683 /* Set tunnel encapsulation limit. */
2684 /* ARGSUSED */
2685 static int
2686 set_tun_encap_limit(char *arg, int64_t param)
2688 short limit;
2689 icfg_if_t interface;
2690 icfg_handle_t handle;
2691 int rc;
2693 if (strchr(name, ':') != NULL) {
2694 errno = EPERM;
2695 Perror0_exit("Tunnel params on logical interfaces");
2698 if ((sscanf(arg, "%hd", &limit) != 1) || (limit < 0) ||
2699 (limit > 255)) {
2700 errno = EINVAL;
2701 Perror0_exit("Invalid encapsulation limit");
2704 /* Open interface for configuration. */
2705 (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
2706 interface.if_protocol = SOCKET_AF(af);
2707 if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
2708 Perror0_exit("couldn't open interface");
2710 rc = icfg_set_tunnel_encaplimit(handle, (int)limit);
2711 icfg_close(handle);
2713 if (rc != ICFG_SUCCESS)
2714 Perror0_exit("Could not configure tunnel encapsulation limit");
2716 return (0);
2719 /* Disable encapsulation limit. */
2720 /* ARGSUSED */
2721 static int
2722 clr_tun_encap_limit(char *arg, int64_t param)
2724 icfg_if_t interface;
2725 icfg_handle_t handle;
2726 int rc;
2728 if (strchr(name, ':') != NULL) {
2729 errno = EPERM;
2730 Perror0_exit("Tunnel params on logical interfaces");
2733 /* Open interface for configuration. */
2734 (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
2735 interface.if_protocol = SOCKET_AF(af);
2736 if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
2737 Perror0_exit("couldn't open interface");
2739 rc = icfg_set_tunnel_encaplimit(handle, -1);
2740 icfg_close(handle);
2742 if (rc != ICFG_SUCCESS)
2743 Perror0_exit((char *)icfg_errmsg(rc));
2745 return (0);
2748 /* Set tunnel hop limit. */
2749 /* ARGSUSED */
2750 static int
2751 set_tun_hop_limit(char *arg, int64_t param)
2753 unsigned short limit;
2754 icfg_if_t interface;
2755 icfg_handle_t handle;
2756 int rc;
2758 if (strchr(name, ':') != NULL) {
2759 errno = EPERM;
2760 Perror0_exit("Tunnel params on logical interfaces");
2764 * Check limit here since it's really only an 8-bit unsigned quantity.
2766 if ((sscanf(arg, "%hu", &limit) != 1) || (limit > 255)) {
2767 errno = EINVAL;
2768 Perror0_exit("Invalid hop limit");
2771 /* Open interface for configuration. */
2772 (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
2773 interface.if_protocol = SOCKET_AF(af);
2774 if (icfg_open(&handle, &interface) != ICFG_SUCCESS)
2775 Perror0_exit("couldn't open interface");
2777 rc = icfg_set_tunnel_hoplimit(handle, (uint8_t)limit);
2778 icfg_close(handle);
2780 if (rc != ICFG_SUCCESS)
2781 Perror0_exit("Could not configure tunnel hop limit");
2783 return (0);
2786 /* Set zone ID */
2787 static int
2788 setzone(char *arg, int64_t param)
2790 zoneid_t zoneid = GLOBAL_ZONEID;
2792 if (param == NEXTARG) {
2793 /* zone must be active */
2794 if ((zoneid = getzoneidbyname(arg)) == -1) {
2795 (void) fprintf(stderr,
2796 "ifconfig: unknown zone '%s'\n", arg);
2797 exit(1);
2800 (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2801 lifr.lifr_zoneid = zoneid;
2802 if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1)
2803 Perror0_exit("SIOCSLIFZONE");
2804 return (0);
2807 /* Put interface into all zones */
2808 /* ARGSUSED */
2809 static int
2810 setallzones(char *arg, int64_t param)
2812 (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2813 lifr.lifr_zoneid = ALL_ZONES;
2814 if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1)
2815 Perror0_exit("SIOCSLIFZONE");
2816 return (0);
2819 /* Set source address to use */
2820 /* ARGSUSED */
2821 static int
2822 setifsrc(char *arg, int64_t param)
2824 uint_t ifindex = 0;
2825 int rval;
2827 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2830 * Argument can be either an interface name or "none". The latter means
2831 * that any previous selection is cleared.
2834 rval = strcmp(arg, name);
2835 if (rval == 0) {
2836 (void) fprintf(stderr,
2837 "ifconfig: Cannot specify same interface for usesrc"
2838 " group\n");
2839 exit(1);
2842 rval = strcmp(arg, NONE_STR);
2843 if (rval != 0) {
2844 if ((ifindex = if_nametoindex(arg)) == 0) {
2845 (void) strncpy(lifr.lifr_name, arg, LIFNAMSIZ);
2846 Perror0_exit("Could not get interface index");
2848 lifr.lifr_index = ifindex;
2849 } else {
2850 if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) != 0)
2851 Perror0_exit("Not a valid usesrc consumer");
2852 lifr.lifr_index = 0;
2855 if (debug)
2856 (void) printf("setifsrc: lifr_name %s, lifr_index %d\n",
2857 lifr.lifr_name, lifr.lifr_index);
2859 if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) == -1) {
2860 if (rval == 0)
2861 Perror0_exit("Cannot reset usesrc group");
2862 else
2863 Perror0_exit("Could not set source interface");
2866 return (0);
2870 * Print the interface status line associated with `ifname'
2872 static void
2873 ifstatus(const char *ifname)
2875 uint64_t flags;
2876 char if_usesrc_name[LIFNAMSIZ];
2877 char *newbuf;
2878 int n, numifs, rval = 0;
2879 struct lifreq *lifrp;
2880 struct lifsrcof lifs;
2882 (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2883 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2884 Perror0_exit("status: SIOCGLIFFLAGS");
2886 flags = lifr.lifr_flags;
2889 * In V4 compatibility mode, we don't print the IFF_IPV4 flag or
2890 * interfaces with IFF_IPV6 set.
2892 if (v4compat) {
2893 flags &= ~IFF_IPV4;
2894 if (flags & IFF_IPV6)
2895 return;
2898 (void) printf("%s: ", ifname);
2899 print_flags(flags);
2901 (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2902 if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
2903 Perror0_exit("status: SIOCGLIFMETRIC");
2904 } else {
2905 if (lifr.lifr_metric)
2906 (void) printf(" metric %d", lifr.lifr_metric);
2908 if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
2909 (void) printf(" mtu %u", lifr.lifr_mtu);
2911 /* don't print index or zone when in compatibility mode */
2912 if (!v4compat) {
2913 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
2914 (void) printf(" index %d", lifr.lifr_index);
2916 * Stack instances use GLOBAL_ZONEID for IP data structures
2917 * even in the non-global zone.
2919 if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) >= 0 &&
2920 lifr.lifr_zoneid != getzoneid() &&
2921 lifr.lifr_zoneid != GLOBAL_ZONEID) {
2922 char zone_name[ZONENAME_MAX];
2924 if (lifr.lifr_zoneid == ALL_ZONES) {
2925 (void) printf("\n\tall-zones");
2926 } else if (getzonenamebyid(lifr.lifr_zoneid, zone_name,
2927 sizeof (zone_name)) < 0) {
2928 (void) printf("\n\tzone %d", lifr.lifr_zoneid);
2929 } else {
2930 (void) printf("\n\tzone %s", zone_name);
2935 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
2936 lifs.lifs_ifindex = lifr.lifr_index;
2939 * Find the number of interfaces that use this interfaces'
2940 * address as a source address
2942 lifs.lifs_buf = NULL;
2943 lifs.lifs_maxlen = 0;
2944 for (;;) {
2945 /* The first pass will give the bufsize we need */
2946 rval = ioctl(s, SIOCGLIFSRCOF, (char *)&lifs);
2947 if (rval < 0) {
2948 if (lifs.lifs_buf != NULL) {
2949 free(lifs.lifs_buf);
2950 lifs.lifs_buf = NULL;
2952 lifs.lifs_len = 0;
2953 break;
2955 if (lifs.lifs_len <= lifs.lifs_maxlen)
2956 break;
2957 /* Use kernel's size + a small margin to avoid loops */
2958 lifs.lifs_maxlen = lifs.lifs_len +
2959 5 * sizeof (struct lifreq);
2960 /* For the first pass, realloc acts like malloc */
2961 newbuf = realloc(lifs.lifs_buf, lifs.lifs_maxlen);
2962 if (newbuf == NULL) {
2963 if (lifs.lifs_buf != NULL) {
2964 free(lifs.lifs_buf);
2965 lifs.lifs_buf = NULL;
2967 lifs.lifs_len = 0;
2968 break;
2970 lifs.lifs_buf = newbuf;
2974 numifs = lifs.lifs_len / sizeof (struct lifreq);
2975 if (numifs > 0) {
2976 lifrp = lifs.lifs_req;
2977 (void) printf("\n\tsrcof");
2978 for (n = numifs; n > 0; n--, lifrp++) {
2979 (void) printf(" %s", lifrp->lifr_name);
2983 if (lifs.lifs_buf != NULL)
2984 free(lifs.lifs_buf);
2987 /* Find the interface whose source address this interface uses */
2988 if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
2989 if (lifr.lifr_index != 0) {
2990 if (if_indextoname(lifr.lifr_index,
2991 if_usesrc_name) == NULL) {
2992 (void) printf("\n\tusesrc ifIndex %d",
2993 lifr.lifr_index);
2994 } else {
2995 (void) printf("\n\tusesrc %s", if_usesrc_name);
3000 (void) putchar('\n');
3005 * Print the status of the interface. If an address family was
3006 * specified, show it and it only; otherwise, show them all.
3008 static void
3009 status(void)
3011 struct afswtch *p = afp;
3012 uint64_t flags;
3014 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3015 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
3016 Perror0_exit("status: SIOCGLIFFLAGS");
3019 flags = lifr.lifr_flags;
3022 * Only print the interface status if the address family matches
3023 * the interface family flag.
3025 if (p != NULL) {
3026 if (((p->af_af == AF_INET6) && (flags & IFF_IPV4)) ||
3027 ((p->af_af == AF_INET) && (flags & IFF_IPV6)))
3028 return;
3032 * In V4 compatibility mode, don't print IFF_IPV6 interfaces.
3034 if (v4compat && (flags & IFF_IPV6))
3035 return;
3037 ifstatus(name);
3039 if (p != NULL) {
3040 (*p->af_status)(1, flags);
3041 } else {
3042 for (p = afs; p->af_name; p++) {
3043 (void) close(s);
3044 s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0);
3045 /* set global af for use in p->af_status */
3046 af = p->af_af;
3047 if (s == -1) {
3048 Perror0_exit("socket");
3050 (*p->af_status)(0, flags);
3054 * Historically, 'ether' has been an address family,
3055 * so print it here.
3057 print_ifether(name);
3062 * Print the status of the interface in a format that can be used to
3063 * reconfigure the interface later. Code stolen from status() above.
3065 /* ARGSUSED */
3066 static int
3067 configinfo(char *null, int64_t param)
3069 struct afswtch *p = afp;
3070 uint64_t flags;
3071 char phydevname[LIFNAMSIZ];
3072 char if_usesrc_name[LIFNAMSIZ];
3073 char *cp;
3075 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3076 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
3077 Perror0_exit("status: SIOCGLIFFLAGS");
3079 flags = lifr.lifr_flags;
3081 if (debug) {
3082 (void) printf("configinfo: name %s flags 0x%llx af_af %d\n",
3083 name, flags, p != NULL ? p->af_af : -1);
3086 /* remove LIF component */
3087 (void) strncpy(phydevname, name, sizeof (phydevname));
3088 cp = strchr(phydevname, ':');
3089 if (cp) {
3090 *cp = 0;
3092 phydevname[sizeof (phydevname) - 1] = '\0';
3095 * if the interface is IPv4
3096 * if we have a IPv6 address family restriction return
3097 * so it won't print
3098 * if we are in IPv4 compatibility mode, clear out IFF_IPV4
3099 * so we don't print it.
3101 if (flags & IFF_IPV4) {
3102 if (p && p->af_af == AF_INET6)
3103 return (-1);
3104 if (v4compat)
3105 flags &= ~IFF_IPV4;
3107 (void) printf("%s inet plumb", phydevname);
3108 } else if (flags & IFF_IPV6) {
3110 * else if the interface is IPv6
3111 * if we have a IPv4 address family restriction return
3112 * or we are in IPv4 compatibiltiy mode, return.
3114 if (p && p->af_af == AF_INET)
3115 return (-1);
3116 if (v4compat)
3117 return (-1);
3119 (void) printf("%s inet6 plumb", phydevname);
3122 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3123 if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
3124 Perror0_exit("configinfo: SIOCGLIFMETRIC");
3125 } else {
3126 if (lifr.lifr_metric)
3127 (void) printf(" metric %d ", lifr.lifr_metric);
3129 if (((flags & (IFF_VIRTUAL|IFF_LOOPBACK)) != IFF_VIRTUAL) &&
3130 ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
3131 (void) printf(" mtu %d", lifr.lifr_metric);
3133 /* don't print index when in compatibility mode */
3134 if (!v4compat) {
3135 if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
3136 (void) printf(" index %d", lifr.lifr_index);
3139 if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
3140 if (lifr.lifr_index != 0) {
3141 if (if_indextoname(lifr.lifr_index,
3142 if_usesrc_name) != NULL) {
3143 (void) printf(" usesrc %s", if_usesrc_name);
3148 if (p != NULL) {
3149 (*p->af_configinfo)(1, flags);
3150 } else {
3151 for (p = afs; p->af_name; p++) {
3152 (void) close(s);
3153 s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0);
3154 /* set global af for use in p->af_configinfo */
3155 af = p->af_af;
3156 if (s == -1) {
3157 Perror0_exit("socket");
3159 (*p->af_configinfo)(0, flags);
3163 (void) printf("\n");
3165 return (0);
3168 static void
3169 print_tsec(struct iftun_req *tparams)
3171 ipsec_req_t *ipsr;
3173 (void) printf("\ttunnel security settings ");
3175 * Deal with versioning, for now just point
3176 * an ipsec_req_t at ifta_secinfo. If versions
3177 * change, something else will overlay ifta_secinfo.
3179 assert(tparams->ifta_vers == IFTUN_VERSION);
3181 if (tparams->ifta_flags & IFTUN_COMPLEX_SECURITY) {
3182 (void) printf("--> use 'ipsecconf -ln -i %s'",
3183 tparams->ifta_lifr_name);
3184 } else {
3185 ipsr = (ipsec_req_t *)(&tparams->ifta_secinfo);
3186 if (ipsr->ipsr_ah_req & IPSEC_PREF_REQUIRED) {
3187 (void) printf("ah (%s) ",
3188 rparsealg(ipsr->ipsr_auth_alg, IPSEC_PROTO_AH));
3190 if (ipsr->ipsr_esp_req & IPSEC_PREF_REQUIRED) {
3191 (void) printf("esp (%s",
3192 rparsealg(ipsr->ipsr_esp_alg, IPSEC_PROTO_ESP));
3193 (void) printf("/%s)",
3194 rparsealg(ipsr->ipsr_esp_auth_alg, IPSEC_PROTO_AH));
3197 (void) printf("\n");
3200 static void
3201 tun_status(void)
3203 icfg_if_t interface;
3204 int rc;
3205 icfg_handle_t handle;
3206 int protocol;
3207 char srcbuf[INET6_ADDRSTRLEN];
3208 char dstbuf[INET6_ADDRSTRLEN];
3209 boolean_t tabbed;
3210 uint8_t hoplimit;
3211 int16_t encaplimit;
3212 struct sockaddr_storage taddr;
3213 socklen_t socklen = sizeof (taddr);
3215 (void) strncpy(interface.if_name, name, sizeof (interface.if_name));
3216 interface.if_protocol = SOCKET_AF(af);
3217 if ((rc = icfg_open(&handle, &interface)) != ICFG_SUCCESS)
3218 Perror0_exit((char *)icfg_errmsg(rc));
3221 * only print tunnel info for lun 0. If ioctl fails, assume
3222 * we are not a tunnel
3224 if (strchr(name, ':') != NULL ||
3225 icfg_get_tunnel_lower(handle, &protocol) != ICFG_SUCCESS) {
3226 icfg_close(handle);
3227 return;
3230 switch (protocol) {
3231 case AF_INET:
3232 (void) printf("\tinet");
3233 break;
3234 case AF_INET6:
3235 (void) printf("\tinet6");
3236 break;
3237 default:
3238 Perror0_exit("\ttunnel: Illegal lower stream\n\t");
3239 break;
3242 rc = icfg_get_tunnel_src(handle, (struct sockaddr *)&taddr, &socklen);
3243 if (rc == ICFG_NOT_SET) {
3244 (void) strlcpy(srcbuf, (protocol == AF_INET) ? "0.0.0.0" :
3245 "::", sizeof (srcbuf));
3246 } else if (rc != ICFG_SUCCESS) {
3247 Perror0_exit((char *)icfg_errmsg(rc));
3248 } else {
3249 rc = icfg_sockaddr_to_str(protocol, (struct sockaddr *)&taddr,
3250 srcbuf, sizeof (srcbuf));
3251 if (rc != ICFG_SUCCESS) {
3252 Perror0_exit((char *)icfg_errmsg(rc));
3256 (void) printf(" tunnel src %s ", srcbuf);
3258 rc = icfg_get_tunnel_dest(handle, (struct sockaddr *)&taddr, &socklen);
3259 if (rc == ICFG_NOT_SET) {
3260 (void) printf("\n");
3261 } else {
3262 rc = icfg_sockaddr_to_str(protocol, (struct sockaddr *)&taddr,
3263 dstbuf, sizeof (dstbuf));
3264 if (rc != ICFG_SUCCESS) {
3265 Perror0_exit((char *)icfg_errmsg(rc));
3267 (void) printf("tunnel dst %s\n", dstbuf);
3270 if (handle->ifh_tunnel_params != NULL &&
3271 (handle->ifh_tunnel_params->ifta_flags & IFTUN_SECURITY))
3272 print_tsec(handle->ifh_tunnel_params);
3275 * tabbed indicates tabbed and printed. Use it tell us whether
3276 * to tab and that we've printed something here, so we need a
3277 * newline
3279 tabbed = _B_FALSE;
3281 if (icfg_get_tunnel_hoplimit(handle, &hoplimit) == ICFG_SUCCESS) {
3282 (void) printf("\ttunnel hop limit %d ", hoplimit);
3283 tabbed = _B_TRUE;
3286 if ((protocol == AF_INET6) &&
3287 (icfg_get_tunnel_encaplimit(handle, &encaplimit) ==
3288 ICFG_SUCCESS)) {
3289 if (!tabbed) {
3290 (void) printf("\t");
3291 tabbed = _B_TRUE;
3293 if (encaplimit >= 0) {
3294 (void) printf("tunnel encapsulation limit %d",
3295 encaplimit);
3296 } else {
3297 (void) printf("tunnel encapsulation limit disabled");
3301 if (tabbed)
3302 (void) printf("\n");
3304 icfg_close(handle);
3307 static void
3308 in_status(int force, uint64_t flags)
3310 struct sockaddr_in *sin, *laddr;
3311 struct sockaddr_in netmask = { AF_INET };
3313 if (debug)
3314 (void) printf("in_status(%s) flags 0x%llx\n", name, flags);
3316 /* only print status for IPv4 interfaces */
3317 if (!(flags & IFF_IPV4))
3318 return;
3320 /* if the interface is a tunnel, print the tunnel status */
3321 tun_status();
3323 if (!(flags & IFF_NOLOCAL)) {
3324 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3325 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3326 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3327 errno == ENXIO) {
3328 if (!force)
3329 return;
3330 (void) memset(&lifr.lifr_addr, 0,
3331 sizeof (lifr.lifr_addr));
3332 } else
3333 Perror0_exit("in_status: SIOCGLIFADDR");
3335 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3336 (void) printf("\tinet %s ", inet_ntoa(sin->sin_addr));
3337 laddr = sin;
3338 } else {
3339 (void) printf("\tinet ");
3342 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3343 if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3344 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3345 errno == ENXIO) {
3346 if (!force)
3347 return;
3348 (void) memset(&lifr.lifr_addr, 0,
3349 sizeof (lifr.lifr_addr));
3350 } else {
3351 Perror0_exit("in_status: SIOCGLIFSUBNET");
3354 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3355 if ((flags & IFF_NOLOCAL) ||
3356 sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
3357 (void) printf("subnet %s/%d ", inet_ntoa(sin->sin_addr),
3358 lifr.lifr_addrlen);
3360 if (sin->sin_family != AF_INET) {
3361 (void) printf("Wrong family: %d\n", sin->sin_family);
3364 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3365 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
3366 if (errno != EADDRNOTAVAIL)
3367 Perror0_exit("in_status: SIOCGLIFNETMASK");
3368 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
3369 } else
3370 netmask.sin_addr =
3371 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
3372 if (flags & IFF_POINTOPOINT) {
3373 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3374 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3375 if (errno == EADDRNOTAVAIL)
3376 (void) memset(&lifr.lifr_addr, 0,
3377 sizeof (lifr.lifr_addr));
3378 else
3379 Perror0_exit("in_status: SIOCGLIFDSTADDR");
3381 sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
3382 (void) printf("--> %s ", inet_ntoa(sin->sin_addr));
3384 (void) printf("netmask %x ", ntohl(netmask.sin_addr.s_addr));
3385 if (flags & IFF_BROADCAST) {
3386 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3387 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
3388 if (errno == EADDRNOTAVAIL)
3389 (void) memset(&lifr.lifr_addr, 0,
3390 sizeof (lifr.lifr_addr));
3391 else
3392 Perror0_exit("in_status: SIOCGLIFBRDADDR");
3394 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3395 if (sin->sin_addr.s_addr != 0) {
3396 (void) printf("broadcast %s",
3397 inet_ntoa(sin->sin_addr));
3400 /* If there is a groupname, print it for lun 0 alone */
3401 if (strchr(name, ':') == NULL) {
3402 (void) memset(lifr.lifr_groupname, 0,
3403 sizeof (lifr.lifr_groupname));
3404 if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr) >= 0) {
3405 if (strlen(lifr.lifr_groupname) > 0) {
3406 (void) printf("\n\tgroupname %s",
3407 lifr.lifr_groupname);
3411 (void) putchar('\n');
3414 static void
3415 in6_status(int force, uint64_t flags)
3417 char abuf[INET6_ADDRSTRLEN];
3418 struct sockaddr_in6 *sin6, *laddr6;
3420 if (debug)
3421 (void) printf("in6_status(%s) flags 0x%llx\n", name, flags);
3423 if (!(flags & IFF_IPV6))
3424 return;
3426 /* if the interface is a tunnel, print the tunnel status */
3427 tun_status();
3429 if (!(flags & IFF_NOLOCAL)) {
3430 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3431 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3432 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3433 errno == ENXIO) {
3434 if (!force)
3435 return;
3436 (void) memset(&lifr.lifr_addr, 0,
3437 sizeof (lifr.lifr_addr));
3438 } else
3439 Perror0_exit("in_status6: SIOCGLIFADDR");
3441 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3442 (void) printf("\tinet6 %s/%d ",
3443 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3444 abuf, sizeof (abuf)),
3445 lifr.lifr_addrlen);
3446 laddr6 = sin6;
3447 } else {
3448 (void) printf("\tinet6 ");
3450 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3451 if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3452 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3453 errno == ENXIO) {
3454 if (!force)
3455 return;
3456 (void) memset(&lifr.lifr_addr, 0,
3457 sizeof (lifr.lifr_addr));
3458 } else
3459 Perror0_exit("in_status6: SIOCGLIFSUBNET");
3461 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3462 if ((flags & IFF_NOLOCAL) ||
3463 !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
3464 (void) printf("subnet %s/%d ",
3465 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3466 abuf, sizeof (abuf)),
3467 lifr.lifr_addrlen);
3469 if (sin6->sin6_family != AF_INET6) {
3470 (void) printf("Wrong family: %d\n", sin6->sin6_family);
3472 if (flags & IFF_POINTOPOINT) {
3473 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3474 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3475 if (errno == EADDRNOTAVAIL)
3476 (void) memset(&lifr.lifr_addr, 0,
3477 sizeof (lifr.lifr_addr));
3478 else
3479 Perror0_exit("in_status6: SIOCGLIFDSTADDR");
3481 sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
3482 (void) printf("--> %s ",
3483 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3484 abuf, sizeof (abuf)));
3486 if (verbose) {
3487 (void) putchar('\n');
3488 (void) putchar('\t');
3489 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3490 if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
3491 if (errno == EADDRNOTAVAIL || errno == EINVAL)
3492 (void) memset(&lifr.lifr_addr, 0,
3493 sizeof (lifr.lifr_addr));
3494 else
3495 Perror0_exit("in_status6: SIOCGLIFTOKEN");
3496 } else {
3497 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3498 (void) printf("token %s/%d ",
3499 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3500 abuf, sizeof (abuf)),
3501 lifr.lifr_addrlen);
3503 if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
3504 if (errno != EINVAL) {
3505 Perror0_exit("in_status6: SIOCGLIFLNKINFO");
3507 } else {
3508 (void) printf("maxhops %u, reachtime %u ms, "
3509 "reachretrans %u ms, maxmtu %u ",
3510 lifr.lifr_ifinfo.lir_maxhops,
3511 lifr.lifr_ifinfo.lir_reachtime,
3512 lifr.lifr_ifinfo.lir_reachretrans,
3513 lifr.lifr_ifinfo.lir_maxmtu);
3516 /* If there is a groupname, print it for only the physical interface */
3517 if (strchr(name, ':') == NULL) {
3518 if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3519 lifr.lifr_groupname[0] != '\0') {
3520 (void) printf("\n\tgroupname %s", lifr.lifr_groupname);
3523 (void) putchar('\n');
3526 static void
3527 in_configinfo(int force, uint64_t flags)
3529 struct sockaddr_in *sin, *laddr;
3530 struct sockaddr_in netmask = { AF_INET };
3532 if (debug)
3533 (void) printf("in_configinfo(%s) flags 0x%llx\n", name, flags);
3535 /* only configinfo info for IPv4 interfaces */
3536 if (!(flags & IFF_IPV4))
3537 return;
3539 if (!(flags & IFF_NOLOCAL)) {
3540 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3541 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3542 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3543 errno == ENXIO) {
3544 if (!force)
3545 return;
3546 (void) memset(&lifr.lifr_addr, 0,
3547 sizeof (lifr.lifr_addr));
3548 } else
3549 Perror0_exit("in_configinfo: SIOCGLIFADDR");
3551 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3552 if (strchr(name, ':') != NULL) {
3553 (void) printf(" addif %s ", inet_ntoa(sin->sin_addr));
3554 } else {
3555 (void) printf(" set %s ", inet_ntoa(sin->sin_addr));
3557 laddr = sin;
3560 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3561 if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3562 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3563 errno == ENXIO) {
3564 if (!force)
3565 return;
3566 (void) memset(&lifr.lifr_addr, 0,
3567 sizeof (lifr.lifr_addr));
3568 } else {
3569 Perror0_exit("in_configinfo: SIOCGLIFSUBNET");
3572 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3574 if ((flags & IFF_NOLOCAL) ||
3575 sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
3576 (void) printf(" subnet %s/%d ", inet_ntoa(sin->sin_addr),
3577 lifr.lifr_addrlen);
3579 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3580 if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
3581 if (errno != EADDRNOTAVAIL)
3582 Perror0_exit("in_configinfo: SIOCGLIFNETMASK");
3583 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
3584 } else
3585 netmask.sin_addr =
3586 ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
3587 if (flags & IFF_POINTOPOINT) {
3588 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3589 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3590 if (errno == EADDRNOTAVAIL)
3591 (void) memset(&lifr.lifr_addr, 0,
3592 sizeof (lifr.lifr_addr));
3593 else
3594 Perror0_exit("in_configinfo: SIOCGLIFDSTADDR");
3596 sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
3597 (void) printf(" destination %s ", inet_ntoa(sin->sin_addr));
3599 (void) printf(" netmask 0x%x ", ntohl(netmask.sin_addr.s_addr));
3600 if (flags & IFF_BROADCAST) {
3601 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3602 if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
3603 if (errno == EADDRNOTAVAIL)
3604 (void) memset(&lifr.lifr_addr, 0,
3605 sizeof (lifr.lifr_addr));
3606 else
3607 Perror0_exit("in_configinfo: SIOCGLIFBRDADDR");
3609 sin = (struct sockaddr_in *)&lifr.lifr_addr;
3610 if (sin->sin_addr.s_addr != 0) {
3611 (void) printf(" broadcast %s ",
3612 inet_ntoa(sin->sin_addr));
3616 /* If there is a groupname, print it for only the physical interface */
3617 if (strchr(name, ':') == NULL) {
3618 if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3619 lifr.lifr_groupname[0] != '\0') {
3620 (void) printf(" group %s ", lifr.lifr_groupname);
3624 /* Print flags to configure */
3625 print_config_flags(flags);
3627 /* IFF_NOARP applies to AF_INET only */
3628 if (flags & IFF_NOARP) {
3629 (void) printf("-arp ");
3633 static void
3634 in6_configinfo(int force, uint64_t flags)
3636 char abuf[INET6_ADDRSTRLEN];
3637 struct sockaddr_in6 *sin6, *laddr6;
3639 if (debug)
3640 (void) printf("in6_configinfo(%s) flags 0x%llx\n", name,
3641 flags);
3643 if (!(flags & IFF_IPV6))
3644 return;
3646 if (!(flags & IFF_NOLOCAL)) {
3647 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3648 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3649 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3650 errno == ENXIO) {
3651 if (!force)
3652 return;
3653 (void) memset(&lifr.lifr_addr, 0,
3654 sizeof (lifr.lifr_addr));
3655 } else
3656 Perror0_exit("in6_configinfo: SIOCGLIFADDR");
3658 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3659 if (strchr(name, ':') != NULL) {
3660 (void) printf(" addif %s/%d ",
3661 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3662 abuf, sizeof (abuf)),
3663 lifr.lifr_addrlen);
3664 } else {
3665 (void) printf(" set %s/%d ",
3666 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3667 abuf, sizeof (abuf)),
3668 lifr.lifr_addrlen);
3670 laddr6 = sin6;
3672 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3673 if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3674 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3675 errno == ENXIO) {
3676 if (!force)
3677 return;
3678 (void) memset(&lifr.lifr_addr, 0,
3679 sizeof (lifr.lifr_addr));
3680 } else
3681 Perror0_exit("in6_configinfo: SIOCGLIFSUBNET");
3683 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3684 if ((flags & IFF_NOLOCAL) ||
3685 !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
3686 (void) printf(" subnet %s/%d ",
3687 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3688 abuf, sizeof (abuf)),
3689 lifr.lifr_addrlen);
3692 if (flags & IFF_POINTOPOINT) {
3693 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3694 if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3695 if (errno == EADDRNOTAVAIL)
3696 (void) memset(&lifr.lifr_addr, 0,
3697 sizeof (lifr.lifr_addr));
3698 else
3699 Perror0_exit("in6_configinfo: SIOCGLIFDSTADDR");
3701 sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
3702 (void) printf(" destination %s ",
3703 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3704 abuf, sizeof (abuf)));
3707 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3708 if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
3709 if (errno == EADDRNOTAVAIL || errno == EINVAL)
3710 (void) memset(&lifr.lifr_addr, 0,
3711 sizeof (lifr.lifr_addr));
3712 else
3713 Perror0_exit("in6_configinfo: SIOCGLIFTOKEN");
3714 } else {
3715 sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3716 (void) printf(" token %s/%d ",
3717 inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3718 abuf, sizeof (abuf)),
3719 lifr.lifr_addrlen);
3722 /* If there is a groupname, print it for only the physical interface */
3723 if (strchr(name, ':') == NULL) {
3724 if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3725 lifr.lifr_groupname[0] != '\0') {
3726 (void) printf(" group %s ", lifr.lifr_groupname);
3730 /* Print flags to configure */
3731 print_config_flags(flags);
3733 /* IFF_NONUD applies to AF_INET6 only */
3734 if (flags & IFF_NONUD) {
3735 (void) printf("-nud ");
3740 * We need to plink both the arp-device stream and the arp-ip-device stream.
3741 * However the muxid is stored only in IP. Plumbing 2 streams individually
3742 * is not atomic, and if ifconfig is killed, the resulting plumbing can
3743 * be inconsistent. For eg. if only the arp stream is plumbed, we have lost
3744 * the muxid, and the half-baked plumbing can neither be unplumbed nor
3745 * replumbed, thus requiring a reboot. To avoid the above the following
3746 * scheme is used.
3748 * Ifconfig asks IP to enforce atomicity of plumbing the arp and IP streams.
3749 * This is done by pushing arp on to the mux (/dev/udp). ARP adds some
3750 * extra information in the I_PLINK and I_PUNLINK ioctls to let IP know
3751 * that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs
3752 * the IP stream first, and unplumbs it last. The kernel (IP) does not
3753 * allow IP stream to be unplumbed without unplumbing arp stream. Similarly
3754 * it does not allow arp stream to be plumbed before IP stream is plumbed.
3755 * There is no need to use SIOCSLIFMUXID, since the whole operation is atomic,
3756 * and IP uses the info in the I_PLINK message to get the muxid.
3758 * a. STREAMS does not allow us to use /dev/ip itself as the mux. So we use
3759 * /dev/udp{,6}.
3760 * b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream
3761 * depending on the open i.e. V4 vs V6 open. So we need to use /dev/udp
3762 * or /dev/udp6 for SIOCGLIFMUXID and SIOCSLIFMUXID.
3763 * c. We need to push ARP in order to get the required kernel support for
3764 * atomic plumbings. The actual work done by ARP is explained in arp.c
3765 * Without pushing ARP, we will still be able to plumb/unplumb. But
3766 * it is not atomic, and is supported by the kernel for backward
3767 * compatibility for other utilities like atmifconfig etc. In this case
3768 * the utility must use SIOCSLIFMUXID.
3770 static void
3771 plumb_one_device(int af)
3773 int arp_muxid = -1, ip_muxid;
3774 int mux_fd, ip_fd, arp_fd;
3775 int retval;
3776 uint_t ppa;
3777 char *udp_dev_name;
3778 char provider[DLPI_LINKNAME_MAX];
3779 dlpi_handle_t dh_arp, dh_ip;
3782 * We use DLPI_NOATTACH because the ip module will do the attach
3783 * itself for DLPI style-2 devices.
3785 retval = dlpi_open(name, &dh_ip, DLPI_NOATTACH);
3786 if (retval != DLPI_SUCCESS)
3787 Perrdlpi_exit("cannot open link", name, retval);
3789 if ((retval = dlpi_parselink(name, provider, &ppa)) != DLPI_SUCCESS)
3790 Perrdlpi_exit("dlpi_parselink", name, retval);
3792 if (debug) {
3793 (void) printf("ifconfig: plumb_one_device: provider %s,"
3794 " ppa %u\n", provider, ppa);
3797 ip_fd = dlpi_fd(dh_ip);
3798 if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1)
3799 Perror2_exit("I_PUSH", IP_MOD_NAME);
3802 * Push the ARP module onto the interface stream. IP uses
3803 * this to send resolution requests up to ARP. We need to
3804 * do this before the SLIFNAME ioctl is sent down because
3805 * the interface becomes publicly known as soon as the SLIFNAME
3806 * ioctl completes. Thus some other process trying to bring up
3807 * the interface after SLIFNAME but before we have pushed ARP
3808 * could hang. We pop the module again later if it is not needed.
3810 if (ioctl(ip_fd, I_PUSH, ARP_MOD_NAME) == -1)
3811 Perror2_exit("I_PUSH", ARP_MOD_NAME);
3814 * Set IFF_IPV4/IFF_IPV6 flags.
3815 * At this point in time the kernel also allows an
3816 * override of the CANTCHANGE flags.
3818 lifr.lifr_name[0] = '\0';
3819 if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1)
3820 Perror0_exit("plumb_one_device: SIOCGLIFFLAGS");
3822 /* Set the name string and the IFF_IPV* flag */
3823 if (af == AF_INET6) {
3824 lifr.lifr_flags |= IFF_IPV6;
3825 lifr.lifr_flags &= ~(IFF_BROADCAST | IFF_IPV4);
3826 } else {
3827 lifr.lifr_flags |= IFF_IPV4;
3828 lifr.lifr_flags &= ~IFF_IPV6;
3831 /* record the device and module names as interface name */
3832 lifr.lifr_ppa = ppa;
3833 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3835 /* set the interface name */
3836 if (ioctl(ip_fd, SIOCSLIFNAME, (char *)&lifr) == -1) {
3837 if (errno != EEXIST)
3838 Perror0_exit("SIOCSLIFNAME for ip");
3840 * This difference between the way we behave for EEXIST
3841 * and that with other errors exists to preserve legacy
3842 * behaviour. Earlier when foreachinterface() and matchif()
3843 * were doing the duplicate interface name checks, for
3844 * already existing interfaces, inetplumb() returned "0".
3845 * To preserve this behaviour, Perror0() and return are
3846 * called for EEXIST.
3848 Perror0("SIOCSLIFNAME for ip");
3849 return;
3852 /* Get the full set of existing flags for this stream */
3853 if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1)
3854 Perror0_exit("plumb_one_device: SIOCFLIFFLAGS");
3856 if (debug) {
3857 (void) printf("ifconfig: plumb_one_device: %s got flags:\n",
3858 lifr.lifr_name);
3859 print_flags(lifr.lifr_flags);
3860 (void) putchar('\n');
3863 /* Check if arp is not actually needed */
3864 if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
3865 if (ioctl(ip_fd, I_POP, 0) == -1)
3866 Perror2_exit("I_POP", ARP_MOD_NAME);
3870 * Open "/dev/udp" for use as a multiplexor to PLINK the
3871 * interface stream under. We use "/dev/udp" instead of "/dev/ip"
3872 * since STREAMS will not let you PLINK a driver under itself,
3873 * and "/dev/ip" is typically the driver at the bottom of
3874 * the stream for tunneling interfaces.
3876 if (af == AF_INET6)
3877 udp_dev_name = UDP6_DEV_NAME;
3878 else
3879 udp_dev_name = UDP_DEV_NAME;
3880 if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1)
3881 exit(EXIT_FAILURE);
3883 /* Check if arp is not needed */
3884 if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
3886 * PLINK the interface stream so that ifconfig can exit
3887 * without tearing down the stream.
3889 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
3890 Perror0_exit("I_PLINK for ip");
3891 (void) close(mux_fd);
3892 return;
3896 * This interface does use ARP, so set up a separate stream
3897 * from the interface to ARP.
3899 * Note: modules specified by the user are pushed
3900 * only on the interface stream, not on the ARP stream.
3902 if (debug)
3903 (void) printf("ifconfig: plumb_one_device: ifname: %s\n", name);
3906 * We use DLPI_NOATTACH because the arp module will do the attach
3907 * itself for DLPI style-2 devices.
3909 retval = dlpi_open(name, &dh_arp, DLPI_NOATTACH);
3910 if (retval != DLPI_SUCCESS)
3911 Perrdlpi_exit("cannot open link", name, retval);
3913 arp_fd = dlpi_fd(dh_arp);
3914 if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1)
3915 Perror2_exit("I_PUSH", ARP_MOD_NAME);
3918 * Tell ARP the name and unit number for this interface.
3919 * Note that arp has no support for transparent ioctls.
3921 if (strioctl(arp_fd, SIOCSLIFNAME, (char *)&lifr,
3922 sizeof (lifr)) == -1) {
3923 if (errno != EEXIST)
3924 Perror0_exit("SIOCSLIFNAME for arp");
3925 Perror0("SIOCSLIFNAME for arp");
3926 dlpi_close(dh_arp);
3927 dlpi_close(dh_ip);
3928 (void) close(mux_fd);
3929 return;
3932 * PLINK the IP and ARP streams so that ifconfig can exit
3933 * without tearing down the stream.
3935 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
3936 Perror0_exit("I_PLINK for ip");
3937 if ((arp_muxid = ioctl(mux_fd, I_PLINK, arp_fd)) == -1) {
3938 (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
3939 Perror0_exit("I_PLINK for arp");
3942 if (debug)
3943 (void) printf("arp muxid = %d\n", arp_muxid);
3944 dlpi_close(dh_ip);
3945 dlpi_close(dh_arp);
3946 (void) close(mux_fd);
3951 * If this is a physical interface then remove it.
3952 * If it is a logical interface name use SIOCLIFREMOVEIF to
3953 * remove it. In both cases fail if it doesn't exist.
3955 /* ARGSUSED */
3956 static int
3957 inetunplumb(char *arg, int64_t param)
3959 int ip_muxid, arp_muxid;
3960 int mux_fd;
3961 int muxid_fd;
3962 char *udp_dev_name;
3963 char *strptr;
3964 uint64_t flags;
3965 boolean_t changed_arp_muxid = _B_FALSE;
3966 int save_errno;
3968 strptr = strchr(name, ':');
3969 if (strptr != NULL || strcmp(name, LOOPBACK_IF) == 0) {
3970 /* Can't unplumb logical interface zero */
3971 if (strptr != NULL && strcmp(strptr, ":0") == 0) {
3972 (void) fprintf(stderr, "ifconfig: unplumb:"
3973 " Cannot unplumb %s: Invalid interface\n", name);
3974 exit(1);
3976 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3977 (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
3979 if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0)
3980 Perror0_exit("unplumb: SIOCLIFREMOVEIF");
3981 return (0);
3985 * We used /dev/udp or udp6 to set up the mux. So we have to use
3986 * the same now for PUNLINK also.
3988 if (afp->af_af == AF_INET6)
3989 udp_dev_name = UDP6_DEV_NAME;
3990 else
3991 udp_dev_name = UDP_DEV_NAME;
3993 if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1)
3994 exit(EXIT_FAILURE);
3996 if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1)
3997 exit(EXIT_FAILURE);
3999 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
4000 if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
4001 Perror0_exit("unplumb: SIOCGLIFFLAGS");
4003 flags = lifr.lifr_flags;
4004 if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
4005 Perror0_exit("unplumb: SIOCGLIFMUXID");
4007 arp_muxid = lifr.lifr_arp_muxid;
4008 ip_muxid = lifr.lifr_ip_muxid;
4010 * We don't have a good way of knowing whether the arp stream is
4011 * plumbed. We can't rely on IFF_NOARP because someone could
4012 * have turned it off later using "ifconfig xxx -arp".
4014 if (arp_muxid != 0) {
4015 if (debug)
4016 (void) printf("arp_muxid %d\n", arp_muxid);
4017 if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
4018 if ((errno == EINVAL) &&
4019 (flags & (IFF_NOARP | IFF_IPV6))) {
4021 * Some plumbing utilities set the muxid to
4022 * -1 or some invalid value to signify that
4023 * there is no arp stream. Set the muxid to 0
4024 * before trying to unplumb the IP stream.
4025 * IP does not allow the IP stream to be
4026 * unplumbed if it sees a non-null arp muxid,
4027 * for consistency of IP-ARP streams.
4029 lifr.lifr_arp_muxid = 0;
4030 (void) ioctl(muxid_fd, SIOCSLIFMUXID,
4031 (caddr_t)&lifr);
4032 changed_arp_muxid = _B_TRUE;
4033 } else {
4034 Perror0("I_PUNLINK for arp");
4038 if (debug)
4039 (void) printf("ip_muxid %d\n", ip_muxid);
4041 if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
4042 if (changed_arp_muxid) {
4044 * Some error occurred, and we need to restore
4045 * everything back to what it was.
4047 save_errno = errno;
4048 lifr.lifr_arp_muxid = arp_muxid;
4049 lifr.lifr_ip_muxid = ip_muxid;
4050 (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
4051 errno = save_errno;
4053 Perror0_exit("I_PUNLINK for ip");
4055 (void) close(mux_fd);
4056 (void) close(muxid_fd);
4057 return (0);
4061 * If this is a physical interface then create it unless it is already
4062 * present. If it is a logical interface name use SIOCLIFADDIF to
4063 * create and (and fail it if already exists.)
4064 * As a special case send SIOCLIFADDIF for the loopback interface. This
4065 * is needed since there is no other notion of plumbing the loopback
4066 * interface.
4068 /* ARGSUSED */
4069 static int
4070 inetplumb(char *arg, int64_t param)
4072 char *strptr;
4073 boolean_t islo;
4074 zoneid_t zoneid;
4076 strptr = strchr(name, ':');
4077 islo = (strcmp(name, LOOPBACK_IF) == 0);
4079 if (strptr != NULL || islo) {
4080 (void) memset(&lifr, 0, sizeof (lifr));
4081 (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
4082 if (islo && ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0) {
4083 if (debug) {
4084 (void) fprintf(stderr,
4085 "ifconfig: %s already exists\n", name);
4087 return (0);
4089 if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
4090 if (errno == EEXIST) {
4091 if (debug) {
4092 (void) fprintf(stderr,
4093 "ifconfig: %s already exists\n",
4094 name);
4096 } else {
4097 Perror2_exit("plumb: SIOCLIFADDIF", name);
4101 * IP can create the new logical interface on a different
4102 * physical interface in the same IPMP group. Take the new
4103 * interface into account for further operations.
4105 (void) strncpy(name, lifr.lifr_name, sizeof (name));
4106 return (0);
4110 * For global zone, check if the interface is used by a non-global
4111 * zone, note that the non-global zones doesn't need this check,
4112 * because zoneadm has taken care of this when the zone boots.
4114 zoneid = getzoneid();
4115 if (zoneid == GLOBAL_ZONEID) {
4116 int ret;
4118 zoneid = ALL_ZONES;
4119 ret = zone_check_datalink(&zoneid, name);
4120 if (ret == 0) {
4121 char zonename[ZONENAME_MAX];
4123 (void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX);
4124 (void) fprintf(stderr, "%s is used by non-global"
4125 "zone: %s\n", name, zonename);
4126 return (1);
4130 if (debug)
4131 (void) printf("inetplumb: %s af %d\n", name, afp->af_af);
4133 plumb_one_device(afp->af_af);
4134 return (0);
4137 void
4138 Perror0(const char *cmd)
4140 Perror2(cmd, lifr.lifr_name);
4143 void
4144 Perror0_exit(const char *cmd)
4146 Perror0(cmd);
4147 exit(1);
4148 /* NOTREACHED */
4151 void
4152 Perror2(const char *cmd, const char *str)
4154 int error = errno;
4156 (void) fprintf(stderr, "ifconfig: %s: ", cmd);
4158 switch (error) {
4159 case ENXIO:
4160 (void) fprintf(stderr, "%s: no such interface\n", str);
4161 break;
4162 case EPERM:
4163 (void) fprintf(stderr, "%s: permission denied\n", str);
4164 break;
4165 case EEXIST:
4166 (void) fprintf(stderr, "%s: already exists\n", str);
4167 break;
4168 default:
4169 errno = error;
4170 perror(str);
4175 * Print out error message (Perror2()) and exit
4177 void
4178 Perror2_exit(const char *cmd, const char *str)
4180 Perror2(cmd, str);
4181 exit(1);
4182 /* NOTREACHED */
4185 void
4186 Perrdlpi(const char *cmd, const char *linkname, int err)
4188 (void) fprintf(stderr, "ifconfig: %s \"%s\": %s\n", cmd,
4189 linkname, dlpi_strerror(err));
4193 * Print out error message (Perrdlpi()) and exit
4195 void
4196 Perrdlpi_exit(const char *cmd, const char *linkname, int err)
4198 Perrdlpi(cmd, linkname, err);
4199 exit(1);
4203 * If the last argument is non-NULL allow a <addr>/<n> syntax and
4204 * pass out <n> in *plenp.
4205 * If <n> doesn't parse return BAD_ADDR as *plenp.
4206 * If no /<n> is present return NO_PREFIX as *plenp.
4208 static void
4209 in_getaddr(char *s, struct sockaddr *saddr, int *plenp)
4211 /* LINTED: alignment */
4212 struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
4213 struct hostent *hp;
4214 struct netent *np;
4215 char str[BUFSIZ];
4216 int error_num;
4218 (void) strncpy(str, s, sizeof (str));
4221 * Look for '/'<n> is plenp
4223 if (plenp != NULL) {
4224 char *cp;
4226 *plenp = in_getprefixlen(str, _B_TRUE, IP_ABITS);
4227 if (*plenp == BAD_ADDR)
4228 return;
4229 cp = strchr(str, '/');
4230 if (cp != NULL)
4231 *cp = '\0';
4232 } else if (strchr(str, '/') != NULL) {
4233 (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
4234 exit(1);
4237 (void) memset(sin, 0, sizeof (*sin));
4240 * Try to catch attempts to set the broadcast address to all 1's.
4242 if (strcmp(str, "255.255.255.255") == 0 ||
4243 (strtoul(str, (char **)NULL, 0) == 0xffffffffUL)) {
4244 sin->sin_family = AF_INET;
4245 sin->sin_addr.s_addr = 0xffffffff;
4246 return;
4249 hp = getipnodebyname(str, AF_INET, 0, &error_num);
4250 if (hp) {
4251 sin->sin_family = hp->h_addrtype;
4252 (void) memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
4253 freehostent(hp);
4254 return;
4256 np = getnetbyname(str);
4257 if (np) {
4258 sin->sin_family = np->n_addrtype;
4259 sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
4260 return;
4262 if (error_num == TRY_AGAIN) {
4263 (void) fprintf(stderr, "ifconfig: %s: bad address "
4264 "(try again later)\n", s);
4265 } else {
4266 (void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
4268 exit(1);
4272 * If the last argument is non-NULL allow a <addr>/<n> syntax and
4273 * pass out <n> in *plenp.
4274 * If <n> doesn't parse return BAD_ADDR as *plenp.
4275 * If no /<n> is present return NO_PREFIX as *plenp.
4277 static void
4278 in6_getaddr(char *s, struct sockaddr *saddr, int *plenp)
4280 /* LINTED: alignment */
4281 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
4282 struct hostent *hp;
4283 char str[BUFSIZ];
4284 int error_num;
4286 (void) strncpy(str, s, sizeof (str));
4289 * Look for '/'<n> is plenp
4291 if (plenp != NULL) {
4292 char *cp;
4294 *plenp = in_getprefixlen(str, _B_TRUE, IPV6_ABITS);
4295 if (*plenp == BAD_ADDR)
4296 return;
4297 cp = strchr(str, '/');
4298 if (cp != NULL)
4299 *cp = '\0';
4300 } else if (strchr(str, '/') != NULL) {
4301 (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
4302 exit(1);
4305 (void) memset(sin6, 0, sizeof (*sin6));
4307 hp = getipnodebyname(str, AF_INET6, 0, &error_num);
4308 if (hp) {
4309 sin6->sin6_family = hp->h_addrtype;
4310 (void) memcpy(&sin6->sin6_addr, hp->h_addr, hp->h_length);
4311 freehostent(hp);
4312 return;
4314 if (error_num == TRY_AGAIN) {
4315 (void) fprintf(stderr, "ifconfig: %s: bad address "
4316 "(try again later)\n", s);
4317 } else {
4318 (void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
4320 exit(1);
4324 * If "slash" is zero this parses the whole string as
4325 * an integer. With "slash" non zero it parses the tail part as an integer.
4327 * If it is not a valid integer this returns BAD_ADDR.
4328 * If there is /<n> present this returns NO_PREFIX.
4330 static int
4331 in_getprefixlen(char *addr, boolean_t slash, int max_plen)
4333 int prefixlen;
4334 char *str, *end;
4336 if (slash) {
4337 str = strchr(addr, '/');
4338 if (str == NULL)
4339 return (NO_PREFIX);
4340 str++;
4341 } else
4342 str = addr;
4344 prefixlen = strtol(str, &end, 10);
4345 if (prefixlen < 0)
4346 return (BAD_ADDR);
4347 if (str == end)
4348 return (BAD_ADDR);
4349 if (max_plen != 0 && max_plen < prefixlen)
4350 return (BAD_ADDR);
4351 return (prefixlen);
4355 * Convert a prefix length to a mask.
4356 * Returns 1 if ok. 0 otherwise.
4357 * Assumes the mask array is zero'ed by the caller.
4359 static boolean_t
4360 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
4362 if (prefixlen < 0 || prefixlen > maxlen)
4363 return (0);
4365 while (prefixlen > 0) {
4366 if (prefixlen >= 8) {
4367 *mask++ = 0xFF;
4368 prefixlen -= 8;
4369 continue;
4371 *mask |= 1 << (8 - prefixlen);
4372 prefixlen--;
4374 return (1);
4377 static void
4378 print_flags(uint64_t flags)
4380 boolean_t first = _B_TRUE;
4381 int cnt, i;
4383 (void) printf("flags=%llx", flags);
4384 cnt = sizeof (if_flags_tbl) / sizeof (if_flags_t);
4385 for (i = 0; i < cnt; i++) {
4386 if (flags & if_flags_tbl[i].iff_value) {
4387 if (first) {
4388 (void) printf("<");
4389 first = _B_FALSE;
4390 } else {
4392 * It has to be here and not with the
4393 * printf below because for the last one,
4394 * we don't want a comma before the ">".
4396 (void) printf(",");
4398 (void) printf("%s", if_flags_tbl[i].iff_name);
4401 if (!first)
4402 (void) printf(">");
4405 static void
4406 print_config_flags(uint64_t flags)
4408 int cnt, i;
4410 cnt = sizeof (if_config_cmd_tbl) / sizeof (if_config_cmd_t);
4411 for (i = 0; i < cnt; i++) {
4412 if (flags & if_config_cmd_tbl[i].iff_flag) {
4413 (void) printf("%s ", if_config_cmd_tbl[i].iff_name);
4419 * Use the configured directory lookup mechanism (e.g. files/NIS/NIS+/...)
4420 * to find the network mask. Returns true if we found one to set.
4422 * The parameter addr_set controls whether we should get the address of
4423 * the working interface for the netmask query. If addr_set is true,
4424 * we will use the address provided. Otherwise, we will find the working
4425 * interface's address and use it instead.
4427 static boolean_t
4428 in_getmask(struct sockaddr_in *saddr, boolean_t addr_set)
4430 struct sockaddr_in ifaddr;
4433 * Read the address from the interface if it is not passed in.
4435 if (!addr_set) {
4436 (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
4437 if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
4438 if (errno != EADDRNOTAVAIL) {
4439 (void) fprintf(stderr, "Need net number for "
4440 "mask\n");
4442 return (_B_FALSE);
4444 ifaddr = *((struct sockaddr_in *)&lifr.lifr_addr);
4445 } else {
4446 ifaddr.sin_addr = saddr->sin_addr;
4448 if (getnetmaskbyaddr(ifaddr.sin_addr, &saddr->sin_addr) == 0) {
4449 saddr->sin_family = AF_INET;
4450 return (_B_TRUE);
4452 return (_B_FALSE);
4455 static int
4456 strioctl(int s, int cmd, char *buf, int buflen)
4458 struct strioctl ioc;
4460 (void) memset(&ioc, 0, sizeof (ioc));
4461 ioc.ic_cmd = cmd;
4462 ioc.ic_timout = 0;
4463 ioc.ic_len = buflen;
4464 ioc.ic_dp = buf;
4465 return (ioctl(s, I_STR, (char *)&ioc));
4468 static void
4469 add_ni(const char *name)
4471 ni_t **pp;
4472 ni_t *p;
4474 for (pp = &ni_list; (p = *pp) != NULL; pp = &(p->ni_next)) {
4475 if (strcmp(p->ni_name, name) == 0) {
4476 if (debug > 2)
4477 (void) fprintf(stderr, "'%s' is a duplicate\n",
4478 name);
4479 return;
4483 if (debug > 2)
4484 (void) fprintf(stderr, "adding '%s'\n",
4485 name);
4487 if ((p = malloc(sizeof (ni_t))) == NULL)
4488 return;
4490 (void) strlcpy(p->ni_name, name, sizeof (p->ni_name));
4491 p->ni_next = NULL;
4493 *pp = p;
4494 num_ni++;
4497 /* ARGSUSED2 */
4498 static boolean_t
4499 ni_entry(const char *linkname, void *arg)
4501 dlpi_handle_t dh;
4503 if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS)
4504 return (_B_FALSE);
4506 add_ni(linkname);
4508 dlpi_close(dh);
4509 return (_B_FALSE);
4513 * dhcp-related routines
4516 static int
4517 setifdhcp(const char *caller, const char *ifname, int argc, char *argv[])
4519 dhcp_ipc_request_t *request;
4520 dhcp_ipc_reply_t *reply = NULL;
4521 int timeout = DHCP_IPC_WAIT_DEFAULT;
4522 dhcp_ipc_type_t type = DHCP_START;
4523 int error;
4524 boolean_t is_primary = _B_FALSE;
4525 boolean_t started = _B_FALSE;
4527 for (argv++; --argc > 0; argv++) {
4529 if (strcmp(*argv, "primary") == 0) {
4530 is_primary = _B_TRUE;
4531 continue;
4534 if (strcmp(*argv, "wait") == 0) {
4535 if (--argc <= 0) {
4536 usage();
4537 return (DHCP_EXIT_BADARGS);
4539 argv++;
4541 if (strcmp(*argv, "forever") == 0) {
4542 timeout = DHCP_IPC_WAIT_FOREVER;
4543 continue;
4546 if (sscanf(*argv, "%d", &timeout) != 1) {
4547 usage();
4548 return (DHCP_EXIT_BADARGS);
4551 if (timeout < 0) {
4552 usage();
4553 return (DHCP_EXIT_BADARGS);
4555 continue;
4558 type = dhcp_string_to_request(*argv);
4559 if (type == -1) {
4560 usage();
4561 return (DHCP_EXIT_BADARGS);
4566 * Only try to start agent on start or inform; in all other cases it
4567 * has to already be running for anything to make sense.
4569 if (type == DHCP_START || type == DHCP_INFORM) {
4570 if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) {
4571 (void) fprintf(stderr, "%s: unable to start %s\n",
4572 caller, DHCP_AGENT_PATH);
4573 return (DHCP_EXIT_FAILURE);
4575 started = _B_TRUE;
4578 if (is_primary)
4579 type |= DHCP_PRIMARY;
4581 if (af != AF_INET)
4582 type |= DHCP_V6;
4584 request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
4585 if (request == NULL) {
4586 (void) fprintf(stderr, "%s: out of memory\n", caller);
4587 return (DHCP_EXIT_SYSTEM);
4590 error = dhcp_ipc_make_request(request, &reply, timeout);
4591 if (error != 0) {
4592 free(request);
4594 * Re-map connect error to not under control if we didn't try a
4595 * start operation, as this has to be true and results in a
4596 * clearer message, not to mention preserving compatibility
4597 * with the days when we always started dhcpagent for every
4598 * request.
4600 if (error == DHCP_IPC_E_CONNECT && !started)
4601 error = DHCP_IPC_E_UNKIF;
4602 (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
4603 dhcp_ipc_strerror(error));
4604 return (DHCP_EXIT_FAILURE);
4607 error = reply->return_code;
4608 if (error != 0) {
4609 free(request);
4610 free(reply);
4612 if (error == DHCP_IPC_E_TIMEOUT && timeout == 0)
4613 return (DHCP_EXIT_SUCCESS);
4615 (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
4616 dhcp_ipc_strerror(error));
4618 if (error == DHCP_IPC_E_TIMEOUT)
4619 return (DHCP_EXIT_TIMEOUT);
4620 else
4621 return (DHCP_EXIT_IF_FAILURE);
4624 if (DHCP_IPC_CMD(type) == DHCP_STATUS) {
4625 (void) printf("%s", dhcp_status_hdr_string());
4626 (void) printf("%s", dhcp_status_reply_to_string(reply));
4629 free(request);
4630 free(reply);
4631 return (DHCP_EXIT_SUCCESS);
4634 static void
4635 usage(void)
4637 (void) fprintf(stderr,
4638 "usage: ifconfig <interface> | -a[ 4 | 6 | D ][ u | d ][ Z ]\n");
4640 (void) fprintf(stderr, "%s",
4641 "\t[ <addr_family> ]\n"
4642 "\t[ <address>[/<prefix_length>] [ <dest_address> ] ]\n"
4643 "\t[ set [ <address>][/<prefix_length>] ]"
4644 " [ <address>/<prefix_length>] ]\n"
4645 "\t[ destination <dest_address> ]\n"
4646 "\t[ addif <address>[/<prefix_length>]"
4647 " [ <dest_address> ] ]\n"
4648 "\t[ removeif <address>[/<prefix_length>] ]\n"
4649 "\t[ arp | -arp ]\n"
4650 "\t[ auto-revarp ]\n"
4651 "\t[ broadcast <broad_addr> ]\n"
4652 "\t[ index <if_index> ]\n"
4653 "\t[ metric <n> ] [ mtu <n> ]\n"
4654 "\t[ netmask <mask> ]\n"
4655 "\t[ plumb ] [ unplumb ]\n"
4656 "\t[ preferred | -preferred ]\n"
4657 "\t[ private | -private ]\n"
4658 "\t[ local | -local ]\n"
4659 "\t[ router | -router ]\n"
4660 "\t[ subnet <subnet_address>]\n"
4661 "\t[ trailers | -trailers ]\n"
4662 "\t[ token <address>/<prefix_length> ]\n"
4663 "\t[ tsrc <tunnel_src_address> ]\n"
4664 "\t[ tdst <tunnel_dest_address> ]\n"
4665 "\t[ auth_algs <tunnel_AH_authentication_algorithm> ]\n"
4666 "\t[ encr_algs <tunnel_ESP_encryption_algorithm> ]\n"
4667 "\t[ encr_auth_algs <tunnel_ESP_authentication_algorithm> ]\n"
4668 "\t[ up ] [ down ]\n"
4669 "\t[ xmit | -xmit ]\n"
4670 "\t[ modlist ]\n"
4671 "\t[ modinsert <module_name@position> ]\n"
4672 "\t[ modremove <module_name@position> ]\n"
4673 "\t[ group <groupname>] | [ group \"\"]\n"
4674 "\t[ deprecated | -deprecated ]\n"
4675 "\t[ standby | -standby ]\n"
4676 "\t[ failover | -failover ]\n"
4677 "\t[ zone <zonename> | -zone ]\n"
4678 "\t[ usesrc <interface> ]\n"
4679 "\t[ all-zones ]\n");
4681 (void) fprintf(stderr, "or\n");
4682 (void) fprintf(stderr,
4683 "\tifconfig <interface> | -a[ 4 | 6 | D ] [ u | d ]\n");
4685 (void) fprintf(stderr, "%s", "\tauto-dhcp | dhcp\n"
4686 "\t[ wait <time> | forever ]\n\t[ primary ]\n"
4687 "\tstart | drop | ping | release | status | inform\n");