ifconfig.c revision 614f161203d313b00e559d24c1d439b11e022fd5
/*
* 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.
*/
#include "defs.h"
#include "strings.h"
#include "ifconfig.h"
#include <compat.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <inet/ipsec_impl.h>
#define LOOPBACK_IF "lo0"
#define NONE_STR "none"
#define ARP_MOD_NAME "arp"
#define TUN_NAME "tun"
#define ATUN_NAME "atun"
#define TUN6TO4_NAME "6to4tun"
#define IPMPSTUB (void *)-1
typedef struct if_flags {
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"}
};
typedef struct {
const char *ia_app;
static const if_appflags_t if_appflags_tbl[] = {
{ NULL, 0, 0 }
};
/* current interface name a particular function is accessing */
/* foreach interface saved name */
static int setaddr;
/*
* 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 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 void print_ifether(char *ifname);
/*
* Address family specific function prototypes.
*/
/*
* Misc support functions
*/
static int settaddr(char *, int (*)(icfg_handle_t,
static void status(void);
static void ifstatus(const char *);
static void usage(void);
static int ip_domux2fd(int *, int *, int *, int *, int *);
static int ip_plink(int, int, int, int, int);
static void start_ipmp_daemon(void);
#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 AF_ANY (-1)
/* Refer to the comments in ifconfig() on the netmask "hack" */
#define NETMASK_CMD "netmask"
struct sockaddr_storage g_netmask;
struct cmd {
char *c_name;
int c_abortonfail; /* don't continue parsing args */
/* for the current interface */
int c_af; /* address family restrictions */
} cmds[] = {
/*
* NOTE: any additions to this table must also be applied to ifparse
*/
{ 0, 0, setifdstaddr, 0, AF_ANY },
{ 0, 0, 0, 0, 0 },
};
typedef struct if_config_cmd {
int iff_af;
char *iff_name;
/*
* 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[] = {
{ 0, 0, NULL },
};
typedef struct ni {
} ni_t;
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[] = {
{ 0, 0, 0, 0, 0 }
};
int
{
char *default_ip_str;
if (argc < 2) {
usage();
exit(1);
}
exit(1);
}
if (v4compat == DEFAULT_PROT_BAD_VALUE) {
"ifconfig: %s: Bad value for %s in %s\n", default_ip_str,
exit(2);
}
if (argc > 0) {
break;
}
}
v4compat = 0;
}
}
Perror0_exit("socket");
/*
* 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 */
int c;
switch ((char)c) {
case 'a':
all = 1;
break;
case 'u':
break;
case 'd':
break;
case 'D':
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;
break;
case '6':
/*
* If they want IPv6, well then we'll assume
* they don't want IPv4 compat
*/
v4compat = 0;
break;
case 'v':
verbose = 1;
break;
case '?':
usage();
exit(1);
}
}
if (!all) {
"ifconfig: %s: no such interface\n", name);
exit(1);
}
} else {
}
return (0);
}
/*
* For each interface, call (*func)(argc, argv, af, lifrp).
* Only call function if onflags and offflags are set or clear, respectively,
* in the interfaces flags field.
*/
static void
{
int n;
char *buf;
int numifs;
unsigned bufsize;
int plumball = 0;
/*
* Special case:
* ifconfig -a plumb should find all network interfaces
* in the machine for the global zone.
* For non-global zones, only find the assigned interfaces.
* Also, there is no need to SIOCGLIF* ioctls, since
* those interfaces have already been plumbed
*/
if (getzoneid() == GLOBAL_ZONEID) {
lifc_flags) != 0)
return;
} else {
lifc_flags) != 0)
return;
}
return;
plumball = 1;
} else {
Perror0_exit("Could not determine number"
" of interfaces");
}
if (debug)
Perror0("out of memory\n");
(void) close(s);
return;
}
Perror0("SIOCGLIFCONF");
(void) close(s);
return;
}
}
if (!plumball) {
/*
* We must close and recreate the socket each time
* since we don't know what type of socket it is now
* (each status function may change it).
*/
(void) close(s);
if (s == -1) {
/*
* Perror0() assumes the name to be in the
* globally defined lifreq structure.
*/
Perror0_exit("socket");
}
}
/*
* Only service interfaces that match the on and off
* flags masks.
*/
/*
* Perror0() assumes the name to be in the
* globally defined lifreq structure.
*/
Perror0_exit("foreachinterface: SIOCGLIFFLAGS");
}
continue;
continue;
}
if (!plumball) {
/*
* Perror0() assumes the name to be in the
* globally defined lifreq structure.
*/
Perror0("foreachinterface: SIOCGLIFADDR");
continue;
}
/* Switch address family */
(void) close(s);
if (s == -1) {
/*
* Perror0() assumes the name to be in
* the globally defined lifreq
* structure.
*/
Perror0_exit("socket");
}
}
}
/*
* 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;
/* the func could have overwritten origname, so restore */
}
}
static void
tun_reality_check(void)
{
/* Return, we don't need to check. */
return;
}
/*
* Either not a tunnel (the SIOCGTUNPARAM fails on
* non-tunnels), the security flag is not set, or
* this is a tunnel with ipsecconf(1M)-set policy.
* Regardless, return.
*/
return;
}
if (ipsr->ipsr_esp_req != 0 &&
ipsr->ipsr_ah_req == 0)
"only ESP and no authentication.\n");
}
/*
* for the specified interface call (*func)(argc, argv, af, lifrp).
*/
static void
{
int ret;
if (argc == 0) {
status();
return;
}
/*
* Some errors are ignored in the case where more than one
* interface is being operated on.
*/
if (ret == DHCP_EXIT_IF_FAILURE) {
if (!all)
} else if (ret != DHCP_EXIT_SUCCESS) {
}
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(1M) 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(1M) 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...
*/
int largc;
char **largv;
/* Only go thru the command list once to find the netmask. */
/*
* 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.
*/
if (--largc == 0)
break;
largv++;
} else {
}
/* Continue the scan. */
}
}
}
while (argc > 0) {
struct cmd *p;
if (debug)
if (p->c_name) {
/*
* indicate that the command was
* found and check to see if
* the address family is valid
*/
break;
}
} else {
break;
}
}
/*
* If we found the keyword, but the address family
* did not match spit out an error
*/
exit(1);
}
/*
* else (no keyword found), we assume it's an address
* of some sort
*/
p++; /* got src, do dst */
if (p->c_func) {
v4compat = 0;
}
if (p->c_parameter == NEXTARG ||
p->c_parameter == OPTARG) {
"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 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;
}
}
}
/* Check to see if there's a security hole in the tunnel setup. */
}
/* ARGSUSED */
static int
{
debug++;
return (0);
}
/* ARGSUSED */
static int
{
verbose++;
return (0);
}
/*
* This function fills in the given lifreq's lifr_addr field based on
* g_netmask_set.
*/
static void
struct sockaddr_storage *mask)
{
switch (g_netmask_set) {
case G_NETMASK_SET:
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.
*/
} else {
}
break;
case G_NETMASK_NIL:
default:
break;
}
}
/*
* Set the interface address. Handles <addr>, <addr>/<n> as well as /<n>
* syntax for setting the address, the address plus netmask, and just
* the netmask respectively.
*/
/* ARGSUSED */
static int
{
int prefixlen = 0;
struct sockaddr_storage laddr;
struct sockaddr_storage netmask;
struct sockaddr_in6 *sin6;
struct sockaddr_in *sin;
struct sockaddr_storage sav_netmask;
if (addr[0] == '/')
return (setifprefixlen(addr, 0));
switch (prefixlen) {
case NO_PREFIX:
/* Nothing there - ok */
break;
case BAD_ADDR:
addr);
exit(1);
default:
"Bad prefix length: %d\n",
exit(1);
}
} else {
"Bad prefix length: %d\n",
exit(1);
}
}
/*
* Just in case of funny setting of both prefix and netmask,
* prefix should override the netmask command.
*/
break;
}
/* Tell parser that an address was set */
setaddr++;
/* save copy of netmask to restore in case of error */
Perror0_exit("SIOCGLIFNETMASK");
/*
* 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.
*/
Perror0_exit("SIOCSLIFNETMASK");
if (debug) {
char abuf[INET6_ADDRSTRLEN];
(void) printf("Setting %s af %d addr %s\n",
}
/*
* Restore the netmask
*/
Perror0_exit("SIOCSLIFADDR");
}
return (0);
}
/*
* The following functions are stolen from the ipseckey(1m) 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
{
errno = 0;
}
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.
*/
static char *
{
struct ipsecalgent *alg;
/*
* 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");
}
} else {
}
return (numprint);
}
static uint_t
{
struct ipsecalgent *alg;
"line.\n");
exit(1);
}
/*
* Special-case "none" and "any".
* Use strcasecmp because its length is bounded.
*/
return ((proto_num == IPSEC_PROTO_ESP) ?
}
return (SADB_AALG_ANY);
}
/*
* Since algorithms can be loaded during kernel run-time, check for
* numeric algorithm values too.
*/
(proto_num == IPSEC_PROTO_ESP) ?
exit(1);
/* NOTREACHED */
}
/*
* Actual ifconfig functions to set tunnel security properties.
*/
/*
* Need global for multiple calls to set_tun_algs
* because we accumulate algorithm selections over
* the lifetime of this ifconfig(1M) invocation.
*/
static int
{
sizeof (treq_tun.ifta_lifr_name));
Perror0_exit("Tunnel params on logical interfaces");
}
Perror0_exit("Not a tunnel");
else Perror0_exit("SIOCGTUNPARAM");
}
"Kernel tunnel secinfo version mismatch.\n");
exit(1);
}
/*
* 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 (first_set_tun) {
}
switch (which_alg) {
case ESP_ENCR_ALG:
if (alg == NO_ESP_EALG) {
ipsr->ipsr_esp_req = 0;
/* Let the user specify NULL encryption implicitly. */
}
} else {
ipsr->ipsr_esp_req =
}
break;
case ESP_AUTH_ALG:
if (alg == NO_ESP_AALG) {
ipsr->ipsr_esp_req = 0;
} else {
ipsr->ipsr_esp_req =
/* Let the user specify NULL encryption implicitly. */
}
break;
case AH_AUTH_ALG:
if (alg == NO_AH_AALG) {
ipsr->ipsr_ah_req = 0;
} else {
ipsr->ipsr_ah_req =
}
break;
/* Will never hit DEFAULT */
}
Perror2_exit("set tunnel security properties",
}
return (0);
}
/* ARGSUSED */
static int
{
return (set_tun_algs(ESP_ENCR_ALG,
}
/* ARGSUSED */
static int
{
return (set_tun_algs(ESP_AUTH_ALG,
}
/* ARGSUSED */
static int
{
return (set_tun_algs(AH_AUTH_ALG,
}
/* ARGSUSED */
static int
{
struct sockaddr_in laddr;
"ifconfig: revarp not possible on IPv6 interface %s\n",
name);
exit(1);
}
Perror0_exit("SIOCSLIFADDR");
}
return (0);
}
/* ARGSUSED */
static int
{
int prefixlen = 0;
struct sockaddr_storage subnet;
switch (prefixlen) {
case NO_PREFIX:
"ifconfig: Missing prefix length in subnet %s\n", addr);
exit(1);
/* NOTREACHED */
case BAD_ADDR:
"ifconfig: Bad prefix length in %s\n", addr);
exit(1);
default:
break;
}
Perror0_exit("SIOCSLIFSUBNET");
return (0);
}
/* ARGSUSED */
static int
{
struct sockaddr_in netmask;
return (0);
} else {
}
Perror0_exit("SIOCSLIFNETMASK");
return (0);
}
/*
* Parse '/<n>' as a netmask.
*/
/* ARGSUSED */
static int
{
int prefixlen;
if (prefixlen < 0) {
"ifconfig: Bad prefix length in %s\n", addr);
exit(1);
}
struct sockaddr_in6 *sin6;
"Bad prefix length: %d\n",
exit(1);
}
struct sockaddr_in *sin;
"Bad prefix length: %d\n",
exit(1);
}
} else {
" for address family inet or inet6\n");
exit(1);
}
Perror0_exit("SIOCSLIFNETMASK");
return (0);
}
/* ARGSUSED */
static int
{
struct sockaddr_in broadaddr;
/*
* 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.
*/
if (errno != EADDRNOTAVAIL)
Perror0_exit("SIOCGLIFADDR");
return (0);
}
Perror0_exit("SIOCGLIFADDR");
return (0);
}
Perror0_exit("SIOCSLIFBRDADDR");
return (0);
}
/*
* set interface destination address
*/
/* ARGSUSED */
static int
{
Perror0_exit("setifdstaddr: SIOCSLIFDSTADDR");
return (0);
}
/* ARGSUSED */
static int
{
Perror0_exit("setifflags: SIOCGLIFFLAGS");
if (value < 0) {
/*
* 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.
*/
}
/*
* 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.
*/
}
} else {
}
/*
* 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.)
*/
Perror0_exit("setifflags: SIOCGLIFGROUPINFO");
Perror0_exit("setifflags: SIOCGLIFFLAGS");
Perror0_exit("setifflags: SIOCSLIFFLAGS");
}
}
Perror0_exit("setifflags: SIOCSLIFFLAGS");
if (bringup) {
Perror0_exit("setifflags: SIOCSLIFFLAGS IFF_UP");
}
return (0);
}
/* ARGSUSED */
static int
{
Perror0_exit("setifmetric: SIOCSLIFMETRIC");
return (0);
}
/* ARGSUSED */
static int
{
Perror0_exit("setifmtu: SIOCSLIFMTU");
return (0);
}
/* ARGSUSED */
static int
{
Perror0_exit("setifindex: SIOCSLIFINDEX");
return (0);
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static int
{
int hwaddrlen;
int retval;
return (0);
}
/*
* if the IP interface in the arguments is a logical
* interface, exit with an error now.
*/
" ethernet address of a logical interface\n");
exit(1);
}
if (hwaddrlen == -1)
"ifconfig: %s: bad address\n", hwaddr);
else
exit(1);
}
if (retval == DLPI_SUCCESS) {
} 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 (!ifaddr_down(ifaddrp)) {
"cannot bring down");
}
}
}
/*
* Change the hardware address.
*/
if (retval != DLPI_SUCCESS) {
"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.
*/
}
return (0);
}
/*
* Print an interface's Ethernet address, if it has one.
*/
static void
print_ifether(char *ifname)
{
int protocol;
int fd;
/*
* It's possible the interface is only configured for
* IPv6; check again with AF_INET6.
*/
return;
}
}
/* VNI and IPMP interfaces don't have MAC addresses */
return;
/*
* We must be careful to set if_protocol based on the current
* properties of the interface. For instance, if "ip.tun0" is
* configured only as an IPv6 tunnel, then if_protocol must be
* set to AF_INET6 or icfg_get_tunnel_lower() will fail and
* we will falsely conclude that it's not a tunnel.
*/
/* Tunnel op succeeded -- it's a tunnel so skip */
return;
}
}
}
/*
* static int find_all_global_interfaces(struct lifconf *lifcp, char **buf,
* int64_t lifc_flags)
*
* It finds all data links for the global zone.
*
* 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
{
unsigned bufsize;
int n;
char errmsg[DLADM_STRSIZE];
"ifconfig: find_all_global_interfaces failed: %s\n",
return (-1);
}
/*
* Now, translate the linked list into
* a struct lifreq buffer
*/
if (num_ni == 0) {
return (0);
}
Perror0_exit("find_all_interfaces: malloc failed");
}
return (0);
}
/*
* static int find_all_zone_interfaces(struct lifconf *lifcp, char **buf,
* int64_t lifc_flags)
*
* It finds all interfaces for an exclusive-IP zone, that is all the interfaces
* assigned to it.
*
* 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
{
unsigned bufsize;
int num_ni_saved, i;
num_ni = 0;
Perror0_exit("find_all_interfaces: list interfaces failed");
/* this zone doesn't have any data-links */
if (num_ni == 0) {
return (0);
}
Perror0_exit("find_all_interfaces: out of memory");
Perror0_exit("find_all_interfaces: list interfaces failed");
if (num_ni_saved < num_ni) {
/* list increased, try again */
goto again;
}
/* this zone doesn't have any data-links now */
if (num_ni == 0) {
return (0);
}
Perror0_exit("find_all_interfaces: malloc failed");
}
for (i = 0; i < num_ni; i++) {
Perror0_exit("find_all_interfaces: overflow");
lifrp++;
}
return (0);
}
/*
* Create the next unused logical interface using the original name
* and assign the address (and mask if '/<n>' 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
{
int prefixlen = 0;
struct sockaddr_storage laddr;
struct sockaddr_storage mask;
"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;
switch (prefixlen) {
case NO_PREFIX:
/* Nothing there - ok */
break;
case BAD_ADDR:
"ifconfig: Bad prefix length in %s\n", str);
exit(1);
default:
struct sockaddr_in6 *sin6;
"Bad prefix length: %d\n",
exit(1);
}
} else {
struct sockaddr_in *sin;
"Bad prefix length: %d\n",
exit(1);
}
}
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.
*/
/* Note: no need to do DAD here since the interface isn't up yet. */
Perror0_exit("addif: SIOCLIFADDIF");
(void) printf("Created new logical interface %s\n",
/*
* Check and see if any "netmask" command is used and perform the
* necessary operation.
*/
/*
* Only set the netmask if "netmask" command is used or a prefix is
* provided.
*/
Perror0_exit("addif: SIOCSLIFNETMASK");
}
/* Finally, we set the interface address. */
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 '/<n>' here.
* Verifies that the interface is down before it is removed.
*/
/* ARGSUSED */
static int
{
struct sockaddr_storage laddr;
"ifconfig: removeif: bad physical interface name %s\n",
name);
exit(1);
}
/* This can only happen if ipif_id = 0 */
"ifconfig: removeif: can't remove interface: %s\n",
name);
exit(1);
}
Perror0_exit("removeif: SIOCLIFREMOVEIF");
}
return (0);
}
/*
* Set the address token for IPv6.
*/
/* ARGSUSED */
static int
{
int prefixlen = 0;
struct sockaddr_in6 token;
switch (prefixlen) {
case NO_PREFIX:
"ifconfig: Missing prefix length in subnet %s\n", addr);
exit(1);
/* NOTREACHED */
case BAD_ADDR:
"ifconfig: Bad prefix length in %s\n", addr);
exit(1);
default:
break;
}
Perror0_exit("setiftoken: SIOCSLIFTOKEN");
}
return (0);
}
/* ARGSUSED */
static int
{
int af;
if (debug) {
(void) printf("Setting groupname %s on interface %s\n",
}
switch (errno) {
case ENOENT:
/*
* The group doesn't yet exist; create it and repeat.
*/
continue;
goto fail;
}
continue;
case EALREADY:
/*
* The interface is already in another group; must
* remove existing membership first.
*/
"IPMP group membership");
goto fail;
}
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.
*/
continue;
goto fail;
}
_B_TRUE) == -1) {
continue;
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.
*/
0, &ifaddrs) == -1) {
goto fail;
}
if (!ifaddr_down(ifaddrp)) {
goto fail;
}
}
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.
*/
ntries = 0;
"cannot get data addresses managed "
goto fail;
}
continue;
goto again;
}
"IPMP group: %s has data addresses managed "
nappaddr++;
}
if (nappaddr > 0)
goto fail;
continue;
}
default:
goto fail;
}
}
/*
* 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.
*/
}
}
return (0);
fail:
/*
* Attempt to bring back up any interfaces that we downed.
*/
}
}
/*
* 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
{
Perror0("SIOCGLIFFLAGS");
return (_B_FALSE);
}
" supported on IPMP interfaces\n", ifname);
return (_B_FALSE);
}
" 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
{
int muxid_fd;
int muxfd;
int ipfd_lowstr;
int arpfd_lowstr;
int num_mods;
int i;
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().
*/
exit(1);
&orig_arpid) < 0) {
return (-1);
}
Perror0("cannot I_LIST to get the number of modules");
} else {
if (debug > 0) {
(void) printf("Listing (%d) modules above %s\n",
}
num_mods);
Perror0("cannot malloc");
} else {
Perror0("cannot I_LIST for module names");
} else {
(void) printf("%d %s\n", i,
}
}
}
}
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
{
}
/*
* To remove a module from the stream of the interface. It is just a
* wrapper. The real function is modop().
*/
/* ARGSUSED */
static int
{
}
/*
* the user may have configured autopush to add modules above
* udp), and push the arp module onto the resulting stream.
* This is used to make IP+ARP be able to atomically track the muxid
* for the I_PLINKed STREAMS, thus it isn't related to ARP running the ARP
* protocol.
*/
static int
open_arp_on_udp(char *udp_dev_name)
{
int fd;
return (-1);
}
errno = 0;
;
} else {
return (fd);
}
return (-1);
}
/*
* 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 *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
*/
static int
int *orig_arpid)
{
char *udp_dev_name;
*orig_arpid = 0;
Perror0_exit("status: SIOCGLIFFLAGS");
}
} else {
return (-1);
}
return (-1);
}
return (-1);
}
if (debug > 0) {
(void) printf("ARP_muxid %d IP_muxid %d\n",
}
/*
*/
return (-1);
if (lifr.lifr_arp_muxid != 0) {
lifr.lifr_arp_muxid)) < 0) {
/*
* 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.
*/
lifr.lifr_arp_muxid = 0;
*arpfd_lowstr = -1;
} else {
Perror0("_I_MUXID2FD");
return (-1);
}
lifr.lifr_arp_muxid) < 0) {
return (-1);
}
} else {
*arpfd_lowstr = -1;
}
lifr.lifr_ip_muxid)) < 0) {
Perror0("_I_MUXID2FD");
/* Undo any changes we made */
if (*orig_arpid != 0) {
}
return (-1);
}
/* Undo any changes we made */
if (*orig_arpid != 0) {
}
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 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
*/
static int
int orig_arpid)
{
int ip_muxid;
if (ip_muxid < 0) {
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) {
return (-1);
}
} else if (orig_arpid != 0) {
/* Undo the changes we did in ip_domux2fd */
}
return (0);
}
/*
*
* 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
{
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().
*/
exit(1);
/* Need to save the original string for -a option. */
Perror0("cannot malloc");
return (-1);
}
"ifconfig: must supply a module name\n");
exit(1);
}
exit(1);
}
/*
* 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 &&
exit(1);
}
exit(1);
}
&orig_arpid) < 0) {
return (-1);
}
switch (op) {
case MODINSERT_OP:
if (debug > 0) {
(void) printf("Inserting module %s at %d\n",
}
}
break;
case MODREMOVE_OP:
if (debug > 0) {
(void) printf("Removing module %s at %d\n",
}
}
break;
default:
/* Should never get to here. */
break;
}
orig_arpid));
}
/*
* Set tunnel source address
*/
/* ARGSUSED */
static int
{
}
/*
* Set tunnel destination address
*/
/* ARGSUSED */
static int
{
}
/*
* sets tunnels src|dst address. settaddr() expects the following:
* addr: Points to a printable string containing the address to be
* set, e.g. 129.153.128.110.
* fn: Pointer to a libinetcfg routine that will do the actual work.
* The only valid functions are icfg_set_tunnel_src and
* icfg_set_tunnel_dest.
*/
static int
{
struct sockaddr_storage laddr;
int lower;
int rc;
Perror0_exit("Tunnel params on logical interfaces");
}
/* Open interface. */
if (rc != ICFG_SUCCESS)
} else {
}
/* Call fn to do the real work, and close the interface. */
sizeof (struct sockaddr_storage));
if (rc != ICFG_SUCCESS)
return (0);
}
/* Set tunnel encapsulation limit. */
/* ARGSUSED */
static int
{
short limit;
int rc;
Perror0_exit("Tunnel params on logical interfaces");
}
(limit > 255)) {
Perror0_exit("Invalid encapsulation limit");
}
/* Open interface for configuration. */
Perror0_exit("couldn't open interface");
if (rc != ICFG_SUCCESS)
Perror0_exit("Could not configure tunnel encapsulation limit");
return (0);
}
/* Disable encapsulation limit. */
/* ARGSUSED */
static int
{
int rc;
Perror0_exit("Tunnel params on logical interfaces");
}
/* Open interface for configuration. */
Perror0_exit("couldn't open interface");
if (rc != ICFG_SUCCESS)
return (0);
}
/* Set tunnel hop limit. */
/* ARGSUSED */
static int
{
unsigned short limit;
int rc;
Perror0_exit("Tunnel params on logical interfaces");
}
/*
* Check limit here since it's really only an 8-bit unsigned quantity.
*/
Perror0_exit("Invalid hop limit");
}
/* Open interface for configuration. */
Perror0_exit("couldn't open interface");
if (rc != ICFG_SUCCESS)
Perror0_exit("Could not configure tunnel hop limit");
return (0);
}
/* Set zone ID */
static int
{
/* zone must be active */
"ifconfig: unknown zone '%s'\n", arg);
exit(1);
}
}
Perror0_exit("SIOCSLIFZONE");
return (0);
}
/* Put interface into all zones */
/* ARGSUSED */
static int
{
Perror0_exit("SIOCSLIFZONE");
return (0);
}
/* Set source address to use */
/* ARGSUSED */
static int
{
int rval;
/*
* Argument can be either an interface name or "none". The latter means
* that any previous selection is cleared.
*/
if (rval == 0) {
"ifconfig: Cannot specify same interface for usesrc"
" group\n");
exit(1);
}
if (rval != 0) {
Perror0_exit("Could not get interface index");
}
} else {
Perror0_exit("Not a valid usesrc consumer");
lifr.lifr_index = 0;
}
if (debug)
(void) printf("setifsrc: lifr_name %s, lifr_index %d\n",
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
{
char if_usesrc_name[LIFNAMSIZ];
char *newbuf;
Perror0_exit("status: SIOCGLIFFLAGS");
}
/*
* In V4 compatibility mode, we don't print the IFF_IPV4 flag or
* interfaces with IFF_IPV6 set.
*/
if (v4compat) {
return;
}
Perror0_exit("status: SIOCGLIFMETRIC");
} else {
if (lifr.lifr_metric)
}
/* don't print index or zone when in compatibility mode */
if (!v4compat) {
/*
* Stack instances use GLOBAL_ZONEID for IP data structures
* even in the non-global zone.
*/
char zone_name[ZONENAME_MAX];
(void) printf("\n\tall-zones");
sizeof (zone_name)) < 0) {
} else {
}
}
}
/*
* Find the number of interfaces that use this interfaces'
* address as a source address
*/
lifs.lifs_maxlen = 0;
for (;;) {
/* The first pass will give the bufsize we need */
if (rval < 0) {
}
break;
}
break;
/* Use kernel's size + a small margin to avoid loops */
5 * sizeof (struct lifreq);
/* For the first pass, realloc acts like malloc */
}
break;
}
}
if (numifs > 0) {
(void) printf("\n\tsrcof");
}
}
}
/* Find the interface whose source address this interface uses */
if (lifr.lifr_index != 0) {
if_usesrc_name) == NULL) {
(void) printf("\n\tusesrc ifIndex %d",
} else {
}
}
}
(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)
{
Perror0_exit("status: SIOCGLIFFLAGS");
}
/*
* Only print the interface status if the address family matches
* the interface family flag.
*/
if (p != NULL) {
return;
}
/*
* In V4 compatibility mode, don't print IFF_IPV6 interfaces.
*/
return;
if (p != NULL) {
} else {
(void) close(s);
/* set global af for use in p->af_status */
if (s == -1) {
Perror0_exit("socket");
}
}
/*
* Historically, 'ether' has been an address family,
* so print it here.
*/
}
}
/*
* 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
{
char *cp;
char if_usesrc_name[LIFNAMSIZ];
Perror0_exit("status: SIOCGLIFFLAGS");
}
if (debug) {
(void) printf("configinfo: name %s flags 0x%llx af_af %d\n",
}
/*
* Build the interface name to print (we can't directly use `name'
* because one cannot "plumb" ":0" interfaces).
*/
*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.
*/
return (-1);
if (v4compat)
/*
* else if the interface is IPv6
* if we have a IPv4 address family restriction return
* or we are in IPv4 compatibiltiy mode, return.
*/
return (-1);
if (v4compat)
return (-1);
}
Perror0_exit("configinfo: SIOCGLIFMETRIC");
} else {
if (lifr.lifr_metric)
}
/* Index only applies to the zeroth interface */
}
if (lifr.lifr_index != 0) {
if_usesrc_name) != NULL) {
}
}
}
if (p != NULL) {
} else {
(void) close(s);
/* set global af for use in p->af_configinfo */
if (s == -1) {
Perror0_exit("socket");
}
(*p->af_configinfo)(0, flags);
}
}
(void) printf("\n");
return (0);
}
static void
{
(void) printf("\ttunnel security settings ");
/*
* Deal with versioning, for now just point
* an ipsec_req_t at ifta_secinfo. If versions
* change, something else will overlay ifta_secinfo.
*/
(void) printf("--> use 'ipsecconf -ln -i %s'",
} else {
(void) printf("ah (%s) ",
}
(void) printf("esp (%s",
(void) printf("/%s)",
}
}
(void) printf("\n");
}
static void
tun_status(void)
{
int rc;
int protocol;
char srcbuf[INET6_ADDRSTRLEN];
char dstbuf[INET6_ADDRSTRLEN];
struct sockaddr_storage taddr;
/*
* only print tunnel info for lun 0. If ioctl fails, assume
* we are not a tunnel
*/
return;
}
switch (protocol) {
case AF_INET:
(void) printf("\tinet");
break;
case AF_INET6:
(void) printf("\tinet6");
break;
default:
Perror0_exit("\ttunnel: Illegal lower stream\n\t");
break;
}
if (rc == ICFG_NOT_SET) {
"::", sizeof (srcbuf));
} else if (rc != ICFG_SUCCESS) {
} else {
if (rc != ICFG_SUCCESS) {
}
}
if (rc == ICFG_NOT_SET) {
(void) printf("\n");
} else {
if (rc != ICFG_SUCCESS) {
}
}
/*
* tabbed indicates tabbed and printed. Use it tell us whether
* to tab and that we've printed something here, so we need a
* newline
*/
}
ICFG_SUCCESS)) {
if (!tabbed) {
(void) printf("\t");
}
if (encaplimit >= 0) {
(void) printf("tunnel encapsulation limit %d",
} else {
(void) printf("tunnel encapsulation limit disabled");
}
}
if (tabbed)
(void) printf("\n");
}
static void
{
if (debug)
/* only print status for IPv4 interfaces */
return;
/* if the interface is a tunnel, print the tunnel status */
tun_status();
if (!(flags & IFF_NOLOCAL)) {
if (!force)
return;
} else
Perror0_exit("in_status: SIOCGLIFADDR");
}
} else {
(void) printf("\tinet ");
}
if (!force)
return;
} else {
Perror0_exit("in_status: SIOCGLIFSUBNET");
}
}
if ((flags & IFF_NOLOCAL) ||
}
}
if (errno != EADDRNOTAVAIL)
Perror0_exit("in_status: SIOCGLIFNETMASK");
} else
if (flags & IFF_POINTOPOINT) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in_status: SIOCGLIFDSTADDR");
}
}
if (flags & IFF_BROADCAST) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in_status: SIOCGLIFBRDADDR");
}
(void) printf("broadcast %s",
}
}
/* If there is a groupname, print it for only the physical interface */
}
}
(void) putchar('\n');
}
static void
{
char abuf[INET6_ADDRSTRLEN];
if (debug)
return;
/* if the interface is a tunnel, print the tunnel status */
tun_status();
if (!(flags & IFF_NOLOCAL)) {
if (!force)
return;
} else
Perror0_exit("in_status6: SIOCGLIFADDR");
}
(void) printf("\tinet6 %s/%d ",
} else {
(void) printf("\tinet6 ");
}
if (!force)
return;
} else
Perror0_exit("in_status6: SIOCGLIFSUBNET");
}
if ((flags & IFF_NOLOCAL) ||
(void) printf("subnet %s/%d ",
}
}
if (flags & IFF_POINTOPOINT) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in_status6: SIOCGLIFDSTADDR");
}
(void) printf("--> %s ",
}
if (verbose) {
(void) putchar('\n');
(void) putchar('\t');
else
Perror0_exit("in_status6: SIOCGLIFTOKEN");
} else {
(void) printf("token %s/%d ",
}
Perror0_exit("in_status6: SIOCGLIFLNKINFO");
}
} else {
(void) printf("maxhops %u, reachtime %u ms, "
"reachretrans %u ms, maxmtu %u ",
}
}
/* If there is a groupname, print it for only the physical interface */
}
}
(void) putchar('\n');
}
static void
{
if (debug)
/* only configinfo info for IPv4 interfaces */
return;
if (!(flags & IFF_NOLOCAL)) {
if (!force)
return;
} else
Perror0_exit("in_configinfo: SIOCGLIFADDR");
}
}
if (!force)
return;
} else {
Perror0_exit("in_configinfo: SIOCGLIFSUBNET");
}
}
if ((flags & IFF_NOLOCAL) ||
}
if (errno != EADDRNOTAVAIL)
Perror0_exit("in_configinfo: SIOCGLIFNETMASK");
} else
if (flags & IFF_POINTOPOINT) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in_configinfo: SIOCGLIFDSTADDR");
}
}
if (flags & IFF_BROADCAST) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in_configinfo: SIOCGLIFBRDADDR");
}
(void) printf(" broadcast %s ",
}
}
/* If there is a groupname, print it for only the zeroth interface */
}
}
/* Print flags to configure */
}
static void
{
char abuf[INET6_ADDRSTRLEN];
if (debug)
flags);
return;
if (!(flags & IFF_NOLOCAL)) {
if (!force)
return;
} else
Perror0_exit("in6_configinfo: SIOCGLIFADDR");
}
(void) printf(" set %s/%d ",
}
if (!force)
return;
} else
Perror0_exit("in6_configinfo: SIOCGLIFSUBNET");
}
if ((flags & IFF_NOLOCAL) ||
(void) printf(" subnet %s/%d ",
}
if (flags & IFF_POINTOPOINT) {
if (errno == EADDRNOTAVAIL)
else
Perror0_exit("in6_configinfo: SIOCGLIFDSTADDR");
}
(void) printf(" destination %s ",
}
else
Perror0_exit("in6_configinfo: SIOCGLIFTOKEN");
} else {
(void) printf(" token %s/%d ",
}
/* If there is a groupname, print it for only the zeroth interface */
}
}
/* Print flags to configure */
}
/*
* We need to plink both the arp-device stream and the arp-ip-device stream.
* However the muxid is stored only in IP. Plumbing 2 streams individually
* is not atomic, and if ifconfig is killed, the resulting plumbing can
* be inconsistent. For eg. if only the arp stream is plumbed, we have lost
* the muxid, and the half-baked plumbing can neither be unplumbed nor
* replumbed, thus requiring a reboot. To avoid the above the following
* scheme is used.
*
* Ifconfig asks IP to enforce atomicity of plumbing the arp and IP streams.
* extra information in the I_PLINK and I_PUNLINK ioctls to let IP know
* that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs
* the IP stream first, and unplumbs it last. The kernel (IP) does not
* allow IP stream to be unplumbed without unplumbing arp stream. Similarly
* it does not allow arp stream to be plumbed before IP stream is plumbed.
* There is no need to use SIOCSLIFMUXID, since the whole operation is atomic,
* and IP uses the info in the I_PLINK message to get the muxid.
*
* b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream
* c. We need to push ARP in order to get the required kernel support for
* atomic plumbings. The actual work done by ARP is explained in arp.c
* it is not atomic, and is supported by the kernel for backward
* compatibility for other utilities like atmifconfig etc. In this case
* the utility must use SIOCSLIFMUXID.
*/
static int
{
int retval;
char *udp_dev_name;
/*
* Always dlpi_open() with DLPI_NOATTACH because the IP and ARP module
* will do the attach themselves for DLPI style-2 links.
*/
/*
* If `linkname' is the special token IPMPSTUB, then this is a request
* pass "ipmpstub0" as `linkname' since an admin *could* have a normal
* vanity-named link named "ipmpstub0" that they'd like to plumb.)
*/
linkname = "ipmpstub0";
}
if (retval != DLPI_SUCCESS)
if (debug) {
(void) printf("ifconfig: ifplumb: link %s, ifname %s, "
}
/*
* Push the ARP module onto the interface stream. IP uses
* this to send resolution requests up to ARP. We need to
* do this before the SLIFNAME ioctl is sent down because
* the interface becomes publicly known as soon as the SLIFNAME
* ioctl completes. Thus some other process trying to bring up
* the interface after SLIFNAME but before we have pushed ARP
* could hang. We pop the module again later if it is not needed.
*/
/*
* (At this point in time the kernel also allows an override of the
* IFF_CANTCHANGE flags.)
*/
Perror0_exit("ifplumb: SIOCGLIFFLAGS");
} else {
}
/*
* Set the interface name. If we've been asked to generate the PPA,
* then find the lowest available PPA (only currently used for IPMP
* interfaces). Otherwise, use the interface name as-is.
*/
if (genppa) {
int ppa;
/*
* We'd like to just set lifr_ppa to UINT_MAX and have the
* kernel pick a PPA. Unfortunately, that would mishandle
* two cases:
*
* 1. If the PPA is available but the groupname is taken
* (e.g., the "ipmp2" IP interface name is available
* but the "ipmp2" groupname is taken) then the
* auto-assignment by the kernel will fail.
*
* 2. If we're creating (e.g.) an IPv6-only IPMP
* interface, and there's already an IPv4-only IPMP
* interface, the kernel will allow us to accidentally
* reuse the IPv6 IPMP interface name (since
* SIOCSLIFNAME uniqueness is per-interface-type).
* This will cause administrative confusion.
*
* Thus, we instead take a brute-force approach of checking
* whether the IPv4 or IPv6 name is already in-use before
* attempting the SIOCSLIFNAME. As per (1) above, the
* SIOCSLIFNAME may still fail, in which case we just proceed
* to the next one. If this approach becomes too slow, we
* can add a new SIOC* to handle this case in the kernel.
*/
continue;
continue;
break;
}
} else {
/*
* The interface name could have come from the command-line;
* check it.
*/
/*
* Before we call SIOCSLIFNAME, ensure that the IPMP group
* interface for this address family exists. Otherwise, the
* kernel will kick the interface out of the group when we do
* the SIOCSLIFNAME.
*
* Example: suppose bge0 is plumbed for IPv4 and in group "a".
* If we're now plumbing bge0 for IPv6, but the IPMP group
* interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
* will kick bge0 out of group "a", which is undesired.
*/
"create %s IPMP group; %s will be removed from "
}
}
if (retval == -1) {
Perror0_exit("SIOCSLIFNAME for ip");
/*
* This difference between the way we behave for EEXIST
* and that with other errors exists to preserve legacy
* behaviour. Earlier when foreachinterface() and matchif()
* were doing the duplicate interface name checks, for
* already existing interfaces, inetplumb() returned "0".
* To preserve this behaviour, Perror0() and return are
* called for EEXIST.
*/
Perror0("SIOCSLIFNAME for ip");
return (-1);
}
/* Get the full set of existing flags for this stream */
Perror0_exit("ifplumb: SIOCGLIFFLAGS");
if (debug) {
(void) printf("ifconfig: ifplumb: %s got flags:\n",
(void) putchar('\n');
}
/* Check if arp is not actually needed */
}
/*
* since STREAMS will not let you PLINK a driver under itself,
* the stream for tunneling interfaces.
*/
else
/* Check if arp is not needed */
/*
* PLINK the interface stream so that ifconfig can exit
* without tearing down the stream.
*/
Perror0_exit("I_PLINK for ip");
}
/*
* This interface does use ARP, so set up a separate stream
* from the interface to ARP.
*
* Note: modules specified by the user are pushed
* only on the interface stream, not on the ARP stream.
*/
if (debug)
if (retval != DLPI_SUCCESS)
/*
* Tell ARP the name and unit number for this interface.
* Note that arp has no support for transparent ioctls.
*/
Perror0_exit("SIOCSLIFNAME for arp");
Perror0("SIOCSLIFNAME for arp");
goto out;
}
/*
* PLINK the IP and ARP streams so that ifconfig can exit
* without tearing down the stream.
*/
Perror0_exit("I_PLINK for ip");
Perror0_exit("I_PLINK for arp");
}
if (debug)
out:
}
/*
* 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
{
int mux_fd;
int muxid_fd;
char *udp_dev_name;
char *strptr;
int save_errno;
/* Can't unplumb logical interface zero */
" Cannot unplumb %s: Invalid interface\n", name);
exit(1);
}
Perror0_exit("unplumb: SIOCLIFREMOVEIF");
return (0);
}
/*
* the same now for PUNLINK also.
*/
if (v6)
else
Perror0_exit("unplumb: SIOCGLIFFLAGS");
}
/*
* The kernel will fail the I_PUNLINK if the group still has
* members, but check now to provide a better error message.
*/
Perror0_exit("unplumb: SIOCGLIFGROUPNAME");
Perror0_exit("unplumb: SIOCGLIFGROUPINFO");
" IPMP group is not empty\n", name);
exit(1);
}
/*
* The kernel will fail the I_PUNLINK if the IPMP interface
* has administratively up addresses; bring 'em down.
*/
continue;
if (!ifaddr_down(ifaddrp)) {
"cannot bring down");
}
}
}
Perror0_exit("unplumb: SIOCGLIFMUXID");
}
/*
* We don't have a good way of knowing whether the arp stream is
* plumbed. We can't rely on IFF_NOARP because someone could
* have turned it off later using "ifconfig xxx -arp".
*/
if (arp_muxid != 0) {
if (debug)
/*
* 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.
*/
lifr.lifr_arp_muxid = 0;
} else {
Perror0("I_PUNLINK for arp");
}
}
}
if (debug)
if (changed_arp_muxid) {
/*
* Some error occurred, and we need to restore
* everything back to what it was.
*/
save_errno = errno;
errno = save_errno;
}
Perror0_exit("I_PUNLINK for ip");
}
return (0);
}
/*
* If this is a physical interface then create it unless it is already
* present. If it is a logical interface name use SIOCLIFADDIF to
* create and (and fail it if already exists.)
* As a special case send SIOCLIFADDIF for the loopback interface. This
* is needed since there is no other notion of plumbing the loopback
* interface.
*/
/* ARGSUSED */
static int
{
char *strptr;
if (debug) {
"ifconfig: %s already exists\n", name);
}
return (0);
}
if (debug) {
"ifconfig: %s already exists\n",
name);
}
} else {
}
}
return (0);
}
/*
* For global zone, check if the interface is used by a non-global
* zone, note that the non-global zones doesn't need this check,
* because zoneadm has taken care of this when the zone boots.
*/
if (zoneid == GLOBAL_ZONEID) {
int ret;
if (ret == 0) {
char zonename[ZONENAME_MAX];
return (1);
}
}
if (debug)
return (0);
}
/* ARGSUSED */
static int
{
int retval;
/*
* Treat e.g. "ifconfig ipmp0:2 ipmp" as "ifconfig ipmp0:2 plumb".
* Otherwise, try to create the requested IPMP interface.
*/
else
/*
* 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
{
int ppa;
static int ipmp_daemon_started;
if (debug) {
(void) printf("create_ipmp: ifname %s grname %s af %d\n",
}
else
if (ppa == -1) {
return (-1);
}
else
/*
* To preserve backward-compatibility, always bring up the link-local
* address for implicitly-created IPv6 IPMP interfaces.
*/
}
}
/*
* If the caller requested a different group name, issue a
* SIOCSLIFGROUPNAME on the new IPMP interface.
*/
Perror0("SIOCSLIFGROUPNAME");
return (-1);
}
}
/*
* If we haven't done so yet, ensure in.mpathd is started.
*/
if (ipmp_daemon_started++ == 0)
return (0);
}
/*
* Check if `ifname' is plumbed and in an IPMP group on its "other" address
* family. If so, create a matching IPMP group for address family `af'.
*/
static int
{
int fd;
/*
* Get the socket for the "other" address family.
*/
return (0);
return (0);
/*
* If `ifname' *is* the IPMP group interface, or if the relevant
* address family is already configured, then there's nothing to do.
*/
return (0);
}
/*
* Start in.mpathd if it's not already running.
*/
static void
start_ipmp_daemon(void)
{
int retval;
/*
* Ping in.mpathd to see if it's running already.
*/
return;
}
switch (retval) {
case IPMP_ENOMPATHD:
break;
case IPMP_SUCCESS:
return;
default:
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:
_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
{
return (_B_FALSE);
if (up)
else
return (_B_FALSE);
/*
* If we're trying to bring the address down, ensure that DAD activity
* (observable by IFF_DUPLICATE) has also been stopped.
*/
return (_B_FALSE);
}
}
return (_B_TRUE);
}
static boolean_t
{
}
static boolean_t
{
}
void
{
}
void
Perror0_exit(const char *cmd)
{
exit(1);
/* NOTREACHED */
}
void
{
switch (error) {
case ENXIO:
break;
case EPERM:
break;
case EEXIST:
break;
default:
}
}
/*
* Print out error message (Perror2()) and exit
*/
void
{
exit(1);
/* NOTREACHED */
}
void
{
}
/*
* Print out error message (Perrdlpi()) and exit
*/
void
{
exit(1);
}
/*
* If the last argument is non-NULL allow a <addr>/<n> syntax and
* pass out <n> in *plenp.
* If <n> doesn't parse return BAD_ADDR as *plenp.
* If no /<n> is present return NO_PREFIX as *plenp.
*/
static void
{
/* LINTED: alignment */
int error_num;
/*
* Look for '/'<n> is plenp
*/
char *cp;
return;
*cp = '\0';
exit(1);
}
/*
* Try to catch attempts to set the broadcast address to all 1's.
*/
return;
}
if (hp) {
return;
}
if (np) {
return;
}
"(try again later)\n", s);
} else {
}
exit(1);
}
/*
* If the last argument is non-NULL allow a <addr>/<n> syntax and
* pass out <n> in *plenp.
* If <n> doesn't parse return BAD_ADDR as *plenp.
* If no /<n> is present return NO_PREFIX as *plenp.
*/
static void
{
/* LINTED: alignment */
int error_num;
/*
* Look for '/'<n> is plenp
*/
char *cp;
return;
*cp = '\0';
exit(1);
}
if (hp) {
return;
}
"(try again later)\n", s);
} else {
}
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 /<n> present this returns NO_PREFIX.
*/
static int
{
int prefixlen;
if (slash) {
return (NO_PREFIX);
str++;
} else
if (prefixlen < 0)
return (BAD_ADDR);
return (BAD_ADDR);
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
{
return (0);
while (prefixlen > 0) {
if (prefixlen >= 8) {
*mask++ = 0xFF;
prefixlen -= 8;
continue;
}
prefixlen--;
}
return (1);
}
static void
{
int cnt, i;
for (i = 0; i < cnt; i++) {
if (first) {
(void) printf("<");
} 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(",");
}
}
}
if (!first)
(void) printf(">");
}
static void
{
}
}
}
/*
* 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
{
struct sockaddr_in ifaddr;
/*
* Read the address from the interface if it is not passed in.
*/
if (!addr_set) {
if (errno != EADDRNOTAVAIL) {
"mask\n");
}
return (_B_FALSE);
}
} else {
}
return (_B_TRUE);
}
return (_B_FALSE);
}
static int
{
const char *cp;
return (0);
else
}
static int
{
}
static void
{
ni_t *p;
if (debug > 2)
name);
return;
}
}
if (debug > 2)
name);
return;
*pp = p;
num_ni++;
}
static boolean_t
{
if (class == DATALINK_CLASS_ETHERSTUB)
return (_B_FALSE);
return (_B_FALSE);
dlpi_close(dh);
return (_B_FALSE);
}
/*
* dhcp-related routines
*/
static int
{
int timeout = DHCP_IPC_WAIT_DEFAULT;
int error;
continue;
}
if (--argc <= 0) {
usage();
return (DHCP_EXIT_BADARGS);
}
argv++;
continue;
}
usage();
return (DHCP_EXIT_BADARGS);
}
if (timeout < 0) {
usage();
return (DHCP_EXIT_BADARGS);
}
continue;
}
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.
*/
return (DHCP_EXIT_FAILURE);
}
}
if (is_primary)
type |= DHCP_PRIMARY;
return (DHCP_EXIT_SYSTEM);
}
if (error != 0) {
/*
* 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.
*/
return (DHCP_EXIT_FAILURE);
}
if (error != 0) {
return (DHCP_EXIT_SUCCESS);
if (error == DHCP_IPC_E_TIMEOUT)
return (DHCP_EXIT_TIMEOUT);
else
return (DHCP_EXIT_IF_FAILURE);
}
}
return (DHCP_EXIT_SUCCESS);
}
static void
usage(void)
{
"usage: ifconfig <interface> | -a[ 4 | 6 | D ][ u | d ][ Z ]\n");
"\t[ <addr_family> ]\n"
"\t[ <address>[/<prefix_length>] [ <dest_address> ] ]\n"
"\t[ set [ <address>][/<prefix_length>] ]"
" [ <address>/<prefix_length>] ]\n"
"\t[ destination <dest_address> ]\n"
"\t[ addif <address>[/<prefix_length>]"
" [ <dest_address> ] ]\n"
"\t[ removeif <address>[/<prefix_length>] ]\n"
"\t[ arp | -arp ]\n"
"\t[ auto-revarp ]\n"
"\t[ broadcast <broad_addr> ]\n"
"\t[ index <if_index> ]\n"
"\t[ metric <n> ] [ mtu <n> ]\n"
"\t[ netmask <mask> ]\n"
"\t[ plumb ] [ unplumb ]\n"
"\t[ preferred | -preferred ]\n"
"\t[ private | -private ]\n"
"\t[ local | -local ]\n"
"\t[ router | -router ]\n"
"\t[ subnet <subnet_address>]\n"
"\t[ trailers | -trailers ]\n"
"\t[ token <address>/<prefix_length> ]\n"
"\t[ tsrc <tunnel_src_address> ]\n"
"\t[ tdst <tunnel_dest_address> ]\n"
"\t[ auth_algs <tunnel_AH_authentication_algorithm> ]\n"
"\t[ encr_algs <tunnel_ESP_encryption_algorithm> ]\n"
"\t[ encr_auth_algs <tunnel_ESP_authentication_algorithm> ]\n"
"\t[ up ] [ down ]\n"
"\t[ xmit | -xmit ]\n"
"\t[ modlist ]\n"
"\t[ modinsert <module_name@position> ]\n"
"\t[ modremove <module_name@position> ]\n"
"\t[ ipmp ]\n"
"\t[ group <groupname>] | [ group \"\"]\n"
"\t[ deprecated | -deprecated ]\n"
"\t[ standby | -standby ]\n"
"\t[ failover | -failover ]\n"
"\t[ zone <zonename> | -zone ]\n"
"\t[ usesrc <interface> ]\n"
"\t[ all-zones ]\n");
"\tifconfig <interface> | -a[ 4 | 6 | D ] [ u | d ]\n");
"\t[ wait <time> | forever ]\n\t[ primary ]\n"
"\tstart | drop | ping | release | status | inform\n");
}