# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # PROG = ifconfig ROOTFS_PROG = $(PROG) LOCALOBJS= ifconfig.o revarp.o COMMONOBJS= compat.o OBJS= $(LOCALOBJS) $(COMMONOBJS) include ../../../Makefile.cmd include ../../Makefile.cmd-inet LOCALSRCS= $(LOCALOBJS:%.o=%.c) COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c) SRCS= $(LOCALSRCS) $(COMMONSRCS) CPPFLAGS += -I$(CMDINETCOMMONDIR) -I$(SRC)/common/net/dhcp CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses # Hammerhead: Suppress DLPI type mismatch warnings (revarp.c) CERRWARN += -Wno-incompatible-pointer-types LDLIBS += -ldhcpagent -ldlpi -linetutil -lipmp -ldladm -lipadm LINTFLAGS += -m ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) # ifconfig uses the ancillary data feature which is available only through # UNIX 98 standards version of Socket interface. This interface is supposed to # be accessed by -lxnet. In addition -lsocket and -lnsl are used to # capture new not-yet-standard interfaces. Someday -lxnet alone should be # enough when IPv6 inspired new interfaces are part of standards. LDLIBS += -lxnet -lsocket -lnsl # these #defines are required to use UNIX 98 interfaces _D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ $(OBJS) : CPPFLAGS += $(_D_UNIX98_EXTN) LINTFLAGS += $(_D_UNIX98_EXTN) .KEEP_STATE: all: $(ROOTFS_PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: all $(ROOTSBINPROG) $(ROOTUSRSBINLINKS) $(ROOTUSRSBINLINKS): -$(RM) $@; $(SYMLINK) ../../sbin/$(@F) $@ clean: $(RM) $(OBJS) lint: lint_SRCS include ../../../Makefile.targ Copyright (c) 1983 Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PORTIONS OF IFCONFIG COMMAND FUNCTIONALITY /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef _DEFS_H #define _DEFS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus } #endif #endif /* _DEFS_H */ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012, Daniil Lunev. All rights reserved. * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved. */ /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "defs.h" #include "strings.h" #include "ifconfig.h" #include #include #include #include #include #include #include #include #include #include #define LOOPBACK_IF "lo0" #define NONE_STR "none" #define ARP_MOD_NAME "arp" #define LIFC_DEFAULT (LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\ LIFC_UNDER_IPMP) typedef struct if_flags { uint64_t iff_value; char *iff_name; } if_flags_t; static if_flags_t if_flags_tbl[] = { { IFF_UP, "UP" }, { IFF_BROADCAST, "BROADCAST" }, { IFF_DEBUG, "DEBUG" }, { IFF_LOOPBACK, "LOOPBACK" }, { IFF_POINTOPOINT, "POINTOPOINT" }, { IFF_NOTRAILERS, "NOTRAILERS" }, { IFF_RUNNING, "RUNNING" }, { IFF_NOARP, "NOARP" }, { IFF_PROMISC, "PROMISC" }, { IFF_ALLMULTI, "ALLMULTI" }, { IFF_INTELLIGENT, "INTELLIGENT" }, { IFF_MULTICAST, "MULTICAST" }, { IFF_MULTI_BCAST, "MULTI_BCAST" }, { IFF_UNNUMBERED, "UNNUMBERED" }, { IFF_DHCPRUNNING, "DHCP" }, { IFF_PRIVATE, "PRIVATE" }, { IFF_NOXMIT, "NOXMIT" }, { IFF_NOLOCAL, "NOLOCAL" }, { IFF_DEPRECATED, "DEPRECATED" }, { IFF_ADDRCONF, "ADDRCONF" }, { IFF_ROUTER, "ROUTER" }, { IFF_NONUD, "NONUD" }, { IFF_ANYCAST, "ANYCAST" }, { IFF_NORTEXCH, "NORTEXCH" }, { IFF_IPV4, "IPv4" }, { IFF_IPV6, "IPv6" }, { IFF_NOFAILOVER, "NOFAILOVER" }, { IFF_FAILED, "FAILED" }, { IFF_STANDBY, "STANDBY" }, { IFF_INACTIVE, "INACTIVE" }, { IFF_OFFLINE, "OFFLINE" }, { IFF_XRESOLV, "XRESOLV" }, { IFF_COS_ENABLED, "CoS" }, { IFF_PREFERRED, "PREFERRED" }, { IFF_TEMPORARY, "TEMPORARY" }, { IFF_FIXEDMTU, "FIXEDMTU" }, { IFF_VIRTUAL, "VIRTUAL" }, { IFF_DUPLICATE, "DUPLICATE" }, { IFF_IPMP, "IPMP"}, { IFF_VRRP, "VRRP"}, { IFF_NOACCEPT, "NOACCEPT"}, { IFF_L3PROTECT, "L3PROTECT"} }; typedef struct { const char *ia_app; uint64_t ia_flag; uint_t ia_tries; } if_appflags_t; static const if_appflags_t if_appflags_tbl[] = { { "dhcpagent(8)", IFF_DHCPRUNNING, 1 }, { "in.ndpd(8)", IFF_ADDRCONF, 3 }, { NULL, 0, 0 } }; static dladm_handle_t dlh; boolean_t dlh_opened; static struct lifreq lifr; /* current interface name a particular function is accessing */ static char name[LIFNAMSIZ]; /* foreach interface saved name */ static char origname[LIFNAMSIZ]; static int setaddr; static boolean_t setaddr_done = _B_FALSE; static boolean_t ipsec_policy_set; static boolean_t ipsec_auth_covered; static ipadm_handle_t iph; static ipadm_addrobj_t ipaddr; /* * Make sure the algorithm variables hold more than the sizeof an algorithm * in PF_KEY. (For now, more than a uint8_t.) The NO_***_?ALG indicates that * there was no algorithm requested, and in the ipsec_req that service should * be disabled. (E.g. if ah_aalg remains NO_AH_AALG, then AH will be * disabled on that tunnel.) */ #define NO_AH_AALG 256 #define NO_ESP_AALG 256 #define NO_ESP_EALG 256 int s, s4, s6; int af = AF_INET; /* default address family */ int debug = 0; int all = 0; /* setifdhcp() needs to know this */ int verbose = 0; int v4compat = 0; /* Compatible printing format */ /* * Function prototypes for command functions. */ static int addif(char *arg, int64_t param); static int inetipmp(char *arg, int64_t param); static int inetplumb(char *arg, int64_t param); static int inetunplumb(char *arg, int64_t param); static int removeif(char *arg, int64_t param); static int setdebugflag(char *arg, int64_t param); static int setifaddr(char *arg, int64_t param); static int setifbroadaddr(char *arg, int64_t param); static int setifdstaddr(char *arg, int64_t param); static int setifether(char *arg, int64_t param); static int setifflags(char *arg, int64_t param); static int setifindex(char *arg, int64_t param); static int setifmetric(char *arg, int64_t param); static int setifmtu(char *arg, int64_t param); static int setifnetmask(char *arg, int64_t param); static int setifprefixlen(char *arg, int64_t param); static int setifrevarp(char *arg, int64_t param); static int setifsubnet(char *arg, int64_t param); static int setiftdst(char *arg, int64_t param); static int setiftoken(char *arg, int64_t param); static int setiftsrc(char *arg, int64_t param); static int setverboseflag(char *arg, int64_t param); static int set_tun_ah_alg(char *arg, int64_t param); static int set_tun_esp_auth_alg(char *arg, int64_t param); static int set_tun_esp_encr_alg(char *arg, int64_t param); static int modlist(char *arg, int64_t param); static int modinsert(char *arg, int64_t param); static int modremove(char *arg, int64_t param); static int setifgroupname(char *arg, int64_t param); static int configinfo(char *arg, int64_t param); static void print_config_flags(int af, uint64_t flags); static void print_flags(uint64_t flags); static void print_ifether(const char *ifname); static int set_tun_encap_limit(char *arg, int64_t param); static int clr_tun_encap_limit(char *arg, int64_t param); static int set_tun_hop_limit(char *arg, int64_t param); static int setzone(char *arg, int64_t param); static int setallzones(char *arg, int64_t param); static int setifsrc(char *arg, int64_t param); static int lifnum(const char *ifname); static void plumball(int, char **, int64_t, int64_t, int64_t); /* * Address family specific function prototypes. */ static void in_getaddr(char *s, struct sockaddr *saddr, int *plenp); static void in_status(int force, uint64_t flags); static void in_configinfo(int force, uint64_t flags); static void in6_getaddr(char *s, struct sockaddr *saddr, int *plenp); static void in6_status(int force, uint64_t flags); static void in6_configinfo(int force, uint64_t flags); /* * Misc support functions */ static boolean_t ni_entry(const char *, void *); static void foreachinterface(int argc, char *argv[], int af, int64_t onflags, int64_t offflags, int64_t lifc_flags); static void ifconfig(int argc, char *argv[], int af, struct ifaddrs *ifa); static boolean_t in_getmask(struct sockaddr_in *saddr, boolean_t addr_set); static int in_getprefixlen(char *addr, boolean_t slash, int plen); static boolean_t in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask); static void status(void); static void ifstatus(const char *ifname); static void tun_status(datalink_id_t); static void usage(void); static int setifdhcp(const char *caller, const char *ifname, int argc, char *argv[]); static int ip_domux2fd(int *, int *, int *, int *, int *); static int ip_plink(int, int, int, int, int); static int modop(char *arg, char op); static int find_all_interfaces(struct lifconf *lifcp, char **buf, int64_t lifc_flags); static int create_ipmp(const char *grname, int af, const char *ifname, boolean_t implicit); static void start_ipmp_daemon(void); static boolean_t ifaddr_up(ifaddrlistx_t *ifaddrp); static boolean_t ifaddr_down(ifaddrlistx_t *ifaddrp); static dladm_status_t ifconfig_dladm_open(const char *, datalink_class_t, datalink_id_t *); static void dladmerr_exit(dladm_status_t status, const char *str); static void ipadmerr_exit(ipadm_status_t status, const char *str); static boolean_t ifconfig_use_libipadm(int, const char *); void Perror0_exit(const char *cmd) __NORETURN; #define max(a, b) ((a) < (b) ? (b) : (a)) /* * DHCP_EXIT_IF_FAILURE indicates that the operation failed, but if there * are more interfaces to act on (i.e., ifconfig was invoked with -a), keep * on going rather than exit with an error. */ #define DHCP_EXIT_IF_FAILURE -1 #define NEXTARG 0xffffff /* command takes an argument */ #define OPTARG 0xfffffe /* command takes an optional argument */ #define AF_ANY (-1) /* Refer to the comments in ifconfig() on the netmask "hack" */ #define NETMASK_CMD "netmask" struct sockaddr_storage g_netmask; enum { G_NETMASK_NIL, G_NETMASK_PENDING, G_NETMASK_SET } g_netmask_set = G_NETMASK_NIL; struct cmd { char *c_name; int64_t c_parameter; /* NEXTARG means next argv */ int (*c_func)(char *, int64_t); int c_abortonfail; /* don't continue parsing args */ /* for the current interface */ int c_af; /* address family restrictions */ } cmds[] = { { "up", IFF_UP, setifflags, 0, AF_ANY }, { "down", -IFF_UP, setifflags, 0, AF_ANY }, { "trailers", -IFF_NOTRAILERS, setifflags, 0, AF_ANY }, { "-trailers", IFF_NOTRAILERS, setifflags, 0, AF_ANY }, { "arp", -IFF_NOARP, setifflags, 0, AF_INET }, { "-arp", IFF_NOARP, setifflags, 0, AF_INET }, { "router", IFF_ROUTER, setifflags, 0, AF_ANY }, { "-router", -IFF_ROUTER, setifflags, 0, AF_ANY }, { "private", IFF_PRIVATE, setifflags, 0, AF_ANY }, { "-private", -IFF_PRIVATE, setifflags, 0, AF_ANY }, { "xmit", -IFF_NOXMIT, setifflags, 0, AF_ANY }, { "-xmit", IFF_NOXMIT, setifflags, 0, AF_ANY }, { "-nud", IFF_NONUD, setifflags, 0, AF_INET6 }, { "nud", -IFF_NONUD, setifflags, 0, AF_INET6 }, { "anycast", IFF_ANYCAST, setifflags, 0, AF_ANY }, { "-anycast", -IFF_ANYCAST, setifflags, 0, AF_ANY }, { "local", -IFF_NOLOCAL, setifflags, 0, AF_ANY }, { "-local", IFF_NOLOCAL, setifflags, 0, AF_ANY }, { "deprecated", IFF_DEPRECATED, setifflags, 0, AF_ANY }, { "-deprecated", -IFF_DEPRECATED, setifflags, 0, AF_ANY }, { "preferred", IFF_PREFERRED, setifflags, 0, AF_INET6 }, { "-preferred", -IFF_PREFERRED, setifflags, 0, AF_INET6 }, { "debug", 0, setdebugflag, 0, AF_ANY }, { "verbose", 0, setverboseflag, 0, AF_ANY }, { NETMASK_CMD, NEXTARG, setifnetmask, 0, AF_INET }, { "metric", NEXTARG, setifmetric, 0, AF_ANY }, { "mtu", NEXTARG, setifmtu, 0, AF_ANY }, { "index", NEXTARG, setifindex, 0, AF_ANY }, { "broadcast", NEXTARG, setifbroadaddr, 0, AF_INET }, { "auto-revarp", 0, setifrevarp, 1, AF_INET }, { "ipmp", 0, inetipmp, 1, AF_ANY }, { "plumb", 0, inetplumb, 1, AF_ANY }, { "unplumb", 0, inetunplumb, 0, AF_ANY }, { "subnet", NEXTARG, setifsubnet, 0, AF_ANY }, { "token", NEXTARG, setiftoken, 0, AF_INET6 }, { "tsrc", NEXTARG, setiftsrc, 0, AF_ANY }, { "tdst", NEXTARG, setiftdst, 0, AF_ANY }, { "encr_auth_algs", NEXTARG, set_tun_esp_auth_alg, 0, AF_ANY }, { "encr_algs", NEXTARG, set_tun_esp_encr_alg, 0, AF_ANY }, { "auth_algs", NEXTARG, set_tun_ah_alg, 0, AF_ANY }, { "addif", NEXTARG, addif, 1, AF_ANY }, { "removeif", NEXTARG, removeif, 1, AF_ANY }, { "modlist", 0, modlist, 1, AF_ANY }, { "modinsert", NEXTARG, modinsert, 1, AF_ANY }, { "modremove", NEXTARG, modremove, 1, AF_ANY }, { "failover", -IFF_NOFAILOVER, setifflags, 1, AF_ANY }, { "-failover", IFF_NOFAILOVER, setifflags, 1, AF_ANY }, { "standby", IFF_STANDBY, setifflags, 1, AF_ANY }, { "-standby", -IFF_STANDBY, setifflags, 1, AF_ANY }, { "failed", IFF_FAILED, setifflags, 1, AF_ANY }, { "-failed", -IFF_FAILED, setifflags, 1, AF_ANY }, { "group", NEXTARG, setifgroupname, 1, AF_ANY }, { "configinfo", 0, configinfo, 1, AF_ANY }, { "encaplimit", NEXTARG, set_tun_encap_limit, 0, AF_ANY }, { "-encaplimit", 0, clr_tun_encap_limit, 0, AF_ANY }, { "thoplimit", NEXTARG, set_tun_hop_limit, 0, AF_ANY }, { "set", NEXTARG, setifaddr, 0, AF_ANY }, { "destination", NEXTARG, setifdstaddr, 0, AF_ANY }, { "zone", NEXTARG, setzone, 0, AF_ANY }, { "-zone", 0, setzone, 0, AF_ANY }, { "all-zones", 0, setallzones, 0, AF_ANY }, { "ether", OPTARG, setifether, 0, AF_ANY }, { "usesrc", NEXTARG, setifsrc, 0, AF_ANY }, /* * NOTE: any additions to this table must also be applied to ifparse * (usr/src/cmd/cmd-inet/sbin/ifparse/ifparse.c) */ { 0, 0, setifaddr, 0, AF_ANY }, { 0, 0, setifdstaddr, 0, AF_ANY }, { 0, 0, 0, 0, 0 }, }; typedef struct if_config_cmd { uint64_t iff_flag; int iff_af; char *iff_name; } if_config_cmd_t; /* * NOTE: print_config_flags() processes this table in order, so we put "up" * last so that we can be sure "-failover" will take effect first. Otherwise, * IPMP test addresses will erroneously migrate to the IPMP interface. */ static if_config_cmd_t if_config_cmd_tbl[] = { { IFF_NOTRAILERS, AF_UNSPEC, "-trailers" }, { IFF_PRIVATE, AF_UNSPEC, "private" }, { IFF_NOXMIT, AF_UNSPEC, "-xmit" }, { IFF_ANYCAST, AF_INET6, "anycast" }, { IFF_NOLOCAL, AF_UNSPEC, "-local" }, { IFF_DEPRECATED, AF_UNSPEC, "deprecated" }, { IFF_NOFAILOVER, AF_UNSPEC, "-failover" }, { IFF_STANDBY, AF_UNSPEC, "standby" }, { IFF_FAILED, AF_UNSPEC, "failed" }, { IFF_PREFERRED, AF_UNSPEC, "preferred" }, { IFF_NONUD, AF_INET6, "-nud" }, { IFF_NOARP, AF_INET, "-arp" }, { IFF_UP, AF_UNSPEC, "up" }, { 0, 0, NULL }, }; typedef struct ni { char ni_name[LIFNAMSIZ]; struct ni *ni_next; } ni_t; static ni_t *ni_list = NULL; static int num_ni = 0; /* End defines and structure definitions for ifconfig -a plumb */ /* Known address families */ struct afswtch { char *af_name; short af_af; void (*af_status)(); void (*af_getaddr)(); void (*af_configinfo)(); } afs[] = { { "inet", AF_INET, in_status, in_getaddr, in_configinfo }, { "inet6", AF_INET6, in6_status, in6_getaddr, in6_configinfo }, { 0, 0, 0, 0, 0 } }; #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) struct afswtch *afp; /* the address family being set or asked about */ int main(int argc, char *argv[]) { int64_t lifc_flags; char *default_ip_str; ipadm_status_t istatus; lifc_flags = LIFC_DEFAULT; if (argc < 2) { (void) strncpy(name, "-a", sizeof (name)); } else { argc--, argv++; if (strlen(*argv) > sizeof (name) - 1) { (void) fprintf(stderr, "%s: interface name too long\n", *argv); exit(1); } (void) strncpy(name, *argv, sizeof (name)); } name[sizeof (name) - 1] = '\0'; (void) strncpy(origname, name, sizeof (origname)); /* For addif */ default_ip_str = NULL; v4compat = get_compat_flag(&default_ip_str); if (v4compat == DEFAULT_PROT_BAD_VALUE) { (void) fprintf(stderr, "ifconfig: %s: Bad value for %s in %s\n", default_ip_str, DEFAULT_IP, INET_DEFAULT_FILE); free(default_ip_str); exit(2); } free(default_ip_str); argc--, argv++; if (argc > 0) { struct afswtch *myafp; for (myafp = afp = afs; myafp->af_name; myafp++) { if (strcmp(myafp->af_name, *argv) == 0) { afp = myafp; argc--; argv++; break; } } af = lifr.lifr_addr.ss_family = afp->af_af; if (af == AF_INET6) { v4compat = 0; } } s = socket(SOCKET_AF(af), SOCK_DGRAM, 0); s4 = socket(AF_INET, SOCK_DGRAM, 0); s6 = socket(AF_INET6, SOCK_DGRAM, 0); if (s == -1 || s4 == -1 || s6 == -1) Perror0_exit("socket"); /* * Open the global libipadm handle. The flag IPH_LEGACY has to * be specified to indicate that logical interface names will * be used during interface creation and address creation. */ if ((istatus = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) ipadmerr_exit(istatus, "unable to open handle to libipadm"); /* * Special interface names is any combination of these flags. * Note that due to the ifconfig syntax they have to be combined * as a single '-' option. * -a All interfaces * -u "up" interfaces * -d "down" interfaces * -D Interfaces not controlled by DHCP * -4 IPv4 interfaces * -6 IPv6 interfaces * -X Turn on debug (not documented) * -v Turn on verbose * -Z Only interfaces in caller's zone */ if (name[0] == '-') { /* One or more options */ int64_t onflags = 0; int64_t offflags = 0; int c; char *av[2] = { "ifconfig", name }; while ((c = getopt(2, av, "audhDXZ46v")) != -1) { switch ((char)c) { case 'a': all = 1; break; case 'u': onflags |= IFF_UP; break; case 'd': offflags |= IFF_UP; break; case 'D': offflags |= IFF_DHCPRUNNING; break; case 'X': debug += 3; break; case 'Z': lifc_flags &= ~LIFC_ALLZONES; break; case '4': /* * -4 is not a compatable flag, therefore * we assume they want v4compat turned off */ v4compat = 0; onflags |= IFF_IPV4; break; case '6': /* * If they want IPv6, well then we'll assume * they don't want IPv4 compat */ v4compat = 0; onflags |= IFF_IPV6; break; case 'v': verbose = 1; break; case 'h': case '?': usage(); exit(1); } } if (!all) { (void) fprintf(stderr, "ifconfig: %s: no such interface\n", name); exit(1); } foreachinterface(argc, argv, af, onflags, offflags, lifc_flags); } else { ifconfig(argc, argv, af, NULL); } ipadm_close(iph); return (0); } /* * For each interface, call ifconfig(argc, argv, af, ifa). * Only call function if onflags and offflags are set or clear, respectively, * in the interfaces flags field. */ static void foreachinterface(int argc, char *argv[], int af, int64_t onflags, int64_t offflags, int64_t lifc_flags) { ipadm_addr_info_t *ainfo, *ainfop; struct ifaddrs *ifa; ipadm_status_t istatus; /* * Special case: * ifconfig -a plumb should find all network interfaces in the current * zone. */ if (argc > 0 && (strcmp(*argv, "plumb") == 0)) { plumball(argc, argv, onflags, offflags, lifc_flags); return; } /* Get all addresses in kernel including addresses that are zero. */ istatus = ipadm_addr_info(iph, NULL, &ainfo, IPADM_OPT_ZEROADDR, lifc_flags); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "could not get addresses from kernel"); /* * For each logical interface, call ifconfig() with the * given arguments. */ for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { if (ainfop->ia_state == IFA_DISABLED) continue; ifa = &ainfop->ia_ifa; if (onflags || offflags) { if ((ifa->ifa_flags & onflags) != onflags) continue; if ((~ifa->ifa_flags & offflags) != offflags) continue; } s = (ifa->ifa_addr->sa_family == AF_INET ? s4 : s6); (void) strncpy(name, ifa->ifa_name, sizeof (name)); (void) strncpy(origname, name, sizeof (origname)); ifconfig(argc, argv, af, ifa); } ipadm_free_addr_info(ainfo); } /* * Used for `ifconfig -a plumb'. Finds all datalinks and plumbs the interface. */ static void plumball(int argc, char *argv[], int64_t onflags, int64_t offflags, int64_t lifc_flags) { int n; struct lifreq *lifrp; struct lifconf lifc; char *buf; if (onflags != 0 || offflags != 0) { (void) fprintf(stderr, "ifconfig: invalid syntax used to " "plumb all interfaces.\n"); exit(1); } if (find_all_interfaces(&lifc, &buf, lifc_flags) != 0 || lifc.lifc_len == 0) return; lifrp = lifc.lifc_req; for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { /* * Reset global state * setaddr: Used by parser to tear apart source and dest * name and origname contain the name of the 'current' * interface. */ setaddr = 0; (void) strncpy(name, lifrp->lifr_name, sizeof (name)); (void) strncpy(origname, name, sizeof (origname)); ifconfig(argc, argv, af, NULL); } } /* * Parses the interface name and the command in argv[]. Calls the * appropriate callback function for the given command from `cmds[]' * table. * If there is no command specified, it prints all addresses. */ static void ifconfig(int argc, char *argv[], int af, struct ifaddrs *ifa) { static boolean_t scan_netmask = _B_FALSE; int ret; ipadm_status_t istatus; struct lifreq lifr; if (argc == 0) { status(); return; } if (strcmp(*argv, "auto-dhcp") == 0 || strcmp(*argv, "dhcp") == 0) { /* * Some errors are ignored in the case where more than one * interface is being operated on. */ ret = setifdhcp("ifconfig", name, argc, argv); if (ret == DHCP_EXIT_IF_FAILURE) { if (!all) exit(DHCP_EXIT_FAILURE); } else if (ret != DHCP_EXIT_SUCCESS) { exit(ret); } return; } /* * The following is a "hack" to get around the existing interface * setting mechanism. Currently, each interface attribute, * such as address, netmask, broadcast, ... is set separately. But * sometimes two or more attributes must be set together. For * example, setting an address without a netmask does not make sense. * Yet they can be set separately for IPv4 address using the current * ifconfig(8) syntax. The kernel then "infers" the correct netmask * using the deprecated "IP address classes." This is simply not * correct. * * The "hack" below is to go thru the whole command list looking for * the netmask command first. Then use this netmask to set the * address. This does not provide an extensible way to accommodate * future need for setting more than one attributes together. * * Note that if the "netmask" command argument is a "+", we need * to save this info and do the query after we know the address to * be set. The reason is that if "addif" is used, the working * interface name will be changed later when the logical interface * is created. In in_getmask(), if an address is not provided, * it will use the working interface's address to do the query. * It will be wrong now as we don't know the logical interface's name. * * ifconfig(8) is too overloaded and the code is so convoluted * that it is "safer" not to re-architect the code to fix the above * issue, hence this "hack." We may be better off to have a new * command with better syntax for configuring network interface * parameters... */ if (!scan_netmask && afp->af_af == AF_INET) { int largc; char **largv; /* Only go thru the command list once to find the netmask. */ scan_netmask = _B_TRUE; /* * Currently, if multiple netmask commands are specified, the * last one will be used as the final netmask. So we need * to scan the whole list to preserve this behavior. */ for (largc = argc, largv = argv; largc > 0; largc--, largv++) { if (strcmp(*largv, NETMASK_CMD) == 0) { if (--largc == 0) break; largv++; if (strcmp(*largv, "+") == 0) { g_netmask_set = G_NETMASK_PENDING; } else { in_getaddr(*largv, (struct sockaddr *) &g_netmask, NULL); g_netmask_set = G_NETMASK_SET; } /* Continue the scan. */ } } } while (argc > 0) { struct cmd *p; boolean_t found_cmd; if (debug) (void) printf("ifconfig: argv %s\n", *argv); found_cmd = _B_FALSE; for (p = cmds; p->c_func; p++) { if (p->c_name) { if (strcmp(*argv, p->c_name) == 0) { /* * indicate that the command was * found and check to see if * the address family is valid */ found_cmd = _B_TRUE; if (p->c_af == AF_ANY || af == p->c_af) break; } } else { if (p->c_af == AF_ANY || af == p->c_af) break; } } /* * If we found the keyword, but the address family * did not match spit out an error */ if (found_cmd && p->c_name == 0) { (void) fprintf(stderr, "ifconfig: Operation %s not" " supported for %s\n", *argv, afp->af_name); exit(1); } /* * else (no keyword found), we assume it's an address * of some sort */ if (setaddr && ipaddr != NULL) { /* * We must have already filled in a source address in * `ipaddr' and we now got a destination address. * Fill it in `ipaddr' and call libipadm to create * the static address. */ if (p->c_name == 0) { istatus = ipadm_set_dst_addr(ipaddr, *argv, (p->c_af == AF_ANY ? AF_UNSPEC : af)); if (istatus != IPADM_SUCCESS) { ipadmerr_exit(istatus, "could not " "set destination address"); } /* * finished processing dstaddr, so reset setaddr */ setaddr = 0; } /* * Both source and destination address are in `ipaddr'. * Add the address by calling libipadm. */ istatus = ipadm_create_addr(iph, ipaddr, IPADM_OPT_ACTIVE); if (istatus != IPADM_SUCCESS) goto createfailed; ipadm_destroy_addrobj(ipaddr); ipaddr = NULL; setaddr_done = _B_TRUE; if (p->c_name == 0) { /* move parser along */ argc--, argv++; continue; } } if (p->c_name == 0 && setaddr_done) { /* * catch odd commands like * "ifconfig addr1 addr2 addr3 addr4 up" */ (void) fprintf(stderr, "%s", "ifconfig: cannot configure more than two " "addresses in one command\n"); exit(1); } if (p->c_func) { if (p->c_af == AF_INET6) { v4compat = 0; } if (p->c_parameter == NEXTARG || p->c_parameter == OPTARG) { argc--, argv++; if (argc == 0 && p->c_parameter == NEXTARG) { (void) fprintf(stderr, "ifconfig: no argument for %s\n", p->c_name); exit(1); } } /* * Call the function if: * * there's no address family * restriction * OR * we don't know the address yet * (because we were called from * main) * OR * there is a restriction AND * the address families match */ if ((p->c_af == AF_ANY) || (ifa == NULL) || (ifa->ifa_addr->sa_family == p->c_af)) { ret = (*p->c_func)(*argv, p->c_parameter); /* * If c_func failed and we should * abort processing for this * interface on failure, return * now rather than going on to * process other commands for * the same interface. */ if (ret != 0 && p->c_abortonfail) return; } } argc--, argv++; } if (setaddr && ipaddr != NULL) { /* * Only the source address was provided, which was already * set in `ipaddr'. Add the address by calling libipadm. */ istatus = ipadm_create_addr(iph, ipaddr, IPADM_OPT_ACTIVE); if (istatus != IPADM_SUCCESS) goto createfailed; ipadm_destroy_addrobj(ipaddr); ipaddr = NULL; setaddr_done = _B_TRUE; } /* Check to see if there's a security hole in the tunnel setup. */ if (ipsec_policy_set && !ipsec_auth_covered) { (void) fprintf(stderr, "ifconfig: WARNING: tunnel with only " "ESP and no authentication.\n"); } return; createfailed: (void) fprintf(stderr, "ifconfig: could not create address:% s\n", ipadm_status2str(istatus)); /* Remove the newly created logical interface. */ if (strcmp(name, origname) != 0) { assert(strchr(name, ':') != NULL); (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); (void) ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr); } exit(1); } /* ARGSUSED */ static int setdebugflag(char *val, int64_t arg) { debug++; return (0); } /* ARGSUSED */ static int setverboseflag(char *val, int64_t arg) { verbose++; return (0); } /* * This function fills in the given lifreq's lifr_addr field based on * g_netmask_set. */ static void set_mask_lifreq(struct lifreq *lifr, struct sockaddr_storage *addr, struct sockaddr_storage *mask) { assert(addr != NULL); assert(mask != NULL); switch (g_netmask_set) { case G_NETMASK_SET: lifr->lifr_addr = g_netmask; break; case G_NETMASK_PENDING: /* * "+" is used as the argument to "netmask" command. Query * the database on the correct netmask based on the address to * be set. */ assert(afp->af_af == AF_INET); g_netmask = *addr; if (!in_getmask((struct sockaddr_in *)&g_netmask, _B_TRUE)) { lifr->lifr_addr = *mask; g_netmask_set = G_NETMASK_NIL; } else { lifr->lifr_addr = g_netmask; g_netmask_set = G_NETMASK_SET; } break; case G_NETMASK_NIL: default: lifr->lifr_addr = *mask; break; } } /* * Set the interface address. Handles , / as well as / * syntax for setting the address, the address plus netmask, and just * the netmask respectively. */ /* ARGSUSED */ static int setifaddr(char *addr, int64_t param) { ipadm_status_t istatus; int prefixlen = 0; struct lifreq lifr1; struct sockaddr_storage laddr; struct sockaddr_storage netmask; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct sockaddr_storage sav_netmask; char cidraddr[BUFSIZ]; if (addr[0] == '/') return (setifprefixlen(addr, 0)); (*afp->af_getaddr)(addr, (struct sockaddr *)&laddr, &prefixlen); (void) memset(&netmask, 0, sizeof (netmask)); netmask.ss_family = afp->af_af; switch (prefixlen) { case NO_PREFIX: /* Nothing there - ok */ break; case BAD_ADDR: (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n", addr); exit(1); default: if (afp->af_af == AF_INET6) { sin6 = (struct sockaddr_in6 *)&netmask; if (!in_prefixlentomask(prefixlen, IPV6_ABITS, (uchar_t *)&sin6->sin6_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } else { sin = (struct sockaddr_in *)&netmask; if (!in_prefixlentomask(prefixlen, IP_ABITS, (uchar_t *)&sin->sin_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } /* * Just in case of funny setting of both prefix and netmask, * prefix should override the netmask command. */ g_netmask_set = G_NETMASK_NIL; break; } /* * Check and see if any "netmask" command is used and perform the * necessary operation. */ set_mask_lifreq(&lifr, &laddr, &netmask); /* This check is temporary until libipadm supports IPMP interfaces. */ if (ifconfig_use_libipadm(s, name)) { char addrstr[INET6_ADDRSTRLEN]; if (af == AF_INET) { sin = (struct sockaddr_in *)&laddr; (void) inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof (addrstr)); } else { sin6 = (struct sockaddr_in6 *)&laddr; (void) inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof (addrstr)); } istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name, &ipaddr); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "setifaddr"); /* * lifr.lifr_addr, which is updated by set_mask_lifreq() * will contain the right mask to use. */ prefixlen = mask2plen((struct sockaddr *)&lifr.lifr_addr); (void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d", addrstr, prefixlen); istatus = ipadm_set_addr(ipaddr, cidraddr, af); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "could not set address"); /* * let parser know we got a source. * Next address, if given, should be dest */ setaddr++; /* * address will be set by the parser after nextarg has * been scanned */ return (0); } /* Tell parser that an address was set */ setaddr++; /* save copy of netmask to restore in case of error */ (void) strncpy(lifr1.lifr_name, name, sizeof (lifr1.lifr_name)); if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr1) < 0) Perror0_exit("SIOCGLIFNETMASK"); sav_netmask = lifr1.lifr_addr; /* * If setting the address and not the mask, clear any existing mask * and the kernel will then assign the default (netmask has been set * to 0 in this case). If setting both (either by using a prefix or * using the netmask command), set the mask first, so the address will * be interpreted correctly. */ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); /* lifr.lifr_addr already contains netmask from set_mask_lifreq() */ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFNETMASK"); if (debug) { char abuf[INET6_ADDRSTRLEN]; void *addr = (afp->af_af == AF_INET) ? (void *)&((struct sockaddr_in *)&laddr)->sin_addr : (void *)&((struct sockaddr_in6 *)&laddr)->sin6_addr; (void) printf("Setting %s af %d addr %s\n", lifr.lifr_name, afp->af_af, inet_ntop(afp->af_af, addr, abuf, sizeof (abuf))); } lifr.lifr_addr = laddr; lifr.lifr_addr.ss_family = afp->af_af; if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) { /* * Restore the netmask */ int saverr = errno; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_addr = sav_netmask; (void) ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr); errno = saverr; Perror0_exit("SIOCSLIFADDR"); } return (0); } /* * The following functions are stolen from the ipseckey(8) program. * Perhaps they should be somewhere common, but for now, we just maintain * two versions. We do this because of the different semantics for which * algorithms we select ("requested" for ifconfig vs. "actual" for key). */ static ulong_t parsenum(char *num) { ulong_t rc; char *end = NULL; errno = 0; rc = strtoul(num, &end, 0); if (errno != 0 || end == num || *end != '\0') { rc = (ulong_t)-1; } return (rc); } /* * Parse and reverse parse possible algorithm values, include numbers. * Mostly stolen from ipseckey.c. See the comments above parsenum() for why * this isn't common to ipseckey.c. * * NOTE: Static buffer in this function for the return value. Since ifconfig * isn't multithreaded, this isn't a huge problem. */ #define NBUF_SIZE 20 /* Enough to print a large integer. */ static char * rparsealg(uint8_t alg_value, int proto_num) { struct ipsecalgent *alg; static char numprint[128]; /* Enough to hold an algorithm name. */ /* * Special cases for "any" and "none" * The kernel needs to be able to distinguish between "any" * and "none" and the APIs are underdefined in this area for auth. */ if (proto_num == IPSEC_PROTO_AH) { if (alg_value == SADB_AALG_NONE) return ("none"); if (alg_value == SADB_AALG_ANY) return ("any"); } alg = getipsecalgbynum(alg_value, proto_num, NULL); if (alg != NULL) { (void) strlcpy(numprint, alg->a_names[0], sizeof (numprint)); freeipsecalgent(alg); } else { (void) snprintf(numprint, sizeof (numprint), "%d", alg_value); } return (numprint); } static uint_t parsealg(char *algname, int proto_num) { struct ipsecalgent *alg; ulong_t invalue; if (algname == NULL) { (void) fprintf(stderr, "ifconfig: Unexpected end of command " "line.\n"); exit(1); } /* * Special-case "none" and "any". * Use strcasecmp because its length is bounded. */ if (strcasecmp("none", algname) == 0) { return ((proto_num == IPSEC_PROTO_ESP) ? NO_ESP_EALG : NO_ESP_AALG); } if ((strcasecmp("any", algname) == 0) && (proto_num == IPSEC_PROTO_AH)) return (SADB_AALG_ANY); alg = getipsecalgbyname(algname, proto_num, NULL); if (alg != NULL) { invalue = alg->a_alg_num; freeipsecalgent(alg); return ((uint_t)invalue); } /* * Since algorithms can be loaded during kernel run-time, check for * numeric algorithm values too. */ invalue = parsenum(algname); if ((invalue & (ulong_t)0xff) == invalue) return ((uint_t)invalue); (void) fprintf(stderr, "ifconfig: %s algorithm type %s unknown.\n", (proto_num == IPSEC_PROTO_ESP) ? "Encryption" : "Authentication", algname); exit(1); /* NOTREACHED */ } /* * Actual ifconfig functions to set tunnel security properties. */ enum ipsec_alg_type { ESP_ENCR_ALG = 1, ESP_AUTH_ALG, AH_AUTH_ALG }; static int set_tun_algs(int which_alg, int alg) { boolean_t encr_alg_set = _B_FALSE; iptun_params_t params; dladm_status_t status; ipsec_req_t *ipsr; if ((status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, ¶ms.iptun_param_linkid)) != DLADM_STATUS_OK) goto done; status = dladm_iptun_getparams(dlh, ¶ms, DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) goto done; ipsr = ¶ms.iptun_param_secinfo; /* * If I'm just starting off this ifconfig, I want a clean slate, * otherwise, I've captured the current tunnel security settings. * In the case of continuation, I merely add to the settings. */ if (!(params.iptun_param_flags & IPTUN_PARAM_SECINFO)) (void) memset(ipsr, 0, sizeof (*ipsr)); /* We're only modifying the IPsec information */ params.iptun_param_flags = IPTUN_PARAM_SECINFO; switch (which_alg) { case ESP_ENCR_ALG: if (alg == NO_ESP_EALG) { if (ipsr->ipsr_esp_auth_alg == SADB_AALG_NONE) ipsr->ipsr_esp_req = 0; ipsr->ipsr_esp_alg = SADB_EALG_NONE; /* Let the user specify NULL encryption implicitly. */ if (ipsr->ipsr_esp_auth_alg != SADB_AALG_NONE) { encr_alg_set = _B_TRUE; ipsr->ipsr_esp_alg = SADB_EALG_NULL; } } else { encr_alg_set = _B_TRUE; ipsr->ipsr_esp_req = IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE; ipsr->ipsr_esp_alg = alg; } break; case ESP_AUTH_ALG: if (alg == NO_ESP_AALG) { if ((ipsr->ipsr_esp_alg == SADB_EALG_NONE || ipsr->ipsr_esp_alg == SADB_EALG_NULL) && !encr_alg_set) ipsr->ipsr_esp_req = 0; ipsr->ipsr_esp_auth_alg = SADB_AALG_NONE; } else { ipsr->ipsr_esp_req = IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE; ipsr->ipsr_esp_auth_alg = alg; /* Let the user specify NULL encryption implicitly. */ if (ipsr->ipsr_esp_alg == SADB_EALG_NONE && !encr_alg_set) ipsr->ipsr_esp_alg = SADB_EALG_NULL; } break; case AH_AUTH_ALG: if (alg == NO_AH_AALG) { ipsr->ipsr_ah_req = 0; ipsr->ipsr_auth_alg = SADB_AALG_NONE; } else { ipsr->ipsr_ah_req = IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE; ipsr->ipsr_auth_alg = alg; } break; /* Will never hit DEFAULT */ } status = dladm_iptun_modify(dlh, ¶ms, DLADM_OPT_ACTIVE); done: if (status != DLADM_STATUS_OK) dladmerr_exit(status, name); else { ipsec_policy_set = _B_TRUE; if ((ipsr->ipsr_esp_req != 0 && ipsr->ipsr_esp_auth_alg != SADB_AALG_NONE) || (ipsr->ipsr_ah_req != 0 && ipsr->ipsr_auth_alg != SADB_AALG_NONE)) ipsec_auth_covered = _B_TRUE; } return (0); } /* ARGSUSED */ static int set_tun_esp_encr_alg(char *addr, int64_t param) { return (set_tun_algs(ESP_ENCR_ALG, parsealg(addr, IPSEC_PROTO_ESP))); } /* ARGSUSED */ static int set_tun_esp_auth_alg(char *addr, int64_t param) { return (set_tun_algs(ESP_AUTH_ALG, parsealg(addr, IPSEC_PROTO_AH))); } /* ARGSUSED */ static int set_tun_ah_alg(char *addr, int64_t param) { return (set_tun_algs(AH_AUTH_ALG, parsealg(addr, IPSEC_PROTO_AH))); } /* ARGSUSED */ static int setifrevarp(char *arg, int64_t param) { struct sockaddr_in laddr; if (afp->af_af == AF_INET6) { (void) fprintf(stderr, "ifconfig: revarp not possible on IPv6 interface %s\n", name); exit(1); } if (doifrevarp(name, &laddr)) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); laddr.sin_family = AF_INET; (void) memcpy(&lifr.lifr_addr, &laddr, sizeof (laddr)); if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFADDR"); } return (0); } /* ARGSUSED */ static int setifsubnet(char *addr, int64_t param) { int prefixlen = 0; struct sockaddr_storage subnet; (*afp->af_getaddr)(addr, &subnet, &prefixlen); switch (prefixlen) { case NO_PREFIX: (void) fprintf(stderr, "ifconfig: Missing prefix length in subnet %s\n", addr); exit(1); /* NOTREACHED */ case BAD_ADDR: (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n", addr); exit(1); default: break; } lifr.lifr_addr = subnet; lifr.lifr_addrlen = prefixlen; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFSUBNET, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFSUBNET"); return (0); } /* ARGSUSED */ static int setifnetmask(char *addr, int64_t param) { struct sockaddr_in netmask; assert(afp->af_af != AF_INET6); if (strcmp(addr, "+") == 0) { if (!in_getmask(&netmask, _B_FALSE)) return (0); (void) printf("Setting netmask of %s to %s\n", name, inet_ntoa(netmask.sin_addr)); } else { in_getaddr(addr, (struct sockaddr *)&netmask, NULL); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); (void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask)); if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFNETMASK"); return (0); } /* * Parse '/' as a netmask. */ /* ARGSUSED */ static int setifprefixlen(char *addr, int64_t param) { int prefixlen; int af = afp->af_af; prefixlen = in_getprefixlen(addr, _B_TRUE, (af == AF_INET) ? IP_ABITS : IPV6_ABITS); if (prefixlen < 0) { (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n", addr); exit(1); } (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); lifr.lifr_addr.ss_family = af; if (af == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; if (!in_prefixlentomask(prefixlen, IPV6_ABITS, (uchar_t *)&sin6->sin6_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } else if (af == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)&lifr.lifr_addr; if (!in_prefixlentomask(prefixlen, IP_ABITS, (uchar_t *)&sin->sin_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } else { (void) fprintf(stderr, "ifconfig: setting prefix only supported" " for address family inet or inet6\n"); exit(1); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFNETMASK"); return (0); } /* ARGSUSED */ static int setifbroadaddr(char *addr, int64_t param) { struct sockaddr_in broadaddr; assert(afp->af_af != AF_INET6); if (strcmp(addr, "+") == 0) { /* * This doesn't set the broadcast address at all. Rather, it * gets, then sets the interface's address, relying on the fact * that resetting the address will reset the broadcast address. */ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) Perror0_exit("SIOCGLIFADDR"); return (0); } if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) Perror0_exit("SIOCGLIFADDR"); return (0); } in_getaddr(addr, (struct sockaddr *)&broadaddr, NULL); (void) memcpy(&lifr.lifr_addr, &broadaddr, sizeof (broadaddr)); (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFBRDADDR, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFBRDADDR"); return (0); } /* * set interface destination address */ /* ARGSUSED */ static int setifdstaddr(char *addr, int64_t param) { (*afp->af_getaddr)(addr, (struct sockaddr *)&lifr.lifr_addr, NULL); (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0) Perror0_exit("setifdstaddr: SIOCSLIFDSTADDR"); return (0); } /* ARGSUSED */ static int setifflags(char *val, int64_t value) { struct lifreq lifrl; /* local lifreq struct */ boolean_t bringup = _B_FALSE; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) Perror0_exit("setifflags: SIOCGLIFFLAGS"); if (value < 0) { value = -value; if ((value & IFF_NOFAILOVER) && (lifr.lifr_flags & IFF_UP)) { /* * The kernel does not allow administratively up test * addresses to be converted to data addresses. Bring * the address down first, then bring it up after it's * been converted to a data address. */ lifr.lifr_flags &= ~IFF_UP; (void) ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr); bringup = _B_TRUE; } lifr.lifr_flags &= ~value; if ((value & (IFF_UP | IFF_NOFAILOVER)) && (lifr.lifr_flags & IFF_DUPLICATE)) { /* * If the user is trying to mark an interface with a * duplicate address as "down," or convert a duplicate * test address to a data address, then fetch the * address and set it. This will cause IP to clear * the IFF_DUPLICATE flag and stop the automatic * recovery timer. */ value = lifr.lifr_flags; if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0) (void) ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr); lifr.lifr_flags = value; } } else { lifr.lifr_flags |= value; } /* * If we're about to bring up an underlying physical IPv6 interface in * an IPMP group, ensure the IPv6 IPMP interface is also up. This is * for backward compatibility with legacy configurations in which * there are no explicit hostname files for IPMP interfaces. (For * IPv4, this is automatically handled by the kernel when migrating * the underlying interface's data address to the IPMP interface.) */ (void) strlcpy(lifrl.lifr_name, name, LIFNAMSIZ); if (lifnum(lifr.lifr_name) == 0 && (lifr.lifr_flags & (IFF_UP|IFF_IPV6)) == (IFF_UP|IFF_IPV6) && ioctl(s, SIOCGLIFGROUPNAME, &lifrl) == 0 && lifrl.lifr_groupname[0] != '\0') { lifgroupinfo_t lifgr; (void) strlcpy(lifgr.gi_grname, lifrl.lifr_groupname, LIFGRNAMSIZ); if (ioctl(s, SIOCGLIFGROUPINFO, &lifgr) == -1) Perror0_exit("setifflags: SIOCGLIFGROUPINFO"); (void) strlcpy(lifrl.lifr_name, lifgr.gi_grifname, LIFNAMSIZ); if (ioctl(s, SIOCGLIFFLAGS, &lifrl) == -1) Perror0_exit("setifflags: SIOCGLIFFLAGS"); if (!(lifrl.lifr_flags & IFF_UP)) { lifrl.lifr_flags |= IFF_UP; if (ioctl(s, SIOCSLIFFLAGS, &lifrl) == -1) Perror0_exit("setifflags: SIOCSLIFFLAGS"); } } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) Perror0_exit("setifflags: SIOCSLIFFLAGS"); if (bringup) { lifr.lifr_flags |= IFF_UP; if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) Perror0_exit("setifflags: SIOCSLIFFLAGS IFF_UP"); } return (0); } /* ARGSUSED */ static int setifmetric(char *val, int64_t param) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_metric = atoi(val); if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0) Perror0_exit("setifmetric: SIOCSLIFMETRIC"); return (0); } /* ARGSUSED */ static int setifmtu(char *val, int64_t param) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_mtu = atoi(val); if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0) Perror0_exit("setifmtu: SIOCSLIFMTU"); return (0); } /* ARGSUSED */ static int setifindex(char *val, int64_t param) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_index = atoi(val); if (ioctl(s, SIOCSLIFINDEX, (caddr_t)&lifr) < 0) Perror0_exit("setifindex: SIOCSLIFINDEX"); return (0); } /* ARGSUSED */ static void notifycb(dlpi_handle_t dh, dlpi_notifyinfo_t *dnip, void *arg) { } /* ARGSUSED */ static int setifether(char *addr, int64_t param) { uchar_t *hwaddr; int hwaddrlen; int retval; ifaddrlistx_t *ifaddrp, *ifaddrs = NULL; dlpi_handle_t dh; dlpi_notifyid_t id; if (addr == NULL) { ifstatus(name); print_ifether(name); return (0); } /* * if the IP interface in the arguments is a logical * interface, exit with an error now. */ if (strchr(name, ':') != NULL) { (void) fprintf(stderr, "ifconfig: cannot change" " ethernet address of a logical interface\n"); exit(1); } if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL) { if (hwaddrlen == -1) (void) fprintf(stderr, "ifconfig: bad ethernet address\n"); else (void) fprintf(stderr, "ifconfig: malloc() failed\n"); exit(1); } if ((retval = dlpi_open(name, &dh, 0)) != DLPI_SUCCESS) Perrdlpi_exit("cannot dlpi_open() link", name, retval); retval = dlpi_enabnotify(dh, DL_NOTE_PHYS_ADDR, notifycb, NULL, &id); if (retval == DLPI_SUCCESS) { (void) dlpi_disabnotify(dh, id, NULL); } else { /* * This link does not support DL_NOTE_PHYS_ADDR: bring down * all of the addresses to flush the old hardware address * information out of IP. * * NOTE: Skipping this when DL_NOTE_PHYS_ADDR is supported is * more than an optimization: in.mpathd will set IFF_OFFLINE * if it's notified and the new address is a duplicate of * another in the group -- but the flags manipulation in * ifaddr_{down,up}() cannot be atomic and thus might clobber * IFF_OFFLINE, confusing in.mpathd. */ if (ifaddrlistx(name, IFF_UP, 0, &ifaddrs) == -1) Perror2_exit(name, "cannot get address list"); ifaddrp = ifaddrs; for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { if (!ifaddr_down(ifaddrp)) { Perror2_exit(ifaddrp->ia_name, "cannot bring down"); } } } /* * Change the hardware address. */ retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr, hwaddrlen); if (retval != DLPI_SUCCESS) { (void) fprintf(stderr, "ifconfig: failed setting mac address on %s\n", name); } dlpi_close(dh); /* * If any addresses were brought down before changing the hardware * address, bring them up again. */ for (ifaddrp = ifaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { if (!ifaddr_up(ifaddrp)) Perror2_exit(ifaddrp->ia_name, "cannot bring up"); } ifaddrlistx_free(ifaddrs); return (0); } /* * Print an interface's Ethernet address, if it has one. */ static void print_ifether(const char *ifname) { int fd; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { /* * It's possible the interface is only configured for * IPv6; check again with AF_INET6. */ (void) close(fd); fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd == -1 || ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) { (void) close(fd); return; } } (void) close(fd); /* VNI and IPMP interfaces don't have MAC addresses */ if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) return; /* IP tunnels also don't have Ethernet-like MAC addresses */ if (ifconfig_dladm_open(ifname, DATALINK_CLASS_IPTUN, NULL) == DLADM_STATUS_OK) return; dlpi_print_address(ifname); } /* * static int find_all_interfaces(struct lifconf *lifcp, char **buf, * int64_t lifc_flags) * * It finds all active data links. * * It takes in input a pointer to struct lifconf to receive interfaces * informations, a **char to hold allocated buffer, and a lifc_flags. * * Return values: * 0 = everything OK * -1 = problem */ static int find_all_interfaces(struct lifconf *lifcp, char **buf, int64_t lifc_flags) { unsigned bufsize; int n; ni_t *nip; struct lifreq *lifrp; dladm_status_t status; if (!dlh_opened) { status = ifconfig_dladm_open(NULL, 0, NULL); if (status != DLADM_STATUS_OK) dladmerr_exit(status, "unable to open dladm handle"); } (void) dlpi_walk(ni_entry, dlh, 0); /* Now, translate the linked list into a struct lifreq buffer */ if (num_ni == 0) { lifcp->lifc_family = AF_UNSPEC; lifcp->lifc_flags = lifc_flags; lifcp->lifc_len = 0; lifcp->lifc_buf = NULL; return (0); } bufsize = num_ni * sizeof (struct lifreq); if ((*buf = malloc(bufsize)) == NULL) Perror0_exit("find_all_interfaces: malloc failed"); lifcp->lifc_family = AF_UNSPEC; lifcp->lifc_flags = lifc_flags; lifcp->lifc_len = bufsize; lifcp->lifc_buf = *buf; for (n = 0, lifrp = lifcp->lifc_req; n < num_ni; n++, lifrp++) { nip = ni_list; (void) strncpy(lifrp->lifr_name, nip->ni_name, sizeof (lifr.lifr_name)); ni_list = nip->ni_next; free(nip); } return (0); } /* * Create the next unused logical interface using the original name * and assign the address (and mask if '/' is part of the address). * Use the new logical interface for subsequent subcommands by updating * the name variable. * * This allows syntax like: * ifconfig le0 addif 109.106.86.130 netmask + up \ * addif 109.106.86.131 netmask + up */ /* ARGSUSED */ static int addif(char *str, int64_t param) { int prefixlen = 0; struct sockaddr_storage laddr; struct sockaddr_storage mask; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; ipadm_status_t istatus; char cidraddr[BUFSIZ]; char addrstr[INET6_ADDRSTRLEN]; (void) strncpy(name, origname, sizeof (name)); if (strchr(name, ':') != NULL) { (void) fprintf(stderr, "ifconfig: addif: bad physical interface name %s\n", name); exit(1); } /* * clear so parser will interpret next address as source followed * by possible dest */ setaddr = 0; (*afp->af_getaddr)(str, (struct sockaddr *)&laddr, &prefixlen); (void) memset(&mask, 0, sizeof (mask)); mask.ss_family = afp->af_af; switch (prefixlen) { case NO_PREFIX: /* Nothing there - ok */ break; case BAD_ADDR: (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n", str); exit(1); default: (void) memset(&mask, 0, sizeof (mask)); mask.ss_family = afp->af_af; if (afp->af_af == AF_INET6) { sin6 = (struct sockaddr_in6 *)&mask; if (!in_prefixlentomask(prefixlen, IPV6_ABITS, (uchar_t *)&sin6->sin6_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } else { sin = (struct sockaddr_in *)&mask; if (!in_prefixlentomask(prefixlen, IP_ABITS, (uchar_t *)&sin->sin_addr)) { (void) fprintf(stderr, "ifconfig: " "Bad prefix length: %d\n", prefixlen); exit(1); } } g_netmask_set = G_NETMASK_NIL; break; } /* * This is a "hack" to get around the problem of SIOCLIFADDIF. The * problem is that this ioctl does not include the netmask when * adding a logical interface. This is the same problem described * in the ifconfig() comments. To get around this problem, we first * add the logical interface with a 0 address. After that, we set * the netmask if provided. Finally we set the interface address. */ (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); /* Note: no need to do DAD here since the interface isn't up yet. */ if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) Perror0_exit("addif: SIOCLIFADDIF"); (void) printf("Created new logical interface %s\n", lifr.lifr_name); (void) strncpy(name, lifr.lifr_name, sizeof (name)); /* * Check and see if any "netmask" command is used and perform the * necessary operation. */ set_mask_lifreq(&lifr, &laddr, &mask); /* This check is temporary until libipadm supports IPMP interfaces. */ if (ifconfig_use_libipadm(s, name)) { /* * We added the logical interface above before calling * ipadm_create_addr(), because, with IPH_LEGACY, we need * to do an addif for `ifconfig ce0 addif ' but not for * `ifconfig ce0 '. libipadm does not have a flag to * to differentiate between these two cases. To keep it simple, * we always create the logical interface and pass it to * libipadm instead of requiring libipadm to addif for some * cases and not do addif for other cases. */ istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name, &ipaddr); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "addif"); if (af == AF_INET) { sin = (struct sockaddr_in *)&laddr; (void) inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof (addrstr)); } else { sin6 = (struct sockaddr_in6 *)&laddr; (void) inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof (addrstr)); } /* * lifr.lifr_addr, which is updated by set_mask_lifreq() * will contain the right mask to use. */ prefixlen = mask2plen((struct sockaddr *)&lifr.lifr_addr); (void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d", addrstr, prefixlen); istatus = ipadm_set_addr(ipaddr, cidraddr, af); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "could not set address"); setaddr++; /* * address will be set by the parser after nextarg * has been scanned */ return (0); } /* * Only set the netmask if "netmask" command is used or a prefix is * provided. */ if (g_netmask_set == G_NETMASK_SET || prefixlen >= 0) { /* * lifr.lifr_addr already contains netmask from * set_mask_lifreq(). */ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("addif: SIOCSLIFNETMASK"); } /* Finally, we set the interface address. */ lifr.lifr_addr = laddr; if (ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFADDR"); /* * let parser know we got a source. * Next address, if given, should be dest */ setaddr++; return (0); } /* * Remove a logical interface based on its IP address. Unlike addif * there is no '/' here. * Verifies that the interface is down before it is removed. */ /* ARGSUSED */ static int removeif(char *str, int64_t param) { struct sockaddr_storage laddr; ipadm_status_t istatus; ipadm_addr_info_t *ainfo, *ainfop; if (strchr(name, ':') != NULL) { (void) fprintf(stderr, "ifconfig: removeif: bad physical interface name %s\n", name); exit(1); } (*afp->af_getaddr)(str, &laddr, NULL); /* * Following check is temporary until libipadm supports * IPMP interfaces. */ if (!ifconfig_use_libipadm(s, name)) goto delete; /* * Get all addresses and search this address among the active * addresses. If an address object was found, delete using * ipadm_delete_addr(). */ istatus = ipadm_addr_info(iph, name, &ainfo, 0, LIFC_DEFAULT); if (istatus != IPADM_SUCCESS) ipadmerr_exit(istatus, "removeif"); for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) if (sockaddrcmp( (struct sockaddr_storage *)ainfop->ia_ifa.ifa_addr, &laddr)) break; if (ainfop != NULL) { if (strchr(ainfop->ia_ifa.ifa_name, ':') == NULL) { (void) fprintf(stderr, "ifconfig: removeif: cannot remove interface: %s\n", name); exit(1); } if (ainfop->ia_aobjname[0] != '\0') { istatus = ipadm_delete_addr(iph, ainfop->ia_aobjname, IPADM_OPT_ACTIVE); if (istatus != IPADM_SUCCESS) { ipadmerr_exit(istatus, "could not delete address"); } ipadm_free_addr_info(ainfo); return (0); } } ipadm_free_addr_info(ainfo); delete: /* * An address object for this address was not found in ipadm. * Delete with SIOCLIFREMOVEIF. */ lifr.lifr_addr = laddr; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) { if (errno == EBUSY) { /* This can only happen if ipif_id = 0 */ (void) fprintf(stderr, "ifconfig: removeif: cannot remove interface: %s\n", name); exit(1); } Perror0_exit("removeif: SIOCLIFREMOVEIF"); } return (0); } /* * Set the address token for IPv6. */ /* ARGSUSED */ static int setiftoken(char *addr, int64_t param) { int prefixlen = 0; struct sockaddr_in6 token; in6_getaddr(addr, (struct sockaddr *)&token, &prefixlen); switch (prefixlen) { case NO_PREFIX: (void) fprintf(stderr, "ifconfig: Missing prefix length in subnet %s\n", addr); exit(1); /* NOTREACHED */ case BAD_ADDR: (void) fprintf(stderr, "ifconfig: Bad prefix length in %s\n", addr); exit(1); default: break; } (void) memcpy(&lifr.lifr_addr, &token, sizeof (token)); lifr.lifr_addrlen = prefixlen; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0) { Perror0_exit("setiftoken: SIOCSLIFTOKEN"); } return (0); } /* ARGSUSED */ static int setifgroupname(char *grname, int64_t param) { lifgroupinfo_t lifgr; struct lifreq lifrl; ifaddrlistx_t *ifaddrp, *nextifaddrp; ifaddrlistx_t *ifaddrs = NULL, *downaddrs = NULL; int af; if (debug) { (void) printf("Setting groupname %s on interface %s\n", grname, name); } (void) strlcpy(lifrl.lifr_name, name, LIFNAMSIZ); (void) strlcpy(lifrl.lifr_groupname, grname, LIFGRNAMSIZ); while (ioctl(s, SIOCSLIFGROUPNAME, &lifrl) == -1) { switch (errno) { case ENOENT: /* * The group doesn't yet exist; create it and repeat. */ af = afp->af_af; if (create_ipmp(grname, af, NULL, _B_TRUE) == -1) { if (errno == EEXIST) continue; Perror2(grname, "cannot create IPMP group"); goto fail; } continue; case EALREADY: /* * The interface is already in another group; must * remove existing membership first. */ lifrl.lifr_groupname[0] = '\0'; if (ioctl(s, SIOCSLIFGROUPNAME, &lifrl) == -1) { Perror2(name, "cannot remove existing " "IPMP group membership"); goto fail; } (void) strlcpy(lifrl.lifr_groupname, grname, LIFGRNAMSIZ); continue; case EAFNOSUPPORT: /* * The group exists, but it's not configured with the * address families the interface needs. Since only * two address families are currently supported, just * configure the "other" address family. Note that we * may race with group deletion or creation by another * process (ENOENT or EEXIST); in such cases we repeat * our original SIOCSLIFGROUPNAME. */ (void) strlcpy(lifgr.gi_grname, grname, LIFGRNAMSIZ); if (ioctl(s, SIOCGLIFGROUPINFO, &lifgr) == -1) { if (errno == ENOENT) continue; Perror2(grname, "SIOCGLIFGROUPINFO"); goto fail; } af = lifgr.gi_v4 ? AF_INET6 : AF_INET; if (create_ipmp(grname, af, lifgr.gi_grifname, _B_TRUE) == -1) { if (errno == EEXIST) continue; Perror2(grname, "cannot configure IPMP group"); goto fail; } continue; case EADDRINUSE: /* * Some addresses are in-use (or under control of DAD). * Bring them down and retry the group join operation. * We will bring them back up after the interface has * been placed in the group. */ if (ifaddrlistx(lifrl.lifr_name, IFF_UP|IFF_DUPLICATE, 0, &ifaddrs) == -1) { Perror2(grname, "cannot get address list"); goto fail; } ifaddrp = ifaddrs; for (; ifaddrp != NULL; ifaddrp = nextifaddrp) { if (!ifaddr_down(ifaddrp)) { ifaddrs = ifaddrp; goto fail; } nextifaddrp = ifaddrp->ia_next; ifaddrp->ia_next = downaddrs; downaddrs = ifaddrp; } ifaddrs = NULL; continue; case EADDRNOTAVAIL: { /* * Some data addresses are under application control. * For some of these (e.g., ADDRCONF), the application * should remove the address, in which case we retry a * few times (since the application's action is not * atomic with respect to us) before bailing out and * informing the user. */ int ntries, nappaddr = 0; const if_appflags_t *iap = if_appflags_tbl; for (; iap->ia_app != NULL; iap++) { ntries = 0; again: if (ifaddrlistx(lifrl.lifr_name, iap->ia_flag, IFF_NOFAILOVER, &ifaddrs) == -1) { (void) fprintf(stderr, "ifconfig: %s: " "cannot get data addresses managed " "by %s\n", lifrl.lifr_name, iap->ia_app); goto fail; } if (ifaddrs == NULL) continue; ifaddrlistx_free(ifaddrs); ifaddrs = NULL; if (++ntries < iap->ia_tries) { (void) poll(NULL, 0, 100); goto again; } (void) fprintf(stderr, "ifconfig: cannot join " "IPMP group: %s has data addresses managed " "by %s\n", lifrl.lifr_name, iap->ia_app); nappaddr++; } if (nappaddr > 0) goto fail; continue; } default: Perror2(name, "SIOCSLIFGROUPNAME"); goto fail; } } /* * If the interface being moved is under the control of `ipmgmtd(8)' * dameon then we should inform the daemon about this move, so that * the daemon can delete the state associated with this interface. * * This workaround is needed until the IPMP support in ipadm(8). */ ipadm_if_move(iph, name); /* * If there were addresses that we had to bring down, it's time to * bring them up again. As part of bringing them up, the kernel will * automatically move them to the new IPMP interface. */ for (ifaddrp = downaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { if (!ifaddr_up(ifaddrp) && errno != ENXIO) { (void) fprintf(stderr, "ifconfig: cannot bring back up " "%s: %s\n", ifaddrp->ia_name, strerror(errno)); } } ifaddrlistx_free(downaddrs); return (0); fail: /* * Attempt to bring back up any interfaces that we downed. */ for (ifaddrp = downaddrs; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { if (!ifaddr_up(ifaddrp) && errno != ENXIO) { (void) fprintf(stderr, "ifconfig: cannot bring back up " "%s: %s\n", ifaddrp->ia_name, strerror(errno)); } } ifaddrlistx_free(downaddrs); ifaddrlistx_free(ifaddrs); /* * We'd return -1, but foreachinterface() doesn't propagate the error * into the exit status, so we're forced to explicitly exit(). */ exit(1); /* NOTREACHED */ } static boolean_t modcheck(const char *ifname) { (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, &lifr) < 0) { Perror0("SIOCGLIFFLAGS"); return (_B_FALSE); } if (lifr.lifr_flags & IFF_IPMP) { (void) fprintf(stderr, "ifconfig: %s: module operations not" " supported on IPMP interfaces\n", ifname); return (_B_FALSE); } if (lifr.lifr_flags & IFF_VIRTUAL) { (void) fprintf(stderr, "ifconfig: %s: module operations not" " supported on virtual IP interfaces\n", ifname); return (_B_FALSE); } return (_B_TRUE); } /* * To list all the modules above a given network interface. */ /* ARGSUSED */ static int modlist(char *null, int64_t param) { int muxid_fd; int muxfd; int ipfd_lowstr; int arpfd_lowstr; int num_mods; int i; struct str_list strlist; int orig_arpid; /* * We'd return -1, but foreachinterface() doesn't propagate the error * into the exit status, so we're forced to explicitly exit(). */ if (!modcheck(name)) exit(1); if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr, &orig_arpid) < 0) { return (-1); } if ((num_mods = ioctl(ipfd_lowstr, I_LIST, NULL)) < 0) { Perror0("cannot I_LIST to get the number of modules"); } else { if (debug > 0) { (void) printf("Listing (%d) modules above %s\n", num_mods, name); } strlist.sl_nmods = num_mods; strlist.sl_modlist = malloc(sizeof (struct str_mlist) * num_mods); if (strlist.sl_modlist == NULL) { Perror0("cannot malloc"); } else { if (ioctl(ipfd_lowstr, I_LIST, (caddr_t)&strlist) < 0) { Perror0("cannot I_LIST for module names"); } else { for (i = 0; i < strlist.sl_nmods; i++) { (void) printf("%d %s\n", i, strlist.sl_modlist[i].l_name); } } free(strlist.sl_modlist); } } return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr, orig_arpid)); } #define MODINSERT_OP 'i' #define MODREMOVE_OP 'r' /* * To insert a module to the stream of the interface. It is just a * wrapper. The real function is modop(). */ /* ARGSUSED */ static int modinsert(char *arg, int64_t param) { return (modop(arg, MODINSERT_OP)); } /* * To remove a module from the stream of the interface. It is just a * wrapper. The real function is modop(). */ /* ARGSUSED */ static int modremove(char *arg, int64_t param) { return (modop(arg, MODREMOVE_OP)); } /* * Helper function for mod*() functions. It gets a fd to the lower IP * stream and I_PUNLINK's the lower stream. It also initializes the * global variable lifr. * * Param: * int *muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK * int *muxid_fd: fd to /dev/udp{,6} for LIFMUXID * int *ipfd_lowstr: fd to the lower IP stream. * int *arpfd_lowstr: fd to the lower ARP stream. * * Return: * -1 if operation fails, 0 otherwise. * * Please see the big block comment above ifplumb() for the logic of the * PLINK/PUNLINK */ static int ip_domux2fd(int *muxfd, int *muxid_fd, int *ipfd_lowstr, int *arpfd_lowstr, int *orig_arpid) { uint64_t flags; char *udp_dev_name; *orig_arpid = 0; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { Perror0_exit("status: SIOCGLIFFLAGS"); } flags = lifr.lifr_flags; if (flags & IFF_IPV4) { udp_dev_name = UDP_DEV_NAME; } else if (flags & IFF_IPV6) { udp_dev_name = UDP6_DEV_NAME; } else { return (-1); } if ((*muxid_fd = open(udp_dev_name, O_RDWR)) < 0) { Perror2("open", udp_dev_name); return (-1); } if (ioctl(*muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) { Perror2("SIOCGLIFMUXID", udp_dev_name); return (-1); } if (debug > 0) { (void) printf("ARP_muxid %d IP_muxid %d\n", lifr.lifr_arp_muxid, lifr.lifr_ip_muxid); } /* * Use /dev/udp{,6} as the mux to avoid linkcycles. */ if (ipadm_open_arp_on_udp(udp_dev_name, muxfd) != IPADM_SUCCESS) return (-1); if (lifr.lifr_arp_muxid != 0) { if ((*arpfd_lowstr = ioctl(*muxfd, _I_MUXID2FD, lifr.lifr_arp_muxid)) < 0) { if ((errno == EINVAL) && (flags & (IFF_NOARP | IFF_IPV6))) { /* * Some plumbing utilities set the muxid to * -1 or some invalid value to signify that * there is no arp stream. Set the muxid to 0 * before trying to unplumb the IP stream. * IP does not allow the IP stream to be * unplumbed if it sees a non-null arp muxid, * for consistency of IP-ARP streams. */ *orig_arpid = lifr.lifr_arp_muxid; lifr.lifr_arp_muxid = 0; (void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr); *arpfd_lowstr = -1; } else { Perror0("_I_MUXID2FD"); return (-1); } } else if (ioctl(*muxfd, I_PUNLINK, lifr.lifr_arp_muxid) < 0) { Perror2("I_PUNLINK", udp_dev_name); return (-1); } } else { *arpfd_lowstr = -1; } if ((*ipfd_lowstr = ioctl(*muxfd, _I_MUXID2FD, lifr.lifr_ip_muxid)) < 0) { Perror0("_I_MUXID2FD"); /* Undo any changes we made */ if (*orig_arpid != 0) { lifr.lifr_arp_muxid = *orig_arpid; (void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr); } return (-1); } if (ioctl(*muxfd, I_PUNLINK, lifr.lifr_ip_muxid) < 0) { Perror2("I_PUNLINK", udp_dev_name); /* Undo any changes we made */ if (*orig_arpid != 0) { lifr.lifr_arp_muxid = *orig_arpid; (void) ioctl(*muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr); } return (-1); } return (0); } /* * Helper function for mod*() functions. It I_PLINK's back the upper and * lower IP streams. Note that this function must be called after * ip_domux2fd(). In ip_domux2fd(), the global variable lifr is initialized * and ip_plink() needs information in lifr. So ip_domux2fd() and ip_plink() * must be called in pairs. * * Param: * int muxfd: fd to /dev/udp{,6} for I_PLINK/I_PUNLINK * int muxid_fd: fd to /dev/udp{,6} for LIFMUXID * int ipfd_lowstr: fd to the lower IP stream. * int arpfd_lowstr: fd to the lower ARP stream. * * Return: * -1 if operation fails, 0 otherwise. * * Please see the big block comment above ifplumb() for the logic of the * PLINK/PUNLINK */ static int ip_plink(int muxfd, int muxid_fd, int ipfd_lowstr, int arpfd_lowstr, int orig_arpid) { int ip_muxid; ip_muxid = ioctl(muxfd, I_PLINK, ipfd_lowstr); if (ip_muxid < 0) { Perror2("I_PLINK", UDP_DEV_NAME); return (-1); } /* * If there is an arp stream, plink it. If there is no * arp stream, then it is possible that the plumbing * utility could have stored any value in the arp_muxid. * If so, restore it from orig_arpid. */ if (arpfd_lowstr != -1) { if (ioctl(muxfd, I_PLINK, arpfd_lowstr) < 0) { Perror2("I_PLINK", UDP_DEV_NAME); return (-1); } } else if (orig_arpid != 0) { /* Undo the changes we did in ip_domux2fd */ lifr.lifr_arp_muxid = orig_arpid; lifr.lifr_ip_muxid = ip_muxid; (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr); } (void) close(muxfd); (void) close(muxid_fd); return (0); } /* * The real function to perform module insertion/removal. * * Param: * char *arg: the argument string module_name@position * char op: operation, either MODINSERT_OP or MODREMOVE_OP. * * Return: * Before doing ip_domux2fd(), this function calls exit(1) in case of * error. After ip_domux2fd() is done, it returns -1 for error, 0 * otherwise. */ static int modop(char *arg, char op) { char *pos_p; int muxfd; int muxid_fd; int ipfd_lowstr; /* IP stream (lower stream of mux) to be plinked */ int arpfd_lowstr; /* ARP stream (lower stream of mux) to be plinked */ struct strmodconf mod; char *at_char = "@"; char *arg_str; int orig_arpid; /* * We'd return -1, but foreachinterface() doesn't propagate the error * into the exit status, so we're forced to explicitly exit(). */ if (!modcheck(name)) exit(1); /* Need to save the original string for -a option. */ if ((arg_str = malloc(strlen(arg) + 1)) == NULL) { Perror0("cannot malloc"); return (-1); } (void) strcpy(arg_str, arg); if (*arg_str == *at_char) { (void) fprintf(stderr, "ifconfig: must supply a module name\n"); exit(1); } mod.mod_name = strtok(arg_str, at_char); if (strlen(mod.mod_name) > FMNAMESZ) { (void) fprintf(stderr, "ifconfig: module name too long: %s\n", mod.mod_name); exit(1); } /* * Need to make sure that the core TCP/IP stack modules are not * removed. Otherwise, "bad" things can happen. If a module * is removed and inserted back, it loses its old state. But * the modules above it still have the old state. E.g. IP assumes * fast data path while tunnel after re-inserted assumes that it can * receive M_DATA only in fast data path for which it does not have * any state. This is a general caveat of _I_REMOVE/_I_INSERT. */ if (op == MODREMOVE_OP && (strcmp(mod.mod_name, ARP_MOD_NAME) == 0 || strcmp(mod.mod_name, IP_MOD_NAME) == 0)) { (void) fprintf(stderr, "ifconfig: cannot remove %s\n", mod.mod_name); exit(1); } if ((pos_p = strtok(NULL, at_char)) == NULL) { (void) fprintf(stderr, "ifconfig: must supply a position\n"); exit(1); } mod.pos = atoi(pos_p); if (ip_domux2fd(&muxfd, &muxid_fd, &ipfd_lowstr, &arpfd_lowstr, &orig_arpid) < 0) { free(arg_str); return (-1); } switch (op) { case MODINSERT_OP: if (debug > 0) { (void) printf("Inserting module %s at %d\n", mod.mod_name, mod.pos); } if (ioctl(ipfd_lowstr, _I_INSERT, (caddr_t)&mod) < 0) { Perror2("fail to insert module", mod.mod_name); } break; case MODREMOVE_OP: if (debug > 0) { (void) printf("Removing module %s at %d\n", mod.mod_name, mod.pos); } if (ioctl(ipfd_lowstr, _I_REMOVE, (caddr_t)&mod) < 0) { Perror2("fail to remove module", mod.mod_name); } break; default: /* Should never get to here. */ (void) fprintf(stderr, "Unknown operation\n"); break; } free(arg_str); return (ip_plink(muxfd, muxid_fd, ipfd_lowstr, arpfd_lowstr, orig_arpid)); } static int modify_tun(iptun_params_t *params) { dladm_status_t status; if ((status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, ¶ms->iptun_param_linkid)) == DLADM_STATUS_OK) status = dladm_iptun_modify(dlh, params, DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) dladmerr_exit(status, name); return (0); } /* * Set tunnel source address */ /* ARGSUSED */ static int setiftsrc(char *addr, int64_t param) { iptun_params_t params; params.iptun_param_flags = IPTUN_PARAM_LADDR; (void) strlcpy(params.iptun_param_laddr, addr, sizeof (params.iptun_param_laddr)); return (modify_tun(¶ms)); } /* * Set tunnel destination address */ /* ARGSUSED */ static int setiftdst(char *addr, int64_t param) { iptun_params_t params; params.iptun_param_flags = IPTUN_PARAM_RADDR; (void) strlcpy(params.iptun_param_raddr, addr, sizeof (params.iptun_param_raddr)); return (modify_tun(¶ms)); } static int set_tun_prop(const char *propname, char *value) { dladm_status_t status; datalink_id_t linkid; status = ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, &linkid); if (status == DLADM_STATUS_OK) { status = dladm_set_linkprop(dlh, linkid, propname, &value, 1, DLADM_OPT_ACTIVE); } if (status != DLADM_STATUS_OK) dladmerr_exit(status, name); return (0); } /* Set tunnel encapsulation limit. */ /* ARGSUSED */ static int set_tun_encap_limit(char *arg, int64_t param) { return (set_tun_prop("encaplimit", arg)); } /* Disable encapsulation limit. */ /* ARGSUSED */ static int clr_tun_encap_limit(char *arg, int64_t param) { return (set_tun_encap_limit("-1", 0)); } /* Set tunnel hop limit. */ /* ARGSUSED */ static int set_tun_hop_limit(char *arg, int64_t param) { return (set_tun_prop("hoplimit", arg)); } /* Set zone ID */ static int setzone(char *arg, int64_t param) { zoneid_t zoneid = GLOBAL_ZONEID; if (param == NEXTARG) { /* zone must be active */ if ((zoneid = getzoneidbyname(arg)) == -1) { (void) fprintf(stderr, "ifconfig: unknown zone '%s'\n", arg); exit(1); } } (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_zoneid = zoneid; if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1) Perror0_exit("SIOCSLIFZONE"); return (0); } /* Put interface into all zones */ /* ARGSUSED */ static int setallzones(char *arg, int64_t param) { (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); lifr.lifr_zoneid = ALL_ZONES; if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) == -1) Perror0_exit("SIOCSLIFZONE"); return (0); } /* Set source address to use */ /* ARGSUSED */ static int setifsrc(char *arg, int64_t param) { uint_t ifindex = 0; int rval; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); /* * Argument can be either an interface name or "none". The latter means * that any previous selection is cleared. */ if (strchr(arg, ':') != NULL) { (void) fprintf(stderr, "ifconfig: Cannot specify logical interface for usesrc \n"); exit(1); } rval = strcmp(arg, NONE_STR); if (rval != 0) { if ((ifindex = if_nametoindex(arg)) == 0) { (void) strncpy(lifr.lifr_name, arg, LIFNAMSIZ); Perror0_exit("Could not get interface index"); } lifr.lifr_index = ifindex; } else { if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) != 0) Perror0_exit("Not a valid usesrc consumer"); lifr.lifr_index = 0; } if (debug) (void) printf("setifsrc: lifr_name %s, lifr_index %d\n", lifr.lifr_name, lifr.lifr_index); if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) == -1) { if (rval == 0) Perror0_exit("Cannot reset usesrc group"); else Perror0_exit("Could not set source interface"); } return (0); } /* * Print the interface status line associated with `ifname' */ static void ifstatus(const char *ifname) { uint64_t flags; char if_usesrc_name[LIFNAMSIZ]; char *newbuf; int n, numifs, rval = 0; struct lifreq *lifrp; struct lifsrcof lifs; (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { Perror0_exit("status: SIOCGLIFFLAGS"); } flags = lifr.lifr_flags; /* * In V4 compatibility mode, we don't print the IFF_IPV4 flag or * interfaces with IFF_IPV6 set. */ if (v4compat) { flags &= ~IFF_IPV4; if (flags & IFF_IPV6) return; } (void) printf("%s: ", ifname); print_flags(flags); (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) { Perror0_exit("status: SIOCGLIFMETRIC"); } else { if (lifr.lifr_metric) (void) printf(" metric %d", lifr.lifr_metric); } if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0) (void) printf(" mtu %u", lifr.lifr_mtu); /* don't print index or zone when in compatibility mode */ if (!v4compat) { if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) (void) printf(" index %d", lifr.lifr_index); /* * Stack instances use GLOBAL_ZONEID for IP data structures * even in the non-global zone. */ if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) >= 0 && lifr.lifr_zoneid != getzoneid() && lifr.lifr_zoneid != GLOBAL_ZONEID) { char zone_name[ZONENAME_MAX]; if (lifr.lifr_zoneid == ALL_ZONES) { (void) printf("\n\tall-zones"); } else if (getzonenamebyid(lifr.lifr_zoneid, zone_name, sizeof (zone_name)) < 0) { (void) printf("\n\tzone %d", lifr.lifr_zoneid); } else { (void) printf("\n\tzone %s", zone_name); } } } if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) { lifs.lifs_ifindex = lifr.lifr_index; /* * Find the number of interfaces that use this interfaces' * address as a source address */ lifs.lifs_buf = NULL; lifs.lifs_maxlen = 0; for (;;) { /* The first pass will give the bufsize we need */ rval = ioctl(s, SIOCGLIFSRCOF, (char *)&lifs); if (rval < 0) { if (lifs.lifs_buf != NULL) { free(lifs.lifs_buf); lifs.lifs_buf = NULL; } lifs.lifs_len = 0; break; } if (lifs.lifs_len <= lifs.lifs_maxlen) break; /* Use kernel's size + a small margin to avoid loops */ lifs.lifs_maxlen = lifs.lifs_len + 5 * sizeof (struct lifreq); /* For the first pass, realloc acts like malloc */ newbuf = realloc(lifs.lifs_buf, lifs.lifs_maxlen); if (newbuf == NULL) { if (lifs.lifs_buf != NULL) { free(lifs.lifs_buf); lifs.lifs_buf = NULL; } lifs.lifs_len = 0; break; } lifs.lifs_buf = newbuf; } numifs = lifs.lifs_len / sizeof (struct lifreq); if (numifs > 0) { lifrp = lifs.lifs_req; (void) printf("\n\tsrcof"); for (n = numifs; n > 0; n--, lifrp++) { (void) printf(" %s", lifrp->lifr_name); } } if (lifs.lifs_buf != NULL) free(lifs.lifs_buf); } /* Find the interface whose source address this interface uses */ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) { if (lifr.lifr_index != 0) { if (if_indextoname(lifr.lifr_index, if_usesrc_name) == NULL) { (void) printf("\n\tusesrc ifIndex %d", lifr.lifr_index); } else { (void) printf("\n\tusesrc %s", if_usesrc_name); } } } (void) putchar('\n'); } /* * Print the status of the interface. If an address family was * specified, show it and it only; otherwise, show them all. */ static void status(void) { struct afswtch *p = afp; uint64_t flags; datalink_id_t linkid; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { Perror0_exit("status: SIOCGLIFFLAGS"); } flags = lifr.lifr_flags; /* * Only print the interface status if the address family matches * the interface family flag. */ if (p != NULL) { if (((p->af_af == AF_INET6) && (flags & IFF_IPV4)) || ((p->af_af == AF_INET) && (flags & IFF_IPV6))) return; } /* * In V4 compatibility mode, don't print IFF_IPV6 interfaces. */ if (v4compat && (flags & IFF_IPV6)) return; ifstatus(name); if (ifconfig_dladm_open(name, DATALINK_CLASS_IPTUN, &linkid) == DLADM_STATUS_OK) tun_status(linkid); if (p != NULL) { (*p->af_status)(1, flags); } else { for (p = afs; p->af_name; p++) { /* set global af for use in p->af_status */ af = p->af_af; (*p->af_status)(0, flags); } /* * Historically, 'ether' has been an address family, * so print it here. */ print_ifether(name); } } /* * Print the status of the interface in a format that can be used to * reconfigure the interface later. Code stolen from status() above. */ /* ARGSUSED */ static int configinfo(char *null, int64_t param) { char *cp; struct afswtch *p = afp; uint64_t flags; char lifname[LIFNAMSIZ]; char if_usesrc_name[LIFNAMSIZ]; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { Perror0_exit("status: SIOCGLIFFLAGS"); } flags = lifr.lifr_flags; if (debug) { (void) printf("configinfo: name %s flags 0x%llx af_af %d\n", name, flags, p != NULL ? p->af_af : -1); } /* * Build the interface name to print (we cannot directly use `name' * because one cannot "plumb" ":0" interfaces). */ (void) strlcpy(lifname, name, LIFNAMSIZ); if ((cp = strchr(lifname, ':')) != NULL && atoi(cp + 1) == 0) *cp = '\0'; /* * if the interface is IPv4 * if we have a IPv6 address family restriction return * so it won't print * if we are in IPv4 compatibility mode, clear out IFF_IPV4 * so we don't print it. */ if (flags & IFF_IPV4) { if (p && p->af_af == AF_INET6) return (-1); if (v4compat) flags &= ~IFF_IPV4; (void) printf("%s inet plumb", lifname); } else if (flags & IFF_IPV6) { /* * else if the interface is IPv6 * if we have a IPv4 address family restriction return * or we are in IPv4 compatibiltiy mode, return. */ if (p && p->af_af == AF_INET) return (-1); if (v4compat) return (-1); (void) printf("%s inet6 plumb", lifname); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) { Perror0_exit("configinfo: SIOCGLIFMETRIC"); } else { if (lifr.lifr_metric) (void) printf(" metric %d ", lifr.lifr_metric); } if (((flags & (IFF_VIRTUAL|IFF_LOOPBACK)) != IFF_VIRTUAL) && ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) >= 0) (void) printf(" mtu %u", lifr.lifr_mtu); /* Index only applies to the zeroth interface */ if (lifnum(name) == 0) { if (ioctl(s, SIOCGLIFINDEX, (caddr_t)&lifr) >= 0) (void) printf(" index %d", lifr.lifr_index); } if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) == 0) { if (lifr.lifr_index != 0) { if (if_indextoname(lifr.lifr_index, if_usesrc_name) != NULL) { (void) printf(" usesrc %s", if_usesrc_name); } } } if (p != NULL) { (*p->af_configinfo)(1, flags); } else { for (p = afs; p->af_name; p++) { (void) close(s); s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0); /* set global af for use in p->af_configinfo */ af = p->af_af; if (s == -1) { Perror0_exit("socket"); } (*p->af_configinfo)(0, flags); } } (void) putchar('\n'); return (0); } static void print_tsec(iptun_params_t *params) { ipsec_req_t *ipsr; (void) printf("\ttunnel security settings "); if (!(params->iptun_param_flags & IPTUN_PARAM_SECINFO)) { (void) printf("--> use 'ipsecconf -ln -i %s'", name); } else { ipsr = ¶ms->iptun_param_secinfo; if (ipsr->ipsr_ah_req & IPSEC_PREF_REQUIRED) { (void) printf("ah (%s) ", rparsealg(ipsr->ipsr_auth_alg, IPSEC_PROTO_AH)); } if (ipsr->ipsr_esp_req & IPSEC_PREF_REQUIRED) { (void) printf("esp (%s", rparsealg(ipsr->ipsr_esp_alg, IPSEC_PROTO_ESP)); (void) printf("/%s)", rparsealg(ipsr->ipsr_esp_auth_alg, IPSEC_PROTO_AH)); } } (void) printf("\n"); } static void tun_status(datalink_id_t linkid) { iptun_params_t params; char propval[DLADM_PROP_VAL_MAX]; char *valptr[1]; uint_t valcnt = 1; boolean_t tabbed = _B_FALSE; params.iptun_param_linkid = linkid; /* If dladm_iptun_getparams() fails, assume we are not a tunnel. */ assert(dlh_opened); if (dladm_iptun_getparams(dlh, ¶ms, DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) return; switch (params.iptun_param_type) { case IPTUN_TYPE_IPV4: case IPTUN_TYPE_6TO4: (void) printf("\tinet"); break; case IPTUN_TYPE_IPV6: (void) printf("\tinet6"); break; default: dladmerr_exit(DLADM_STATUS_IPTUNTYPE, name); break; } /* * There is always a source address. If it hasn't been explicitly * set, the API will pass back a buffer containing the unspecified * address. */ (void) printf(" tunnel src %s ", params.iptun_param_laddr); if (params.iptun_param_flags & IPTUN_PARAM_RADDR) (void) printf("tunnel dst %s\n", params.iptun_param_raddr); else (void) putchar('\n'); if (params.iptun_param_flags & IPTUN_PARAM_IPSECPOL) print_tsec(¶ms); valptr[0] = propval; if (dladm_get_linkprop(dlh, linkid, DLADM_PROP_VAL_CURRENT, "hoplimit", (char **)valptr, &valcnt) == DLADM_STATUS_OK) { (void) printf("\ttunnel hop limit %s ", propval); tabbed = _B_TRUE; } if (dladm_get_linkprop(dlh, linkid, DLADM_PROP_VAL_CURRENT, "encaplimit", (char **)valptr, &valcnt) == DLADM_STATUS_OK) { uint32_t elim; if (!tabbed) { (void) putchar('\t'); tabbed = _B_TRUE; } elim = strtol(propval, NULL, 10); if (elim > 0) (void) printf("tunnel encapsulation limit %s", propval); else (void) printf("tunnel encapsulation limit disabled"); } if (tabbed) (void) putchar('\n'); } static void in_status(int force, uint64_t flags) { struct sockaddr_in *sin, *laddr; struct sockaddr_in netmask = { AF_INET }; if (debug) (void) printf("in_status(%s) flags 0x%llx\n", name, flags); /* only print status for IPv4 interfaces */ if (!(flags & IFF_IPV4)) return; if (!(flags & IFF_NOLOCAL)) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in_status: SIOCGLIFADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_addr; (void) printf("\tinet %s ", inet_ntoa(sin->sin_addr)); laddr = sin; } else { (void) printf("\tinet "); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else { Perror0_exit("in_status: SIOCGLIFSUBNET"); } } sin = (struct sockaddr_in *)&lifr.lifr_addr; if ((flags & IFF_NOLOCAL) || sin->sin_addr.s_addr != laddr->sin_addr.s_addr) { (void) printf("subnet %s/%d ", inet_ntoa(sin->sin_addr), lifr.lifr_addrlen); } if (sin->sin_family != AF_INET) { (void) printf("Wrong family: %d\n", sin->sin_family); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) Perror0_exit("in_status: SIOCGLIFNETMASK"); (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else netmask.sin_addr = ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr; if (flags & IFF_POINTOPOINT) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_status: SIOCGLIFDSTADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_dstaddr; (void) printf("--> %s ", inet_ntoa(sin->sin_addr)); } (void) printf("netmask %x ", ntohl(netmask.sin_addr.s_addr)); if (flags & IFF_BROADCAST) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_status: SIOCGLIFBRDADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_addr; if (sin->sin_addr.s_addr != 0) { (void) printf("broadcast %s", inet_ntoa(sin->sin_addr)); } } /* If there is a groupname, print it for only the physical interface */ if (strchr(name, ':') == NULL) { if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 && lifr.lifr_groupname[0] != '\0') { (void) printf("\n\tgroupname %s", lifr.lifr_groupname); } } (void) putchar('\n'); } static void in6_status(int force, uint64_t flags) { char abuf[INET6_ADDRSTRLEN]; struct sockaddr_in6 *sin6, *laddr6; if (debug) (void) printf("in6_status(%s) flags 0x%llx\n", name, flags); if (!(flags & IFF_IPV6)) return; if (!(flags & IFF_NOLOCAL)) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in_status6: SIOCGLIFADDR"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; (void) printf("\tinet6 %s/%d ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); laddr6 = sin6; } else { (void) printf("\tinet6 "); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in_status6: SIOCGLIFSUBNET"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; if ((flags & IFF_NOLOCAL) || !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) { (void) printf("subnet %s/%d ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); } if (sin6->sin6_family != AF_INET6) { (void) printf("Wrong family: %d\n", sin6->sin6_family); } if (flags & IFF_POINTOPOINT) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_status6: SIOCGLIFDSTADDR"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr; (void) printf("--> %s ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf))); } if (verbose) { (void) putchar('\n'); (void) putchar('\t'); (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EINVAL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_status6: SIOCGLIFTOKEN"); } else { sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; (void) printf("token %s/%d ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); } if (ioctl(s, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) { if (errno != EINVAL) { Perror0_exit("in_status6: SIOCGLIFLNKINFO"); } } else { (void) printf("maxhops %u, reachtime %u ms, " "reachretrans %u ms, maxmtu %u ", lifr.lifr_ifinfo.lir_maxhops, lifr.lifr_ifinfo.lir_reachtime, lifr.lifr_ifinfo.lir_reachretrans, lifr.lifr_ifinfo.lir_maxmtu); } } /* If there is a groupname, print it for only the physical interface */ if (strchr(name, ':') == NULL) { if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 && lifr.lifr_groupname[0] != '\0') { (void) printf("\n\tgroupname %s", lifr.lifr_groupname); } } (void) putchar('\n'); } static void in_configinfo(int force, uint64_t flags) { struct sockaddr_in *sin, *laddr; struct sockaddr_in netmask = { AF_INET }; if (debug) (void) printf("in_configinfo(%s) flags 0x%llx\n", name, flags); /* only configinfo info for IPv4 interfaces */ if (!(flags & IFF_IPV4)) return; if (!(flags & IFF_NOLOCAL)) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in_configinfo: SIOCGLIFADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_addr; (void) printf(" set %s ", inet_ntoa(sin->sin_addr)); laddr = sin; } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else { Perror0_exit("in_configinfo: SIOCGLIFSUBNET"); } } sin = (struct sockaddr_in *)&lifr.lifr_addr; if ((flags & IFF_NOLOCAL) || sin->sin_addr.s_addr != laddr->sin_addr.s_addr) { (void) printf(" subnet %s/%d ", inet_ntoa(sin->sin_addr), lifr.lifr_addrlen); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) Perror0_exit("in_configinfo: SIOCGLIFNETMASK"); (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else netmask.sin_addr = ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr; if (flags & IFF_POINTOPOINT) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_configinfo: SIOCGLIFDSTADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_dstaddr; (void) printf(" destination %s ", inet_ntoa(sin->sin_addr)); } (void) printf(" netmask 0x%x ", ntohl(netmask.sin_addr.s_addr)); if (flags & IFF_BROADCAST) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in_configinfo: SIOCGLIFBRDADDR"); } sin = (struct sockaddr_in *)&lifr.lifr_addr; if (sin->sin_addr.s_addr != 0) { (void) printf(" broadcast %s ", inet_ntoa(sin->sin_addr)); } } /* If there is a groupname, print it for only the zeroth interface */ if (lifnum(name) == 0) { if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 && lifr.lifr_groupname[0] != '\0') { (void) printf(" group %s ", lifr.lifr_groupname); } } /* Print flags to configure */ print_config_flags(AF_INET, flags); } static void in6_configinfo(int force, uint64_t flags) { char abuf[INET6_ADDRSTRLEN]; struct sockaddr_in6 *sin6, *laddr6; if (debug) (void) printf("in6_configinfo(%s) flags 0x%llx\n", name, flags); if (!(flags & IFF_IPV6)) return; if (!(flags & IFF_NOLOCAL)) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in6_configinfo: SIOCGLIFADDR"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; (void) printf(" set %s/%d ", inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); laddr6 = sin6; } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || errno == ENXIO) { if (!force) return; (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); } else Perror0_exit("in6_configinfo: SIOCGLIFSUBNET"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; if ((flags & IFF_NOLOCAL) || !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &laddr6->sin6_addr)) { (void) printf(" subnet %s/%d ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); } if (flags & IFF_POINTOPOINT) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in6_configinfo: SIOCGLIFDSTADDR"); } sin6 = (struct sockaddr_in6 *)&lifr.lifr_dstaddr; (void) printf(" destination %s ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf))); } (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) { if (errno == EADDRNOTAVAIL || errno == EINVAL) (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); else Perror0_exit("in6_configinfo: SIOCGLIFTOKEN"); } else { sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; (void) printf(" token %s/%d ", inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, abuf, sizeof (abuf)), lifr.lifr_addrlen); } /* If there is a groupname, print it for only the zeroth interface */ if (lifnum(name) == 0) { if (ioctl(s, SIOCGLIFGROUPNAME, &lifr) >= 0 && lifr.lifr_groupname[0] != '\0') { (void) printf(" group %s ", lifr.lifr_groupname); } } /* Print flags to configure */ print_config_flags(AF_INET6, flags); } /* * If this is a physical interface then remove it. * If it is a logical interface name use SIOCLIFREMOVEIF to * remove it. In both cases fail if it doesn't exist. */ /* ARGSUSED */ static int inetunplumb(char *arg, int64_t param) { ipadm_status_t istatus; istatus = ipadm_delete_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE); if (istatus != IPADM_SUCCESS) { (void) fprintf(stderr, "ifconfig: cannot unplumb %s: %s\n", name, ipadm_status2str(istatus)); exit(1); } return (0); } /* * Create the interface in `name', using ipadm_create_if(). If `name' is a * logical interface or loopback interface, ipadm_create_if() uses * SIOCLIFADDIF to create it. */ /* ARGSUSED */ static int inetplumb(char *arg, int64_t param) { ipadm_status_t istatus; istatus = ipadm_create_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE); if (istatus != IPADM_SUCCESS) { (void) fprintf(stderr, "ifconfig: cannot plumb %s: %s\n", name, ipadm_status2str(istatus)); if (istatus != IPADM_IF_EXISTS) exit(1); } return (0); } /* ARGSUSED */ static int inetipmp(char *arg, int64_t param) { int retval; /* * Treat e.g. "ifconfig ipmp0:2 ipmp" as "ifconfig ipmp0:2 plumb". * Otherwise, try to create the requested IPMP interface. */ if (strchr(name, ':') != NULL) retval = inetplumb(arg, param); else retval = create_ipmp(name, afp->af_af, name, _B_FALSE); /* * We'd return -1, but foreachinterface() doesn't propagate the error * into the exit status, so we're forced to explicitly exit(). */ if (retval == -1) exit(1); return (0); } /* * Create an IPMP group `grname' with address family `af'. If `ifname' is * non-NULL, it specifies the interface name to use. Otherwise, use the name * ipmpN, where N corresponds to the lowest available integer. If `implicit' * is set, then the group is being created as a side-effect of placing an * underlying interface in a group. Also start in.mpathd if necessary. */ static int create_ipmp(const char *grname, int af, const char *ifname, boolean_t implicit) { static int ipmp_daemon_started; uint32_t flags = IPADM_OPT_IPMP|IPADM_OPT_ACTIVE; ipadm_status_t istatus; if (debug) { (void) printf("create_ipmp: ifname %s grname %s af %d\n", ifname != NULL ? ifname : "NULL", grname, af); } /* * ipadm_create_if() creates the IPMP interface and fills in the * ppa in lifr.lifr_name, if `ifname'="ipmp". */ (void) strlcpy(lifr.lifr_name, (ifname ? ifname : "ipmp"), sizeof (lifr.lifr_name)); if (ifname == NULL) flags |= IPADM_OPT_GENPPA; istatus = ipadm_create_if(iph, lifr.lifr_name, af, flags); if (istatus != IPADM_SUCCESS) { (void) fprintf(stderr, "ifconfig: cannot create IPMP interface " "%s: %s\n", grname, ipadm_status2str(istatus)); return (-1); } /* * To preserve backward-compatibility, always bring up the link-local * address for implicitly-created IPv6 IPMP interfaces. */ if (implicit && af == AF_INET6) { if (ioctl(s6, SIOCGLIFFLAGS, &lifr) == 0) { lifr.lifr_flags |= IFF_UP; (void) ioctl(s6, SIOCSLIFFLAGS, &lifr); } } /* * If the caller requested a different group name, issue a * SIOCSLIFGROUPNAME on the new IPMP interface. */ if (strcmp(lifr.lifr_name, grname) != 0) { (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ); if (ioctl(s, SIOCSLIFGROUPNAME, &lifr) == -1) { Perror0("SIOCSLIFGROUPNAME"); return (-1); } } /* * If we haven't done so yet, ensure in.mpathd is started. */ if (ipmp_daemon_started++ == 0) start_ipmp_daemon(); return (0); } /* * Start in.mpathd if it's not already running. */ static void start_ipmp_daemon(void) { int retval; ipmp_handle_t ipmp_handle; /* * Ping in.mpathd to see if it's running already. */ if ((retval = ipmp_open(&ipmp_handle)) != IPMP_SUCCESS) { (void) fprintf(stderr, "ifconfig: cannot create IPMP handle: " "%s\n", ipmp_errmsg(retval)); return; } retval = ipmp_ping_daemon(ipmp_handle); ipmp_close(ipmp_handle); switch (retval) { case IPMP_ENOMPATHD: break; case IPMP_SUCCESS: return; default: (void) fprintf(stderr, "ifconfig: cannot ping in.mpathd: %s\n", ipmp_errmsg(retval)); break; } /* * Start in.mpathd. Note that in.mpathd will handle multiple * incarnations (ipmp_ping_daemon() is just an optimization) so we * don't need to worry about racing with another ifconfig process. */ switch (fork()) { case -1: Perror0_exit("start_ipmp_daemon: fork"); /* NOTREACHED */ case 0: (void) execl(MPATHD_PATH, MPATHD_PATH, NULL); _exit(1); /* NOTREACHED */ default: break; } } /* * Bring the address named by `ifaddrp' up or down. Doesn't trust any mutable * values in ia_flags since they may be stale. */ static boolean_t ifaddr_op(ifaddrlistx_t *ifaddrp, boolean_t up) { struct lifreq lifrl; /* Local lifreq struct */ int fd = (ifaddrp->ia_flags & IFF_IPV4) ? s4 : s6; (void) memset(&lifrl, 0, sizeof (lifrl)); (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name, LIFNAMSIZ); if (ioctl(fd, SIOCGLIFFLAGS, &lifrl) == -1) return (_B_FALSE); if (up) lifrl.lifr_flags |= IFF_UP; else lifrl.lifr_flags &= ~IFF_UP; if (ioctl(fd, SIOCSLIFFLAGS, &lifrl) == -1) return (_B_FALSE); /* * If we're trying to bring the address down, ensure that DAD activity * (observable by IFF_DUPLICATE) has also been stopped. */ if (!up && ioctl(fd, SIOCGLIFFLAGS, &lifrl) != -1 && lifrl.lifr_flags & IFF_DUPLICATE) { if (ioctl(fd, SIOCGLIFADDR, &lifrl) == -1 || ioctl(fd, SIOCSLIFADDR, &lifrl) == -1) { return (_B_FALSE); } } return (_B_TRUE); } static boolean_t ifaddr_up(ifaddrlistx_t *ifaddrp) { return (ifaddr_op(ifaddrp, _B_TRUE)); } static boolean_t ifaddr_down(ifaddrlistx_t *ifaddrp) { return (ifaddr_op(ifaddrp, _B_FALSE)); } /* * Open the global libdladm handle "dlh" if it isn't already opened. The * caller may optionally supply a link name to obtain its linkid. If a link * of a specific class or classes is required, reqclass specifies the class * mask. */ static dladm_status_t ifconfig_dladm_open(const char *name, datalink_class_t reqclass, datalink_id_t *linkid) { dladm_status_t status = DLADM_STATUS_OK; datalink_class_t class; if (!dlh_opened) { if ((status = dladm_open(&dlh)) != DLADM_STATUS_OK) return (status); dlh_opened = _B_TRUE; } if (name != NULL) { status = dladm_name2info(dlh, name, linkid, NULL, &class, NULL); if (status == DLADM_STATUS_OK) { if (!(class & reqclass)) status = DLADM_STATUS_LINKINVAL; } } return (status); } /* * This function checks if we can use libipadm API's. We will only * call libipadm functions for non-IPMP interfaces. This check is * temporary until libipadm supports IPMP interfaces. */ static boolean_t ifconfig_use_libipadm(int s, const char *lifname) { struct lifreq lifr1; (void) strlcpy(lifr1.lifr_name, lifname, sizeof (lifr1.lifr_name)); if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr1) < 0) { (void) strncpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name)); Perror0_exit("error"); } return (lifr1.lifr_groupname[0] == '\0'); } static void ipadmerr_exit(ipadm_status_t status, const char *str) { (void) fprintf(stderr, "ifconfig: %s: %s\n", str, ipadm_status2str(status)); exit(1); } static void dladmerr_exit(dladm_status_t status, const char *str) { char errstr[DLADM_STRSIZE]; (void) fprintf(stderr, "%s: %s\n", str, dladm_status2str(status, errstr)); exit(1); } void Perror0(const char *cmd) { Perror2(cmd, lifr.lifr_name); } void Perror0_exit(const char *cmd) { Perror0(cmd); exit(1); } void Perror2(const char *cmd, const char *str) { int error = errno; (void) fprintf(stderr, "ifconfig: %s: ", cmd); switch (error) { case ENXIO: (void) fprintf(stderr, "%s: no such interface\n", str); break; case EPERM: (void) fprintf(stderr, "%s: permission denied\n", str); break; case EEXIST: (void) fprintf(stderr, "%s: already exists\n", str); break; case ENAMETOOLONG: (void) fprintf(stderr, "%s: interface name too long\n", str); break; case ERANGE: (void) fprintf(stderr, "%s: logical interface id is outside " "allowed range\n", str); break; default: errno = error; perror(str); } } /* * Print out error message (Perror2()) and exit */ void Perror2_exit(const char *cmd, const char *str) { Perror2(cmd, str); exit(1); /* NOTREACHED */ } void Perrdlpi(const char *cmd, const char *linkname, int err) { (void) fprintf(stderr, "ifconfig: %s \"%s\": %s\n", cmd, linkname, dlpi_strerror(err)); } /* * Print out error message (Perrdlpi()) and exit */ void Perrdlpi_exit(const char *cmd, const char *linkname, int err) { Perrdlpi(cmd, linkname, err); exit(1); } /* * If the last argument is non-NULL allow a / syntax and * pass out in *plenp. * If doesn't parse return BAD_ADDR as *plenp. * If no / is present return NO_PREFIX as *plenp. */ static void in_getaddr(char *s, struct sockaddr *saddr, int *plenp) { /* LINTED: alignment */ struct sockaddr_in *sin = (struct sockaddr_in *)saddr; struct hostent *hp; struct netent *np; char str[BUFSIZ]; int error_num; (void) strncpy(str, s, sizeof (str)); /* * Look for '/' is plenp */ if (plenp != NULL) { char *cp; *plenp = in_getprefixlen(str, _B_TRUE, IP_ABITS); if (*plenp == BAD_ADDR) return; cp = strchr(str, '/'); if (cp != NULL) *cp = '\0'; } else if (strchr(str, '/') != NULL) { (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str); exit(1); } (void) memset(sin, 0, sizeof (*sin)); /* * Try to catch attempts to set the broadcast address to all 1's. */ if (strcmp(str, "255.255.255.255") == 0 || (strtoul(str, (char **)NULL, 0) == 0xffffffffUL)) { sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0xffffffff; return; } hp = getipnodebyname(str, AF_INET, 0, &error_num); if (hp) { sin->sin_family = hp->h_addrtype; (void) memcpy(&sin->sin_addr, hp->h_addr, hp->h_length); freehostent(hp); return; } np = getnetbyname(str); if (np) { sin->sin_family = np->n_addrtype; sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY); return; } if (error_num == TRY_AGAIN) { (void) fprintf(stderr, "ifconfig: %s: bad address " "(try again later)\n", s); } else { (void) fprintf(stderr, "ifconfig: %s: bad address\n", s); } exit(1); } /* * If the last argument is non-NULL allow a / syntax and * pass out in *plenp. * If doesn't parse return BAD_ADDR as *plenp. * If no / is present return NO_PREFIX as *plenp. */ static void in6_getaddr(char *s, struct sockaddr *saddr, int *plenp) { /* LINTED: alignment */ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; struct hostent *hp; char str[BUFSIZ]; int error_num; (void) strncpy(str, s, sizeof (str)); /* * Look for '/' is plenp */ if (plenp != NULL) { char *cp; *plenp = in_getprefixlen(str, _B_TRUE, IPV6_ABITS); if (*plenp == BAD_ADDR) return; cp = strchr(str, '/'); if (cp != NULL) *cp = '\0'; } else if (strchr(str, '/') != NULL) { (void) fprintf(stderr, "ifconfig: %s: unexpected '/'\n", str); exit(1); } (void) memset(sin6, 0, sizeof (*sin6)); hp = getipnodebyname(str, AF_INET6, 0, &error_num); if (hp) { sin6->sin6_family = hp->h_addrtype; (void) memcpy(&sin6->sin6_addr, hp->h_addr, hp->h_length); freehostent(hp); return; } if (error_num == TRY_AGAIN) { (void) fprintf(stderr, "ifconfig: %s: bad address " "(try again later)\n", s); } else { (void) fprintf(stderr, "ifconfig: %s: bad address\n", s); } exit(1); } /* * If "slash" is zero this parses the whole string as * an integer. With "slash" non zero it parses the tail part as an integer. * * If it is not a valid integer this returns BAD_ADDR. * If there is / present this returns NO_PREFIX. */ static int in_getprefixlen(char *addr, boolean_t slash, int max_plen) { int prefixlen; char *str, *end; if (slash) { str = strchr(addr, '/'); if (str == NULL) return (NO_PREFIX); str++; } else str = addr; prefixlen = strtol(str, &end, 10); if (prefixlen < 0) return (BAD_ADDR); if (str == end) return (BAD_ADDR); if (max_plen != 0 && max_plen < prefixlen) return (BAD_ADDR); return (prefixlen); } /* * Convert a prefix length to a mask. * Returns 1 if ok. 0 otherwise. * Assumes the mask array is zero'ed by the caller. */ static boolean_t in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask) { if (prefixlen < 0 || prefixlen > maxlen) return (0); while (prefixlen > 0) { if (prefixlen >= 8) { *mask++ = 0xFF; prefixlen -= 8; continue; } *mask |= 1 << (8 - prefixlen); prefixlen--; } return (1); } static void print_flags(uint64_t flags) { boolean_t first = _B_TRUE; int cnt, i; (void) printf("flags=%llx", flags); cnt = sizeof (if_flags_tbl) / sizeof (if_flags_t); for (i = 0; i < cnt; i++) { if (flags & if_flags_tbl[i].iff_value) { if (first) { (void) printf("<"); first = _B_FALSE; } else { /* * It has to be here and not with the * printf below because for the last one, * we don't want a comma before the ">". */ (void) printf(","); } (void) printf("%s", if_flags_tbl[i].iff_name); } } if (!first) (void) printf(">"); } static void print_config_flags(int af, uint64_t flags) { if_config_cmd_t *cmdp; for (cmdp = if_config_cmd_tbl; cmdp->iff_flag != 0; cmdp++) { if ((flags & cmdp->iff_flag) && (cmdp->iff_af == AF_UNSPEC || cmdp->iff_af == af)) { (void) printf("%s ", cmdp->iff_name); } } } /* * Use the configured directory lookup mechanism (e.g. files/NIS/...) * to find the network mask. Returns true if we found one to set. * * The parameter addr_set controls whether we should get the address of * the working interface for the netmask query. If addr_set is true, * we will use the address provided. Otherwise, we will find the working * interface's address and use it instead. */ static boolean_t in_getmask(struct sockaddr_in *saddr, boolean_t addr_set) { struct sockaddr_in ifaddr; /* * Read the address from the interface if it is not passed in. */ if (!addr_set) { (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0) { if (errno != EADDRNOTAVAIL) { (void) fprintf(stderr, "Need net number for " "mask\n"); } return (_B_FALSE); } ifaddr = *((struct sockaddr_in *)&lifr.lifr_addr); } else { ifaddr.sin_addr = saddr->sin_addr; } if (getnetmaskbyaddr(ifaddr.sin_addr, &saddr->sin_addr) == 0) { saddr->sin_family = AF_INET; return (_B_TRUE); } return (_B_FALSE); } static int lifnum(const char *ifname) { const char *cp; if ((cp = strchr(ifname, ':')) == NULL) return (0); else return (atoi(cp + 1)); } static void add_ni(const char *name) { ni_t **pp; ni_t *p; for (pp = &ni_list; (p = *pp) != NULL; pp = &(p->ni_next)) { if (strcmp(p->ni_name, name) == 0) { if (debug > 2) (void) fprintf(stderr, "'%s' is a duplicate\n", name); return; } } if (debug > 2) (void) fprintf(stderr, "adding '%s'\n", name); if ((p = malloc(sizeof (ni_t))) == NULL) return; (void) strlcpy(p->ni_name, name, sizeof (p->ni_name)); p->ni_next = NULL; *pp = p; num_ni++; } static boolean_t ni_entry(const char *linkname, void *arg) { dlpi_handle_t dh; datalink_class_t class; (void) dladm_name2info(arg, linkname, NULL, NULL, &class, NULL); if (class == DATALINK_CLASS_ETHERSTUB) return (_B_FALSE); if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS) return (_B_FALSE); add_ni(linkname); dlpi_close(dh); return (_B_FALSE); } /* * dhcp-related routines */ static int setifdhcp(const char *caller, const char *ifname, int argc, char *argv[]) { dhcp_ipc_request_t *request; dhcp_ipc_reply_t *reply = NULL; int timeout = DHCP_IPC_WAIT_DEFAULT; dhcp_ipc_type_t type = DHCP_START; int error; boolean_t is_primary = _B_FALSE; boolean_t started = _B_FALSE; for (argv++; --argc > 0; argv++) { if (strcmp(*argv, "primary") == 0) { is_primary = _B_TRUE; continue; } if (strcmp(*argv, "wait") == 0) { if (--argc <= 0) { usage(); return (DHCP_EXIT_BADARGS); } argv++; if (strcmp(*argv, "forever") == 0) { timeout = DHCP_IPC_WAIT_FOREVER; continue; } if (sscanf(*argv, "%d", &timeout) != 1) { usage(); return (DHCP_EXIT_BADARGS); } if (timeout < 0) { usage(); return (DHCP_EXIT_BADARGS); } continue; } type = dhcp_string_to_request(*argv); if (type == -1) { usage(); return (DHCP_EXIT_BADARGS); } } /* * Only try to start agent on start or inform; in all other cases it * has to already be running for anything to make sense. */ if (type == DHCP_START || type == DHCP_INFORM) { if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) { (void) fprintf(stderr, "%s: unable to start %s\n", caller, DHCP_AGENT_PATH); return (DHCP_EXIT_FAILURE); } started = _B_TRUE; } if (is_primary) type |= DHCP_PRIMARY; if (af != AF_INET) type |= DHCP_V6; request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE); if (request == NULL) { (void) fprintf(stderr, "%s: out of memory\n", caller); return (DHCP_EXIT_SYSTEM); } error = dhcp_ipc_make_request(request, &reply, timeout); if (error != 0) { free(request); /* * Re-map connect error to not under control if we didn't try a * start operation, as this has to be true and results in a * clearer message, not to mention preserving compatibility * with the days when we always started dhcpagent for every * request. */ if (error == DHCP_IPC_E_CONNECT && !started) error = DHCP_IPC_E_UNKIF; (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname, dhcp_ipc_strerror(error)); return (DHCP_EXIT_FAILURE); } error = reply->return_code; if (error != 0) { free(request); free(reply); if (error == DHCP_IPC_E_TIMEOUT && timeout == 0) return (DHCP_EXIT_SUCCESS); (void) fprintf(stderr, "%s: %s: %s\n", caller, ifname, dhcp_ipc_strerror(error)); if (error == DHCP_IPC_E_TIMEOUT) return (DHCP_EXIT_TIMEOUT); else return (DHCP_EXIT_IF_FAILURE); } if (DHCP_IPC_CMD(type) == DHCP_STATUS) { (void) printf("%s", dhcp_status_hdr_string()); (void) printf("%s", dhcp_status_reply_to_string(reply)); } free(request); free(reply); return (DHCP_EXIT_SUCCESS); } static void usage(void) { (void) fprintf(stderr, "usage: ifconfig | -a[ 4 | 6 | D ][ u | d ][ Z ]\n"); (void) fprintf(stderr, "%s", "\t[ ]\n" "\t[
[/] [ ] ]\n" "\t[ set [
][/] ]" " [
/] ]\n" "\t[ destination ]\n" "\t[ addif
[/]" " [ ] ]\n" "\t[ removeif
[/] ]\n" "\t[ arp | -arp ]\n" "\t[ auto-revarp ]\n" "\t[ broadcast ]\n" "\t[ index ]\n" "\t[ metric ] [ mtu ]\n" "\t[ netmask ]\n" "\t[ plumb ] [ unplumb ]\n" "\t[ preferred | -preferred ]\n" "\t[ private | -private ]\n" "\t[ local | -local ]\n" "\t[ router | -router ]\n" "\t[ subnet ]\n" "\t[ trailers | -trailers ]\n" "\t[ token
/ ]\n" "\t[ tsrc ]\n" "\t[ tdst ]\n" "\t[ auth_algs ]\n" "\t[ encr_algs ]\n" "\t[ encr_auth_algs ]\n" "\t[ up ] [ down ]\n" "\t[ xmit | -xmit ]\n" "\t[ modlist ]\n" "\t[ modinsert ]\n" "\t[ modremove ]\n" "\t[ ipmp ]\n" "\t[ group ] | [ group \"\"]\n" "\t[ deprecated | -deprecated ]\n" "\t[ standby | -standby ]\n" "\t[ failover | -failover ]\n" "\t[ zone | -zone ]\n" "\t[ usesrc ]\n" "\t[ all-zones ]\n"); (void) fprintf(stderr, "or\n"); (void) fprintf(stderr, "\tifconfig | -a[ 4 | 6 | D ] [ u | d ]\n"); (void) fprintf(stderr, "%s", "\tauto-dhcp | dhcp\n" "\t[ wait