mirror of
https://git.proxmox.com/git/mirror_iproute2
synced 2025-10-17 16:24:14 +00:00

Not sure why this was limited to ip-link before. It is semantically equal to the 'master' keyword, which is not restricted at all. The man page and help text adjustments include the 'master' keyword as well since that is also supported but wasn't documented before. Cc: Vadim Kochan <vadim4j@gmail.com> Signed-off-by: Phil Sutter <phil@nwl.cc>
2067 lines
52 KiB
C
2067 lines
52 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 <syslog.h>
|
|
#include <inttypes.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.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 "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
|
|
{
|
|
int ifindex;
|
|
int family;
|
|
int oneline;
|
|
int showqueue;
|
|
inet_prefix pfx;
|
|
int scope, scopemask;
|
|
int flags, flagmask;
|
|
int up;
|
|
char *label;
|
|
int flushed;
|
|
char *flushb;
|
|
int flushp;
|
|
int flushe;
|
|
int group;
|
|
int master;
|
|
char *kind;
|
|
} 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");
|
|
fprintf(stderr, " [ CONFFLAG-LIST ]\n");
|
|
fprintf(stderr, " ip address del IFADDR dev IFNAME [mngtmpaddr]\n");
|
|
fprintf(stderr, " ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n");
|
|
fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n");
|
|
fprintf(stderr, " ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n");
|
|
fprintf(stderr, " [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n");
|
|
fprintf(stderr, " [ label LABEL ] [up] ]\n");
|
|
fprintf(stderr, " ip address {showdump|restore}\n");
|
|
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
|
|
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
|
|
fprintf(stderr, " [ label IFNAME ] [ scope SCOPE-ID ]\n");
|
|
fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
|
|
fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
|
|
fprintf(stderr, "FLAG := [ permanent | dynamic | secondary | primary |\n");
|
|
fprintf(stderr, " [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n");
|
|
fprintf(stderr, " CONFFLAG-LIST ]\n");
|
|
fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
|
|
fprintf(stderr, "CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n");
|
|
fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
|
|
fprintf(stderr, "LFT := forever | SECONDS\n");
|
|
fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
|
|
fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n");
|
|
fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n");
|
|
fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf }\n");
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
|
|
{
|
|
fprintf(fp, "<");
|
|
if (flags & IFF_UP && !(flags & IFF_RUNNING))
|
|
fprintf(fp, "NO-CARRIER%s", flags ? "," : "");
|
|
flags &= ~IFF_RUNNING;
|
|
#define _PF(f) if (flags&IFF_##f) { \
|
|
flags &= ~IFF_##f ; \
|
|
fprintf(fp, #f "%s", flags ? "," : ""); }
|
|
_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)
|
|
fprintf(fp, "%x", flags);
|
|
if (mdown)
|
|
fprintf(fp, ",M-DOWN");
|
|
fprintf(fp, "> ");
|
|
}
|
|
|
|
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)) {
|
|
fprintf(f, "state %#x ", state);
|
|
} else if (brief) {
|
|
color_fprintf(f, oper_state_color(state),
|
|
"%-14s ", 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 = *(int *)RTA_DATA(tb[IFLA_TXQLEN]);
|
|
else {
|
|
struct ifreq ifr;
|
|
int s = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (s < 0)
|
|
return;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
strcpy(ifr.ifr_name, rta_getattr_str(tb[IFLA_IFNAME]));
|
|
if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
|
|
fprintf(f, "ioctl(SIOCGIFTXQLEN) failed: %s\n", strerror(errno));
|
|
close(s);
|
|
return;
|
|
}
|
|
close(s);
|
|
qlen = ifr.ifr_qlen;
|
|
}
|
|
if (qlen)
|
|
fprintf(f, "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))
|
|
fprintf(f, "mode %d ", mode);
|
|
else
|
|
fprintf(f, "mode %s ", link_modes[mode]);
|
|
}
|
|
|
|
static char *parse_link_kind(struct rtattr *tb)
|
|
{
|
|
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
|
|
|
|
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
|
|
|
|
if (linkinfo[IFLA_INFO_KIND])
|
|
return RTA_DATA(linkinfo[IFLA_INFO_KIND]);
|
|
|
|
return "";
|
|
}
|
|
|
|
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 *kind;
|
|
char *slave_kind;
|
|
|
|
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb);
|
|
|
|
if (linkinfo[IFLA_INFO_KIND]) {
|
|
kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]);
|
|
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " %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;
|
|
}
|
|
lu->print_opt(lu, fp, data);
|
|
|
|
if (linkinfo[IFLA_INFO_XSTATS] && show_stats &&
|
|
lu->print_xstats)
|
|
lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]);
|
|
}
|
|
}
|
|
|
|
if (linkinfo[IFLA_INFO_SLAVE_KIND]) {
|
|
slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
|
|
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " %s_slave ", slave_kind);
|
|
|
|
slave_lu = get_link_slave_kind(slave_kind);
|
|
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;
|
|
}
|
|
slave_lu->print_opt(slave_lu, fp, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
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]);
|
|
|
|
switch (mode) {
|
|
case IN6_ADDR_GEN_MODE_EUI64:
|
|
fprintf(fp, "addrgenmode eui64 ");
|
|
break;
|
|
case IN6_ADDR_GEN_MODE_NONE:
|
|
fprintf(fp, "addrgenmode none ");
|
|
break;
|
|
case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
|
|
fprintf(fp, "addrgenmode stable_secret ");
|
|
break;
|
|
case IN6_ADDR_GEN_MODE_RANDOM:
|
|
fprintf(fp, "addrgenmode random ");
|
|
break;
|
|
default:
|
|
fprintf(fp, "addrgenmode %#.2hhx ", mode);
|
|
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_vlan *vf_vlan;
|
|
struct ifla_vf_tx_rate *vf_tx_rate;
|
|
struct ifla_vf_spoofchk *vf_spoofchk;
|
|
struct ifla_vf_link_state *vf_linkstate;
|
|
struct rtattr *vf[IFLA_VF_MAX + 1] = {};
|
|
struct rtattr *tmp;
|
|
|
|
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_vlan = RTA_DATA(vf[IFLA_VF_VLAN]);
|
|
vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
|
|
|
|
/* Check if the spoof checking vf info type is supported by
|
|
* this kernel.
|
|
*/
|
|
tmp = (struct rtattr *)((char *)vf[IFLA_VF_TX_RATE] +
|
|
vf[IFLA_VF_TX_RATE]->rta_len);
|
|
|
|
if (tmp->rta_type != IFLA_VF_SPOOFCHK)
|
|
vf_spoofchk = NULL;
|
|
else
|
|
vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]);
|
|
|
|
if (vf_spoofchk) {
|
|
/* Check if the link state vf info type is supported by
|
|
* this kernel.
|
|
*/
|
|
tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] +
|
|
vf[IFLA_VF_SPOOFCHK]->rta_len);
|
|
|
|
if (tmp->rta_type != IFLA_VF_LINK_STATE)
|
|
vf_linkstate = NULL;
|
|
else
|
|
vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]);
|
|
} else
|
|
vf_linkstate = NULL;
|
|
|
|
fprintf(fp, "%s vf %d MAC %s", _SL_, vf_mac->vf,
|
|
ll_addr_n2a((unsigned char *)&vf_mac->mac,
|
|
ETH_ALEN, 0, b1, sizeof(b1)));
|
|
if (vf_vlan->vlan)
|
|
fprintf(fp, ", vlan %d", vf_vlan->vlan);
|
|
if (vf_vlan->qos)
|
|
fprintf(fp, ", qos %d", vf_vlan->qos);
|
|
if (vf_tx_rate->rate)
|
|
fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate);
|
|
|
|
if (vf[IFLA_VF_RATE]) {
|
|
struct ifla_vf_rate *vf_rate = RTA_DATA(vf[IFLA_VF_RATE]);
|
|
|
|
if (vf_rate->max_tx_rate)
|
|
fprintf(fp, ", max_tx_rate %dMbps", vf_rate->max_tx_rate);
|
|
if (vf_rate->min_tx_rate)
|
|
fprintf(fp, ", min_tx_rate %dMbps", vf_rate->min_tx_rate);
|
|
}
|
|
|
|
if (vf_spoofchk && vf_spoofchk->setting != -1) {
|
|
if (vf_spoofchk->setting)
|
|
fprintf(fp, ", spoof checking on");
|
|
else
|
|
fprintf(fp, ", spoof checking off");
|
|
}
|
|
if (vf_linkstate) {
|
|
if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO)
|
|
fprintf(fp, ", link-state auto");
|
|
else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE)
|
|
fprintf(fp, ", link-state enable");
|
|
else
|
|
fprintf(fp, ", link-state disable");
|
|
}
|
|
if (vf[IFLA_VF_TRUST]) {
|
|
struct ifla_vf_trust *vf_trust = RTA_DATA(vf[IFLA_VF_TRUST]);
|
|
|
|
if (vf_trust->setting != -1)
|
|
fprintf(fp, ", trust %s",
|
|
vf_trust->setting ? "on" : "off");
|
|
}
|
|
if (vf[IFLA_VF_STATS] && show_stats)
|
|
print_vf_stats64(fp, vf[IFLA_VF_STATS]);
|
|
}
|
|
|
|
static 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_MAX, vfstats);
|
|
|
|
/* RX stats */
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " RX: bytes packets mcast bcast %s", _SL_);
|
|
fprintf(fp, " ");
|
|
|
|
print_num(fp, 10, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_RX_BYTES]));
|
|
print_num(fp, 8, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_RX_PACKETS]));
|
|
print_num(fp, 7, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_MULTICAST]));
|
|
print_num(fp, 7, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_BROADCAST]));
|
|
|
|
/* TX stats */
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " TX: bytes packets %s", _SL_);
|
|
fprintf(fp, " ");
|
|
|
|
print_num(fp, 10, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_TX_BYTES]));
|
|
print_num(fp, 8, *(__u64 *)RTA_DATA(vf[IFLA_VF_STATS_TX_PACKETS]));
|
|
}
|
|
|
|
static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s,
|
|
const struct rtattr *carrier_changes)
|
|
{
|
|
/* 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, *(uint32_t *)RTA_DATA(carrier_changes));
|
|
}
|
|
}
|
|
|
|
static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s,
|
|
const struct rtattr *carrier_changes)
|
|
{
|
|
/* 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, *(uint32_t *)RTA_DATA(carrier_changes));
|
|
}
|
|
}
|
|
|
|
static void __print_link_stats(FILE *fp, struct rtattr **tb)
|
|
{
|
|
const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
|
|
|
|
if (tb[IFLA_STATS64]) {
|
|
struct rtnl_link_stats64 stats = { 0 };
|
|
|
|
memcpy(&stats, RTA_DATA(tb[IFLA_STATS64]),
|
|
MIN(RTA_PAYLOAD(tb[IFLA_STATS64]), sizeof(stats)));
|
|
|
|
print_link_stats64(fp, &stats, carrier_changes);
|
|
} else if (tb[IFLA_STATS]) {
|
|
struct rtnl_link_stats stats = { 0 };
|
|
|
|
memcpy(&stats, RTA_DATA(tb[IFLA_STATS]),
|
|
MIN(RTA_PAYLOAD(tb[IFLA_STATS]), sizeof(stats)));
|
|
|
|
print_link_stats32(fp, &stats, 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);
|
|
fprintf(fp, "%s", _SL_);
|
|
}
|
|
|
|
int print_linkinfo_brief(const struct sockaddr_nl *who,
|
|
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;
|
|
char *name;
|
|
char buf[32] = { 0, };
|
|
unsigned int m_flag = 0;
|
|
|
|
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
|
|
return -1;
|
|
|
|
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);
|
|
if (tb[IFLA_IFNAME] == NULL) {
|
|
fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
|
|
}
|
|
if (filter.label &&
|
|
(!filter.family || filter.family == AF_PACKET) &&
|
|
fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
|
|
return -1;
|
|
|
|
if (tb[IFLA_GROUP]) {
|
|
int group = *(int *)RTA_DATA(tb[IFLA_GROUP]);
|
|
|
|
if (filter.group != -1 && group != filter.group)
|
|
return -1;
|
|
}
|
|
|
|
if (tb[IFLA_MASTER]) {
|
|
int master = *(int *)RTA_DATA(tb[IFLA_MASTER]);
|
|
|
|
if (filter.master > 0 && master != filter.master)
|
|
return -1;
|
|
} else if (filter.master > 0)
|
|
return -1;
|
|
|
|
if (filter.kind) {
|
|
if (tb[IFLA_LINKINFO]) {
|
|
char *kind = parse_link_kind(tb[IFLA_LINKINFO]);
|
|
|
|
if (strcmp(kind, filter.kind))
|
|
return -1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (n->nlmsg_type == RTM_DELLINK)
|
|
fprintf(fp, "Deleted ");
|
|
|
|
name = (char *)(tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
|
|
|
|
if (tb[IFLA_LINK]) {
|
|
SPRINT_BUF(b1);
|
|
int iflink = *(int *)RTA_DATA(tb[IFLA_LINK]);
|
|
|
|
if (iflink == 0)
|
|
snprintf(buf, sizeof(buf), "%s@NONE", name);
|
|
else {
|
|
snprintf(buf, sizeof(buf),
|
|
"%s@%s", name, ll_idx_n2a(iflink, b1));
|
|
m_flag = ll_index_to_flags(iflink);
|
|
m_flag = !(m_flag & IFF_UP);
|
|
}
|
|
} else
|
|
snprintf(buf, sizeof(buf), "%s", name);
|
|
|
|
fprintf(fp, "%-16s ", buf);
|
|
|
|
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]) {
|
|
color_fprintf(fp, COLOR_MAC, "%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);
|
|
|
|
if (filter.family == AF_PACKET)
|
|
fprintf(fp, "\n");
|
|
fflush(fp);
|
|
return 0;
|
|
}
|
|
|
|
int print_linkinfo(const struct sockaddr_nl *who,
|
|
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;
|
|
unsigned int m_flag = 0;
|
|
|
|
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 0;
|
|
if (filter.up && !(ifi->ifi_flags&IFF_UP))
|
|
return 0;
|
|
|
|
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
|
|
if (tb[IFLA_IFNAME] == NULL) {
|
|
fprintf(stderr, "BUG: device with ifindex %d has nil ifname\n", ifi->ifi_index);
|
|
}
|
|
if (filter.label &&
|
|
(!filter.family || filter.family == AF_PACKET) &&
|
|
fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
|
|
return 0;
|
|
|
|
if (tb[IFLA_GROUP]) {
|
|
int group = *(int *)RTA_DATA(tb[IFLA_GROUP]);
|
|
|
|
if (filter.group != -1 && group != filter.group)
|
|
return -1;
|
|
}
|
|
|
|
if (tb[IFLA_MASTER]) {
|
|
int master = *(int *)RTA_DATA(tb[IFLA_MASTER]);
|
|
|
|
if (filter.master > 0 && master != filter.master)
|
|
return -1;
|
|
} else if (filter.master > 0)
|
|
return -1;
|
|
|
|
if (filter.kind) {
|
|
if (tb[IFLA_LINKINFO]) {
|
|
char *kind = parse_link_kind(tb[IFLA_LINKINFO]);
|
|
|
|
if (strcmp(kind, filter.kind))
|
|
return -1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (n->nlmsg_type == RTM_DELLINK)
|
|
fprintf(fp, "Deleted ");
|
|
|
|
fprintf(fp, "%d: ", ifi->ifi_index);
|
|
color_fprintf(fp, COLOR_IFNAME, "%s",
|
|
tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : "<nil>");
|
|
|
|
if (tb[IFLA_LINK]) {
|
|
SPRINT_BUF(b1);
|
|
int iflink = *(int *)RTA_DATA(tb[IFLA_LINK]);
|
|
|
|
if (iflink == 0)
|
|
fprintf(fp, "@NONE: ");
|
|
else {
|
|
if (tb[IFLA_LINK_NETNSID])
|
|
fprintf(fp, "@if%d: ", iflink);
|
|
else {
|
|
fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
|
|
m_flag = ll_index_to_flags(iflink);
|
|
m_flag = !(m_flag & IFF_UP);
|
|
}
|
|
}
|
|
} else {
|
|
fprintf(fp, ": ");
|
|
}
|
|
print_link_flags(fp, ifi->ifi_flags, m_flag);
|
|
|
|
if (tb[IFLA_MTU])
|
|
fprintf(fp, "mtu %u ", *(int *)RTA_DATA(tb[IFLA_MTU]));
|
|
if (tb[IFLA_QDISC])
|
|
fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC]));
|
|
if (tb[IFLA_MASTER]) {
|
|
SPRINT_BUF(b1);
|
|
fprintf(fp, "master %s ", ll_idx_n2a(*(int *)RTA_DATA(tb[IFLA_MASTER]), b1));
|
|
}
|
|
|
|
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]) {
|
|
SPRINT_BUF(b1);
|
|
int group = *(int *)RTA_DATA(tb[IFLA_GROUP]);
|
|
|
|
fprintf(fp, "group %s ", rtnl_group_n2a(group, b1, sizeof(b1)));
|
|
}
|
|
|
|
if (filter.showqueue)
|
|
print_queuelen(fp, tb);
|
|
|
|
if (!filter.family || filter.family == AF_PACKET || show_details) {
|
|
SPRINT_BUF(b1);
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
|
|
|
|
if (tb[IFLA_ADDRESS]) {
|
|
color_fprintf(fp, COLOR_MAC, "%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)
|
|
fprintf(fp, " peer ");
|
|
else
|
|
fprintf(fp, " brd ");
|
|
color_fprintf(fp, COLOR_MAC, "%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 = *(int *)RTA_DATA(tb[IFLA_LINK_NETNSID]);
|
|
|
|
if (id >= 0)
|
|
fprintf(fp, " link-netnsid %d", id);
|
|
else
|
|
fprintf(fp, " link-netnsid unknown");
|
|
}
|
|
|
|
if (tb[IFLA_PROTO_DOWN]) {
|
|
if (rta_getattr_u8(tb[IFLA_PROTO_DOWN]))
|
|
fprintf(fp, " protodown on ");
|
|
}
|
|
|
|
if (show_details) {
|
|
if (tb[IFLA_PROMISCUITY])
|
|
fprintf(fp, " promiscuity %u ",
|
|
*(int *)RTA_DATA(tb[IFLA_PROMISCUITY]));
|
|
|
|
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])
|
|
fprintf(fp, "numtxqueues %u ",
|
|
rta_getattr_u32(tb[IFLA_NUM_TX_QUEUES]));
|
|
|
|
if (tb[IFLA_NUM_RX_QUEUES])
|
|
fprintf(fp, "numrxqueues %u ",
|
|
rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES]));
|
|
|
|
if (tb[IFLA_PHYS_PORT_NAME])
|
|
fprintf(fp, "portname %s ",
|
|
rta_getattr_str(tb[IFLA_PHYS_PORT_NAME]));
|
|
|
|
if (tb[IFLA_PHYS_PORT_ID]) {
|
|
SPRINT_BUF(b1);
|
|
fprintf(fp, "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]) {
|
|
SPRINT_BUF(b1);
|
|
fprintf(fp, "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]) {
|
|
fprintf(fp, "%s alias %s", _SL_,
|
|
rta_getattr_str(tb[IFLA_IFALIAS]));
|
|
}
|
|
|
|
if (do_link && show_stats) {
|
|
fprintf(fp, "%s", _SL_);
|
|
__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);
|
|
|
|
for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem))
|
|
print_vfinfo(fp, i);
|
|
}
|
|
|
|
fprintf(fp, "\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;
|
|
}
|
|
|
|
int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
|
|
void *arg)
|
|
{
|
|
FILE *fp = arg;
|
|
struct ifaddrmsg *ifa = NLMSG_DATA(n);
|
|
int len = n->nlmsg_len;
|
|
int deprecated = 0;
|
|
/* Use local copy of ifa_flags to not interfere with filtering code */
|
|
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.label) {
|
|
SPRINT_BUF(b1);
|
|
const char *label;
|
|
|
|
if (rta_tb[IFA_LABEL])
|
|
label = RTA_DATA(rta_tb[IFA_LABEL]);
|
|
else
|
|
label = ll_idx_n2a(ifa->ifa_index, b1);
|
|
if (fnmatch(filter.label, label, 0) != 0)
|
|
return 0;
|
|
}
|
|
if (filter.pfx.family) {
|
|
if (rta_tb[IFA_LOCAL]) {
|
|
inet_prefix dst;
|
|
|
|
memset(&dst, 0, sizeof(dst));
|
|
dst.family = ifa->ifa_family;
|
|
memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
|
|
if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (filter.family && filter.family != ifa->ifa_family)
|
|
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)
|
|
fprintf(fp, "Deleted ");
|
|
|
|
if (!brief) {
|
|
if (filter.oneline || filter.flushb)
|
|
fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
|
|
if (ifa->ifa_family == AF_INET)
|
|
fprintf(fp, " inet ");
|
|
else if (ifa->ifa_family == AF_INET6)
|
|
fprintf(fp, " inet6 ");
|
|
else if (ifa->ifa_family == AF_DECnet)
|
|
fprintf(fp, " dnet ");
|
|
else if (ifa->ifa_family == AF_IPX)
|
|
fprintf(fp, " ipx ");
|
|
else
|
|
fprintf(fp, " family %d ", ifa->ifa_family);
|
|
}
|
|
|
|
if (rta_tb[IFA_LOCAL]) {
|
|
color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%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)) {
|
|
fprintf(fp, " peer ");
|
|
color_fprintf(fp, ifa_family_color(ifa->ifa_family),
|
|
"%s", format_host_rta(ifa->ifa_family,
|
|
rta_tb[IFA_ADDRESS]));
|
|
}
|
|
fprintf(fp, "/%d ", ifa->ifa_prefixlen);
|
|
}
|
|
|
|
if (brief)
|
|
goto brief_exit;
|
|
|
|
if (rta_tb[IFA_BROADCAST]) {
|
|
fprintf(fp, "brd ");
|
|
color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ",
|
|
format_host_rta(ifa->ifa_family,
|
|
rta_tb[IFA_BROADCAST]));
|
|
}
|
|
if (rta_tb[IFA_ANYCAST]) {
|
|
fprintf(fp, "any ");
|
|
color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ",
|
|
format_host_rta(ifa->ifa_family,
|
|
rta_tb[IFA_ANYCAST]));
|
|
}
|
|
fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
|
|
if (ifa_flags & IFA_F_SECONDARY) {
|
|
ifa_flags &= ~IFA_F_SECONDARY;
|
|
if (ifa->ifa_family == AF_INET6)
|
|
fprintf(fp, "temporary ");
|
|
else
|
|
fprintf(fp, "secondary ");
|
|
}
|
|
if (ifa_flags & IFA_F_TENTATIVE) {
|
|
ifa_flags &= ~IFA_F_TENTATIVE;
|
|
fprintf(fp, "tentative ");
|
|
}
|
|
if (ifa_flags & IFA_F_DEPRECATED) {
|
|
ifa_flags &= ~IFA_F_DEPRECATED;
|
|
deprecated = 1;
|
|
fprintf(fp, "deprecated ");
|
|
}
|
|
if (ifa_flags & IFA_F_HOMEADDRESS) {
|
|
ifa_flags &= ~IFA_F_HOMEADDRESS;
|
|
fprintf(fp, "home ");
|
|
}
|
|
if (ifa_flags & IFA_F_NODAD) {
|
|
ifa_flags &= ~IFA_F_NODAD;
|
|
fprintf(fp, "nodad ");
|
|
}
|
|
if (ifa_flags & IFA_F_MANAGETEMPADDR) {
|
|
ifa_flags &= ~IFA_F_MANAGETEMPADDR;
|
|
fprintf(fp, "mngtmpaddr ");
|
|
}
|
|
if (ifa_flags & IFA_F_NOPREFIXROUTE) {
|
|
ifa_flags &= ~IFA_F_NOPREFIXROUTE;
|
|
fprintf(fp, "noprefixroute ");
|
|
}
|
|
if (ifa_flags & IFA_F_MCAUTOJOIN) {
|
|
ifa_flags &= ~IFA_F_MCAUTOJOIN;
|
|
fprintf(fp, "autojoin ");
|
|
}
|
|
if (!(ifa_flags & IFA_F_PERMANENT)) {
|
|
fprintf(fp, "dynamic ");
|
|
} else
|
|
ifa_flags &= ~IFA_F_PERMANENT;
|
|
if (ifa_flags & IFA_F_DADFAILED) {
|
|
ifa_flags &= ~IFA_F_DADFAILED;
|
|
fprintf(fp, "dadfailed ");
|
|
}
|
|
if (ifa_flags)
|
|
fprintf(fp, "flags %02x ", ifa_flags);
|
|
if (rta_tb[IFA_LABEL])
|
|
fprintf(fp, "%s", rta_getattr_str(rta_tb[IFA_LABEL]));
|
|
if (rta_tb[IFA_CACHEINFO]) {
|
|
struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
|
|
|
|
fprintf(fp, "%s", _SL_);
|
|
fprintf(fp, " valid_lft ");
|
|
if (ci->ifa_valid == INFINITY_LIFE_TIME)
|
|
fprintf(fp, "forever");
|
|
else
|
|
fprintf(fp, "%usec", ci->ifa_valid);
|
|
fprintf(fp, " preferred_lft ");
|
|
if (ci->ifa_prefered == INFINITY_LIFE_TIME)
|
|
fprintf(fp, "forever");
|
|
else {
|
|
if (deprecated)
|
|
fprintf(fp, "%dsec", ci->ifa_prefered);
|
|
else
|
|
fprintf(fp, "%usec", ci->ifa_prefered);
|
|
}
|
|
}
|
|
fprintf(fp, "\n");
|
|
brief_exit:
|
|
fflush(fp);
|
|
return 0;
|
|
}
|
|
|
|
struct nlmsg_list {
|
|
struct nlmsg_list *next;
|
|
struct nlmsghdr h;
|
|
};
|
|
|
|
struct nlmsg_chain {
|
|
struct nlmsg_list *head;
|
|
struct nlmsg_list *tail;
|
|
};
|
|
|
|
static int print_selected_addrinfo(struct ifinfomsg *ifi,
|
|
struct nlmsg_list *ainfo, FILE *fp)
|
|
{
|
|
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;
|
|
|
|
print_addrinfo(NULL, n, fp);
|
|
}
|
|
if (brief) {
|
|
fprintf(fp, "\n");
|
|
fflush(fp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int store_nlmsg(const struct sockaddr_nl *who, 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(who, 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(const struct sockaddr_nl *who, 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(const struct sockaddr_nl *nl,
|
|
struct rtnl_ctrl_data *ctrl,
|
|
struct nlmsghdr *n, void *arg)
|
|
{
|
|
struct ifaddrmsg *ifa = NLMSG_DATA(n);
|
|
|
|
printf("if%d:\n", ifa->ifa_index);
|
|
print_addrinfo(NULL, n, stdout);
|
|
return 0;
|
|
}
|
|
|
|
static int ipaddr_showdump(void)
|
|
{
|
|
if (ipadd_dump_check_magic())
|
|
exit(-1);
|
|
|
|
exit(rtnl_from_file(stdin, &show_handler, NULL));
|
|
}
|
|
|
|
static int restore_handler(const struct sockaddr_nl *nl,
|
|
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, n, sizeof(*n));
|
|
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));
|
|
}
|
|
|
|
static 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 (filter.pfx.family || filter.label) {
|
|
if (!tb[IFA_LOCAL])
|
|
tb[IFA_LOCAL] = tb[IFA_ADDRESS];
|
|
|
|
if (filter.pfx.family && tb[IFA_LOCAL]) {
|
|
inet_prefix dst;
|
|
|
|
memset(&dst, 0, sizeof(dst));
|
|
dst.family = ifa->ifa_family;
|
|
memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
|
|
if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
|
|
continue;
|
|
}
|
|
if (filter.label) {
|
|
SPRINT_BUF(b1);
|
|
const char *label;
|
|
|
|
if (tb[IFA_LABEL])
|
|
label = RTA_DATA(tb[IFA_LABEL]);
|
|
else
|
|
label = ll_idx_n2a(ifa->ifa_index, b1);
|
|
if (fnmatch(filter.label, label, 0) != 0)
|
|
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_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_wilddump_request(&rth, filter.family, RTM_GETADDR) < 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_list_flush_or_save(int argc, char **argv, int action)
|
|
{
|
|
struct nlmsg_chain linfo = { NULL, NULL};
|
|
struct nlmsg_chain ainfo = { NULL, NULL};
|
|
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();
|
|
get_prefix(&filter.pfx, *argv, filter.family);
|
|
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 (strcmp(*argv, "dynamic") == 0) {
|
|
filter.flags &= ~IFA_F_PERMANENT;
|
|
filter.flagmask |= IFA_F_PERMANENT;
|
|
} else if (strcmp(*argv, "permanent") == 0) {
|
|
filter.flags |= IFA_F_PERMANENT;
|
|
filter.flagmask |= IFA_F_PERMANENT;
|
|
} else if (strcmp(*argv, "secondary") == 0 ||
|
|
strcmp(*argv, "temporary") == 0) {
|
|
filter.flags |= IFA_F_SECONDARY;
|
|
filter.flagmask |= IFA_F_SECONDARY;
|
|
} else if (strcmp(*argv, "primary") == 0) {
|
|
filter.flags &= ~IFA_F_SECONDARY;
|
|
filter.flagmask |= IFA_F_SECONDARY;
|
|
} else if (strcmp(*argv, "tentative") == 0) {
|
|
filter.flags |= IFA_F_TENTATIVE;
|
|
filter.flagmask |= IFA_F_TENTATIVE;
|
|
} else if (strcmp(*argv, "-tentative") == 0) {
|
|
filter.flags &= ~IFA_F_TENTATIVE;
|
|
filter.flagmask |= IFA_F_TENTATIVE;
|
|
} else if (strcmp(*argv, "deprecated") == 0) {
|
|
filter.flags |= IFA_F_DEPRECATED;
|
|
filter.flagmask |= IFA_F_DEPRECATED;
|
|
} else if (strcmp(*argv, "-deprecated") == 0) {
|
|
filter.flags &= ~IFA_F_DEPRECATED;
|
|
filter.flagmask |= IFA_F_DEPRECATED;
|
|
} else if (strcmp(*argv, "home") == 0) {
|
|
filter.flags |= IFA_F_HOMEADDRESS;
|
|
filter.flagmask |= IFA_F_HOMEADDRESS;
|
|
} else if (strcmp(*argv, "nodad") == 0) {
|
|
filter.flags |= IFA_F_NODAD;
|
|
filter.flagmask |= IFA_F_NODAD;
|
|
} else if (strcmp(*argv, "mngtmpaddr") == 0) {
|
|
filter.flags |= IFA_F_MANAGETEMPADDR;
|
|
filter.flagmask |= IFA_F_MANAGETEMPADDR;
|
|
} else if (strcmp(*argv, "noprefixroute") == 0) {
|
|
filter.flags |= IFA_F_NOPREFIXROUTE;
|
|
filter.flagmask |= IFA_F_NOPREFIXROUTE;
|
|
} else if (strcmp(*argv, "autojoin") == 0) {
|
|
filter.flags |= IFA_F_MCAUTOJOIN;
|
|
filter.flagmask |= IFA_F_MCAUTOJOIN;
|
|
} else if (strcmp(*argv, "dadfailed") == 0) {
|
|
filter.flags |= IFA_F_DADFAILED;
|
|
filter.flagmask |= IFA_F_DADFAILED;
|
|
} else if (strcmp(*argv, "-dadfailed") == 0) {
|
|
filter.flags &= ~IFA_F_DADFAILED;
|
|
filter.flagmask |= IFA_F_DADFAILED;
|
|
} 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, "type") == 0) {
|
|
NEXT_ARG();
|
|
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_wilddump_request(&rth, preferred_family, RTM_GETADDR) < 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);
|
|
}
|
|
|
|
/*
|
|
* 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(0, filter_dev, RTEXT_FILTER_VF) < 0) {
|
|
perror("Cannot send link get request");
|
|
exit(1);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
if (rtnl_wilddump_req_filter_fn(&rth, preferred_family, RTM_GETLINK,
|
|
iplink_filter_req) < 0) {
|
|
perror("Cannot send dump request");
|
|
exit(1);
|
|
}
|
|
|
|
if (rtnl_dump_filter(&rth, store_nlmsg, &linfo) < 0) {
|
|
fprintf(stderr, "Dump terminated\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (filter.family != AF_PACKET) {
|
|
if (filter.oneline)
|
|
no_link = 1;
|
|
|
|
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
|
|
perror("Cannot send dump request");
|
|
exit(1);
|
|
}
|
|
|
|
if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo) < 0) {
|
|
fprintf(stderr, "Dump terminated\n");
|
|
exit(1);
|
|
}
|
|
|
|
ipaddr_filter(&linfo, &ainfo);
|
|
}
|
|
|
|
for (l = linfo.head; l; l = l->next) {
|
|
int res = 0;
|
|
struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
|
|
|
|
if (brief) {
|
|
if (print_linkinfo_brief(NULL, &l->h, stdout) == 0)
|
|
if (filter.family != AF_PACKET)
|
|
print_selected_addrinfo(ifi,
|
|
ainfo.head,
|
|
stdout);
|
|
} else if (no_link ||
|
|
(res = print_linkinfo(NULL, &l->h, stdout)) >= 0) {
|
|
if (filter.family != AF_PACKET)
|
|
print_selected_addrinfo(ifi,
|
|
ainfo.head, stdout);
|
|
if (res > 0 && !do_link && show_stats)
|
|
print_link_stats(stdout, &l->h);
|
|
}
|
|
}
|
|
fflush(stdout);
|
|
|
|
free_nlmsg_chain(&ainfo);
|
|
free_nlmsg_chain(&linfo);
|
|
|
|
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);
|
|
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, int idx)
|
|
{
|
|
struct nlmsg_chain linfo = { NULL, NULL};
|
|
struct rtattr *tb[IFLA_MAX+1];
|
|
struct ifinfomsg *ifi;
|
|
struct nlmsg_list *l;
|
|
struct nlmsghdr *n;
|
|
int len;
|
|
|
|
if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 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 int ipaddr_modify(int cmd, int flags, int argc, char **argv)
|
|
{
|
|
struct {
|
|
struct nlmsghdr n;
|
|
struct ifaddrmsg ifa;
|
|
char buf[256];
|
|
} req;
|
|
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;
|
|
struct ifa_cacheinfo cinfo;
|
|
unsigned int ifa_flags = 0;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
|
|
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
|
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
|
|
req.n.nlmsg_type = cmd;
|
|
req.ifa.ifa_family = preferred_family;
|
|
|
|
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, "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 && matches(d, l) != 0) {
|
|
fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
|
|
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);
|
|
|
|
if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
|
|
fprintf(stderr, "Cannot find device \"%s\"\n", d);
|
|
return -1;
|
|
}
|
|
|
|
if (valid_lftp || preferred_lftp) {
|
|
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;
|
|
}
|
|
|
|
memset(&cinfo, 0, sizeof(cinfo));
|
|
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) < 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);
|
|
}
|