xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c (revision 60b43c45a204a6c0651e8017e1e505015ba74094)
1 /*
2  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
3  * Copyright 2012, Daniil Lunev. All rights reserved.
4  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
5  */
6 /*
7  * Copyright (c) 1983 Regents of the University of California.
8  * All rights reserved.  The Berkeley software License Agreement
9  * specifies the terms and conditions for redistribution.
10  */
11 
12 #include "defs.h"
13 #include "strings.h"
14 #include "ifconfig.h"
15 #include <compat.h>
16 #include <libdlpi.h>
17 #include <libdllink.h>
18 #include <libdliptun.h>
19 #include <libdllink.h>
20 #include <inet/ip.h>
21 #include <inet/ipsec_impl.h>
22 #include <libipadm.h>
23 #include <ifaddrs.h>
24 #include <libsocket_priv.h>
25 
26 #define	LOOPBACK_IF	"lo0"
27 #define	NONE_STR	"none"
28 #define	ARP_MOD_NAME	"arp"
29 #define	LIFC_DEFAULT	(LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\
30 			LIFC_UNDER_IPMP)
31 
32 typedef struct if_flags {
33 	uint64_t iff_value;
34 	char	*iff_name;
35 } if_flags_t;
36 
37 static if_flags_t	if_flags_tbl[] = {
38 	{ IFF_UP,		"UP" },
39 	{ IFF_BROADCAST,	"BROADCAST" },
40 	{ IFF_DEBUG,		"DEBUG" },
41 	{ IFF_LOOPBACK,		"LOOPBACK" },
42 	{ IFF_POINTOPOINT,	"POINTOPOINT" },
43 	{ IFF_NOTRAILERS,	"NOTRAILERS" },
44 	{ IFF_RUNNING,		"RUNNING" },
45 	{ IFF_NOARP,		"NOARP" },
46 	{ IFF_PROMISC,		"PROMISC" },
47 	{ IFF_ALLMULTI,		"ALLMULTI" },
48 	{ IFF_INTELLIGENT,	"INTELLIGENT" },
49 	{ IFF_MULTICAST,	"MULTICAST" },
50 	{ IFF_MULTI_BCAST,	"MULTI_BCAST" },
51 	{ IFF_UNNUMBERED,	"UNNUMBERED" },
52 	{ IFF_DHCPRUNNING,	"DHCP" },
53 	{ IFF_PRIVATE,		"PRIVATE" },
54 	{ IFF_NOXMIT,		"NOXMIT" },
55 	{ IFF_NOLOCAL,		"NOLOCAL" },
56 	{ IFF_DEPRECATED,	"DEPRECATED" },
57 	{ IFF_ADDRCONF,		"ADDRCONF" },
58 	{ IFF_ROUTER,		"ROUTER" },
59 	{ IFF_NONUD,		"NONUD" },
60 	{ IFF_ANYCAST,		"ANYCAST" },
61 	{ IFF_NORTEXCH,		"NORTEXCH" },
62 	{ IFF_IPV4,		"IPv4" },
63 	{ IFF_IPV6,		"IPv6" },
64 	{ IFF_NOFAILOVER,	"NOFAILOVER" },
65 	{ IFF_FAILED,		"FAILED" },
66 	{ IFF_STANDBY,		"STANDBY" },
67 	{ IFF_INACTIVE,		"INACTIVE" },
68 	{ IFF_OFFLINE,		"OFFLINE" },
69 	{ IFF_XRESOLV,		"XRESOLV" },
70 	{ IFF_COS_ENABLED,	"CoS" },
71 	{ IFF_PREFERRED,	"PREFERRED" },
72 	{ IFF_TEMPORARY,	"TEMPORARY" },
73 	{ IFF_FIXEDMTU,		"FIXEDMTU" },
74 	{ IFF_VIRTUAL,		"VIRTUAL" },
75 	{ IFF_DUPLICATE,	"DUPLICATE" },
76 	{ IFF_IPMP,		"IPMP"},
77 	{ IFF_VRRP,		"VRRP"},
78 	{ IFF_NOACCEPT,		"NOACCEPT"},
79 	{ IFF_L3PROTECT,	"L3PROTECT"}
80 };
81 
82 typedef struct {
83 	const char		*ia_app;
84 	uint64_t		ia_flag;
85 	uint_t			ia_tries;
86 } if_appflags_t;
87 
88 static const if_appflags_t if_appflags_tbl[] = {
89 	{ "dhcpagent(1M)",	IFF_DHCPRUNNING,	1 },
90 	{ "in.ndpd(1M)",	IFF_ADDRCONF,		3 },
91 	{  NULL,		0,			0 }
92 };
93 
94 static dladm_handle_t	dlh;
95 boolean_t		dlh_opened;
96 static struct		lifreq lifr;
97 /* current interface name a particular function is accessing */
98 static char		name[LIFNAMSIZ];
99 /* foreach interface saved name */
100 static char		origname[LIFNAMSIZ];
101 static int		setaddr;
102 static boolean_t	setaddr_done = _B_FALSE;
103 static boolean_t	ipsec_policy_set;
104 static boolean_t	ipsec_auth_covered;
105 static ipadm_handle_t	iph;
106 static ipadm_addrobj_t	ipaddr;
107 
108 /*
109  * Make sure the algorithm variables hold more than the sizeof an algorithm
110  * in PF_KEY.  (For now, more than a uint8_t.)  The NO_***_?ALG indicates that
111  * there was no algorithm requested, and in the ipsec_req that service should
112  * be disabled.  (E.g. if ah_aalg remains NO_AH_AALG, then AH will be
113  * disabled on that tunnel.)
114  */
115 #define	NO_AH_AALG 256
116 #define	NO_ESP_AALG 256
117 #define	NO_ESP_EALG 256
118 
119 int	s, s4, s6;
120 int	af = AF_INET;	/* default address family */
121 int	debug = 0;
122 int	all = 0;	/* setifdhcp() needs to know this */
123 int	verbose = 0;
124 int	v4compat = 0;	/* Compatible printing format */
125 
126 /*
127  * Function prototypes for command functions.
128  */
129 static int	addif(char *arg, int64_t param);
130 static int	inetipmp(char *arg, int64_t param);
131 static int	inetplumb(char *arg, int64_t param);
132 static int	inetunplumb(char *arg, int64_t param);
133 static int	removeif(char *arg, int64_t param);
134 static int	setdebugflag(char *arg, int64_t param);
135 static int	setifaddr(char *arg, int64_t param);
136 static int	setifbroadaddr(char *arg, int64_t param);
137 static int	setifdstaddr(char *arg, int64_t param);
138 static int	setifether(char *arg, int64_t param);
139 static int	setifflags(char *arg, int64_t param);
140 static int	setifindex(char *arg, int64_t param);
141 static int	setifmetric(char *arg, int64_t param);
142 static int	setifmtu(char *arg, int64_t param);
143 static int	setifnetmask(char *arg, int64_t param);
144 static int	setifprefixlen(char *arg, int64_t param);
145 static int	setifrevarp(char *arg, int64_t param);
146 static int	setifsubnet(char *arg, int64_t param);
147 static int	setiftdst(char *arg, int64_t param);
148 static int	setiftoken(char *arg, int64_t param);
149 static int	setiftsrc(char *arg, int64_t param);
150 static int	setverboseflag(char *arg, int64_t param);
151 static int	set_tun_ah_alg(char *arg, int64_t param);
152 static int	set_tun_esp_auth_alg(char *arg, int64_t param);
153 static int	set_tun_esp_encr_alg(char *arg, int64_t param);
154 static int	modlist(char *arg, int64_t param);
155 static int	modinsert(char *arg, int64_t param);
156 static int	modremove(char *arg, int64_t param);
157 static int	setifgroupname(char *arg, int64_t param);
158 static int	configinfo(char *arg, int64_t param);
159 static void	print_config_flags(int af, uint64_t flags);
160 static void	print_flags(uint64_t flags);
161 static void	print_ifether(const char *ifname);
162 static int	set_tun_encap_limit(char *arg, int64_t param);
163 static int	clr_tun_encap_limit(char *arg, int64_t param);
164 static int	set_tun_hop_limit(char *arg, int64_t param);
165 static int	setzone(char *arg, int64_t param);
166 static int	setallzones(char *arg, int64_t param);
167 static int	setifsrc(char *arg, int64_t param);
168 static int	lifnum(const char *ifname);
169 static void	plumball(int, char **, int64_t, int64_t, int64_t);
170 
171 /*
172  * Address family specific function prototypes.
173  */
174 static void	in_getaddr(char *s, struct sockaddr *saddr, int *plenp);
175 static void	in_status(int force, uint64_t flags);
176 static void	in_configinfo(int force, uint64_t flags);
177 static void	in6_getaddr(char *s, struct sockaddr *saddr, int *plenp);
178 static void	in6_status(int force, uint64_t flags);
179 static void	in6_configinfo(int force, uint64_t flags);
180 
181 /*
182  * Misc support functions
183  */
184 static boolean_t	ni_entry(const char *, void *);
185 static void		foreachinterface(int argc, char *argv[],
186 			    int af, int64_t onflags, int64_t offflags,
187 			    int64_t lifc_flags);
188 static void		ifconfig(int argc, char *argv[], int af,
189 			    struct ifaddrs *ifa);
190 static boolean_t	in_getmask(struct sockaddr_in *saddr,
191 			    boolean_t addr_set);
192 static int		in_getprefixlen(char *addr, boolean_t slash, int plen);
193 static boolean_t	in_prefixlentomask(int prefixlen, int maxlen,
194 			    uchar_t *mask);
195 static void		status(void);
196 static void		ifstatus(const char *ifname);
197 static void		tun_status(datalink_id_t);
198 static void		usage(void);
199 static int		setifdhcp(const char *caller, const char *ifname,
200 			    int argc, char *argv[]);
201 static int		ip_domux2fd(int *, int *, int *, int *, int *);
202 static int		ip_plink(int, int, int, int, int);
203 static int		modop(char *arg, char op);
204 static int		find_all_interfaces(struct lifconf *lifcp, char **buf,
205 			    int64_t lifc_flags);
206 static int		create_ipmp(const char *grname, int af,
207 			    const char *ifname, boolean_t implicit);
208 static void		start_ipmp_daemon(void);
209 static boolean_t 	ifaddr_up(ifaddrlistx_t *ifaddrp);
210 static boolean_t 	ifaddr_down(ifaddrlistx_t *ifaddrp);
211 static dladm_status_t	ifconfig_dladm_open(const char *, datalink_class_t,
212 			    datalink_id_t *);
213 static void		dladmerr_exit(dladm_status_t status, const char *str);
214 static void		ipadmerr_exit(ipadm_status_t status, const char *str);
215 static boolean_t	ifconfig_use_libipadm(int, const char *);
216 
217 #define	max(a, b)	((a) < (b) ? (b) : (a))
218 
219 /*
220  * DHCP_EXIT_IF_FAILURE indicates that the operation failed, but if there
221  * are more interfaces to act on (i.e., ifconfig was invoked with -a), keep
222  * on going rather than exit with an error.
223  */
224 
225 #define	DHCP_EXIT_IF_FAILURE	-1
226 
227 #define	NEXTARG		0xffffff	/* command takes an argument */
228 #define	OPTARG		0xfffffe 	/* command takes an optional argument */
229 #define	AF_ANY		(-1)
230 
231 /* Refer to the comments in ifconfig() on the netmask "hack" */
232 #define	NETMASK_CMD	"netmask"
233 struct sockaddr_storage	g_netmask;
234 enum { G_NETMASK_NIL, G_NETMASK_PENDING, G_NETMASK_SET }
235     g_netmask_set = G_NETMASK_NIL;
236 
237 struct	cmd {
238 	char		*c_name;
239 	int64_t		c_parameter;	/* NEXTARG means next argv */
240 	int		(*c_func)(char *, int64_t);
241 	int		c_abortonfail;	/* don't continue parsing args */
242 					/* for the current interface */
243 	int	c_af;			/* address family restrictions */
244 } cmds[] = {
245 	{ "up",		IFF_UP,		setifflags,	0,	AF_ANY },
246 	{ "down",	-IFF_UP,	setifflags,	0,	AF_ANY },
247 	{ "trailers",	-IFF_NOTRAILERS, setifflags,	0,	AF_ANY },
248 	{ "-trailers",	IFF_NOTRAILERS,	setifflags,	0,	AF_ANY },
249 	{ "arp",	-IFF_NOARP,	setifflags,	0,	AF_INET },
250 	{ "-arp",	IFF_NOARP,	setifflags,	0,	AF_INET },
251 	{ "router",	IFF_ROUTER,	setifflags,	0,	AF_ANY },
252 	{ "-router",	-IFF_ROUTER,	setifflags,	0,	AF_ANY },
253 	{ "private",	IFF_PRIVATE,	setifflags,	0,	AF_ANY },
254 	{ "-private",	-IFF_PRIVATE,	setifflags,	0,	AF_ANY },
255 	{ "xmit",	-IFF_NOXMIT,	setifflags,	0,	AF_ANY },
256 	{ "-xmit",	IFF_NOXMIT,	setifflags,	0,	AF_ANY },
257 	{ "-nud",	IFF_NONUD,	setifflags,	0,	AF_INET6 },
258 	{ "nud",	-IFF_NONUD,	setifflags,	0,	AF_INET6 },
259 	{ "anycast",	IFF_ANYCAST,	setifflags,	0,	AF_ANY },
260 	{ "-anycast",	-IFF_ANYCAST,	setifflags,	0,	AF_ANY },
261 	{ "local",	-IFF_NOLOCAL,	setifflags,	0,	AF_ANY },
262 	{ "-local",	IFF_NOLOCAL,	setifflags,	0,	AF_ANY },
263 	{ "deprecated",	IFF_DEPRECATED,	setifflags,	0,	AF_ANY },
264 	{ "-deprecated", -IFF_DEPRECATED, setifflags,	0,	AF_ANY },
265 	{ "preferred",	IFF_PREFERRED,	setifflags,	0,	AF_INET6 },
266 	{ "-preferred",	-IFF_PREFERRED,	setifflags,	0,	AF_INET6 },
267 	{ "debug",	0,		setdebugflag,	0,	AF_ANY },
268 	{ "verbose",	0,		setverboseflag,	0,	AF_ANY },
269 	{ NETMASK_CMD,	NEXTARG,	setifnetmask,	0,	AF_INET },
270 	{ "metric",	NEXTARG,	setifmetric,	0,	AF_ANY },
271 	{ "mtu",	NEXTARG,	setifmtu,	0,	AF_ANY },
272 	{ "index",	NEXTARG,	setifindex,	0,	AF_ANY },
273 	{ "broadcast",	NEXTARG,	setifbroadaddr,	0,	AF_INET },
274 	{ "auto-revarp", 0,		setifrevarp,	1,	AF_INET },
275 	{ "ipmp",	0,		inetipmp,	1,	AF_ANY },
276 	{ "plumb",	0,		inetplumb,	1,	AF_ANY },
277 	{ "unplumb",	0,		inetunplumb,	0,	AF_ANY },
278 	{ "subnet",	NEXTARG,	setifsubnet,	0,	AF_ANY },
279 	{ "token",	NEXTARG,	setiftoken,	0,	AF_INET6 },
280 	{ "tsrc",	NEXTARG,	setiftsrc,	0,	AF_ANY },
281 	{ "tdst",	NEXTARG,	setiftdst,	0,	AF_ANY },
282 	{ "encr_auth_algs", NEXTARG,	set_tun_esp_auth_alg, 0, AF_ANY },
283 	{ "encr_algs",	NEXTARG,	set_tun_esp_encr_alg, 0, AF_ANY },
284 	{ "auth_algs",	NEXTARG,	set_tun_ah_alg,	0,	AF_ANY },
285 	{ "addif",	NEXTARG,	addif,		1,	AF_ANY },
286 	{ "removeif",	NEXTARG,	removeif,	1,	AF_ANY },
287 	{ "modlist",	0,		modlist,	1,	AF_ANY },
288 	{ "modinsert",	NEXTARG,	modinsert,	1,	AF_ANY },
289 	{ "modremove",	NEXTARG,	modremove,	1,	AF_ANY },
290 	{ "failover",	-IFF_NOFAILOVER, setifflags,	1,	AF_ANY },
291 	{ "-failover",	IFF_NOFAILOVER, setifflags,	1,	AF_ANY },
292 	{ "standby",	IFF_STANDBY,	setifflags,	1,	AF_ANY },
293 	{ "-standby",	-IFF_STANDBY,	setifflags,	1,	AF_ANY },
294 	{ "failed",	IFF_FAILED,	setifflags,	1,	AF_ANY },
295 	{ "-failed",	-IFF_FAILED,	setifflags,	1,	AF_ANY },
296 	{ "group",	NEXTARG,	setifgroupname,	1,	AF_ANY },
297 	{ "configinfo",	0,		configinfo,	1,	AF_ANY },
298 	{ "encaplimit",	NEXTARG,	set_tun_encap_limit,	0, AF_ANY },
299 	{ "-encaplimit", 0,		clr_tun_encap_limit,	0, AF_ANY },
300 	{ "thoplimit",	NEXTARG,	set_tun_hop_limit,	0, AF_ANY },
301 	{ "set",	NEXTARG,	setifaddr,	0,	AF_ANY },
302 	{ "destination", NEXTARG,	setifdstaddr,	0,	AF_ANY },
303 	{ "zone",	NEXTARG,	setzone,	0,	AF_ANY },
304 	{ "-zone",	0,		setzone,	0,	AF_ANY },
305 	{ "all-zones",	0,		setallzones,	0,	AF_ANY },
306 	{ "ether",	OPTARG,		setifether,	0,	AF_ANY },
307 	{ "usesrc",	NEXTARG,	setifsrc,	0,	AF_ANY },
308 
309 	/*
310 	 * NOTE: any additions to this table must also be applied to ifparse
311 	 *	(usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c)
312 	 */
313 
314 	{ 0,		0,		setifaddr,	0,	AF_ANY },
315 	{ 0,		0,		setifdstaddr,	0,	AF_ANY },
316 	{ 0,		0,		0,		0,	0 },
317 };
318 
319 
320 typedef struct if_config_cmd {
321 	uint64_t	iff_flag;
322 	int		iff_af;
323 	char		*iff_name;
324 } if_config_cmd_t;
325 
326 /*
327  * NOTE: print_config_flags() processes this table in order, so we put "up"
328  * last so that we can be sure "-failover" will take effect first.  Otherwise,
329  * IPMP test addresses will erroneously migrate to the IPMP interface.
330  */
331 static if_config_cmd_t	if_config_cmd_tbl[] = {
332 	{ IFF_NOTRAILERS,	AF_UNSPEC,	"-trailers"	},
333 	{ IFF_PRIVATE,		AF_UNSPEC,	"private"	},
334 	{ IFF_NOXMIT,		AF_UNSPEC,	"-xmit"		},
335 	{ IFF_ANYCAST,		AF_INET6,	"anycast"	},
336 	{ IFF_NOLOCAL,		AF_UNSPEC,	"-local"	},
337 	{ IFF_DEPRECATED,	AF_UNSPEC,	"deprecated"	},
338 	{ IFF_NOFAILOVER,	AF_UNSPEC,	"-failover"	},
339 	{ IFF_STANDBY,		AF_UNSPEC,	"standby"	},
340 	{ IFF_FAILED,		AF_UNSPEC,	"failed"	},
341 	{ IFF_PREFERRED,	AF_UNSPEC,	"preferred"	},
342 	{ IFF_NONUD,		AF_INET6,	"-nud"		},
343 	{ IFF_NOARP,		AF_INET,	"-arp"		},
344 	{ IFF_UP,		AF_UNSPEC, 	"up" 		},
345 	{ 0,			0,		NULL		},
346 };
347 
348 typedef struct ni {
349 	char		ni_name[LIFNAMSIZ];
350 	struct ni	*ni_next;
351 } ni_t;
352 
353 static ni_t	*ni_list = NULL;
354 static int	num_ni = 0;
355 
356 /* End defines and structure definitions for ifconfig -a plumb */
357 
358 /* Known address families */
359 struct afswtch {
360 	char *af_name;
361 	short af_af;
362 	void (*af_status)();
363 	void (*af_getaddr)();
364 	void (*af_configinfo)();
365 } afs[] = {
366 	{ "inet", AF_INET, in_status, in_getaddr, in_configinfo },
367 	{ "inet6", AF_INET6, in6_status, in6_getaddr, in6_configinfo },
368 	{ 0, 0,	0, 0, 0 }
369 };
370 
371 #define	SOCKET_AF(af)	(((af) == AF_UNSPEC) ? AF_INET : (af))
372 
373 struct afswtch *afp;	/* the address family being set or asked about */
374 
375 int
main(int argc,char * argv[])376 main(int argc, char *argv[])
377 {
378 	int64_t		lifc_flags;
379 	char		*default_ip_str;
380 	ipadm_status_t	istatus;
381 
382 	lifc_flags = LIFC_DEFAULT;
383 
384 	if (argc < 2) {
385 		(void) strncpy(name, "-a", sizeof (name));
386 	} else {
387 		argc--, argv++;
388 		if (strlen(*argv) > sizeof (name) - 1) {
389 			(void) fprintf(stderr, "%s: interface name too long\n",
390 			    *argv);
391 			exit(1);
392 		}
393 		(void) strncpy(name, *argv, sizeof (name));
394 	}
395 	name[sizeof (name) - 1] = '\0';
396 	(void) strncpy(origname, name, sizeof (origname));	/* For addif */
397 	default_ip_str = NULL;
398 	v4compat = get_compat_flag(&default_ip_str);
399 	if (v4compat == DEFAULT_PROT_BAD_VALUE) {
400 		(void) fprintf(stderr,
401 		    "ifconfig: %s: Bad value for %s in %s\n", default_ip_str,
402 		    DEFAULT_IP, INET_DEFAULT_FILE);
403 		free(default_ip_str);
404 		exit(2);
405 	}
406 	free(default_ip_str);
407 	argc--, argv++;
408 	if (argc > 0) {
409 		struct afswtch *myafp;
410 
411 		for (myafp = afp = afs; myafp->af_name; myafp++) {
412 			if (strcmp(myafp->af_name, *argv) == 0) {
413 				afp = myafp; argc--; argv++;
414 				break;
415 			}
416 		}
417 		af = lifr.lifr_addr.ss_family = afp->af_af;
418 		if (af == AF_INET6) {
419 			v4compat = 0;
420 		}
421 	}
422 
423 	s = socket(SOCKET_AF(af), SOCK_DGRAM, 0);
424 	s4 = socket(AF_INET, SOCK_DGRAM, 0);
425 	s6 = socket(AF_INET6, SOCK_DGRAM, 0);
426 	if (s == -1 || s4 == -1 || s6 == -1)
427 		Perror0_exit("socket");
428 	/*
429 	 * Open the global libipadm handle. The flag IPH_LEGACY has to
430 	 * be specified to indicate that logical interface names will
431 	 * be used during interface creation and address creation.
432 	 */
433 	if ((istatus = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS)
434 		ipadmerr_exit(istatus, "unable to open handle to libipadm");
435 
436 	/*
437 	 * Special interface names is any combination of these flags.
438 	 * Note that due to the ifconfig syntax they have to be combined
439 	 * as a single '-' option.
440 	 *	-a	All interfaces
441 	 *	-u	"up" interfaces
442 	 *	-d	"down" interfaces
443 	 *	-D	Interfaces not controlled by DHCP
444 	 *	-4	IPv4 interfaces
445 	 *	-6	IPv6 interfaces
446 	 *	-X	Turn on debug (not documented)
447 	 *	-v	Turn on verbose
448 	 *	-Z	Only interfaces in caller's zone
449 	 */
450 
451 	if (name[0] == '-') {
452 		/* One or more options */
453 		int64_t onflags = 0;
454 		int64_t offflags = 0;
455 		int c;
456 		char *av[2] = { "ifconfig", name };
457 
458 		while ((c = getopt(2, av, "audhDXZ46v")) != -1) {
459 			switch ((char)c) {
460 			case 'a':
461 				all = 1;
462 				break;
463 			case 'u':
464 				onflags |= IFF_UP;
465 				break;
466 			case 'd':
467 				offflags |= IFF_UP;
468 				break;
469 			case 'D':
470 				offflags |= IFF_DHCPRUNNING;
471 				break;
472 			case 'X':
473 				debug += 3;
474 				break;
475 			case 'Z':
476 				lifc_flags &= ~LIFC_ALLZONES;
477 				break;
478 			case '4':
479 				/*
480 				 * -4 is not a compatable flag, therefore
481 				 * we assume they want v4compat turned off
482 				 */
483 				v4compat = 0;
484 				onflags |= IFF_IPV4;
485 				break;
486 			case '6':
487 				/*
488 				 * If they want IPv6, well then we'll assume
489 				 * they don't want IPv4 compat
490 				 */
491 				v4compat = 0;
492 				onflags |= IFF_IPV6;
493 				break;
494 			case 'v':
495 				verbose = 1;
496 				break;
497 			case 'h':
498 			case '?':
499 				usage();
500 				exit(1);
501 			}
502 		}
503 		if (!all) {
504 			(void) fprintf(stderr,
505 			    "ifconfig: %s: no such interface\n", name);
506 			exit(1);
507 		}
508 		foreachinterface(argc, argv, af, onflags, offflags,
509 		    lifc_flags);
510 	} else {
511 		ifconfig(argc, argv, af, NULL);
512 	}
513 	ipadm_close(iph);
514 	return (0);
515 }
516 
517 /*
518  * For each interface, call ifconfig(argc, argv, af, ifa).
519  * Only call function if onflags and offflags are set or clear, respectively,
520  * in the interfaces flags field.
521  */
522 static void
foreachinterface(int argc,char * argv[],int af,int64_t onflags,int64_t offflags,int64_t lifc_flags)523 foreachinterface(int argc, char *argv[], int af,
524     int64_t onflags, int64_t offflags, int64_t lifc_flags)
525 {
526 	ipadm_addr_info_t *ainfo, *ainfop;
527 	struct ifaddrs *ifa;
528 	ipadm_status_t istatus;
529 
530 	/*
531 	 * Special case:
532 	 * ifconfig -a plumb should find all network interfaces in the current
533 	 * zone.
534 	 */
535 	if (argc > 0 && (strcmp(*argv, "plumb") == 0)) {
536 		plumball(argc, argv, onflags, offflags, lifc_flags);
537 		return;
538 	}
539 	/* Get all addresses in kernel including addresses that are zero. */
540 	istatus = ipadm_addr_info(iph, NULL, &ainfo, IPADM_OPT_ZEROADDR,
541 	    lifc_flags);
542 	if (istatus != IPADM_SUCCESS)
543 		ipadmerr_exit(istatus, "could not get addresses from kernel");
544 
545 	/*
546 	 * For each logical interface, call ifconfig() with the
547 	 * given arguments.
548 	 */
549 	for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
550 		if (ainfop->ia_state == IFA_DISABLED)
551 			continue;
552 		ifa = &ainfop->ia_ifa;
553 		if (onflags || offflags) {
554 			if ((ifa->ifa_flags & onflags) != onflags)
555 				continue;
556 			if ((~ifa->ifa_flags & offflags) != offflags)
557 				continue;
558 		}
559 		s = (ifa->ifa_addr->sa_family == AF_INET ? s4 : s6);
560 		(void) strncpy(name, ifa->ifa_name, sizeof (name));
561 		(void) strncpy(origname, name, sizeof (origname));
562 		ifconfig(argc, argv, af, ifa);
563 	}
564 	ipadm_free_addr_info(ainfo);
565 }
566 
567 /*
568  * Used for `ifconfig -a plumb'. Finds all datalinks and plumbs the interface.
569  */
570 static void
plumball(int argc,char * argv[],int64_t onflags,int64_t offflags,int64_t lifc_flags)571 plumball(int argc, char *argv[], int64_t onflags, int64_t offflags,
572     int64_t lifc_flags)
573 {
574 	int n;
575 	struct lifreq *lifrp;
576 	struct lifconf lifc;
577 	char *buf;
578 
579 	if (onflags != 0 || offflags != 0) {
580 		(void) fprintf(stderr, "ifconfig: invalid syntax used to "
581 		    "plumb all interfaces.\n");
582 		exit(1);
583 	}
584 
585 	if (find_all_interfaces(&lifc, &buf, lifc_flags) != 0 ||
586 	    lifc.lifc_len == 0)
587 		return;
588 
589 	lifrp = lifc.lifc_req;
590 	for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) {
591 		/*
592 		 * Reset global state
593 		 * setaddr: Used by parser to tear apart source and dest
594 		 * name and origname contain the name of the 'current'
595 		 * interface.
596 		 */
597 		setaddr = 0;
598 		(void) strncpy(name, lifrp->lifr_name, sizeof (name));
599 		(void) strncpy(origname, name, sizeof (origname));
600 		ifconfig(argc, argv, af, NULL);
601 	}
602 }
603 
604 /*
605  * Parses the interface name and the command in argv[]. Calls the
606  * appropriate callback function for the given command from `cmds[]'
607  * table.
608  * If there is no command specified, it prints all addresses.
609  */
610 static void
ifconfig(int argc,char * argv[],int af,struct ifaddrs * ifa)611 ifconfig(int argc, char *argv[], int af, struct ifaddrs *ifa)
612 {
613 	static boolean_t scan_netmask = _B_FALSE;
614 	int ret;
615 	ipadm_status_t istatus;
616 	struct lifreq lifr;
617 
618 	if (argc == 0) {
619 		status();
620 		return;
621 	}
622 
623 	if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) {
624 		/*
625 		 * Some errors are ignored in the case where more than one
626 		 * interface is being operated on.
627 		 */
628 		ret = setifdhcp("ifconfig", name, argc, argv);
629 		if (ret == DHCP_EXIT_IF_FAILURE) {
630 			if (!all)
631 				exit(DHCP_EXIT_FAILURE);
632 		} else if (ret != DHCP_EXIT_SUCCESS) {
633 			exit(ret);
634 		}
635 		return;
636 	}
637 
638 	/*
639 	 * The following is a "hack" to get around the existing interface
640 	 * setting mechanism.  Currently, each interface attribute,
641 	 * such as address, netmask, broadcast, ... is set separately.  But
642 	 * sometimes two or more attributes must be set together.  For
643 	 * example, setting an address without a netmask does not make sense.
644 	 * Yet they can be set separately for IPv4 address using the current
645 	 * ifconfig(1M) syntax.  The kernel then "infers" the correct netmask
646 	 * using the deprecated "IP address classes."  This is simply not
647 	 * correct.
648 	 *
649 	 * The "hack" below is to go thru the whole command list looking for
650 	 * the netmask command first.  Then use this netmask to set the
651 	 * address.  This does not provide an extensible way to accommodate
652 	 * future need for setting more than one attributes together.
653 	 *
654 	 * Note that if the "netmask" command argument is a "+", we need
655 	 * to save this info and do the query after we know the address to
656 	 * be set.  The reason is that if "addif" is used, the working
657 	 * interface name will be changed later when the logical interface
658 	 * is created.  In in_getmask(), if an address is not provided,
659 	 * it will use the working interface's address to do the query.
660 	 * It will be wrong now as we don't know the logical interface's name.
661 	 *
662 	 * ifconfig(1M) is too overloaded and the code is so convoluted
663 	 * that it is "safer" not to re-architect the code to fix the above
664 	 * issue, hence this "hack."  We may be better off to have a new
665 	 * command with better syntax for configuring network interface
666 	 * parameters...
667 	 */
668 	if (!scan_netmask && afp->af_af == AF_INET) {
669 		int	largc;
670 		char	**largv;
671 
672 		/* Only go thru the command list once to find the netmask. */
673 		scan_netmask = _B_TRUE;
674 
675 		/*
676 		 * Currently, if multiple netmask commands are specified, the
677 		 * last one will be used as the final netmask.  So we need
678 		 * to scan the whole list to preserve this behavior.
679 		 */
680 		for (largc = argc, largv = argv; largc > 0; largc--, largv++) {
681 			if (strcmp(*largv, NETMASK_CMD) == 0) {
682 				if (--largc == 0)
683 					break;
684 				largv++;
685 				if (strcmp(*largv, "+") == 0) {
686 					g_netmask_set = G_NETMASK_PENDING;
687 				} else {
688 					in_getaddr(*largv, (struct sockaddr *)
689 					    &g_netmask, NULL);
690 					g_netmask_set = G_NETMASK_SET;
691 				}
692 				/* Continue the scan. */
693 			}
694 		}
695 	}
696 
697 	while (argc > 0) {
698 		struct cmd *p;
699 		boolean_t found_cmd;
700 
701 		if (debug)
702 			(void) printf("ifconfig: argv %s\n", *argv);
703 
704 		found_cmd = _B_FALSE;
705 		for (p = cmds; p->c_func; p++) {
706 			if (p->c_name) {
707 				if (strcmp(*argv, p->c_name) == 0) {
708 					/*
709 					 * indicate that the command was
710 					 * found and check to see if
711 					 * the address family is valid
712 					 */
713 					found_cmd = _B_TRUE;
714 					if (p->c_af == AF_ANY ||
715 					    af == p->c_af)
716 						break;
717 				}
718 			} else {
719 				if (p->c_af == AF_ANY ||
720 				    af == p->c_af)
721 					break;
722 			}
723 		}
724 		/*
725 		 * If we found the keyword, but the address family
726 		 * did not match spit out an error
727 		 */
728 		if (found_cmd && p->c_name == 0) {
729 			(void) fprintf(stderr, "ifconfig: Operation %s not"
730 			    " supported for %s\n", *argv, afp->af_name);
731 			exit(1);
732 		}
733 		/*
734 		 * else (no keyword found), we assume it's an address
735 		 * of some sort
736 		 */
737 		if (setaddr && ipaddr != NULL) {
738 			/*
739 			 * We must have already filled in a source address in
740 			 * `ipaddr' and we now got a destination address.
741 			 * Fill it in `ipaddr' and call libipadm to create
742 			 * the static address.
743 			 */
744 			if (p->c_name == 0) {
745 				istatus = ipadm_set_dst_addr(ipaddr, *argv,
746 				    (p->c_af == AF_ANY ? AF_UNSPEC : af));
747 				if (istatus != IPADM_SUCCESS) {
748 					ipadmerr_exit(istatus, "could not "
749 					    "set destination address");
750 				}
751 				/*
752 				 * finished processing dstaddr, so reset setaddr
753 				 */
754 				setaddr = 0;
755 			}
756 			/*
757 			 * Both source and destination address are in `ipaddr'.
758 			 * Add the address by calling libipadm.
759 			 */
760 			istatus = ipadm_create_addr(iph, ipaddr,
761 			    IPADM_OPT_ACTIVE);
762 			if (istatus != IPADM_SUCCESS)
763 				goto createfailed;
764 			ipadm_destroy_addrobj(ipaddr);
765 			ipaddr = NULL;
766 			setaddr_done = _B_TRUE;
767 			if (p->c_name == 0) {
768 				/* move parser along */
769 				argc--, argv++;
770 				continue;
771 			}
772 		}
773 		if (p->c_name == 0 && setaddr_done) {
774 			/*
775 			 * catch odd commands like
776 			 * "ifconfig <intf> addr1 addr2 addr3 addr4 up"
777 			 */
778 			(void) fprintf(stderr, "%s",
779 			    "ifconfig: cannot configure more than two "
780 			    "addresses in one command\n");
781 			exit(1);
782 		}
783 		if (p->c_func) {
784 			if (p->c_af == AF_INET6) {
785 				v4compat = 0;
786 			}
787 			if (p->c_parameter == NEXTARG ||
788 			    p->c_parameter == OPTARG) {
789 				argc--, argv++;
790 				if (argc == 0 && p->c_parameter == NEXTARG) {
791 					(void) fprintf(stderr,
792 					    "ifconfig: no argument for %s\n",
793 					    p->c_name);
794 					exit(1);
795 				}
796 			}
797 			/*
798 			 *	Call the function if:
799 			 *
800 			 *		there's no address family
801 			 *		restriction
802 			 *	OR
803 			 *		we don't know the address yet
804 			 *		(because we were called from
805 			 *		main)
806 			 *	OR
807 			 *		there is a restriction AND
808 			 *		the address families match
809 			 */
810 			if ((p->c_af == AF_ANY)	||
811 			    (ifa == NULL) ||
812 			    (ifa->ifa_addr->sa_family == p->c_af)) {
813 				ret = (*p->c_func)(*argv, p->c_parameter);
814 				/*
815 				 *	If c_func failed and we should
816 				 *	abort processing for this
817 				 *	interface on failure, return
818 				 *	now rather than going on to
819 				 *	process other commands for
820 				 *	the same interface.
821 				 */
822 				if (ret != 0 && p->c_abortonfail)
823 					return;
824 			}
825 		}
826 		argc--, argv++;
827 	}
828 
829 	if (setaddr && ipaddr != NULL) {
830 		/*
831 		 * Only the source address was provided, which was already
832 		 * set in `ipaddr'. Add the address by calling libipadm.
833 		 */
834 		istatus = ipadm_create_addr(iph, ipaddr, IPADM_OPT_ACTIVE);
835 		if (istatus != IPADM_SUCCESS)
836 			goto createfailed;
837 		ipadm_destroy_addrobj(ipaddr);
838 		ipaddr = NULL;
839 		setaddr_done = _B_TRUE;
840 	}
841 
842 	/* Check to see if there's a security hole in the tunnel setup. */
843 	if (ipsec_policy_set && !ipsec_auth_covered) {
844 		(void) fprintf(stderr, "ifconfig: WARNING: tunnel with only "
845 		    "ESP and no authentication.\n");
846 	}
847 	return;
848 
849 createfailed:
850 	(void) fprintf(stderr, "ifconfig: could not create address:% s\n",
851 	    ipadm_status2str(istatus));
852 	/* Remove the newly created logical interface. */
853 	if (strcmp(name, origname) != 0) {
854 		assert(strchr(name, ':') != NULL);
855 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
856 		(void) ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr);
857 	}
858 	exit(1);
859 }
860 
861 /* ARGSUSED */
862 static int
setdebugflag(char * val,int64_t arg)863 setdebugflag(char *val, int64_t arg)
864 {
865 	debug++;
866 	return (0);
867 }
868 
869 /* ARGSUSED */
870 static int
setverboseflag(char * val,int64_t arg)871 setverboseflag(char *val, int64_t arg)
872 {
873 	verbose++;
874 	return (0);
875 }
876 
877 /*
878  * This function fills in the given lifreq's lifr_addr field based on
879  * g_netmask_set.
880  */
881 static void
set_mask_lifreq(struct lifreq * lifr,struct sockaddr_storage * addr,struct sockaddr_storage * mask)882 set_mask_lifreq(struct lifreq *lifr, struct sockaddr_storage *addr,
883     struct sockaddr_storage *mask)
884 {
885 	assert(addr != NULL);
886 	assert(mask != NULL);
887 
888 	switch (g_netmask_set) {
889 	case G_NETMASK_SET:
890 		lifr->lifr_addr = g_netmask;
891 		break;
892 
893 	case G_NETMASK_PENDING:
894 		/*
895 		 * "+" is used as the argument to "netmask" command.  Query
896 		 * the database on the correct netmask based on the address to
897 		 * be set.
898 		 */
899 		assert(afp->af_af == AF_INET);
900 		g_netmask = *addr;
901 		if (!in_getmask((struct sockaddr_in *)&g_netmask, _B_TRUE)) {
902 			lifr->lifr_addr = *mask;
903 			g_netmask_set = G_NETMASK_NIL;
904 		} else {
905 			lifr->lifr_addr = g_netmask;
906 			g_netmask_set = G_NETMASK_SET;
907 		}
908 		break;
909 
910 	case G_NETMASK_NIL:
911 	default:
912 		lifr->lifr_addr = *mask;
913 		break;
914 	}
915 }
916 
917 /*
918  * Set the interface address. Handles <addr>, <addr>/<n> as well as /<n>
919  * syntax for setting the address, the address plus netmask, and just
920  * the netmask respectively.
921  */
922 /* ARGSUSED */
923 static int
setifaddr(char * addr,int64_t param)924 setifaddr(char *addr, int64_t param)
925 {
926 	ipadm_status_t istatus;
927 	int prefixlen = 0;
928 	struct  lifreq lifr1;
929 	struct	sockaddr_storage laddr;
930 	struct	sockaddr_storage netmask;
931 	struct	sockaddr_in6 *sin6;
932 	struct	sockaddr_in *sin;
933 	struct	sockaddr_storage sav_netmask;
934 	char cidraddr[BUFSIZ];
935 
936 	if (addr[0] == '/')
937 		return (setifprefixlen(addr, 0));
938 
939 	(*afp->af_getaddr)(addr, (struct sockaddr *)&laddr, &prefixlen);
940 
941 	(void) memset(&netmask, 0, sizeof (netmask));
942 	netmask.ss_family = afp->af_af;
943 	switch (prefixlen) {
944 	case NO_PREFIX:
945 		/* Nothing there - ok */
946 		break;
947 	case BAD_ADDR:
948 		(void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n",
949 		    addr);
950 		exit(1);
951 	default:
952 		if (afp->af_af == AF_INET6) {
953 			sin6 = (struct sockaddr_in6 *)&netmask;
954 			if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
955 			    (uchar_t *)&sin6->sin6_addr)) {
956 				(void) fprintf(stderr, "ifconfig: "
957 				    "Bad prefix length: %d\n",
958 				    prefixlen);
959 				exit(1);
960 			}
961 		} else {
962 			sin = (struct sockaddr_in *)&netmask;
963 			if (!in_prefixlentomask(prefixlen, IP_ABITS,
964 			    (uchar_t *)&sin->sin_addr)) {
965 				(void) fprintf(stderr, "ifconfig: "
966 				    "Bad prefix length: %d\n",
967 				    prefixlen);
968 				exit(1);
969 			}
970 		}
971 		/*
972 		 * Just in case of funny setting of both prefix and netmask,
973 		 * prefix should override the netmask command.
974 		 */
975 		g_netmask_set = G_NETMASK_NIL;
976 		break;
977 	}
978 
979 	/*
980 	 * Check and see if any "netmask" command is used and perform the
981 	 * necessary operation.
982 	 */
983 	set_mask_lifreq(&lifr, &laddr, &netmask);
984 
985 	/* This check is temporary until libipadm supports IPMP interfaces. */
986 	if (ifconfig_use_libipadm(s, name)) {
987 		char	addrstr[INET6_ADDRSTRLEN];
988 
989 		if (af == AF_INET) {
990 			sin = (struct sockaddr_in *)&laddr;
991 			(void) inet_ntop(AF_INET, &sin->sin_addr, addrstr,
992 			    sizeof (addrstr));
993 		} else {
994 			sin6 = (struct sockaddr_in6 *)&laddr;
995 			(void) inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr,
996 			    sizeof (addrstr));
997 		}
998 		istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name,
999 		    &ipaddr);
1000 		if (istatus != IPADM_SUCCESS)
1001 			ipadmerr_exit(istatus, "setifaddr");
1002 
1003 		/*
1004 		 * lifr.lifr_addr, which is updated by set_mask_lifreq()
1005 		 * will contain the right mask to use.
1006 		 */
1007 		prefixlen = mask2plen((struct sockaddr *)&lifr.lifr_addr);
1008 		(void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d",
1009 		    addrstr, prefixlen);
1010 
1011 		istatus = ipadm_set_addr(ipaddr, cidraddr, af);
1012 		if (istatus != IPADM_SUCCESS)
1013 			ipadmerr_exit(istatus, "could not set address");
1014 		/*
1015 		 * let parser know we got a source.
1016 		 * Next address, if given, should be dest
1017 		 */
1018 		setaddr++;
1019 
1020 		/*
1021 		 * address will be set by the parser after nextarg has
1022 		 * been scanned
1023 		 */
1024 		return (0);
1025 	}
1026 
1027 	/* Tell parser that an address was set */
1028 	setaddr++;
1029 	/* save copy of netmask to restore in case of error */
1030 	(void) strncpy(lifr1.lifr_name, name, sizeof (lifr1.lifr_name));
1031 	if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr1) < 0)
1032 		Perror0_exit("SIOCGLIFNETMASK");
1033 	sav_netmask = lifr1.lifr_addr;
1034 
1035 	/*
1036 	 * If setting the address and not the mask, clear any existing mask
1037 	 * and the kernel will then assign the default (netmask has been set
1038 	 * to 0 in this case).  If setting both (either by using a prefix or
1039 	 * using the netmask command), set the mask first, so the address will
1040 	 * be interpreted correctly.
1041 	 */
1042 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1043 	/* lifr.lifr_addr already contains netmask from set_mask_lifreq() */
1044 	if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1045 		Perror0_exit("SIOCSLIFNETMASK");
1046 
1047 	if (debug) {
1048 		char abuf[INET6_ADDRSTRLEN];
1049 		void *addr = (afp->af_af == AF_INET) ?
1050 		    (void *)&((struct sockaddr_in *)&laddr)->sin_addr :
1051 		    (void *)&((struct sockaddr_in6 *)&laddr)->sin6_addr;
1052 
1053 		(void) printf("Setting %s af %d addr %s\n",
1054 		    lifr.lifr_name, afp->af_af,
1055 		    inet_ntop(afp->af_af, addr, abuf, sizeof (abuf)));
1056 	}
1057 	lifr.lifr_addr = laddr;
1058 	lifr.lifr_addr.ss_family = afp->af_af;
1059 	if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
1060 		/*
1061 		 * Restore the netmask
1062 		 */
1063 		int saverr = errno;
1064 
1065 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1066 		lifr.lifr_addr = sav_netmask;
1067 		(void) ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr);
1068 		errno = saverr;
1069 		Perror0_exit("SIOCSLIFADDR");
1070 	}
1071 
1072 	return (0);
1073 }
1074 
1075 /*
1076  * The following functions are stolen from the ipseckey(1m) program.
1077  * Perhaps they should be somewhere common, but for now, we just maintain
1078  * two versions.  We do this because of the different semantics for which
1079  * algorithms we select ("requested" for ifconfig vs. "actual" for key).
1080  */
1081 
1082 static ulong_t
parsenum(char * num)1083 parsenum(char *num)
1084 {
1085 	ulong_t rc;
1086 	char *end = NULL;
1087 
1088 	errno = 0;
1089 	rc = strtoul(num, &end, 0);
1090 	if (errno != 0 || end == num || *end != '\0') {
1091 		rc = (ulong_t)-1;
1092 	}
1093 
1094 	return (rc);
1095 }
1096 
1097 /*
1098  * Parse and reverse parse possible algorithm values, include numbers.
1099  * Mostly stolen from ipseckey.c. See the comments above parsenum() for why
1100  * this isn't common to ipseckey.c.
1101  *
1102  * NOTE: Static buffer in this function for the return value.  Since ifconfig
1103  *       isn't multithreaded, this isn't a huge problem.
1104  */
1105 
1106 #define	NBUF_SIZE 20	/* Enough to print a large integer. */
1107 
1108 static char *
rparsealg(uint8_t alg_value,int proto_num)1109 rparsealg(uint8_t alg_value, int proto_num)
1110 {
1111 	struct ipsecalgent *alg;
1112 	static char numprint[128];	/* Enough to hold an algorithm name. */
1113 
1114 	/*
1115 	 * Special cases for "any" and "none"
1116 	 * The kernel needs to be able to distinguish between "any"
1117 	 * and "none" and the APIs are underdefined in this area for auth.
1118 	 */
1119 	if (proto_num == IPSEC_PROTO_AH) {
1120 		if (alg_value == SADB_AALG_NONE)
1121 			return ("none");
1122 		if (alg_value == SADB_AALG_ANY)
1123 			return ("any");
1124 	}
1125 
1126 	alg = getipsecalgbynum(alg_value, proto_num, NULL);
1127 	if (alg != NULL) {
1128 		(void) strlcpy(numprint, alg->a_names[0], sizeof (numprint));
1129 		freeipsecalgent(alg);
1130 	} else {
1131 		(void) snprintf(numprint, sizeof (numprint), "%d", alg_value);
1132 	}
1133 
1134 	return (numprint);
1135 }
1136 
1137 static uint_t
parsealg(char * algname,int proto_num)1138 parsealg(char *algname, int proto_num)
1139 {
1140 	struct ipsecalgent *alg;
1141 	ulong_t invalue;
1142 
1143 	if (algname == NULL) {
1144 		(void) fprintf(stderr, "ifconfig: Unexpected end of command "
1145 		    "line.\n");
1146 		exit(1);
1147 	}
1148 
1149 	/*
1150 	 * Special-case "none" and "any".
1151 	 * Use strcasecmp because its length is bounded.
1152 	 */
1153 	if (strcasecmp("none", algname) == 0) {
1154 		return ((proto_num == IPSEC_PROTO_ESP) ?
1155 		    NO_ESP_EALG : NO_ESP_AALG);
1156 	}
1157 	if ((strcasecmp("any", algname) == 0) && (proto_num == IPSEC_PROTO_AH))
1158 		return (SADB_AALG_ANY);
1159 
1160 	alg = getipsecalgbyname(algname, proto_num, NULL);
1161 	if (alg != NULL) {
1162 		invalue = alg->a_alg_num;
1163 		freeipsecalgent(alg);
1164 		return ((uint_t)invalue);
1165 	}
1166 
1167 	/*
1168 	 * Since algorithms can be loaded during kernel run-time, check for
1169 	 * numeric algorithm values too.
1170 	 */
1171 	invalue = parsenum(algname);
1172 	if ((invalue & (ulong_t)0xff) == invalue)
1173 		return ((uint_t)invalue);
1174 
1175 	(void) fprintf(stderr, "ifconfig: %s algorithm type %s unknown.\n",
1176 	    (proto_num == IPSEC_PROTO_ESP) ?
1177 	    "Encryption" : "Authentication", algname);
1178 	exit(1);
1179 	/* NOTREACHED */
1180 }
1181 
1182 /*
1183  * Actual ifconfig functions to set tunnel security properties.
1184  */
1185 
1186 enum ipsec_alg_type { ESP_ENCR_ALG = 1, ESP_AUTH_ALG, AH_AUTH_ALG };
1187 
1188 static int
set_tun_algs(int which_alg,int alg)1189 set_tun_algs(int which_alg, int alg)
1190 {
1191 	boolean_t	encr_alg_set = _B_FALSE;
1192 	iptun_params_t	params;
1193 	dladm_status_t	status;
1194 	ipsec_req_t	*ipsr;
1195 
1196 	if ((status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN,
1197 	    &params.iptun_param_linkid)) != DLADM_STATUS_OK)
1198 		goto done;
1199 
1200 	status = dladm_iptun_getparams(dlh, &params, DLADM_OPT_ACTIVE);
1201 	if (status != DLADM_STATUS_OK)
1202 		goto done;
1203 
1204 	ipsr = &params.iptun_param_secinfo;
1205 
1206 	/*
1207 	 * If I'm just starting off this ifconfig, I want a clean slate,
1208 	 * otherwise, I've captured the current tunnel security settings.
1209 	 * In the case of continuation, I merely add to the settings.
1210 	 */
1211 	if (!(params.iptun_param_flags & IPTUN_PARAM_SECINFO))
1212 		(void) memset(ipsr, 0, sizeof (*ipsr));
1213 
1214 	/* We're only modifying the IPsec information */
1215 	params.iptun_param_flags = IPTUN_PARAM_SECINFO;
1216 
1217 	switch (which_alg) {
1218 	case ESP_ENCR_ALG:
1219 		if (alg == NO_ESP_EALG) {
1220 			if (ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE)
1221 				ipsr->ipsr_esp_req = 0;
1222 			ipsr->ipsr_esp_alg = SADB_EALG_NONE;
1223 
1224 			/* Let the user specify NULL encryption implicitly. */
1225 			if (ipsr->ipsr_esp_auth_alg != SADB_AALG_NONE) {
1226 				encr_alg_set = _B_TRUE;
1227 				ipsr->ipsr_esp_alg = SADB_EALG_NULL;
1228 			}
1229 		} else {
1230 			encr_alg_set = _B_TRUE;
1231 			ipsr->ipsr_esp_req =
1232 			    IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1233 			ipsr->ipsr_esp_alg = alg;
1234 		}
1235 		break;
1236 	case ESP_AUTH_ALG:
1237 		if (alg == NO_ESP_AALG) {
1238 			if ((ipsr->ipsr_esp_alg == SADB_EALG_NONE ||
1239 			    ipsr->ipsr_esp_alg == SADB_EALG_NULL) &&
1240 			    !encr_alg_set)
1241 				ipsr->ipsr_esp_req = 0;
1242 			ipsr->ipsr_esp_auth_alg = SADB_AALG_NONE;
1243 		} else {
1244 			ipsr->ipsr_esp_req =
1245 			    IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1246 			ipsr->ipsr_esp_auth_alg = alg;
1247 
1248 			/* Let the user specify NULL encryption implicitly. */
1249 			if (ipsr->ipsr_esp_alg == SADB_EALG_NONE &&
1250 			    !encr_alg_set)
1251 				ipsr->ipsr_esp_alg = SADB_EALG_NULL;
1252 		}
1253 		break;
1254 	case AH_AUTH_ALG:
1255 		if (alg == NO_AH_AALG) {
1256 			ipsr->ipsr_ah_req = 0;
1257 			ipsr->ipsr_auth_alg = SADB_AALG_NONE;
1258 		} else {
1259 			ipsr->ipsr_ah_req =
1260 			    IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE;
1261 			ipsr->ipsr_auth_alg = alg;
1262 		}
1263 		break;
1264 		/* Will never hit DEFAULT */
1265 	}
1266 
1267 	status = dladm_iptun_modify(dlh, &params, DLADM_OPT_ACTIVE);
1268 
1269 done:
1270 	if (status != DLADM_STATUS_OK)
1271 		dladmerr_exit(status, name);
1272 	else {
1273 		ipsec_policy_set = _B_TRUE;
1274 		if ((ipsr->ipsr_esp_req != 0 &&
1275 		    ipsr->ipsr_esp_auth_alg != SADB_AALG_NONE) ||
1276 		    (ipsr->ipsr_ah_req != 0 &&
1277 		    ipsr->ipsr_auth_alg != SADB_AALG_NONE))
1278 			ipsec_auth_covered = _B_TRUE;
1279 	}
1280 	return (0);
1281 }
1282 
1283 /* ARGSUSED */
1284 static int
set_tun_esp_encr_alg(char * addr,int64_t param)1285 set_tun_esp_encr_alg(char *addr, int64_t param)
1286 {
1287 	return (set_tun_algs(ESP_ENCR_ALG,
1288 	    parsealg(addr, IPSEC_PROTO_ESP)));
1289 }
1290 
1291 /* ARGSUSED */
1292 static int
set_tun_esp_auth_alg(char * addr,int64_t param)1293 set_tun_esp_auth_alg(char *addr, int64_t param)
1294 {
1295 	return (set_tun_algs(ESP_AUTH_ALG,
1296 	    parsealg(addr, IPSEC_PROTO_AH)));
1297 }
1298 
1299 /* ARGSUSED */
1300 static int
set_tun_ah_alg(char * addr,int64_t param)1301 set_tun_ah_alg(char *addr, int64_t param)
1302 {
1303 	return (set_tun_algs(AH_AUTH_ALG,
1304 	    parsealg(addr, IPSEC_PROTO_AH)));
1305 }
1306 
1307 /* ARGSUSED */
1308 static int
setifrevarp(char * arg,int64_t param)1309 setifrevarp(char *arg, int64_t param)
1310 {
1311 	struct sockaddr_in	laddr;
1312 
1313 	if (afp->af_af == AF_INET6) {
1314 		(void) fprintf(stderr,
1315 		    "ifconfig: revarp not possible on IPv6 interface %s\n",
1316 		    name);
1317 		exit(1);
1318 	}
1319 	if (doifrevarp(name, &laddr)) {
1320 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1321 		laddr.sin_family = AF_INET;
1322 		(void) memcpy(&lifr.lifr_addr, &laddr, sizeof (laddr));
1323 		if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
1324 			Perror0_exit("SIOCSLIFADDR");
1325 	}
1326 	return (0);
1327 }
1328 
1329 /* ARGSUSED */
1330 static int
setifsubnet(char * addr,int64_t param)1331 setifsubnet(char *addr, int64_t param)
1332 {
1333 	int prefixlen = 0;
1334 	struct	sockaddr_storage subnet;
1335 
1336 	(*afp->af_getaddr)(addr, &subnet, &prefixlen);
1337 
1338 	switch (prefixlen) {
1339 	case NO_PREFIX:
1340 		(void) fprintf(stderr,
1341 		    "ifconfig: Missing prefix length in subnet %s\n", addr);
1342 		exit(1);
1343 		/* NOTREACHED */
1344 	case BAD_ADDR:
1345 		(void) fprintf(stderr,
1346 		    "ifconfig: Bad prefix length in %s\n", addr);
1347 		exit(1);
1348 	default:
1349 		break;
1350 	}
1351 
1352 	lifr.lifr_addr = subnet;
1353 	lifr.lifr_addrlen = prefixlen;
1354 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1355 	if (ioctl(s, SIOCSLIFSUBNET, (caddr_t)&lifr) < 0)
1356 		Perror0_exit("SIOCSLIFSUBNET");
1357 
1358 	return (0);
1359 }
1360 
1361 /* ARGSUSED */
1362 static int
setifnetmask(char * addr,int64_t param)1363 setifnetmask(char *addr, int64_t param)
1364 {
1365 	struct sockaddr_in netmask;
1366 
1367 	assert(afp->af_af != AF_INET6);
1368 
1369 	if (strcmp(addr, "+") == 0) {
1370 		if (!in_getmask(&netmask, _B_FALSE))
1371 			return (0);
1372 		(void) printf("Setting netmask of %s to %s\n", name,
1373 		    inet_ntoa(netmask.sin_addr));
1374 	} else {
1375 		in_getaddr(addr, (struct sockaddr *)&netmask, NULL);
1376 	}
1377 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1378 	(void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask));
1379 	if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1380 		Perror0_exit("SIOCSLIFNETMASK");
1381 	return (0);
1382 }
1383 
1384 /*
1385  * Parse '/<n>' as a netmask.
1386  */
1387 /* ARGSUSED */
1388 static int
setifprefixlen(char * addr,int64_t param)1389 setifprefixlen(char *addr, int64_t param)
1390 {
1391 	int prefixlen;
1392 	int af = afp->af_af;
1393 
1394 	prefixlen = in_getprefixlen(addr, _B_TRUE,
1395 	    (af == AF_INET) ? IP_ABITS : IPV6_ABITS);
1396 	if (prefixlen < 0) {
1397 		(void) fprintf(stderr,
1398 		    "ifconfig: Bad prefix length in %s\n", addr);
1399 		exit(1);
1400 	}
1401 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
1402 	lifr.lifr_addr.ss_family = af;
1403 	if (af == AF_INET6) {
1404 		struct sockaddr_in6 *sin6;
1405 
1406 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
1407 		if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
1408 		    (uchar_t *)&sin6->sin6_addr)) {
1409 			(void) fprintf(stderr, "ifconfig: "
1410 			    "Bad prefix length: %d\n",
1411 			    prefixlen);
1412 			exit(1);
1413 		}
1414 	} else if (af == AF_INET) {
1415 		struct sockaddr_in *sin;
1416 
1417 		sin = (struct sockaddr_in *)&lifr.lifr_addr;
1418 		if (!in_prefixlentomask(prefixlen, IP_ABITS,
1419 		    (uchar_t *)&sin->sin_addr)) {
1420 			(void) fprintf(stderr, "ifconfig: "
1421 			    "Bad prefix length: %d\n",
1422 			    prefixlen);
1423 			exit(1);
1424 		}
1425 	} else {
1426 		(void) fprintf(stderr, "ifconfig: setting prefix only supported"
1427 		    " for address family inet or inet6\n");
1428 		exit(1);
1429 	}
1430 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1431 	if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1432 		Perror0_exit("SIOCSLIFNETMASK");
1433 	return (0);
1434 }
1435 
1436 /* ARGSUSED */
1437 static int
setifbroadaddr(char * addr,int64_t param)1438 setifbroadaddr(char *addr, int64_t param)
1439 {
1440 	struct	sockaddr_in broadaddr;
1441 
1442 	assert(afp->af_af != AF_INET6);
1443 
1444 	if (strcmp(addr, "+") == 0) {
1445 		/*
1446 		 * This doesn't set the broadcast address at all. Rather, it
1447 		 * gets, then sets the interface's address, relying on the fact
1448 		 * that resetting the address will reset the broadcast address.
1449 		 */
1450 		(void) strncpy(lifr.lifr_name, name,
1451 		    sizeof (lifr.lifr_name));
1452 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
1453 			if (errno != EADDRNOTAVAIL)
1454 				Perror0_exit("SIOCGLIFADDR");
1455 			return (0);
1456 		}
1457 		if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
1458 			Perror0_exit("SIOCGLIFADDR");
1459 
1460 		return (0);
1461 	}
1462 	in_getaddr(addr, (struct sockaddr *)&broadaddr, NULL);
1463 
1464 	(void) memcpy(&lifr.lifr_addr, &broadaddr, sizeof (broadaddr));
1465 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1466 	if (ioctl(s, SIOCSLIFBRDADDR, (caddr_t)&lifr) < 0)
1467 		Perror0_exit("SIOCSLIFBRDADDR");
1468 	return (0);
1469 }
1470 
1471 /*
1472  * set interface destination address
1473  */
1474 /* ARGSUSED */
1475 static int
setifdstaddr(char * addr,int64_t param)1476 setifdstaddr(char *addr, int64_t param)
1477 {
1478 	(*afp->af_getaddr)(addr, (struct sockaddr *)&lifr.lifr_addr, NULL);
1479 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1480 	if (ioctl(s, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0)
1481 		Perror0_exit("setifdstaddr: SIOCSLIFDSTADDR");
1482 	return (0);
1483 }
1484 
1485 /* ARGSUSED */
1486 static int
setifflags(char * val,int64_t value)1487 setifflags(char *val, int64_t value)
1488 {
1489 	struct lifreq lifrl;	/* local lifreq struct */
1490 	boolean_t bringup = _B_FALSE;
1491 
1492 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1493 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
1494 		Perror0_exit("setifflags: SIOCGLIFFLAGS");
1495 
1496 	if (value < 0) {
1497 		value = -value;
1498 
1499 		if ((value & IFF_NOFAILOVER) && (lifr.lifr_flags & IFF_UP)) {
1500 			/*
1501 			 * The kernel does not allow administratively up test
1502 			 * addresses to be converted to data addresses.  Bring
1503 			 * the address down first, then bring it up after it's
1504 			 * been converted to a data address.
1505 			 */
1506 			lifr.lifr_flags &= ~IFF_UP;
1507 			(void) ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr);
1508 			bringup = _B_TRUE;
1509 		}
1510 
1511 		lifr.lifr_flags &= ~value;
1512 		if ((value & (IFF_UP | IFF_NOFAILOVER)) &&
1513 		    (lifr.lifr_flags & IFF_DUPLICATE)) {
1514 			/*
1515 			 * If the user is trying to mark an interface with a
1516 			 * duplicate address as "down," or convert a duplicate
1517 			 * test address to a data address, then fetch the
1518 			 * address and set it.  This will cause IP to clear
1519 			 * the IFF_DUPLICATE flag and stop the automatic
1520 			 * recovery timer.
1521 			 */
1522 			value = lifr.lifr_flags;
1523 			if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
1524 				(void) ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr);
1525 			lifr.lifr_flags = value;
1526 		}
1527 	} else {
1528 		lifr.lifr_flags |= value;
1529 	}
1530 
1531 	/*
1532 	 * If we're about to bring up an underlying physical IPv6 interface in
1533 	 * an IPMP group, ensure the IPv6 IPMP interface is also up.  This is
1534 	 * for backward compatibility with legacy configurations in which
1535 	 * there are no explicit hostname files for IPMP interfaces.  (For
1536 	 * IPv4, this is automatically handled by the kernel when migrating
1537 	 * the underlying interface's data address to the IPMP interface.)
1538 	 */
1539 	(void) strlcpy(lifrl.lifr_name, name, LIFNAMSIZ);
1540 
1541 	if (lifnum(lifr.lifr_name) == 0 &&
1542 	    (lifr.lifr_flags & (IFF_UP|IFF_IPV6)) == (IFF_UP|IFF_IPV6) &&
1543 	    ioctl(s, SIOCGLIFGROUPNAME, &lifrl) == 0 &&
1544 	    lifrl.lifr_groupname[0] != '\0') {
1545 		lifgroupinfo_t lifgr;
1546 
1547 		(void) strlcpy(lifgr.gi_grname, lifrl.lifr_groupname,
1548 		    LIFGRNAMSIZ);
1549 		if (ioctl(s, SIOCGLIFGROUPINFO, &lifgr) == -1)
1550 			Perror0_exit("setifflags: SIOCGLIFGROUPINFO");
1551 
1552 		(void) strlcpy(lifrl.lifr_name, lifgr.gi_grifname, LIFNAMSIZ);
1553 		if (ioctl(s, SIOCGLIFFLAGS, &lifrl) == -1)
1554 			Perror0_exit("setifflags: SIOCGLIFFLAGS");
1555 		if (!(lifrl.lifr_flags & IFF_UP)) {
1556 			lifrl.lifr_flags |= IFF_UP;
1557 			if (ioctl(s, SIOCSLIFFLAGS, &lifrl) == -1)
1558 				Perror0_exit("setifflags: SIOCSLIFFLAGS");
1559 		}
1560 	}
1561 
1562 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1563 	if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0)
1564 		Perror0_exit("setifflags: SIOCSLIFFLAGS");
1565 
1566 	if (bringup) {
1567 		lifr.lifr_flags |= IFF_UP;
1568 		if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0)
1569 			Perror0_exit("setifflags: SIOCSLIFFLAGS IFF_UP");
1570 	}
1571 
1572 	return (0);
1573 }
1574 
1575 /* ARGSUSED */
1576 static int
setifmetric(char * val,int64_t param)1577 setifmetric(char *val, int64_t param)
1578 {
1579 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1580 	lifr.lifr_metric = atoi(val);
1581 	if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
1582 		Perror0_exit("setifmetric: SIOCSLIFMETRIC");
1583 	return (0);
1584 }
1585 
1586 /* ARGSUSED */
1587 static int
setifmtu(char * val,int64_t param)1588 setifmtu(char *val, int64_t param)
1589 {
1590 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1591 	lifr.lifr_mtu = atoi(val);
1592 	if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
1593 		Perror0_exit("setifmtu: SIOCSLIFMTU");
1594 	return (0);
1595 }
1596 
1597 /* ARGSUSED */
1598 static int
setifindex(char * val,int64_t param)1599 setifindex(char *val, int64_t param)
1600 {
1601 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1602 	lifr.lifr_index = atoi(val);
1603 	if (ioctl(s, SIOCSLIFINDEX, (caddr_t)&lifr) < 0)
1604 		Perror0_exit("setifindex: SIOCSLIFINDEX");
1605 	return (0);
1606 }
1607 
1608 /* ARGSUSED */
1609 static void
notifycb(dlpi_handle_t dh,dlpi_notifyinfo_t * dnip,void * arg)1610 notifycb(dlpi_handle_t dh, dlpi_notifyinfo_t *dnip, void *arg)
1611 {
1612 }
1613 
1614 /* ARGSUSED */
1615 static int
setifether(char * addr,int64_t param)1616 setifether(char *addr, int64_t param)
1617 {
1618 	uchar_t		*hwaddr;
1619 	int		hwaddrlen;
1620 	int		retval;
1621 	ifaddrlistx_t	*ifaddrp, *ifaddrs = NULL;
1622 	dlpi_handle_t	dh;
1623 	dlpi_notifyid_t id;
1624 
1625 	if (addr == NULL) {
1626 		ifstatus(name);
1627 		print_ifether(name);
1628 		return (0);
1629 	}
1630 
1631 	/*
1632 	 * if the IP interface in the arguments is a logical
1633 	 * interface, exit with an error now.
1634 	 */
1635 	if (strchr(name, ':') != NULL) {
1636 		(void) fprintf(stderr, "ifconfig: cannot change"
1637 		    " ethernet address of a logical interface\n");
1638 		exit(1);
1639 	}
1640 
1641 	if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL) {
1642 		if (hwaddrlen == -1)
1643 			(void) fprintf(stderr,
1644 			    "ifconfig: bad ethernet address\n");
1645 		else
1646 			(void) fprintf(stderr, "ifconfig: malloc() failed\n");
1647 		exit(1);
1648 	}
1649 
1650 	if ((retval = dlpi_open(name, &dh, 0)) != DLPI_SUCCESS)
1651 		Perrdlpi_exit("cannot dlpi_open() link", name, retval);
1652 
1653 	retval = dlpi_enabnotify(dh, DL_NOTE_PHYS_ADDR, notifycb, NULL, &id);
1654 	if (retval == DLPI_SUCCESS) {
1655 		(void) dlpi_disabnotify(dh, id, NULL);
1656 	} else {
1657 		/*
1658 		 * This link does not support DL_NOTE_PHYS_ADDR: bring down
1659 		 * all of the addresses to flush the old hardware address
1660 		 * information out of IP.
1661 		 *
1662 		 * NOTE: Skipping this when DL_NOTE_PHYS_ADDR is supported is
1663 		 * more than an optimization: in.mpathd will set IFF_OFFLINE
1664 		 * if it's notified and the new address is a duplicate of
1665 		 * another in the group -- but the flags manipulation in
1666 		 * ifaddr_{down,up}() cannot be atomic and thus might clobber
1667 		 * IFF_OFFLINE, confusing in.mpathd.
1668 		 */
1669 		if (ifaddrlistx(name, IFF_UP, 0, &ifaddrs) == -1)
1670 			Perror2_exit(name, "cannot get address list");
1671 
1672 		ifaddrp = ifaddrs;
1673 		for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1674 			if (!ifaddr_down(ifaddrp)) {
1675 				Perror2_exit(ifaddrp->ia_name,
1676 				    "cannot bring down");
1677 			}
1678 		}
1679 	}
1680 
1681 	/*
1682 	 * Change the hardware address.
1683 	 */
1684 	retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr, hwaddrlen);
1685 	if (retval != DLPI_SUCCESS) {
1686 		(void) fprintf(stderr,
1687 		    "ifconfig: failed setting mac address on %s\n", name);
1688 	}
1689 	dlpi_close(dh);
1690 
1691 	/*
1692 	 * If any addresses were brought down before changing the hardware
1693 	 * address, bring them up again.
1694 	 */
1695 	for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1696 		if (!ifaddr_up(ifaddrp))
1697 			Perror2_exit(ifaddrp->ia_name, "cannot bring up");
1698 	}
1699 	ifaddrlistx_free(ifaddrs);
1700 
1701 	return (0);
1702 }
1703 
1704 /*
1705  * Print an interface's Ethernet address, if it has one.
1706  */
1707 static void
print_ifether(const char * ifname)1708 print_ifether(const char *ifname)
1709 {
1710 	int fd;
1711 
1712 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1713 
1714 	fd = socket(AF_INET, SOCK_DGRAM, 0);
1715 	if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1716 		/*
1717 		 * It's possible the interface is only configured for
1718 		 * IPv6; check again with AF_INET6.
1719 		 */
1720 		(void) close(fd);
1721 		fd = socket(AF_INET6, SOCK_DGRAM, 0);
1722 		if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1723 			(void) close(fd);
1724 			return;
1725 		}
1726 	}
1727 	(void) close(fd);
1728 
1729 	/* VNI and IPMP interfaces don't have MAC addresses */
1730 	if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP))
1731 		return;
1732 
1733 	/* IP tunnels also don't have Ethernet-like MAC addresses */
1734 	if (ifconfig_dladm_open(ifname, DATALINK_CLASS_IPTUN, NULL) ==
1735 	    DLADM_STATUS_OK)
1736 		return;
1737 
1738 	dlpi_print_address(ifname);
1739 }
1740 
1741 /*
1742  * static int find_all_interfaces(struct lifconf *lifcp, char **buf,
1743  *     int64_t lifc_flags)
1744  *
1745  * It finds all active data links.
1746  *
1747  * It takes in input a pointer to struct lifconf to receive interfaces
1748  * informations, a **char to hold allocated buffer, and a lifc_flags.
1749  *
1750  * Return values:
1751  *  0 = everything OK
1752  * -1 = problem
1753  */
1754 static int
find_all_interfaces(struct lifconf * lifcp,char ** buf,int64_t lifc_flags)1755 find_all_interfaces(struct lifconf *lifcp, char **buf, int64_t lifc_flags)
1756 {
1757 	unsigned bufsize;
1758 	int n;
1759 	ni_t *nip;
1760 	struct lifreq *lifrp;
1761 	dladm_status_t status;
1762 
1763 	if (!dlh_opened) {
1764 		status = ifconfig_dladm_open(NULL, 0, NULL);
1765 		if (status != DLADM_STATUS_OK)
1766 			dladmerr_exit(status, "unable to open dladm handle");
1767 	}
1768 
1769 	(void) dlpi_walk(ni_entry, dlh, 0);
1770 
1771 	/* Now, translate the linked list into a struct lifreq buffer */
1772 	if (num_ni == 0) {
1773 		lifcp->lifc_family = AF_UNSPEC;
1774 		lifcp->lifc_flags = lifc_flags;
1775 		lifcp->lifc_len = 0;
1776 		lifcp->lifc_buf = NULL;
1777 		return (0);
1778 	}
1779 
1780 	bufsize = num_ni * sizeof (struct lifreq);
1781 	if ((*buf = malloc(bufsize)) == NULL)
1782 		Perror0_exit("find_all_interfaces: malloc failed");
1783 
1784 	lifcp->lifc_family = AF_UNSPEC;
1785 	lifcp->lifc_flags = lifc_flags;
1786 	lifcp->lifc_len = bufsize;
1787 	lifcp->lifc_buf = *buf;
1788 
1789 	for (n = 0, lifrp = lifcp->lifc_req; n < num_ni; n++, lifrp++) {
1790 		nip = ni_list;
1791 		(void) strncpy(lifrp->lifr_name, nip->ni_name,
1792 		    sizeof (lifr.lifr_name));
1793 		ni_list = nip->ni_next;
1794 		free(nip);
1795 	}
1796 	return (0);
1797 }
1798 
1799 /*
1800  * Create the next unused logical interface using the original name
1801  * and assign the address (and mask if '/<n>' is part of the address).
1802  * Use the new logical interface for subsequent subcommands by updating
1803  * the name variable.
1804  *
1805  * This allows syntax like:
1806  *	ifconfig le0 addif 109.106.86.130 netmask + up \
1807  *	addif 109.106.86.131 netmask + up
1808  */
1809 /* ARGSUSED */
1810 static int
addif(char * str,int64_t param)1811 addif(char *str, int64_t param)
1812 {
1813 	int prefixlen = 0;
1814 	struct sockaddr_storage laddr;
1815 	struct sockaddr_storage mask;
1816 	struct sockaddr_in6 *sin6;
1817 	struct sockaddr_in *sin;
1818 	ipadm_status_t istatus;
1819 	char cidraddr[BUFSIZ];
1820 	char addrstr[INET6_ADDRSTRLEN];
1821 
1822 	(void) strncpy(name, origname, sizeof (name));
1823 
1824 	if (strchr(name, ':') != NULL) {
1825 		(void) fprintf(stderr,
1826 		    "ifconfig: addif: bad physical interface name %s\n",
1827 		    name);
1828 		exit(1);
1829 	}
1830 
1831 	/*
1832 	 * clear so parser will interpret next address as source followed
1833 	 * by possible dest
1834 	 */
1835 	setaddr = 0;
1836 	(*afp->af_getaddr)(str, (struct sockaddr *)&laddr, &prefixlen);
1837 
1838 	(void) memset(&mask, 0, sizeof (mask));
1839 	mask.ss_family = afp->af_af;
1840 
1841 	switch (prefixlen) {
1842 	case NO_PREFIX:
1843 		/* Nothing there - ok */
1844 		break;
1845 	case BAD_ADDR:
1846 		(void) fprintf(stderr,
1847 		    "ifconfig: Bad prefix length in %s\n", str);
1848 		exit(1);
1849 	default:
1850 		(void) memset(&mask, 0, sizeof (mask));
1851 		mask.ss_family = afp->af_af;
1852 		if (afp->af_af == AF_INET6) {
1853 			sin6 = (struct sockaddr_in6 *)&mask;
1854 			if (!in_prefixlentomask(prefixlen, IPV6_ABITS,
1855 			    (uchar_t *)&sin6->sin6_addr)) {
1856 				(void) fprintf(stderr, "ifconfig: "
1857 				    "Bad prefix length: %d\n",
1858 				    prefixlen);
1859 				exit(1);
1860 			}
1861 		} else {
1862 			sin = (struct sockaddr_in *)&mask;
1863 			if (!in_prefixlentomask(prefixlen, IP_ABITS,
1864 			    (uchar_t *)&sin->sin_addr)) {
1865 				(void) fprintf(stderr, "ifconfig: "
1866 				    "Bad prefix length: %d\n",
1867 				    prefixlen);
1868 				exit(1);
1869 			}
1870 		}
1871 		g_netmask_set = G_NETMASK_NIL;
1872 		break;
1873 	}
1874 
1875 	/*
1876 	 * This is a "hack" to get around the problem of SIOCLIFADDIF.  The
1877 	 * problem is that this ioctl does not include the netmask when
1878 	 * adding a logical interface.  This is the same problem described
1879 	 * in the ifconfig() comments.  To get around this problem, we first
1880 	 * add the logical interface with a 0 address.  After that, we set
1881 	 * the netmask if provided.  Finally we set the interface address.
1882 	 */
1883 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
1884 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
1885 
1886 	/* Note: no need to do DAD here since the interface isn't up yet. */
1887 
1888 	if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
1889 		Perror0_exit("addif: SIOCLIFADDIF");
1890 
1891 	(void) printf("Created new logical interface %s\n",
1892 	    lifr.lifr_name);
1893 	(void) strncpy(name, lifr.lifr_name, sizeof (name));
1894 
1895 	/*
1896 	 * Check and see if any "netmask" command is used and perform the
1897 	 * necessary operation.
1898 	 */
1899 	set_mask_lifreq(&lifr, &laddr, &mask);
1900 
1901 	/* This check is temporary until libipadm supports IPMP interfaces. */
1902 	if (ifconfig_use_libipadm(s, name)) {
1903 		/*
1904 		 * We added the logical interface above before calling
1905 		 * ipadm_create_addr(), because, with IPH_LEGACY, we need
1906 		 * to do an addif for `ifconfig ce0 addif <addr>' but not for
1907 		 * `ifconfig ce0 <addr>'. libipadm does not have a flag to
1908 		 * to differentiate between these two cases. To keep it simple,
1909 		 * we always create the logical interface and pass it to
1910 		 * libipadm instead of requiring libipadm to addif for some
1911 		 * cases and not do addif for other cases.
1912 		 */
1913 		istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name,
1914 		    &ipaddr);
1915 		if (istatus != IPADM_SUCCESS)
1916 			ipadmerr_exit(istatus, "addif");
1917 
1918 		if (af == AF_INET) {
1919 			sin = (struct sockaddr_in *)&laddr;
1920 			(void) inet_ntop(AF_INET, &sin->sin_addr, addrstr,
1921 			    sizeof (addrstr));
1922 		} else {
1923 			sin6 = (struct sockaddr_in6 *)&laddr;
1924 			(void) inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr,
1925 			    sizeof (addrstr));
1926 		}
1927 		/*
1928 		 * lifr.lifr_addr, which is updated by set_mask_lifreq()
1929 		 * will contain the right mask to use.
1930 		 */
1931 		prefixlen = mask2plen((struct sockaddr *)&lifr.lifr_addr);
1932 
1933 		(void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d",
1934 		    addrstr, prefixlen);
1935 
1936 		istatus = ipadm_set_addr(ipaddr, cidraddr, af);
1937 
1938 		if (istatus != IPADM_SUCCESS)
1939 			ipadmerr_exit(istatus, "could not set address");
1940 		setaddr++;
1941 		/*
1942 		 * address will be set by the parser after nextarg
1943 		 * has been scanned
1944 		 */
1945 		return (0);
1946 	}
1947 
1948 	/*
1949 	 * Only set the netmask if "netmask" command is used or a prefix is
1950 	 * provided.
1951 	 */
1952 	if (g_netmask_set == G_NETMASK_SET || prefixlen >= 0) {
1953 		/*
1954 		 * lifr.lifr_addr already contains netmask from
1955 		 * set_mask_lifreq().
1956 		 */
1957 		if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
1958 			Perror0_exit("addif: SIOCSLIFNETMASK");
1959 	}
1960 
1961 	/* Finally, we set the interface address. */
1962 	lifr.lifr_addr = laddr;
1963 	if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
1964 		Perror0_exit("SIOCSLIFADDR");
1965 
1966 	/*
1967 	 * let parser know we got a source.
1968 	 * Next address, if given, should be dest
1969 	 */
1970 	setaddr++;
1971 	return (0);
1972 }
1973 
1974 /*
1975  * Remove a logical interface based on its IP address. Unlike addif
1976  * there is no '/<n>' here.
1977  * Verifies that the interface is down before it is removed.
1978  */
1979 /* ARGSUSED */
1980 static int
removeif(char * str,int64_t param)1981 removeif(char *str, int64_t param)
1982 {
1983 	struct sockaddr_storage laddr;
1984 	ipadm_status_t istatus;
1985 	ipadm_addr_info_t *ainfo, *ainfop;
1986 
1987 	if (strchr(name, ':') != NULL) {
1988 		(void) fprintf(stderr,
1989 		    "ifconfig: removeif: bad physical interface name %s\n",
1990 		    name);
1991 		exit(1);
1992 	}
1993 
1994 	(*afp->af_getaddr)(str, &laddr, NULL);
1995 
1996 	/*
1997 	 * Following check is temporary until libipadm supports
1998 	 * IPMP interfaces.
1999 	 */
2000 	if (!ifconfig_use_libipadm(s, name))
2001 		goto delete;
2002 
2003 	/*
2004 	 * Get all addresses and search this address among the active
2005 	 * addresses. If an address object was found, delete using
2006 	 * ipadm_delete_addr().
2007 	 */
2008 	istatus = ipadm_addr_info(iph, name, &ainfo, 0, LIFC_DEFAULT);
2009 	if (istatus != IPADM_SUCCESS)
2010 		ipadmerr_exit(istatus, "removeif");
2011 
2012 	for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop))
2013 		if (sockaddrcmp(
2014 		    (struct sockaddr_storage *)ainfop->ia_ifa.ifa_addr, &laddr))
2015 			break;
2016 
2017 	if (ainfop != NULL) {
2018 		if (strchr(ainfop->ia_ifa.ifa_name, ':') == NULL) {
2019 			(void) fprintf(stderr,
2020 			    "ifconfig: removeif: cannot remove interface: %s\n",
2021 			    name);
2022 			exit(1);
2023 		}
2024 		if (ainfop->ia_aobjname[0] != '\0') {
2025 			istatus = ipadm_delete_addr(iph, ainfop->ia_aobjname,
2026 			    IPADM_OPT_ACTIVE);
2027 			if (istatus != IPADM_SUCCESS) {
2028 				ipadmerr_exit(istatus,
2029 				    "could not delete address");
2030 			}
2031 			ipadm_free_addr_info(ainfo);
2032 			return (0);
2033 		}
2034 	}
2035 	ipadm_free_addr_info(ainfo);
2036 
2037 delete:
2038 	/*
2039 	 * An address object for this address was not found in ipadm.
2040 	 * Delete with SIOCLIFREMOVEIF.
2041 	 */
2042 	lifr.lifr_addr = laddr;
2043 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2044 	if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
2045 		if (errno == EBUSY) {
2046 			/* This can only happen if ipif_id = 0 */
2047 			(void) fprintf(stderr,
2048 			    "ifconfig: removeif: cannot remove interface: %s\n",
2049 			    name);
2050 			exit(1);
2051 		}
2052 		Perror0_exit("removeif: SIOCLIFREMOVEIF");
2053 	}
2054 	return (0);
2055 }
2056 
2057 /*
2058  * Set the address token for IPv6.
2059  */
2060 /* ARGSUSED */
2061 static int
setiftoken(char * addr,int64_t param)2062 setiftoken(char *addr, int64_t param)
2063 {
2064 	int prefixlen = 0;
2065 	struct sockaddr_in6 token;
2066 
2067 	in6_getaddr(addr, (struct sockaddr *)&token, &prefixlen);
2068 	switch (prefixlen) {
2069 	case NO_PREFIX:
2070 		(void) fprintf(stderr,
2071 		    "ifconfig: Missing prefix length in subnet %s\n", addr);
2072 		exit(1);
2073 		/* NOTREACHED */
2074 	case BAD_ADDR:
2075 		(void) fprintf(stderr,
2076 		    "ifconfig: Bad prefix length in %s\n", addr);
2077 		exit(1);
2078 	default:
2079 		break;
2080 	}
2081 	(void) memcpy(&lifr.lifr_addr, &token, sizeof (token));
2082 	lifr.lifr_addrlen = prefixlen;
2083 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2084 	if (ioctl(s, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0)  {
2085 		Perror0_exit("setiftoken: SIOCSLIFTOKEN");
2086 	}
2087 	return (0);
2088 }
2089 
2090 /* ARGSUSED */
2091 static int
setifgroupname(char * grname,int64_t param)2092 setifgroupname(char *grname, int64_t param)
2093 {
2094 	lifgroupinfo_t		lifgr;
2095 	struct lifreq		lifrl;
2096 	ifaddrlistx_t		*ifaddrp, *nextifaddrp;
2097 	ifaddrlistx_t		*ifaddrs = NULL, *downaddrs = NULL;
2098 	int			af;
2099 
2100 	if (debug) {
2101 		(void) printf("Setting groupname %s on interface %s\n",
2102 		    grname, name);
2103 	}
2104 
2105 	(void) strlcpy(lifrl.lifr_name, name, LIFNAMSIZ);
2106 	(void) strlcpy(lifrl.lifr_groupname, grname, LIFGRNAMSIZ);
2107 
2108 	while (ioctl(s, SIOCSLIFGROUPNAME, &lifrl) == -1) {
2109 		switch (errno) {
2110 		case ENOENT:
2111 			/*
2112 			 * The group doesn't yet exist; create it and repeat.
2113 			 */
2114 			af = afp->af_af;
2115 			if (create_ipmp(grname, af, NULL, _B_TRUE) == -1) {
2116 				if (errno == EEXIST)
2117 					continue;
2118 
2119 				Perror2(grname, "cannot create IPMP group");
2120 				goto fail;
2121 			}
2122 			continue;
2123 
2124 		case EALREADY:
2125 			/*
2126 			 * The interface is already in another group; must
2127 			 * remove existing membership first.
2128 			 */
2129 			lifrl.lifr_groupname[0] = '\0';
2130 			if (ioctl(s, SIOCSLIFGROUPNAME, &lifrl) == -1) {
2131 				Perror2(name, "cannot remove existing "
2132 				    "IPMP group membership");
2133 				goto fail;
2134 			}
2135 			(void) strlcpy(lifrl.lifr_groupname, grname,
2136 			    LIFGRNAMSIZ);
2137 			continue;
2138 
2139 		case EAFNOSUPPORT:
2140 			/*
2141 			 * The group exists, but it's not configured with the
2142 			 * address families the interface needs.  Since only
2143 			 * two address families are currently supported, just
2144 			 * configure the "other" address family.  Note that we
2145 			 * may race with group deletion or creation by another
2146 			 * process (ENOENT or EEXIST); in such cases we repeat
2147 			 * our original SIOCSLIFGROUPNAME.
2148 			 */
2149 			(void) strlcpy(lifgr.gi_grname, grname, LIFGRNAMSIZ);
2150 			if (ioctl(s, SIOCGLIFGROUPINFO, &lifgr) == -1) {
2151 				if (errno == ENOENT)
2152 					continue;
2153 
2154 				Perror2(grname, "SIOCGLIFGROUPINFO");
2155 				goto fail;
2156 			}
2157 
2158 			af = lifgr.gi_v4 ? AF_INET6 : AF_INET;
2159 			if (create_ipmp(grname, af, lifgr.gi_grifname,
2160 			    _B_TRUE) == -1) {
2161 				if (errno == EEXIST)
2162 					continue;
2163 
2164 				Perror2(grname, "cannot configure IPMP group");
2165 				goto fail;
2166 			}
2167 			continue;
2168 
2169 		case EADDRINUSE:
2170 			/*
2171 			 * Some addresses are in-use (or under control of DAD).
2172 			 * Bring them down and retry the group join operation.
2173 			 * We will bring them back up after the interface has
2174 			 * been placed in the group.
2175 			 */
2176 			if (ifaddrlistx(lifrl.lifr_name, IFF_UP|IFF_DUPLICATE,
2177 			    0, &ifaddrs) == -1) {
2178 				Perror2(grname, "cannot get address list");
2179 				goto fail;
2180 			}
2181 
2182 			ifaddrp = ifaddrs;
2183 			for (; ifaddrp != NULL; ifaddrp = nextifaddrp) {
2184 				if (!ifaddr_down(ifaddrp)) {
2185 					ifaddrs = ifaddrp;
2186 					goto fail;
2187 				}
2188 				nextifaddrp = ifaddrp->ia_next;
2189 				ifaddrp->ia_next = downaddrs;
2190 				downaddrs = ifaddrp;
2191 			}
2192 			ifaddrs = NULL;
2193 			continue;
2194 
2195 		case EADDRNOTAVAIL: {
2196 			/*
2197 			 * Some data addresses are under application control.
2198 			 * For some of these (e.g., ADDRCONF), the application
2199 			 * should remove the address, in which case we retry a
2200 			 * few times (since the application's action is not
2201 			 * atomic with respect to us) before bailing out and
2202 			 * informing the user.
2203 			 */
2204 			int ntries, nappaddr = 0;
2205 			const if_appflags_t *iap = if_appflags_tbl;
2206 
2207 			for (; iap->ia_app != NULL; iap++) {
2208 				ntries = 0;
2209 again:
2210 				if (ifaddrlistx(lifrl.lifr_name, iap->ia_flag,
2211 				    IFF_NOFAILOVER, &ifaddrs) == -1) {
2212 					(void) fprintf(stderr, "ifconfig: %s: "
2213 					    "cannot get data addresses managed "
2214 					    "by %s\n", lifrl.lifr_name,
2215 					    iap->ia_app);
2216 					goto fail;
2217 				}
2218 
2219 				if (ifaddrs == NULL)
2220 					continue;
2221 
2222 				ifaddrlistx_free(ifaddrs);
2223 				ifaddrs = NULL;
2224 
2225 				if (++ntries < iap->ia_tries) {
2226 					(void) poll(NULL, 0, 100);
2227 					goto again;
2228 				}
2229 
2230 				(void) fprintf(stderr, "ifconfig: cannot join "
2231 				    "IPMP group: %s has data addresses managed "
2232 				    "by %s\n", lifrl.lifr_name, iap->ia_app);
2233 				nappaddr++;
2234 			}
2235 			if (nappaddr > 0)
2236 				goto fail;
2237 			continue;
2238 		}
2239 		default:
2240 			Perror2(name, "SIOCSLIFGROUPNAME");
2241 			goto fail;
2242 		}
2243 	}
2244 
2245 	/*
2246 	 * If the interface being moved is under the control of `ipmgmtd(1M)'
2247 	 * dameon then we should inform the daemon about this move, so that
2248 	 * the daemon can delete the state associated with this interface.
2249 	 *
2250 	 * This workaround is needed until the IPMP support in ipadm(1M).
2251 	 */
2252 	ipadm_if_move(iph, name);
2253 
2254 	/*
2255 	 * If there were addresses that we had to bring down, it's time to
2256 	 * bring them up again.  As part of bringing them up, the kernel will
2257 	 * automatically move them to the new IPMP interface.
2258 	 */
2259 	for (ifaddrp = downaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
2260 		if (!ifaddr_up(ifaddrp) && errno != ENXIO) {
2261 			(void) fprintf(stderr, "ifconfig: cannot bring back up "
2262 			    "%s: %s\n", ifaddrp->ia_name, strerror(errno));
2263 		}
2264 	}
2265 	ifaddrlistx_free(downaddrs);
2266 	return (0);
2267 fail:
2268 	/*
2269 	 * Attempt to bring back up any interfaces that we downed.
2270 	 */
2271 	for (ifaddrp = downaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
2272 		if (!ifaddr_up(ifaddrp) && errno != ENXIO) {
2273 			(void) fprintf(stderr, "ifconfig: cannot bring back up "
2274 			    "%s: %s\n", ifaddrp->ia_name, strerror(errno));
2275 		}
2276 	}
2277 	ifaddrlistx_free(downaddrs);
2278 	ifaddrlistx_free(ifaddrs);
2279 
2280 	/*
2281 	 * We'd return -1, but foreachinterface() doesn't propagate the error
2282 	 * into the exit status, so we're forced to explicitly exit().
2283 	 */
2284 	exit(1);
2285 	/* NOTREACHED */
2286 }
2287 
2288 static boolean_t
modcheck(const char * ifname)2289 modcheck(const char *ifname)
2290 {
2291 	(void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2292 
2293 	if (ioctl(s, SIOCGLIFFLAGS, &lifr) < 0) {
2294 		Perror0("SIOCGLIFFLAGS");
2295 		return (_B_FALSE);
2296 	}
2297 
2298 	if (lifr.lifr_flags & IFF_IPMP) {
2299 		(void) fprintf(stderr, "ifconfig: %s: module operations not"
2300 		    " supported on IPMP interfaces\n", ifname);
2301 		return (_B_FALSE);
2302 	}
2303 	if (lifr.lifr_flags & IFF_VIRTUAL) {
2304 		(void) fprintf(stderr, "ifconfig: %s: module operations not"
2305 		    " supported on virtual IP interfaces\n", ifname);
2306 		return (_B_FALSE);
2307 	}
2308 	return (_B_TRUE);
2309 }
2310 
2311 /*
2312  * To list all the modules above a given network interface.
2313  */
2314 /* ARGSUSED */
2315 static int
modlist(char * null,int64_t param)2316 modlist(char *null, int64_t param)
2317 {
2318 	int muxid_fd;
2319 	int muxfd;
2320 	int ipfd_lowstr;
2321 	int arpfd_lowstr;
2322 	int num_mods;
2323 	int i;
2324 	struct str_list strlist;
2325 	int orig_arpid;
2326 
2327 	/*
2328 	 * We'd return -1, but foreachinterface() doesn't propagate the error
2329 	 * into the exit status, so we're forced to explicitly exit().
2330 	 */
2331 	if (!modcheck(name))
2332 		exit(1);
2333 
2334 	if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr,
2335 	    &orig_arpid) < 0) {
2336 		return (-1);
2337 	}
2338 	if ((num_mods = ioctl(ipfd_lowstr, I_LIST, NULL)) < 0) {
2339 		Perror0("cannot I_LIST to get the number of modules");
2340 	} else {
2341 		if (debug > 0) {
2342 			(void) printf("Listing (%d) modules above %s\n",
2343 			    num_mods, name);
2344 		}
2345 
2346 		strlist.sl_nmods = num_mods;
2347 		strlist.sl_modlist = malloc(sizeof (struct str_mlist) *
2348 		    num_mods);
2349 		if (strlist.sl_modlist == NULL) {
2350 			Perror0("cannot malloc");
2351 		} else {
2352 			if (ioctl(ipfd_lowstr, I_LIST, (caddr_t)&strlist) < 0) {
2353 				Perror0("cannot I_LIST for module names");
2354 			} else {
2355 				for (i = 0; i < strlist.sl_nmods; i++) {
2356 					(void) printf("%d %s\n", i,
2357 					    strlist.sl_modlist[i].l_name);
2358 				}
2359 			}
2360 			free(strlist.sl_modlist);
2361 		}
2362 	}
2363 	return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr,
2364 	    orig_arpid));
2365 }
2366 
2367 #define	MODINSERT_OP	'i'
2368 #define	MODREMOVE_OP	'r'
2369 
2370 /*
2371  * To insert a module to the stream of the interface.  It is just a
2372  * wrapper.  The real function is modop().
2373  */
2374 /* ARGSUSED */
2375 static int
modinsert(char * arg,int64_t param)2376 modinsert(char *arg, int64_t param)
2377 {
2378 	return (modop(arg, MODINSERT_OP));
2379 }
2380 
2381 /*
2382  * To remove a module from the stream of the interface.  It is just a
2383  * wrapper.  The real function is modop().
2384  */
2385 /* ARGSUSED */
2386 static int
modremove(char * arg,int64_t param)2387 modremove(char *arg, int64_t param)
2388 {
2389 	return (modop(arg, MODREMOVE_OP));
2390 }
2391 
2392 /*
2393  * Helper function for mod*() functions.  It gets a fd to the lower IP
2394  * stream and I_PUNLINK's the lower stream.  It also initializes the
2395  * global variable lifr.
2396  *
2397  * Param:
2398  *	int *muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK
2399  *	int *muxid_fd: fd to /dev/udp{,6} for LIFMUXID
2400  *	int *ipfd_lowstr: fd to the lower IP stream.
2401  *	int *arpfd_lowstr: fd to the lower ARP stream.
2402  *
2403  * Return:
2404  *	-1 if operation fails, 0 otherwise.
2405  *
2406  * Please see the big block comment above ifplumb() for the logic of the
2407  * PLINK/PUNLINK
2408  */
2409 static int
ip_domux2fd(int * muxfd,int * muxid_fd,int * ipfd_lowstr,int * arpfd_lowstr,int * orig_arpid)2410 ip_domux2fd(int *muxfd, int *muxid_fd, int *ipfd_lowstr, int *arpfd_lowstr,
2411     int *orig_arpid)
2412 {
2413 	uint64_t	flags;
2414 	char		*udp_dev_name;
2415 
2416 	*orig_arpid = 0;
2417 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2418 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2419 		Perror0_exit("status: SIOCGLIFFLAGS");
2420 	}
2421 	flags = lifr.lifr_flags;
2422 	if (flags & IFF_IPV4) {
2423 		udp_dev_name = UDP_DEV_NAME;
2424 	} else if (flags & IFF_IPV6) {
2425 		udp_dev_name = UDP6_DEV_NAME;
2426 	} else {
2427 		return (-1);
2428 	}
2429 
2430 	if ((*muxid_fd = open(udp_dev_name, O_RDWR)) < 0) {
2431 		Perror2("open", udp_dev_name);
2432 		return (-1);
2433 	}
2434 	if (ioctl(*muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
2435 		Perror2("SIOCGLIFMUXID", udp_dev_name);
2436 		return (-1);
2437 	}
2438 	if (debug > 0) {
2439 		(void) printf("ARP_muxid %d IP_muxid %d\n",
2440 		    lifr.lifr_arp_muxid, lifr.lifr_ip_muxid);
2441 	}
2442 
2443 	/*
2444 	 * Use /dev/udp{,6} as the mux to avoid linkcycles.
2445 	 */
2446 	if (ipadm_open_arp_on_udp(udp_dev_name, muxfd) != IPADM_SUCCESS)
2447 		return (-1);
2448 
2449 	if (lifr.lifr_arp_muxid != 0) {
2450 		if ((*arpfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
2451 		    lifr.lifr_arp_muxid)) < 0) {
2452 			if ((errno == EINVAL) &&
2453 			    (flags & (IFF_NOARP | IFF_IPV6))) {
2454 				/*
2455 				 * Some plumbing utilities set the muxid to
2456 				 * -1 or some invalid value to signify that
2457 				 * there is no arp stream. Set the muxid to 0
2458 				 * before trying to unplumb the IP stream.
2459 				 * IP does not allow the IP stream to be
2460 				 * unplumbed if it sees a non-null arp muxid,
2461 				 * for consistency of IP-ARP streams.
2462 				 */
2463 				*orig_arpid = lifr.lifr_arp_muxid;
2464 				lifr.lifr_arp_muxid = 0;
2465 				(void) ioctl(*muxid_fd, SIOCSLIFMUXID,
2466 				    (caddr_t)&lifr);
2467 				*arpfd_lowstr = -1;
2468 			} else {
2469 				Perror0("_I_MUXID2FD");
2470 				return (-1);
2471 			}
2472 		} else if (ioctl(*muxfd, I_PUNLINK,
2473 		    lifr.lifr_arp_muxid) < 0) {
2474 			Perror2("I_PUNLINK", udp_dev_name);
2475 			return (-1);
2476 		}
2477 	} else {
2478 		*arpfd_lowstr = -1;
2479 	}
2480 
2481 	if ((*ipfd_lowstr = ioctl(*muxfd, _I_MUXID2FD,
2482 	    lifr.lifr_ip_muxid)) < 0) {
2483 		Perror0("_I_MUXID2FD");
2484 		/* Undo any changes we made */
2485 		if (*orig_arpid != 0) {
2486 			lifr.lifr_arp_muxid = *orig_arpid;
2487 			(void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2488 		}
2489 		return (-1);
2490 	}
2491 	if (ioctl(*muxfd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) {
2492 		Perror2("I_PUNLINK", udp_dev_name);
2493 		/* Undo any changes we made */
2494 		if (*orig_arpid != 0) {
2495 			lifr.lifr_arp_muxid = *orig_arpid;
2496 			(void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2497 		}
2498 		return (-1);
2499 	}
2500 	return (0);
2501 }
2502 
2503 /*
2504  * Helper function for mod*() functions.  It I_PLINK's back the upper and
2505  * lower IP streams.  Note that this function must be called after
2506  * ip_domux2fd().  In ip_domux2fd(), the global variable lifr is initialized
2507  * and ip_plink() needs information in lifr.  So ip_domux2fd() and ip_plink()
2508  * must be called in pairs.
2509  *
2510  * Param:
2511  *	int muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK
2512  *	int muxid_fd: fd to /dev/udp{,6} for LIFMUXID
2513  *	int ipfd_lowstr: fd to the lower IP stream.
2514  *	int arpfd_lowstr: fd to the lower ARP stream.
2515  *
2516  * Return:
2517  *	-1 if operation fails, 0 otherwise.
2518  *
2519  * Please see the big block comment above ifplumb() for the logic of the
2520  * PLINK/PUNLINK
2521  */
2522 static int
ip_plink(int muxfd,int muxid_fd,int ipfd_lowstr,int arpfd_lowstr,int orig_arpid)2523 ip_plink(int muxfd, int muxid_fd, int ipfd_lowstr, int arpfd_lowstr,
2524     int orig_arpid)
2525 {
2526 	int ip_muxid;
2527 
2528 	ip_muxid = ioctl(muxfd, I_PLINK, ipfd_lowstr);
2529 	if (ip_muxid < 0) {
2530 		Perror2("I_PLINK", UDP_DEV_NAME);
2531 		return (-1);
2532 	}
2533 
2534 	/*
2535 	 * If there is an arp stream, plink it. If there is no
2536 	 * arp stream, then it is possible that the plumbing
2537 	 * utility could have stored any value in the arp_muxid.
2538 	 * If so, restore it from orig_arpid.
2539 	 */
2540 	if (arpfd_lowstr != -1) {
2541 		if (ioctl(muxfd, I_PLINK, arpfd_lowstr) < 0) {
2542 			Perror2("I_PLINK", UDP_DEV_NAME);
2543 			return (-1);
2544 		}
2545 	} else if (orig_arpid != 0) {
2546 		/* Undo the changes we did in ip_domux2fd */
2547 		lifr.lifr_arp_muxid = orig_arpid;
2548 		lifr.lifr_ip_muxid = ip_muxid;
2549 		(void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
2550 	}
2551 
2552 	(void) close(muxfd);
2553 	(void) close(muxid_fd);
2554 	return (0);
2555 }
2556 
2557 /*
2558  * The real function to perform module insertion/removal.
2559  *
2560  * Param:
2561  *	char *arg: the argument string module_name@position
2562  *	char op: operation, either MODINSERT_OP or MODREMOVE_OP.
2563  *
2564  * Return:
2565  *	Before doing ip_domux2fd(), this function calls exit(1) in case of
2566  *	error.  After ip_domux2fd() is done, it returns -1 for error, 0
2567  *	otherwise.
2568  */
2569 static int
modop(char * arg,char op)2570 modop(char *arg, char op)
2571 {
2572 	char *pos_p;
2573 	int muxfd;
2574 	int muxid_fd;
2575 	int ipfd_lowstr;  /* IP stream (lower stream of mux) to be plinked */
2576 	int arpfd_lowstr; /* ARP stream (lower stream of mux) to be plinked */
2577 	struct strmodconf mod;
2578 	char *at_char = "@";
2579 	char *arg_str;
2580 	int orig_arpid;
2581 
2582 	/*
2583 	 * We'd return -1, but foreachinterface() doesn't propagate the error
2584 	 * into the exit status, so we're forced to explicitly exit().
2585 	 */
2586 	if (!modcheck(name))
2587 		exit(1);
2588 
2589 	/* Need to save the original string for -a option. */
2590 	if ((arg_str = malloc(strlen(arg) + 1)) == NULL) {
2591 		Perror0("cannot malloc");
2592 		return (-1);
2593 	}
2594 	(void) strcpy(arg_str, arg);
2595 
2596 	if (*arg_str == *at_char) {
2597 		(void) fprintf(stderr,
2598 		    "ifconfig: must supply a module name\n");
2599 		exit(1);
2600 	}
2601 	mod.mod_name = strtok(arg_str, at_char);
2602 	if (strlen(mod.mod_name) > FMNAMESZ) {
2603 		(void) fprintf(stderr, "ifconfig: module name too long: %s\n",
2604 		    mod.mod_name);
2605 		exit(1);
2606 	}
2607 
2608 	/*
2609 	 * Need to make sure that the core TCP/IP stack modules are not
2610 	 * removed.  Otherwise, "bad" things can happen.  If a module
2611 	 * is removed and inserted back, it loses its old state.  But
2612 	 * the modules above it still have the old state.  E.g. IP assumes
2613 	 * fast data path while tunnel after re-inserted assumes that it can
2614 	 * receive M_DATA only in fast data path for which it does not have
2615 	 * any state.  This is a general caveat of _I_REMOVE/_I_INSERT.
2616 	 */
2617 	if (op == MODREMOVE_OP &&
2618 	    (strcmp(mod.mod_name, ARP_MOD_NAME) == 0 ||
2619 	    strcmp(mod.mod_name, IP_MOD_NAME) == 0)) {
2620 		(void) fprintf(stderr, "ifconfig: cannot remove %s\n",
2621 		    mod.mod_name);
2622 		exit(1);
2623 	}
2624 
2625 	if ((pos_p = strtok(NULL, at_char)) == NULL) {
2626 		(void) fprintf(stderr, "ifconfig: must supply a position\n");
2627 		exit(1);
2628 	}
2629 	mod.pos = atoi(pos_p);
2630 
2631 	if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr,
2632 	    &orig_arpid) < 0) {
2633 		free(arg_str);
2634 		return (-1);
2635 	}
2636 	switch (op) {
2637 	case MODINSERT_OP:
2638 		if (debug > 0) {
2639 			(void) printf("Inserting module %s at %d\n",
2640 			    mod.mod_name, mod.pos);
2641 		}
2642 		if (ioctl(ipfd_lowstr, _I_INSERT, (caddr_t)&mod) < 0) {
2643 			Perror2("fail to insert module", mod.mod_name);
2644 		}
2645 		break;
2646 	case MODREMOVE_OP:
2647 		if (debug > 0) {
2648 			(void) printf("Removing module %s at %d\n",
2649 			    mod.mod_name, mod.pos);
2650 		}
2651 		if (ioctl(ipfd_lowstr, _I_REMOVE, (caddr_t)&mod) < 0) {
2652 			Perror2("fail to remove module", mod.mod_name);
2653 		}
2654 		break;
2655 	default:
2656 		/* Should never get to here. */
2657 		(void) fprintf(stderr, "Unknown operation\n");
2658 		break;
2659 	}
2660 	free(arg_str);
2661 	return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr,
2662 	    orig_arpid));
2663 }
2664 
2665 static int
modify_tun(iptun_params_t * params)2666 modify_tun(iptun_params_t *params)
2667 {
2668 	dladm_status_t status;
2669 
2670 	if ((status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN,
2671 	    &params->iptun_param_linkid)) == DLADM_STATUS_OK)
2672 		status = dladm_iptun_modify(dlh, params, DLADM_OPT_ACTIVE);
2673 	if (status != DLADM_STATUS_OK)
2674 		dladmerr_exit(status, name);
2675 	return (0);
2676 }
2677 
2678 /*
2679  * Set tunnel source address
2680  */
2681 /* ARGSUSED */
2682 static int
setiftsrc(char * addr,int64_t param)2683 setiftsrc(char *addr, int64_t param)
2684 {
2685 	iptun_params_t params;
2686 
2687 	params.iptun_param_flags = IPTUN_PARAM_LADDR;
2688 	(void) strlcpy(params.iptun_param_laddr, addr,
2689 	    sizeof (params.iptun_param_laddr));
2690 	return (modify_tun(&params));
2691 }
2692 
2693 /*
2694  * Set tunnel destination address
2695  */
2696 /* ARGSUSED */
2697 static int
setiftdst(char * addr,int64_t param)2698 setiftdst(char *addr, int64_t param)
2699 {
2700 	iptun_params_t params;
2701 
2702 	params.iptun_param_flags = IPTUN_PARAM_RADDR;
2703 	(void) strlcpy(params.iptun_param_raddr, addr,
2704 	    sizeof (params.iptun_param_raddr));
2705 	return (modify_tun(&params));
2706 }
2707 
2708 static int
set_tun_prop(const char * propname,char * value)2709 set_tun_prop(const char *propname, char *value)
2710 {
2711 	dladm_status_t	status;
2712 	datalink_id_t	linkid;
2713 
2714 	status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, &linkid);
2715 	if (status == DLADM_STATUS_OK) {
2716 		status = dladm_set_linkprop(dlh, linkid, propname, &value, 1,
2717 		    DLADM_OPT_ACTIVE);
2718 	}
2719 	if (status != DLADM_STATUS_OK)
2720 		dladmerr_exit(status, name);
2721 	return (0);
2722 }
2723 
2724 /* Set tunnel encapsulation limit. */
2725 /* ARGSUSED */
2726 static int
set_tun_encap_limit(char * arg,int64_t param)2727 set_tun_encap_limit(char *arg, int64_t param)
2728 {
2729 	return (set_tun_prop("encaplimit", arg));
2730 }
2731 
2732 /* Disable encapsulation limit. */
2733 /* ARGSUSED */
2734 static int
clr_tun_encap_limit(char * arg,int64_t param)2735 clr_tun_encap_limit(char *arg, int64_t param)
2736 {
2737 	return (set_tun_encap_limit("-1", 0));
2738 }
2739 
2740 /* Set tunnel hop limit. */
2741 /* ARGSUSED */
2742 static int
set_tun_hop_limit(char * arg,int64_t param)2743 set_tun_hop_limit(char *arg, int64_t param)
2744 {
2745 	return (set_tun_prop("hoplimit", arg));
2746 }
2747 
2748 /* Set zone ID */
2749 static int
setzone(char * arg,int64_t param)2750 setzone(char *arg, int64_t param)
2751 {
2752 	zoneid_t zoneid = GLOBAL_ZONEID;
2753 
2754 	if (param == NEXTARG) {
2755 		/* zone must be active */
2756 		if ((zoneid = getzoneidbyname(arg)) == -1) {
2757 			(void) fprintf(stderr,
2758 			    "ifconfig: unknown zone '%s'\n", arg);
2759 			exit(1);
2760 		}
2761 	}
2762 	(void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2763 	lifr.lifr_zoneid = zoneid;
2764 	if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1)
2765 		Perror0_exit("SIOCSLIFZONE");
2766 	return (0);
2767 }
2768 
2769 /* Put interface into all zones */
2770 /* ARGSUSED */
2771 static int
setallzones(char * arg,int64_t param)2772 setallzones(char *arg, int64_t param)
2773 {
2774 	(void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2775 	lifr.lifr_zoneid = ALL_ZONES;
2776 	if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1)
2777 		Perror0_exit("SIOCSLIFZONE");
2778 	return (0);
2779 }
2780 
2781 /* Set source address to use */
2782 /* ARGSUSED */
2783 static int
setifsrc(char * arg,int64_t param)2784 setifsrc(char *arg, int64_t param)
2785 {
2786 	uint_t ifindex = 0;
2787 	int rval;
2788 
2789 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2790 
2791 	/*
2792 	 * Argument can be either an interface name or "none". The latter means
2793 	 * that any previous selection is cleared.
2794 	 */
2795 
2796 	if (strchr(arg, ':') != NULL) {
2797 		(void) fprintf(stderr,
2798 		    "ifconfig: Cannot specify logical interface for usesrc \n");
2799 		exit(1);
2800 	}
2801 
2802 	rval = strcmp(arg, NONE_STR);
2803 	if (rval != 0) {
2804 		if ((ifindex = if_nametoindex(arg)) == 0) {
2805 			(void) strncpy(lifr.lifr_name, arg, LIFNAMSIZ);
2806 			Perror0_exit("Could not get interface index");
2807 		}
2808 		lifr.lifr_index = ifindex;
2809 	} else {
2810 		if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) != 0)
2811 			Perror0_exit("Not a valid usesrc consumer");
2812 		lifr.lifr_index = 0;
2813 	}
2814 
2815 	if (debug)
2816 		(void) printf("setifsrc: lifr_name %s, lifr_index %d\n",
2817 		    lifr.lifr_name, lifr.lifr_index);
2818 
2819 	if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) == -1) {
2820 		if (rval == 0)
2821 			Perror0_exit("Cannot reset usesrc group");
2822 		else
2823 			Perror0_exit("Could not set source interface");
2824 	}
2825 
2826 	return (0);
2827 }
2828 
2829 /*
2830  * Print the interface status line associated with `ifname'
2831  */
2832 static void
ifstatus(const char * ifname)2833 ifstatus(const char *ifname)
2834 {
2835 	uint64_t flags;
2836 	char if_usesrc_name[LIFNAMSIZ];
2837 	char *newbuf;
2838 	int n, numifs, rval = 0;
2839 	struct lifreq *lifrp;
2840 	struct lifsrcof lifs;
2841 
2842 	(void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2843 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2844 		Perror0_exit("status: SIOCGLIFFLAGS");
2845 	}
2846 	flags = lifr.lifr_flags;
2847 
2848 	/*
2849 	 * In V4 compatibility mode, we don't print the IFF_IPV4 flag or
2850 	 * interfaces with IFF_IPV6 set.
2851 	 */
2852 	if (v4compat) {
2853 		flags &= ~IFF_IPV4;
2854 		if (flags & IFF_IPV6)
2855 			return;
2856 	}
2857 
2858 	(void) printf("%s: ", ifname);
2859 	print_flags(flags);
2860 
2861 	(void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2862 	if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
2863 		Perror0_exit("status: SIOCGLIFMETRIC");
2864 	} else {
2865 		if (lifr.lifr_metric)
2866 			(void) printf(" metric %d", lifr.lifr_metric);
2867 	}
2868 	if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
2869 		(void) printf(" mtu %u", lifr.lifr_mtu);
2870 
2871 	/* don't print index or zone when in compatibility mode */
2872 	if (!v4compat) {
2873 		if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
2874 			(void) printf(" index %d", lifr.lifr_index);
2875 		/*
2876 		 * Stack instances use GLOBAL_ZONEID for IP data structures
2877 		 * even in the non-global zone.
2878 		 */
2879 		if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) >= 0 &&
2880 		    lifr.lifr_zoneid != getzoneid() &&
2881 		    lifr.lifr_zoneid != GLOBAL_ZONEID) {
2882 			char zone_name[ZONENAME_MAX];
2883 
2884 			if (lifr.lifr_zoneid == ALL_ZONES) {
2885 				(void) printf("\n\tall-zones");
2886 			} else if (getzonenamebyid(lifr.lifr_zoneid, zone_name,
2887 			    sizeof (zone_name)) < 0) {
2888 				(void) printf("\n\tzone %d", lifr.lifr_zoneid);
2889 			} else {
2890 				(void) printf("\n\tzone %s", zone_name);
2891 			}
2892 		}
2893 	}
2894 
2895 	if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) {
2896 		lifs.lifs_ifindex = lifr.lifr_index;
2897 
2898 		/*
2899 		 * Find the number of interfaces that use this interfaces'
2900 		 * address as a source address
2901 		 */
2902 		lifs.lifs_buf = NULL;
2903 		lifs.lifs_maxlen = 0;
2904 		for (;;) {
2905 			/* The first pass will give the bufsize we need */
2906 			rval = ioctl(s, SIOCGLIFSRCOF, (char *)&lifs);
2907 			if (rval < 0) {
2908 				if (lifs.lifs_buf != NULL) {
2909 					free(lifs.lifs_buf);
2910 					lifs.lifs_buf = NULL;
2911 				}
2912 				lifs.lifs_len = 0;
2913 				break;
2914 			}
2915 			if (lifs.lifs_len <= lifs.lifs_maxlen)
2916 				break;
2917 			/* Use kernel's size + a small margin to avoid loops */
2918 			lifs.lifs_maxlen = lifs.lifs_len +
2919 			    5 * sizeof (struct lifreq);
2920 			/* For the first pass, realloc acts like malloc */
2921 			newbuf = realloc(lifs.lifs_buf, lifs.lifs_maxlen);
2922 			if (newbuf == NULL) {
2923 				if (lifs.lifs_buf != NULL) {
2924 					free(lifs.lifs_buf);
2925 					lifs.lifs_buf = NULL;
2926 				}
2927 				lifs.lifs_len = 0;
2928 				break;
2929 			}
2930 			lifs.lifs_buf = newbuf;
2931 		}
2932 
2933 
2934 		numifs = lifs.lifs_len / sizeof (struct lifreq);
2935 		if (numifs > 0) {
2936 			lifrp = lifs.lifs_req;
2937 			(void) printf("\n\tsrcof");
2938 			for (n = numifs; n > 0; n--, lifrp++) {
2939 				(void) printf(" %s", lifrp->lifr_name);
2940 			}
2941 		}
2942 
2943 		if (lifs.lifs_buf != NULL)
2944 			free(lifs.lifs_buf);
2945 	}
2946 
2947 	/* Find the interface whose source address this interface uses */
2948 	if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
2949 		if (lifr.lifr_index != 0) {
2950 			if (if_indextoname(lifr.lifr_index,
2951 			    if_usesrc_name) == NULL) {
2952 				(void) printf("\n\tusesrc ifIndex %d",
2953 				    lifr.lifr_index);
2954 			} else {
2955 				(void) printf("\n\tusesrc %s", if_usesrc_name);
2956 			}
2957 		}
2958 	}
2959 
2960 	(void) putchar('\n');
2961 }
2962 
2963 /*
2964  * Print the status of the interface.  If an address family was
2965  * specified, show it and it only; otherwise, show them all.
2966  */
2967 static void
status(void)2968 status(void)
2969 {
2970 	struct afswtch	*p = afp;
2971 	uint64_t	flags;
2972 	datalink_id_t	linkid;
2973 
2974 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
2975 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
2976 		Perror0_exit("status: SIOCGLIFFLAGS");
2977 	}
2978 
2979 	flags = lifr.lifr_flags;
2980 
2981 	/*
2982 	 * Only print the interface status if the address family matches
2983 	 * the interface family flag.
2984 	 */
2985 	if (p != NULL) {
2986 		if (((p->af_af == AF_INET6) && (flags & IFF_IPV4)) ||
2987 		    ((p->af_af == AF_INET) && (flags & IFF_IPV6)))
2988 			return;
2989 	}
2990 
2991 	/*
2992 	 * In V4 compatibility mode, don't print IFF_IPV6 interfaces.
2993 	 */
2994 	if (v4compat && (flags & IFF_IPV6))
2995 		return;
2996 
2997 	ifstatus(name);
2998 
2999 	if (ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, &linkid) ==
3000 	    DLADM_STATUS_OK)
3001 		tun_status(linkid);
3002 
3003 	if (p != NULL) {
3004 		(*p->af_status)(1, flags);
3005 	} else {
3006 		for (p = afs; p->af_name; p++) {
3007 			/* set global af for use in p->af_status */
3008 			af = p->af_af;
3009 			(*p->af_status)(0, flags);
3010 		}
3011 
3012 		/*
3013 		 * Historically, 'ether' has been an address family,
3014 		 * so print it here.
3015 		 */
3016 		print_ifether(name);
3017 	}
3018 }
3019 
3020 /*
3021  * Print the status of the interface in a format that can be used to
3022  * reconfigure the interface later. Code stolen from status() above.
3023  */
3024 /* ARGSUSED */
3025 static int
configinfo(char * null,int64_t param)3026 configinfo(char *null, int64_t param)
3027 {
3028 	char *cp;
3029 	struct afswtch *p = afp;
3030 	uint64_t flags;
3031 	char lifname[LIFNAMSIZ];
3032 	char if_usesrc_name[LIFNAMSIZ];
3033 
3034 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3035 
3036 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
3037 		Perror0_exit("status: SIOCGLIFFLAGS");
3038 	}
3039 	flags = lifr.lifr_flags;
3040 
3041 	if (debug) {
3042 		(void) printf("configinfo: name %s flags  0x%llx af_af %d\n",
3043 		    name, flags, p != NULL ? p->af_af : -1);
3044 	}
3045 
3046 	/*
3047 	 * Build the interface name to print (we cannot directly use `name'
3048 	 * because one cannot "plumb" ":0" interfaces).
3049 	 */
3050 	(void) strlcpy(lifname, name, LIFNAMSIZ);
3051 	if ((cp = strchr(lifname, ':')) != NULL && atoi(cp + 1) == 0)
3052 		*cp = '\0';
3053 
3054 	/*
3055 	 * if the interface is IPv4
3056 	 *	if we have a IPv6 address family restriction return
3057 	 *		so it won't print
3058 	 *	if we are in IPv4 compatibility mode, clear out IFF_IPV4
3059 	 *		so we don't print it.
3060 	 */
3061 	if (flags & IFF_IPV4) {
3062 		if (p && p->af_af == AF_INET6)
3063 			return (-1);
3064 		if (v4compat)
3065 			flags &= ~IFF_IPV4;
3066 
3067 		(void) printf("%s inet plumb", lifname);
3068 	} else if (flags & IFF_IPV6) {
3069 		/*
3070 		 * else if the interface is IPv6
3071 		 *	if we have a IPv4 address family restriction return
3072 		 *	or we are in IPv4 compatibiltiy mode, return.
3073 		 */
3074 		if (p && p->af_af == AF_INET)
3075 			return (-1);
3076 		if (v4compat)
3077 			return (-1);
3078 
3079 		(void) printf("%s inet6 plumb", lifname);
3080 	}
3081 
3082 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3083 	if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
3084 		Perror0_exit("configinfo: SIOCGLIFMETRIC");
3085 	} else {
3086 		if (lifr.lifr_metric)
3087 			(void) printf(" metric %d ", lifr.lifr_metric);
3088 	}
3089 	if (((flags & (IFF_VIRTUAL|IFF_LOOPBACK)) != IFF_VIRTUAL) &&
3090 	    ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0)
3091 		(void) printf(" mtu %d", lifr.lifr_metric);
3092 
3093 	/* Index only applies to the zeroth interface */
3094 	if (lifnum(name) == 0) {
3095 		if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0)
3096 			(void) printf(" index %d", lifr.lifr_index);
3097 	}
3098 
3099 	if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) {
3100 		if (lifr.lifr_index != 0) {
3101 			if (if_indextoname(lifr.lifr_index,
3102 			    if_usesrc_name) != NULL) {
3103 				(void) printf(" usesrc %s", if_usesrc_name);
3104 			}
3105 		}
3106 	}
3107 
3108 	if (p != NULL) {
3109 		(*p->af_configinfo)(1, flags);
3110 	} else {
3111 		for (p = afs; p->af_name; p++) {
3112 			(void) close(s);
3113 			s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0);
3114 			/* set global af for use in p->af_configinfo */
3115 			af = p->af_af;
3116 			if (s == -1) {
3117 				Perror0_exit("socket");
3118 			}
3119 			(*p->af_configinfo)(0, flags);
3120 		}
3121 	}
3122 
3123 	(void) putchar('\n');
3124 	return (0);
3125 }
3126 
3127 static void
print_tsec(iptun_params_t * params)3128 print_tsec(iptun_params_t *params)
3129 {
3130 	ipsec_req_t *ipsr;
3131 
3132 	(void) printf("\ttunnel security settings  ");
3133 	if (!(params->iptun_param_flags & IPTUN_PARAM_SECINFO)) {
3134 		(void) printf("-->  use 'ipsecconf -ln -i %s'", name);
3135 	} else {
3136 		ipsr = &params->iptun_param_secinfo;
3137 		if (ipsr->ipsr_ah_req & IPSEC_PREF_REQUIRED) {
3138 			(void) printf("ah (%s)  ",
3139 			    rparsealg(ipsr->ipsr_auth_alg, IPSEC_PROTO_AH));
3140 		}
3141 		if (ipsr->ipsr_esp_req & IPSEC_PREF_REQUIRED) {
3142 			(void) printf("esp (%s",
3143 			    rparsealg(ipsr->ipsr_esp_alg, IPSEC_PROTO_ESP));
3144 			(void) printf("/%s)",
3145 			    rparsealg(ipsr->ipsr_esp_auth_alg, IPSEC_PROTO_AH));
3146 		}
3147 	}
3148 	(void) printf("\n");
3149 }
3150 
3151 static void
tun_status(datalink_id_t linkid)3152 tun_status(datalink_id_t linkid)
3153 {
3154 	iptun_params_t	params;
3155 	char		propval[DLADM_PROP_VAL_MAX];
3156 	char		*valptr[1];
3157 	uint_t		valcnt = 1;
3158 	boolean_t	tabbed = _B_FALSE;
3159 
3160 	params.iptun_param_linkid = linkid;
3161 
3162 	/* If dladm_iptun_getparams() fails, assume we are not a tunnel. */
3163 	assert(dlh_opened);
3164 	if (dladm_iptun_getparams(dlh, &params, DLADM_OPT_ACTIVE) !=
3165 	    DLADM_STATUS_OK)
3166 		return;
3167 
3168 	switch (params.iptun_param_type) {
3169 	case IPTUN_TYPE_IPV4:
3170 	case IPTUN_TYPE_6TO4:
3171 		(void) printf("\tinet");
3172 		break;
3173 	case IPTUN_TYPE_IPV6:
3174 		(void) printf("\tinet6");
3175 		break;
3176 	default:
3177 		dladmerr_exit(DLADM_STATUS_IPTUNTYPE, name);
3178 		break;
3179 	}
3180 
3181 	/*
3182 	 * There is always a source address.  If it hasn't been explicitly
3183 	 * set, the API will pass back a buffer containing the unspecified
3184 	 * address.
3185 	 */
3186 	(void) printf(" tunnel src %s ", params.iptun_param_laddr);
3187 
3188 	if (params.iptun_param_flags & IPTUN_PARAM_RADDR)
3189 		(void) printf("tunnel dst %s\n", params.iptun_param_raddr);
3190 	else
3191 		(void) putchar('\n');
3192 
3193 	if (params.iptun_param_flags & IPTUN_PARAM_IPSECPOL)
3194 		print_tsec(&params);
3195 
3196 	valptr[0] = propval;
3197 	if (dladm_get_linkprop(dlh, linkid, DLADM_PROP_VAL_CURRENT, "hoplimit",
3198 	    (char **)valptr, &valcnt) == DLADM_STATUS_OK) {
3199 		(void) printf("\ttunnel hop limit %s ", propval);
3200 		tabbed = _B_TRUE;
3201 	}
3202 
3203 	if (dladm_get_linkprop(dlh, linkid, DLADM_PROP_VAL_CURRENT,
3204 	    "encaplimit", (char **)valptr, &valcnt) == DLADM_STATUS_OK) {
3205 		uint32_t elim;
3206 
3207 		if (!tabbed) {
3208 			(void) putchar('\t');
3209 			tabbed = _B_TRUE;
3210 		}
3211 		elim = strtol(propval, NULL, 10);
3212 		if (elim > 0)
3213 			(void) printf("tunnel encapsulation limit %s", propval);
3214 		else
3215 			(void) printf("tunnel encapsulation limit disabled");
3216 	}
3217 
3218 	if (tabbed)
3219 		(void) putchar('\n');
3220 }
3221 
3222 static void
in_status(int force,uint64_t flags)3223 in_status(int force, uint64_t flags)
3224 {
3225 	struct sockaddr_in	*sin, *laddr;
3226 	struct sockaddr_in	netmask = { AF_INET };
3227 
3228 	if (debug)
3229 		(void) printf("in_status(%s) flags 0x%llx\n", name, flags);
3230 
3231 	/* only print status for IPv4 interfaces */
3232 	if (!(flags & IFF_IPV4))
3233 		return;
3234 
3235 	if (!(flags & IFF_NOLOCAL)) {
3236 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3237 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3238 			if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3239 			    errno == ENXIO) {
3240 				if (!force)
3241 					return;
3242 				(void) memset(&lifr.lifr_addr, 0,
3243 				    sizeof (lifr.lifr_addr));
3244 			} else
3245 				Perror0_exit("in_status: SIOCGLIFADDR");
3246 		}
3247 		sin = (struct sockaddr_in *)&lifr.lifr_addr;
3248 		(void) printf("\tinet %s ", inet_ntoa(sin->sin_addr));
3249 		laddr = sin;
3250 	} else {
3251 		(void) printf("\tinet ");
3252 	}
3253 
3254 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3255 	if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3256 		if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3257 		    errno == ENXIO) {
3258 			if (!force)
3259 				return;
3260 			(void) memset(&lifr.lifr_addr, 0,
3261 			    sizeof (lifr.lifr_addr));
3262 		} else {
3263 			Perror0_exit("in_status: SIOCGLIFSUBNET");
3264 		}
3265 	}
3266 	sin = (struct sockaddr_in *)&lifr.lifr_addr;
3267 	if ((flags & IFF_NOLOCAL) ||
3268 	    sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
3269 		(void) printf("subnet %s/%d ", inet_ntoa(sin->sin_addr),
3270 		    lifr.lifr_addrlen);
3271 	}
3272 	if (sin->sin_family != AF_INET) {
3273 		(void) printf("Wrong family: %d\n", sin->sin_family);
3274 	}
3275 
3276 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3277 	if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
3278 		if (errno != EADDRNOTAVAIL)
3279 			Perror0_exit("in_status: SIOCGLIFNETMASK");
3280 		(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
3281 	} else
3282 		netmask.sin_addr =
3283 		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
3284 	if (flags & IFF_POINTOPOINT) {
3285 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3286 		if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3287 			if (errno == EADDRNOTAVAIL)
3288 				(void) memset(&lifr.lifr_addr, 0,
3289 				    sizeof (lifr.lifr_addr));
3290 			else
3291 				Perror0_exit("in_status: SIOCGLIFDSTADDR");
3292 		}
3293 		sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
3294 		(void) printf("--> %s ", inet_ntoa(sin->sin_addr));
3295 	}
3296 	(void) printf("netmask %x ", ntohl(netmask.sin_addr.s_addr));
3297 	if (flags & IFF_BROADCAST) {
3298 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3299 		if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
3300 			if (errno == EADDRNOTAVAIL)
3301 				(void) memset(&lifr.lifr_addr, 0,
3302 				    sizeof (lifr.lifr_addr));
3303 			else
3304 				Perror0_exit("in_status: SIOCGLIFBRDADDR");
3305 		}
3306 		sin = (struct sockaddr_in *)&lifr.lifr_addr;
3307 		if (sin->sin_addr.s_addr != 0) {
3308 			(void) printf("broadcast %s",
3309 			    inet_ntoa(sin->sin_addr));
3310 		}
3311 	}
3312 	/* If there is a groupname, print it for only the physical interface */
3313 	if (strchr(name, ':') == NULL) {
3314 		if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3315 		    lifr.lifr_groupname[0] != '\0') {
3316 			(void) printf("\n\tgroupname %s", lifr.lifr_groupname);
3317 		}
3318 	}
3319 	(void) putchar('\n');
3320 }
3321 
3322 static void
in6_status(int force,uint64_t flags)3323 in6_status(int force, uint64_t flags)
3324 {
3325 	char			abuf[INET6_ADDRSTRLEN];
3326 	struct sockaddr_in6	*sin6, *laddr6;
3327 
3328 	if (debug)
3329 		(void) printf("in6_status(%s) flags 0x%llx\n", name, flags);
3330 
3331 	if (!(flags & IFF_IPV6))
3332 		return;
3333 
3334 	if (!(flags & IFF_NOLOCAL)) {
3335 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3336 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3337 			if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3338 			    errno == ENXIO) {
3339 				if (!force)
3340 					return;
3341 				(void) memset(&lifr.lifr_addr, 0,
3342 				    sizeof (lifr.lifr_addr));
3343 			} else
3344 				Perror0_exit("in_status6: SIOCGLIFADDR");
3345 		}
3346 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3347 		(void) printf("\tinet6 %s/%d ",
3348 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3349 		    abuf, sizeof (abuf)),
3350 		    lifr.lifr_addrlen);
3351 		laddr6 = sin6;
3352 	} else {
3353 		(void) printf("\tinet6 ");
3354 	}
3355 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3356 	if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3357 		if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3358 		    errno == ENXIO) {
3359 			if (!force)
3360 				return;
3361 			(void) memset(&lifr.lifr_addr, 0,
3362 			    sizeof (lifr.lifr_addr));
3363 		} else
3364 			Perror0_exit("in_status6: SIOCGLIFSUBNET");
3365 	}
3366 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3367 	if ((flags & IFF_NOLOCAL) ||
3368 	    !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
3369 		(void) printf("subnet %s/%d ",
3370 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3371 		    abuf, sizeof (abuf)),
3372 		    lifr.lifr_addrlen);
3373 	}
3374 	if (sin6->sin6_family != AF_INET6) {
3375 		(void) printf("Wrong family: %d\n", sin6->sin6_family);
3376 	}
3377 	if (flags & IFF_POINTOPOINT) {
3378 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3379 		if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3380 			if (errno == EADDRNOTAVAIL)
3381 				(void) memset(&lifr.lifr_addr, 0,
3382 				    sizeof (lifr.lifr_addr));
3383 			else
3384 				Perror0_exit("in_status6: SIOCGLIFDSTADDR");
3385 		}
3386 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
3387 		(void) printf("--> %s ",
3388 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3389 		    abuf, sizeof (abuf)));
3390 	}
3391 	if (verbose) {
3392 		(void) putchar('\n');
3393 		(void) putchar('\t');
3394 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3395 		if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
3396 			if (errno == EADDRNOTAVAIL || errno == EINVAL)
3397 				(void) memset(&lifr.lifr_addr, 0,
3398 				    sizeof (lifr.lifr_addr));
3399 			else
3400 				Perror0_exit("in_status6: SIOCGLIFTOKEN");
3401 		} else {
3402 			sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3403 			(void) printf("token %s/%d ",
3404 			    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3405 			    abuf, sizeof (abuf)),
3406 			    lifr.lifr_addrlen);
3407 		}
3408 		if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
3409 			if (errno != EINVAL) {
3410 				Perror0_exit("in_status6: SIOCGLIFLNKINFO");
3411 			}
3412 		} else {
3413 			(void) printf("maxhops %u, reachtime %u ms, "
3414 			    "reachretrans %u ms, maxmtu %u ",
3415 			    lifr.lifr_ifinfo.lir_maxhops,
3416 			    lifr.lifr_ifinfo.lir_reachtime,
3417 			    lifr.lifr_ifinfo.lir_reachretrans,
3418 			    lifr.lifr_ifinfo.lir_maxmtu);
3419 		}
3420 	}
3421 	/* If there is a groupname, print it for only the physical interface */
3422 	if (strchr(name, ':') == NULL) {
3423 		if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3424 		    lifr.lifr_groupname[0] != '\0') {
3425 			(void) printf("\n\tgroupname %s", lifr.lifr_groupname);
3426 		}
3427 	}
3428 	(void) putchar('\n');
3429 }
3430 
3431 static void
in_configinfo(int force,uint64_t flags)3432 in_configinfo(int force, uint64_t flags)
3433 {
3434 	struct sockaddr_in *sin, *laddr;
3435 	struct	sockaddr_in netmask = { AF_INET };
3436 
3437 	if (debug)
3438 		(void) printf("in_configinfo(%s) flags 0x%llx\n", name, flags);
3439 
3440 	/* only configinfo info for IPv4 interfaces */
3441 	if (!(flags & IFF_IPV4))
3442 		return;
3443 
3444 	if (!(flags & IFF_NOLOCAL)) {
3445 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3446 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3447 			if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3448 			    errno == ENXIO) {
3449 				if (!force)
3450 					return;
3451 				(void) memset(&lifr.lifr_addr, 0,
3452 				    sizeof (lifr.lifr_addr));
3453 			} else
3454 				Perror0_exit("in_configinfo: SIOCGLIFADDR");
3455 		}
3456 		sin = (struct sockaddr_in *)&lifr.lifr_addr;
3457 		(void) printf(" set %s ", inet_ntoa(sin->sin_addr));
3458 		laddr = sin;
3459 	}
3460 
3461 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3462 	if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
3463 		if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3464 		    errno == ENXIO) {
3465 			if (!force)
3466 				return;
3467 			(void) memset(&lifr.lifr_addr, 0,
3468 			    sizeof (lifr.lifr_addr));
3469 		} else {
3470 			Perror0_exit("in_configinfo: SIOCGLIFSUBNET");
3471 		}
3472 	}
3473 	sin = (struct sockaddr_in *)&lifr.lifr_addr;
3474 
3475 	if ((flags & IFF_NOLOCAL) ||
3476 	    sin->sin_addr.s_addr != laddr->sin_addr.s_addr) {
3477 		(void) printf(" subnet %s/%d ", inet_ntoa(sin->sin_addr),
3478 		    lifr.lifr_addrlen);
3479 	}
3480 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3481 	if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
3482 		if (errno != EADDRNOTAVAIL)
3483 			Perror0_exit("in_configinfo: SIOCGLIFNETMASK");
3484 		(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
3485 	} else
3486 		netmask.sin_addr =
3487 		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr;
3488 	if (flags & IFF_POINTOPOINT) {
3489 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3490 		if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3491 			if (errno == EADDRNOTAVAIL)
3492 				(void) memset(&lifr.lifr_addr, 0,
3493 				    sizeof (lifr.lifr_addr));
3494 			else
3495 				Perror0_exit("in_configinfo: SIOCGLIFDSTADDR");
3496 		}
3497 		sin = (struct sockaddr_in *)&lifr.lifr_dstaddr;
3498 		(void) printf(" destination %s ", inet_ntoa(sin->sin_addr));
3499 	}
3500 	(void) printf(" netmask 0x%x ", ntohl(netmask.sin_addr.s_addr));
3501 	if (flags & IFF_BROADCAST) {
3502 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3503 		if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
3504 			if (errno == EADDRNOTAVAIL)
3505 				(void) memset(&lifr.lifr_addr, 0,
3506 				    sizeof (lifr.lifr_addr));
3507 			else
3508 				Perror0_exit("in_configinfo: SIOCGLIFBRDADDR");
3509 		}
3510 		sin = (struct sockaddr_in *)&lifr.lifr_addr;
3511 		if (sin->sin_addr.s_addr != 0) {
3512 			(void) printf(" broadcast %s ",
3513 			    inet_ntoa(sin->sin_addr));
3514 		}
3515 	}
3516 
3517 	/* If there is a groupname, print it for only the zeroth interface */
3518 	if (lifnum(name) == 0) {
3519 		if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3520 		    lifr.lifr_groupname[0] != '\0') {
3521 			(void) printf(" group %s ", lifr.lifr_groupname);
3522 		}
3523 	}
3524 
3525 	/* Print flags to configure */
3526 	print_config_flags(AF_INET, flags);
3527 }
3528 
3529 static void
in6_configinfo(int force,uint64_t flags)3530 in6_configinfo(int force, uint64_t flags)
3531 {
3532 	char abuf[INET6_ADDRSTRLEN];
3533 	struct sockaddr_in6 *sin6, *laddr6;
3534 
3535 	if (debug)
3536 		(void) printf("in6_configinfo(%s) flags 0x%llx\n", name,
3537 		    flags);
3538 
3539 	if (!(flags & IFF_IPV6))
3540 		return;
3541 
3542 	if (!(flags & IFF_NOLOCAL)) {
3543 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3544 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
3545 			if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT ||
3546 			    errno == ENXIO) {
3547 				if (!force)
3548 					return;
3549 				(void) memset(&lifr.lifr_addr, 0,
3550 				    sizeof (lifr.lifr_addr));
3551 			} else
3552 				Perror0_exit("in6_configinfo: SIOCGLIFADDR");
3553 		}
3554 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3555 		(void) printf(" set %s/%d ",
3556 		    inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof (abuf)),
3557 		    lifr.lifr_addrlen);
3558 		laddr6 = sin6;
3559 	}
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("in6_configinfo: SIOCGLIFSUBNET");
3570 	}
3571 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3572 	if ((flags & IFF_NOLOCAL) ||
3573 	    !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) {
3574 		(void) printf(" subnet %s/%d ",
3575 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3576 		    abuf, sizeof (abuf)),
3577 		    lifr.lifr_addrlen);
3578 	}
3579 
3580 	if (flags & IFF_POINTOPOINT) {
3581 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3582 		if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
3583 			if (errno == EADDRNOTAVAIL)
3584 				(void) memset(&lifr.lifr_addr, 0,
3585 				    sizeof (lifr.lifr_addr));
3586 			else
3587 				Perror0_exit("in6_configinfo: SIOCGLIFDSTADDR");
3588 		}
3589 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr;
3590 		(void) printf(" destination %s ",
3591 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3592 		    abuf, sizeof (abuf)));
3593 	}
3594 
3595 	(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
3596 	if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
3597 		if (errno == EADDRNOTAVAIL || errno == EINVAL)
3598 			(void) memset(&lifr.lifr_addr, 0,
3599 			    sizeof (lifr.lifr_addr));
3600 		else
3601 			Perror0_exit("in6_configinfo: SIOCGLIFTOKEN");
3602 	} else {
3603 		sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
3604 		(void) printf(" token %s/%d ",
3605 		    inet_ntop(AF_INET6, (void *)&sin6->sin6_addr,
3606 		    abuf, sizeof (abuf)),
3607 		    lifr.lifr_addrlen);
3608 	}
3609 
3610 	/* If there is a groupname, print it for only the zeroth interface */
3611 	if (lifnum(name) == 0) {
3612 		if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 &&
3613 		    lifr.lifr_groupname[0] != '\0') {
3614 			(void) printf(" group %s ", lifr.lifr_groupname);
3615 		}
3616 	}
3617 
3618 	/* Print flags to configure */
3619 	print_config_flags(AF_INET6, flags);
3620 }
3621 
3622 /*
3623  * If this is a physical interface then remove it.
3624  * If it is a logical interface name use SIOCLIFREMOVEIF to
3625  * remove it. In both cases fail if it doesn't exist.
3626  */
3627 /* ARGSUSED */
3628 static int
inetunplumb(char * arg,int64_t param)3629 inetunplumb(char *arg, int64_t param)
3630 {
3631 	ipadm_status_t	istatus;
3632 
3633 	istatus = ipadm_delete_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE);
3634 	if (istatus != IPADM_SUCCESS) {
3635 		(void) fprintf(stderr, "ifconfig: cannot unplumb %s: %s\n",
3636 		    name, ipadm_status2str(istatus));
3637 		exit(1);
3638 	}
3639 
3640 	return (0);
3641 }
3642 
3643 /*
3644  * Create the interface in `name', using ipadm_create_if(). If `name' is a
3645  * logical interface or loopback interface, ipadm_create_if() uses
3646  * SIOCLIFADDIF to create it.
3647  */
3648 /* ARGSUSED */
3649 static int
inetplumb(char * arg,int64_t param)3650 inetplumb(char *arg, int64_t param)
3651 {
3652 	ipadm_status_t	istatus;
3653 
3654 	istatus = ipadm_create_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE);
3655 	if (istatus != IPADM_SUCCESS) {
3656 		(void) fprintf(stderr, "ifconfig: cannot plumb %s: %s\n",
3657 		    name, ipadm_status2str(istatus));
3658 		if (istatus != IPADM_IF_EXISTS)
3659 			exit(1);
3660 	}
3661 	return (0);
3662 }
3663 
3664 /* ARGSUSED */
3665 static int
inetipmp(char * arg,int64_t param)3666 inetipmp(char *arg, int64_t param)
3667 {
3668 	int retval;
3669 
3670 	/*
3671 	 * Treat e.g. "ifconfig ipmp0:2 ipmp" as "ifconfig ipmp0:2 plumb".
3672 	 * Otherwise, try to create the requested IPMP interface.
3673 	 */
3674 	if (strchr(name, ':') != NULL)
3675 		retval = inetplumb(arg, param);
3676 	else
3677 		retval = create_ipmp(name, afp->af_af, name, _B_FALSE);
3678 
3679 	/*
3680 	 * We'd return -1, but foreachinterface() doesn't propagate the error
3681 	 * into the exit status, so we're forced to explicitly exit().
3682 	 */
3683 	if (retval == -1)
3684 		exit(1);
3685 	return (0);
3686 }
3687 
3688 /*
3689  * Create an IPMP group `grname' with address family `af'.  If `ifname' is
3690  * non-NULL, it specifies the interface name to use.  Otherwise, use the name
3691  * ipmpN, where N corresponds to the lowest available integer.  If `implicit'
3692  * is set, then the group is being created as a side-effect of placing an
3693  * underlying interface in a group.  Also start in.mpathd if necessary.
3694  */
3695 static int
create_ipmp(const char * grname,int af,const char * ifname,boolean_t implicit)3696 create_ipmp(const char *grname, int af, const char *ifname, boolean_t implicit)
3697 {
3698 	static int ipmp_daemon_started;
3699 	uint32_t flags = IPADM_OPT_IPMP|IPADM_OPT_ACTIVE;
3700 	ipadm_status_t istatus;
3701 
3702 	if (debug) {
3703 		(void) printf("create_ipmp: ifname %s grname %s af %d\n",
3704 		    ifname != NULL ? ifname : "NULL", grname, af);
3705 	}
3706 
3707 	/*
3708 	 * ipadm_create_if() creates the IPMP interface and fills in the
3709 	 * ppa in lifr.lifr_name, if `ifname'="ipmp".
3710 	 */
3711 	(void) strlcpy(lifr.lifr_name, (ifname ? ifname : "ipmp"),
3712 	    sizeof (lifr.lifr_name));
3713 	if (ifname == NULL)
3714 		flags |= IPADM_OPT_GENPPA;
3715 	istatus = ipadm_create_if(iph, lifr.lifr_name, af, flags);
3716 	if (istatus != IPADM_SUCCESS) {
3717 		(void) fprintf(stderr, "ifconfig: cannot create IPMP interface "
3718 		    "%s: %s\n", grname, ipadm_status2str(istatus));
3719 		return (-1);
3720 	}
3721 
3722 	/*
3723 	 * To preserve backward-compatibility, always bring up the link-local
3724 	 * address for implicitly-created IPv6 IPMP interfaces.
3725 	 */
3726 	if (implicit && af == AF_INET6) {
3727 		if (ioctl(s6, SIOCGLIFFLAGS, &lifr) == 0) {
3728 			lifr.lifr_flags |= IFF_UP;
3729 			(void) ioctl(s6, SIOCSLIFFLAGS, &lifr);
3730 		}
3731 	}
3732 
3733 	/*
3734 	 * If the caller requested a different group name, issue a
3735 	 * SIOCSLIFGROUPNAME on the new IPMP interface.
3736 	 */
3737 	if (strcmp(lifr.lifr_name, grname) != 0) {
3738 		(void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
3739 		if (ioctl(s, SIOCSLIFGROUPNAME, &lifr) == -1) {
3740 			Perror0("SIOCSLIFGROUPNAME");
3741 			return (-1);
3742 		}
3743 	}
3744 
3745 	/*
3746 	 * If we haven't done so yet, ensure in.mpathd is started.
3747 	 */
3748 	if (ipmp_daemon_started++ == 0)
3749 		start_ipmp_daemon();
3750 
3751 	return (0);
3752 }
3753 
3754 /*
3755  * Start in.mpathd if it's not already running.
3756  */
3757 static void
start_ipmp_daemon(void)3758 start_ipmp_daemon(void)
3759 {
3760 	int retval;
3761 	ipmp_handle_t ipmp_handle;
3762 
3763 	/*
3764 	 * Ping in.mpathd to see if it's running already.
3765 	 */
3766 	if ((retval = ipmp_open(&ipmp_handle)) != IPMP_SUCCESS) {
3767 		(void) fprintf(stderr, "ifconfig: cannot create IPMP handle: "
3768 		    "%s\n", ipmp_errmsg(retval));
3769 		return;
3770 	}
3771 
3772 	retval = ipmp_ping_daemon(ipmp_handle);
3773 	ipmp_close(ipmp_handle);
3774 
3775 	switch (retval) {
3776 	case IPMP_ENOMPATHD:
3777 		break;
3778 	case IPMP_SUCCESS:
3779 		return;
3780 	default:
3781 		(void) fprintf(stderr, "ifconfig: cannot ping in.mpathd: %s\n",
3782 		    ipmp_errmsg(retval));
3783 		break;
3784 	}
3785 
3786 	/*
3787 	 * Start in.mpathd.  Note that in.mpathd will handle multiple
3788 	 * incarnations (ipmp_ping_daemon() is just an optimization) so we
3789 	 * don't need to worry about racing with another ifconfig process.
3790 	 */
3791 	switch (fork()) {
3792 	case -1:
3793 		Perror0_exit("start_ipmp_daemon: fork");
3794 		/* NOTREACHED */
3795 	case 0:
3796 		(void) execl(MPATHD_PATH, MPATHD_PATH, NULL);
3797 		_exit(1);
3798 		/* NOTREACHED */
3799 	default:
3800 		break;
3801 	}
3802 }
3803 
3804 /*
3805  * Bring the address named by `ifaddrp' up or down.  Doesn't trust any mutable
3806  * values in ia_flags since they may be stale.
3807  */
3808 static boolean_t
ifaddr_op(ifaddrlistx_t * ifaddrp,boolean_t up)3809 ifaddr_op(ifaddrlistx_t *ifaddrp, boolean_t up)
3810 {
3811 	struct lifreq	lifrl;	/* Local lifreq struct */
3812 	int		fd = (ifaddrp->ia_flags & IFF_IPV4) ? s4 : s6;
3813 
3814 	(void) memset(&lifrl, 0, sizeof (lifrl));
3815 	(void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name, LIFNAMSIZ);
3816 	if (ioctl(fd, SIOCGLIFFLAGS, &lifrl) == -1)
3817 		return (_B_FALSE);
3818 
3819 	if (up)
3820 		lifrl.lifr_flags |= IFF_UP;
3821 	else
3822 		lifrl.lifr_flags &= ~IFF_UP;
3823 
3824 	if (ioctl(fd, SIOCSLIFFLAGS, &lifrl) == -1)
3825 		return (_B_FALSE);
3826 
3827 	/*
3828 	 * If we're trying to bring the address down, ensure that DAD activity
3829 	 * (observable by IFF_DUPLICATE) has also been stopped.
3830 	 */
3831 	if (!up && ioctl(fd, SIOCGLIFFLAGS, &lifrl) != -1 &&
3832 	    lifrl.lifr_flags & IFF_DUPLICATE) {
3833 		if (ioctl(fd, SIOCGLIFADDR, &lifrl) == -1 ||
3834 		    ioctl(fd, SIOCSLIFADDR, &lifrl) == -1) {
3835 			return (_B_FALSE);
3836 		}
3837 	}
3838 	return (_B_TRUE);
3839 }
3840 
3841 static boolean_t
ifaddr_up(ifaddrlistx_t * ifaddrp)3842 ifaddr_up(ifaddrlistx_t *ifaddrp)
3843 {
3844 	return (ifaddr_op(ifaddrp, _B_TRUE));
3845 }
3846 
3847 static boolean_t
ifaddr_down(ifaddrlistx_t * ifaddrp)3848 ifaddr_down(ifaddrlistx_t *ifaddrp)
3849 {
3850 	return (ifaddr_op(ifaddrp, _B_FALSE));
3851 }
3852 
3853 /*
3854  * Open the global libdladm handle "dlh" if it isn't already opened.  The
3855  * caller may optionally supply a link name to obtain its linkid.  If a link
3856  * of a specific class or classes is required, reqclass specifies the class
3857  * mask.
3858  */
3859 static dladm_status_t
ifconfig_dladm_open(const char * name,datalink_class_t reqclass,datalink_id_t * linkid)3860 ifconfig_dladm_open(const char *name, datalink_class_t reqclass,
3861     datalink_id_t *linkid)
3862 {
3863 	dladm_status_t status = DLADM_STATUS_OK;
3864 	datalink_class_t class;
3865 
3866 	if (!dlh_opened) {
3867 		if ((status = dladm_open(&dlh)) != DLADM_STATUS_OK)
3868 			return (status);
3869 		dlh_opened = _B_TRUE;
3870 	}
3871 	if (name != NULL) {
3872 		status = dladm_name2info(dlh, name, linkid, NULL, &class, NULL);
3873 		if (status == DLADM_STATUS_OK) {
3874 			if (!(class & reqclass))
3875 				status = DLADM_STATUS_LINKINVAL;
3876 		}
3877 	}
3878 	return (status);
3879 }
3880 
3881 /*
3882  * This function checks if we can use libipadm API's. We will only
3883  * call libipadm functions for non-IPMP interfaces. This check is
3884  * temporary until libipadm supports IPMP interfaces.
3885  */
3886 static boolean_t
ifconfig_use_libipadm(int s,const char * lifname)3887 ifconfig_use_libipadm(int s, const char *lifname)
3888 {
3889 	struct lifreq lifr1;
3890 
3891 	(void) strlcpy(lifr1.lifr_name, lifname, sizeof (lifr1.lifr_name));
3892 	if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr1) < 0) {
3893 		(void) strncpy(lifr.lifr_name, lifname,
3894 		    sizeof (lifr.lifr_name));
3895 		Perror0_exit("error");
3896 	}
3897 
3898 	return (lifr1.lifr_groupname[0] == '\0');
3899 }
3900 
3901 static void
ipadmerr_exit(ipadm_status_t status,const char * str)3902 ipadmerr_exit(ipadm_status_t status, const char *str)
3903 {
3904 	(void) fprintf(stderr, "ifconfig: %s: %s\n", str,
3905 	    ipadm_status2str(status));
3906 	exit(1);
3907 }
3908 
3909 static void
dladmerr_exit(dladm_status_t status,const char * str)3910 dladmerr_exit(dladm_status_t status, const char *str)
3911 {
3912 	char errstr[DLADM_STRSIZE];
3913 
3914 	(void) fprintf(stderr, "%s: %s\n", str,
3915 	    dladm_status2str(status, errstr));
3916 	exit(1);
3917 }
3918 
3919 void
Perror0(const char * cmd)3920 Perror0(const char *cmd)
3921 {
3922 	Perror2(cmd, lifr.lifr_name);
3923 }
3924 
3925 void
Perror0_exit(const char * cmd)3926 Perror0_exit(const char *cmd)
3927 {
3928 	Perror0(cmd);
3929 	exit(1);
3930 }
3931 
3932 void
Perror2(const char * cmd,const char * str)3933 Perror2(const char *cmd, const char *str)
3934 {
3935 	int error = errno;
3936 
3937 	(void) fprintf(stderr, "ifconfig: %s: ", cmd);
3938 
3939 	switch (error) {
3940 	case ENXIO:
3941 		(void) fprintf(stderr, "%s: no such interface\n", str);
3942 		break;
3943 	case EPERM:
3944 		(void) fprintf(stderr, "%s: permission denied\n", str);
3945 		break;
3946 	case EEXIST:
3947 		(void) fprintf(stderr, "%s: already exists\n", str);
3948 		break;
3949 	case ENAMETOOLONG:
3950 		(void) fprintf(stderr, "%s: interface name too long\n", str);
3951 		break;
3952 	case ERANGE:
3953 		(void) fprintf(stderr, "%s: logical interface id is outside "
3954 		    "allowed range\n", str);
3955 		break;
3956 	default:
3957 		errno = error;
3958 		perror(str);
3959 	}
3960 }
3961 
3962 /*
3963  * Print out error message (Perror2()) and exit
3964  */
3965 void
Perror2_exit(const char * cmd,const char * str)3966 Perror2_exit(const char *cmd, const char *str)
3967 {
3968 	Perror2(cmd, str);
3969 	exit(1);
3970 	/* NOTREACHED */
3971 }
3972 
3973 void
Perrdlpi(const char * cmd,const char * linkname,int err)3974 Perrdlpi(const char *cmd, const char *linkname, int err)
3975 {
3976 	(void) fprintf(stderr, "ifconfig: %s \"%s\": %s\n", cmd,
3977 	    linkname, dlpi_strerror(err));
3978 }
3979 
3980 /*
3981  * Print out error message (Perrdlpi()) and exit
3982  */
3983 void
Perrdlpi_exit(const char * cmd,const char * linkname,int err)3984 Perrdlpi_exit(const char *cmd, const char *linkname, int err)
3985 {
3986 	Perrdlpi(cmd, linkname, err);
3987 	exit(1);
3988 }
3989 
3990 /*
3991  * If the last argument is non-NULL allow a <addr>/<n> syntax and
3992  * pass out <n> in *plenp.
3993  * If <n> doesn't parse return BAD_ADDR as *plenp.
3994  * If no /<n> is present return NO_PREFIX as *plenp.
3995  */
3996 static void
in_getaddr(char * s,struct sockaddr * saddr,int * plenp)3997 in_getaddr(char *s, struct sockaddr *saddr, int *plenp)
3998 {
3999 	/* LINTED: alignment */
4000 	struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
4001 	struct hostent *hp;
4002 	struct netent *np;
4003 	char str[BUFSIZ];
4004 	int error_num;
4005 
4006 	(void) strncpy(str, s, sizeof (str));
4007 
4008 	/*
4009 	 * Look for '/'<n> is plenp
4010 	 */
4011 	if (plenp != NULL) {
4012 		char *cp;
4013 
4014 		*plenp = in_getprefixlen(str, _B_TRUE, IP_ABITS);
4015 		if (*plenp == BAD_ADDR)
4016 			return;
4017 		cp = strchr(str, '/');
4018 		if (cp != NULL)
4019 			*cp = '\0';
4020 	} else if (strchr(str, '/') != NULL) {
4021 		(void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
4022 		exit(1);
4023 	}
4024 
4025 	(void) memset(sin, 0, sizeof (*sin));
4026 
4027 	/*
4028 	 *	Try to catch attempts to set the broadcast address to all 1's.
4029 	 */
4030 	if (strcmp(str, "255.255.255.255") == 0 ||
4031 	    (strtoul(str, (char **)NULL, 0) == 0xffffffffUL)) {
4032 		sin->sin_family = AF_INET;
4033 		sin->sin_addr.s_addr = 0xffffffff;
4034 		return;
4035 	}
4036 
4037 	hp = getipnodebyname(str, AF_INET, 0, &error_num);
4038 	if (hp) {
4039 		sin->sin_family = hp->h_addrtype;
4040 		(void) memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
4041 		freehostent(hp);
4042 		return;
4043 	}
4044 	np = getnetbyname(str);
4045 	if (np) {
4046 		sin->sin_family = np->n_addrtype;
4047 		sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
4048 		return;
4049 	}
4050 	if (error_num == TRY_AGAIN) {
4051 		(void) fprintf(stderr, "ifconfig: %s: bad address "
4052 		    "(try again later)\n", s);
4053 	} else {
4054 		(void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
4055 	}
4056 	exit(1);
4057 }
4058 
4059 /*
4060  * If the last argument is non-NULL allow a <addr>/<n> syntax and
4061  * pass out <n> in *plenp.
4062  * If <n> doesn't parse return BAD_ADDR as *plenp.
4063  * If no /<n> is present return NO_PREFIX as *plenp.
4064  */
4065 static void
in6_getaddr(char * s,struct sockaddr * saddr,int * plenp)4066 in6_getaddr(char *s, struct sockaddr *saddr, int *plenp)
4067 {
4068 	/* LINTED: alignment */
4069 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
4070 	struct hostent *hp;
4071 	char str[BUFSIZ];
4072 	int error_num;
4073 
4074 	(void) strncpy(str, s, sizeof (str));
4075 
4076 	/*
4077 	 * Look for '/'<n> is plenp
4078 	 */
4079 	if (plenp != NULL) {
4080 		char *cp;
4081 
4082 		*plenp = in_getprefixlen(str, _B_TRUE, IPV6_ABITS);
4083 		if (*plenp == BAD_ADDR)
4084 			return;
4085 		cp = strchr(str, '/');
4086 		if (cp != NULL)
4087 			*cp = '\0';
4088 	} else if (strchr(str, '/') != NULL) {
4089 		(void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str);
4090 		exit(1);
4091 	}
4092 
4093 	(void) memset(sin6, 0, sizeof (*sin6));
4094 
4095 	hp = getipnodebyname(str, AF_INET6, 0, &error_num);
4096 	if (hp) {
4097 		sin6->sin6_family = hp->h_addrtype;
4098 		(void) memcpy(&sin6->sin6_addr, hp->h_addr, hp->h_length);
4099 		freehostent(hp);
4100 		return;
4101 	}
4102 	if (error_num == TRY_AGAIN) {
4103 		(void) fprintf(stderr, "ifconfig: %s: bad address "
4104 		    "(try again later)\n", s);
4105 	} else {
4106 		(void) fprintf(stderr, "ifconfig: %s: bad address\n", s);
4107 	}
4108 	exit(1);
4109 }
4110 
4111 /*
4112  * If "slash" is zero this parses the whole string as
4113  * an integer. With "slash" non zero it parses the tail part as an integer.
4114  *
4115  * If it is not a valid integer this returns BAD_ADDR.
4116  * If there is /<n> present this returns NO_PREFIX.
4117  */
4118 static int
in_getprefixlen(char * addr,boolean_t slash,int max_plen)4119 in_getprefixlen(char *addr, boolean_t slash, int max_plen)
4120 {
4121 	int prefixlen;
4122 	char *str, *end;
4123 
4124 	if (slash) {
4125 		str = strchr(addr, '/');
4126 		if (str == NULL)
4127 			return (NO_PREFIX);
4128 		str++;
4129 	} else
4130 		str = addr;
4131 
4132 	prefixlen = strtol(str, &end, 10);
4133 	if (prefixlen < 0)
4134 		return (BAD_ADDR);
4135 	if (str == end)
4136 		return (BAD_ADDR);
4137 	if (max_plen != 0 && max_plen < prefixlen)
4138 		return (BAD_ADDR);
4139 	return (prefixlen);
4140 }
4141 
4142 /*
4143  * Convert a prefix length to a mask.
4144  * Returns 1 if ok. 0 otherwise.
4145  * Assumes the mask array is zero'ed by the caller.
4146  */
4147 static boolean_t
in_prefixlentomask(int prefixlen,int maxlen,uchar_t * mask)4148 in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask)
4149 {
4150 	if (prefixlen < 0 || prefixlen > maxlen)
4151 		return (0);
4152 
4153 	while (prefixlen > 0) {
4154 		if (prefixlen >= 8) {
4155 			*mask++ = 0xFF;
4156 			prefixlen -= 8;
4157 			continue;
4158 		}
4159 		*mask |= 1 << (8 - prefixlen);
4160 		prefixlen--;
4161 	}
4162 	return (1);
4163 }
4164 
4165 static void
print_flags(uint64_t flags)4166 print_flags(uint64_t flags)
4167 {
4168 	boolean_t first = _B_TRUE;
4169 	int cnt, i;
4170 
4171 	(void) printf("flags=%llx", flags);
4172 	cnt = sizeof (if_flags_tbl) / sizeof (if_flags_t);
4173 	for (i = 0; i < cnt; i++) {
4174 		if (flags & if_flags_tbl[i].iff_value) {
4175 			if (first) {
4176 				(void) printf("<");
4177 				first = _B_FALSE;
4178 			} else {
4179 				/*
4180 				 * It has to be here and not with the
4181 				 * printf below because for the last one,
4182 				 * we don't want a comma before the ">".
4183 				 */
4184 				(void) printf(",");
4185 			}
4186 			(void) printf("%s", if_flags_tbl[i].iff_name);
4187 		}
4188 	}
4189 	if (!first)
4190 		(void) printf(">");
4191 }
4192 
4193 static void
print_config_flags(int af,uint64_t flags)4194 print_config_flags(int af, uint64_t flags)
4195 {
4196 	if_config_cmd_t *cmdp;
4197 
4198 	for (cmdp = if_config_cmd_tbl; cmdp->iff_flag != 0; cmdp++) {
4199 		if ((flags & cmdp->iff_flag) &&
4200 		    (cmdp->iff_af == AF_UNSPEC || cmdp->iff_af == af)) {
4201 			(void) printf("%s ", cmdp->iff_name);
4202 		}
4203 	}
4204 }
4205 
4206 /*
4207  * Use the configured directory lookup mechanism (e.g. files/NIS/...)
4208  * to find the network mask.  Returns true if we found one to set.
4209  *
4210  * The parameter addr_set controls whether we should get the address of
4211  * the working interface for the netmask query.  If addr_set is true,
4212  * we will use the address provided.  Otherwise, we will find the working
4213  * interface's address and use it instead.
4214  */
4215 static boolean_t
in_getmask(struct sockaddr_in * saddr,boolean_t addr_set)4216 in_getmask(struct sockaddr_in *saddr, boolean_t addr_set)
4217 {
4218 	struct sockaddr_in ifaddr;
4219 
4220 	/*
4221 	 * Read the address from the interface if it is not passed in.
4222 	 */
4223 	if (!addr_set) {
4224 		(void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
4225 		if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
4226 			if (errno != EADDRNOTAVAIL) {
4227 				(void) fprintf(stderr, "Need net number for "
4228 				    "mask\n");
4229 			}
4230 			return (_B_FALSE);
4231 		}
4232 		ifaddr = *((struct sockaddr_in *)&lifr.lifr_addr);
4233 	} else {
4234 		ifaddr.sin_addr = saddr->sin_addr;
4235 	}
4236 	if (getnetmaskbyaddr(ifaddr.sin_addr, &saddr->sin_addr) == 0) {
4237 		saddr->sin_family = AF_INET;
4238 		return (_B_TRUE);
4239 	}
4240 	return (_B_FALSE);
4241 }
4242 
4243 static int
lifnum(const char * ifname)4244 lifnum(const char *ifname)
4245 {
4246 	const char *cp;
4247 
4248 	if ((cp = strchr(ifname, ':')) == NULL)
4249 		return (0);
4250 	else
4251 		return (atoi(cp + 1));
4252 }
4253 
4254 static void
add_ni(const char * name)4255 add_ni(const char *name)
4256 {
4257 	ni_t **pp;
4258 	ni_t *p;
4259 
4260 	for (pp = &ni_list; (p = *pp) != NULL; pp = &(p->ni_next)) {
4261 		if (strcmp(p->ni_name, name) == 0) {
4262 			if (debug > 2)
4263 				(void) fprintf(stderr, "'%s' is a duplicate\n",
4264 				    name);
4265 			return;
4266 		}
4267 	}
4268 
4269 	if (debug > 2)
4270 		(void) fprintf(stderr, "adding '%s'\n",
4271 		    name);
4272 
4273 	if ((p = malloc(sizeof (ni_t))) == NULL)
4274 		return;
4275 
4276 	(void) strlcpy(p->ni_name, name, sizeof (p->ni_name));
4277 	p->ni_next = NULL;
4278 
4279 	*pp = p;
4280 	num_ni++;
4281 }
4282 
4283 static boolean_t
ni_entry(const char * linkname,void * arg)4284 ni_entry(const char *linkname, void *arg)
4285 {
4286 	dlpi_handle_t	dh;
4287 	datalink_class_t class;
4288 
4289 	(void) dladm_name2info(arg, linkname, NULL, NULL, &class, NULL);
4290 
4291 	if (class == DATALINK_CLASS_ETHERSTUB)
4292 		return (_B_FALSE);
4293 	if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS)
4294 		return (_B_FALSE);
4295 
4296 	add_ni(linkname);
4297 
4298 	dlpi_close(dh);
4299 	return (_B_FALSE);
4300 }
4301 
4302 /*
4303  * dhcp-related routines
4304  */
4305 
4306 static int
setifdhcp(const char * caller,const char * ifname,int argc,char * argv[])4307 setifdhcp(const char *caller, const char *ifname, int argc, char *argv[])
4308 {
4309 	dhcp_ipc_request_t	*request;
4310 	dhcp_ipc_reply_t	*reply	= NULL;
4311 	int			timeout = DHCP_IPC_WAIT_DEFAULT;
4312 	dhcp_ipc_type_t		type	= DHCP_START;
4313 	int			error;
4314 	boolean_t		is_primary = _B_FALSE;
4315 	boolean_t		started = _B_FALSE;
4316 
4317 	for (argv++; --argc > 0; argv++) {
4318 
4319 		if (strcmp(*argv, "primary") == 0) {
4320 			is_primary = _B_TRUE;
4321 			continue;
4322 		}
4323 
4324 		if (strcmp(*argv, "wait") == 0) {
4325 			if (--argc <= 0) {
4326 				usage();
4327 				return (DHCP_EXIT_BADARGS);
4328 			}
4329 			argv++;
4330 
4331 			if (strcmp(*argv, "forever") == 0) {
4332 				timeout = DHCP_IPC_WAIT_FOREVER;
4333 				continue;
4334 			}
4335 
4336 			if (sscanf(*argv, "%d", &timeout) != 1) {
4337 				usage();
4338 				return (DHCP_EXIT_BADARGS);
4339 			}
4340 
4341 			if (timeout < 0) {
4342 				usage();
4343 				return (DHCP_EXIT_BADARGS);
4344 			}
4345 			continue;
4346 		}
4347 
4348 		type = dhcp_string_to_request(*argv);
4349 		if (type == -1) {
4350 			usage();
4351 			return (DHCP_EXIT_BADARGS);
4352 		}
4353 	}
4354 
4355 	/*
4356 	 * Only try to start agent on start or inform; in all other cases it
4357 	 * has to already be running for anything to make sense.
4358 	 */
4359 	if (type == DHCP_START || type == DHCP_INFORM) {
4360 		if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) {
4361 			(void) fprintf(stderr, "%s: unable to start %s\n",
4362 			    caller, DHCP_AGENT_PATH);
4363 			return (DHCP_EXIT_FAILURE);
4364 		}
4365 		started = _B_TRUE;
4366 	}
4367 
4368 	if (is_primary)
4369 		type |= DHCP_PRIMARY;
4370 
4371 	if (af != AF_INET)
4372 		type |= DHCP_V6;
4373 
4374 	request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
4375 	if (request == NULL) {
4376 		(void) fprintf(stderr, "%s: out of memory\n", caller);
4377 		return (DHCP_EXIT_SYSTEM);
4378 	}
4379 
4380 	error = dhcp_ipc_make_request(request, &reply, timeout);
4381 	if (error != 0) {
4382 		free(request);
4383 		/*
4384 		 * Re-map connect error to not under control if we didn't try a
4385 		 * start operation, as this has to be true and results in a
4386 		 * clearer message, not to mention preserving compatibility
4387 		 * with the days when we always started dhcpagent for every
4388 		 * request.
4389 		 */
4390 		if (error == DHCP_IPC_E_CONNECT && !started)
4391 			error = DHCP_IPC_E_UNKIF;
4392 		(void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
4393 		    dhcp_ipc_strerror(error));
4394 		return (DHCP_EXIT_FAILURE);
4395 	}
4396 
4397 	error = reply->return_code;
4398 	if (error != 0) {
4399 		free(request);
4400 		free(reply);
4401 
4402 		if (error == DHCP_IPC_E_TIMEOUT && timeout == 0)
4403 			return (DHCP_EXIT_SUCCESS);
4404 
4405 		(void) fprintf(stderr, "%s: %s: %s\n", caller, ifname,
4406 		    dhcp_ipc_strerror(error));
4407 
4408 		if (error == DHCP_IPC_E_TIMEOUT)
4409 			return (DHCP_EXIT_TIMEOUT);
4410 		else
4411 			return (DHCP_EXIT_IF_FAILURE);
4412 	}
4413 
4414 	if (DHCP_IPC_CMD(type) == DHCP_STATUS) {
4415 		(void) printf("%s", dhcp_status_hdr_string());
4416 		(void) printf("%s", dhcp_status_reply_to_string(reply));
4417 	}
4418 
4419 	free(request);
4420 	free(reply);
4421 	return (DHCP_EXIT_SUCCESS);
4422 }
4423 
4424 static void
usage(void)4425 usage(void)
4426 {
4427 	(void) fprintf(stderr,
4428 	    "usage: ifconfig <interface> | -a[ 4 | 6 | D ][ u | d ][ Z ]\n");
4429 
4430 	(void) fprintf(stderr, "%s",
4431 	    "\t[ <addr_family> ]\n"
4432 	    "\t[ <address>[/<prefix_length>] [ <dest_address> ] ]\n"
4433 	    "\t[ set [ <address>][/<prefix_length>] ]"
4434 	    " [ <address>/<prefix_length>] ]\n"
4435 	    "\t[ destination <dest_address> ]\n"
4436 	    "\t[ addif <address>[/<prefix_length>]"
4437 	    "  [ <dest_address> ] ]\n"
4438 	    "\t[ removeif <address>[/<prefix_length>] ]\n"
4439 	    "\t[ arp | -arp ]\n"
4440 	    "\t[ auto-revarp ]\n"
4441 	    "\t[ broadcast <broad_addr> ]\n"
4442 	    "\t[ index <if_index> ]\n"
4443 	    "\t[ metric <n> ] [ mtu <n> ]\n"
4444 	    "\t[ netmask <mask> ]\n"
4445 	    "\t[ plumb ] [ unplumb ]\n"
4446 	    "\t[ preferred | -preferred ]\n"
4447 	    "\t[ private | -private ]\n"
4448 	    "\t[ local | -local ]\n"
4449 	    "\t[ router | -router ]\n"
4450 	    "\t[ subnet <subnet_address>]\n"
4451 	    "\t[ trailers | -trailers ]\n"
4452 	    "\t[ token <address>/<prefix_length> ]\n"
4453 	    "\t[ tsrc <tunnel_src_address> ]\n"
4454 	    "\t[ tdst <tunnel_dest_address> ]\n"
4455 	    "\t[ auth_algs <tunnel_AH_authentication_algorithm> ]\n"
4456 	    "\t[ encr_algs <tunnel_ESP_encryption_algorithm> ]\n"
4457 	    "\t[ encr_auth_algs <tunnel_ESP_authentication_algorithm> ]\n"
4458 	    "\t[ up ] [ down ]\n"
4459 	    "\t[ xmit | -xmit ]\n"
4460 	    "\t[ modlist ]\n"
4461 	    "\t[ modinsert <module_name@position> ]\n"
4462 	    "\t[ modremove <module_name@position> ]\n"
4463 	    "\t[ ipmp ]\n"
4464 	    "\t[ group <groupname>] | [ group \"\"]\n"
4465 	    "\t[ deprecated | -deprecated ]\n"
4466 	    "\t[ standby | -standby ]\n"
4467 	    "\t[ failover | -failover ]\n"
4468 	    "\t[ zone <zonename> | -zone ]\n"
4469 	    "\t[ usesrc <interface> ]\n"
4470 	    "\t[ all-zones ]\n");
4471 
4472 	(void) fprintf(stderr, "or\n");
4473 	(void) fprintf(stderr,
4474 	    "\tifconfig <interface> |  -a[ 4 | 6 | D ] [ u | d ]\n");
4475 
4476 	(void) fprintf(stderr, "%s", "\tauto-dhcp | dhcp\n"
4477 	    "\t[ wait <time> | forever ]\n\t[ primary ]\n"
4478 	    "\tstart | drop | ping | release | status | inform\n");
4479 }
4480