mirror_iproute2/ip/ipaddress.c
Matteo Croce 8589eb4efd treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:

        ip/ip:
        add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
        Total: Before=672591, After=667898, chg -0.70%
        ip/rtmon:
        add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
        Total: Before=48879, After=48825, chg -0.11%
        tc/tc:
        add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
        Total: Before=351912, After=346661, chg -1.49%
        bridge/bridge:
        add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
        Total: Before=70502, After=70043, chg -0.65%
        misc/lnstat:
        add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
        Total: Before=9960, After=9522, chg -4.40%
        tipc/tipc:
        add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
        Total: Before=79182, After=79138, chg -0.06%

While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.

Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-20 14:35:07 -07:00

2392 lines
59 KiB
C

/*
* ipaddress.c "ip address".
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <fnmatch.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
#include <linux/net_namespace.h>
#include "utils.h"
#include "rt_names.h"
#include "utils.h"
#include "ll_map.h"
#include "ip_common.h"
#include "color.h"
enum {
IPADD_LIST,
IPADD_FLUSH,
IPADD_SAVE,
};
static struct link_filter filter;
static int do_link;
static void usage(void) __attribute__((noreturn));
static void usage(void)
{
if (do_link)
iplink_usage();
fprintf(stderr,
"Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n"
" [ CONFFLAG-LIST ]\n"
" ip address del IFADDR dev IFNAME [mngtmpaddr]\n"
" ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"
" [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"
" ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"
" [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"
" [ label LABEL ] [up] [ vrf NAME ] ]\n"
" ip address {showdump|restore}\n"
"IFADDR := PREFIX | ADDR peer PREFIX\n"
" [ broadcast ADDR ] [ anycast ADDR ]\n"
" [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n"
"SCOPE-ID := [ host | link | global | NUMBER ]\n"
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
"FLAG := [ permanent | dynamic | secondary | primary |\n"
" [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n"
" CONFFLAG-LIST ]\n"
"CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n"
"CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n"
"LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"
"LFT := forever | SECONDS\n"
"TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n"
" bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n"
" gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n"
" nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n"
" hsr | macsec | netdevsim }\n");
exit(-1);
}
static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
{
open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<");
if (flags & IFF_UP && !(flags & IFF_RUNNING))
print_string(PRINT_ANY, NULL,
flags ? "%s," : "%s", "NO-CARRIER");
flags &= ~IFF_RUNNING;
#define _PF(f) if (flags&IFF_##f) { \
flags &= ~IFF_##f ; \
print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); }
_PF(LOOPBACK);
_PF(BROADCAST);
_PF(POINTOPOINT);
_PF(MULTICAST);
_PF(NOARP);
_PF(ALLMULTI);
_PF(PROMISC);
_PF(MASTER);
_PF(SLAVE);
_PF(DEBUG);
_PF(DYNAMIC);
_PF(AUTOMEDIA);
_PF(PORTSEL);
_PF(NOTRAILERS);
_PF(UP);
_PF(LOWER_UP);
_PF(DORMANT);
_PF(ECHO);
#undef _PF
if (flags)
print_hex(PRINT_ANY, NULL, "%x", flags);
if (mdown)
print_string(PRINT_ANY, NULL, ",%s", "M-DOWN");
close_json_array(PRINT_ANY, "> ");
}
static const char *oper_states[] = {
"UNKNOWN", "NOTPRESENT", "DOWN", "LOWERLAYERDOWN",
"TESTING", "DORMANT", "UP"
};
static void print_operstate(FILE *f, __u8 state)
{
if (state >= ARRAY_SIZE(oper_states)) {
if (is_json_context())
print_uint(PRINT_JSON, "operstate_index", NULL, state);
else
print_0xhex(PRINT_FP, NULL, "state %#llx", state);
} else if (brief) {
print_color_string(PRINT_ANY,
oper_state_color(state),
"operstate",
"%-14s ",
oper_states[state]);
} else {
if (is_json_context())
print_string(PRINT_JSON,
"operstate",
NULL, oper_states[state]);
else {
fprintf(f, "state ");
color_fprintf(f, oper_state_color(state),
"%s ", oper_states[state]);
}
}
}
int get_operstate(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(oper_states); i++)
if (strcasecmp(name, oper_states[i]) == 0)
return i;
return -1;
}
static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1])
{
int qlen;
if (tb[IFLA_TXQLEN])
qlen = rta_getattr_u32(tb[IFLA_TXQLEN]);
else {
struct ifreq ifr = {};
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
return;
strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
fprintf(stderr,
"ioctl(SIOCGIFTXQLEN) failed: %s\n",
strerror(errno));
close(s);
return;
}
close(s);
qlen = ifr.ifr_qlen;
}
if (qlen)
print_int(PRINT_ANY, "txqlen", "qlen %d", qlen);
}
static const char *link_modes[] = {
"DEFAULT", "DORMANT"
};
static void print_linkmode(FILE *f, struct rtattr *tb)
{
unsigned int mode = rta_getattr_u8(tb);
if (mode >= ARRAY_SIZE(link_modes))
print_int(PRINT_ANY,
"linkmode_index",
"mode %d ",
mode);
else
print_string(PRINT_ANY,
"linkmode",
"mode %s "
, link_modes[mode]);
}
static char *parse_link_kind(struct rtattr *tb, bool slave)
{
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
int attr = slave ? IFLA_INFO_SLAVE_KIND : IFLA_INFO_KIND;
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
if (linkinfo[attr])
return RTA_DATA(linkinfo[attr]);
return "";
}
static int match_link_kind(struct rtattr **tb, const char *kind, bool slave)
{
if (!tb[IFLA_LINKINFO])
return -1;
return strcmp(parse_link_kind(tb[IFLA_LINKINFO], slave), kind);
}
static void print_linktype(FILE *fp, struct rtattr *tb)
{
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
struct link_util *lu;
struct link_util *slave_lu;
char slave[32];
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
open_json_object("linkinfo");
if (linkinfo[IFLA_INFO_KIND]) {
const char *kind
= rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
print_nl();
print_string(PRINT_ANY, "info_kind", " %s ", kind);
lu = get_link_kind(kind);
if (lu && lu->print_opt) {
struct rtattr *attr[lu->maxattr+1], **data = NULL;
if (linkinfo[IFLA_INFO_DATA]) {
parse_rtattr_nested(attr, lu->maxattr,
linkinfo[IFLA_INFO_DATA]);
data = attr;
}
open_json_object("info_data");
lu->print_opt(lu, fp, data);
close_json_object();
if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
lu->print_xstats) {
open_json_object("info_xstats");
lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
close_json_object();
}
}
}
if (linkinfo[IFLA_INFO_SLAVE_KIND]) {
const char *slave_kind
= rta_getattr_str(linkinfo[IFLA_INFO_SLAVE_KIND]);
print_nl();
print_string(PRINT_ANY,
"info_slave_kind",
" %s_slave ",
slave_kind);
snprintf(slave, sizeof(slave), "%s_slave", slave_kind);
slave_lu = get_link_kind(slave);
if (slave_lu && slave_lu->print_opt) {
struct rtattr *attr[slave_lu->maxattr+1], **data = NULL;
if (linkinfo[IFLA_INFO_SLAVE_DATA]) {
parse_rtattr_nested(attr, slave_lu->maxattr,
linkinfo[IFLA_INFO_SLAVE_DATA]);
data = attr;
}
open_json_object("info_slave_data");
slave_lu->print_opt(slave_lu, fp, data);
close_json_object();
}
}
close_json_object();
}
static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr)
{
struct rtattr *inet6_attr;
struct rtattr *tb[IFLA_INET6_MAX + 1];
inet6_attr = parse_rtattr_one_nested(AF_INET6, af_spec_attr);
if (!inet6_attr)
return;
parse_rtattr_nested(tb, IFLA_INET6_MAX, inet6_attr);
if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
__u8 mode = rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
SPRINT_BUF(b1);
switch (mode) {
case IN6_ADDR_GEN_MODE_EUI64:
print_string(PRINT_ANY,
"inet6_addr_gen_mode",
"addrgenmode %s ",
"eui64");
break;
case IN6_ADDR_GEN_MODE_NONE:
print_string(PRINT_ANY,
"inet6_addr_gen_mode",
"addrgenmode %s ",
"none");
break;
case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
print_string(PRINT_ANY,
"inet6_addr_gen_mode",
"addrgenmode %s ",
"stable_secret");
break;
case IN6_ADDR_GEN_MODE_RANDOM:
print_string(PRINT_ANY,
"inet6_addr_gen_mode",
"addrgenmode %s ",
"random");
break;
default:
snprintf(b1, sizeof(b1), "%#.2hhx", mode);
print_string(PRINT_ANY,
"inet6_addr_gen_mode",
"addrgenmode %s ",
b1);
break;
}
}
}
static void print_vf_stats64(FILE *fp, struct rtattr *vfstats);
static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
{
struct ifla_vf_mac *vf_mac;
struct ifla_vf_tx_rate *vf_tx_rate;
struct rtattr *vf[IFLA_VF_MAX + 1] = {};
SPRINT_BUF(b1);
if (vfinfo->rta_type != IFLA_VF_INFO) {
fprintf(stderr, "BUG: rta type is %d\n", vfinfo->rta_type);
return;
}
parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
print_string(PRINT_FP, NULL, "%s ", _SL_);
print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf);
print_string(PRINT_ANY, "mac", "MAC %s",
ll_addr_n2a((unsigned char *) &vf_mac->mac,
ETH_ALEN, 0, b1, sizeof(b1)));
if (vf[IFLA_VF_VLAN_LIST]) {
struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST];
int rem = RTA_PAYLOAD(vfvlanlist);
open_json_array(PRINT_JSON, "vlan_list");
for (i = RTA_DATA(vfvlanlist);
RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
struct ifla_vf_vlan_info *vf_vlan_info = RTA_DATA(i);
SPRINT_BUF(b2);
open_json_object(NULL);
if (vf_vlan_info->vlan)
print_int(PRINT_ANY,
"vlan",
", vlan %d",
vf_vlan_info->vlan);
if (vf_vlan_info->qos)
print_int(PRINT_ANY,
"qos",
", qos %d",
vf_vlan_info->qos);
if (vf_vlan_info->vlan_proto &&
vf_vlan_info->vlan_proto != htons(ETH_P_8021Q))
print_string(PRINT_ANY,
"protocol",
", vlan protocol %s",
ll_proto_n2a(
vf_vlan_info->vlan_proto,
b2, sizeof(b2)));
close_json_object();
}
close_json_array(PRINT_JSON, NULL);
} else {
struct ifla_vf_vlan *vf_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
if (vf_vlan->vlan)
print_int(PRINT_ANY,
"vlan",
", vlan %d",
vf_vlan->vlan);
if (vf_vlan->qos)
print_int(PRINT_ANY, "qos", ", qos %d", vf_vlan->qos);
}
if (vf_tx_rate->rate)
print_uint(PRINT_ANY,
"tx_rate",
", tx rate %u (Mbps)",
vf_tx_rate->rate);
if (vf[IFLA_VF_RATE]) {
struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
int max_tx = vf_rate->max_tx_rate;
int min_tx = vf_rate->min_tx_rate;
if (is_json_context()) {
open_json_object("rate");
print_uint(PRINT_JSON, "max_tx", NULL, max_tx);
print_uint(PRINT_ANY, "min_tx", NULL, min_tx);
close_json_object();
} else {
if (max_tx)
fprintf(fp, ", max_tx_rate %uMbps", max_tx);
if (min_tx)
fprintf(fp, ", min_tx_rate %uMbps", min_tx);
}
}
if (vf[IFLA_VF_SPOOFCHK]) {
struct ifla_vf_spoofchk *vf_spoofchk =
RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
if (vf_spoofchk->setting != -1)
print_bool(PRINT_ANY,
"spoofchk",
vf_spoofchk->setting ?
", spoof checking on" : ", spoof checking off",
vf_spoofchk->setting);
}
if (vf[IFLA_VF_LINK_STATE]) {
struct ifla_vf_link_state *vf_linkstate =
RTA_DATA(vf[IFLA_VF_LINK_STATE]);
if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
print_string(PRINT_ANY,
"link_state",
", link-state %s",
"auto");
else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
print_string(PRINT_ANY,
"link_state",
", link-state %s",
"enable");
else
print_string(PRINT_ANY,
"link_state",
", link-state %s",
"disable");
}
if (vf[IFLA_VF_TRUST]) {
struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]);
if (vf_trust->setting != -1)
print_bool(PRINT_ANY,
"trust",
vf_trust->setting ? ", trust on" : ", trust off",
vf_trust->setting);
}
if (vf[IFLA_VF_RSS_QUERY_EN]) {
struct ifla_vf_rss_query_en *rss_query =
RTA_DATA(vf[IFLA_VF_RSS_QUERY_EN]);
if (rss_query->setting != -1)
print_bool(PRINT_ANY,
"query_rss_en",
rss_query->setting ? ", query_rss on"
: ", query_rss off",
rss_query->setting);
}
if (vf[IFLA_VF_STATS] && show_stats)
print_vf_stats64(fp, vf[IFLA_VF_STATS]);
}
void print_num(FILE *fp, unsigned int width, uint64_t count)
{
const char *prefix = "kMGTPE";
const unsigned int base = use_iec ? 1024 : 1000;
uint64_t powi = 1;
uint16_t powj = 1;
uint8_t precision = 2;
char buf[64];
if (!human_readable || count < base) {
fprintf(fp, "%-*"PRIu64" ", width, count);
return;
}
/* increase value by a factor of 1000/1024 and print
* if result is something a human can read
*/
for (;;) {
powi *= base;
if (count / base < powi)
break;
if (!prefix[1])
break;
++prefix;
}
/* try to guess a good number of digits for precision */
for (; precision > 0; precision--) {
powj *= 10;
if (count / powi < powj)
break;
}
snprintf(buf, sizeof(buf), "%.*f%c%s", precision,
(double) count / powi, *prefix, use_iec ? "i" : "");
fprintf(fp, "%-*s ", width, buf);
}
static void print_vf_stats64(FILE *fp, struct rtattr *vfstats)
{
struct rtattr *vf[IFLA_VF_STATS_MAX + 1];
if (vfstats->rta_type != IFLA_VF_STATS) {
fprintf(stderr, "BUG: rta type is %d\n", vfstats->rta_type);
return;
}
parse_rtattr_nested(vf, IFLA_VF_STATS_MAX, vfstats);
if (is_json_context()) {
open_json_object("stats");
/* RX stats */
open_json_object("rx");
print_u64(PRINT_JSON, "bytes", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
print_u64(PRINT_JSON, "packets", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
print_u64(PRINT_JSON, "multicast", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
print_u64(PRINT_JSON, "broadcast", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
if (vf[IFLA_VF_STATS_RX_DROPPED])
print_u64(PRINT_JSON, "dropped", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED]));
close_json_object();
/* TX stats */
open_json_object("tx");
print_u64(PRINT_JSON, "tx_bytes", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
print_u64(PRINT_JSON, "tx_packets", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
if (vf[IFLA_VF_STATS_TX_DROPPED])
print_u64(PRINT_JSON, "dropped", NULL,
rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED]));
close_json_object();
close_json_object();
} else {
/* RX stats */
fprintf(fp, "%s", _SL_);
fprintf(fp, " RX: bytes packets mcast bcast ");
if (vf[IFLA_VF_STATS_RX_DROPPED])
fprintf(fp, " dropped ");
fprintf(fp, "%s", _SL_);
fprintf(fp, " ");
print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES]));
print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS]));
print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST]));
print_num(fp, 7, rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST]));
if (vf[IFLA_VF_STATS_RX_DROPPED])
print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_RX_DROPPED]));
/* TX stats */
fprintf(fp, "%s", _SL_);
fprintf(fp, " TX: bytes packets ");
if (vf[IFLA_VF_STATS_TX_DROPPED])
fprintf(fp, " dropped ");
fprintf(fp, "%s", _SL_);
fprintf(fp, " ");
print_num(fp, 10, rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES]));
print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS]));
if (vf[IFLA_VF_STATS_TX_DROPPED])
print_num(fp, 8, rta_getattr_u64(vf[IFLA_VF_STATS_TX_DROPPED]));
}
}
static void __print_link_stats(FILE *fp, struct rtattr *tb[])
{
const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
struct rtnl_link_stats64 _s, *s = &_s;
int ret;
ret = get_rtnl_link_stats_rta(s, tb);
if (ret < 0)
return;
if (is_json_context()) {
open_json_object((ret == sizeof(*s)) ? "stats64" : "stats");
/* RX stats */
open_json_object("rx");
print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
print_u64(PRINT_JSON, "over_errors", NULL, s->rx_over_errors);
print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
if (s->rx_compressed)
print_u64(PRINT_JSON,
"compressed", NULL, s->rx_compressed);
/* RX error stats */
if (show_stats > 1) {
print_u64(PRINT_JSON,
"length_errors",
NULL, s->rx_length_errors);
print_u64(PRINT_JSON,
"crc_errors",
NULL, s->rx_crc_errors);
print_u64(PRINT_JSON,
"frame_errors",
NULL, s->rx_frame_errors);
print_u64(PRINT_JSON,
"fifo_errors",
NULL, s->rx_fifo_errors);
print_u64(PRINT_JSON,
"missed_errors",
NULL, s->rx_missed_errors);
if (s->rx_nohandler)
print_u64(PRINT_JSON,
"nohandler", NULL, s->rx_nohandler);
}
close_json_object();
/* TX stats */
open_json_object("tx");
print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
print_u64(PRINT_JSON,
"carrier_errors",
NULL, s->tx_carrier_errors);
print_u64(PRINT_JSON, "collisions", NULL, s->collisions);
if (s->tx_compressed)
print_u64(PRINT_JSON,
"compressed", NULL, s->tx_compressed);
/* TX error stats */
if (show_stats > 1) {
print_u64(PRINT_JSON,
"aborted_errors",
NULL, s->tx_aborted_errors);
print_u64(PRINT_JSON,
"fifo_errors",
NULL, s->tx_fifo_errors);
print_u64(PRINT_JSON,
"window_errors",
NULL, s->tx_window_errors);
print_u64(PRINT_JSON,
"heartbeat_errors",
NULL, s->tx_heartbeat_errors);
if (carrier_changes)
print_u64(PRINT_JSON, "carrier_changes", NULL,
rta_getattr_u32(carrier_changes));
}
close_json_object();
close_json_object();
} else {
/* RX stats */
fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s",
s->rx_compressed ? "compressed" : "", _SL_);
fprintf(fp, " ");
print_num(fp, 10, s->rx_bytes);
print_num(fp, 8, s->rx_packets);
print_num(fp, 7, s->rx_errors);
print_num(fp, 7, s->rx_dropped);
print_num(fp, 7, s->rx_over_errors);
print_num(fp, 7, s->multicast);
if (s->rx_compressed)
print_num(fp, 7, s->rx_compressed);
/* RX error stats */
if (show_stats > 1) {
fprintf(fp, "%s", _SL_);
fprintf(fp, " RX errors: length crc frame fifo missed%s%s",
s->rx_nohandler ? " nohandler" : "", _SL_);
fprintf(fp, " ");
print_num(fp, 8, s->rx_length_errors);
print_num(fp, 7, s->rx_crc_errors);
print_num(fp, 7, s->rx_frame_errors);
print_num(fp, 7, s->rx_fifo_errors);
print_num(fp, 7, s->rx_missed_errors);
if (s->rx_nohandler)
print_num(fp, 7, s->rx_nohandler);
}
fprintf(fp, "%s", _SL_);
/* TX stats */
fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s",
s->tx_compressed ? "compressed" : "", _SL_);
fprintf(fp, " ");
print_num(fp, 10, s->tx_bytes);
print_num(fp, 8, s->tx_packets);
print_num(fp, 7, s->tx_errors);
print_num(fp, 7, s->tx_dropped);
print_num(fp, 7, s->tx_carrier_errors);
print_num(fp, 7, s->collisions);
if (s->tx_compressed)
print_num(fp, 7, s->tx_compressed);
/* TX error stats */
if (show_stats > 1) {
fprintf(fp, "%s", _SL_);
fprintf(fp, " TX errors: aborted fifo window heartbeat");
if (carrier_changes)
fprintf(fp, " transns");
fprintf(fp, "%s", _SL_);
fprintf(fp, " ");
print_num(fp, 8, s->tx_aborted_errors);
print_num(fp, 7, s->tx_fifo_errors);
print_num(fp, 7, s->tx_window_errors);
print_num(fp, 7, s->tx_heartbeat_errors);
if (carrier_changes)
print_num(fp, 7,
rta_getattr_u32(carrier_changes));
}
}
}
static void print_link_stats(FILE *fp, struct nlmsghdr *n)
{
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr *tb[IFLA_MAX+1];
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi),
n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
__print_link_stats(fp, tb);
print_nl();
}
static int print_linkinfo_brief(FILE *fp, const char *name,
const struct ifinfomsg *ifi,
struct rtattr *tb[])
{
unsigned int m_flag = 0;
m_flag = print_name_and_link("%-16s ", name, tb);
if (tb[IFLA_OPERSTATE])
print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
if (filter.family == AF_PACKET) {
SPRINT_BUF(b1);
if (tb[IFLA_ADDRESS]) {
print_color_string(PRINT_ANY, COLOR_MAC,
"address", "%s ",
ll_addr_n2a(
RTA_DATA(tb[IFLA_ADDRESS]),
RTA_PAYLOAD(tb[IFLA_ADDRESS]),
ifi->ifi_type,
b1, sizeof(b1)));
}
}
if (filter.family == AF_PACKET) {
print_link_flags(fp, ifi->ifi_flags, m_flag);
print_string(PRINT_FP, NULL, "%s", "\n");
}
fflush(fp);
return 0;
}
static const char *link_events[] = {
[IFLA_EVENT_NONE] = "NONE",
[IFLA_EVENT_REBOOT] = "REBOOT",
[IFLA_EVENT_FEATURES] = "FEATURE CHANGE",
[IFLA_EVENT_BONDING_FAILOVER] = "BONDING FAILOVER",
[IFLA_EVENT_NOTIFY_PEERS] = "NOTIFY PEERS",
[IFLA_EVENT_IGMP_RESEND] = "RESEND IGMP",
[IFLA_EVENT_BONDING_OPTIONS] = "BONDING OPTION"
};
static void print_link_event(FILE *f, __u32 event)
{
if (event >= ARRAY_SIZE(link_events))
print_int(PRINT_ANY, "event", "event %d ", event);
else {
if (event)
print_string(PRINT_ANY,
"event", "event %s ",
link_events[event]);
}
}
int print_linkinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = (FILE *)arg;
struct ifinfomsg *ifi = NLMSG_DATA(n);
struct rtattr *tb[IFLA_MAX+1];
int len = n->nlmsg_len;
const char *name;
unsigned int m_flag = 0;
SPRINT_BUF(b1);
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifi));
if (len < 0)
return -1;
if (filter.ifindex && ifi->ifi_index != filter.ifindex)
return -1;
if (filter.up && !(ifi->ifi_flags&IFF_UP))
return -1;
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
name = get_ifname_rta(ifi->ifi_index, tb[IFLA_IFNAME]);
if (!name)
return -1;
if (filter.label)
return 0;
if (tb[IFLA_GROUP]) {
int group = rta_getattr_u32(tb[IFLA_GROUP]);
if (filter.group != -1 && group != filter.group)
return -1;
}
if (tb[IFLA_MASTER]) {
int master = rta_getattr_u32(tb[IFLA_MASTER]);
if (filter.master > 0 && master != filter.master)
return -1;
} else if (filter.master > 0)
return -1;
if (filter.kind && match_link_kind(tb, filter.kind, 0))
return -1;
if (filter.slave_kind && match_link_kind(tb, filter.slave_kind, 1))
return -1;
if (n->nlmsg_type == RTM_DELLINK)
print_bool(PRINT_ANY, "deleted", "Deleted ", true);
if (brief)
return print_linkinfo_brief(fp, name, ifi, tb);
print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index);
m_flag = print_name_and_link("%s: ", name, tb);
print_link_flags(fp, ifi->ifi_flags, m_flag);
if (tb[IFLA_MTU])
print_int(PRINT_ANY,
"mtu", "mtu %u ",
rta_getattr_u32(tb[IFLA_MTU]));
if (tb[IFLA_XDP])
xdp_dump(fp, tb[IFLA_XDP], do_link, false);
if (tb[IFLA_QDISC])
print_string(PRINT_ANY,
"qdisc",
"qdisc %s ",
rta_getattr_str(tb[IFLA_QDISC]));
if (tb[IFLA_MASTER]) {
int master = rta_getattr_u32(tb[IFLA_MASTER]);
print_string(PRINT_ANY,
"master", "master %s ",
ll_index_to_name(master));
}
if (tb[IFLA_OPERSTATE])
print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE]));
if (do_link && tb[IFLA_LINKMODE])
print_linkmode(fp, tb[IFLA_LINKMODE]);
if (tb[IFLA_GROUP]) {
int group = rta_getattr_u32(tb[IFLA_GROUP]);
print_string(PRINT_ANY,
"group",
"group %s ",
rtnl_group_n2a(group, b1, sizeof(b1)));
}
if (filter.showqueue)
print_queuelen(fp, tb);
if (tb[IFLA_EVENT])
print_link_event(fp, rta_getattr_u32(tb[IFLA_EVENT]));
if (!filter.family || filter.family == AF_PACKET || show_details) {
print_nl();
print_string(PRINT_ANY,
"link_type",
" link/%s ",
ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
if (tb[IFLA_ADDRESS]) {
print_color_string(PRINT_ANY,
COLOR_MAC,
"address",
"%s",
ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
RTA_PAYLOAD(tb[IFLA_ADDRESS]),
ifi->ifi_type,
b1, sizeof(b1)));
}
if (tb[IFLA_BROADCAST]) {
if (ifi->ifi_flags&IFF_POINTOPOINT) {
print_string(PRINT_FP, NULL, " peer ", NULL);
print_bool(PRINT_JSON,
"link_pointtopoint", NULL, true);
} else {
print_string(PRINT_FP, NULL, " brd ", NULL);
}
print_color_string(PRINT_ANY,
COLOR_MAC,
"broadcast",
"%s",
ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
RTA_PAYLOAD(tb[IFLA_BROADCAST]),
ifi->ifi_type,
b1, sizeof(b1)));
}
}
if (tb[IFLA_LINK_NETNSID]) {
int id = rta_getattr_u32(tb[IFLA_LINK_NETNSID]);
if (is_json_context()) {
print_int(PRINT_JSON, "link_netnsid", NULL, id);
} else {
if (id >= 0) {
char *name = get_name_from_nsid(id);
if (name)
print_string(PRINT_FP, NULL,
" link-netns %s", name);
else
print_int(PRINT_FP, NULL,
" link-netnsid %d", id);
} else
print_string(PRINT_FP, NULL,
" link-netnsid %s", "unknown");
}
}
if (tb[IFLA_NEW_NETNSID]) {
int id = rta_getattr_u32(tb[IFLA_NEW_NETNSID]);
char *name = get_name_from_nsid(id);
if (name)
print_string(PRINT_FP, NULL, " new-netns %s", name);
else
print_int(PRINT_FP, NULL, " new-netnsid %d", id);
}
if (tb[IFLA_NEW_IFINDEX]) {
int id = rta_getattr_u32(tb[IFLA_NEW_IFINDEX]);
print_int(PRINT_FP, NULL, " new-ifindex %d", id);
}
if (tb[IFLA_PROTO_DOWN]) {
if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
print_bool(PRINT_ANY,
"proto_down", " protodown on ", true);
}
if (show_details) {
if (tb[IFLA_PROMISCUITY])
print_uint(PRINT_ANY,
"promiscuity",
" promiscuity %u ",
rta_getattr_u32(tb[IFLA_PROMISCUITY]));
if (tb[IFLA_MIN_MTU])
print_uint(PRINT_ANY,
"min_mtu", "minmtu %u ",
rta_getattr_u32(tb[IFLA_MIN_MTU]));
if (tb[IFLA_MAX_MTU])
print_uint(PRINT_ANY,
"max_mtu", "maxmtu %u ",
rta_getattr_u32(tb[IFLA_MAX_MTU]));
if (tb[IFLA_LINKINFO])
print_linktype(fp, tb[IFLA_LINKINFO]);
if (do_link && tb[IFLA_AF_SPEC])
print_af_spec(fp, tb[IFLA_AF_SPEC]);
if (tb[IFLA_NUM_TX_QUEUES])
print_uint(PRINT_ANY,
"num_tx_queues",
"numtxqueues %u ",
rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES]));
if (tb[IFLA_NUM_RX_QUEUES])
print_uint(PRINT_ANY,
"num_rx_queues",
"numrxqueues %u ",
rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES]));
if (tb[IFLA_GSO_MAX_SIZE])
print_uint(PRINT_ANY,
"gso_max_size",
"gso_max_size %u ",
rta_getattr_u32(tb[IFLA_GSO_MAX_SIZE]));
if (tb[IFLA_GSO_MAX_SEGS])
print_uint(PRINT_ANY,
"gso_max_segs",
"gso_max_segs %u ",
rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS]));
if (tb[IFLA_PHYS_PORT_NAME])
print_string(PRINT_ANY,
"phys_port_name",
"portname %s ",
rta_getattr_str(tb[IFLA_PHYS_PORT_NAME]));
if (tb[IFLA_PHYS_PORT_ID]) {
print_string(PRINT_ANY,
"phys_port_id",
"portid %s ",
hexstring_n2a(
RTA_DATA(tb[IFLA_PHYS_PORT_ID]),
RTA_PAYLOAD(tb[IFLA_PHYS_PORT_ID]),
b1, sizeof(b1)));
}
if (tb[IFLA_PHYS_SWITCH_ID]) {
print_string(PRINT_ANY,
"phys_switch_id",
"switchid %s ",
hexstring_n2a(RTA_DATA(tb[IFLA_PHYS_SWITCH_ID]),
RTA_PAYLOAD(tb[IFLA_PHYS_SWITCH_ID]),
b1, sizeof(b1)));
}
}
if ((do_link || show_details) && tb[IFLA_IFALIAS]) {
print_string(PRINT_FP, NULL, "%s ", _SL_);
print_string(PRINT_ANY,
"ifalias",
"alias %s",
rta_getattr_str(tb[IFLA_IFALIAS]));
}
if ((do_link || show_details) && tb[IFLA_XDP])
xdp_dump(fp, tb[IFLA_XDP], true, true);
if (do_link && show_stats) {
print_nl();
__print_link_stats(fp, tb);
}
if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) {
struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST];
int rem = RTA_PAYLOAD(vflist);
open_json_array(PRINT_JSON, "vfinfo_list");
for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
open_json_object(NULL);
print_vfinfo(fp, i);
close_json_object();
}
close_json_array(PRINT_JSON, NULL);
}
print_string(PRINT_FP, NULL, "%s", "\n");
fflush(fp);
return 1;
}
static int flush_update(void)
{
/*
* Note that the kernel may delete multiple addresses for one
* delete request (e.g. if ipv4 address promotion is disabled).
* Since a flush operation is really a series of delete requests
* its possible that we may request an address delete that has
* already been done by the kernel. Therefore, ignore EADDRNOTAVAIL
* errors returned from a flush request
*/
if ((rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) &&
(errno != EADDRNOTAVAIL)) {
perror("Failed to send flush request");
return -1;
}
filter.flushp = 0;
return 0;
}
static int set_lifetime(unsigned int *lifetime, char *argv)
{
if (strcmp(argv, "forever") == 0)
*lifetime = INFINITY_LIFE_TIME;
else if (get_u32(lifetime, argv, 0))
return -1;
return 0;
}
static unsigned int get_ifa_flags(struct ifaddrmsg *ifa,
struct rtattr *ifa_flags_attr)
{
return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) :
ifa->ifa_flags;
}
/* Mapping from argument to address flag mask */
static const struct {
const char *name;
unsigned long value;
} ifa_flag_names[] = {
{ "secondary", IFA_F_SECONDARY },
{ "temporary", IFA_F_SECONDARY },
{ "nodad", IFA_F_NODAD },
{ "optimistic", IFA_F_OPTIMISTIC },
{ "dadfailed", IFA_F_DADFAILED },
{ "home", IFA_F_HOMEADDRESS },
{ "deprecated", IFA_F_DEPRECATED },
{ "tentative", IFA_F_TENTATIVE },
{ "permanent", IFA_F_PERMANENT },
{ "mngtmpaddr", IFA_F_MANAGETEMPADDR },
{ "noprefixroute", IFA_F_NOPREFIXROUTE },
{ "autojoin", IFA_F_MCAUTOJOIN },
{ "stable-privacy", IFA_F_STABLE_PRIVACY },
};
static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa,
unsigned int flags)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
unsigned long mask = ifa_flag_names[i].value;
if (mask == IFA_F_PERMANENT) {
if (!(flags & mask))
print_bool(PRINT_ANY,
"dynamic", "dynamic ", true);
} else if (flags & mask) {
if (mask == IFA_F_SECONDARY &&
ifa->ifa_family == AF_INET6) {
print_bool(PRINT_ANY,
"temporary", "temporary ", true);
} else {
print_string(PRINT_FP, NULL,
"%s ", ifa_flag_names[i].name);
print_bool(PRINT_JSON,
ifa_flag_names[i].name, NULL, true);
}
}
flags &= ~mask;
}
if (flags) {
if (is_json_context()) {
SPRINT_BUF(b1);
snprintf(b1, sizeof(b1), "%02x", flags);
print_string(PRINT_JSON, "ifa_flags", NULL, b1);
} else {
fprintf(fp, "flags %02x ", flags);
}
}
}
static int get_filter(const char *arg)
{
bool inv = false;
unsigned int i;
if (arg[0] == '-') {
inv = true;
arg++;
}
/* Special cases */
if (strcmp(arg, "dynamic") == 0) {
inv = !inv;
arg = "permanent";
} else if (strcmp(arg, "primary") == 0) {
inv = !inv;
arg = "secondary";
}
for (i = 0; i < ARRAY_SIZE(ifa_flag_names); i++) {
if (strcmp(arg, ifa_flag_names[i].name))
continue;
if (inv)
filter.flags &= ~ifa_flag_names[i].value;
else
filter.flags |= ifa_flag_names[i].value;
filter.flagmask |= ifa_flag_names[i].value;
return 0;
}
return -1;
}
static int ifa_label_match_rta(int ifindex, const struct rtattr *rta)
{
const char *label;
if (!filter.label)
return 0;
if (rta)
label = RTA_DATA(rta);
else
label = ll_index_to_name(ifindex);
return fnmatch(filter.label, label, 0);
}
int print_addrinfo(struct nlmsghdr *n, void *arg)
{
FILE *fp = arg;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
unsigned int ifa_flags;
struct rtattr *rta_tb[IFA_MAX+1];
SPRINT_BUF(b1);
if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifa));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
return 0;
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa),
n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
ifa_flags = get_ifa_flags(ifa, rta_tb[IFA_FLAGS]);
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
return 0;
if ((filter.flags ^ ifa_flags) & filter.flagmask)
return 0;
if (filter.family && filter.family != ifa->ifa_family)
return 0;
if (ifa_label_match_rta(ifa->ifa_index, rta_tb[IFA_LABEL]))
return 0;
if (inet_addr_match_rta(&filter.pfx, rta_tb[IFA_LOCAL]))
return 0;
if (filter.flushb) {
struct nlmsghdr *fn;
if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
if (flush_update())
return -1;
}
fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp));
memcpy(fn, n, n->nlmsg_len);
fn->nlmsg_type = RTM_DELADDR;
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = ++rth.seq;
filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb;
filter.flushed++;
if (show_stats < 2)
return 0;
}
if (n->nlmsg_type == RTM_DELADDR)
print_bool(PRINT_ANY, "deleted", "Deleted ", true);
if (!brief) {
const char *name;
if (filter.oneline || filter.flushb) {
const char *dev = ll_index_to_name(ifa->ifa_index);
if (is_json_context()) {
print_int(PRINT_JSON,
"index", NULL, ifa->ifa_index);
print_string(PRINT_JSON, "dev", NULL, dev);
} else {
fprintf(fp, "%u: %s", ifa->ifa_index, dev);
}
}
name = family_name(ifa->ifa_family);
if (*name != '?') {
print_string(PRINT_ANY, "family", " %s ", name);
} else {
print_int(PRINT_ANY, "family_index", " family %d ",
ifa->ifa_family);
}
}
if (rta_tb[IFA_LOCAL]) {
print_color_string(PRINT_ANY,
ifa_family_color(ifa->ifa_family),
"local", "%s",
format_host_rta(ifa->ifa_family,
rta_tb[IFA_LOCAL]));
if (rta_tb[IFA_ADDRESS] &&
memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]),
RTA_DATA(rta_tb[IFA_LOCAL]),
ifa->ifa_family == AF_INET ? 4 : 16)) {
print_string(PRINT_FP, NULL, " %s ", "peer");
print_color_string(PRINT_ANY,
ifa_family_color(ifa->ifa_family),
"address",
"%s",
format_host_rta(ifa->ifa_family,
rta_tb[IFA_ADDRESS]));
}
print_int(PRINT_ANY, "prefixlen", "/%d ", ifa->ifa_prefixlen);
if (rta_tb[IFA_RT_PRIORITY])
print_uint(PRINT_ANY, "metric", "metric %u ",
rta_getattr_u32(rta_tb[IFA_RT_PRIORITY]));
}
if (brief)
goto brief_exit;
if (rta_tb[IFA_BROADCAST]) {
print_string(PRINT_FP, NULL, "%s ", "brd");
print_color_string(PRINT_ANY,
ifa_family_color(ifa->ifa_family),
"broadcast",
"%s ",
format_host_rta(ifa->ifa_family,
rta_tb[IFA_BROADCAST]));
}
if (rta_tb[IFA_ANYCAST]) {
print_string(PRINT_FP, NULL, "%s ", "any");
print_color_string(PRINT_ANY,
ifa_family_color(ifa->ifa_family),
"anycast",
"%s ",
format_host_rta(ifa->ifa_family,
rta_tb[IFA_ANYCAST]));
}
print_string(PRINT_ANY,
"scope",
"scope %s ",
rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
print_ifa_flags(fp, ifa, ifa_flags);
if (rta_tb[IFA_LABEL])
print_string(PRINT_ANY,
"label",
"%s",
rta_getattr_str(rta_tb[IFA_LABEL]));
if (rta_tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
print_nl();
print_string(PRINT_FP, NULL, " valid_lft ", NULL);
if (ci->ifa_valid == INFINITY_LIFE_TIME) {
print_uint(PRINT_JSON,
"valid_life_time",
NULL, INFINITY_LIFE_TIME);
print_string(PRINT_FP, NULL, "%s", "forever");
} else {
print_uint(PRINT_ANY,
"valid_life_time", "%usec", ci->ifa_valid);
}
print_string(PRINT_FP, NULL, " preferred_lft ", NULL);
if (ci->ifa_prefered == INFINITY_LIFE_TIME) {
print_uint(PRINT_JSON,
"preferred_life_time",
NULL, INFINITY_LIFE_TIME);
print_string(PRINT_FP, NULL, "%s", "forever");
} else {
if (ifa_flags & IFA_F_DEPRECATED)
print_int(PRINT_ANY,
"preferred_life_time",
"%dsec",
ci->ifa_prefered);
else
print_uint(PRINT_ANY,
"preferred_life_time",
"%usec",
ci->ifa_prefered);
}
}
print_string(PRINT_FP, NULL, "%s", "\n");
brief_exit:
fflush(fp);
return 0;
}
static int print_selected_addrinfo(struct ifinfomsg *ifi,
struct nlmsg_list *ainfo, FILE *fp)
{
open_json_array(PRINT_JSON, "addr_info");
for ( ; ainfo ; ainfo = ainfo->next) {
struct nlmsghdr *n = &ainfo->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
if (n->nlmsg_type != RTM_NEWADDR)
continue;
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifa)))
return -1;
if (ifa->ifa_index != ifi->ifi_index ||
(filter.family && filter.family != ifa->ifa_family))
continue;
if (filter.up && !(ifi->ifi_flags&IFF_UP))
continue;
open_json_object(NULL);
print_addrinfo(n, fp);
close_json_object();
}
close_json_array(PRINT_JSON, NULL);
if (brief) {
print_string(PRINT_FP, NULL, "%s", "\n");
fflush(fp);
}
return 0;
}
static int store_nlmsg(struct nlmsghdr *n, void *arg)
{
struct nlmsg_chain *lchain = (struct nlmsg_chain *)arg;
struct nlmsg_list *h;
h = malloc(n->nlmsg_len+sizeof(void *));
if (h == NULL)
return -1;
memcpy(&h->h, n, n->nlmsg_len);
h->next = NULL;
if (lchain->tail)
lchain->tail->next = h;
else
lchain->head = h;
lchain->tail = h;
ll_remember_index(n, NULL);
return 0;
}
static __u32 ipadd_dump_magic = 0x47361222;
static int ipadd_save_prep(void)
{
int ret;
if (isatty(STDOUT_FILENO)) {
fprintf(stderr, "Not sending a binary stream to stdout\n");
return -1;
}
ret = write(STDOUT_FILENO, &ipadd_dump_magic, sizeof(ipadd_dump_magic));
if (ret != sizeof(ipadd_dump_magic)) {
fprintf(stderr, "Can't write magic to dump file\n");
return -1;
}
return 0;
}
static int ipadd_dump_check_magic(void)
{
int ret;
__u32 magic = 0;
if (isatty(STDIN_FILENO)) {
fprintf(stderr, "Can't restore address dump from a terminal\n");
return -1;
}
ret = fread(&magic, sizeof(magic), 1, stdin);
if (magic != ipadd_dump_magic) {
fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic);
return -1;
}
return 0;
}
static int save_nlmsg(struct nlmsghdr *n, void *arg)
{
int ret;
ret = write(STDOUT_FILENO, n, n->nlmsg_len);
if ((ret > 0) && (ret != n->nlmsg_len)) {
fprintf(stderr, "Short write while saving nlmsg\n");
ret = -EIO;
}
return ret == n->nlmsg_len ? 0 : ret;
}
static int show_handler(struct rtnl_ctrl_data *ctrl,
struct nlmsghdr *n, void *arg)
{
struct ifaddrmsg *ifa = NLMSG_DATA(n);
open_json_object(NULL);
print_int(PRINT_ANY, "index", "if%d:\n", ifa->ifa_index);
print_addrinfo(n, stdout);
close_json_object();
return 0;
}
static int ipaddr_showdump(void)
{
int err;
if (ipadd_dump_check_magic())
exit(-1);
new_json_obj(json);
open_json_object(NULL);
open_json_array(PRINT_JSON, "addr_info");
err = rtnl_from_file(stdin, &show_handler, NULL);
close_json_array(PRINT_JSON, NULL);
close_json_object();
delete_json_obj();
exit(err);
}
static int restore_handler(struct rtnl_ctrl_data *ctrl,
struct nlmsghdr *n, void *arg)
{
int ret;
n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
ll_init_map(&rth);
ret = rtnl_talk(&rth, n, NULL);
if ((ret < 0) && (errno == EEXIST))
ret = 0;
return ret;
}
static int ipaddr_restore(void)
{
if (ipadd_dump_check_magic())
exit(-1);
exit(rtnl_from_file(stdin, &restore_handler, NULL));
}
void free_nlmsg_chain(struct nlmsg_chain *info)
{
struct nlmsg_list *l, *n;
for (l = info->head; l; l = n) {
n = l->next;
free(l);
}
}
static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo)
{
struct nlmsg_list *l, **lp;
lp = &linfo->head;
while ((l = *lp) != NULL) {
int ok = 0;
int missing_net_address = 1;
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
struct nlmsg_list *a;
for (a = ainfo->head; a; a = a->next) {
struct nlmsghdr *n = &a->h;
struct ifaddrmsg *ifa = NLMSG_DATA(n);
struct rtattr *tb[IFA_MAX + 1];
unsigned int ifa_flags;
if (ifa->ifa_index != ifi->ifi_index)
continue;
missing_net_address = 0;
if (filter.family && filter.family != ifa->ifa_family)
continue;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
continue;
parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
ifa_flags = get_ifa_flags(ifa, tb[IFA_FLAGS]);
if ((filter.flags ^ ifa_flags) & filter.flagmask)
continue;
if (ifa_label_match_rta(ifa->ifa_index, tb[IFA_LABEL]))
continue;
if (!tb[IFA_LOCAL])
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (inet_addr_match_rta(&filter.pfx, tb[IFA_LOCAL]))
continue;
ok = 1;
break;
}
if (missing_net_address &&
(filter.family == AF_UNSPEC || filter.family == AF_PACKET))
ok = 1;
if (!ok) {
*lp = l->next;
free(l);
} else
lp = &l->next;
}
}
static int ipaddr_dump_filter(struct nlmsghdr *nlh, int reqlen)
{
struct ifaddrmsg *ifa = NLMSG_DATA(nlh);
ifa->ifa_index = filter.ifindex;
return 0;
}
static int ipaddr_flush(void)
{
int round = 0;
char flushb[4096-512];
filter.flushb = flushb;
filter.flushp = 0;
filter.flushe = sizeof(flushb);
while ((max_flush_loops == 0) || (round < max_flush_loops)) {
if (rtnl_addrdump_req(&rth, filter.family,
ipaddr_dump_filter) < 0) {
perror("Cannot send dump request");
exit(1);
}
filter.flushed = 0;
if (rtnl_dump_filter_nc(&rth, print_addrinfo,
stdout, NLM_F_DUMP_INTR) < 0) {
fprintf(stderr, "Flush terminated\n");
exit(1);
}
if (filter.flushed == 0) {
flush_done:
if (show_stats) {
if (round == 0)
printf("Nothing to flush.\n");
else
printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
}
fflush(stdout);
return 0;
}
round++;
if (flush_update() < 0)
return 1;
if (show_stats) {
printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
fflush(stdout);
}
/* If we are flushing, and specifying primary, then we
* want to flush only a single round. Otherwise, we'll
* start flushing secondaries that were promoted to
* primaries.
*/
if (!(filter.flags & IFA_F_SECONDARY) && (filter.flagmask & IFA_F_SECONDARY))
goto flush_done;
}
fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops);
fflush(stderr);
return 1;
}
static int iplink_filter_req(struct nlmsghdr *nlh, int reqlen)
{
int err;
err = addattr32(nlh, reqlen, IFLA_EXT_MASK, RTEXT_FILTER_VF);
if (err)
return err;
if (filter.master) {
err = addattr32(nlh, reqlen, IFLA_MASTER, filter.master);
if (err)
return err;
}
if (filter.kind) {
struct rtattr *linkinfo;
linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
err = addattr_l(nlh, reqlen, IFLA_INFO_KIND, filter.kind,
strlen(filter.kind));
if (err)
return err;
addattr_nest_end(nlh, linkinfo);
}
return 0;
}
static int ipaddr_link_get(int index, struct nlmsg_chain *linfo)
{
struct iplink_req req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.n.nlmsg_flags = NLM_F_REQUEST,
.n.nlmsg_type = RTM_GETLINK,
.i.ifi_family = filter.family,
.i.ifi_index = index,
};
__u32 filt_mask = RTEXT_FILTER_VF;
struct nlmsghdr *answer;
if (!show_stats)
filt_mask |= RTEXT_FILTER_SKIP_STATS;
addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
if (rtnl_talk(&rth, &req.n, &answer) < 0) {
perror("Cannot send link request");
return 1;
}
if (store_nlmsg(answer, linfo) < 0) {
fprintf(stderr, "Failed to process link information\n");
return 1;
}
return 0;
}
/* fills in linfo with link data and optionally ainfo with address info
* caller can walk lists as desired and must call free_nlmsg_chain for
* both when done
*/
int ip_link_list(req_filter_fn_t filter_fn, struct nlmsg_chain *linfo)
{
if (rtnl_linkdump_req_filter_fn(&rth, preferred_family,
filter_fn) < 0) {
perror("Cannot send dump request");
return 1;
}
if (rtnl_dump_filter(&rth, store_nlmsg, linfo) < 0) {
fprintf(stderr, "Dump terminated\n");
return 1;
}
return 0;
}
static int ip_addr_list(struct nlmsg_chain *ainfo)
{
if (rtnl_addrdump_req(&rth, filter.family, ipaddr_dump_filter) < 0) {
perror("Cannot send dump request");
return 1;
}
if (rtnl_dump_filter(&rth, store_nlmsg, ainfo) < 0) {
fprintf(stderr, "Dump terminated\n");
return 1;
}
return 0;
}
static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
{
struct nlmsg_chain linfo = { NULL, NULL};
struct nlmsg_chain _ainfo = { NULL, NULL}, *ainfo = &_ainfo;
struct nlmsg_list *l;
char *filter_dev = NULL;
int no_link = 0;
ipaddr_reset_filter(oneline, 0);
filter.showqueue = 1;
filter.family = preferred_family;
filter.group = -1;
if (action == IPADD_FLUSH) {
if (argc <= 0) {
fprintf(stderr, "Flush requires arguments.\n");
return -1;
}
if (filter.family == AF_PACKET) {
fprintf(stderr, "Cannot flush link addresses.\n");
return -1;
}
}
while (argc > 0) {
if (strcmp(*argv, "to") == 0) {
NEXT_ARG();
if (get_prefix(&filter.pfx, *argv, filter.family))
invarg("invalid \"to\"\n", *argv);
if (filter.family == AF_UNSPEC)
filter.family = filter.pfx.family;
} else if (strcmp(*argv, "scope") == 0) {
unsigned int scope = 0;
NEXT_ARG();
filter.scopemask = -1;
if (rtnl_rtscope_a2n(&scope, *argv)) {
if (strcmp(*argv, "all") != 0)
invarg("invalid \"scope\"\n", *argv);
scope = RT_SCOPE_NOWHERE;
filter.scopemask = 0;
}
filter.scope = scope;
} else if (strcmp(*argv, "up") == 0) {
filter.up = 1;
} else if (get_filter(*argv) == 0) {
} else if (strcmp(*argv, "label") == 0) {
NEXT_ARG();
filter.label = *argv;
} else if (strcmp(*argv, "group") == 0) {
NEXT_ARG();
if (rtnl_group_a2n(&filter.group, *argv))
invarg("Invalid \"group\" value\n", *argv);
} else if (strcmp(*argv, "master") == 0) {
int ifindex;
NEXT_ARG();
ifindex = ll_name_to_index(*argv);
if (!ifindex)
invarg("Device does not exist\n", *argv);
filter.master = ifindex;
} else if (strcmp(*argv, "vrf") == 0) {
int ifindex;
NEXT_ARG();
ifindex = ll_name_to_index(*argv);
if (!ifindex)
invarg("Not a valid VRF name\n", *argv);
if (!name_is_vrf(*argv))
invarg("Not a valid VRF name\n", *argv);
filter.master = ifindex;
} else if (strcmp(*argv, "type") == 0) {
int soff;
NEXT_ARG();
soff = strlen(*argv) - strlen("_slave");
if (!strcmp(*argv + soff, "_slave")) {
(*argv)[soff] = '\0';
filter.slave_kind = *argv;
} else {
filter.kind = *argv;
}
} else {
if (strcmp(*argv, "dev") == 0)
NEXT_ARG();
else if (matches(*argv, "help") == 0)
usage();
if (filter_dev)
duparg2("dev", *argv);
filter_dev = *argv;
}
argv++; argc--;
}
if (filter_dev) {
filter.ifindex = ll_name_to_index(filter_dev);
if (filter.ifindex <= 0) {
fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev);
return -1;
}
}
if (action == IPADD_FLUSH)
return ipaddr_flush();
if (action == IPADD_SAVE) {
if (ipadd_save_prep())
exit(1);
if (rtnl_addrdump_req(&rth, preferred_family,
ipaddr_dump_filter) < 0) {
perror("Cannot send dump request");
exit(1);
}
if (rtnl_dump_filter(&rth, save_nlmsg, stdout) < 0) {
fprintf(stderr, "Save terminated\n");
exit(1);
}
exit(0);
}
/*
* Initialize a json_writer and open an array object
* if -json was specified.
*/
new_json_obj(json);
/*
* If only filter_dev present and none of the other
* link filters are present, use RTM_GETLINK to get
* the link device
*/
if (filter_dev && filter.group == -1 && do_link == 1) {
if (iplink_get(filter_dev, RTEXT_FILTER_VF) < 0) {
perror("Cannot send link get request");
delete_json_obj();
exit(1);
}
delete_json_obj();
goto out;
}
if (filter.ifindex) {
if (ipaddr_link_get(filter.ifindex, &linfo) != 0)
goto out;
} else {
if (ip_link_list(iplink_filter_req, &linfo) != 0)
goto out;
}
if (filter.family != AF_PACKET) {
if (filter.oneline)
no_link = 1;
if (ip_addr_list(ainfo) != 0)
goto out;
ipaddr_filter(&linfo, ainfo);
}
for (l = linfo.head; l; l = l->next) {
struct nlmsghdr *n = &l->h;
struct ifinfomsg *ifi = NLMSG_DATA(n);
int res = 0;
open_json_object(NULL);
if (brief || !no_link)
res = print_linkinfo(n, stdout);
if (res >= 0 && filter.family != AF_PACKET)
print_selected_addrinfo(ifi, ainfo->head, stdout);
if (res > 0 && !do_link && show_stats)
print_link_stats(stdout, n);
close_json_object();
}
fflush(stdout);
out:
free_nlmsg_chain(ainfo);
free_nlmsg_chain(&linfo);
delete_json_obj();
return 0;
}
static void
ipaddr_loop_each_vf(struct rtattr *tb[], int vfnum, int *min, int *max)
{
struct rtattr *vflist = tb[IFLA_VFINFO_LIST];
struct rtattr *i, *vf[IFLA_VF_MAX+1];
struct ifla_vf_rate *vf_rate;
int rem;
rem = RTA_PAYLOAD(vflist);
for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
parse_rtattr_nested(vf, IFLA_VF_MAX, i);
if (!vf[IFLA_VF_RATE]) {
fprintf(stderr, "VF min/max rate API not supported\n");
exit(1);
}
vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
if (vf_rate->vf == vfnum) {
*min = vf_rate->min_tx_rate;
*max = vf_rate->max_tx_rate;
return;
}
}
fprintf(stderr, "Cannot find VF %d\n", vfnum);
exit(1);
}
void ipaddr_get_vf_rate(int vfnum, int *min, int *max, const char *dev)
{
struct nlmsg_chain linfo = { NULL, NULL};
struct rtattr *tb[IFLA_MAX+1];
struct ifinfomsg *ifi;
struct nlmsg_list *l;
struct nlmsghdr *n;
int idx, len;
idx = ll_name_to_index(dev);
if (idx == 0) {
fprintf(stderr, "Device %s does not exist\n", dev);
exit(1);
}
if (rtnl_linkdump_req(&rth, AF_UNSPEC) < 0) {
perror("Cannot send dump request");
exit(1);
}
if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
fprintf(stderr, "Dump terminated\n");
exit(1);
}
for (l = linfo.head; l; l = l->next) {
n = &l->h;
ifi = NLMSG_DATA(n);
len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
if (len < 0 || (idx && idx != ifi->ifi_index))
continue;
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
if ((tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF])) {
ipaddr_loop_each_vf(tb, vfnum, min, max);
return;
}
}
}
int ipaddr_list_link(int argc, char **argv)
{
preferred_family = AF_PACKET;
do_link = 1;
return ipaddr_list_flush_or_save(argc, argv, IPADD_LIST);
}
void ipaddr_reset_filter(int oneline, int ifindex)
{
memset(&filter, 0, sizeof(filter));
filter.oneline = oneline;
filter.ifindex = ifindex;
}
static int default_scope(inet_prefix *lcl)
{
if (lcl->family == AF_INET) {
if (lcl->bytelen >= 1 && *(__u8 *)&lcl->data == 127)
return RT_SCOPE_HOST;
}
return 0;
}
static bool ipaddr_is_multicast(inet_prefix *a)
{
if (a->family == AF_INET)
return IN_MULTICAST(ntohl(a->data[0]));
else if (a->family == AF_INET6)
return IN6_IS_ADDR_MULTICAST(a->data);
else
return false;
}
static bool is_valid_label(const char *dev, const char *label)
{
size_t len = strlen(dev);
if (strncmp(label, dev, len) != 0)
return false;
return label[len] == '\0' || label[len] == ':';
}
static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
{
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[256];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
.n.nlmsg_type = cmd,
.ifa.ifa_family = preferred_family,
};
char *d = NULL;
char *l = NULL;
char *lcl_arg = NULL;
char *valid_lftp = NULL;
char *preferred_lftp = NULL;
inet_prefix lcl = {};
inet_prefix peer;
int local_len = 0;
int peer_len = 0;
int brd_len = 0;
int any_len = 0;
int scoped = 0;
__u32 preferred_lft = INFINITY_LIFE_TIME;
__u32 valid_lft = INFINITY_LIFE_TIME;
unsigned int ifa_flags = 0;
while (argc > 0) {
if (strcmp(*argv, "peer") == 0 ||
strcmp(*argv, "remote") == 0) {
NEXT_ARG();
if (peer_len)
duparg("peer", *argv);
get_prefix(&peer, *argv, req.ifa.ifa_family);
peer_len = peer.bytelen;
if (req.ifa.ifa_family == AF_UNSPEC)
req.ifa.ifa_family = peer.family;
addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
req.ifa.ifa_prefixlen = peer.bitlen;
} else if (matches(*argv, "broadcast") == 0 ||
strcmp(*argv, "brd") == 0) {
inet_prefix addr;
NEXT_ARG();
if (brd_len)
duparg("broadcast", *argv);
if (strcmp(*argv, "+") == 0)
brd_len = -1;
else if (strcmp(*argv, "-") == 0)
brd_len = -2;
else {
get_addr(&addr, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC)
req.ifa.ifa_family = addr.family;
addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
brd_len = addr.bytelen;
}
} else if (strcmp(*argv, "anycast") == 0) {
inet_prefix addr;
NEXT_ARG();
if (any_len)
duparg("anycast", *argv);
get_addr(&addr, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC)
req.ifa.ifa_family = addr.family;
addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
any_len = addr.bytelen;
} else if (strcmp(*argv, "scope") == 0) {
unsigned int scope = 0;
NEXT_ARG();
if (rtnl_rtscope_a2n(&scope, *argv))
invarg("invalid scope value.", *argv);
req.ifa.ifa_scope = scope;
scoped = 1;
} else if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
d = *argv;
} else if (strcmp(*argv, "label") == 0) {
NEXT_ARG();
l = *argv;
addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
} else if (matches(*argv, "metric") == 0 ||
matches(*argv, "priority") == 0 ||
matches(*argv, "preference") == 0) {
__u32 metric;
NEXT_ARG();
if (get_u32(&metric, *argv, 0))
invarg("\"metric\" value is invalid\n", *argv);
addattr32(&req.n, sizeof(req), IFA_RT_PRIORITY, metric);
} else if (matches(*argv, "valid_lft") == 0) {
if (valid_lftp)
duparg("valid_lft", *argv);
NEXT_ARG();
valid_lftp = *argv;
if (set_lifetime(&valid_lft, *argv))
invarg("valid_lft value", *argv);
} else if (matches(*argv, "preferred_lft") == 0) {
if (preferred_lftp)
duparg("preferred_lft", *argv);
NEXT_ARG();
preferred_lftp = *argv;
if (set_lifetime(&preferred_lft, *argv))
invarg("preferred_lft value", *argv);
} else if (strcmp(*argv, "home") == 0) {
ifa_flags |= IFA_F_HOMEADDRESS;
} else if (strcmp(*argv, "nodad") == 0) {
ifa_flags |= IFA_F_NODAD;
} else if (strcmp(*argv, "mngtmpaddr") == 0) {
ifa_flags |= IFA_F_MANAGETEMPADDR;
} else if (strcmp(*argv, "noprefixroute") == 0) {
ifa_flags |= IFA_F_NOPREFIXROUTE;
} else if (strcmp(*argv, "autojoin") == 0) {
ifa_flags |= IFA_F_MCAUTOJOIN;
} else {
if (strcmp(*argv, "local") == 0)
NEXT_ARG();
if (matches(*argv, "help") == 0)
usage();
if (local_len)
duparg2("local", *argv);
lcl_arg = *argv;
get_prefix(&lcl, *argv, req.ifa.ifa_family);
if (req.ifa.ifa_family == AF_UNSPEC)
req.ifa.ifa_family = lcl.family;
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
local_len = lcl.bytelen;
}
argc--; argv++;
}
if (ifa_flags <= 0xff)
req.ifa.ifa_flags = ifa_flags;
else
addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags);
if (d == NULL) {
fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
return -1;
}
if (l && !is_valid_label(d, l)) {
fprintf(stderr,
"\"label\" (%s) must match \"dev\" (%s) or be prefixed by \"dev\" with a colon.\n",
l, d);
return -1;
}
if (peer_len == 0 && local_len) {
if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
fprintf(stderr,
"Warning: Executing wildcard deletion to stay compatible with old scripts.\n"
" Explicitly specify the prefix length (%s/%d) to avoid this warning.\n"
" This special behaviour is likely to disappear in further releases,\n"
" fix your scripts!\n", lcl_arg, local_len*8);
} else {
peer = lcl;
addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
}
}
if (req.ifa.ifa_prefixlen == 0)
req.ifa.ifa_prefixlen = lcl.bitlen;
if (brd_len < 0 && cmd != RTM_DELADDR) {
inet_prefix brd;
int i;
if (req.ifa.ifa_family != AF_INET) {
fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
return -1;
}
brd = peer;
if (brd.bitlen <= 30) {
for (i = 31; i >= brd.bitlen; i--) {
if (brd_len == -1)
brd.data[0] |= htonl(1<<(31-i));
else
brd.data[0] &= ~htonl(1<<(31-i));
}
addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
brd_len = brd.bytelen;
}
}
if (!scoped && cmd != RTM_DELADDR)
req.ifa.ifa_scope = default_scope(&lcl);
req.ifa.ifa_index = ll_name_to_index(d);
if (!req.ifa.ifa_index)
return nodev(d);
if (valid_lftp || preferred_lftp) {
struct ifa_cacheinfo cinfo = {};
if (!valid_lft) {
fprintf(stderr, "valid_lft is zero\n");
return -1;
}
if (valid_lft < preferred_lft) {
fprintf(stderr, "preferred_lft is greater than valid_lft\n");
return -1;
}
cinfo.ifa_prefered = preferred_lft;
cinfo.ifa_valid = valid_lft;
addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
sizeof(cinfo));
}
if ((ifa_flags & IFA_F_MCAUTOJOIN) && !ipaddr_is_multicast(&lcl)) {
fprintf(stderr, "autojoin needs multicast address\n");
return -1;
}
if (rtnl_talk(&rth, &req.n, NULL) < 0)
return -2;
return 0;
}
int do_ipaddr(int argc, char **argv)
{
if (argc < 1)
return ipaddr_list_flush_or_save(0, NULL, IPADD_LIST);
if (matches(*argv, "add") == 0)
return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
if (matches(*argv, "change") == 0 ||
strcmp(*argv, "chg") == 0)
return ipaddr_modify(RTM_NEWADDR, NLM_F_REPLACE, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_REPLACE, argc-1, argv+1);
if (matches(*argv, "delete") == 0)
return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1);
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_LIST);
if (matches(*argv, "flush") == 0)
return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_FLUSH);
if (matches(*argv, "save") == 0)
return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_SAVE);
if (matches(*argv, "showdump") == 0)
return ipaddr_showdump();
if (matches(*argv, "restore") == 0)
return ipaddr_restore();
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv);
exit(-1);
}