/*
* 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 <stdarg.h>
#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/if_infiniband.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 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"
" [ nomaster ]\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");
iplink_types_usage();
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:
pri