ifconfig.c revision 7906a3e0743d363f65208909d45d7471892fa5dc
/*
* Copyright 2006 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include "strings.h"
#include "ifconfig.h"
#include <compat.h>
#include <libdlpi.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"
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_MIPRUNNING, "MIP" },
{ 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"}
};
/* 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
/*
* iface_t
* used by setifether to create a list of interfaces to mark
* down-up when changing the ethernet address of an interface
*/
typedef struct iface {
} iface_t;
int s;
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 *);
static int ip_plink(int, int, int, int);
static int get_lun(char *);
#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[] = {
{ 0, 0, setifdstaddr, 0, AF_ANY },
{ 0, 0, 0, 0, 0 },
};
typedef struct if_config_cmd {
char *iff_name;
static if_config_cmd_t if_config_cmd_tbl[] = {
{ IFF_UP, "up" },
{ IFF_NOTRAILERS, "-trailers" },
{ IFF_PRIVATE, "private" },
{ IFF_NOXMIT, "-xmit" },
{ IFF_ANYCAST, "anycast" },
{ IFF_NOLOCAL, "-local" },
{ IFF_DEPRECATED, "deprecated" },
{ IFF_NOFAILOVER, "-failover" },
{ IFF_STANDBY, "standby" },
{ IFF_FAILED, "failed" },
{ IFF_PREFERRED, "preferred" },
{ 0, 0 },
};
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
{
/* Include IFF_NOXMIT, IFF_TEMPORARY and all zone interfaces */
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;
}
}
if (s < 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 by traversing the devinfo tree.
* Also, there is no need to SIOCGLIF* ioctls, since
* those interfaces have already been plumbed
*/
/*
* Look through the kernel's devinfo tree for
* network devices
*/
/*
* DINFOCACHE is equivalent to DINFOSUBTREE | DINFOMINOR |
* DINFOPROP | DINFOFORCE.
*/
" check the devinfo driver.\n");
exit(1);
}
/*
* Now, translate the linked list into
* a struct lifreq buffer
*/
Perror0_exit("foreachinterface: malloc failed");
}
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), or the security flag is not set. Either
* way, return.
*/
return;
}
if (ipsr->ipsr_esp_req != 0 &&
ipsr->ipsr_ah_req == 0)
"only ESP and potentially 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)
} else
"for inet6\n");
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");
/*
* Catch set of address for AF_INET6 to perform
* duplicate address detection. Check that the interface is
* up.
*/
Perror0_exit("ifsetaddr: SIOCGLIFFLAGS");
}
if (debug)
(void) printf(
"setifaddr: Calling ifdad flags %llx\n",
exit(3);
}
}
/*
* 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-case 0 to return "<any-none>" */
if (alg_value == 0)
return ("<any-none>");
} else {
}
return (numprint);
}
static uint_t
{
struct ipsecalgent *alg;
"line.\n");
exit(1);
}
/*
* Special-case "none". Use strcasecmp because its length is
* bound.
*/
return ((proto_num == IPSEC_PROTO_ESP) ?
}
}
/*
* 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.
*/
static int
{
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;
} 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
{
int phyintlen, origphyintlen;
Perror0_exit("setifflags: SIOCGLIFFLAGS");
if (value == IFF_NOFAILOVER) {
/*
* Fail if '-failover' is set after a prior addif created the
* alias on a different interface. This can happen when the
* interface is part of an IPMP group.
*/
if (phyintlen != origphyintlen ||
origname);
exit(1);
}
}
/*
* Catch "up" transition for AF_INET6 to perform duplicate address
* detection. ifdad checks if an address has been set.
*/
if (debug)
(void) printf(
"setifaddr:Calling ifdad flags %llx value 0x%llx\n",
exit(1);
}
if (value < 0) {
} else
Perror0_exit("setifflags: SIOCSLIFFLAGS");
}
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 int
{
int maclen;
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 (maclen == -1)
"ifconfig: %s: bad address\n", addr);
else
exit(1);
}
/*
* Call selectifs only for the IP interfaces that are ipv4.
* offflags == IFF_IPV6 because you should not change the
* Ethernet address of an ipv6 interface
*/
/* If physical interface not found, exit now */
"ifconfig: interface %s not found\n", savedname);
exit(1);
}
/* Restore */
/*
* close and reopen the socket
* we don't know which type of socket we have now
*/
(void) close(s);
if (s < 0) {
Perror0_exit("socket");
}
/*
* mark down the logical interfaces first,
* and then the physical interface
*/
Perror0_exit("mark down interface failed");
}
/*
* Change the physical address
*/
"ifconfig: failed setting mac address on %s\n",
}
/*
* if any interfaces were marked down before changing the
* ethernet address, put them up again.
* First the physical interface, then the logical ones.
*/
Perror0_exit("mark down interface failed");
}
/* Free the memory allocated by selectifs */
}
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;
}
}
/* Virtual 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 void selectifs(int argc, char *argv[], int af, struct lifreq *rp)
*
* Called inside setifether() to create a list of interfaces to
* If the current interface is the physical interface passed
* as an argument to ifconfig, update phyif.
* If the current interface is a logical interface associated
* to the physical interface, add it to the logifs list.
*/
/* ARGSUSED */
static void
{
char *colonp;
int length;
/*
* savedname= name of the IP interface to which you want to
* change ethernet address
* name= name of the current IP interface
*/
else
Perror0("selectifs: SIOCGLIFFLAGS");
return;
}
Perror0_exit("selectifs: malloc failed\n");
}
/* this is the physical interface */
} else {
/* this is a logical interface */
}
}
}
/*
* static int updownifs(iface_t *ifs, int up)
*
* It takes in input a list of IP interfaces (ifs)
* and a flag (up).
* It marks each interface in the list down (up = 0)
* or up (up > 0). This is done ONLY if the IP
* interface was originally up.
*
* Return values:
* 0 = everything OK
* -1 = problem
*/
static int
{
int ret = 0;
int save_errno;
if (!up)
save_errno = errno;
ret = -1;
}
if (!up) /* restore the original flags */
}
}
if (ret == -1) {
errno = save_errno;
}
return (ret);
}
/*
* 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);
}
/*
* If laddr is non-NULL it is used - otherwise we use the address on
* the interface.
*/
/* ARGSUSED */
static int
{
struct sockaddr_in6 testaddr;
if (debug)
/*
* Check the address assigned to the interface.
* Skip the check if IFF_NOLOCAL, IFF_NONUD, IFF_ANYCAST, or
* IFF_LOOPBACK.
* Note that IFF_NONUD turns of both NUD and DAD.
*/
Perror0_exit("ifdad: SIOCGLIFFLAGS");
}
return (0);
} else {
Perror0_exit("ifdad: SIOCGLIFADDR");
}
}
return (0);
return (-1);
else
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);
}
/*
* Return value: 0 on success, -1 on failure.
*/
static int
connect_to_mpathd(int family)
{
int s;
struct sockaddr_storage ss;
int addrlen;
int ret;
int on;
if (s < 0) {
Perror0_exit("connect_to_mpathd: socket");
}
/*
* Need to bind to a privileged port. For non-root, this
* will fail. in.mpathd verifies that only commands coming
* from privileged ports succeed so that ordinary users
* can't connect and start talking to in.mpathd
*/
on = 1;
sizeof (on)) < 0) {
Perror0_exit("connect_to_mpathd: setsockopt");
}
switch (family) {
case AF_INET:
addrlen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
addrlen = sizeof (struct sockaddr_in6);
break;
}
if (ret != 0) {
(void) close(s);
return (-1);
}
switch (family) {
case AF_INET:
break;
case AF_INET6:
break;
}
(void) close(s);
return (ret);
}
/* ARGSUSED */
static int
{
if (debug) {
(void) printf("Setting groupname %s on interface %s\n",
}
sizeof (lifr.lifr_groupname));
Perror0_exit("setifgroupname: SIOCSLIFGROUPNAME");
}
/*
* If the SUNW_NO_MPATHD environment variable is set then don't
* bother starting up in.mpathd. See PSARC/2002/249 for the
* depressing details on this bit of stupidity.
*/
return (0);
}
/*
* Try to connect to in.mpathd using IPv4. If we succeed,
* we conclude that in.mpathd is running, and quit.
*/
if (connect_to_mpathd(AF_INET) == 0) {
/* connect succeeded, mpathd is already running */
return (0);
}
/*
* Try to connect to in.mpathd using IPv6. If we succeed,
* we conclude that in.mpathd is running, and quit.
*/
if (connect_to_mpathd(AF_INET6) == 0) {
/* connect succeeded, mpathd is already running */
return (0);
}
/*
* in.mpathd may not be running. Start it now. If it is already
* running, in.mpathd will take care of handling multiple incarnations
* of itself. ifconfig only tries to optimize performance by not
* starting another incarnation of in.mpathd.
*/
switch (fork()) {
case -1:
Perror0_exit("setifgroupname: fork");
/* NOTREACHED */
case 0:
_exit(1);
/* NOTREACHED */
default:
return (0);
}
}
/*
* To list all the modules above a given network interface.
*/
/* ARGSUSED */
static int
{
int muxfd;
int ipfd_lowstr;
int arpfd_lowstr;
int num_mods;
int i;
int orig_arpid;
&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,
}
}
}
}
}
#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 or below
* udp), and push the arp module onto raw IP.
*/
static int
open_arp_on_udp(char *udp_dev_name)
{
int fd;
return (-1);
}
errno = 0;
if (!popped) {
} 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 *fd: (referenced) fd to the lower IP stream.
*
* Return:
* -1 if operation fails, 0 otherwise.
*
* Please see the big block comment above plumb_one_device()
*/
static int
{
int ip_fd;
char *udp_dev_name;
char *ip_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 fd: fd to the lower IP stream.
*
* Return:
* -1 if operation fails, 0 otherwise.
*
* Please see the big block comment above plumb_one_device()
*/
static int
{
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 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;
/* 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;
}
}
/*
* 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) {
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 phydevname[LIFNAMSIZ];
char if_usesrc_name[LIFNAMSIZ];
char *cp;
Perror0_exit("status: SIOCGLIFFLAGS");
}
if (debug) {
(void) printf("configinfo: name %s flags 0x%llx af_af %d\n",
}
/* remove LIF component */
if (cp) {
*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)
}
/* don't print index when in compatibility mode */
if (!v4compat) {
}
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("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 lun 0 alone */
sizeof (lifr.lifr_groupname));
(void) printf("\n\tgroupname %s",
}
}
}
(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 lun 0 alone */
sizeof (lifr.lifr_groupname));
(void) printf("\n\tgroupname %s",
}
}
}
(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");
}
} else {
}
}
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 lun 0 alone */
sizeof (lifr.lifr_groupname));
(void) printf(" group %s ",
}
}
}
/* Print flags to configure */
/* IFF_NOARP applies to AF_INET only */
(void) printf("-arp ");
}
}
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(" addif %s/%d ",
} else {
(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 lun 0 alone */
sizeof (lifr.lifr_groupname));
(void) printf(" group %s ",
}
}
}
/* Print flags to configure */
/* IFF_NONUD applies to AF_INET6 only */
(void) printf("-nud ");
}
}
/* ARGSUSED */
static int
{
char *cp;
/* remove LIF component */
if (cp) {
cp++;
}
return (0);
}
/*
* 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 void
{
int arp_fd = -1;
int mux_fd;
char *udp_dev_name;
if (debug)
(void) printf("plumb_one_device: ifname %s, ppa %d\n",
Perror0_exit("dlpi_detach");
/*
* 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 CANTCHANGE flags.
*/
Perror0_exit("plumb_one_device: SIOCGLIFFLAGS");
/* Set the name string and the IFF_IPV* flag */
} else {
}
/* record the device and module names as interface name */
/* set the interface name */
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 matcif()
* 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;
}
/* Get the full set of existing flags for this stream */
Perror0_exit("plumb_one_device: SIOCFLIFFLAGS");
if (debug) {
(void) printf("plumb_one_device: %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");
}
return;
}
/*
* 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)
(void) printf("plumb_one_device: ifname: %s\n",
Perror0_exit("dlpi_detach");
/*
* Tell ARP the name and unit number for this interface.
* Note that arp has no support for transparent ioctls.
*/
sizeof (lifr)) == -1) {
Perror0_exit("SIOCSLIFNAME for arp");
Perror0("SIOCSLIFNAME for arp");
return;
}
/*
* 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)
}
/*
* 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;
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.
*/
else
Perror0_exit("unplumb: SIOCGLIFFLAGS");
}
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;
int dev_fd;
if (debug) {
"ifconfig: %s already exists\n", name);
}
return (0);
}
if (debug) {
"ifconfig: %s already exists\n",
name);
}
} else {
}
}
/*
* IP can create the new logical interface on a different
* physical interface in the same IPMP group. Take the new
* interface into account for further operations.
*/
return (0);
}
if (debug)
/* NOTREACHED */
}
return (0);
}
void
{
int save_errno;
save_errno = errno;
errno = save_errno;
switch (errno) {
case ENXIO:
break;
case EPERM:
break;
case EEXIST:
break;
default: {
}
}
}
void
Perror0_exit(char *cmd)
{
exit(1);
/* NOTREACHED */
}
void
{
int save_errno;
save_errno = errno;
errno = save_errno;
switch (errno) {
case ENXIO:
break;
case EPERM:
break;
default: {
}
}
}
/*
* Print out error message (Perror2()) and exit
*/
void
{
exit(1);
/* NOTREACHED */
}
/*
* 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
{
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
{
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
{
int cnt, i;
for (i = 0; i < cnt; i++) {
}
}
}
/*
* 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
{
}
static void
{
ni_t *p;
if (debug > 2)
name);
return;
}
}
if (debug > 2)
name);
return;
*pp = p;
num_ni++;
}
/* ARGSUSED2 */
static int
{
char *provider;
int fd;
return (DI_WALK_CONTINUE);
if (debug > 2)
return (DI_WALK_CONTINUE);
if (debug > 2)
goto done;
if (debug > 2)
goto done;
}
if (debug > 2)
if (debug > 2)
"non-alias node, ignoring\n");
goto done;
}
if (debug > 2)
"alias node, using instance\n");
if (debug > 2)
"non-existent PPA, ignoring\n");
goto done;
}
done:
(void) dlpi_close(fd);
return (DI_WALK_CONTINUE);
}
/*
* 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)
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[ 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");
}