diff --git a/.gitignore b/.gitignore index c3b8d386..308aec6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ static-syms.h config.* -Config *.o *.a *.so diff --git a/Makefile b/Makefile index dbb4a4af..7a691dea 100644 --- a/Makefile +++ b/Makefile @@ -54,12 +54,12 @@ SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) -all: Config +all: config.mk @set -e; \ for i in $(SUBDIRS); \ do echo; echo $$i; $(MAKE) $(MFLAGS) -C $$i; done -Config: +config.mk: sh configure $(KERNEL_INCLUDE) install: all @@ -88,9 +88,9 @@ clean: do $(MAKE) $(MFLAGS) -C $$i clean; done clobber: - touch Config + touch config.mk $(MAKE) $(MFLAGS) clean - rm -f Config cscope.* + rm -f config.mk cscope.* distclean: clobber diff --git a/README b/README index c7a5118d..386fbaf6 100644 --- a/README +++ b/README @@ -21,8 +21,9 @@ database routines. Often this is in the db-devel package. 2. make -The makefile will automatically build a Config file which -contains whether or not ATM is available, etc. +The makefile will automatically build a config.mk file which +contains definitions of libraries that may or may not be available +on the system such as: ATM, ELF, MNL, and SELINUX. 3. To make documentation, cd to doc/ directory , then look at start of Makefile and set correct values for diff --git a/bridge/Makefile b/bridge/Makefile index fa448ea2..b2ae0a4e 100644 --- a/bridge/Makefile +++ b/bridge/Makefile @@ -1,15 +1,6 @@ BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o -include ../Config - -ifeq ($(IP_CONFIG_SETNS),y) - CFLAGS += -DHAVE_SETNS -endif - -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif +include ../config.mk all: bridge diff --git a/bridge/mdb.c b/bridge/mdb.c index e60ff3ef..748091b8 100644 --- a/bridge/mdb.c +++ b/bridge/mdb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "libnetlink.h" #include "br_common.h" @@ -25,6 +26,9 @@ #endif static unsigned int filter_index, filter_vlan; +json_writer_t *jw_global; +static bool print_mdb_entries = true; +static bool print_mdb_router = true; static void usage(void) { @@ -49,13 +53,26 @@ static void __print_router_port_stats(FILE *f, struct rtattr *pattr) if (tb[MDBA_ROUTER_PATTR_TIMER]) { __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_ROUTER_PATTR_TIMER])); - fprintf(f, " %4i.%.2i", - (int)tv.tv_sec, (int)tv.tv_usec/10000); + if (jw_global) { + char formatted_time[9]; + + snprintf(formatted_time, sizeof(formatted_time), + "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + jsonw_string_field(jw_global, "timer", formatted_time); + } else { + fprintf(f, " %4i.%.2i", + (int)tv.tv_sec, (int)tv.tv_usec/10000); + } } if (tb[MDBA_ROUTER_PATTR_TYPE]) { type = rta_getattr_u8(tb[MDBA_ROUTER_PATTR_TYPE]); - fprintf(f, " %s", - is_temp_mcast_rtr(type) ? "temp" : "permanent"); + if (jw_global) + jsonw_string_field(jw_global, "type", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); + else + fprintf(f, " %s", + is_temp_mcast_rtr(type) ? "temp" : "permanent"); } } @@ -65,24 +82,50 @@ static void br_print_router_ports(FILE *f, struct rtattr *attr, __u32 brifidx) struct rtattr *i; int rem; - if (!show_stats) - fprintf(f, "router ports on %s: ", ll_index_to_name(brifidx)); - rem = RTA_PAYLOAD(attr); - for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { - port_ifindex = RTA_DATA(i); - if (show_stats) { - fprintf(f, "router ports on %s: %s", - ll_index_to_name(brifidx), - ll_index_to_name(*port_ifindex)); - __print_router_port_stats(f, i); - fprintf(f, "\n"); - } else { - fprintf(f, "%s ", ll_index_to_name(*port_ifindex)); + if (jw_global) { + jsonw_name(jw_global, ll_index_to_name(brifidx)); + jsonw_start_array(jw_global); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + port_ifindex = RTA_DATA(i); + jsonw_start_object(jw_global); + jsonw_string_field(jw_global, + "port", + ll_index_to_name(*port_ifindex)); + if (show_stats) + __print_router_port_stats(f, i); + jsonw_end_object(jw_global); } + jsonw_end_array(jw_global); + } else { + if (!show_stats) + fprintf(f, "router ports on %s: ", + ll_index_to_name(brifidx)); + for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + port_ifindex = RTA_DATA(i); + if (show_stats) { + fprintf(f, "router ports on %s: %s", + ll_index_to_name(brifidx), + ll_index_to_name(*port_ifindex)); + __print_router_port_stats(f, i); + fprintf(f, "\n"); + } else{ + fprintf(f, "%s ", + ll_index_to_name(*port_ifindex)); + } + } + if (!show_stats) + fprintf(f, "\n"); } - if (!show_stats) - fprintf(f, "\n"); +} + +static void start_json_mdb_flags_array(bool *mdb_flags) +{ + if (*mdb_flags) + return; + jsonw_name(jw_global, "flags"); + jsonw_start_array(jw_global); + *mdb_flags = true; } static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, @@ -91,28 +134,70 @@ static void print_mdb_entry(FILE *f, int ifindex, struct br_mdb_entry *e, SPRINT_BUF(abuf); const void *src; int af; + bool mdb_flags = false; if (filter_vlan && e->vid != filter_vlan) return; af = e->addr.proto == htons(ETH_P_IP) ? AF_INET : AF_INET6; src = af == AF_INET ? (const void *)&e->addr.u.ip4 : (const void *)&e->addr.u.ip6; - if (n->nlmsg_type == RTM_DELMDB) - fprintf(f, "Deleted "); - fprintf(f, "dev %s port %s grp %s %s %s", ll_index_to_name(ifindex), - ll_index_to_name(e->ifindex), - inet_ntop(af, src, abuf, sizeof(abuf)), - (e->state & MDB_PERMANENT) ? "permanent" : "temp", - (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); - if (e->vid) - fprintf(f, " vid %hu", e->vid); + if (jw_global) + jsonw_start_object(jw_global); + if (n->nlmsg_type == RTM_DELMDB) { + if (jw_global) + jsonw_string_field(jw_global, "opCode", "deleted"); + else + fprintf(f, "Deleted "); + } + if (jw_global) { + jsonw_string_field(jw_global, "dev", ll_index_to_name(ifindex)); + jsonw_string_field(jw_global, + "port", + ll_index_to_name(e->ifindex)); + jsonw_string_field(jw_global, "grp", inet_ntop(af, src, + abuf, sizeof(abuf))); + jsonw_string_field(jw_global, "state", + (e->state & MDB_PERMANENT) ? "permanent" : "temp"); + if (e->flags & MDB_FLAGS_OFFLOAD) { + start_json_mdb_flags_array(&mdb_flags); + jsonw_string(jw_global, "offload"); + } + if (mdb_flags) + jsonw_end_array(jw_global); + } else{ + fprintf(f, "dev %s port %s grp %s %s %s", + ll_index_to_name(ifindex), + ll_index_to_name(e->ifindex), + inet_ntop(af, src, abuf, sizeof(abuf)), + (e->state & MDB_PERMANENT) ? "permanent" : "temp", + (e->flags & MDB_FLAGS_OFFLOAD) ? "offload" : ""); + } + if (e->vid) { + if (jw_global) + jsonw_uint_field(jw_global, "vid", e->vid); + else + fprintf(f, " vid %hu", e->vid); + } if (show_stats && tb && tb[MDBA_MDB_EATTR_TIMER]) { struct timeval tv; __jiffies_to_tv(&tv, rta_getattr_u32(tb[MDBA_MDB_EATTR_TIMER])); - fprintf(f, "%4i.%.2i", (int)tv.tv_sec, (int)tv.tv_usec/10000); + if (jw_global) { + char formatted_time[9]; + + snprintf(formatted_time, sizeof(formatted_time), + "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + jsonw_string_field(jw_global, "timer", formatted_time); + } else { + fprintf(f, "%4i.%.2i", (int)tv.tv_sec, + (int)tv.tv_usec/10000); + } } - fprintf(f, "\n"); + if (jw_global) + jsonw_end_object(jw_global); + else + fprintf(f, "\n"); } static void br_print_mdb_entry(FILE *f, int ifindex, struct rtattr *attr, @@ -157,14 +242,14 @@ int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) parse_rtattr(tb, MDBA_MAX, MDBA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); - if (tb[MDBA_MDB]) { + if (tb[MDBA_MDB] && print_mdb_entries) { int rem = RTA_PAYLOAD(tb[MDBA_MDB]); for (i = RTA_DATA(tb[MDBA_MDB]); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) br_print_mdb_entry(fp, r->ifindex, i, n); } - if (tb[MDBA_ROUTER]) { + if (tb[MDBA_ROUTER] && print_mdb_router) { if (n->nlmsg_type == RTM_GETMDB) { if (show_details) br_print_router_ports(fp, tb[MDBA_ROUTER], @@ -174,15 +259,33 @@ int print_mdb(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) i = RTA_DATA(tb[MDBA_ROUTER]); port_ifindex = RTA_DATA(i); - if (n->nlmsg_type == RTM_DELMDB) - fprintf(fp, "Deleted "); - fprintf(fp, "router port dev %s master %s\n", - ll_index_to_name(*port_ifindex), - ll_index_to_name(r->ifindex)); + if (n->nlmsg_type == RTM_DELMDB) { + if (jw_global) + jsonw_string_field(jw_global, + "opCode", + "deleted"); + else + fprintf(fp, "Deleted "); + } + if (jw_global) { + jsonw_name(jw_global, + ll_index_to_name(r->ifindex)); + jsonw_start_array(jw_global); + jsonw_start_object(jw_global); + jsonw_string_field(jw_global, "port", + ll_index_to_name(*port_ifindex)); + jsonw_end_object(jw_global); + jsonw_end_array(jw_global); + } else { + fprintf(fp, "router port dev %s master %s\n", + ll_index_to_name(*port_ifindex), + ll_index_to_name(r->ifindex)); + } } } - fflush(fp); + if (!jw_global) + fflush(fp); return 0; } @@ -215,15 +318,54 @@ static int mdb_show(int argc, char **argv) } } + /* get mdb entries*/ if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { perror("Cannot send dump request"); return -1; } + if (!json_output) { + /* Normal output */ + if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + return 0; + } + /* Json output */ + jw_global = jsonw_new(stdout); + jsonw_pretty(jw_global, 1); + jsonw_start_object(jw_global); + jsonw_name(jw_global, "mdb"); + jsonw_start_array(jw_global); + + /* print mdb entries */ + print_mdb_entries = true; + print_mdb_router = false; if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); return -1; } + jsonw_end_array(jw_global); + + /* get router ports */ + if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETMDB) < 0) { + perror("Cannot send dump request"); + return -1; + } + jsonw_name(jw_global, "router"); + jsonw_start_object(jw_global); + + /* print router ports */ + print_mdb_entries = false; + print_mdb_router = true; + if (rtnl_dump_filter(&rth, print_mdb, stdout) < 0) { + fprintf(stderr, "Dump terminated\n"); + return -1; + } + jsonw_end_object(jw_global); + jsonw_end_object(jw_global); + jsonw_destroy(&jw_global); return 0; } diff --git a/configure b/configure index 88cbdb82..7be8fb11 100755 --- a/configure +++ b/configure @@ -3,6 +3,9 @@ # INCLUDE=${1:-"$PWD/include"} +# Output file which is input to Makefile +CONFIG=config.mk + # Make a temp directory in build tree. TMPDIR=$(mktemp -d config.XXXXXX) trap 'status=$?; rm -rf $TMPDIR; exit $status' EXIT HUP INT QUIT TERM @@ -10,7 +13,7 @@ trap 'status=$?; rm -rf $TMPDIR; exit $status' EXIT HUP INT QUIT TERM check_prog() { echo -n "$2" - command -v $1 >/dev/null 2>&1 && (echo "$3:=y" >> Config; echo "yes") || (echo "no"; return 1) + command -v $1 >/dev/null 2>&1 && (echo "$3:=y" >> $CONFIG; echo "yes") || (echo "no"; return 1) } check_docs() @@ -30,9 +33,9 @@ check_toolchain() : ${PKG_CONFIG:=pkg-config} : ${AR=ar} : ${CC=gcc} - echo "PKG_CONFIG:=${PKG_CONFIG}" >>Config - echo "AR:=${AR}" >>Config - echo "CC:=${CC}" >>Config + echo "PKG_CONFIG:=${PKG_CONFIG}" >>$CONFIG + echo "AR:=${AR}" >>$CONFIG + echo "CC:=${CC}" >>$CONFIG } check_atm() @@ -49,7 +52,7 @@ EOF $CC -I$INCLUDE -o $TMPDIR/atmtest $TMPDIR/atmtest.c -latm >/dev/null 2>&1 if [ $? -eq 0 ] then - echo "TC_CONFIG_ATM:=y" >>Config + echo "TC_CONFIG_ATM:=y" >>$CONFIG echo yes else echo no @@ -61,7 +64,7 @@ check_xtables() { if ! ${PKG_CONFIG} xtables --exists then - echo "TC_CONFIG_NO_XT:=y" >>Config + echo "TC_CONFIG_NO_XT:=y" >>$CONFIG fi } @@ -90,7 +93,7 @@ EOF if $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL \ $(${PKG_CONFIG} xtables --cflags --libs) -ldl >/dev/null 2>&1 then - echo "TC_CONFIG_XT:=y" >>Config + echo "TC_CONFIG_XT:=y" >>$CONFIG echo "using xtables" fi rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest @@ -99,7 +102,7 @@ EOF check_xt_old() { # bail if previous XT checks has already succeded. - if grep -q TC_CONFIG_XT Config + if grep -q TC_CONFIG_XT $CONFIG then return fi @@ -129,7 +132,7 @@ EOF $CC -I$INCLUDE $IPTC -o $TMPDIR/ipttest $TMPDIR/ipttest.c $IPTL -ldl >/dev/null 2>&1 if [ $? -eq 0 ] then - echo "TC_CONFIG_XT_OLD:=y" >>Config + echo "TC_CONFIG_XT_OLD:=y" >>$CONFIG echo "using old xtables (no need for xt-internal.h)" fi rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest @@ -138,7 +141,7 @@ EOF check_xt_old_internal_h() { # bail if previous XT checks has already succeded. - if grep -q TC_CONFIG_XT Config + if grep -q TC_CONFIG_XT $CONFIG then return fi @@ -170,14 +173,14 @@ EOF if [ $? -eq 0 ] then echo "using old xtables with xt-internal.h" - echo "TC_CONFIG_XT_OLD_H:=y" >>Config + echo "TC_CONFIG_XT_OLD_H:=y" >>$CONFIG fi rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest } check_ipt() { - if ! grep TC_CONFIG_XT Config > /dev/null + if ! grep TC_CONFIG_XT $CONFIG > /dev/null then echo "using iptables" fi @@ -188,7 +191,7 @@ check_ipt_lib_dir() IPT_LIB_DIR=$(${PKG_CONFIG} --variable=xtlibdir xtables) if [ -n "$IPT_LIB_DIR" ]; then echo $IPT_LIB_DIR - echo "IPT_LIB_DIR:=$IPT_LIB_DIR" >> Config + echo "IPT_LIB_DIR:=$IPT_LIB_DIR" >> $CONFIG return fi @@ -197,7 +200,7 @@ check_ipt_lib_dir() for file in $dir/{xtables,iptables}/lib*t_*so ; do if [ -f $file ]; then echo ${file%/*} - echo "IPT_LIB_DIR:=${file%/*}" >> Config + echo "IPT_LIB_DIR:=${file%/*}" >> $CONFIG return fi done @@ -218,8 +221,9 @@ EOF $CC -I$INCLUDE -o $TMPDIR/setnstest $TMPDIR/setnstest.c >/dev/null 2>&1 if [ $? -eq 0 ] then - echo "IP_CONFIG_SETNS:=y" >>Config + echo "IP_CONFIG_SETNS:=y" >>$CONFIG echo "yes" + echo "CFLAGS += -DHAVE_SETNS" >>$CONFIG else echo "no" fi @@ -249,7 +253,7 @@ EOF if $CC -I$INCLUDE -o $TMPDIR/ipsettest $TMPDIR/ipsettest.c >/dev/null 2>&1 then - echo "TC_CONFIG_IPSET:=y" >>Config + echo "TC_CONFIG_IPSET:=y" >>$CONFIG echo "yes" else echo "no" @@ -259,25 +263,16 @@ EOF check_elf() { - cat >$TMPDIR/elftest.c < -#include -int main(void) -{ - Elf_Scn *scn __attribute__((__unused__)); - GElf_Shdr shdr __attribute__((__unused__));; - return elf_version(EV_CURRENT); -} -EOF - - if $CC -I$INCLUDE -o $TMPDIR/elftest $TMPDIR/elftest.c -lelf >/dev/null 2>&1 + if ${PKG_CONFIG} libelf --exists then - echo "HAVE_ELF:=y" >>Config + echo "HAVE_ELF:=y" >>$CONFIG echo "yes" + + echo 'CFLAGS += -DHAVE_ELF' `${PKG_CONFIG} libelf --cflags` >> $CONFIG + echo 'LDLIBS += ' `${PKG_CONFIG} libelf --libs` >>$CONFIG else echo "no" fi - rm -f $TMPDIR/elftest.c $TMPDIR/elftest } check_selinux() @@ -285,8 +280,11 @@ check_selinux() { if ${PKG_CONFIG} libselinux --exists then - echo "HAVE_SELINUX:=y" >>Config + echo "HAVE_SELINUX:=y" >>$CONFIG echo "yes" + + echo 'LDLIBS +=' `${PKG_CONFIG} --libs libselinux` >>$CONFIG + echo 'CFLAGS += -DHAVE_SELINUX' `${PKG_CONFIG} --cflags libselinux` >>$CONFIG else echo "no" fi @@ -296,8 +294,11 @@ check_mnl() { if ${PKG_CONFIG} libmnl --exists then - echo "HAVE_MNL:=y" >>Config + echo "HAVE_MNL:=y" >>$CONFIG echo "yes" + + echo 'CFLAGS += -DHAVE_LIBMNL' `${PKG_CONFIG} libmnl --cflags` >>$CONFIG + echo 'LDLIBS +=' `${PKG_CONFIG} libmnl --libs` >> $CONFIG else echo "no" fi @@ -317,7 +318,7 @@ EOF $CC -I$INCLUDE -o $TMPDIR/dbtest $TMPDIR/dbtest.c -ldb >/dev/null 2>&1 if [ $? -eq 0 ] then - echo "HAVE_BERKELEY_DB:=y" >>Config + echo "HAVE_BERKELEY_DB:=y" >>$CONFIG echo "yes" else echo "no" @@ -351,8 +352,8 @@ endif EOF } -echo "# Generated config based on" $INCLUDE >Config -quiet_config >> Config +echo "# Generated config based on" $INCLUDE >$CONFIG +quiet_config >> $CONFIG check_toolchain @@ -362,7 +363,7 @@ echo -n " ATM " check_atm check_xtables -if ! grep -q TC_CONFIG_NO_XT Config +if ! grep -q TC_CONFIG_NO_XT $CONFIG then echo -n " IPT " check_xt @@ -375,7 +376,7 @@ then fi echo -if ! grep -q TC_CONFIG_NO_XT Config +if ! grep -q TC_CONFIG_NO_XT $CONFIG then echo -n "iptables modules directory: " check_ipt_lib_dir @@ -401,6 +402,6 @@ echo -n "docs:" check_docs echo -echo >> Config -echo "%.o: %.c" >> Config -echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> Config +echo >> $CONFIG +echo "%.o: %.c" >> $CONFIG +echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> $CONFIG diff --git a/devlink/Makefile b/devlink/Makefile index 7256c287..3b2067d6 100644 --- a/devlink/Makefile +++ b/devlink/Makefile @@ -1,4 +1,5 @@ -include ../Config +include ../config.mk + ifeq ($(HAVE_MNL),y) DEVLINKOBJ = devlink.o mnlg.o diff --git a/genl/Makefile b/genl/Makefile index f0efe1ac..de1635e4 100644 --- a/genl/Makefile +++ b/genl/Makefile @@ -1,6 +1,6 @@ GENLOBJ=genl.o -include ../Config +include ../config.mk SHARED_LIBS ?= y CFLAGS += -fno-strict-aliasing @@ -17,11 +17,6 @@ LDFLAGS += -Wl,-export-dynamic LDLIBS += -lm -ldl endif -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif - all: genl genl: $(GENLOBJ) $(LIBNETLINK) $(LIBUTIL) $(GENLLIB) diff --git a/include/color.h b/include/color.h index ba0b237e..1cd6f7d2 100644 --- a/include/color.h +++ b/include/color.h @@ -2,6 +2,7 @@ #define __COLOR_H__ 1 enum color_attr { + COLOR_NONE, COLOR_IFNAME, COLOR_MAC, COLOR_INET, @@ -12,6 +13,7 @@ enum color_attr { }; void enable_color(void); +void check_if_color_enabled(void); void set_color_palette(void); int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...); enum color_attr ifa_family_color(__u8 ifa_family); diff --git a/include/json_writer.h b/include/json_writer.h index ab9a008a..1516aafb 100644 --- a/include/json_writer.h +++ b/include/json_writer.h @@ -33,20 +33,29 @@ void jsonw_pretty(json_writer_t *self, bool on); void jsonw_name(json_writer_t *self, const char *name); /* Add value */ +void jsonw_printf(json_writer_t *self, const char *fmt, ...); void jsonw_string(json_writer_t *self, const char *value); void jsonw_bool(json_writer_t *self, bool value); void jsonw_float(json_writer_t *self, double number); +void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num); void jsonw_uint(json_writer_t *self, uint64_t number); +void jsonw_hu(json_writer_t *self, unsigned short number); void jsonw_int(json_writer_t *self, int64_t number); void jsonw_null(json_writer_t *self); +void jsonw_lluint(json_writer_t *self, unsigned long long int num); /* Useful Combinations of name and value */ void jsonw_string_field(json_writer_t *self, const char *prop, const char *val); void jsonw_bool_field(json_writer_t *self, const char *prop, bool value); void jsonw_float_field(json_writer_t *self, const char *prop, double num); void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num); +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num); void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num); void jsonw_null_field(json_writer_t *self, const char *prop); +void jsonw_lluint_field(json_writer_t *self, const char *prop, + unsigned long long int num); +void jsonw_float_field_fmt(json_writer_t *self, const char *prop, + const char *fmt, double val); /* Collections */ void jsonw_start_object(json_writer_t *self); diff --git a/include/linux/bpf.h b/include/linux/bpf.h index fb84d2c2..b94acdc0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -30,9 +30,14 @@ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE +/* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ +#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ +#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ +#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ +#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ @@ -104,6 +109,8 @@ enum bpf_map_type { BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, + BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, }; enum bpf_prog_type { @@ -121,6 +128,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, }; enum bpf_attach_type { @@ -128,6 +136,8 @@ enum bpf_attach_type { BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_SOCK_CREATE, BPF_CGROUP_SOCK_OPS, + BPF_SK_SKB_STREAM_PARSER, + BPF_SK_SKB_STREAM_VERDICT, __MAX_BPF_ATTACH_TYPE }; @@ -153,6 +163,7 @@ enum bpf_attach_type { #define BPF_NOEXIST 1 /* create new element if it didn't exist */ #define BPF_EXIST 2 /* update existing element */ +/* flags for BPF_MAP_CREATE command */ #define BPF_F_NO_PREALLOC (1U << 0) /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list @@ -161,6 +172,8 @@ enum bpf_attach_type { * across different LRU lists. */ #define BPF_F_NO_COMMON_LRU (1U << 1) +/* Specify numa node during map creation */ +#define BPF_F_NUMA_NODE (1U << 2) union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ @@ -168,8 +181,13 @@ union bpf_attr { __u32 key_size; /* size of key in bytes */ __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ - __u32 map_flags; /* prealloc or not */ + __u32 map_flags; /* BPF_MAP_CREATE related + * flags defined above. + */ __u32 inner_map_fd; /* fd pointing to the inner map */ + __u32 numa_node; /* numa node (effective only if + * BPF_F_NUMA_NODE is set). + */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -344,9 +362,20 @@ union bpf_attr { * int bpf_redirect(ifindex, flags) * redirect to another netdev * @ifindex: ifindex of the net device - * @flags: bit 0 - if set, redirect to ingress instead of egress - * other bits - reserved - * Return: TC_ACT_REDIRECT + * @flags: + * cls_bpf: + * bit 0 - if set, redirect to ingress instead of egress + * other bits - reserved + * xdp_bpf: + * all bits - reserved + * Return: cls_bpf: TC_ACT_REDIRECT on success or TC_ACT_SHOT on error + * xdp_bfp: XDP_REDIRECT on success or XDP_ABORT on error + * int bpf_redirect_map(map, key, flags) + * redirect to endpoint in map + * @map: pointer to dev map + * @key: index in map to lookup + * @flags: -- + * Return: XDP_REDIRECT on success or XDP_ABORT on error * * u32 bpf_get_route_realm(skb) * retrieve a dst's tclassid @@ -539,6 +568,20 @@ union bpf_attr { * @mode: operation mode (enum bpf_adj_room_mode) * @flags: reserved for future use * Return: 0 on success or negative error code + * + * int bpf_sk_redirect_map(map, key, flags) + * Redirect skb to a sock in map using key as a lookup key for the + * sock in map. + * @map: pointer to sockmap + * @key: key to lookup sock in map + * @flags: reserved for future use + * Return: SK_REDIRECT + * + * int bpf_sock_map_update(skops, map, key, flags) + * @skops: pointer to bpf_sock_ops + * @map: pointer to sockmap to update + * @key: key to insert/update sock in map + * @flags: same flags as map update elem */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -591,7 +634,10 @@ union bpf_attr { FN(get_socket_uid), \ FN(set_hash), \ FN(setsockopt), \ - FN(skb_adjust_room), + FN(skb_adjust_room), \ + FN(redirect_map), \ + FN(sk_redirect_map), \ + FN(sock_map_update), \ /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -668,6 +714,15 @@ struct __sk_buff { __u32 data; __u32 data_end; __u32 napi_id; + + /* accessed by BPF_PROG_TYPE_sk_skb types */ + __u32 family; + __u32 remote_ip4; /* Stored in network byte order */ + __u32 local_ip4; /* Stored in network byte order */ + __u32 remote_ip6[4]; /* Stored in network byte order */ + __u32 local_ip6[4]; /* Stored in network byte order */ + __u32 remote_port; /* Stored in network byte order */ + __u32 local_port; /* stored in host byte order */ }; struct bpf_tunnel_key { @@ -703,6 +758,8 @@ struct bpf_sock { __u32 family; __u32 type; __u32 protocol; + __u32 mark; + __u32 priority; }; #define XDP_PACKET_HEADROOM 256 @@ -717,6 +774,7 @@ enum xdp_action { XDP_DROP, XDP_PASS, XDP_TX, + XDP_REDIRECT, }; /* user accessible metadata for XDP packet hook @@ -727,6 +785,12 @@ struct xdp_md { __u32 data_end; }; +enum sk_action { + SK_ABORTED = 0, + SK_DROP, + SK_REDIRECT, +}; + #define BPF_TAG_SIZE 8 struct bpf_prog_info { diff --git a/include/linux/devlink.h b/include/linux/devlink.h index 76440050..a62695e2 100644 --- a/include/linux/devlink.h +++ b/include/linux/devlink.h @@ -226,4 +226,22 @@ enum devlink_dpipe_action_type { DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY, }; +enum devlink_dpipe_field_ethernet_id { + DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, +}; + +enum devlink_dpipe_field_ipv4_id { + DEVLINK_DPIPE_FIELD_IPV4_DST_IP, +}; + +enum devlink_dpipe_field_ipv6_id { + DEVLINK_DPIPE_FIELD_IPV6_DST_IP, +}; + +enum devlink_dpipe_header_id { + DEVLINK_DPIPE_HEADER_ETHERNET, + DEVLINK_DPIPE_HEADER_IPV4, + DEVLINK_DPIPE_HEADER_IPV6, +}; + #endif /* _LINUX_DEVLINK_H_ */ diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 8ce598b9..199f253b 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -59,6 +59,7 @@ #define ARPHRD_LAPB 516 /* LAPB */ #define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ +#define ARPHRD_RAWIP 519 /* Raw IP */ #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ #define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index b7d3beb5..7dde037a 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -66,6 +66,7 @@ #define ETH_P_ATALK 0x809B /* Appletalk DDP */ #define ETH_P_AARP 0x80F3 /* Appletalk AARP */ #define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ #define ETH_P_IPX 0x8137 /* IPX over DIX */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ #define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ @@ -98,11 +99,13 @@ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ #define ETH_P_80221 0x8917 /* IEEE 802.21 Media Independent Handover Protocol */ #define ETH_P_HSR 0x892F /* IEC 62439-3 HSRv1 */ +#define ETH_P_NSH 0x894F /* Network Service Header */ #define ETH_P_LOOPBACK 0x9000 /* Ethernet loopback packet, per IEEE 802.3 */ #define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is less than this value @@ -137,6 +140,9 @@ #define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */ #define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */ #define ETH_P_XDSA 0x00F8 /* Multiplexed DSA protocol */ +#define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and + * aggregation protocol + */ /* * This is an Ethernet frame header. diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index 7375335a..21834cac 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -134,6 +134,7 @@ enum { IFLA_GRE_COLLECT_METADATA, IFLA_GRE_IGNORE_DF, IFLA_GRE_FWMARK, + IFLA_GRE_ERSPAN_INDEX, __IFLA_GRE_MAX, }; diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index f7bf7819..bada4d7b 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -142,6 +142,8 @@ enum { INET_DIAG_PAD, INET_DIAG_MARK, INET_DIAG_BBRINFO, + INET_DIAG_CLASS_ID, + INET_DIAG_MD5SIG, __INET_DIAG_MAX, }; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h deleted file mode 100644 index 5c08b222..00000000 --- a/include/linux/ipv6.h +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef _IPV6_H -#define _IPV6_H - -#include -#include -#include -#include - -/* The latest drafts declared increase in minimal mtu up to 1280. */ - -#define IPV6_MIN_MTU 1280 - -/* - * Advanced API - * source interface/address selection, source routing, etc... - * *under construction* - */ - -#if __UAPI_DEF_IN6_PKTINFO -struct in6_pktinfo { - struct in6_addr ipi6_addr; - int ipi6_ifindex; -}; -#endif - -#if __UAPI_DEF_IP6_MTUINFO -struct ip6_mtuinfo { - struct sockaddr_in6 ip6m_addr; - __u32 ip6m_mtu; -}; -#endif - -struct in6_ifreq { - struct in6_addr ifr6_addr; - __u32 ifr6_prefixlen; - int ifr6_ifindex; -}; - -#define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */ -#define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */ -#define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */ -#define IPV6_SRCRT_TYPE_4 4 /* Segment Routing with IPv6 */ - -/* - * routing header - */ -struct ipv6_rt_hdr { - __u8 nexthdr; - __u8 hdrlen; - __u8 type; - __u8 segments_left; - - /* - * type specific data - * variable length field - */ -}; - - -struct ipv6_opt_hdr { - __u8 nexthdr; - __u8 hdrlen; - /* - * TLV encoded option data follows. - */ -} __attribute__((packed)); /* required for some archs */ - -#define ipv6_destopt_hdr ipv6_opt_hdr -#define ipv6_hopopt_hdr ipv6_opt_hdr - -/* Router Alert option values (RFC2711) */ -#define IPV6_OPT_ROUTERALERT_MLD 0x0000 /* MLD(RFC2710) */ - -/* - * routing header type 0 (used in cmsghdr struct) - */ - -struct rt0_hdr { - struct ipv6_rt_hdr rt_hdr; - __u32 reserved; - struct in6_addr addr[0]; - -#define rt0_type rt_hdr.type -}; - -/* - * routing header type 2 - */ - -struct rt2_hdr { - struct ipv6_rt_hdr rt_hdr; - __u32 reserved; - struct in6_addr addr; - -#define rt2_type rt_hdr.type -}; - -/* - * home address option in destination options header - */ - -struct ipv6_destopt_hao { - __u8 type; - __u8 length; - struct in6_addr addr; -} __attribute__((packed)); - -/* - * IPv6 fixed header - * - * BEWARE, it is incorrect. The first 4 bits of flow_lbl - * are glued to priority now, forming "class". - */ - -struct ipv6hdr { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 priority:4, - version:4; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u8 version:4, - priority:4; -#else -#error "Please fix " -#endif - __u8 flow_lbl[3]; - - __be16 payload_len; - __u8 nexthdr; - __u8 hop_limit; - - struct in6_addr saddr; - struct in6_addr daddr; -}; - - -/* index values for the variables in ipv6_devconf */ -enum { - DEVCONF_FORWARDING = 0, - DEVCONF_HOPLIMIT, - DEVCONF_MTU6, - DEVCONF_ACCEPT_RA, - DEVCONF_ACCEPT_REDIRECTS, - DEVCONF_AUTOCONF, - DEVCONF_DAD_TRANSMITS, - DEVCONF_RTR_SOLICITS, - DEVCONF_RTR_SOLICIT_INTERVAL, - DEVCONF_RTR_SOLICIT_DELAY, - DEVCONF_USE_TEMPADDR, - DEVCONF_TEMP_VALID_LFT, - DEVCONF_TEMP_PREFERED_LFT, - DEVCONF_REGEN_MAX_RETRY, - DEVCONF_MAX_DESYNC_FACTOR, - DEVCONF_MAX_ADDRESSES, - DEVCONF_FORCE_MLD_VERSION, - DEVCONF_ACCEPT_RA_DEFRTR, - DEVCONF_ACCEPT_RA_PINFO, - DEVCONF_ACCEPT_RA_RTR_PREF, - DEVCONF_RTR_PROBE_INTERVAL, - DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, - DEVCONF_PROXY_NDP, - DEVCONF_OPTIMISTIC_DAD, - DEVCONF_ACCEPT_SOURCE_ROUTE, - DEVCONF_MC_FORWARDING, - DEVCONF_DISABLE_IPV6, - DEVCONF_ACCEPT_DAD, - DEVCONF_FORCE_TLLAO, - DEVCONF_NDISC_NOTIFY, - DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, - DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, - DEVCONF_SUPPRESS_FRAG_NDISC, - DEVCONF_ACCEPT_RA_FROM_LOCAL, - DEVCONF_USE_OPTIMISTIC, - DEVCONF_ACCEPT_RA_MTU, - DEVCONF_STABLE_SECRET, - DEVCONF_USE_OIF_ADDRS_ONLY, - DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT, - DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, - DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, - DEVCONF_DROP_UNSOLICITED_NA, - DEVCONF_KEEP_ADDR_ON_DOWN, - DEVCONF_RTR_SOLICIT_MAX_INTERVAL, - DEVCONF_SEG6_ENABLED, - DEVCONF_SEG6_REQUIRE_HMAC, - DEVCONF_ENHANCED_DAD, - DEVCONF_ADDR_GEN_MODE, - DEVCONF_DISABLE_POLICY, - DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, - DEVCONF_MAX -}; - - -#endif /* _IPV6_H */ diff --git a/include/linux/lwtunnel.h b/include/linux/lwtunnel.h index faa6eabe..32984262 100644 --- a/include/linux/lwtunnel.h +++ b/include/linux/lwtunnel.h @@ -11,6 +11,7 @@ enum lwtunnel_encap_types { LWTUNNEL_ENCAP_IP6, LWTUNNEL_ENCAP_SEG6, LWTUNNEL_ENCAP_BPF, + LWTUNNEL_ENCAP_SEG6_LOCAL, __LWTUNNEL_ENCAP_MAX, }; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 3a53b9aa..ec0690b5 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -69,6 +69,9 @@ struct nlmsghdr { #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_APPEND 0x800 /* Add to end of list */ +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ + /* Flags for ACK message */ #define NLM_F_CAPPED 0x100 /* request was capped */ #define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ @@ -222,5 +225,22 @@ struct nlattr { #define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) #define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; #endif /* __LINUX_NETLINK_H */ diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h new file mode 100644 index 00000000..ada7f017 --- /dev/null +++ b/include/linux/pfkeyv2.h @@ -0,0 +1,383 @@ +/* PF_KEY user interface, this is defined by rfc2367 so + * do not make arbitrary modifications or else this header + * file will not be compliant. + */ + +#ifndef _LINUX_PFKEY2_H +#define _LINUX_PFKEY2_H + +#include + +#define PF_KEY_V2 2 +#define PFKEYV2_REVISION 199806L + +struct sadb_msg { + __u8 sadb_msg_version; + __u8 sadb_msg_type; + __u8 sadb_msg_errno; + __u8 sadb_msg_satype; + __u16 sadb_msg_len; + __u16 sadb_msg_reserved; + __u32 sadb_msg_seq; + __u32 sadb_msg_pid; +} __attribute__((packed)); +/* sizeof(struct sadb_msg) == 16 */ + +struct sadb_ext { + __u16 sadb_ext_len; + __u16 sadb_ext_type; +} __attribute__((packed)); +/* sizeof(struct sadb_ext) == 4 */ + +struct sadb_sa { + __u16 sadb_sa_len; + __u16 sadb_sa_exttype; + __be32 sadb_sa_spi; + __u8 sadb_sa_replay; + __u8 sadb_sa_state; + __u8 sadb_sa_auth; + __u8 sadb_sa_encrypt; + __u32 sadb_sa_flags; +} __attribute__((packed)); +/* sizeof(struct sadb_sa) == 16 */ + +struct sadb_lifetime { + __u16 sadb_lifetime_len; + __u16 sadb_lifetime_exttype; + __u32 sadb_lifetime_allocations; + __u64 sadb_lifetime_bytes; + __u64 sadb_lifetime_addtime; + __u64 sadb_lifetime_usetime; +} __attribute__((packed)); +/* sizeof(struct sadb_lifetime) == 32 */ + +struct sadb_address { + __u16 sadb_address_len; + __u16 sadb_address_exttype; + __u8 sadb_address_proto; + __u8 sadb_address_prefixlen; + __u16 sadb_address_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_address) == 8 */ + +struct sadb_key { + __u16 sadb_key_len; + __u16 sadb_key_exttype; + __u16 sadb_key_bits; + __u16 sadb_key_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_key) == 8 */ + +struct sadb_ident { + __u16 sadb_ident_len; + __u16 sadb_ident_exttype; + __u16 sadb_ident_type; + __u16 sadb_ident_reserved; + __u64 sadb_ident_id; +} __attribute__((packed)); +/* sizeof(struct sadb_ident) == 16 */ + +struct sadb_sens { + __u16 sadb_sens_len; + __u16 sadb_sens_exttype; + __u32 sadb_sens_dpd; + __u8 sadb_sens_sens_level; + __u8 sadb_sens_sens_len; + __u8 sadb_sens_integ_level; + __u8 sadb_sens_integ_len; + __u32 sadb_sens_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_sens) == 16 */ + +/* followed by: + __u64 sadb_sens_bitmap[sens_len]; + __u64 sadb_integ_bitmap[integ_len]; */ + +struct sadb_prop { + __u16 sadb_prop_len; + __u16 sadb_prop_exttype; + __u8 sadb_prop_replay; + __u8 sadb_prop_reserved[3]; +} __attribute__((packed)); +/* sizeof(struct sadb_prop) == 8 */ + +/* followed by: + struct sadb_comb sadb_combs[(sadb_prop_len + + sizeof(__u64) - sizeof(struct sadb_prop)) / + sizeof(struct sadb_comb)]; */ + +struct sadb_comb { + __u8 sadb_comb_auth; + __u8 sadb_comb_encrypt; + __u16 sadb_comb_flags; + __u16 sadb_comb_auth_minbits; + __u16 sadb_comb_auth_maxbits; + __u16 sadb_comb_encrypt_minbits; + __u16 sadb_comb_encrypt_maxbits; + __u32 sadb_comb_reserved; + __u32 sadb_comb_soft_allocations; + __u32 sadb_comb_hard_allocations; + __u64 sadb_comb_soft_bytes; + __u64 sadb_comb_hard_bytes; + __u64 sadb_comb_soft_addtime; + __u64 sadb_comb_hard_addtime; + __u64 sadb_comb_soft_usetime; + __u64 sadb_comb_hard_usetime; +} __attribute__((packed)); +/* sizeof(struct sadb_comb) == 72 */ + +struct sadb_supported { + __u16 sadb_supported_len; + __u16 sadb_supported_exttype; + __u32 sadb_supported_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_supported) == 8 */ + +/* followed by: + struct sadb_alg sadb_algs[(sadb_supported_len + + sizeof(__u64) - sizeof(struct sadb_supported)) / + sizeof(struct sadb_alg)]; */ + +struct sadb_alg { + __u8 sadb_alg_id; + __u8 sadb_alg_ivlen; + __u16 sadb_alg_minbits; + __u16 sadb_alg_maxbits; + __u16 sadb_alg_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_alg) == 8 */ + +struct sadb_spirange { + __u16 sadb_spirange_len; + __u16 sadb_spirange_exttype; + __u32 sadb_spirange_min; + __u32 sadb_spirange_max; + __u32 sadb_spirange_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_spirange) == 16 */ + +struct sadb_x_kmprivate { + __u16 sadb_x_kmprivate_len; + __u16 sadb_x_kmprivate_exttype; + __u32 sadb_x_kmprivate_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_kmprivate) == 8 */ + +struct sadb_x_sa2 { + __u16 sadb_x_sa2_len; + __u16 sadb_x_sa2_exttype; + __u8 sadb_x_sa2_mode; + __u8 sadb_x_sa2_reserved1; + __u16 sadb_x_sa2_reserved2; + __u32 sadb_x_sa2_sequence; + __u32 sadb_x_sa2_reqid; +} __attribute__((packed)); +/* sizeof(struct sadb_x_sa2) == 16 */ + +struct sadb_x_policy { + __u16 sadb_x_policy_len; + __u16 sadb_x_policy_exttype; + __u16 sadb_x_policy_type; + __u8 sadb_x_policy_dir; + __u8 sadb_x_policy_reserved; + __u32 sadb_x_policy_id; + __u32 sadb_x_policy_priority; +} __attribute__((packed)); +/* sizeof(struct sadb_x_policy) == 16 */ + +struct sadb_x_ipsecrequest { + __u16 sadb_x_ipsecrequest_len; + __u16 sadb_x_ipsecrequest_proto; + __u8 sadb_x_ipsecrequest_mode; + __u8 sadb_x_ipsecrequest_level; + __u16 sadb_x_ipsecrequest_reserved1; + __u32 sadb_x_ipsecrequest_reqid; + __u32 sadb_x_ipsecrequest_reserved2; +} __attribute__((packed)); +/* sizeof(struct sadb_x_ipsecrequest) == 16 */ + +/* This defines the TYPE of Nat Traversal in use. Currently only one + * type of NAT-T is supported, draft-ietf-ipsec-udp-encaps-06 + */ +struct sadb_x_nat_t_type { + __u16 sadb_x_nat_t_type_len; + __u16 sadb_x_nat_t_type_exttype; + __u8 sadb_x_nat_t_type_type; + __u8 sadb_x_nat_t_type_reserved[3]; +} __attribute__((packed)); +/* sizeof(struct sadb_x_nat_t_type) == 8 */ + +/* Pass a NAT Traversal port (Source or Dest port) */ +struct sadb_x_nat_t_port { + __u16 sadb_x_nat_t_port_len; + __u16 sadb_x_nat_t_port_exttype; + __be16 sadb_x_nat_t_port_port; + __u16 sadb_x_nat_t_port_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_nat_t_port) == 8 */ + +/* Generic LSM security context */ +struct sadb_x_sec_ctx { + __u16 sadb_x_sec_len; + __u16 sadb_x_sec_exttype; + __u8 sadb_x_ctx_alg; /* LSMs: e.g., selinux == 1 */ + __u8 sadb_x_ctx_doi; + __u16 sadb_x_ctx_len; +} __attribute__((packed)); +/* sizeof(struct sadb_sec_ctx) = 8 */ + +/* Used by MIGRATE to pass addresses IKE will use to perform + * negotiation with the peer */ +struct sadb_x_kmaddress { + __u16 sadb_x_kmaddress_len; + __u16 sadb_x_kmaddress_exttype; + __u32 sadb_x_kmaddress_reserved; +} __attribute__((packed)); +/* sizeof(struct sadb_x_kmaddress) == 8 */ + +/* To specify the SA dump filter */ +struct sadb_x_filter { + __u16 sadb_x_filter_len; + __u16 sadb_x_filter_exttype; + __u32 sadb_x_filter_saddr[4]; + __u32 sadb_x_filter_daddr[4]; + __u16 sadb_x_filter_family; + __u8 sadb_x_filter_splen; + __u8 sadb_x_filter_dplen; +} __attribute__((packed)); +/* sizeof(struct sadb_x_filter) == 40 */ + +/* Message types */ +#define SADB_RESERVED 0 +#define SADB_GETSPI 1 +#define SADB_UPDATE 2 +#define SADB_ADD 3 +#define SADB_DELETE 4 +#define SADB_GET 5 +#define SADB_ACQUIRE 6 +#define SADB_REGISTER 7 +#define SADB_EXPIRE 8 +#define SADB_FLUSH 9 +#define SADB_DUMP 10 +#define SADB_X_PROMISC 11 +#define SADB_X_PCHANGE 12 +#define SADB_X_SPDUPDATE 13 +#define SADB_X_SPDADD 14 +#define SADB_X_SPDDELETE 15 +#define SADB_X_SPDGET 16 +#define SADB_X_SPDACQUIRE 17 +#define SADB_X_SPDDUMP 18 +#define SADB_X_SPDFLUSH 19 +#define SADB_X_SPDSETIDX 20 +#define SADB_X_SPDEXPIRE 21 +#define SADB_X_SPDDELETE2 22 +#define SADB_X_NAT_T_NEW_MAPPING 23 +#define SADB_X_MIGRATE 24 +#define SADB_MAX 24 + +/* Security Association flags */ +#define SADB_SAFLAGS_PFS 1 +#define SADB_SAFLAGS_NOPMTUDISC 0x20000000 +#define SADB_SAFLAGS_DECAP_DSCP 0x40000000 +#define SADB_SAFLAGS_NOECN 0x80000000 + +/* Security Association states */ +#define SADB_SASTATE_LARVAL 0 +#define SADB_SASTATE_MATURE 1 +#define SADB_SASTATE_DYING 2 +#define SADB_SASTATE_DEAD 3 +#define SADB_SASTATE_MAX 3 + +/* Security Association types */ +#define SADB_SATYPE_UNSPEC 0 +#define SADB_SATYPE_AH 2 +#define SADB_SATYPE_ESP 3 +#define SADB_SATYPE_RSVP 5 +#define SADB_SATYPE_OSPFV2 6 +#define SADB_SATYPE_RIPV2 7 +#define SADB_SATYPE_MIP 8 +#define SADB_X_SATYPE_IPCOMP 9 +#define SADB_SATYPE_MAX 9 + +/* Authentication algorithms */ +#define SADB_AALG_NONE 0 +#define SADB_AALG_MD5HMAC 2 +#define SADB_AALG_SHA1HMAC 3 +#define SADB_X_AALG_SHA2_256HMAC 5 +#define SADB_X_AALG_SHA2_384HMAC 6 +#define SADB_X_AALG_SHA2_512HMAC 7 +#define SADB_X_AALG_RIPEMD160HMAC 8 +#define SADB_X_AALG_AES_XCBC_MAC 9 +#define SADB_X_AALG_NULL 251 /* kame */ +#define SADB_AALG_MAX 251 + +/* Encryption algorithms */ +#define SADB_EALG_NONE 0 +#define SADB_EALG_DESCBC 2 +#define SADB_EALG_3DESCBC 3 +#define SADB_X_EALG_CASTCBC 6 +#define SADB_X_EALG_BLOWFISHCBC 7 +#define SADB_EALG_NULL 11 +#define SADB_X_EALG_AESCBC 12 +#define SADB_X_EALG_AESCTR 13 +#define SADB_X_EALG_AES_CCM_ICV8 14 +#define SADB_X_EALG_AES_CCM_ICV12 15 +#define SADB_X_EALG_AES_CCM_ICV16 16 +#define SADB_X_EALG_AES_GCM_ICV8 18 +#define SADB_X_EALG_AES_GCM_ICV12 19 +#define SADB_X_EALG_AES_GCM_ICV16 20 +#define SADB_X_EALG_CAMELLIACBC 22 +#define SADB_X_EALG_NULL_AES_GMAC 23 +#define SADB_EALG_MAX 253 /* last EALG */ +/* private allocations should use 249-255 (RFC2407) */ +#define SADB_X_EALG_SERPENTCBC 252 /* draft-ietf-ipsec-ciph-aes-cbc-00 */ +#define SADB_X_EALG_TWOFISHCBC 253 /* draft-ietf-ipsec-ciph-aes-cbc-00 */ + +/* Compression algorithms */ +#define SADB_X_CALG_NONE 0 +#define SADB_X_CALG_OUI 1 +#define SADB_X_CALG_DEFLATE 2 +#define SADB_X_CALG_LZS 3 +#define SADB_X_CALG_LZJH 4 +#define SADB_X_CALG_MAX 4 + +/* Extension Header values */ +#define SADB_EXT_RESERVED 0 +#define SADB_EXT_SA 1 +#define SADB_EXT_LIFETIME_CURRENT 2 +#define SADB_EXT_LIFETIME_HARD 3 +#define SADB_EXT_LIFETIME_SOFT 4 +#define SADB_EXT_ADDRESS_SRC 5 +#define SADB_EXT_ADDRESS_DST 6 +#define SADB_EXT_ADDRESS_PROXY 7 +#define SADB_EXT_KEY_AUTH 8 +#define SADB_EXT_KEY_ENCRYPT 9 +#define SADB_EXT_IDENTITY_SRC 10 +#define SADB_EXT_IDENTITY_DST 11 +#define SADB_EXT_SENSITIVITY 12 +#define SADB_EXT_PROPOSAL 13 +#define SADB_EXT_SUPPORTED_AUTH 14 +#define SADB_EXT_SUPPORTED_ENCRYPT 15 +#define SADB_EXT_SPIRANGE 16 +#define SADB_X_EXT_KMPRIVATE 17 +#define SADB_X_EXT_POLICY 18 +#define SADB_X_EXT_SA2 19 +/* The next four entries are for setting up NAT Traversal */ +#define SADB_X_EXT_NAT_T_TYPE 20 +#define SADB_X_EXT_NAT_T_SPORT 21 +#define SADB_X_EXT_NAT_T_DPORT 22 +#define SADB_X_EXT_NAT_T_OA 23 +#define SADB_X_EXT_SEC_CTX 24 +/* Used with MIGRATE to pass @ to IKE for negotiation */ +#define SADB_X_EXT_KMADDRESS 25 +#define SADB_X_EXT_FILTER 26 +#define SADB_EXT_MAX 26 + +/* Identity Extension values */ +#define SADB_IDENTTYPE_RESERVED 0 +#define SADB_IDENTTYPE_PREFIX 1 +#define SADB_IDENTTYPE_FQDN 2 +#define SADB_IDENTTYPE_USERFQDN 3 +#define SADB_IDENTTYPE_MAX 3 + +#endif /* !(_LINUX_PFKEY2_H) */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 1d62dad0..813e9e07 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -681,10 +681,29 @@ struct tcamsg { unsigned char tca__pad1; unsigned short tca__pad2; }; + +enum { + TCA_ROOT_UNSPEC, + TCA_ROOT_TAB, +#define TCA_ACT_TAB TCA_ROOT_TAB +#define TCAA_MAX TCA_ROOT_TAB + TCA_ROOT_FLAGS, + TCA_ROOT_COUNT, + TCA_ROOT_TIME_DELTA, /* in msecs */ + __TCA_ROOT_MAX, +#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) +}; + #define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) -#define TCA_ACT_TAB 1 /* attr type must be >=1 */ -#define TCAA_MAX 1 +/* tcamsg flags stored in attribute TCA_ROOT_FLAGS + * + * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO + * actions in a dump. All dump responses will contain the number of actions + * being dumped stored in for user app's consumption in TCA_ROOT_COUNT + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) /* New extended info filters for IFLA_EXT_MASK */ #define RTEXT_FILTER_VF (1 << 0) diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h index c9bba0e7..a5dc05a1 100644 --- a/include/linux/seg6_iptunnel.h +++ b/include/linux/seg6_iptunnel.h @@ -33,6 +33,7 @@ struct seg6_iptunnel_encap { enum { SEG6_IPTUN_MODE_INLINE, SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, }; diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h new file mode 100644 index 00000000..76b90d60 --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,68 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +#endif diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 79b2d6fc..8edad3f9 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -231,6 +231,14 @@ enum { TCP_NLA_SNDBUF_LIMITED, /* Time (usec) limited by send buffer */ TCP_NLA_DATA_SEGS_OUT, /* Data pkts sent including retransmission */ TCP_NLA_TOTAL_RETRANS, /* Data pkts retransmitted */ + TCP_NLA_PACING_RATE, /* Pacing rate in bytes per second */ + TCP_NLA_DELIVERY_RATE, /* Delivery rate in bytes per second */ + TCP_NLA_SND_CWND, /* Sending congestion window */ + TCP_NLA_REORDERING, /* Reordering metric */ + TCP_NLA_MIN_RTT, /* minimum RTT */ + TCP_NLA_RECUR_RETRANS, /* Recurring retransmits for the current pkt */ + TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ + }; /* for TCP_MD5SIG socket option */ @@ -248,4 +256,13 @@ struct tcp_md5sig { __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; +/* INET_DIAG_MD5SIG */ +struct tcp_diag_md5sig { + __u8 tcpm_family; + __u8 tcpm_prefixlen; + __u16 tcpm_keylen; + __be32 tcpm_addr[4]; + __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; +}; + #endif /* _LINUX_TCP_H */ diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 3af99e02..5790293b 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -304,6 +304,7 @@ enum xfrm_attr_type_t { XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ XFRMA_PAD, XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_OUTPUT_MARK, /* __u32 */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) diff --git a/include/utils.h b/include/utils.h index f161588b..c9ed230b 100644 --- a/include/utils.h +++ b/include/utils.h @@ -20,6 +20,7 @@ extern int show_raw; extern int resolve_hosts; extern int oneline; extern int brief; +extern int json; extern int timestamp; extern int timestamp_short; extern const char * _SL_; diff --git a/ip/Makefile b/ip/Makefile index a754c04d..52c9a2e5 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -9,24 +9,11 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \ link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \ iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \ iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \ - ipvrf.o iplink_xstats.o ipseg6.o + ipvrf.o iplink_xstats.o ipseg6.o ip_print.o RTMONOBJ=rtmon.o -include ../Config - -ifeq ($(IP_CONFIG_SETNS),y) - CFLAGS += -DHAVE_SETNS -endif - -ifeq ($(HAVE_ELF),y) - CFLAGS += -DHAVE_ELF - LDLIBS += -lelf -endif -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif +include ../config.mk ALLOBJ=$(IPOBJ) $(RTMONOBJ) SCRIPTS=ifcfg rtpr routel routef diff --git a/ip/ip.c b/ip/ip.c index 7c14a8ec..e66f6970 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -33,6 +33,7 @@ int show_details; int resolve_hosts; int oneline; int brief; +int json; int timestamp; const char *_SL_; int force; @@ -258,6 +259,8 @@ int main(int argc, char **argv) batch_file = argv[1]; } else if (matches(opt, "-brief") == 0) { ++brief; + } else if (matches(opt, "-json") == 0) { + ++json; } else if (matches(opt, "-rcvbuf") == 0) { unsigned int size; @@ -292,6 +295,9 @@ int main(int argc, char **argv) _SL_ = oneline ? "\\" : "\n"; + if (json) + check_if_color_enabled(); + if (batch_file) return batch(batch_file); diff --git a/ip/ip_common.h b/ip/ip_common.h index 77e9dd06..efc789cb 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -140,3 +140,59 @@ int name_is_vrf(const char *name); #endif void print_num(FILE *fp, unsigned int width, uint64_t count); + +#include "json_writer.h" + +json_writer_t *get_json_writer(void); +/* + * use: + * - PRINT_ANY for context based output + * - PRINT_FP for non json specific output + * - PRINT_JSON for json specific output + */ +enum output_type { + PRINT_FP = 1, + PRINT_JSON = 2, + PRINT_ANY = 4, +}; + +void new_json_obj(int json, FILE *fp); +void delete_json_obj(void); + +bool is_json_context(void); + +void set_current_fp(FILE *fp); + +void fflush_fp(void); + +void open_json_object(const char *str); +void close_json_object(void); +void open_json_array(enum output_type type, const char *delim); +void close_json_array(enum output_type type, const char *delim); + +#include "color.h" + +#define _PRINT_FUNC(type_name, type) \ + void print_color_##type_name(enum output_type t, \ + enum color_attr color, \ + const char *key, \ + const char *fmt, \ + type value); \ + \ + static inline void print_##type_name(enum output_type t, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + print_color_##type_name(t, -1, key, fmt, value); \ + } +_PRINT_FUNC(int, int); +_PRINT_FUNC(bool, bool); +_PRINT_FUNC(null, const char*); +_PRINT_FUNC(string, const char*); +_PRINT_FUNC(uint, uint64_t); +_PRINT_FUNC(hu, unsigned short); +_PRINT_FUNC(hex, unsigned int); +_PRINT_FUNC(0xhex, unsigned int); +_PRINT_FUNC(lluint, unsigned long long int); +#undef _PRINT_FUNC diff --git a/ip/ip_print.c b/ip/ip_print.c new file mode 100644 index 00000000..4cd6a0bc --- /dev/null +++ b/ip/ip_print.c @@ -0,0 +1,233 @@ +/* + * ip_print.c "ip print regular or json output". + * + * 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: Julien Fortin, + * + */ + +#include +#include + +#include "utils.h" +#include "ip_common.h" +#include "json_writer.h" + +static json_writer_t *_jw; +static FILE *_fp; + +#define _IS_JSON_CONTEXT(type) ((type & PRINT_JSON || type & PRINT_ANY) && _jw) +#define _IS_FP_CONTEXT(type) (!_jw && (type & PRINT_FP || type & PRINT_ANY)) + +void new_json_obj(int json, FILE *fp) +{ + if (json) { + _jw = jsonw_new(fp); + if (!_jw) { + perror("json object"); + exit(1); + } + jsonw_pretty(_jw, true); + jsonw_start_array(_jw); + } + set_current_fp(fp); +} + +void delete_json_obj(void) +{ + if (_jw) { + jsonw_end_array(_jw); + jsonw_destroy(&_jw); + } +} + +bool is_json_context(void) +{ + return _jw != NULL; +} + +void set_current_fp(FILE *fp) +{ + if (!fp) { + fprintf(stderr, "Error: invalid file pointer.\n"); + exit(1); + } + _fp = fp; +} + +json_writer_t *get_json_writer(void) +{ + return _jw; +} + +void open_json_object(const char *str) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_object(_jw); + } +} + +void close_json_object(void) +{ + if (_IS_JSON_CONTEXT(PRINT_JSON)) + jsonw_end_object(_jw); +} + +/* + * Start json array or string array using + * the provided string as json key (if not null) + * or as array delimiter in non-json context. + */ +void open_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) { + if (str) + jsonw_name(_jw, str); + jsonw_start_array(_jw); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(_fp, "%s", str); + } +} + +/* + * End json array or string array + */ +void close_json_array(enum output_type type, const char *str) +{ + if (_IS_JSON_CONTEXT(type)) { + jsonw_pretty(_jw, false); + jsonw_end_array(_jw); + jsonw_pretty(_jw, true); + } else if (_IS_FP_CONTEXT(type)) { + fprintf(_fp, "%s", str); + } +} + +/* + * pre-processor directive to generate similar + * functions handling different types + */ +#define _PRINT_FUNC(type_name, type) \ + void print_color_##type_name(enum output_type t, \ + enum color_attr color, \ + const char *key, \ + const char *fmt, \ + type value) \ + { \ + if (_IS_JSON_CONTEXT(t)) { \ + if (!key) \ + jsonw_##type_name(_jw, value); \ + else \ + jsonw_##type_name##_field(_jw, key, value); \ + } else if (_IS_FP_CONTEXT(t)) { \ + color_fprintf(_fp, color, fmt, value); \ + } \ + } +_PRINT_FUNC(int, int); +_PRINT_FUNC(hu, unsigned short); +_PRINT_FUNC(uint, uint64_t); +_PRINT_FUNC(lluint, unsigned long long int); +#undef _PRINT_FUNC + +void print_color_string(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key && !value) + jsonw_name(_jw, key); + else if (!key && value) + jsonw_string(_jw, value); + else + jsonw_string_field(_jw, key, value); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(_fp, color, fmt, value); + } +} + +/* + * value's type is bool. When using this function in FP context you can't pass + * a value to it, you will need to use "is_json_context()" to have different + * branch for json and regular output. grep -r "print_bool" for example + */ +void print_color_bool(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + bool value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_bool_field(_jw, key, value); + else + jsonw_bool(_jw, value); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(_fp, color, fmt, value ? "true" : "false"); + } +} + +/* + * In JSON context uses hardcode %#x format: 42 -> 0x2a + */ +void print_color_0xhex(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + unsigned int hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%#x", hex); + print_string(PRINT_JSON, key, NULL, b1); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(_fp, color, fmt, hex); + } +} + +void print_color_hex(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + unsigned int hex) +{ + if (_IS_JSON_CONTEXT(type)) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%x", hex); + if (key) + jsonw_string_field(_jw, key, b1); + else + jsonw_string(_jw, b1); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(_fp, color, fmt, hex); + } +} + +/* + * In JSON context we don't use the argument "value" we simply call jsonw_null + * whereas FP context can use "value" to output anything + */ +void print_color_null(enum output_type type, + enum color_attr color, + const char *key, + const char *fmt, + const char *value) +{ + if (_IS_JSON_CONTEXT(type)) { + if (key) + jsonw_null_field(_jw, key); + else + jsonw_null(_jw); + } else if (_IS_FP_CONTEXT(type)) { + color_fprintf(_fp, color, fmt, value); + } +} diff --git a/ip/ipaddress.c b/ip/ipaddress.c index dbdd839c..97971450 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -30,6 +30,7 @@ #include #include +#include "utils.h" #include "rt_names.h" #include "utils.h" #include "ll_map.h" @@ -76,7 +77,7 @@ static void usage(void) 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 | lowpan |\n"); - fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon | can |\n"); + fprintf(stderr, " gre | gretap | erspan | ip6gre | ip6gretap | vti | nlmon | can |\n"); fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf | hsr | macsec }\n"); exit(-1); @@ -84,13 +85,14 @@ static void usage(void) static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) { - fprintf(fp, "<"); + open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<"); if (flags & IFF_UP && !(flags & IFF_RUNNING)) - fprintf(fp, "NO-CARRIER%s", flags ? "," : ""); + print_string(PRINT_ANY, NULL, + flags ? "%s," : "%s", "NO-CARRIER"); flags &= ~IFF_RUNNING; -#define _PF(f) if (flags&IFF_##f) { \ - flags &= ~IFF_##f ; \ - fprintf(fp, #f "%s", flags ? "," : ""); } +#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); @@ -111,10 +113,10 @@ static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown) _PF(ECHO); #undef _PF if (flags) - fprintf(fp, "%x", flags); + print_hex(PRINT_ANY, NULL, "%x", flags); if (mdown) - fprintf(fp, ",M-DOWN"); - fprintf(fp, "> "); + print_string(PRINT_ANY, NULL, ",%s", "M-DOWN"); + close_json_array(PRINT_ANY, "> "); } static const char *oper_states[] = { @@ -125,14 +127,26 @@ static const char *oper_states[] = { static void print_operstate(FILE *f, __u8 state) { if (state >= ARRAY_SIZE(oper_states)) { - fprintf(f, "state %#x ", state); + if (is_json_context()) + print_uint(PRINT_JSON, "operstate_index", NULL, state); + else + print_0xhex(PRINT_FP, NULL, "state %#x", state); } else if (brief) { - color_fprintf(f, oper_state_color(state), - "%-14s ", oper_states[state]); + print_color_string(PRINT_ANY, + oper_state_color(state), + "operstate", + "%-14s ", + oper_states[state]); } else { - fprintf(f, "state "); - color_fprintf(f, oper_state_color(state), - "%s ", oper_states[state]); + 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]); + } } } @@ -169,7 +183,7 @@ static void print_queuelen(FILE *f, struct rtattr *tb[IFLA_MAX + 1]) qlen = ifr.ifr_qlen; } if (qlen) - fprintf(f, "qlen %d", qlen); + print_int(PRINT_ANY, "txqlen", "qlen %d", qlen); } static const char *link_modes[] = { @@ -181,9 +195,15 @@ 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); + print_int(PRINT_ANY, + "linkmode_index", + "mode %d ", + mode); else - fprintf(f, "mode %s ", link_modes[mode]); + print_string(PRINT_ANY, + "linkmode", + "mode %s " + , link_modes[mode]); } static char *parse_link_kind(struct rtattr *tb, bool slave) @@ -215,13 +235,14 @@ static void print_linktype(FILE *fp, struct rtattr *tb) 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]); - fprintf(fp, "%s", _SL_); - fprintf(fp, " %s ", kind); + print_string(PRINT_FP, NULL, "%s", _SL_); + print_string(PRINT_ANY, "info_kind", " %s ", kind); lu = get_link_kind(kind); if (lu && lu->print_opt) { @@ -232,11 +253,16 @@ static void print_linktype(FILE *fp, struct rtattr *tb) 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) + lu->print_xstats) { + open_json_object("info_xstats"); lu->print_xstats(lu, fp, linkinfo[IFLA_INFO_XSTATS]); + close_json_object(); + } } } @@ -244,8 +270,12 @@ static void print_linktype(FILE *fp, struct rtattr *tb) const char *slave_kind = rta_getattr_str(linkinfo[IFLA_INFO_SLAVE_KIND]); - fprintf(fp, "%s", _SL_); - fprintf(fp, " %s_slave ", slave_kind); + print_string(PRINT_FP, NULL, "%s", _SL_); + 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); @@ -257,9 +287,12 @@ static void print_linktype(FILE *fp, struct rtattr *tb) 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) @@ -275,22 +308,39 @@ static void print_af_spec(FILE *fp, struct rtattr *af_spec_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: - fprintf(fp, "addrgenmode eui64 "); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "eui64"); break; case IN6_ADDR_GEN_MODE_NONE: - fprintf(fp, "addrgenmode none "); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "none"); break; case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: - fprintf(fp, "addrgenmode stable_secret "); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "stable_secret"); break; case IN6_ADDR_GEN_MODE_RANDOM: - fprintf(fp, "addrgenmode random "); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + "random"); break; default: - fprintf(fp, "addrgenmode %#.2hhx ", mode); + snprintf(b1, sizeof(b1), "%#.2hhx", mode); + print_string(PRINT_ANY, + "inet6_addr_gen_mode", + "addrgenmode %s ", + b1); break; } } @@ -316,83 +366,135 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) vf_mac = RTA_DATA(vf[IFLA_VF_MAC]); vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]); - 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))); + 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); + 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) - fprintf(fp, ", vlan %d", vf_vlan_info->vlan); + print_int(PRINT_ANY, + "vlan", + ", vlan %d", + vf_vlan_info->vlan); if (vf_vlan_info->qos) - fprintf(fp, ", qos %d", 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)) - fprintf(fp, ", vlan protocol %s", - ll_proto_n2a(vf_vlan_info->vlan_proto, + 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) - fprintf(fp, ", vlan %d", vf_vlan->vlan); + print_int(PRINT_ANY, + "vlan", + ", vlan %d", + vf_vlan->vlan); if (vf_vlan->qos) - fprintf(fp, ", qos %d", vf_vlan->qos); + print_int(PRINT_ANY, "qos", ", qos %d", vf_vlan->qos); } + if (vf_tx_rate->rate) - fprintf(fp, ", tx rate %d (Mbps)", vf_tx_rate->rate); + print_int(PRINT_ANY, + "tx_rate", + ", 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]); + int max_tx = vf_rate->max_tx_rate; + int min_tx = vf_rate->min_tx_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 (is_json_context()) { + open_json_object("rate"); + print_int(PRINT_JSON, "max_tx", NULL, max_tx); + print_int(PRINT_ANY, "min_tx", NULL, min_tx); + close_json_object(); + } else { + if (max_tx) + fprintf(fp, ", max_tx_rate %dMbps", max_tx); + if (min_tx) + fprintf(fp, ", min_tx_rate %dMbps", min_tx); + } } + if (vf[IFLA_VF_SPOOFCHK]) { struct ifla_vf_spoofchk *vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]); if (vf_spoofchk->setting != -1) - fprintf(fp, ", spoof checking %s", - vf_spoofchk->setting ? "on" : "off"); + 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) - fprintf(fp, ", link-state auto"); + print_string(PRINT_ANY, + "link_state", + ", link-state %s", + "auto"); else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE) - fprintf(fp, ", link-state enable"); + print_string(PRINT_ANY, + "link_state", + ", link-state %s", + "enable"); else - fprintf(fp, ", link-state disable"); + 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) - fprintf(fp, ", trust %s", - vf_trust->setting ? "on" : "off"); + 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) - fprintf(fp, ", query_rss %s", - rss_query->setting ? "on" : "off"); + 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]); } @@ -432,7 +534,7 @@ void print_num(FILE *fp, unsigned int width, uint64_t count) } snprintf(buf, sizeof(buf), "%.*f%c%s", precision, - (double) count / powi, *prefix, use_iec ? "i" : ""); + (double) count / powi, *prefix, use_iec ? "i" : ""); fprintf(fp, "%-*s ", width, buf); } @@ -448,155 +550,339 @@ static void print_vf_stats64(FILE *fp, struct rtattr *vfstats) 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, " "); + if (is_json_context()) { + open_json_object("stats"); - 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])); + /* RX stats */ + open_json_object("rx"); + print_uint(PRINT_JSON, "bytes", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_RX_BYTES])); + print_uint(PRINT_JSON, "packets", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_RX_PACKETS])); + print_uint(PRINT_JSON, "multicast", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_MULTICAST])); + print_uint(PRINT_JSON, "broadcast", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_BROADCAST])); + close_json_object(); - /* TX stats */ - fprintf(fp, "%s", _SL_); - fprintf(fp, " TX: bytes packets %s", _SL_); - fprintf(fp, " "); + /* TX stats */ + open_json_object("tx"); + print_uint(PRINT_JSON, "tx_bytes", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_TX_BYTES])); + print_uint(PRINT_JSON, "tx_packets", NULL, + rta_getattr_u64(vf[IFLA_VF_STATS_TX_PACKETS])); + close_json_object(); + close_json_object(); + } else { + /* RX stats */ + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX: bytes packets mcast bcast %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])); + 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])); + + /* TX stats */ + fprintf(fp, "%s", _SL_); + fprintf(fp, " TX: bytes packets %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])); + } } 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_); + if (is_json_context()) { + open_json_object("stats644"); - 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 stats */ + open_json_object("rx"); + print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes); + print_uint(PRINT_JSON, "packets", NULL, s->rx_packets); + print_uint(PRINT_JSON, "errors", NULL, s->rx_errors); + print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped); + print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors); + print_uint(PRINT_JSON, "multicast", NULL, s->multicast); + if (s->rx_compressed) + print_uint(PRINT_JSON, + "compressed", + NULL, 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_); + /* RX error stats */ + if (show_stats > 1) { + print_uint(PRINT_JSON, + "length_errors", + NULL, s->rx_length_errors); + print_uint(PRINT_JSON, + "crc_errors", + NULL, s->rx_crc_errors); + print_uint(PRINT_JSON, + "frame_errors", + NULL, s->rx_frame_errors); + print_uint(PRINT_JSON, + "fifo_errors", + NULL, s->rx_fifo_errors); + print_uint(PRINT_JSON, + "missed_errors", + NULL, s->rx_missed_errors); + if (s->rx_nohandler) + print_uint(PRINT_JSON, + "nohandler", NULL, s->rx_nohandler); + } + close_json_object(); - 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); + /* TX stats */ + open_json_object("tx"); + print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes); + print_uint(PRINT_JSON, "packets", NULL, s->tx_packets); + print_uint(PRINT_JSON, "errors", NULL, s->tx_errors); + print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped); + print_uint(PRINT_JSON, + "carrier_errors", + NULL, s->tx_carrier_errors); + print_uint(PRINT_JSON, "collisions", NULL, s->collisions); + if (s->tx_compressed) + print_uint(PRINT_JSON, + "compressed", + NULL, s->tx_compressed); - } - fprintf(fp, "%s", _SL_); + /* TX error stats */ + if (show_stats > 1) { + print_uint(PRINT_JSON, + "aborted_errors", + NULL, s->tx_aborted_errors); + print_uint(PRINT_JSON, + "fifo_errors", + NULL, s->tx_fifo_errors); + print_uint(PRINT_JSON, + "window_errors", + NULL, s->tx_window_errors); + print_uint(PRINT_JSON, + "heartbeat_errors", + NULL, s->tx_heartbeat_errors); + if (carrier_changes) + print_uint(PRINT_JSON, "carrier_changes", NULL, + rta_getattr_u32(carrier_changes)); + } + close_json_object(); + close_json_object(); - /* TX stats */ - fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", - s->tx_compressed ? "compressed" : "", _SL_); + } 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->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); + 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); - /* 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"); + /* 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_); - 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)); + /* 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_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_); + if (is_json_context()) { + open_json_object("stats"); + + /* RX stats */ + open_json_object("rx"); + print_uint(PRINT_JSON, "bytes", NULL, s->rx_bytes); + print_uint(PRINT_JSON, "packets", NULL, s->rx_packets); + print_uint(PRINT_JSON, "errors", NULL, s->rx_errors); + print_uint(PRINT_JSON, "dropped", NULL, s->rx_dropped); + print_uint(PRINT_JSON, "over_errors", NULL, s->rx_over_errors); + print_uint(PRINT_JSON, "multicast", NULL, s->multicast); + if (s->rx_compressed) + print_int(PRINT_JSON, + "compressed", + NULL, s->rx_compressed); + + /* RX error stats */ + if (show_stats > 1) { + print_uint(PRINT_JSON, + "length_errors", + NULL, s->rx_length_errors); + print_uint(PRINT_JSON, + "crc_errors", + NULL, s->rx_crc_errors); + print_uint(PRINT_JSON, + "frame_errors", + NULL, s->rx_frame_errors); + print_uint(PRINT_JSON, + "fifo_errors", + NULL, s->rx_fifo_errors); + print_uint(PRINT_JSON, + "missed_errors", + NULL, s->rx_missed_errors); + if (s->rx_nohandler) + print_int(PRINT_JSON, + "nohandler", + NULL, s->rx_nohandler); + } + close_json_object(); + + /* TX stats */ + open_json_object("tx"); + print_uint(PRINT_JSON, "bytes", NULL, s->tx_bytes); + print_uint(PRINT_JSON, "packets", NULL, s->tx_packets); + print_uint(PRINT_JSON, "errors", NULL, s->tx_errors); + print_uint(PRINT_JSON, "dropped", NULL, s->tx_dropped); + print_uint(PRINT_JSON, + "carrier_errors", + NULL, s->tx_carrier_errors); + print_uint(PRINT_JSON, "collisions", NULL, s->collisions); + if (s->tx_compressed) + print_int(PRINT_JSON, + "compressed", + NULL, s->tx_compressed); + + /* TX error stats */ + if (show_stats > 1) { + print_uint(PRINT_JSON, + "aborted_errors", + NULL, s->tx_aborted_errors); + print_uint(PRINT_JSON, + "fifo_errors", + NULL, s->tx_fifo_errors); + print_uint(PRINT_JSON, + "window_errors", + NULL, s->tx_window_errors); + print_uint(PRINT_JSON, + "heartbeat_errors", + NULL, s->tx_heartbeat_errors); + if (carrier_changes) + print_uint(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); + 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"); + /* 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_); - 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)); + /* 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)); + } } } @@ -694,44 +980,50 @@ int print_linkinfo_brief(const struct sockaddr_nl *who, return -1; if (n->nlmsg_type == RTM_DELLINK) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (tb[IFLA_LINK]) { SPRINT_BUF(b1); int iflink = rta_getattr_u32(tb[IFLA_LINK]); - if (iflink == 0) + if (iflink == 0) { snprintf(buf, sizeof(buf), "%s@NONE", name); - else { - snprintf(buf, sizeof(buf), - "%s@%s", name, ll_idx_n2a(iflink, b1)); + print_null(PRINT_JSON, "link", NULL, NULL); + } else { + const char *link = ll_idx_n2a(iflink, b1); + + print_string(PRINT_JSON, "link", NULL, link); + snprintf(buf, sizeof(buf), "%s@%s", name, link); m_flag = ll_index_to_flags(iflink); m_flag = !(m_flag & IFF_UP); } } else snprintf(buf, sizeof(buf), "%s", name); - fprintf(fp, "%-16s ", buf); + print_string(PRINT_FP, NULL, "%-16s ", buf); + print_string(PRINT_JSON, "ifname", NULL, name); if (tb[IFLA_OPERSTATE]) print_operstate(fp, rta_getattr_u8(tb[IFLA_OPERSTATE])); if (pfilter->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))); + 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 (pfilter->family == AF_PACKET) + if (pfilter->family == AF_PACKET) { print_link_flags(fp, ifi->ifi_flags, m_flag); - - if (pfilter->family == AF_PACKET) - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "%s", "\n"); + } fflush(fp); return 0; } @@ -749,10 +1041,12 @@ static const char *link_events[] = { static void print_link_event(FILE *f, __u32 event) { if (event >= ARRAY_SIZE(link_events)) - fprintf(f, "event %d ", event); + print_int(PRINT_ANY, "event", "event %d ", event); else { if (event) - fprintf(f, "event %s ", link_events[event]); + print_string(PRINT_ANY, + "event", "event %s ", + link_events[event]); } } @@ -808,41 +1102,63 @@ int print_linkinfo(const struct sockaddr_nl *who, return -1; if (n->nlmsg_type == RTM_DELLINK) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); - fprintf(fp, "%d: ", ifi->ifi_index); - color_fprintf(fp, COLOR_IFNAME, "%s", - tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : ""); + print_int(PRINT_ANY, "ifindex", "%d: ", ifi->ifi_index); + if (tb[IFLA_IFNAME]) { + print_color_string(PRINT_ANY, + COLOR_IFNAME, + "ifname", "%s", + rta_getattr_str(tb[IFLA_IFNAME])); + } else { + print_null(PRINT_JSON, "ifname", NULL, NULL); + print_color_null(PRINT_FP, COLOR_IFNAME, + "ifname", "%s", ""); + } if (tb[IFLA_LINK]) { - SPRINT_BUF(b1); int iflink = rta_getattr_u32(tb[IFLA_LINK]); if (iflink == 0) - fprintf(fp, "@NONE: "); + print_null(PRINT_ANY, "link", "@%s: ", "NONE"); else { if (tb[IFLA_LINK_NETNSID]) - fprintf(fp, "@if%d: ", iflink); + print_int(PRINT_ANY, + "link_index", "@if%d: ", iflink); else { - fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + SPRINT_BUF(b1); + + print_string(PRINT_ANY, + "link", + "@%s: ", + ll_idx_n2a(iflink, b1)); m_flag = ll_index_to_flags(iflink); m_flag = !(m_flag & IFF_UP); } } } else { - fprintf(fp, ": "); + print_string(PRINT_FP, NULL, ": ", NULL); } print_link_flags(fp, ifi->ifi_flags, m_flag); if (tb[IFLA_MTU]) - fprintf(fp, "mtu %u ", rta_getattr_u32(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]) - fprintf(fp, "qdisc %s ", rta_getattr_str(tb[IFLA_QDISC])); + print_string(PRINT_ANY, + "qdisc", + "qdisc %s ", + rta_getattr_str(tb[IFLA_QDISC])); if (tb[IFLA_MASTER]) { SPRINT_BUF(b1); - fprintf(fp, "master %s ", ll_idx_n2a(rta_getattr_u32(tb[IFLA_MASTER]), b1)); + + print_string(PRINT_ANY, + "master", + "master %s ", + ll_idx_n2a(rta_getattr_u32(tb[IFLA_MASTER]), b1)); } if (tb[IFLA_OPERSTATE]) @@ -855,7 +1171,10 @@ int print_linkinfo(const struct sockaddr_nl *who, SPRINT_BUF(b1); int group = rta_getattr_u32(tb[IFLA_GROUP]); - fprintf(fp, "group %s ", rtnl_group_n2a(group, b1, sizeof(b1))); + print_string(PRINT_ANY, + "group", + "group %s ", + rtnl_group_n2a(group, b1, sizeof(b1))); } if (filter.showqueue) @@ -866,47 +1185,68 @@ int print_linkinfo(const struct sockaddr_nl *who, 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))); + print_string(PRINT_FP, NULL, "%s", _SL_); + print_string(PRINT_ANY, + "link_type", + " 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))); + 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) - 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 (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 (id >= 0) - fprintf(fp, " link-netnsid %d", id); - else - fprintf(fp, " link-netnsid unknown"); + if (is_json_context()) { + print_int(PRINT_JSON, "link_netnsid", NULL, id); + } else { + if (id >= 0) + print_int(PRINT_FP, NULL, + " link-netnsid %d", id); + else + print_string(PRINT_FP, NULL, + " link-netnsid %s", "unknown"); + } } if (tb[IFLA_PROTO_DOWN]) { if (rta_getattr_u8(tb[IFLA_PROTO_DOWN])) - fprintf(fp, " protodown on "); + print_bool(PRINT_ANY, + "proto_down", " protodown on ", true); } if (show_details) { if (tb[IFLA_PROMISCUITY]) - fprintf(fp, " promiscuity %u ", - rta_getattr_u32(tb[IFLA_PROMISCUITY])); + print_uint(PRINT_ANY, + "promiscuity", + " promiscuity %u ", + rta_getattr_u32(tb[IFLA_PROMISCUITY])); if (tb[IFLA_LINKINFO]) print_linktype(fp, tb[IFLA_LINKINFO]); @@ -915,52 +1255,70 @@ int print_linkinfo(const struct sockaddr_nl *who, 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])); + print_uint(PRINT_ANY, + "num_tx_queues", + "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])); + print_uint(PRINT_ANY, + "num_rx_queues", + "numrxqueues %u ", + rta_getattr_u32(tb[IFLA_NUM_RX_QUEUES])); if (tb[IFLA_GSO_MAX_SIZE]) - fprintf(fp, "gso_max_size %u ", - rta_getattr_u32(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]) - fprintf(fp, "gso_max_segs %u ", - rta_getattr_u32(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]) - fprintf(fp, "portname %s ", - rta_getattr_str(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]) { 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))); + 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]) { 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))); + 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]) { - fprintf(fp, "%s alias %s", _SL_, - rta_getattr_str(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) { - fprintf(fp, "%s", _SL_); + print_string(PRINT_FP, NULL, "%s", _SL_); __print_link_stats(fp, tb); } @@ -968,11 +1326,16 @@ int print_linkinfo(const struct sockaddr_nl *who, 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)) + 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); } - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "\n", NULL); fflush(fp); return 1; } @@ -1011,7 +1374,7 @@ 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; + ifa->ifa_flags; } /* Mapping from argument to address flag mask */ @@ -1044,20 +1407,34 @@ static void print_ifa_flags(FILE *fp, const struct ifaddrmsg *ifa, if (mask == IFA_F_PERMANENT) { if (!(flags & mask)) - fprintf(fp, "dynamic "); + print_bool(PRINT_ANY, + "dynamic", "dynamic ", true); } else if (flags & mask) { if (mask == IFA_F_SECONDARY && - ifa->ifa_family == AF_INET6) - fprintf(fp, "temporary "); - else - fprintf(fp, "%s ", ifa_flag_names[i].name); + 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) - fprintf(fp, "flags %02x ", flags); + 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); + } + } } @@ -1178,80 +1555,130 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, } if (n->nlmsg_type == RTM_DELADDR) - fprintf(fp, "Deleted "); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); if (!brief) { - if (filter.oneline || filter.flushb) - fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); + 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); + } + } + + int family = ifa->ifa_family; + if (ifa->ifa_family == AF_INET) - fprintf(fp, " inet "); + print_string(PRINT_ANY, "family", " %s ", "inet"); else if (ifa->ifa_family == AF_INET6) - fprintf(fp, " inet6 "); + print_string(PRINT_ANY, "family", " %s ", "inet6"); else if (ifa->ifa_family == AF_DECnet) - fprintf(fp, " dnet "); + print_string(PRINT_ANY, "family", " %s ", "dnet"); else if (ifa->ifa_family == AF_IPX) - fprintf(fp, " ipx "); + print_string(PRINT_ANY, "family", " %s ", "ipx"); else - fprintf(fp, " family %d ", ifa->ifa_family); + print_int(PRINT_ANY, + "family_index", + " family %d ", 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])); + 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)) { - fprintf(fp, " peer "); - color_fprintf(fp, ifa_family_color(ifa->ifa_family), - "%s", format_host_rta(ifa->ifa_family, - rta_tb[IFA_ADDRESS])); + 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])); } - fprintf(fp, "/%d ", ifa->ifa_prefixlen); + print_int(PRINT_ANY, "prefixlen", "/%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])); + 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]) { - fprintf(fp, "any "); - color_fprintf(fp, ifa_family_color(ifa->ifa_family), "%s ", - format_host_rta(ifa->ifa_family, - 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])); } - fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); + + 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]) - fprintf(fp, "%s", rta_getattr_str(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]); - 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 { + print_string(PRINT_FP, NULL, "%s", _SL_); + 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) - fprintf(fp, "%dsec", ci->ifa_prefered); + print_int(PRINT_ANY, + "preferred_life_time", + "%dsec", + ci->ifa_prefered); else - fprintf(fp, "%usec", ci->ifa_prefered); + print_uint(PRINT_ANY, + "preferred_life_time", + "%usec", + ci->ifa_prefered); } } - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "%s", "\n"); brief_exit: fflush(fp); return 0; @@ -1260,6 +1687,7 @@ brief_exit: 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); @@ -1277,10 +1705,14 @@ static int print_selected_addrinfo(struct ifinfomsg *ifi, if (filter.up && !(ifi->ifi_flags&IFF_UP)) continue; + open_json_object(NULL); print_addrinfo(NULL, n, fp); + close_json_object(); } + close_json_array(PRINT_JSON, NULL); + if (brief) { - fprintf(fp, "\n"); + print_string(PRINT_FP, NULL, "%s", "\n"); fflush(fp); } return 0; @@ -1726,6 +2158,12 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) exit(0); } + /* + * Initialize a json_writer and open an array object + * if -json was specified. + */ + new_json_obj(json, stdout); + /* * If only filter_dev present and none of the other * link filters are present, use RTM_GETLINK to get @@ -1734,8 +2172,10 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) 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"); + delete_json_obj(); exit(1); } + delete_json_obj(); exit(0); } @@ -1757,6 +2197,7 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) int res = 0; struct ifinfomsg *ifi = NLMSG_DATA(&l->h); + open_json_object(NULL); if (brief) { if (print_linkinfo_brief(NULL, &l->h, stdout, NULL) == 0) @@ -1765,13 +2206,14 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) ainfo->head, stdout); } else if (no_link || - (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) { + (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); } + close_json_object(); } fflush(stdout); @@ -1779,7 +2221,7 @@ out: if (ainfo) free_nlmsg_chain(ainfo); free_nlmsg_chain(&linfo); - + delete_json_obj(); return 0; } diff --git a/ip/iplink.c b/ip/iplink.c index 72c34793..ff5b56c0 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -112,8 +112,9 @@ void iplink_usage(void) "\n" "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n" " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" - " gre | gretap | ip6gre | ip6gretap | vti | nlmon | team_slave |\n" - " bond_slave | ipvlan | geneve | bridge_slave | vrf | macsec }\n"); + " gre | gretap | erspan | ip6gre | ip6gretap | vti | nlmon |\n" + " team_slave | bond_slave | ipvlan | geneve | bridge_slave |\n" + " vrf | macsec }\n"); } exit(-1); } @@ -1046,10 +1047,12 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask) return -2; } + open_json_object(NULL); if (brief) print_linkinfo_brief(NULL, &answer.n, stdout, NULL); else print_linkinfo(NULL, &answer.n, stdout); + close_json_object(); return 0; } diff --git a/ip/iplink_bond.c b/ip/iplink_bond.c index 772b05fd..2b5cf4f6 100644 --- a/ip/iplink_bond.c +++ b/ip/iplink_bond.c @@ -376,8 +376,8 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_BOND_MODE]) { const char *mode = get_name(mode_tbl, - rta_getattr_u8(tb[IFLA_BOND_MODE])); - fprintf(f, "mode %s ", mode); + rta_getattr_u8(tb[IFLA_BOND_MODE])); + print_string(PRINT_ANY, "mode", "mode %s ", mode); } if (tb[IFLA_BOND_ACTIVE_SLAVE] && @@ -386,61 +386,97 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) const char *n = if_indextoname(ifindex, buf); if (n) - fprintf(f, "active_slave %s ", n); + print_string(PRINT_ANY, + "active_slave", + "active_slave %s ", + n); else - fprintf(f, "active_slave %u ", ifindex); + print_uint(PRINT_ANY, + "active_slave_index", + "active_slave %u ", + ifindex); } if (tb[IFLA_BOND_MIIMON]) - fprintf(f, "miimon %u ", rta_getattr_u32(tb[IFLA_BOND_MIIMON])); + print_uint(PRINT_ANY, + "miimon", + "miimon %u ", + rta_getattr_u32(tb[IFLA_BOND_MIIMON])); if (tb[IFLA_BOND_UPDELAY]) - fprintf(f, "updelay %u ", rta_getattr_u32(tb[IFLA_BOND_UPDELAY])); + print_uint(PRINT_ANY, + "updelay", + "updelay %u ", + rta_getattr_u32(tb[IFLA_BOND_UPDELAY])); if (tb[IFLA_BOND_DOWNDELAY]) - fprintf(f, "downdelay %u ", - rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY])); + print_uint(PRINT_ANY, + "downdelay", + "downdelay %u ", + rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY])); if (tb[IFLA_BOND_USE_CARRIER]) - fprintf(f, "use_carrier %u ", - rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER])); + print_uint(PRINT_ANY, + "use_carrier", + "use_carrier %u ", + rta_getattr_u8(tb[IFLA_BOND_USE_CARRIER])); if (tb[IFLA_BOND_ARP_INTERVAL]) - fprintf(f, "arp_interval %u ", - rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL])); + print_uint(PRINT_ANY, + "arp_interval", + "arp_interval %u ", + rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL])); if (tb[IFLA_BOND_ARP_IP_TARGET]) { struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1]; int i; parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS, - tb[IFLA_BOND_ARP_IP_TARGET]); + tb[IFLA_BOND_ARP_IP_TARGET]); - if (iptb[0]) - fprintf(f, "arp_ip_target "); + if (iptb[0]) { + open_json_array(PRINT_JSON, "arp_ip_target"); + print_string(PRINT_FP, NULL, "arp_ip_target ", NULL); + } for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { if (iptb[i]) - fprintf(f, "%s", - rt_addr_n2a_rta(AF_INET, iptb[i])); - if (i < BOND_MAX_ARP_TARGETS-1 && iptb[i+1]) + print_string(PRINT_ANY, + NULL, + "%s", + rt_addr_n2a_rta(AF_INET, iptb[i])); + if (!is_json_context() + && i < BOND_MAX_ARP_TARGETS-1 + && iptb[i+1]) fprintf(f, ","); } - if (iptb[0]) - fprintf(f, " "); + if (iptb[0]) { + print_string(PRINT_FP, NULL, " ", NULL); + close_json_array(PRINT_JSON, NULL); + } } if (tb[IFLA_BOND_ARP_VALIDATE]) { - const char *arp_validate = get_name(arp_validate_tbl, - rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE])); - fprintf(f, "arp_validate %s ", arp_validate); + __u32 arp_v = rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]); + const char *arp_validate = get_name(arp_validate_tbl, arp_v); + + if (!arp_v && is_json_context()) + print_null(PRINT_JSON, "arp_validate", NULL, NULL); + else + print_string(PRINT_ANY, + "arp_validate", + "arp_validate %s ", + arp_validate); } if (tb[IFLA_BOND_ARP_ALL_TARGETS]) { const char *arp_all_targets = get_name(arp_all_targets_tbl, - rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS])); - fprintf(f, "arp_all_targets %s ", arp_all_targets); + rta_getattr_u32(tb[IFLA_BOND_ARP_ALL_TARGETS])); + print_string(PRINT_ANY, + "arp_all_targets", + "arp_all_targets %s ", + arp_all_targets); } if (tb[IFLA_BOND_PRIMARY] && @@ -449,123 +485,176 @@ static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) const char *n = if_indextoname(ifindex, buf); if (n) - fprintf(f, "primary %s ", n); + print_string(PRINT_ANY, "primary", "primary %s ", n); else - fprintf(f, "primary %u ", ifindex); + print_uint(PRINT_ANY, + "primary_index", + "primary %u ", + ifindex); } if (tb[IFLA_BOND_PRIMARY_RESELECT]) { const char *primary_reselect = get_name(primary_reselect_tbl, - rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT])); - fprintf(f, "primary_reselect %s ", primary_reselect); + rta_getattr_u8(tb[IFLA_BOND_PRIMARY_RESELECT])); + print_string(PRINT_ANY, + "primary_reselect", + "primary_reselect %s ", + primary_reselect); } if (tb[IFLA_BOND_FAIL_OVER_MAC]) { const char *fail_over_mac = get_name(fail_over_mac_tbl, - rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC])); - fprintf(f, "fail_over_mac %s ", fail_over_mac); + rta_getattr_u8(tb[IFLA_BOND_FAIL_OVER_MAC])); + print_string(PRINT_ANY, + "fail_over_mac", + "fail_over_mac %s ", + fail_over_mac); } if (tb[IFLA_BOND_XMIT_HASH_POLICY]) { const char *xmit_hash_policy = get_name(xmit_hash_policy_tbl, - rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY])); - fprintf(f, "xmit_hash_policy %s ", xmit_hash_policy); + rta_getattr_u8(tb[IFLA_BOND_XMIT_HASH_POLICY])); + print_string(PRINT_ANY, + "xmit_hash_policy", + "xmit_hash_policy %s ", + xmit_hash_policy); } if (tb[IFLA_BOND_RESEND_IGMP]) - fprintf(f, "resend_igmp %u ", - rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP])); + print_uint(PRINT_ANY, + "resend_igmp", + "resend_igmp %u ", + rta_getattr_u32(tb[IFLA_BOND_RESEND_IGMP])); if (tb[IFLA_BOND_NUM_PEER_NOTIF]) - fprintf(f, "num_grat_arp %u ", - rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF])); + print_uint(PRINT_ANY, + "num_peer_notif", + "num_grat_arp %u ", + rta_getattr_u8(tb[IFLA_BOND_NUM_PEER_NOTIF])); if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE]) - fprintf(f, "all_slaves_active %u ", - rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE])); + print_uint(PRINT_ANY, + "all_slaves_active", + "all_slaves_active %u ", + rta_getattr_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE])); if (tb[IFLA_BOND_MIN_LINKS]) - fprintf(f, "min_links %u ", - rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS])); + print_uint(PRINT_ANY, + "min_links", + "min_links %u ", + rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS])); if (tb[IFLA_BOND_LP_INTERVAL]) - fprintf(f, "lp_interval %u ", - rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL])); + print_uint(PRINT_ANY, + "lp_interval", + "lp_interval %u ", + rta_getattr_u32(tb[IFLA_BOND_LP_INTERVAL])); if (tb[IFLA_BOND_PACKETS_PER_SLAVE]) - fprintf(f, "packets_per_slave %u ", - rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE])); + print_uint(PRINT_ANY, + "packets_per_slave", + "packets_per_slave %u ", + rta_getattr_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE])); if (tb[IFLA_BOND_AD_LACP_RATE]) { const char *lacp_rate = get_name(lacp_rate_tbl, - rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE])); - fprintf(f, "lacp_rate %s ", lacp_rate); + rta_getattr_u8(tb[IFLA_BOND_AD_LACP_RATE])); + print_string(PRINT_ANY, + "ad_lacp_rate", + "lacp_rate %s ", + lacp_rate); } if (tb[IFLA_BOND_AD_SELECT]) { const char *ad_select = get_name(ad_select_tbl, - rta_getattr_u8(tb[IFLA_BOND_AD_SELECT])); - fprintf(f, "ad_select %s ", ad_select); + rta_getattr_u8(tb[IFLA_BOND_AD_SELECT])); + print_string(PRINT_ANY, + "ad_select", + "ad_select %s ", + ad_select); } if (tb[IFLA_BOND_AD_INFO]) { struct rtattr *adtb[IFLA_BOND_AD_INFO_MAX + 1]; parse_rtattr_nested(adtb, IFLA_BOND_AD_INFO_MAX, - tb[IFLA_BOND_AD_INFO]); + tb[IFLA_BOND_AD_INFO]); + + open_json_object("ad_info"); if (adtb[IFLA_BOND_AD_INFO_AGGREGATOR]) - fprintf(f, "ad_aggregator %d ", - rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR])); + print_int(PRINT_ANY, + "aggregator", + "ad_aggregator %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_AGGREGATOR])); if (adtb[IFLA_BOND_AD_INFO_NUM_PORTS]) - fprintf(f, "ad_num_ports %d ", - rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS])); + print_int(PRINT_ANY, + "num_ports", + "ad_num_ports %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_NUM_PORTS])); if (adtb[IFLA_BOND_AD_INFO_ACTOR_KEY]) - fprintf(f, "ad_actor_key %d ", - rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY])); + print_int(PRINT_ANY, + "actor_key", + "ad_actor_key %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_ACTOR_KEY])); if (adtb[IFLA_BOND_AD_INFO_PARTNER_KEY]) - fprintf(f, "ad_partner_key %d ", - rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY])); + print_int(PRINT_ANY, + "partner_key", + "ad_partner_key %d ", + rta_getattr_u16(adtb[IFLA_BOND_AD_INFO_PARTNER_KEY])); if (adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]) { unsigned char *p = RTA_DATA(adtb[IFLA_BOND_AD_INFO_PARTNER_MAC]); SPRINT_BUF(b); - fprintf(f, "ad_partner_mac %s ", - ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b))); + print_string(PRINT_ANY, + "partner_mac", + "ad_partner_mac %s ", + ll_addr_n2a(p, ETH_ALEN, 0, b, sizeof(b))); } + + close_json_object(); } if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]) { - fprintf(f, "ad_actor_sys_prio %u ", - rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO])); + print_uint(PRINT_ANY, + "ad_actor_sys_prio", + "ad_actor_sys_prio %u ", + rta_getattr_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO])); } if (tb[IFLA_BOND_AD_USER_PORT_KEY]) { - fprintf(f, "ad_user_port_key %u ", - rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY])); + print_uint(PRINT_ANY, + "ad_user_port_key", + "ad_user_port_key %u ", + rta_getattr_u16(tb[IFLA_BOND_AD_USER_PORT_KEY])); } if (tb[IFLA_BOND_AD_ACTOR_SYSTEM]) { /* We assume the l2 address is an Ethernet MAC address */ SPRINT_BUF(b1); - fprintf(f, "ad_actor_system %s ", - ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), - RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), - 1 /*ARPHDR_ETHER*/, b1, sizeof(b1))); + + print_string(PRINT_ANY, + "ad_actor_system", + "ad_actor_system %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), + RTA_PAYLOAD(tb[IFLA_BOND_AD_ACTOR_SYSTEM]), + 1 /*ARPHDR_ETHER*/, b1, sizeof(b1))); } if (tb[IFLA_BOND_TLB_DYNAMIC_LB]) { - fprintf(f, "tlb_dynamic_lb %u ", - rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB])); + print_uint(PRINT_ANY, + "tlb_dynamic_lb", + "tlb_dynamic_lb %u ", + rta_getattr_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB])); } } static void bond_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_explain(f); } diff --git a/ip/iplink_bond_slave.c b/ip/iplink_bond_slave.c index 877e2d9e..67219c67 100644 --- a/ip/iplink_bond_slave.c +++ b/ip/iplink_bond_slave.c @@ -37,9 +37,12 @@ static void print_slave_state(FILE *f, struct rtattr *tb) unsigned int state = rta_getattr_u8(tb); if (state >= ARRAY_SIZE(slave_states)) - fprintf(f, "state %d ", state); + print_int(PRINT_ANY, "state_index", "state %d ", state); else - fprintf(f, "state %s ", slave_states[state]); + print_string(PRINT_ANY, + "state", + "state %s ", + slave_states[state]); } static const char *slave_mii_status[] = { @@ -54,9 +57,15 @@ static void print_slave_mii_status(FILE *f, struct rtattr *tb) unsigned int status = rta_getattr_u8(tb); if (status >= ARRAY_SIZE(slave_mii_status)) - fprintf(f, "mii_status %d ", status); + print_int(PRINT_ANY, + "mii_status_index", + "mii_status %d ", + status); else - fprintf(f, "mii_status %s ", slave_mii_status[status]); + print_string(PRINT_ANY, + "mii_status", + "mii_status %s ", + slave_mii_status[status]); } static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) @@ -72,30 +81,42 @@ static void bond_slave_print_opt(struct link_util *lu, FILE *f, struct rtattr *t print_slave_mii_status(f, tb[IFLA_BOND_SLAVE_MII_STATUS]); if (tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT]) - fprintf(f, "link_failure_count %d ", - rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT])); + print_int(PRINT_ANY, + "link_failure_count", + "link_failure_count %d ", + rta_getattr_u32(tb[IFLA_BOND_SLAVE_LINK_FAILURE_COUNT])); if (tb[IFLA_BOND_SLAVE_PERM_HWADDR]) - fprintf(f, "perm_hwaddr %s ", - ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), - RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), - 0, b1, sizeof(b1))); + print_string(PRINT_ANY, + "perm_hwaddr", + "perm_hwaddr %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), + RTA_PAYLOAD(tb[IFLA_BOND_SLAVE_PERM_HWADDR]), + 0, b1, sizeof(b1))); if (tb[IFLA_BOND_SLAVE_QUEUE_ID]) - fprintf(f, "queue_id %d ", - rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID])); + print_int(PRINT_ANY, + "queue_id", + "queue_id %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_QUEUE_ID])); if (tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID]) - fprintf(f, "ad_aggregator_id %d ", - rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])); + print_int(PRINT_ANY, + "ad_aggregator_id", + "ad_aggregator_id %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_AGGREGATOR_ID])); if (tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE]) - fprintf(f, "ad_actor_oper_port_state %d ", - rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])); + print_int(PRINT_ANY, + "ad_actor_oper_port_state", + "ad_actor_oper_port_state %d ", + rta_getattr_u8(tb[IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE])); if (tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE]) - fprintf(f, "ad_partner_oper_port_state %d ", - rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE])); + print_int(PRINT_ANY, + "ad_partner_oper_port_state", + "ad_partner_oper_port_state %d ", + rta_getattr_u16(tb[IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE])); } static int bond_slave_parse_opt(struct link_util *lu, int argc, char **argv, diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c index cccdec1c..d3250980 100644 --- a/ip/iplink_bridge.c +++ b/ip/iplink_bridge.c @@ -373,45 +373,81 @@ static int bridge_parse_opt(struct link_util *lu, int argc, char **argv, return 0; } +static void _bridge_print_timer(FILE *f, + const char *attr, + struct rtattr *timer) +{ + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(timer)); + if (is_json_context()) { + json_writer_t *jw = get_json_writer(); + + jsonw_name(jw, attr); + jsonw_printf(jw, "%i.%.2i", + (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } else { + fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } +} + static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { if (!tb) return; if (tb[IFLA_BR_FORWARD_DELAY]) - fprintf(f, "forward_delay %u ", - rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY])); + print_uint(PRINT_ANY, + "forward_delay", + "forward_delay %u ", + rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY])); if (tb[IFLA_BR_HELLO_TIME]) - fprintf(f, "hello_time %u ", - rta_getattr_u32(tb[IFLA_BR_HELLO_TIME])); + print_uint(PRINT_ANY, + "hello_time", + "hello_time %u ", + rta_getattr_u32(tb[IFLA_BR_HELLO_TIME])); if (tb[IFLA_BR_MAX_AGE]) - fprintf(f, "max_age %u ", - rta_getattr_u32(tb[IFLA_BR_MAX_AGE])); + print_uint(PRINT_ANY, + "max_age", + "max_age %u ", + rta_getattr_u32(tb[IFLA_BR_MAX_AGE])); if (tb[IFLA_BR_AGEING_TIME]) - fprintf(f, "ageing_time %u ", - rta_getattr_u32(tb[IFLA_BR_AGEING_TIME])); + print_uint(PRINT_ANY, + "ageing_time", + "ageing_time %u ", + rta_getattr_u32(tb[IFLA_BR_AGEING_TIME])); if (tb[IFLA_BR_STP_STATE]) - fprintf(f, "stp_state %u ", - rta_getattr_u32(tb[IFLA_BR_STP_STATE])); + print_uint(PRINT_ANY, + "stp_state", + "stp_state %u ", + rta_getattr_u32(tb[IFLA_BR_STP_STATE])); if (tb[IFLA_BR_PRIORITY]) - fprintf(f, "priority %u ", - rta_getattr_u16(tb[IFLA_BR_PRIORITY])); + print_uint(PRINT_ANY, + "priority", + "priority %u ", + rta_getattr_u16(tb[IFLA_BR_PRIORITY])); if (tb[IFLA_BR_VLAN_FILTERING]) - fprintf(f, "vlan_filtering %u ", - rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING])); + print_uint(PRINT_ANY, + "vlan_filtering", + "vlan_filtering %u ", + rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING])); if (tb[IFLA_BR_VLAN_PROTOCOL]) { SPRINT_BUF(b1); - fprintf(f, "vlan_protocol %s ", - ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), - b1, sizeof(b1))); + print_string(PRINT_ANY, + "vlan_protocol", + "vlan_protocol %s ", + ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), + b1, sizeof(b1))); } if (tb[IFLA_BR_BRIDGE_ID]) { @@ -419,7 +455,10 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id, sizeof(bridge_id)); - fprintf(f, "bridge_id %s ", bridge_id); + print_string(PRINT_ANY, + "bridge_id", + "bridge_id %s ", + bridge_id); } if (tb[IFLA_BR_ROOT_ID]) { @@ -427,163 +466,201 @@ static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id, sizeof(root_id)); - fprintf(f, "designated_root %s ", root_id); + print_string(PRINT_ANY, + "root_id", + "designated_root %s ", + root_id); } if (tb[IFLA_BR_ROOT_PORT]) - fprintf(f, "root_port %u ", - rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); + print_uint(PRINT_ANY, + "root_port", + "root_port %u ", + rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); if (tb[IFLA_BR_ROOT_PATH_COST]) - fprintf(f, "root_path_cost %u ", - rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); + print_uint(PRINT_ANY, + "root_path_cost", + "root_path_cost %u ", + rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); if (tb[IFLA_BR_TOPOLOGY_CHANGE]) - fprintf(f, "topology_change %u ", - rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); + print_uint(PRINT_ANY, + "topology_change", + "topology_change %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]) - fprintf(f, "topology_change_detected %u ", - rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); + print_uint(PRINT_ANY, + "topology_change_detected", + "topology_change_detected %u ", + rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); - if (tb[IFLA_BR_HELLO_TIMER]) { - struct timeval tv; + if (tb[IFLA_BR_HELLO_TIMER]) + _bridge_print_timer(f, "hello_timer", tb[IFLA_BR_HELLO_TIMER]); - __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_HELLO_TIMER])); - fprintf(f, "hello_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } + if (tb[IFLA_BR_TCN_TIMER]) + _bridge_print_timer(f, "tcn_timer", tb[IFLA_BR_TCN_TIMER]); - if (tb[IFLA_BR_TCN_TIMER]) { - struct timeval tv; + if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) + _bridge_print_timer(f, "topology_change_timer", + tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); - __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_TCN_TIMER])); - fprintf(f, "tcn_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } - - if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) { - unsigned long jiffies; - struct timeval tv; - - jiffies = rta_getattr_u64(tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); - __jiffies_to_tv(&tv, jiffies); - fprintf(f, "topology_change_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } - - if (tb[IFLA_BR_GC_TIMER]) { - struct timeval tv; - - __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_GC_TIMER])); - fprintf(f, "gc_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } + if (tb[IFLA_BR_GC_TIMER]) + _bridge_print_timer(f, "gc_timer", tb[IFLA_BR_GC_TIMER]); if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) - fprintf(f, "vlan_default_pvid %u ", - rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); + print_uint(PRINT_ANY, + "vlan_default_pvid", + "vlan_default_pvid %u ", + rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); if (tb[IFLA_BR_VLAN_STATS_ENABLED]) - fprintf(f, "vlan_stats_enabled %u ", - rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED])); + print_uint(PRINT_ANY, + "vlan_stats_enabled", + "vlan_stats_enabled %u ", + rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED])); if (tb[IFLA_BR_GROUP_FWD_MASK]) - fprintf(f, "group_fwd_mask %#x ", - rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); + print_0xhex(PRINT_ANY, + "group_fwd_mask", + "group_fwd_mask %#x ", + rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); if (tb[IFLA_BR_GROUP_ADDR]) { SPRINT_BUF(mac); - fprintf(f, "group_address %s ", - ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), - RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), - 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); + print_string(PRINT_ANY, + "group_addr", + "group_address %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), + RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), + 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); } if (tb[IFLA_BR_MCAST_SNOOPING]) - fprintf(f, "mcast_snooping %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); + print_uint(PRINT_ANY, + "mcast_snooping", + "mcast_snooping %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); if (tb[IFLA_BR_MCAST_ROUTER]) - fprintf(f, "mcast_router %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); + print_uint(PRINT_ANY, + "mcast_router", + "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) - fprintf(f, "mcast_query_use_ifaddr %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); + print_uint(PRINT_ANY, + "mcast_query_use_ifaddr", + "mcast_query_use_ifaddr %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); if (tb[IFLA_BR_MCAST_QUERIER]) - fprintf(f, "mcast_querier %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); + print_uint(PRINT_ANY, + "mcast_querier", + "mcast_querier %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); if (tb[IFLA_BR_MCAST_HASH_ELASTICITY]) - fprintf(f, "mcast_hash_elasticity %u ", - rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); + print_uint(PRINT_ANY, + "mcast_hash_elasticity", + "mcast_hash_elasticity %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); if (tb[IFLA_BR_MCAST_HASH_MAX]) - fprintf(f, "mcast_hash_max %u ", - rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); + print_uint(PRINT_ANY, + "mcast_hash_max", + "mcast_hash_max %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) - fprintf(f, "mcast_last_member_count %u ", - rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); + print_uint(PRINT_ANY, + "mcast_last_member_cnt", + "mcast_last_member_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) - fprintf(f, "mcast_startup_query_count %u ", - rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); + print_uint(PRINT_ANY, + "mcast_startup_query_cnt", + "mcast_startup_query_count %u ", + rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) - fprintf(f, "mcast_last_member_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); + print_lluint(PRINT_ANY, + "mcast_last_member_intvl", + "mcast_last_member_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) - fprintf(f, "mcast_membership_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); + print_lluint(PRINT_ANY, + "mcast_membership_intvl", + "mcast_membership_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) - fprintf(f, "mcast_querier_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); + print_lluint(PRINT_ANY, + "mcast_querier_intvl", + "mcast_querier_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); if (tb[IFLA_BR_MCAST_QUERY_INTVL]) - fprintf(f, "mcast_query_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); + print_lluint(PRINT_ANY, + "mcast_query_intvl", + "mcast_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) - fprintf(f, "mcast_query_response_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); + print_lluint(PRINT_ANY, + "mcast_query_response_intvl", + "mcast_query_response_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) - fprintf(f, "mcast_startup_query_interval %llu ", - rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); + print_lluint(PRINT_ANY, + "mcast_startup_query_intvl", + "mcast_startup_query_interval %llu ", + rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); if (tb[IFLA_BR_MCAST_STATS_ENABLED]) - fprintf(f, "mcast_stats_enabled %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED])); + print_uint(PRINT_ANY, + "mcast_stats_enabled", + "mcast_stats_enabled %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED])); if (tb[IFLA_BR_MCAST_IGMP_VERSION]) - fprintf(f, "mcast_igmp_version %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION])); + print_uint(PRINT_ANY, + "mcast_igmp_version", + "mcast_igmp_version %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION])); if (tb[IFLA_BR_MCAST_MLD_VERSION]) - fprintf(f, "mcast_mld_version %u ", - rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION])); + print_uint(PRINT_ANY, + "mcast_mld_version", + "mcast_mld_version %u ", + rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION])); if (tb[IFLA_BR_NF_CALL_IPTABLES]) - fprintf(f, "nf_call_iptables %u ", - rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); + print_uint(PRINT_ANY, + "nf_call_iptables", + "nf_call_iptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); if (tb[IFLA_BR_NF_CALL_IP6TABLES]) - fprintf(f, "nf_call_ip6tables %u ", - rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); + print_uint(PRINT_ANY, + "nf_call_ip6tables", + "nf_call_ip6tables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); if (tb[IFLA_BR_NF_CALL_ARPTABLES]) - fprintf(f, "nf_call_arptables %u ", - rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); + print_uint(PRINT_ANY, + "nf_call_arptables", + "nf_call_arptables %u ", + rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); } static void bridge_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_explain(f); } diff --git a/ip/iplink_bridge_slave.c b/ip/iplink_bridge_slave.c index 3e883328..80272b09 100644 --- a/ip/iplink_bridge_slave.c +++ b/ip/iplink_bridge_slave.c @@ -56,14 +56,52 @@ static const char *port_states[] = { static void print_portstate(FILE *f, __u8 state) { if (state <= BR_STATE_BLOCKING) - fprintf(f, "state %s ", port_states[state]); + print_string(PRINT_ANY, + "state", + "state %s ", + port_states[state]); else - fprintf(f, "state (%d) ", state); + print_int(PRINT_ANY, "state_index", "state (%d) ", state); } -static void print_onoff(FILE *f, char *flag, __u8 val) +static void _print_onoff(FILE *f, char *json_flag, char *flag, __u8 val) { - fprintf(f, "%s %s ", flag, val ? "on" : "off"); + if (is_json_context()) + print_bool(PRINT_JSON, flag, NULL, val); + else + fprintf(f, "%s %s ", flag, val ? "on" : "off"); +} + +static void _print_hex(FILE *f, + const char *json_attr, + const char *attr, + __u16 val) +{ + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%x", val); + print_string(PRINT_JSON, json_attr, NULL, b1); + } else { + fprintf(f, "%s 0x%x ", attr, val); + } +} + +static void _print_timer(FILE *f, const char *attr, struct rtattr *timer) +{ + struct timeval tv; + + __jiffies_to_tv(&tv, rta_getattr_u64(timer)); + if (is_json_context()) { + json_writer_t *jw = get_json_writer(); + + jsonw_name(jw, attr); + jsonw_printf(jw, "%i.%.2i", + (int)tv.tv_sec, (int)tv.tv_usec / 10000); + } else { + fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec, + (int)tv.tv_usec / 10000); + } } static void bridge_slave_print_opt(struct link_util *lu, FILE *f, @@ -76,59 +114,70 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f, print_portstate(f, rta_getattr_u8(tb[IFLA_BRPORT_STATE])); if (tb[IFLA_BRPORT_PRIORITY]) - fprintf(f, "priority %d ", - rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY])); + print_int(PRINT_ANY, + "priority", + "priority %d ", + rta_getattr_u16(tb[IFLA_BRPORT_PRIORITY])); if (tb[IFLA_BRPORT_COST]) - fprintf(f, "cost %d ", - rta_getattr_u32(tb[IFLA_BRPORT_COST])); + print_int(PRINT_ANY, + "cost", + "cost %d ", + rta_getattr_u32(tb[IFLA_BRPORT_COST])); if (tb[IFLA_BRPORT_MODE]) - print_onoff(f, "hairpin", - rta_getattr_u8(tb[IFLA_BRPORT_MODE])); + _print_onoff(f, "mode", "hairpin", + rta_getattr_u8(tb[IFLA_BRPORT_MODE])); if (tb[IFLA_BRPORT_GUARD]) - print_onoff(f, "guard", - rta_getattr_u8(tb[IFLA_BRPORT_GUARD])); + _print_onoff(f, "guard", "guard", + rta_getattr_u8(tb[IFLA_BRPORT_GUARD])); if (tb[IFLA_BRPORT_PROTECT]) - print_onoff(f, "root_block", - rta_getattr_u8(tb[IFLA_BRPORT_PROTECT])); + _print_onoff(f, "protect", "root_block", + rta_getattr_u8(tb[IFLA_BRPORT_PROTECT])); if (tb[IFLA_BRPORT_FAST_LEAVE]) - print_onoff(f, "fastleave", - rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE])); + _print_onoff(f, "fast_leave", "fastleave", + rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE])); if (tb[IFLA_BRPORT_LEARNING]) - print_onoff(f, "learning", - rta_getattr_u8(tb[IFLA_BRPORT_LEARNING])); + _print_onoff(f, "learning", "learning", + rta_getattr_u8(tb[IFLA_BRPORT_LEARNING])); if (tb[IFLA_BRPORT_UNICAST_FLOOD]) - print_onoff(f, "flood", - rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD])); + _print_onoff(f, "unicast_flood", "flood", + rta_getattr_u8(tb[IFLA_BRPORT_UNICAST_FLOOD])); if (tb[IFLA_BRPORT_ID]) - fprintf(f, "port_id 0x%x ", - rta_getattr_u16(tb[IFLA_BRPORT_ID])); + _print_hex(f, "id", "port_id", + rta_getattr_u16(tb[IFLA_BRPORT_ID])); if (tb[IFLA_BRPORT_NO]) - fprintf(f, "port_no 0x%x ", - rta_getattr_u16(tb[IFLA_BRPORT_NO])); + _print_hex(f, "no", "port_no", + rta_getattr_u16(tb[IFLA_BRPORT_NO])); if (tb[IFLA_BRPORT_DESIGNATED_PORT]) - fprintf(f, "designated_port %u ", - rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT])); + print_uint(PRINT_ANY, + "designated_port", + "designated_port %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_PORT])); if (tb[IFLA_BRPORT_DESIGNATED_COST]) - fprintf(f, "designated_cost %u ", - rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST])); + print_uint(PRINT_ANY, + "designated_cost", + "designated_cost %u ", + rta_getattr_u16(tb[IFLA_BRPORT_DESIGNATED_COST])); if (tb[IFLA_BRPORT_BRIDGE_ID]) { char bridge_id[32]; br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_BRIDGE_ID]), bridge_id, sizeof(bridge_id)); - fprintf(f, "designated_bridge %s ", bridge_id); + print_string(PRINT_ANY, + "bridge_id", + "designated_bridge %s ", + bridge_id); } if (tb[IFLA_BRPORT_ROOT_ID]) { @@ -136,65 +185,59 @@ static void bridge_slave_print_opt(struct link_util *lu, FILE *f, br_dump_bridge_id(RTA_DATA(tb[IFLA_BRPORT_ROOT_ID]), root_id, sizeof(root_id)); - fprintf(f, "designated_root %s ", root_id); + print_string(PRINT_ANY, + "root_id", + "designated_root %s ", root_id); } - if (tb[IFLA_BRPORT_HOLD_TIMER]) { - struct timeval tv; - __u64 htimer; + if (tb[IFLA_BRPORT_HOLD_TIMER]) + _print_timer(f, "hold_timer", tb[IFLA_BRPORT_HOLD_TIMER]); - htimer = rta_getattr_u64(tb[IFLA_BRPORT_HOLD_TIMER]); - __jiffies_to_tv(&tv, htimer); - fprintf(f, "hold_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } + if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]) + _print_timer(f, "message_age_timer", + tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]); - if (tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]) { - struct timeval tv; - __u64 agetimer; - - agetimer = rta_getattr_u64(tb[IFLA_BRPORT_MESSAGE_AGE_TIMER]); - __jiffies_to_tv(&tv, agetimer); - fprintf(f, "message_age_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } - - if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]) { - struct timeval tv; - __u64 fwdtimer; - - fwdtimer = rta_getattr_u64(tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]); - __jiffies_to_tv(&tv, fwdtimer); - fprintf(f, "forward_delay_timer %4i.%.2i ", (int)tv.tv_sec, - (int)tv.tv_usec/10000); - } + if (tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]) + _print_timer(f, "forward_delay_timer", + tb[IFLA_BRPORT_FORWARD_DELAY_TIMER]); if (tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK]) - fprintf(f, "topology_change_ack %u ", - rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])); + print_uint(PRINT_ANY, + "topology_change_ack", + "topology_change_ack %u ", + rta_getattr_u8(tb[IFLA_BRPORT_TOPOLOGY_CHANGE_ACK])); if (tb[IFLA_BRPORT_CONFIG_PENDING]) - fprintf(f, "config_pending %u ", - rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING])); + print_uint(PRINT_ANY, + "config_pending", + "config_pending %u ", + rta_getattr_u8(tb[IFLA_BRPORT_CONFIG_PENDING])); + if (tb[IFLA_BRPORT_PROXYARP]) - print_onoff(f, "proxy_arp", - rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP])); + _print_onoff(f, "proxyarp", "proxy_arp", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP])); if (tb[IFLA_BRPORT_PROXYARP_WIFI]) - print_onoff(f, "proxy_arp_wifi", - rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI])); + _print_onoff(f, "proxyarp_wifi", "proxy_arp_wifi", + rta_getattr_u8(tb[IFLA_BRPORT_PROXYARP_WIFI])); if (tb[IFLA_BRPORT_MULTICAST_ROUTER]) - fprintf(f, "mcast_router %u ", - rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER])); + print_uint(PRINT_ANY, + "multicast_router", + "mcast_router %u ", + rta_getattr_u8(tb[IFLA_BRPORT_MULTICAST_ROUTER])); if (tb[IFLA_BRPORT_FAST_LEAVE]) - print_onoff(f, "mcast_fast_leave", - rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE])); + // not printing any json here because + // we already printed fast_leave before + print_string(PRINT_FP, + NULL, + "mcast_fast_leave %s ", + rta_getattr_u8(tb[IFLA_BRPORT_FAST_LEAVE]) ? "on" : "off"); if (tb[IFLA_BRPORT_MCAST_FLOOD]) - print_onoff(f, "mcast_flood", - rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD])); + _print_onoff(f, "mcast_flood", "mcast_flood", + rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD])); } static void bridge_slave_parse_on_off(char *arg_name, char *arg_val, diff --git a/ip/iplink_can.c b/ip/iplink_can.c index 2954010f..587413da 100644 --- a/ip/iplink_can.c +++ b/ip/iplink_can.c @@ -89,11 +89,11 @@ static void set_ctrlmode(char *name, char *arg, static void print_ctrlmode(FILE *f, __u32 cm) { - fprintf(f, "<"); -#define _PF(cmflag, cmname) \ - if (cm & cmflag) { \ - cm &= ~cmflag; \ - fprintf(f, "%s%s", cmname, cm ? "," : ""); \ + open_json_array(PRINT_ANY, is_json_context() ? "ctrlmode" : "<"); +#define _PF(cmflag, cmname) \ + if (cm & cmflag) { \ + cm &= ~cmflag; \ + print_string(PRINT_ANY, NULL, cm ? "%s," : "%s", cmname); \ } _PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK"); _PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY"); @@ -105,8 +105,8 @@ static void print_ctrlmode(FILE *f, __u32 cm) _PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK"); #undef _PF if (cm) - fprintf(f, "%x", cm); - fprintf(f, "> "); + print_hex(PRINT_ANY, NULL, "%x", cm); + close_json_array(PRINT_ANY, "> "); } static int can_parse_opt(struct link_util *lu, int argc, char **argv, @@ -260,6 +260,14 @@ static const char *can_state_names[CAN_STATE_MAX] = { [CAN_STATE_SLEEPING] = "SLEEPING" }; +static void can_print_json_timing_min_max(const char *attr, int min, int max) +{ + open_json_object(attr); + print_int(PRINT_JSON, "min", NULL, min); + print_int(PRINT_JSON, "max", NULL, max); + close_json_object(); +} + static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) { if (!tb) @@ -283,24 +291,53 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) struct can_berr_counter *bc = RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]); - fprintf(f, "(berr-counter tx %d rx %d) ", bc->txerr, bc->rxerr); + if (is_json_context()) { + open_json_object("berr_counter"); + print_int(PRINT_JSON, "tx", NULL, bc->txerr); + print_int(PRINT_JSON, "rx", NULL, bc->rxerr); + close_json_object(); + } else { + fprintf(f, "(berr-counter tx %d rx %d) ", + bc->txerr, bc->rxerr); + } } if (tb[IFLA_CAN_RESTART_MS]) { __u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]); - fprintf(f, "restart-ms %d ", *restart_ms); + print_int(PRINT_ANY, + "restart_ms", + "restart-ms %d ", + *restart_ms); } /* bittiming is irrelevant if fixed bitrate is defined */ if (tb[IFLA_CAN_BITTIMING] && !tb[IFLA_CAN_BITRATE_CONST]) { struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]); - fprintf(f, "\n bitrate %d sample-point %.3f ", - bt->bitrate, (float)bt->sample_point / 1000.); - fprintf(f, "\n tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d", - bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2, - bt->sjw); + if (is_json_context()) { + open_json_object("bittiming"); + print_int(PRINT_ANY, "bitrate", NULL, bt->bitrate); + jsonw_float_field_fmt(get_json_writer(), + "sample_point", "%.3f", + (float) bt->sample_point / 1000.); + print_int(PRINT_ANY, "tq", NULL, bt->tq); + print_int(PRINT_ANY, "prop_seg", NULL, bt->prop_seg); + print_int(PRINT_ANY, "phase_seg1", + NULL, bt->phase_seg1); + print_int(PRINT_ANY, "phase_seg2", + NULL, bt->phase_seg2); + print_int(PRINT_ANY, "sjw", NULL, bt->sjw); + close_json_object(); + } else { + fprintf(f, "\n bitrate %d sample-point %.3f ", + bt->bitrate, (float) bt->sample_point / 1000.); + fprintf(f, + "\n tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d", + bt->tq, bt->prop_seg, + bt->phase_seg1, bt->phase_seg2, + bt->sjw); + } } /* bittiming const is irrelevant if fixed bitrate is defined */ @@ -308,40 +345,68 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) struct can_bittiming_const *btc = RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]); - fprintf(f, "\n %s: tseg1 %d..%d tseg2 %d..%d " - "sjw 1..%d brp %d..%d brp-inc %d", - btc->name, btc->tseg1_min, btc->tseg1_max, - btc->tseg2_min, btc->tseg2_max, btc->sjw_max, - btc->brp_min, btc->brp_max, btc->brp_inc); + if (is_json_context()) { + open_json_object("bittiming_const"); + print_string(PRINT_JSON, "name", NULL, btc->name); + can_print_json_timing_min_max("tseg1", + btc->tseg1_min, + btc->tseg1_max); + can_print_json_timing_min_max("tseg2", + btc->tseg2_min, + btc->tseg2_max); + can_print_json_timing_min_max("sjw", 1, btc->sjw_max); + can_print_json_timing_min_max("brp", + btc->brp_min, + btc->brp_max); + print_int(PRINT_JSON, "brp_inc", NULL, btc->brp_inc); + close_json_object(); + } else { + fprintf(f, "\n %s: tseg1 %d..%d tseg2 %d..%d " + "sjw 1..%d brp %d..%d brp-inc %d", + btc->name, btc->tseg1_min, btc->tseg1_max, + btc->tseg2_min, btc->tseg2_max, btc->sjw_max, + btc->brp_min, btc->brp_max, btc->brp_inc); + } } if (tb[IFLA_CAN_BITRATE_CONST]) { __u32 *bitrate_const = RTA_DATA(tb[IFLA_CAN_BITRATE_CONST]); int bitrate_cnt = RTA_PAYLOAD(tb[IFLA_CAN_BITRATE_CONST]) / - sizeof(*bitrate_const); + sizeof(*bitrate_const); int i; __u32 bitrate = 0; if (tb[IFLA_CAN_BITTIMING]) { struct can_bittiming *bt = - RTA_DATA(tb[IFLA_CAN_BITTIMING]); + RTA_DATA(tb[IFLA_CAN_BITTIMING]); bitrate = bt->bitrate; } - fprintf(f, "\n bitrate %u", bitrate); - fprintf(f, "\n ["); + if (is_json_context()) { + print_uint(PRINT_JSON, + "bittiming_bitrate", + NULL, bitrate); + open_json_array(PRINT_JSON, "bitrate_const"); + for (i = 0; i < bitrate_cnt; ++i) + print_uint(PRINT_JSON, NULL, NULL, + bitrate_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n bitrate %u", bitrate); + fprintf(f, "\n ["); + + for (i = 0; i < bitrate_cnt - 1; ++i) { + /* This will keep lines below 80 signs */ + if (!(i % 6) && i) + fprintf(f, "\n "); + + fprintf(f, "%8u, ", bitrate_const[i]); + } - for (i = 0; i < bitrate_cnt - 1; ++i) { - /* This will keep lines below 80 signs */ if (!(i % 6) && i) fprintf(f, "\n "); - - fprintf(f, "%8u, ", bitrate_const[i]); + fprintf(f, "%8u ]", bitrate_const[i]); } - - if (!(i % 6) && i) - fprintf(f, "\n "); - fprintf(f, "%8u ]", bitrate_const[i]); } /* data bittiming is irrelevant if fixed bitrate is defined */ @@ -349,12 +414,30 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) struct can_bittiming *dbt = RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); - fprintf(f, "\n dbitrate %d dsample-point %.3f ", - dbt->bitrate, (float)dbt->sample_point / 1000.); - fprintf(f, "\n dtq %d dprop-seg %d dphase-seg1 %d " - "dphase-seg2 %d dsjw %d", - dbt->tq, dbt->prop_seg, dbt->phase_seg1, - dbt->phase_seg2, dbt->sjw); + if (is_json_context()) { + open_json_object("data_bittiming"); + print_int(PRINT_JSON, "bitrate", NULL, dbt->bitrate); + jsonw_float_field_fmt(get_json_writer(), + "sample_point", + "%.3f", + (float) dbt->sample_point / 1000.); + print_int(PRINT_JSON, "tq", NULL, dbt->tq); + print_int(PRINT_JSON, "prop_seg", NULL, dbt->prop_seg); + print_int(PRINT_JSON, "phase_seg1", + NULL, dbt->phase_seg1); + print_int(PRINT_JSON, "phase_seg2", + NULL, dbt->phase_seg2); + print_int(PRINT_JSON, "sjw", NULL, dbt->sjw); + close_json_object(); + } else { + fprintf(f, "\n dbitrate %d dsample-point %.3f ", + dbt->bitrate, + (float) dbt->sample_point / 1000.); + fprintf(f, "\n dtq %d dprop-seg %d dphase-seg1 %d " + "dphase-seg2 %d dsjw %d", + dbt->tq, dbt->prop_seg, dbt->phase_seg1, + dbt->phase_seg2, dbt->sjw); + } } /* data bittiming const is irrelevant if fixed bitrate is defined */ @@ -363,63 +446,102 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) struct can_bittiming_const *dbtc = RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]); - fprintf(f, "\n %s: dtseg1 %d..%d dtseg2 %d..%d " - "dsjw 1..%d dbrp %d..%d dbrp-inc %d", - dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max, - dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max, - dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc); + if (is_json_context()) { + open_json_object("data_bittiming_const"); + print_string(PRINT_JSON, "name", NULL, dbtc->name); + can_print_json_timing_min_max("tseg1", + dbtc->tseg1_min, + dbtc->tseg1_max); + can_print_json_timing_min_max("tseg2", + dbtc->tseg2_min, + dbtc->tseg2_max); + can_print_json_timing_min_max("sjw", 1, dbtc->sjw_max); + can_print_json_timing_min_max("brp", + dbtc->brp_min, + dbtc->brp_max); + + print_int(PRINT_JSON, "brp_inc", NULL, dbtc->brp_inc); + close_json_object(); + } else { + fprintf(f, "\n %s: dtseg1 %d..%d dtseg2 %d..%d " + "dsjw 1..%d dbrp %d..%d dbrp-inc %d", + dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max, + dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max, + dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc); + } } if (tb[IFLA_CAN_DATA_BITRATE_CONST]) { __u32 *dbitrate_const = - RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]); + RTA_DATA(tb[IFLA_CAN_DATA_BITRATE_CONST]); int dbitrate_cnt = - RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) / - sizeof(*dbitrate_const); + RTA_PAYLOAD(tb[IFLA_CAN_DATA_BITRATE_CONST]) / + sizeof(*dbitrate_const); int i; __u32 dbitrate = 0; if (tb[IFLA_CAN_DATA_BITTIMING]) { struct can_bittiming *dbt = - RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); + RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]); dbitrate = dbt->bitrate; } - fprintf(f, "\n dbitrate %u", dbitrate); - fprintf(f, "\n ["); + if (is_json_context()) { + print_uint(PRINT_JSON, "data_bittiming_bitrate", + NULL, dbitrate); + open_json_array(PRINT_JSON, "data_bitrate_const"); + for (i = 0; i < dbitrate_cnt; ++i) + print_uint(PRINT_JSON, NULL, NULL, + dbitrate_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n dbitrate %u", dbitrate); + fprintf(f, "\n ["); + + for (i = 0; i < dbitrate_cnt - 1; ++i) { + /* This will keep lines below 80 signs */ + if (!(i % 6) && i) + fprintf(f, "\n "); + + fprintf(f, "%8u, ", dbitrate_const[i]); + } - for (i = 0; i < dbitrate_cnt - 1; ++i) { - /* This will keep lines below 80 signs */ if (!(i % 6) && i) fprintf(f, "\n "); - - fprintf(f, "%8u, ", dbitrate_const[i]); + fprintf(f, "%8u ]", dbitrate_const[i]); } - - if (!(i % 6) && i) - fprintf(f, "\n "); - fprintf(f, "%8u ]", dbitrate_const[i]); } if (tb[IFLA_CAN_TERMINATION_CONST] && tb[IFLA_CAN_TERMINATION]) { __u16 *trm = RTA_DATA(tb[IFLA_CAN_TERMINATION]); __u16 *trm_const = RTA_DATA(tb[IFLA_CAN_TERMINATION_CONST]); int trm_cnt = RTA_PAYLOAD(tb[IFLA_CAN_TERMINATION_CONST]) / - sizeof(*trm_const); + sizeof(*trm_const); int i; - fprintf(f, "\n termination %hu [ ", *trm); + if (is_json_context()) { + print_hu(PRINT_JSON, "termination", NULL, *trm); + open_json_array(PRINT_JSON, "termination_const"); + for (i = 0; i < trm_cnt; ++i) + print_hu(PRINT_JSON, NULL, NULL, trm_const[i]); + close_json_array(PRINT_JSON, NULL); + } else { + fprintf(f, "\n termination %hu [ ", *trm); - for (i = 0; i < trm_cnt - 1; ++i) - fprintf(f, "%hu, ", trm_const[i]); + for (i = 0; i < trm_cnt - 1; ++i) + fprintf(f, "%hu, ", trm_const[i]); - fprintf(f, "%hu ]", trm_const[i]); + fprintf(f, "%hu ]", trm_const[i]); + } } if (tb[IFLA_CAN_CLOCK]) { struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]); - fprintf(f, "\n clock %d", clock->freq); + print_int(PRINT_ANY, + "clock", + "\n clock %d", + clock->freq); } } @@ -431,17 +553,32 @@ static void can_print_xstats(struct link_util *lu, if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) { stats = RTA_DATA(xstats); - fprintf(f, "\n re-started bus-errors arbit-lost " - "error-warn error-pass bus-off"); - fprintf(f, "\n %-10d %-10d %-10d %-10d %-10d %-10d", - stats->restarts, stats->bus_error, - stats->arbitration_lost, stats->error_warning, - stats->error_passive, stats->bus_off); + + if (is_json_context()) { + print_int(PRINT_JSON, "restarts", + NULL, stats->restarts); + print_int(PRINT_JSON, "bus_error", + NULL, stats->bus_error); + print_int(PRINT_JSON, "arbitration_lost", + NULL, stats->arbitration_lost); + print_int(PRINT_JSON, "error_warning", + NULL, stats->error_warning); + print_int(PRINT_JSON, "error_passive", + NULL, stats->error_passive); + print_int(PRINT_JSON, "bus_off", NULL, stats->bus_off); + } else { + fprintf(f, "\n re-started bus-errors arbit-lost " + "error-warn error-pass bus-off"); + fprintf(f, "\n %-10d %-10d %-10d %-10d %-10d %-10d", + stats->restarts, stats->bus_error, + stats->arbitration_lost, stats->error_warning, + stats->error_passive, stats->bus_off); + } } } static void can_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_usage(f); } diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c index 594a3e59..f0f1d1c7 100644 --- a/ip/iplink_geneve.c +++ b/ip/iplink_geneve.c @@ -237,22 +237,28 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) return; vni = rta_getattr_u32(tb[IFLA_GENEVE_ID]); - fprintf(f, "id %u ", vni); + print_uint(PRINT_ANY, "id", "id %u ", vni); if (tb[IFLA_GENEVE_REMOTE]) { __be32 addr = rta_getattr_u32(tb[IFLA_GENEVE_REMOTE]); if (addr) - fprintf(f, "remote %s ", - format_host(AF_INET, 4, &addr)); + print_string(PRINT_ANY, + "remote", + "remote %s ", + format_host(AF_INET, 4, &addr)); } else if (tb[IFLA_GENEVE_REMOTE6]) { struct in6_addr addr; memcpy(&addr, RTA_DATA(tb[IFLA_GENEVE_REMOTE6]), sizeof(struct in6_addr)); if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { if (!IN6_IS_ADDR_MULTICAST(&addr)) - fprintf(f, "remote %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr)); + print_string(PRINT_ANY, + "remote6", + "remote %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); } } @@ -260,47 +266,81 @@ static void geneve_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) __u8 ttl = rta_getattr_u8(tb[IFLA_GENEVE_TTL]); if (ttl) - fprintf(f, "ttl %d ", ttl); + print_int(PRINT_ANY, "ttl", "ttl %d ", ttl); } if (tb[IFLA_GENEVE_TOS] && (tos = rta_getattr_u8(tb[IFLA_GENEVE_TOS]))) { - if (tos == 1) - fprintf(f, "tos inherit "); - else - fprintf(f, "tos %#x ", tos); + if (is_json_context()) { + print_0xhex(PRINT_JSON, "tos", "%#x", tos); + } else { + if (tos == 1) { + print_string(PRINT_FP, + "tos", + "tos %s ", + "inherit"); + } else { + fprintf(f, "tos %#x ", tos); + } + } } if (tb[IFLA_GENEVE_LABEL]) { __u32 label = rta_getattr_u32(tb[IFLA_GENEVE_LABEL]); if (label) - fprintf(f, "flowlabel %#x ", ntohl(label)); + print_0xhex(PRINT_ANY, + "label", + "flowlabel %#x ", + ntohl(label)); } if (tb[IFLA_GENEVE_PORT]) - fprintf(f, "dstport %u ", - rta_getattr_be16(tb[IFLA_GENEVE_PORT])); + print_uint(PRINT_ANY, + "port", + "dstport %u ", + rta_getattr_be16(tb[IFLA_GENEVE_PORT])); if (tb[IFLA_GENEVE_COLLECT_METADATA]) - fputs("external ", f); + print_bool(PRINT_ANY, "collect_metadata", "external ", true); if (tb[IFLA_GENEVE_UDP_CSUM]) { - if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) - fputs("no", f); - fputs("udpcsum ", f); + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_csum", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_CSUM])) + fputs("no", f); + fputs("udpcsum ", f); + } } if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX]) { - if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) - fputs("no", f); - fputs("udp6zerocsumtx ", f); + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_zero_csum6_tx", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_TX])) + fputs("no", f); + fputs("udp6zerocsumtx ", f); + } } if (tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX]) { - if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) - fputs("no", f); - fputs("udp6zerocsumrx ", f); + if (is_json_context()) { + print_bool(PRINT_JSON, + "udp_zero_csum6_rx", + NULL, + rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])); + } else { + if (!rta_getattr_u8(tb[IFLA_GENEVE_UDP_ZERO_CSUM6_RX])) + fputs("no", f); + fputs("udp6zerocsumrx ", f); + } } } diff --git a/ip/iplink_hsr.c b/ip/iplink_hsr.c index 696b2c91..c673ccf7 100644 --- a/ip/iplink_hsr.c +++ b/ip/iplink_hsr.c @@ -110,30 +110,36 @@ static void hsr_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]) < ETH_ALEN) return; - fprintf(f, "slave1 "); if (tb[IFLA_HSR_SLAVE1]) - fprintf(f, "%s ", - ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1]))); + print_string(PRINT_ANY, + "slave1", + "slave1 %s ", + ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE1]))); else - fprintf(f, " "); + print_null(PRINT_ANY, "slave1", "slave1 %s ", ""); - fprintf(f, "slave2 "); if (tb[IFLA_HSR_SLAVE2]) - fprintf(f, "%s ", - ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2]))); + print_string(PRINT_ANY, + "slave2", + "slave2 %s ", + ll_index_to_name(rta_getattr_u32(tb[IFLA_HSR_SLAVE2]))); else - fprintf(f, " "); + print_null(PRINT_ANY, "slave2", "slave2 %s ", ""); if (tb[IFLA_HSR_SEQ_NR]) - fprintf(f, "sequence %d ", - rta_getattr_u16(tb[IFLA_HSR_SEQ_NR])); + print_int(PRINT_ANY, + "seq_nr", + "sequence %d ", + rta_getattr_u16(tb[IFLA_HSR_SEQ_NR])); if (tb[IFLA_HSR_SUPERVISION_ADDR]) - fprintf(f, "supervision %s ", - ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]), - RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]), - ARPHRD_VOID, - b1, sizeof(b1))); + print_string(PRINT_ANY, + "supervision_addr", + "supervision %s ", + ll_addr_n2a(RTA_DATA(tb[IFLA_HSR_SUPERVISION_ADDR]), + RTA_PAYLOAD(tb[IFLA_HSR_SUPERVISION_ADDR]), + ARPHRD_VOID, + b1, sizeof(b1))); } static void hsr_print_help(struct link_util *lu, int argc, char **argv, diff --git a/ip/iplink_ipoib.c b/ip/iplink_ipoib.c index 86dc65ca..e69bda0e 100644 --- a/ip/iplink_ipoib.c +++ b/ip/iplink_ipoib.c @@ -91,23 +91,43 @@ static void ipoib_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) RTA_PAYLOAD(tb[IFLA_IPOIB_PKEY]) < sizeof(__u16)) return; - fprintf(f, "pkey %#.4x ", rta_getattr_u16(tb[IFLA_IPOIB_PKEY])); + __u16 pkey = rta_getattr_u16(tb[IFLA_IPOIB_PKEY]); + + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%#.4x", pkey); + print_string(PRINT_JSON, "key", NULL, b1); + } else { + fprintf(f, "pkey %#.4x ", pkey); + } if (!tb[IFLA_IPOIB_MODE] || RTA_PAYLOAD(tb[IFLA_IPOIB_MODE]) < sizeof(__u16)) return; mode = rta_getattr_u16(tb[IFLA_IPOIB_MODE]); - fprintf(f, "mode %s ", + + const char *mode_str = mode == IPOIB_MODE_DATAGRAM ? "datagram" : - mode == IPOIB_MODE_CONNECTED ? "connected" : - "unknown"); + mode == IPOIB_MODE_CONNECTED ? "connected" : "unknown"; + + print_string(PRINT_ANY, "mode", "mode %s ", mode_str); if (!tb[IFLA_IPOIB_UMCAST] || RTA_PAYLOAD(tb[IFLA_IPOIB_UMCAST]) < sizeof(__u16)) return; - fprintf(f, "umcast %.4x ", rta_getattr_u16(tb[IFLA_IPOIB_UMCAST])); + __u16 umcast = rta_getattr_u16(tb[IFLA_IPOIB_UMCAST]); + + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%.4x", umcast); + print_string(PRINT_JSON, "umcast", NULL, b1); + } else { + fprintf(f, "umcast %.4x ", umcast); + } } static void ipoib_print_help(struct link_util *lu, int argc, char **argv, diff --git a/ip/iplink_ipvlan.c b/ip/iplink_ipvlan.c index f7735f3a..9f48309e 100644 --- a/ip/iplink_ipvlan.c +++ b/ip/iplink_ipvlan.c @@ -68,11 +68,11 @@ static void ipvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_IPVLAN_MODE]) { if (RTA_PAYLOAD(tb[IFLA_IPVLAN_MODE]) == sizeof(__u16)) { __u16 mode = rta_getattr_u16(tb[IFLA_IPVLAN_MODE]); - - fprintf(f, " mode %s ", - mode == IPVLAN_MODE_L2 ? "l2" : + const char *mode_str = mode == IPVLAN_MODE_L2 ? "l2" : mode == IPVLAN_MODE_L3 ? "l3" : - mode == IPVLAN_MODE_L3S ? "l3s" : "unknown"); + mode == IPVLAN_MODE_L3S ? "l3s" : "unknown"; + + print_string(PRINT_ANY, "mode", " mode %s ", mode_str); } } } diff --git a/ip/iplink_macvlan.c b/ip/iplink_macvlan.c index 662eb6ff..b966a615 100644 --- a/ip/iplink_macvlan.c +++ b/ip/iplink_macvlan.c @@ -193,13 +193,15 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] return; mode = rta_getattr_u32(tb[IFLA_MACVLAN_MODE]); - fprintf(f, "mode %s ", - mode == MACVLAN_MODE_PRIVATE ? "private" - : mode == MACVLAN_MODE_VEPA ? "vepa" - : mode == MACVLAN_MODE_BRIDGE ? "bridge" - : mode == MACVLAN_MODE_PASSTHRU ? "passthru" - : mode == MACVLAN_MODE_SOURCE ? "source" - : "unknown"); + print_string(PRINT_ANY, + "mode", + "mode %s ", + mode == MACVLAN_MODE_PRIVATE ? "private" + : mode == MACVLAN_MODE_VEPA ? "vepa" + : mode == MACVLAN_MODE_BRIDGE ? "bridge" + : mode == MACVLAN_MODE_PASSTHRU ? "passthru" + : mode == MACVLAN_MODE_SOURCE ? "source" + : "unknown"); if (!tb[IFLA_MACVLAN_FLAGS] || RTA_PAYLOAD(tb[IFLA_MACVLAN_FLAGS]) < sizeof(__u16)) @@ -208,7 +210,7 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] flags = rta_getattr_u16(tb[IFLA_MACVLAN_FLAGS]); if (flags & MACVLAN_FLAG_NOPROMISC) - fprintf(f, "nopromisc "); + print_bool(PRINT_ANY, "nopromisc", "nopromisc ", true); /* in source mode, there are more options to print */ @@ -220,7 +222,7 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] return; count = rta_getattr_u32(tb[IFLA_MACVLAN_MACADDR_COUNT]); - fprintf(f, "remotes (%d) ", count); + print_int(PRINT_ANY, "macaddr_count", "remotes (%d) ", count); if (!tb[IFLA_MACVLAN_MACADDR_DATA]) return; @@ -228,18 +230,29 @@ static void macvlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[] rta = RTA_DATA(tb[IFLA_MACVLAN_MACADDR_DATA]); len = RTA_PAYLOAD(tb[IFLA_MACVLAN_MACADDR_DATA]); + open_json_array(PRINT_JSON, "macaddr_data"); for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { if (rta->rta_type != IFLA_MACVLAN_MACADDR || RTA_PAYLOAD(rta) < 6) continue; addr = RTA_DATA(rta); - fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], - addr[1], addr[2], addr[3], addr[4], addr[5]); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), + "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + print_string(PRINT_JSON, NULL, NULL, b1); + } else { + fprintf(f, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ", addr[0], + addr[1], addr[2], addr[3], addr[4], addr[5]); + } } + close_json_array(PRINT_JSON, NULL); } static void macvlan_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_explain(lu, f); } diff --git a/ip/iplink_vlan.c b/ip/iplink_vlan.c index b47236d8..4d78cf9e 100644 --- a/ip/iplink_vlan.c +++ b/ip/iplink_vlan.c @@ -164,37 +164,51 @@ static int vlan_parse_opt(struct link_util *lu, int argc, char **argv, return 0; } -static void vlan_print_map(FILE *f, char *name, struct rtattr *attr) +static void vlan_print_map(FILE *f, + const char *name_json, + const char *name_fp, + struct rtattr *attr) { struct ifla_vlan_qos_mapping *m; struct rtattr *i; int rem; - fprintf(f, "\n %s { ", name); + open_json_array(PRINT_JSON, name_json); + print_string(PRINT_FP, NULL, "\n %s { ", name_fp); rem = RTA_PAYLOAD(attr); for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { m = RTA_DATA(i); - fprintf(f, "%u:%u ", m->from, m->to); + + if (is_json_context()) { + open_json_object(NULL); + print_uint(PRINT_JSON, "from", NULL, m->from); + print_uint(PRINT_JSON, "to", NULL, m->to); + close_json_object(); + } else { + fprintf(f, "%u:%u ", m->from, m->to); + } } - fprintf(f, "} "); + + close_json_array(PRINT_JSON, NULL); + print_string(PRINT_FP, NULL, "%s ", "}"); } static void vlan_print_flags(FILE *fp, __u32 flags) { - fprintf(fp, "<"); -#define _PF(f) if (flags & VLAN_FLAG_##f) { \ - flags &= ~VLAN_FLAG_##f; \ - fprintf(fp, #f "%s", flags ? "," : ""); \ - } + open_json_array(PRINT_ANY, is_json_context() ? "flags" : "<"); +#define _PF(f) if (flags & VLAN_FLAG_##f) { \ + flags &= ~VLAN_FLAG_##f; \ + print_string(PRINT_ANY, NULL, flags ? "%s," : "%s", #f); \ + } _PF(REORDER_HDR); _PF(GVRP); _PF(MVRP); _PF(LOOSE_BINDING); #undef _PF if (flags) - fprintf(fp, "%x", flags); - fprintf(fp, "> "); + print_hex(PRINT_ANY, NULL, "%x", flags); + close_json_array(PRINT_ANY, "> "); } static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) @@ -214,13 +228,19 @@ static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) return; if (tb[IFLA_VLAN_PROTOCOL]) - fprintf(f, "protocol %s ", - ll_proto_n2a(rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]), + print_string(PRINT_ANY, + "protocol", + "protocol %s ", + ll_proto_n2a( + rta_getattr_u16(tb[IFLA_VLAN_PROTOCOL]), b1, sizeof(b1))); else - fprintf(f, "protocol 802.1q "); + print_string(PRINT_ANY, "protocol", "protocol %s ", "802.1q"); - fprintf(f, "id %u ", rta_getattr_u16(tb[IFLA_VLAN_ID])); + print_uint(PRINT_ANY, + "id", + "id %u ", + rta_getattr_u16(tb[IFLA_VLAN_ID])); if (tb[IFLA_VLAN_FLAGS]) { if (RTA_PAYLOAD(tb[IFLA_VLAN_FLAGS]) < sizeof(*flags)) @@ -229,13 +249,19 @@ static void vlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) vlan_print_flags(f, flags->flags); } if (tb[IFLA_VLAN_INGRESS_QOS]) - vlan_print_map(f, "ingress-qos-map", tb[IFLA_VLAN_INGRESS_QOS]); + vlan_print_map(f, + "ingress_qos", + "ingress-qos-map", + tb[IFLA_VLAN_INGRESS_QOS]); if (tb[IFLA_VLAN_EGRESS_QOS]) - vlan_print_map(f, "egress-qos-map", tb[IFLA_VLAN_EGRESS_QOS]); + vlan_print_map(f, + "egress_qos", + "egress-qos-map", + tb[IFLA_VLAN_EGRESS_QOS]); } static void vlan_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_explain(f); } diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c index 2b85a3a5..7a1bb5e7 100644 --- a/ip/iplink_vrf.c +++ b/ip/iplink_vrf.c @@ -62,7 +62,10 @@ static void vrf_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) return; if (tb[IFLA_VRF_TABLE]) - fprintf(f, "table %u ", rta_getattr_u32(tb[IFLA_VRF_TABLE])); + print_uint(PRINT_ANY, + "table", + "table %u ", + rta_getattr_u32(tb[IFLA_VRF_TABLE])); } static void vrf_slave_print_opt(struct link_util *lu, FILE *f, @@ -72,13 +75,15 @@ static void vrf_slave_print_opt(struct link_util *lu, FILE *f, return; if (tb[IFLA_VRF_PORT_TABLE]) { - fprintf(f, "table %u ", - rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE])); + print_uint(PRINT_ANY, + "table", + "table %u ", + rta_getattr_u32(tb[IFLA_VRF_PORT_TABLE])); } } static void vrf_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { vrf_explain(f); } diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c index 2bd619d4..a0530dda 100644 --- a/ip/iplink_vxlan.c +++ b/ip/iplink_vxlan.c @@ -406,18 +406,22 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) return; vni = rta_getattr_u32(tb[IFLA_VXLAN_ID]); - fprintf(f, "id %u ", vni); + print_uint(PRINT_ANY, "id", "id %u ", vni); if (tb[IFLA_VXLAN_GROUP]) { __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]); if (addr) { if (IN_MULTICAST(ntohl(addr))) - fprintf(f, "group %s ", - format_host(AF_INET, 4, &addr)); + print_string(PRINT_ANY, + "group", + "group %s ", + format_host(AF_INET, 4, &addr)); else - fprintf(f, "remote %s ", - format_host(AF_INET, 4, &addr)); + print_string(PRINT_ANY, + "remote", + "remote %s ", + format_host(AF_INET, 4, &addr)); } } else if (tb[IFLA_VXLAN_GROUP6]) { struct in6_addr addr; @@ -425,11 +429,19 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr)); if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) { if (IN6_IS_ADDR_MULTICAST(&addr)) - fprintf(f, "group %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr)); + print_string(PRINT_ANY, + "group6", + "group %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); else - fprintf(f, "remote %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr)); + print_string(PRINT_ANY, + "remote6", + "remote %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); } } @@ -437,15 +449,21 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_LOCAL]); if (addr) - fprintf(f, "local %s ", - format_host(AF_INET, 4, &addr)); + print_string(PRINT_ANY, + "local", + "local %s ", + format_host(AF_INET, 4, &addr)); } else if (tb[IFLA_VXLAN_LOCAL6]) { struct in6_addr addr; memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr)); if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) - fprintf(f, "local %s ", - format_host(AF_INET6, sizeof(struct in6_addr), &addr)); + print_string(PRINT_ANY, + "local6", + "local %s ", + format_host(AF_INET6, + sizeof(struct in6_addr), + &addr)); } if (tb[IFLA_VXLAN_LINK] && @@ -453,110 +471,155 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } if (tb[IFLA_VXLAN_PORT_RANGE]) { const struct ifla_vxlan_port_range *r = RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]); - fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high)); + if (is_json_context()) { + open_json_object("port_range"); + print_uint(PRINT_JSON, "low", NULL, ntohs(r->low)); + print_uint(PRINT_JSON, "high", NULL, ntohs(r->high)); + close_json_object(); + } else { + fprintf(f, "srcport %u %u ", + ntohs(r->low), ntohs(r->high)); + } } if (tb[IFLA_VXLAN_PORT]) - fprintf(f, "dstport %u ", - rta_getattr_be16(tb[IFLA_VXLAN_PORT])); + print_uint(PRINT_ANY, + "port", + "dstport %u ", + rta_getattr_be16(tb[IFLA_VXLAN_PORT])); - if (tb[IFLA_VXLAN_LEARNING] && - !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING])) - fputs("nolearning ", f); + if (tb[IFLA_VXLAN_LEARNING]) { + __u8 learning = rta_getattr_u8(tb[IFLA_VXLAN_LEARNING]); + + print_bool(PRINT_JSON, "learning", NULL, learning); + if (!learning) + print_bool(PRINT_FP, NULL, "nolearning ", true); + } if (tb[IFLA_VXLAN_PROXY] && rta_getattr_u8(tb[IFLA_VXLAN_PROXY])) - fputs("proxy ", f); + print_bool(PRINT_ANY, "proxy", "proxy ", true); if (tb[IFLA_VXLAN_RSC] && rta_getattr_u8(tb[IFLA_VXLAN_RSC])) - fputs("rsc ", f); + print_bool(PRINT_ANY, "rsc", "rsc ", true); if (tb[IFLA_VXLAN_L2MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L2MISS])) - fputs("l2miss ", f); + print_bool(PRINT_ANY, "l2miss", "l2miss ", true); if (tb[IFLA_VXLAN_L3MISS] && rta_getattr_u8(tb[IFLA_VXLAN_L3MISS])) - fputs("l3miss ", f); + print_bool(PRINT_ANY, "l3miss", "l3miss ", true); if (tb[IFLA_VXLAN_TOS] && (tos = rta_getattr_u8(tb[IFLA_VXLAN_TOS]))) { - if (tos == 1) - fprintf(f, "tos inherit "); - else - fprintf(f, "tos %#x ", tos); + if (is_json_context()) { + print_0xhex(PRINT_JSON, "tos", "%#x", tos); + } else { + if (tos == 1) + fprintf(f, "tos %s ", "inherit"); + else + fprintf(f, "tos %#x ", tos); + } } if (tb[IFLA_VXLAN_TTL]) { __u8 ttl = rta_getattr_u8(tb[IFLA_VXLAN_TTL]); if (ttl) - fprintf(f, "ttl %d ", ttl); + print_int(PRINT_ANY, "ttl", "ttl %d ", ttl); + else + print_int(PRINT_JSON, "ttl", NULL, ttl); } if (tb[IFLA_VXLAN_LABEL]) { __u32 label = rta_getattr_u32(tb[IFLA_VXLAN_LABEL]); if (label) - fprintf(f, "flowlabel %#x ", ntohl(label)); + print_0xhex(PRINT_ANY, + "label", + "flowlabel %#x ", + ntohl(label)); } if (tb[IFLA_VXLAN_AGEING]) { __u32 age = rta_getattr_u32(tb[IFLA_VXLAN_AGEING]); if (age == 0) - fprintf(f, "ageing none "); + print_uint(PRINT_ANY, "ageing", "ageing none ", 0); else - fprintf(f, "ageing %u ", age); + print_uint(PRINT_ANY, "ageing", "ageing %u ", age); } if (tb[IFLA_VXLAN_LIMIT] && ((maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT])) != 0)) - fprintf(f, "maxaddr %u ", maxaddr); + print_uint(PRINT_ANY, "limit", "maxaddr %u ", maxaddr); if (tb[IFLA_VXLAN_UDP_CSUM]) { - if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM])) - fputs("no", f); - fputs("udpcsum ", f); + __u8 udp_csum = rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM]); + + if (is_json_context()) { + print_bool(PRINT_ANY, "udp_csum", NULL, udp_csum); + } else { + if (!udp_csum) + fputs("no", f); + fputs("udpcsum ", f); + } } if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]) { - if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX])) - fputs("no", f); - fputs("udp6zerocsumtx ", f); + __u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX]); + + if (is_json_context()) { + print_bool(PRINT_ANY, + "udp_zero_csum6_tx", NULL, csum6); + } else { + if (!csum6) + fputs("no", f); + fputs("udp6zerocsumtx ", f); + } } if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]) { - if (!rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) - fputs("no", f); - fputs("udp6zerocsumrx ", f); + __u8 csum6 = rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]); + + if (is_json_context()) { + print_bool(PRINT_ANY, + "udp_zero_csum6_rx", + NULL, + csum6); + } else { + if (!csum6) + fputs("no", f); + fputs("udp6zerocsumrx ", f); + } } if (tb[IFLA_VXLAN_REMCSUM_TX] && rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_TX])) - fputs("remcsumtx ", f); + print_bool(PRINT_ANY, "remcsum_tx", "remcsumtx ", true); if (tb[IFLA_VXLAN_REMCSUM_RX] && rta_getattr_u8(tb[IFLA_VXLAN_REMCSUM_RX])) - fputs("remcsumrx ", f); + print_bool(PRINT_ANY, "remcsum_rx", "remcsumrx ", true); if (tb[IFLA_VXLAN_COLLECT_METADATA] && rta_getattr_u8(tb[IFLA_VXLAN_COLLECT_METADATA])) - fputs("external ", f); + print_bool(PRINT_ANY, "collect_metadata", "external ", true); if (tb[IFLA_VXLAN_GBP]) - fputs("gbp ", f); + print_bool(PRINT_ANY, "gbp", "gbp ", true); if (tb[IFLA_VXLAN_GPE]) - fputs("gpe ", f); + print_bool(PRINT_ANY, "gpe", "gpe ", true); } static void vxlan_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_explain(f); } diff --git a/ip/iplink_xdp.c b/ip/iplink_xdp.c index 5aa66fe7..71f77982 100644 --- a/ip/iplink_xdp.c +++ b/ip/iplink_xdp.c @@ -16,6 +16,7 @@ #include "xdp.h" #include "bpf_util.h" +#include "ip_common.h" extern int force; @@ -93,30 +94,34 @@ void xdp_dump(FILE *fp, struct rtattr *xdp, bool link, bool details) return; mode = rta_getattr_u8(tb[IFLA_XDP_ATTACHED]); - if (mode == XDP_ATTACHED_NONE) - return; - else if (details && link) - fprintf(fp, "%s prog/xdp", _SL_); - else if (mode == XDP_ATTACHED_DRV) - fprintf(fp, "xdp"); - else if (mode == XDP_ATTACHED_SKB) - fprintf(fp, "xdpgeneric"); - else if (mode == XDP_ATTACHED_HW) - fprintf(fp, "xdpoffload"); - else - fprintf(fp, "xdp[%u]", mode); + if (is_json_context()) { + print_uint(PRINT_JSON, "attached", NULL, mode); + } else { + if (mode == XDP_ATTACHED_NONE) + return; + else if (details && link) + fprintf(fp, "%s prog/xdp", _SL_); + else if (mode == XDP_ATTACHED_DRV) + fprintf(fp, "xdp"); + else if (mode == XDP_ATTACHED_SKB) + fprintf(fp, "xdpgeneric"); + else if (mode == XDP_ATTACHED_HW) + fprintf(fp, "xdpoffload"); + else + fprintf(fp, "xdp[%u]", mode); - if (tb[IFLA_XDP_PROG_ID]) - prog_id = rta_getattr_u32(tb[IFLA_XDP_PROG_ID]); - if (!details) { - if (prog_id && !link) - fprintf(fp, "/id:%u", prog_id); - fprintf(fp, " "); - return; - } + if (tb[IFLA_XDP_PROG_ID]) + prog_id = rta_getattr_u32(tb[IFLA_XDP_PROG_ID]); + if (!details) { + if (prog_id && !link) + fprintf(fp, "/id:%u", prog_id); + fprintf(fp, " "); + return; + } - if (prog_id) { - fprintf(fp, " "); - bpf_dump_prog_info(fp, prog_id); + if (prog_id) { + fprintf(fp, " "); + bpf_dump_prog_info(fp, prog_id); + } } } diff --git a/ip/ipmacsec.c b/ip/ipmacsec.c index aa89a00f..ecc371a5 100644 --- a/ip/ipmacsec.c +++ b/ip/ipmacsec.c @@ -561,9 +561,14 @@ static int validate_secy_dump(struct rtattr **attrs) static void print_flag(FILE *f, struct rtattr *attrs[], const char *desc, int field) { - if (attrs[field]) - fprintf(f, "%s %s ", desc, - values_on_off[!!rta_getattr_u8(attrs[field])]); + if (attrs[field]) { + const char *v = values_on_off[!!rta_getattr_u8(attrs[field])]; + + if (is_json_context()) + print_string(PRINT_JSON, desc, NULL, v); + else + fprintf(f, "%s %s ", desc, v); + } } #define DEFAULT_CIPHER_NAME "GCM-AES-128" @@ -1017,8 +1022,16 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) return; if (tb[IFLA_MACSEC_SCI]) { - fprintf(f, "sci %016llx ", - ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "%016llx", + ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + print_string(PRINT_JSON, "sci", NULL, b1); + } else { + fprintf(f, "sci %016llx ", + ntohll(rta_getattr_u64(tb[IFLA_MACSEC_SCI]))); + } } print_flag(f, tb, "protect", IFLA_MACSEC_PROTECT); @@ -1026,35 +1039,70 @@ static void macsec_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_MACSEC_CIPHER_SUITE]) { __u64 csid = rta_getattr_u64(tb[IFLA_MACSEC_CIPHER_SUITE]); - fprintf(f, "cipher %s ", cs_id_to_name(csid)); + print_string(PRINT_ANY, + "cipher_suite", + "cipher %s ", + cs_id_to_name(csid)); } if (tb[IFLA_MACSEC_ICV_LEN]) { - fprintf(f, "icvlen %hhu ", - rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + if (is_json_context()) { + char b2[4]; + + snprintf(b2, sizeof(b2), "%hhu", + rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + print_uint(PRINT_JSON, "icv_len", NULL, atoi(b2)); + } else { + fprintf(f, "icvlen %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ICV_LEN])); + } } if (tb[IFLA_MACSEC_ENCODING_SA]) { - fprintf(f, "encodingsa %hhu ", - rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + if (is_json_context()) { + char b2[4]; + + snprintf(b2, sizeof(b2), "%hhu", + rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + print_uint(PRINT_JSON, "encoding_sa", NULL, atoi(b2)); + } else { + fprintf(f, "encodingsa %hhu ", + rta_getattr_u8(tb[IFLA_MACSEC_ENCODING_SA])); + } } if (tb[IFLA_MACSEC_VALIDATION]) { __u8 val = rta_getattr_u8(tb[IFLA_MACSEC_VALIDATION]); - fprintf(f, "validate %s ", VALIDATE_STR[val]); + print_string(PRINT_ANY, + "validation", + "validate %s ", + VALIDATE_STR[val]); + } + + const char *inc_sci, *es, *replay; + + if (is_json_context()) { + inc_sci = "inc_sci"; + replay = "replay_protect"; + es = "es"; + } else { + inc_sci = "send_sci"; + es = "end_station"; + replay = "replay"; } print_flag(f, tb, "encrypt", IFLA_MACSEC_ENCRYPT); - print_flag(f, tb, "send_sci", IFLA_MACSEC_INC_SCI); - print_flag(f, tb, "end_station", IFLA_MACSEC_ES); + print_flag(f, tb, inc_sci, IFLA_MACSEC_INC_SCI); + print_flag(f, tb, es, IFLA_MACSEC_ES); print_flag(f, tb, "scb", IFLA_MACSEC_SCB); + print_flag(f, tb, replay, IFLA_MACSEC_REPLAY_PROTECT); - print_flag(f, tb, "replay", IFLA_MACSEC_REPLAY_PROTECT); - if (tb[IFLA_MACSEC_WINDOW]) { - fprintf(f, "window %d ", - rta_getattr_u32(tb[IFLA_MACSEC_WINDOW])); - } + if (tb[IFLA_MACSEC_WINDOW]) + print_int(PRINT_ANY, + "window", + "window %d ", + rta_getattr_u32(tb[IFLA_MACSEC_WINDOW])); } static bool check_txsc_flags(bool es, bool scb, bool sci) diff --git a/ip/iproute.c b/ip/iproute.c index 83fd70c3..a8733f45 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -100,7 +100,7 @@ static void usage(void) fprintf(stderr, "TIME := NUMBER[s|ms]\n"); fprintf(stderr, "BOOL := [1|0]\n"); fprintf(stderr, "FEATURES := ecn\n"); - fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 | seg6 ]\n"); + fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local ]\n"); fprintf(stderr, "ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"); fprintf(stderr, "SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"); fprintf(stderr, "SEGMODE := [ encap | inline ]\n"); @@ -698,6 +698,8 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "onlink "); if (nh->rtnh_flags & RTNH_F_PERVASIVE) fprintf(fp, "pervasive "); + if (nh->rtnh_flags & RTNH_F_OFFLOAD) + fprintf(fp, "offload "); if (nh->rtnh_flags & RTNH_F_LINKDOWN) fprintf(fp, "linkdown "); len -= NLMSG_ALIGN(nh->rtnh_len); diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c index 4c2d3b07..1c8adbe7 100644 --- a/ip/iproute_lwtunnel.c +++ b/ip/iproute_lwtunnel.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include static const char *format_encap_type(int type) { @@ -45,6 +47,8 @@ static const char *format_encap_type(int type) return "bpf"; case LWTUNNEL_ENCAP_SEG6: return "seg6"; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + return "seg6local"; default: return "unknown"; } @@ -77,30 +81,18 @@ static int read_encap_type(const char *name) return LWTUNNEL_ENCAP_BPF; else if (strcmp(name, "seg6") == 0) return LWTUNNEL_ENCAP_SEG6; + else if (strcmp(name, "seg6local") == 0) + return LWTUNNEL_ENCAP_SEG6_LOCAL; else if (strcmp(name, "help") == 0) encap_type_usage(); return LWTUNNEL_ENCAP_NONE; } -static void print_encap_seg6(FILE *fp, struct rtattr *encap) +static void print_srh(FILE *fp, struct ipv6_sr_hdr *srh) { - struct rtattr *tb[SEG6_IPTUNNEL_MAX+1]; - struct seg6_iptunnel_encap *tuninfo; - struct ipv6_sr_hdr *srh; int i; - parse_rtattr_nested(tb, SEG6_IPTUNNEL_MAX, encap); - - if (!tb[SEG6_IPTUNNEL_SRH]) - return; - - tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]); - fprintf(fp, "mode %s ", - (tuninfo->mode == SEG6_IPTUN_MODE_ENCAP) ? "encap" : "inline"); - - srh = tuninfo->srh; - fprintf(fp, "segs %d [ ", srh->first_segment + 1); for (i = srh->first_segment; i >= 0; i--) @@ -118,6 +110,136 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap) } } +static const char *seg6_mode_types[] = { + [SEG6_IPTUN_MODE_INLINE] = "inline", + [SEG6_IPTUN_MODE_ENCAP] = "encap", + [SEG6_IPTUN_MODE_L2ENCAP] = "l2encap", +}; + +static const char *format_seg6mode_type(int mode) +{ + if (mode < 0 || mode > ARRAY_SIZE(seg6_mode_types)) + return ""; + + return seg6_mode_types[mode]; +} + +static int read_seg6mode_type(const char *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(seg6_mode_types); i++) { + if (strcmp(mode, seg6_mode_types[i]) == 0) + return i; + } + + return -1; +} + +static void print_encap_seg6(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[SEG6_IPTUNNEL_MAX+1]; + struct seg6_iptunnel_encap *tuninfo; + + parse_rtattr_nested(tb, SEG6_IPTUNNEL_MAX, encap); + + if (!tb[SEG6_IPTUNNEL_SRH]) + return; + + tuninfo = RTA_DATA(tb[SEG6_IPTUNNEL_SRH]); + fprintf(fp, "mode %s ", format_seg6mode_type(tuninfo->mode)); + + print_srh(fp, tuninfo->srh); +} + +static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = { + [SEG6_LOCAL_ACTION_END] = "End", + [SEG6_LOCAL_ACTION_END_X] = "End.X", + [SEG6_LOCAL_ACTION_END_T] = "End.T", + [SEG6_LOCAL_ACTION_END_DX2] = "End.DX2", + [SEG6_LOCAL_ACTION_END_DX6] = "End.DX6", + [SEG6_LOCAL_ACTION_END_DX4] = "End.DX4", + [SEG6_LOCAL_ACTION_END_DT6] = "End.DT6", + [SEG6_LOCAL_ACTION_END_DT4] = "End.DT4", + [SEG6_LOCAL_ACTION_END_B6] = "End.B6", + [SEG6_LOCAL_ACTION_END_B6_ENCAP] = "End.B6.Encaps", + [SEG6_LOCAL_ACTION_END_BM] = "End.BM", + [SEG6_LOCAL_ACTION_END_S] = "End.S", + [SEG6_LOCAL_ACTION_END_AS] = "End.AS", + [SEG6_LOCAL_ACTION_END_AM] = "End.AM", +}; + +static const char *format_action_type(int action) +{ + if (action < 0 || action > SEG6_LOCAL_ACTION_MAX) + return ""; + + return seg6_action_names[action] ?: ""; +} + +static int read_action_type(const char *name) +{ + int i; + + for (i = 0; i < SEG6_LOCAL_ACTION_MAX + 1; i++) { + if (!seg6_action_names[i]) + continue; + + if (strcmp(seg6_action_names[i], name) == 0) + return i; + } + + return SEG6_LOCAL_ACTION_UNSPEC; +} + +static void print_encap_seg6local(FILE *fp, struct rtattr *encap) +{ + struct rtattr *tb[SEG6_LOCAL_MAX + 1]; + char ifbuf[IFNAMSIZ]; + int action; + + parse_rtattr_nested(tb, SEG6_LOCAL_MAX, encap); + + if (!tb[SEG6_LOCAL_ACTION]) + return; + + action = rta_getattr_u32(tb[SEG6_LOCAL_ACTION]); + + fprintf(fp, "action %s ", format_action_type(action)); + + if (tb[SEG6_LOCAL_SRH]) { + fprintf(fp, "srh "); + print_srh(fp, RTA_DATA(tb[SEG6_LOCAL_SRH])); + } + + if (tb[SEG6_LOCAL_TABLE]) + fprintf(fp, "table %u ", rta_getattr_u32(tb[SEG6_LOCAL_TABLE])); + + if (tb[SEG6_LOCAL_NH4]) { + fprintf(fp, "nh4 %s ", + rt_addr_n2a_rta(AF_INET, tb[SEG6_LOCAL_NH4])); + } + + if (tb[SEG6_LOCAL_NH6]) { + fprintf(fp, "nh6 %s ", + rt_addr_n2a_rta(AF_INET6, tb[SEG6_LOCAL_NH6])); + } + + if (tb[SEG6_LOCAL_IIF]) { + int iif = rta_getattr_u32(tb[SEG6_LOCAL_IIF]); + + fprintf(fp, "iif %s ", + if_indextoname(iif, ifbuf) ?: ""); + } + + if (tb[SEG6_LOCAL_OIF]) { + int oif = rta_getattr_u32(tb[SEG6_LOCAL_OIF]); + + fprintf(fp, "oif %s ", + if_indextoname(oif, ifbuf) ?: ""); + } +} + static void print_encap_mpls(FILE *fp, struct rtattr *encap) { struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; @@ -287,56 +409,20 @@ void lwt_print_encap(FILE *fp, struct rtattr *encap_type, case LWTUNNEL_ENCAP_SEG6: print_encap_seg6(fp, encap); break; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + print_encap_seg6local(fp, encap); + break; } } -static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, - char ***argvp) +static struct ipv6_sr_hdr *parse_srh(char *segbuf, int hmac, bool encap) { - int mode_ok = 0, segs_ok = 0, hmac_ok = 0; - struct seg6_iptunnel_encap *tuninfo; struct ipv6_sr_hdr *srh; - char **argv = *argvp; - char segbuf[1024]; - int argc = *argcp; - int encap = -1; - __u32 hmac = 0; int nsegs = 0; int srhlen; char *s; int i; - while (argc > 0) { - if (strcmp(*argv, "mode") == 0) { - NEXT_ARG(); - if (mode_ok++) - duparg2("mode", *argv); - if (strcmp(*argv, "encap") == 0) - encap = 1; - else if (strcmp(*argv, "inline") == 0) - encap = 0; - else - invarg("\"mode\" value is invalid\n", *argv); - } else if (strcmp(*argv, "segs") == 0) { - NEXT_ARG(); - if (segs_ok++) - duparg2("segs", *argv); - if (encap == -1) - invarg("\"segs\" provided before \"mode\"\n", - *argv); - - strlcpy(segbuf, *argv, 1024); - } else if (strcmp(*argv, "hmac") == 0) { - NEXT_ARG(); - if (hmac_ok++) - duparg2("hmac", *argv); - get_u32(&hmac, *argv, 0); - } else { - break; - } - argc--; argv++; - } - s = segbuf; for (i = 0; *s; *s++ == ',' ? i++ : *s); nsegs = i + 1; @@ -349,15 +435,9 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, if (hmac) srhlen += 40; - tuninfo = malloc(sizeof(*tuninfo) + srhlen); - memset(tuninfo, 0, sizeof(*tuninfo) + srhlen); + srh = malloc(srhlen); + memset(srh, 0, srhlen); - if (encap) - tuninfo->mode = SEG6_IPTUN_MODE_ENCAP; - else - tuninfo->mode = SEG6_IPTUN_MODE_INLINE; - - srh = tuninfo->srh; srh->hdrlen = (srhlen >> 3) - 1; srh->type = 4; srh->segments_left = nsegs - 1; @@ -381,9 +461,173 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, tlv->hmackeyid = htonl(hmac); } + return srh; +} + +static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + int mode_ok = 0, segs_ok = 0, hmac_ok = 0; + struct seg6_iptunnel_encap *tuninfo; + struct ipv6_sr_hdr *srh; + char **argv = *argvp; + char segbuf[1024]; + int argc = *argcp; + int encap = -1; + __u32 hmac = 0; + int srhlen; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (mode_ok++) + duparg2("mode", *argv); + encap = read_seg6mode_type(*argv); + if (encap < 0) + invarg("\"mode\" value is invalid\n", *argv); + } else if (strcmp(*argv, "segs") == 0) { + NEXT_ARG(); + if (segs_ok++) + duparg2("segs", *argv); + if (encap == -1) + invarg("\"segs\" provided before \"mode\"\n", + *argv); + + strlcpy(segbuf, *argv, 1024); + } else if (strcmp(*argv, "hmac") == 0) { + NEXT_ARG(); + if (hmac_ok++) + duparg2("hmac", *argv); + get_u32(&hmac, *argv, 0); + } else { + break; + } + argc--; argv++; + } + + srh = parse_srh(segbuf, hmac, encap); + srhlen = (srh->hdrlen + 1) << 3; + + tuninfo = malloc(sizeof(*tuninfo) + srhlen); + memset(tuninfo, 0, sizeof(*tuninfo) + srhlen); + + tuninfo->mode = encap; + + memcpy(tuninfo->srh, srh, srhlen); + rta_addattr_l(rta, len, SEG6_IPTUNNEL_SRH, tuninfo, sizeof(*tuninfo) + srhlen); + free(tuninfo); + free(srh); + + *argcp = argc + 1; + *argvp = argv - 1; + + return 0; +} + +static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp, + char ***argvp) +{ + int segs_ok = 0, hmac_ok = 0, table_ok = 0, nh4_ok = 0, nh6_ok = 0; + int iif_ok = 0, oif_ok = 0, action_ok = 0, srh_ok = 0; + __u32 action = 0, table, iif, oif; + struct ipv6_sr_hdr *srh; + char **argv = *argvp; + int argc = *argcp; + char segbuf[1024]; + inet_prefix addr; + __u32 hmac = 0; + + while (argc > 0) { + if (strcmp(*argv, "action") == 0) { + NEXT_ARG(); + if (action_ok++) + duparg2("action", *argv); + action = read_action_type(*argv); + if (!action) + invarg("\"action\" value is invalid\n", *argv); + rta_addattr32(rta, len, SEG6_LOCAL_ACTION, action); + } else if (strcmp(*argv, "table") == 0) { + NEXT_ARG(); + if (table_ok++) + duparg2("table", *argv); + get_u32(&table, *argv, 0); + rta_addattr32(rta, len, SEG6_LOCAL_TABLE, table); + } else if (strcmp(*argv, "nh4") == 0) { + NEXT_ARG(); + if (nh4_ok++) + duparg2("nh4", *argv); + get_addr(&addr, *argv, AF_INET); + rta_addattr_l(rta, len, SEG6_LOCAL_NH4, &addr.data, + addr.bytelen); + } else if (strcmp(*argv, "nh6") == 0) { + NEXT_ARG(); + if (nh6_ok++) + duparg2("nh6", *argv); + get_addr(&addr, *argv, AF_INET6); + rta_addattr_l(rta, len, SEG6_LOCAL_NH6, &addr.data, + addr.bytelen); + } else if (strcmp(*argv, "iif") == 0) { + NEXT_ARG(); + if (iif_ok++) + duparg2("iif", *argv); + iif = if_nametoindex(*argv); + if (!iif) + invarg("\"iif\" interface not found\n", *argv); + rta_addattr32(rta, len, SEG6_LOCAL_IIF, iif); + } else if (strcmp(*argv, "oif") == 0) { + NEXT_ARG(); + if (oif_ok++) + duparg2("oif", *argv); + oif = if_nametoindex(*argv); + if (!oif) + invarg("\"oif\" interface not found\n", *argv); + rta_addattr32(rta, len, SEG6_LOCAL_OIF, oif); + } else if (strcmp(*argv, "srh") == 0) { + NEXT_ARG(); + if (srh_ok++) + duparg2("srh", *argv); + if (strcmp(*argv, "segs") != 0) + invarg("missing \"segs\" attribute for srh\n", + *argv); + NEXT_ARG(); + if (segs_ok++) + duparg2("segs", *argv); + strncpy(segbuf, *argv, 1024); + segbuf[1023] = 0; + if (!NEXT_ARG_OK()) + break; + NEXT_ARG(); + if (strcmp(*argv, "hmac") == 0) { + NEXT_ARG(); + if (hmac_ok++) + duparg2("hmac", *argv); + get_u32(&hmac, *argv, 0); + } else { + continue; + } + } else { + break; + } + argc--; argv++; + } + + if (!action) { + fprintf(stderr, "Missing action type\n"); + exit(-1); + } + + if (srh_ok) { + int srhlen; + + srh = parse_srh(segbuf, hmac, + action == SEG6_LOCAL_ACTION_END_B6_ENCAP); + srhlen = (srh->hdrlen + 1) << 3; + rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen); + free(srh); + } *argcp = argc + 1; *argvp = argv - 1; @@ -751,6 +995,9 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp) case LWTUNNEL_ENCAP_SEG6: parse_encap_seg6(rta, len, &argc, &argv); break; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + parse_encap_seg6local(rta, len, &argc, &argv); + break; default: fprintf(stderr, "Error: unsupported encap type\n"); break; diff --git a/ip/link_gre.c b/ip/link_gre.c index c2ec5f26..9ea2970c 100644 --- a/ip/link_gre.c +++ b/ip/link_gre.c @@ -26,7 +26,7 @@ static void print_usage(FILE *f) { fprintf(f, - "Usage: ... { gre | gretap } [ remote ADDR ]\n" + "Usage: ... { gre | gretap | erspan } [ remote ADDR ]\n" " [ local ADDR ]\n" " [ [i|o]seq ]\n" " [ [i|o]key KEY ]\n" @@ -44,6 +44,7 @@ static void print_usage(FILE *f) " [ [no]encap-csum6 ]\n" " [ [no]encap-remcsum ]\n" " [ fwmark MARK ]\n" + " [ erspan IDX ]\n" "\n" "Where: ADDR := { IP_ADDRESS | any }\n" " TOS := { NUMBER | inherit }\n" @@ -96,6 +97,7 @@ static int gre_parse_opt(struct link_util *lu, int argc, char **argv, __u8 metadata = 0; __u8 ignore_df = 0; __u32 fwmark = 0; + __u32 erspan_idx = 0; if (!(n->nlmsg_flags & NLM_F_CREATE)) { if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) { @@ -172,6 +174,9 @@ get_failed: if (greinfo[IFLA_GRE_FWMARK]) fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]); + + if (greinfo[IFLA_GRE_ERSPAN_INDEX]) + erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]); } while (argc > 0) { @@ -328,6 +333,12 @@ get_failed: NEXT_ARG(); if (get_u32(&fwmark, *argv, 0)) invarg("invalid fwmark\n", *argv); + } else if (strcmp(*argv, "erspan") == 0) { + NEXT_ARG(); + if (get_u32(&erspan_idx, *argv, 0)) + invarg("invalid erspan index\n", *argv); + if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0) + invarg("erspan index must be > 0 and <= 20-bit\n", *argv); } else usage(); argc--; argv++; @@ -359,6 +370,8 @@ get_failed: addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1); addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1); addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark); + if (erspan_idx != 0) + addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx); } else { addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0); } @@ -389,7 +402,7 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[]) remote = format_host(AF_INET, 4, &addr); } - fprintf(f, "remote %s ", remote); + print_string(PRINT_ANY, "remote", "remote %s ", remote); if (tb[IFLA_GRE_LOCAL]) { unsigned int addr = rta_getattr_u32(tb[IFLA_GRE_LOCAL]); @@ -398,36 +411,52 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[]) local = format_host(AF_INET, 4, &addr); } - fprintf(f, "local %s ", local); + print_string(PRINT_ANY, "local", "local %s ", local); if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) { unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]); const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } - if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL])) - fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_GRE_TTL])); - else - fprintf(f, "ttl inherit "); + if (tb[IFLA_GRE_TTL]) { + __u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]); + + if (ttl) + print_int(PRINT_ANY, "ttl", "ttl %d ", ttl); + else + print_int(PRINT_JSON, "ttl", NULL, ttl); + } else { + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + } if (tb[IFLA_GRE_TOS] && rta_getattr_u8(tb[IFLA_GRE_TOS])) { int tos = rta_getattr_u8(tb[IFLA_GRE_TOS]); - fputs("tos ", f); - if (tos == 1) - fputs("inherit ", f); - else - fprintf(f, "0x%x ", tos); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%x", tos); + print_string(PRINT_JSON, "tos", NULL, b1); + } else { + fputs("tos ", f); + if (tos == 1) + fputs("inherit ", f); + else + fprintf(f, "0x%x ", tos); + } } - if (tb[IFLA_GRE_PMTUDISC] && - !rta_getattr_u8(tb[IFLA_GRE_PMTUDISC])) - fputs("nopmtudisc ", f); + if (tb[IFLA_GRE_PMTUDISC]) { + if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC])) + print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false); + else + print_bool(PRINT_JSON, "pmtudisc", NULL, true); + } if (tb[IFLA_GRE_IFLAGS]) iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]); @@ -437,26 +466,31 @@ static void gre_print_direct_opt(FILE *f, struct rtattr *tb[]) if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2)); - fprintf(f, "ikey %s ", s2); + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); } if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2)); - fprintf(f, "okey %s ", s2); + print_string(PRINT_ANY, "okey", "okey %s ", s2); } if (iflags & GRE_SEQ) - fputs("iseq ", f); + print_bool(PRINT_ANY, "iseq", "iseq ", true); if (oflags & GRE_SEQ) - fputs("oseq ", f); + print_bool(PRINT_ANY, "oseq", "oseq ", true); if (iflags & GRE_CSUM) - fputs("icsum ", f); + print_bool(PRINT_ANY, "icsum", "icsum ", true); if (oflags & GRE_CSUM) - fputs("ocsum ", f); + print_bool(PRINT_ANY, "ocsum", "ocsum ", true); - if (tb[IFLA_GRE_FWMARK] && rta_getattr_u32(tb[IFLA_GRE_FWMARK])) { - fprintf(f, "fwmark 0x%x ", - rta_getattr_u32(tb[IFLA_GRE_FWMARK])); + if (tb[IFLA_GRE_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]); + + if (fwmark) { + snprintf(s2, sizeof(s2), "0x%x", fwmark); + + print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2); + } } } @@ -468,10 +502,16 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (!tb[IFLA_GRE_COLLECT_METADATA]) gre_print_direct_opt(f, tb); else - fputs("external ", f); + print_bool(PRINT_ANY, "external", "external ", true); if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF])) - fputs("ignore-df ", f); + print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true); + + if (tb[IFLA_GRE_ERSPAN_INDEX]) { + __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]); + + fprintf(f, "erspan_index %u ", erspan_idx); + } if (tb[IFLA_GRE_ENCAP_TYPE] && rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { @@ -480,45 +520,73 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]); __u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]); - fputs("encap ", f); + + open_json_object("encap"); + print_string(PRINT_FP, NULL, "encap ", NULL); + switch (type) { case TUNNEL_ENCAP_FOU: - fputs("fou ", f); + print_string(PRINT_ANY, "type", "%s ", "fou"); break; case TUNNEL_ENCAP_GUE: - fputs("gue ", f); + print_string(PRINT_ANY, "type", "%s ", "gue"); break; default: - fputs("unknown ", f); + print_null(PRINT_ANY, "type", "%s ", "unknown"); break; } - if (sport == 0) - fputs("encap-sport auto ", f); - else - fprintf(f, "encap-sport %u", ntohs(sport)); + if (is_json_context()) { + print_uint(PRINT_JSON, + "sport", + NULL, + sport ? ntohs(sport) : 0); + print_uint(PRINT_JSON, "dport", NULL, ntohs(dport)); - fprintf(f, "encap-dport %u ", ntohs(dport)); + print_bool(PRINT_JSON, + "csum", + NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM); - if (flags & TUNNEL_ENCAP_FLAG_CSUM) - fputs("encap-csum ", f); - else - fputs("noencap-csum ", f); + print_bool(PRINT_JSON, + "csum6", + NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM6); - if (flags & TUNNEL_ENCAP_FLAG_CSUM6) - fputs("encap-csum6 ", f); - else - fputs("noencap-csum6 ", f); + print_bool(PRINT_JSON, + "remcsum", + NULL, + flags & TUNNEL_ENCAP_FLAG_REMCSUM); - if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) - fputs("encap-remcsum ", f); - else - fputs("noencap-remcsum ", f); + close_json_object(); + } else { + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); + + fprintf(f, "encap-dport %u ", ntohs(dport)); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); + + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); + + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } } static void gre_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_usage(f); } @@ -538,3 +606,11 @@ struct link_util gretap_link_util = { .print_opt = gre_print_opt, .print_help = gre_print_help, }; + +struct link_util erspan_link_util = { + .id = "erspan", + .maxattr = IFLA_GRE_MAX, + .parse_opt = gre_parse_opt, + .print_opt = gre_print_opt, + .print_help = gre_print_help, +}; diff --git a/ip/link_gre6.c b/ip/link_gre6.c index 78b5215c..7d07932a 100644 --- a/ip/link_gre6.c +++ b/ip/link_gre6.c @@ -425,7 +425,7 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) remote = format_host(AF_INET6, sizeof(addr), &addr); } - fprintf(f, "remote %s ", remote); + print_string(PRINT_ANY, "remote", "remote %s ", remote); if (tb[IFLA_GRE_LOCAL]) { struct in6_addr addr; @@ -436,43 +436,83 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) local = format_host(AF_INET6, sizeof(addr), &addr); } - fprintf(f, "local %s ", local); + print_string(PRINT_ANY, "local", "local %s ", local); if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) { unsigned int link = rta_getattr_u32(tb[IFLA_GRE_LINK]); const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } - if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL])) - fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL])); + if (tb[IFLA_GRE_TTL]) { + __u8 ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]); + + if (ttl) + print_int(PRINT_ANY, "ttl", "hoplimit %d ", ttl); + else + print_int(PRINT_JSON, "ttl", NULL, ttl); + } if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) - fprintf(f, "encaplimit none "); + print_bool(PRINT_ANY, + "ip6_tnl_f_ign_encap_limit", + "encaplimit none ", + true); else if (tb[IFLA_GRE_ENCAP_LIMIT]) { int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]); - fprintf(f, "encaplimit %d ", encap_limit); + print_int(PRINT_ANY, + "encap_limit", + "encaplimit %d ", + encap_limit); } - if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) - fprintf(f, "flowlabel inherit "); - else - fprintf(f, "flowlabel 0x%05x ", - ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_flowlabel", + "flowlabel inherit ", + true); + } else { + if (is_json_context()) { + SPRINT_BUF(b1); - if (flags & IP6_TNL_F_USE_ORIG_TCLASS) - fprintf(f, "tclass inherit "); - else - fprintf(f, "tclass 0x%02x ", - ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20); + snprintf(b1, sizeof(b1), "0x%05x", + ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + print_string(PRINT_JSON, "flowlabel", NULL, b1); + + } else { + fprintf(f, "flowlabel 0x%05x ", + ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + } + } + + if (flags & IP6_TNL_F_USE_ORIG_TCLASS) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_tclass", + "tclass inherit ", + true); + } else { + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%05x", + ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20); + print_string(PRINT_JSON, "tclass", NULL, b1); + } else { + fprintf(f, "tclass 0x%02x ", + ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20); + } + } if (flags & IP6_TNL_F_RCV_DSCP_COPY) - fprintf(f, "dscp inherit "); + print_bool(PRINT_ANY, + "ip6_tnl_f_rcv_dscp_copy", + "dscp inherit ", + true); if (tb[IFLA_GRE_IFLAGS]) iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]); @@ -482,27 +522,37 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2)); - fprintf(f, "ikey %s ", s2); + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); } if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2)); - fprintf(f, "okey %s ", s2); + print_string(PRINT_ANY, "okey", "okey %s ", s2); } if (iflags & GRE_SEQ) - fputs("iseq ", f); + print_bool(PRINT_ANY, "iseq", "iseq ", true); if (oflags & GRE_SEQ) - fputs("oseq ", f); + print_bool(PRINT_ANY, "oseq", "oseq ", true); if (iflags & GRE_CSUM) - fputs("icsum ", f); + print_bool(PRINT_ANY, "icsum", "icsum ", true); if (oflags & GRE_CSUM) - fputs("ocsum ", f); + print_bool(PRINT_ANY, "ocsum", "ocsum ", true); if (flags & IP6_TNL_F_USE_ORIG_FWMARK) - fprintf(f, "fwmark inherit "); - else if (tb[IFLA_GRE_FWMARK] && rta_getattr_u32(tb[IFLA_GRE_FWMARK])) - fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_GRE_FWMARK])); + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_fwmark", + "fwmark inherit ", + true); + else if (tb[IFLA_GRE_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]); + + if (fwmark) { + snprintf(s2, sizeof(s2), "0x%x", fwmark); + + print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2); + } + } if (tb[IFLA_GRE_ENCAP_TYPE] && rta_getattr_u16(tb[IFLA_GRE_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { @@ -511,40 +561,57 @@ static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) __u16 sport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_SPORT]); __u16 dport = rta_getattr_u16(tb[IFLA_GRE_ENCAP_DPORT]); - fputs("encap ", f); + open_json_object("encap"); + + print_string(PRINT_FP, NULL, "encap ", NULL); switch (type) { case TUNNEL_ENCAP_FOU: - fputs("fou ", f); + print_string(PRINT_ANY, "type", "%s ", "fou"); break; case TUNNEL_ENCAP_GUE: - fputs("gue ", f); + print_string(PRINT_ANY, "type", "%s ", "gue"); break; default: - fputs("unknown ", f); + print_null(PRINT_ANY, "type", "unknown ", NULL); break; } - if (sport == 0) - fputs("encap-sport auto ", f); - else - fprintf(f, "encap-sport %u", ntohs(sport)); + if (is_json_context()) { + print_uint(PRINT_JSON, + "sport", + NULL, + sport ? ntohs(sport) : 0); + print_uint(PRINT_JSON, "dport", NULL, ntohs(dport)); + print_bool(PRINT_JSON, "csum", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM); + print_bool(PRINT_JSON, "csum6", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM6); + print_bool(PRINT_JSON, "remcsum", NULL, + flags & TUNNEL_ENCAP_FLAG_REMCSUM); + close_json_object(); + } else { + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); - fprintf(f, "encap-dport %u ", ntohs(dport)); + fprintf(f, "encap-dport %u ", ntohs(dport)); - if (flags & TUNNEL_ENCAP_FLAG_CSUM) - fputs("encap-csum ", f); - else - fputs("noencap-csum ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); - if (flags & TUNNEL_ENCAP_FLAG_CSUM6) - fputs("encap-csum6 ", f); - else - fputs("noencap-csum6 ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); - if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) - fputs("encap-remcsum ", f); - else - fputs("noencap-remcsum ", f); + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } } diff --git a/ip/link_ip6tnl.c b/ip/link_ip6tnl.c index 505fb476..a4199006 100644 --- a/ip/link_ip6tnl.c +++ b/ip/link_ip6tnl.c @@ -346,25 +346,29 @@ static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb if (tb[IFLA_IPTUN_PROTO]) { switch (rta_getattr_u8(tb[IFLA_IPTUN_PROTO])) { case IPPROTO_IPIP: - fprintf(f, "ipip6 "); + print_string(PRINT_ANY, "proto", "%s ", "ipip6"); break; case IPPROTO_IPV6: - fprintf(f, "ip6ip6 "); + print_string(PRINT_ANY, "proto", "%s ", "ip6ip6"); break; case 0: - fprintf(f, "any "); + print_string(PRINT_ANY, "proto", "%s ", "any"); break; } } if (tb[IFLA_IPTUN_REMOTE]) { - fprintf(f, "remote %s ", - rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE])); + print_string(PRINT_ANY, + "remote", + "remote %s ", + rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_REMOTE])); } if (tb[IFLA_IPTUN_LOCAL]) { - fprintf(f, "local %s ", - rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL])); + print_string(PRINT_ANY, + "local", + "local %s ", + rt_addr_n2a_rta(AF_INET6, tb[IFLA_IPTUN_LOCAL])); } if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { @@ -372,93 +376,161 @@ static void ip6tunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) - printf("encaplimit none "); + print_bool(PRINT_ANY, + "ip6_tnl_f_ign_encap_limit", + "encaplimit none ", + true); else if (tb[IFLA_IPTUN_ENCAP_LIMIT]) - fprintf(f, "encaplimit %u ", - rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT])); + print_uint(PRINT_ANY, + "encap_limit", + "encaplimit %u ", + rta_getattr_u8(tb[IFLA_IPTUN_ENCAP_LIMIT])); if (tb[IFLA_IPTUN_TTL]) - fprintf(f, "hoplimit %u ", rta_getattr_u8(tb[IFLA_IPTUN_TTL])); + print_uint(PRINT_ANY, + "ttl", + "hoplimit %u ", + rta_getattr_u8(tb[IFLA_IPTUN_TTL])); if (flags & IP6_TNL_F_USE_ORIG_TCLASS) - printf("tclass inherit "); + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_tclass", + "tclass inherit ", + true); else if (tb[IFLA_IPTUN_FLOWINFO]) { __u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS); - printf("tclass 0x%02x ", (__u8)(val >> 20)); + if (is_json_context()) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20)); + print_string(PRINT_JSON, "flowinfo_tclass", NULL, b1); + } else { + printf("tclass 0x%02x ", (__u8)(val >> 20)); + } } - if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) - printf("flowlabel inherit "); - else - printf("flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_flowlabel", + "flowlabel inherit ", + true); + } else { + if (is_json_context()) { + SPRINT_BUF(b1); - printf("(flowinfo 0x%08x) ", ntohl(flowinfo)); + snprintf(b1, sizeof(b1), "0x%05x", + ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + print_string(PRINT_JSON, "flowlabel", NULL, b1); + } else { + printf("flowlabel 0x%05x ", + ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL)); + } + } + + if (is_json_context()) { + SPRINT_BUF(flwinfo); + + snprintf(flwinfo, sizeof(flwinfo), "0x%08x", ntohl(flowinfo)); + print_string(PRINT_JSON, "flowinfo", NULL, flwinfo); + } else { + printf("(flowinfo 0x%08x) ", ntohl(flowinfo)); + + } if (flags & IP6_TNL_F_RCV_DSCP_COPY) - printf("dscp inherit "); + print_bool(PRINT_ANY, + "ip6_tnl_f_rcv_dscp_copy", + "dscp inherit ", + true); if (flags & IP6_TNL_F_MIP6_DEV) - fprintf(f, "mip6 "); + print_bool(PRINT_ANY, "ip6_tnl_f_mip6_dev", "mip6 ", true); - if (flags & IP6_TNL_F_USE_ORIG_FWMARK) - fprintf(f, "fwmark inherit "); - else if (tb[IFLA_IPTUN_FWMARK] && rta_getattr_u32(tb[IFLA_IPTUN_FWMARK])) - fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_IPTUN_FWMARK])); + if (flags & IP6_TNL_F_USE_ORIG_FWMARK) { + print_bool(PRINT_ANY, + "ip6_tnl_f_use_orig_fwmark", + "fwmark inherit ", + true); + } else if (tb[IFLA_IPTUN_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]); + + if (fwmark) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%x", fwmark); + print_string(PRINT_ANY, "fwmark", "fwmark %s ", b1); + } + } if (tb[IFLA_IPTUN_ENCAP_TYPE] && - rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) != - TUNNEL_ENCAP_NONE) { + rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]) != TUNNEL_ENCAP_NONE) { __u16 type = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_TYPE]); __u16 flags = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_FLAGS]); __u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]); __u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]); - fputs("encap ", f); + open_json_object("encap"); + print_string(PRINT_FP, NULL, "encap ", NULL); switch (type) { case TUNNEL_ENCAP_FOU: - fputs("fou ", f); + print_string(PRINT_ANY, "type", "%s ", "fou"); break; case TUNNEL_ENCAP_GUE: - fputs("gue ", f); + print_string(PRINT_ANY, "type", "%s ", "gue"); break; default: - fputs("unknown ", f); + print_null(PRINT_ANY, "type", "unknown ", NULL); break; } - if (sport == 0) - fputs("encap-sport auto ", f); - else - fprintf(f, "encap-sport %u", ntohs(sport)); + if (is_json_context()) { + print_uint(PRINT_JSON, + "sport", + NULL, + sport ? ntohs(sport) : 0); + print_uint(PRINT_JSON, "dport", NULL, ntohs(dport)); + print_bool(PRINT_JSON, "csum", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM); + print_bool(PRINT_JSON, "csum6", NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM6); + print_bool(PRINT_JSON, "remcsum", NULL, + flags & TUNNEL_ENCAP_FLAG_REMCSUM); + close_json_object(); + } else { + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); - fprintf(f, "encap-dport %u ", ntohs(dport)); + fprintf(f, "encap-dport %u ", ntohs(dport)); - if (flags & TUNNEL_ENCAP_FLAG_CSUM) - fputs("encap-csum ", f); - else - fputs("noencap-csum ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); - if (flags & TUNNEL_ENCAP_FLAG_CSUM6) - fputs("encap-csum6 ", f); - else - fputs("noencap-csum6 ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); - if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) - fputs("encap-remcsum ", f); - else - fputs("noencap-remcsum ", f); + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } } static void ip6tunnel_print_help(struct link_util *lu, int argc, char **argv, - FILE *f) + FILE *f) { print_usage(f); } diff --git a/ip/link_iptnl.c b/ip/link_iptnl.c index d24e7376..6a725e91 100644 --- a/ip/link_iptnl.c +++ b/ip/link_iptnl.c @@ -398,7 +398,7 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[ remote = format_host(AF_INET, 4, &addr); } - fprintf(f, "remote %s ", remote); + print_string(PRINT_ANY, "remote", "remote %s ", remote); if (tb[IFLA_IPTUN_LOCAL]) { unsigned int addr = rta_getattr_u32(tb[IFLA_IPTUN_LOCAL]); @@ -407,43 +407,55 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[ local = format_host(AF_INET, 4, &addr); } - fprintf(f, "local %s ", local); + print_string(PRINT_ANY, "local", "local %s ", local); if (tb[IFLA_IPTUN_LINK] && rta_getattr_u32(tb[IFLA_IPTUN_LINK])) { unsigned int link = rta_getattr_u32(tb[IFLA_IPTUN_LINK]); const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_int(PRINT_ANY, "link_index", "dev %u ", link); } - if (tb[IFLA_IPTUN_TTL] && rta_getattr_u8(tb[IFLA_IPTUN_TTL])) - fprintf(f, "ttl %d ", rta_getattr_u8(tb[IFLA_IPTUN_TTL])); - else - fprintf(f, "ttl inherit "); + if (tb[IFLA_IPTUN_TTL]) { + __u8 ttl = rta_getattr_u8(tb[IFLA_IPTUN_TTL]); - if (tb[IFLA_IPTUN_TOS] && rta_getattr_u8(tb[IFLA_IPTUN_TOS])) { + if (ttl) + print_int(PRINT_ANY, "ttl", "ttl %d ", ttl); + else + print_int(PRINT_JSON, "ttl", NULL, ttl); + } else { + print_string(PRINT_FP, NULL, "ttl %s ", "inherit"); + } + + if (tb[IFLA_IPTUN_TOS]) { int tos = rta_getattr_u8(tb[IFLA_IPTUN_TOS]); - fputs("tos ", f); - if (tos == 1) - fputs("inherit ", f); - else - fprintf(f, "0x%x ", tos); + if (tos) { + if (is_json_context()) { + print_0xhex(PRINT_JSON, "tos", "%#x", tos); + } else { + fputs("tos ", f); + if (tos == 1) + fputs("inherit ", f); + else + fprintf(f, "0x%x ", tos); + } + } } if (tb[IFLA_IPTUN_PMTUDISC] && rta_getattr_u8(tb[IFLA_IPTUN_PMTUDISC])) - fprintf(f, "pmtudisc "); + print_bool(PRINT_ANY, "pmtudisc", "pmtudisc ", true); else - fprintf(f, "nopmtudisc "); + print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false); if (tb[IFLA_IPTUN_FLAGS]) { __u16 iflags = rta_getattr_u16(tb[IFLA_IPTUN_FLAGS]); if (iflags & SIT_ISATAP) - fprintf(f, "isatap "); + print_bool(PRINT_ANY, "isatap", "isatap ", true); } if (tb[IFLA_IPTUN_6RD_PREFIXLEN] && @@ -453,14 +465,32 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[ __u32 relayprefix = rta_getattr_u32(tb[IFLA_IPTUN_6RD_RELAY_PREFIX]); - printf("6rd-prefix %s/%u ", - inet_ntop(AF_INET6, RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]), - s1, sizeof(s1)), - prefixlen); - if (relayprefix) { - printf("6rd-relay_prefix %s/%u ", - format_host(AF_INET, 4, &relayprefix), - relayprefixlen); + const char *prefix = inet_ntop(AF_INET6, + RTA_DATA(tb[IFLA_IPTUN_6RD_PREFIX]), + s1, sizeof(s1)); + + if (is_json_context()) { + print_string(PRINT_JSON, "prefix", NULL, prefix); + print_int(PRINT_JSON, "prefixlen", NULL, prefixlen); + if (relayprefix) { + print_string(PRINT_JSON, + "relay_prefix", + NULL, + format_host(AF_INET, + 4, + &relayprefix)); + print_int(PRINT_JSON, + "relay_prefixlen", + NULL, + relayprefixlen); + } + } else { + printf("6rd-prefix %s/%u ", prefix, prefixlen); + if (relayprefix) { + printf("6rd-relay_prefix %s/%u ", + format_host(AF_INET, 4, &relayprefix), + relayprefixlen); + } } } @@ -470,45 +500,72 @@ static void iptunnel_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[ __u16 sport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_SPORT]); __u16 dport = rta_getattr_u16(tb[IFLA_IPTUN_ENCAP_DPORT]); - fputs("encap ", f); + print_string(PRINT_FP, NULL, "encap ", NULL); switch (type) { case TUNNEL_ENCAP_FOU: - fputs("fou ", f); + print_string(PRINT_ANY, "type", "%s ", "fou"); break; case TUNNEL_ENCAP_GUE: - fputs("gue ", f); + print_string(PRINT_ANY, "type", "%s ", "gue"); break; default: - fputs("unknown ", f); + print_null(PRINT_ANY, "type", "unknown ", NULL); break; } - if (sport == 0) - fputs("encap-sport auto ", f); - else - fprintf(f, "encap-sport %u", ntohs(sport)); + if (is_json_context()) { + print_uint(PRINT_JSON, + "sport", + NULL, + sport ? ntohs(sport) : 0); + print_uint(PRINT_JSON, "dport", NULL, ntohs(dport)); + print_bool(PRINT_JSON, + "csum", + NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM); + print_bool(PRINT_JSON, + "csum6", + NULL, + flags & TUNNEL_ENCAP_FLAG_CSUM6); + print_bool(PRINT_JSON, + "remcsum", + NULL, + flags & TUNNEL_ENCAP_FLAG_REMCSUM); + close_json_object(); + } else { + if (sport == 0) + fputs("encap-sport auto ", f); + else + fprintf(f, "encap-sport %u", ntohs(sport)); - fprintf(f, "encap-dport %u ", ntohs(dport)); + fprintf(f, "encap-dport %u ", ntohs(dport)); - if (flags & TUNNEL_ENCAP_FLAG_CSUM) - fputs("encap-csum ", f); - else - fputs("noencap-csum ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM) + fputs("encap-csum ", f); + else + fputs("noencap-csum ", f); - if (flags & TUNNEL_ENCAP_FLAG_CSUM6) - fputs("encap-csum6 ", f); - else - fputs("noencap-csum6 ", f); + if (flags & TUNNEL_ENCAP_FLAG_CSUM6) + fputs("encap-csum6 ", f); + else + fputs("noencap-csum6 ", f); - if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) - fputs("encap-remcsum ", f); - else - fputs("noencap-remcsum ", f); + if (flags & TUNNEL_ENCAP_FLAG_REMCSUM) + fputs("encap-remcsum ", f); + else + fputs("noencap-remcsum ", f); + } } - if (tb[IFLA_IPTUN_FWMARK] && rta_getattr_u32(tb[IFLA_IPTUN_FWMARK])) - fprintf(f, "fwmark 0x%x ", - rta_getattr_u32(tb[IFLA_IPTUN_FWMARK])); + if (tb[IFLA_IPTUN_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_IPTUN_FWMARK]); + + if (fwmark) { + snprintf(s2, sizeof(s2), "0x%x", fwmark); + + print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2); + } + } } static void iptunnel_print_help(struct link_util *lu, int argc, char **argv, diff --git a/ip/link_vti.c b/ip/link_vti.c index 3ffecfac..8bd4d900 100644 --- a/ip/link_vti.c +++ b/ip/link_vti.c @@ -224,7 +224,7 @@ static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) remote = format_host(AF_INET, 4, &addr); } - fprintf(f, "remote %s ", remote); + print_string(PRINT_ANY, "remote", "remote %s ", remote); if (tb[IFLA_VTI_LOCAL]) { unsigned int addr = rta_getattr_u32(tb[IFLA_VTI_LOCAL]); @@ -233,30 +233,36 @@ static void vti_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) local = format_host(AF_INET, 4, &addr); } - fprintf(f, "local %s ", local); + print_string(PRINT_ANY, "local", "local %s ", local); if (tb[IFLA_VTI_LINK] && (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) { const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } if (tb[IFLA_VTI_IKEY] && (key = rta_getattr_u32(tb[IFLA_VTI_IKEY]))) - fprintf(f, "ikey %#x ", ntohl(key)); + print_0xhex(PRINT_ANY, "ikey", "ikey %#x ", ntohl(key)); if (tb[IFLA_VTI_OKEY] && (key = rta_getattr_u32(tb[IFLA_VTI_OKEY]))) - fprintf(f, "okey %#x ", ntohl(key)); + print_0xhex(PRINT_ANY, "okey", "okey %#x ", ntohl(key)); - if (tb[IFLA_VTI_FWMARK] && rta_getattr_u32(tb[IFLA_VTI_FWMARK])) { - fprintf(f, "fwmark 0x%x ", - rta_getattr_u32(tb[IFLA_VTI_FWMARK])); + if (tb[IFLA_VTI_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]); + + if (fwmark) { + SPRINT_BUF(b1); + + snprintf(b1, sizeof(b1), "0x%x", fwmark); + print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2); + } } } diff --git a/ip/link_vti6.c b/ip/link_vti6.c index 6ea1fc23..8198d468 100644 --- a/ip/link_vti6.c +++ b/ip/link_vti6.c @@ -225,7 +225,7 @@ static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) remote = format_host(AF_INET6, 16, &daddr); } - fprintf(f, "remote %s ", remote); + print_string(PRINT_ANY, "remote", "remote %s ", remote); if (tb[IFLA_VTI_LOCAL]) { memcpy(&saddr, RTA_DATA(tb[IFLA_VTI_LOCAL]), sizeof(saddr)); @@ -233,29 +233,35 @@ static void vti6_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) local = format_host(AF_INET6, 16, &saddr); } - fprintf(f, "local %s ", local); + print_string(PRINT_ANY, "local", "local %s ", local); if (tb[IFLA_VTI_LINK] && (link = rta_getattr_u32(tb[IFLA_VTI_LINK]))) { const char *n = if_indextoname(link, s2); if (n) - fprintf(f, "dev %s ", n); + print_string(PRINT_ANY, "link", "dev %s ", n); else - fprintf(f, "dev %u ", link); + print_uint(PRINT_ANY, "link_index", "dev %u ", link); } if (tb[IFLA_VTI_IKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_IKEY]), s2, sizeof(s2)); - fprintf(f, "ikey %s ", s2); + print_string(PRINT_ANY, "ikey", "ikey %s ", s2); } if (tb[IFLA_VTI_OKEY]) { inet_ntop(AF_INET, RTA_DATA(tb[IFLA_VTI_OKEY]), s2, sizeof(s2)); - fprintf(f, "okey %s ", s2); + print_string(PRINT_ANY, "okey", "okey %s ", s2); } - if (tb[IFLA_VTI_FWMARK] && rta_getattr_u32(tb[IFLA_VTI_FWMARK])) { - fprintf(f, "fwmark 0x%x ", rta_getattr_u32(tb[IFLA_VTI_FWMARK])); + if (tb[IFLA_VTI_FWMARK]) { + __u32 fwmark = rta_getattr_u32(tb[IFLA_VTI_FWMARK]); + + if (fwmark) { + snprintf(s2, sizeof(s2), "0x%x", fwmark); + + print_string(PRINT_ANY, "fwmark", "fwmark %s ", s2); + } } } diff --git a/lib/Makefile b/lib/Makefile index b7b1d568..5e9f72fa 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,16 +1,4 @@ -include ../Config - -ifeq ($(IP_CONFIG_SETNS),y) - CFLAGS += -DHAVE_SETNS -endif - -ifeq ($(HAVE_ELF),y) - CFLAGS += -DHAVE_ELF -endif - -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) -endif +include ../config.mk CFLAGS += -fPIC diff --git a/lib/color.c b/lib/color.c index 4e947500..79d5e289 100644 --- a/lib/color.c +++ b/lib/color.c @@ -89,6 +89,14 @@ void set_color_palette(void) is_dark_bg = 1; } +void check_if_color_enabled(void) +{ + if (color_is_enabled) { + fprintf(stderr, "Option \"-json\" conflicts with \"-color\".\n"); + exit(1); + } +} + int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...) { int ret = 0; @@ -96,13 +104,13 @@ int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...) va_start(args, fmt); - if (!color_is_enabled) { + if (!color_is_enabled || attr == COLOR_NONE) { ret = vfprintf(fp, fmt, args); goto end; } ret += fprintf(fp, "%s", - color_codes[attr_colors[is_dark_bg ? attr + 7 : attr]]); + color_codes[attr_colors[is_dark_bg ? attr + 8 : attr]]); ret += vfprintf(fp, fmt, args); ret += fprintf(fp, "%s", color_codes[C_CLEAR]); diff --git a/lib/json_writer.c b/lib/json_writer.c index 9fc05e96..6b77d288 100644 --- a/lib/json_writer.c +++ b/lib/json_writer.c @@ -156,7 +156,7 @@ void jsonw_name(json_writer_t *self, const char *name) putc(' ', self->out); } -static void jsonw_printf(json_writer_t *self, const char *fmt, ...) +void jsonw_printf(json_writer_t *self, const char *fmt, ...) { va_list ap; @@ -199,23 +199,38 @@ void jsonw_bool(json_writer_t *self, bool val) jsonw_printf(self, "%s", val ? "true" : "false"); } -#ifdef notused void jsonw_null(json_writer_t *self) { jsonw_printf(self, "null"); } +void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num) +{ + jsonw_printf(self, fmt, num); +} + +#ifdef notused void jsonw_float(json_writer_t *self, double num) { jsonw_printf(self, "%g", num); } #endif +void jsonw_hu(json_writer_t *self, unsigned short num) +{ + jsonw_printf(self, "%hu", num); +} + void jsonw_uint(json_writer_t *self, uint64_t num) { jsonw_printf(self, "%"PRIu64, num); } +void jsonw_lluint(json_writer_t *self, unsigned long long int num) +{ + jsonw_printf(self, "%llu", num); +} + void jsonw_int(json_writer_t *self, int64_t num) { jsonw_printf(self, "%"PRId64, num); @@ -242,25 +257,46 @@ void jsonw_float_field(json_writer_t *self, const char *prop, double val) } #endif +void jsonw_float_field_fmt(json_writer_t *self, + const char *prop, + const char *fmt, + double val) +{ + jsonw_name(self, prop); + jsonw_float_fmt(self, fmt, val); +} + void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) { jsonw_name(self, prop); jsonw_uint(self, num); } +void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) +{ + jsonw_name(self, prop); + jsonw_hu(self, num); +} + +void jsonw_lluint_field(json_writer_t *self, + const char *prop, + unsigned long long int num) +{ + jsonw_name(self, prop); + jsonw_lluint(self, num); +} + void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) { jsonw_name(self, prop); jsonw_int(self, num); } -#ifdef notused void jsonw_null_field(json_writer_t *self, const char *prop) { jsonw_name(self, prop); jsonw_null(self); } -#endif #ifdef TEST int main(int argc, char **argv) diff --git a/man/man8/ip-address.8.in b/man/man8/ip-address.8.in index 43385813..988a7965 100644 --- a/man/man8/ip-address.8.in +++ b/man/man8/ip-address.8.in @@ -120,6 +120,7 @@ ip-address \- protocol address management .BR sit " |" .BR gre " |" .BR gretap " |" +.BR erspan " |" .BR ip6gre " |" .BR ip6gretap " |" .BR vti " |" diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index c0207281..851b308c 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -202,6 +202,7 @@ ip-link \- network device configuration .BR sit " |" .BR gre " |" .BR gretap " |" +.BR erspan " |" .BR ip6gre " |" .BR ip6gretap " |" .BR vti " |" @@ -297,6 +298,9 @@ Link types: .BR gretap - Virtual L2 tunnel interface GRE over IPv4 .sp +.BR erspan +- Encapsulated Remote SPAN over GRE and IPv4 +.sp .BR ip6gre - Virtual tunnel interface GRE over IPv6 .sp @@ -643,13 +647,13 @@ keyword. .in -8 .TP -GRE, IPIP, SIT Type Support +GRE, IPIP, SIT, ERSPAN Type Support For a link of types -.I GRE/IPIP/SIT +.I GRE/IPIP/SIT/ERSPAN the following additional arguments are supported: .BI "ip link add " DEVICE -.BR type " { " gre " | " ipip " | " sit " }" +.BR type " { " gre " | " ipip " | " sit " | " erspan " }" .BI " remote " ADDR " local " ADDR [ .BR encap " { " fou " | " gue " | " none " }" @@ -663,6 +667,8 @@ the following additional arguments are supported: .I " [no]encap-remcsum " ] [ .I " mode " { ip6ip | ipip | mplsip | any } " +] [ +.BR erspan " \fIIDX " ] .in +8 @@ -707,6 +713,13 @@ MPLS-Over-IPv4, "any" indicates IPv6, IPv4 or MPLS Over IPv4. Supported for SIT where the default is "ip6ip" and IPIP where the default is "ipip". IPv6-Over-IPv4 is not supported for IPIP. +.sp +.BR erspan " \fIIDX " +- specifies the ERSPAN index field. +.IR IDX +indicates a 20 bit index/port number associated with the ERSPAN +traffic's source port and direction. + .in -8 .TP diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in index fc284921..803de3b9 100644 --- a/man/man8/ip-route.8.in +++ b/man/man8/ip-route.8.in @@ -177,7 +177,7 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]" .ti -8 .IR ENCAP " := [ " -.IR MPLS " | " IP " | " BPF " | " SEG6 " ] " +.IR MPLS " | " IP " | " BPF " | " SEG6 " | " SEG6LOCAL " ] " .ti -8 .IR ENCAP_MPLS " := " @@ -214,12 +214,19 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]" .IR ENCAP_SEG6 " := " .B seg6 .BR mode " [ " -.BR encap " | " inline " ] " +.BR encap " | " inline " | " l2encap " ] " .B segs .IR SEGMENTS " [ " .B hmac .IR KEYID " ]" +.ti -8 +.IR ENCAP_SEG6LOCAL " := " +.B seg6local +.BR action +.IR SEG6_ACTION " [ " +.IR SEG6_ACTION_PARAM " ] " + .ti -8 .IR ROUTE_GET_FLAGS " := " .BR " [ " @@ -674,6 +681,9 @@ is a string specifying the supported encapsulation type. Namely: .sp .BI seg6 - encapsulation type IPv6 Segment Routing +.sp +.BI seg6local +- local SRv6 segment processing .in -8 .I ENCAPHDR @@ -740,6 +750,10 @@ is a set of encapsulation attributes specific to the - Encapsulate packet in an outer IPv6 header with SRH .sp +.B mode l2encap +- Encapsulate ingress L2 frame within an outer IPv6 header and SRH +.sp + .I SEGMENTS - List of comma-separated IPv6 addresses .sp @@ -749,6 +763,56 @@ is a set of encapsulation attributes specific to the .in -2 .sp +.B seg6local +.in +2 +.IR SEG6_ACTION " [ " +.IR SEG6_ACTION_PARAM " ] " +- Operation to perform on matching packets. +The following actions are currently supported (\fB4.14+ only\fR). +.in +2 + +.B End +- Regular SRv6 processing as intermediate segment endpoint. +This action only accepts packets with a non-zero Segments Left +value. Other matching packets are dropped. + +.B End.X nh6 +.I NEXTHOP +- Regular SRv6 processing as intermediate segment endpoint. +Additionally, forward processed packets to given next-hop. +This action only accepts packets with a non-zero Segments Left +value. Other matching packets are dropped. + +.B End.DX6 nh6 +.I NEXTHOP +- Decapsulate inner IPv6 packet and forward it to the +specified next-hop. If the argument is set to ::, then +the next-hop is selected according to the local selection +rules. This action only accepts packets with either a zero Segments +Left value or no SRH at all, and an inner IPv6 packet. Other +matching packets are dropped. + +.B End.B6 srh segs +.IR SEGMENTS " [ " +.B hmac +.IR KEYID " ] " +- Insert the specified SRH immediately after the IPv6 header, +update the DA with the first segment of the newly inserted SRH, +then forward the resulting packet. The original SRH is not +modified. This action only accepts packets with a non-zero +Segments Left value. Other matching packets are dropped. + +.B End.B6.Encaps srh segs +.IR SEGMENTS " [ " +.B hmac +.IR KEYID " ] " +- Regular SRv6 processing as intermediate segment endpoint. +Additionally, encapsulate the matching packet within an outer IPv6 header +followed by the specified SRH. The destination address of the outer IPv6 +header is set to the first segment of the new SRH. The source +address is set as described in \fBip-sr\fR(8). +.in -4 + .in -8 .TP diff --git a/man/man8/tc-actions.8 b/man/man8/tc-actions.8 index 51f43afe..f46166e3 100644 --- a/man/man8/tc-actions.8 +++ b/man/man8/tc-actions.8 @@ -1,4 +1,4 @@ -.TH "actions in tc" 8 "4 Jul 2017" "iproute2" "Linux" +.TH "actions in tc" 8 "1 Aug 2017" "iproute2" "Linux" .SH NAME actions \- independently defined actions in tc @@ -33,6 +33,9 @@ actions \- independently defined actions in tc .B actions .BR ls " | " list .I ACTNAMESPEC +[ +.I ACTFILTER +] .in +8 .I ACTSPEC @@ -60,6 +63,10 @@ ACTNAME := .BI index " INDEX" +.I ACTFILTER +:= +.BI since " MSTIME" + .I COOKIESPEC := .BI cookie " COOKIE" @@ -71,9 +78,8 @@ ACTNAME .I ACTNAME may be any valid action type: gact, mirred, bpf, connmark, csum, police, etc. -.I ACTPARAMS -are the action-specific parameters; see the man page for the specific action -type to be used for details. +.I MSTIME +Time since last update. .I CONTROL := { @@ -132,6 +138,10 @@ List all the actions in the specified table. When combined with the option for .BR tc "," display the statistics for all actions in the specified table. +When combined with the option +.B since +allows doing a millisecond time-filter since the last time an +action was used in the datapath. .TP .B flush Delete all actions stored in the specified table. @@ -176,6 +186,19 @@ As such, it can be used as a correlating value for maintaining user state. The value to be stored is completely arbitrary and does not require a specific format. It is stored inside the action structure itself. +.TP +.BI since " MSTIME" +When dumping large number of actions, a millisecond time-filter can be +specified +.IR MSTIME "." +The +.I MSTIME +is a millisecond count since last time a packet hit the action. +As an example specifying "since 20000" implies to dump all actions +that have seen packets in the last 20 seconds. This option is useful +when the kernel has a large number of actions and you are only interested +in recently used actions. + .TP .I CONTROL The diff --git a/man/man8/tc-ife.8 b/man/man8/tc-ife.8 index 3a42d51b..fd2df6c3 100644 --- a/man/man8/tc-ife.8 +++ b/man/man8/tc-ife.8 @@ -82,7 +82,7 @@ is required only when Optional six byte destination or source MAC address to encode. .TP .BI type " TYPE" -Optional 16-bit ethertype to encode. +Optional 16-bit ethertype to encode. If not specified value of 0xED3E will be used. .TP .BI CONTROL Action to take following an encode/decode. diff --git a/misc/Makefile b/misc/Makefile index fa90308e..d02616e3 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -3,26 +3,12 @@ LNSTATOBJ=lnstat.o lnstat_util.o TARGETS=ss nstat ifstat rtacct lnstat -include ../Config +include ../config.mk ifeq ($(HAVE_BERKELEY_DB),y) TARGETS += arpd endif -ifeq ($(HAVE_SELINUX),y) - LDLIBS += $(shell $(PKG_CONFIG) --libs libselinux) - CFLAGS += $(shell $(PKG_CONFIG) --cflags libselinux) -DHAVE_SELINUX -endif - -ifeq ($(IP_CONFIG_SETNS),y) - CFLAGS += -DHAVE_SETNS -endif - -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif - all: $(TARGETS) ss: $(SSOBJ) diff --git a/netem/Makefile b/netem/Makefile index 2499f086..43418527 100644 --- a/netem/Makefile +++ b/netem/Makefile @@ -1,4 +1,4 @@ -include ../Config +include ../config.mk DISTGEN = maketable normal pareto paretonormal DISTDATA = normal.dist pareto.dist paretonormal.dist experimental.dist @@ -7,11 +7,6 @@ HOSTCC ?= $(CC) CCOPTS = $(CBUILD_CFLAGS) LDLIBS += -lm -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif - all: $(DISTGEN) $(DISTDATA) $(DISTGEN): diff --git a/rdma/Makefile b/rdma/Makefile index 1a9e4b1a..1b5aa50b 100644 --- a/rdma/Makefile +++ b/rdma/Makefile @@ -1,13 +1,10 @@ -include ../Config +include ../config.mk ifeq ($(HAVE_MNL),y) RDMA_OBJ = rdma.o utils.o dev.o link.o TARGETS=rdma -CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) -LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) - endif all: $(TARGETS) $(LIBS) diff --git a/tc/Makefile b/tc/Makefile index a9b4b8e6..777de5e6 100644 --- a/tc/Makefile +++ b/tc/Makefile @@ -2,11 +2,7 @@ TCOBJ= tc.o tc_qdisc.o tc_class.o tc_filter.o tc_util.o tc_monitor.o \ tc_exec.o m_police.o m_estimator.o m_action.o m_ematch.o \ emp_ematch.yacc.o emp_ematch.lex.o -include ../Config - -ifeq ($(IP_CONFIG_SETNS),y) - CFLAGS += -DHAVE_SETNS -endif +include ../config.mk SHARED_LIBS ?= y @@ -102,15 +98,6 @@ endif TCOBJ += $(TCMODULES) LDLIBS += -L. -lm -ifeq ($(HAVE_ELF),y) - CFLAGS += -DHAVE_ELF - LDLIBS += -lelf -endif -ifeq ($(HAVE_MNL),y) - CFLAGS += -DHAVE_LIBMNL $(shell $(PKG_CONFIG) libmnl --cflags) - LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) -endif - ifeq ($(SHARED_LIBS),y) LDLIBS += -ldl LDFLAGS += -Wl,-export-dynamic diff --git a/tc/f_basic.c b/tc/f_basic.c index d663668a..8370ea60 100644 --- a/tc/f_basic.c +++ b/tc/f_basic.c @@ -135,7 +135,7 @@ static int basic_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_BASIC_ACT]) { - tc_print_action(f, tb[TCA_BASIC_ACT]); + tc_print_action(f, tb[TCA_BASIC_ACT], 0); } return 0; diff --git a/tc/f_bpf.c b/tc/f_bpf.c index 4fb92093..3f619d0d 100644 --- a/tc/f_bpf.c +++ b/tc/f_bpf.c @@ -239,7 +239,7 @@ static int bpf_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_BPF_ACT]) - tc_print_action(f, tb[TCA_BPF_ACT]); + tc_print_action(f, tb[TCA_BPF_ACT], 0); return 0; } diff --git a/tc/f_cgroup.c b/tc/f_cgroup.c index ecf99099..633700e6 100644 --- a/tc/f_cgroup.c +++ b/tc/f_cgroup.c @@ -102,7 +102,7 @@ static int cgroup_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_CGROUP_ACT]) - tc_print_action(f, tb[TCA_CGROUP_ACT]); + tc_print_action(f, tb[TCA_CGROUP_ACT], 0); return 0; } diff --git a/tc/f_flow.c b/tc/f_flow.c index 09ddcaa6..b1571049 100644 --- a/tc/f_flow.c +++ b/tc/f_flow.c @@ -347,7 +347,7 @@ static int flow_print_opt(struct filter_util *fu, FILE *f, struct rtattr *opt, tc_print_police(f, tb[TCA_FLOW_POLICE]); if (tb[TCA_FLOW_ACT]) { fprintf(f, "\n"); - tc_print_action(f, tb[TCA_FLOW_ACT]); + tc_print_action(f, tb[TCA_FLOW_ACT], 0); } return 0; } diff --git a/tc/f_flower.c b/tc/f_flower.c index 5be693ab..934832e2 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -1316,7 +1316,7 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_FLOWER_ACT]) - tc_print_action(f, tb[TCA_FLOWER_ACT]); + tc_print_action(f, tb[TCA_FLOWER_ACT], 0); return 0; } diff --git a/tc/f_fw.c b/tc/f_fw.c index 790bef96..c39789b3 100644 --- a/tc/f_fw.c +++ b/tc/f_fw.c @@ -160,7 +160,7 @@ static int fw_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, __u if (tb[TCA_FW_ACT]) { fprintf(f, "\n"); - tc_print_action(f, tb[TCA_FW_ACT]); + tc_print_action(f, tb[TCA_FW_ACT], 0); } return 0; } diff --git a/tc/f_matchall.c b/tc/f_matchall.c index 5a51e755..d78660e7 100644 --- a/tc/f_matchall.c +++ b/tc/f_matchall.c @@ -145,7 +145,7 @@ static int matchall_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_MATCHALL_ACT]) - tc_print_action(f, tb[TCA_MATCHALL_ACT]); + tc_print_action(f, tb[TCA_MATCHALL_ACT], 0); return 0; } diff --git a/tc/f_route.c b/tc/f_route.c index 30514c4f..e88313f6 100644 --- a/tc/f_route.c +++ b/tc/f_route.c @@ -168,7 +168,7 @@ static int route_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, if (tb[TCA_ROUTE4_POLICE]) tc_print_police(f, tb[TCA_ROUTE4_POLICE]); if (tb[TCA_ROUTE4_ACT]) - tc_print_action(f, tb[TCA_ROUTE4_ACT]); + tc_print_action(f, tb[TCA_ROUTE4_ACT], 0); return 0; } diff --git a/tc/f_rsvp.c b/tc/f_rsvp.c index 94bfbefe..65caeb42 100644 --- a/tc/f_rsvp.c +++ b/tc/f_rsvp.c @@ -402,7 +402,7 @@ static int rsvp_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, _ } if (tb[TCA_RSVP_ACT]) { - tc_print_action(f, tb[TCA_RSVP_ACT]); + tc_print_action(f, tb[TCA_RSVP_ACT], 0); } if (tb[TCA_RSVP_POLICE]) tc_print_police(f, tb[TCA_RSVP_POLICE]); diff --git a/tc/f_tcindex.c b/tc/f_tcindex.c index 784c8905..dd1cb475 100644 --- a/tc/f_tcindex.c +++ b/tc/f_tcindex.c @@ -173,7 +173,7 @@ static int tcindex_print_opt(struct filter_util *qu, FILE *f, } if (tb[TCA_TCINDEX_ACT]) { fprintf(f, "\n"); - tc_print_action(f, tb[TCA_TCINDEX_ACT]); + tc_print_action(f, tb[TCA_TCINDEX_ACT], 0); } return 0; } diff --git a/tc/f_u32.c b/tc/f_u32.c index b272c2cb..5815be9c 100644 --- a/tc/f_u32.c +++ b/tc/f_u32.c @@ -1337,7 +1337,7 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt, } if (tb[TCA_U32_ACT]) - tc_print_action(f, tb[TCA_U32_ACT]); + tc_print_action(f, tb[TCA_U32_ACT], 0); return 0; } diff --git a/tc/m_action.c b/tc/m_action.c index 397e9c5d..402228bb 100644 --- a/tc/m_action.c +++ b/tc/m_action.c @@ -346,21 +346,25 @@ tc_print_action_flush(FILE *f, const struct rtattr *arg) } int -tc_print_action(FILE *f, const struct rtattr *arg) +tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts) { int i; - struct rtattr *tb[TCA_ACT_MAX_PRIO + 1]; if (arg == NULL) return 0; - parse_rtattr_nested(tb, TCA_ACT_MAX_PRIO, arg); + if (!tot_acts) + tot_acts = TCA_ACT_MAX_PRIO; + + struct rtattr *tb[tot_acts + 1]; + + parse_rtattr_nested(tb, tot_acts, arg); if (tab_flush && NULL != tb[0] && NULL == tb[1]) return tc_print_action_flush(f, tb[0]); - for (i = 0; i < TCA_ACT_MAX_PRIO; i++) { + for (i = 0; i < tot_acts; i++) { if (tb[i]) { fprintf(f, "\n\taction order %d: ", i); if (tc_print_one_action(f, tb[i]) < 0) { @@ -380,7 +384,8 @@ int print_action(const struct sockaddr_nl *who, FILE *fp = (FILE *)arg; struct tcamsg *t = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr *tb[TCAA_MAX+1]; + __u32 *tot_acts = NULL; + struct rtattr *tb[TCA_ROOT_MAX+1]; len -= NLMSG_LENGTH(sizeof(*t)); @@ -389,8 +394,12 @@ int print_action(const struct sockaddr_nl *who, return -1; } - parse_rtattr(tb, TCAA_MAX, TA_RTA(t), len); + parse_rtattr(tb, TCA_ROOT_MAX, TA_RTA(t), len); + if (tb[TCA_ROOT_COUNT]) + tot_acts = RTA_DATA(tb[TCA_ROOT_COUNT]); + + fprintf(fp, "total acts %d\n", tot_acts ? *tot_acts:0); if (tb[TCA_ACT_TAB] == NULL) { if (n->nlmsg_type != RTM_GETACTION) fprintf(stderr, "print_action: NULL kind\n"); @@ -414,7 +423,9 @@ int print_action(const struct sockaddr_nl *who, fprintf(fp, "Replaced action "); } } - tc_print_action(fp, tb[TCA_ACT_TAB]); + + + tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0); return 0; } @@ -427,7 +438,7 @@ static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p char **argv = *argv_p; int prio = 0; int ret = 0; - __u32 i; + __u32 i = 0; struct rtattr *tail; struct rtattr *tail2; struct nlmsghdr *ans = NULL; @@ -498,7 +509,8 @@ static int tc_action_gd(int cmd, unsigned int flags, int *argc_p, char ***argv_p tail2 = NLMSG_TAIL(&req.n); addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); - addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); + if (i > 0) + addattr32(&req.n, MAX_MSG, TCA_ACT_INDEX, i); tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; } @@ -561,12 +573,16 @@ static int tc_action_modify(int cmd, unsigned int flags, int *argc_p, char ***ar return ret; } -static int tc_act_list_or_flush(int argc, char **argv, int event) +static int tc_act_list_or_flush(int *argc_p, char ***argv_p, int event) { + struct rtattr *tail, *tail2, *tail3, *tail4; int ret = 0, prio = 0, msg_size = 0; - char k[16]; - struct rtattr *tail, *tail2; struct action_util *a = NULL; + struct nla_bitfield32 flag_select = { 0 }; + char **argv = *argv_p; + __u32 msec_since = 0; + int argc = *argc_p; + char k[16]; struct { struct nlmsghdr n; struct tcamsg t; @@ -597,11 +613,31 @@ static int tc_act_list_or_flush(int argc, char **argv, int event) } strncpy(k, *argv, sizeof(k) - 1); + argc -= 1; + argv += 1; + + if (argc && (strcmp(*argv, "since") == 0)) { + NEXT_ARG(); + if (get_u32(&msec_since, *argv, 0)) + invarg("dump time \"since\" is invalid", *argv); + } + addattr_l(&req.n, MAX_MSG, ++prio, NULL, 0); addattr_l(&req.n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1); tail2->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail2; tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail; + tail3 = NLMSG_TAIL(&req.n); + flag_select.value |= TCA_FLAG_LARGE_DUMP_ON; + flag_select.selector |= TCA_FLAG_LARGE_DUMP_ON; + addattr_l(&req.n, MAX_MSG, TCA_ROOT_FLAGS, &flag_select, + sizeof(struct nla_bitfield32)); + tail3->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail3; + if (msec_since) { + tail4 = NLMSG_TAIL(&req.n); + addattr32(&req.n, MAX_MSG, TCA_ROOT_TIME_DELTA, msec_since); + tail4->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail4; + } msg_size = NLMSG_ALIGN(req.n.nlmsg_len) - NLMSG_ALIGN(sizeof(struct nlmsghdr)); if (event == RTM_GETACTION) { @@ -626,6 +662,8 @@ static int tc_act_list_or_flush(int argc, char **argv, int event) bad_val: + *argc_p = argc; + *argv_p = argv; return ret; } @@ -655,13 +693,21 @@ int do_action(int argc, char **argv) act_usage(); return -1; } - return tc_act_list_or_flush(argc-2, argv+2, RTM_GETACTION); + + argc -= 2; + argv += 2; + return tc_act_list_or_flush(&argc, &argv, + RTM_GETACTION); } else if (matches(*argv, "flush") == 0) { if (argc <= 2) { act_usage(); return -1; } - return tc_act_list_or_flush(argc-2, argv+2, RTM_DELACTION); + + argc -= 2; + argv += 2; + return tc_act_list_or_flush(&argc, &argv, + RTM_DELACTION); } else if (matches(*argv, "help") == 0) { act_usage(); return -1; diff --git a/tc/m_ife.c b/tc/m_ife.c index e3521e62..5633ab90 100644 --- a/tc/m_ife.c +++ b/tc/m_ife.c @@ -63,6 +63,7 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, char dbuf[ETH_ALEN]; char sbuf[ETH_ALEN]; __u16 ife_type = 0; + int user_type = 0; __u32 ife_prio = 0; __u32 ife_prio_v = 0; __u32 ife_mark = 0; @@ -124,7 +125,8 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, NEXT_ARG(); if (get_u16(&ife_type, *argv, 0)) invarg("ife type is invalid", *argv); - fprintf(stderr, "IFE type 0x%x\n", ife_type); + fprintf(stderr, "IFE type 0x%04X\n", ife_type); + user_type = 1; } else if (matches(*argv, "dst") == 0) { NEXT_ARG(); daddr = *argv; @@ -185,8 +187,10 @@ static int parse_ife(struct action_util *a, int *argc_p, char ***argv_p, if (daddr) addattr_l(n, MAX_MSG, TCA_IFE_DMAC, dbuf, ETH_ALEN); - if (ife_type) + if (user_type) addattr_l(n, MAX_MSG, TCA_IFE_TYPE, &ife_type, 2); + else + fprintf(stderr, "IFE type 0x%04X\n", ETH_P_IFE); if (saddr) addattr_l(n, MAX_MSG, TCA_IFE_SMAC, sbuf, ETH_ALEN); diff --git a/tc/tc_util.h b/tc/tc_util.h index 5c54ad38..583a21a8 100644 --- a/tc/tc_util.h +++ b/tc/tc_util.h @@ -113,7 +113,7 @@ int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); int print_police(struct action_util *a, FILE *f, struct rtattr *tb); int police_print_xstats(struct action_util *a, FILE *f, struct rtattr *tb); -int tc_print_action(FILE *f, const struct rtattr *tb); +int tc_print_action(FILE *f, const struct rtattr *tb, unsigned short tot_acts); int tc_print_ipt(FILE *f, const struct rtattr *tb); int parse_action(int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n); void print_tm(FILE *f, const struct tcf_t *tm); diff --git a/tipc/Makefile b/tipc/Makefile index 87e3cac3..2212beb0 100644 --- a/tipc/Makefile +++ b/tipc/Makefile @@ -1,4 +1,5 @@ -include ../Config +include ../config.mk + ifeq ($(HAVE_MNL),y) TIPCOBJ=bearer.o \ @@ -8,8 +9,6 @@ TIPCOBJ=bearer.o \ node.o socket.o \ peer.o tipc.o -include ../Config - TARGETS=tipc CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs)