From patchwork Thu Aug 17 17:35:52 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Julien Fortin X-Patchwork-Id: 802792 X-Patchwork-Delegate: shemminger@vyatta.com Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=cumulusnetworks.com header.i=@cumulusnetworks.com header.b="IYKeFLl+"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3xYD5Q6kd8z9t4X for ; Fri, 18 Aug 2017 03:40:42 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753482AbdHQRkl (ORCPT ); Thu, 17 Aug 2017 13:40:41 -0400 Received: from mail-wr0-f181.google.com ([209.85.128.181]:38548 "EHLO mail-wr0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753458AbdHQRki (ORCPT ); Thu, 17 Aug 2017 13:40:38 -0400 Received: by mail-wr0-f181.google.com with SMTP id 5so25407710wrz.5 for ; Thu, 17 Aug 2017 10:40:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cumulusnetworks.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=VmdRKyZ7eic8iTim0PDi8Na81cMsqvelciSPXHvjR3g=; b=IYKeFLl+eHhOgjtQpvdbsJhV1l25b4Q6+UZRP4PLGUAc0tnMl17fbuA/7OVzU65d+h zm9RAgNZhJ3WHUpD1zLnWYgaLA0TuezzJU/AHk6KulLgU/VmMimw4IHJq9NE5iR8lIZe bnO1moxMq8TPVv2h3VaOtPvjw7+6rBE0cvi78= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=VmdRKyZ7eic8iTim0PDi8Na81cMsqvelciSPXHvjR3g=; b=EDwlGv/h+hCEQaRu7RJV3mgJhyuMzCDcT6LRamJJ6auntS60Kih8O3kFfzULpj/Wmd PocGZDyEbxGFn9UmhWPEn98wasW9nyHD+wuR64h2jmzIPD5hlhDPFGMN1IClRGAi+hFT 8zJWM3ik2FdMF8tFnhB7W8/n73ab2xvZGudWzfFemMdrDGQOH+qPrw9nV7wghJBjWJR0 6D9zDZaK9Kyp6OfiGxzar2wwXWcniOs5//kXFsLvtlKw0hwHBpEY+hMV6csdUGgmZcUg e5qbWscesQLBle0K4lgPwcFqP8MX0jPsOjM71ZirXSRZYD7V2ANoatmVRFKEavixmmDR RnKw== X-Gm-Message-State: AHYfb5jOm+yYUEOJ3JPsJxNgECYDYO5Ff1a/0lvEOahfu5BrzQ6kcH3m ApFyVZ7xEalwHJc8K9JVlXQu X-Received: by 10.28.30.77 with SMTP id e74mr1870278wme.172.1502991635577; Thu, 17 Aug 2017 10:40:35 -0700 (PDT) Received: from localhost.localdomain ([37.169.21.172]) by smtp.googlemail.com with ESMTPSA id k13sm3902040wrd.4.2017.08.17.10.40.31 (version=TLS1 cipher=AES128-SHA bits=128/128); Thu, 17 Aug 2017 10:40:34 -0700 (PDT) From: Julien Fortin X-Google-Original-From: Julien Fortin To: netdev@vger.kernel.org Cc: roopa@cumulusnetworks.com, nikolay@cumulusnetworks.com, dsa@cumulusnetworks.com, Julien Fortin Subject: [PATCH iproute2 json v2 05/27] ip: ipaddress.c: add support for json output Date: Thu, 17 Aug 2017 10:35:52 -0700 Message-Id: <20170817173614.54987-6-julien@cumulusnetworks.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20170817173614.54987-1-julien@cumulusnetworks.com> References: <20170817173614.54987-1-julien@cumulusnetworks.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Julien Fortin This patch converts all output (mostly fprintfs) to the new ip_print api which handle both regular and json output. Initialize a json_writer and open an array object if -json was specified. Note that the JSON attribute naming follows the NETLINK_ATTRIBUTE naming. In many places throughout the code, IP, matches integer values with hardcoded strings tables, such as link mode, link operstate or link family. In JSON context, this will result in a named string field. In the very unlikely event that the requested index is out of bound, IP displays the raw integer value. For JSON context this result in having a different integer field example bellow: if (mode >= ARRAY_SIZE(link_modes)) print_int(PRINT_ANY, "linkmode_index", "mode %d ", mode); else print_string(PRINT_ANY, "linkmode", "mode %s ", link_modes[mode]); The "_index" suffix is open to discussion and it is something that I came up with. The bottom line is that you can't have a string field that may become an int field in specific cases. Programs written in strongly type languages (like C) might break if they are expecting a string value and got an integer instead. We don't want to confuse anybody or make the code even more complicated handling these specifics cases. Hence the extra "_index" field that is easy to check for and deal with. JSON schema, followed by live example: Live config used: $ ip link add dev vxlan42 type vxlan id 42 $ ip link add dev bond0 type bond $ ip link add name swp1.50 link swp1 type vlan id 50 $ ip link add dev br0 type bridge $ ip link set dev vxlan42 master br0 $ ip link set dev bond0 master br0 $ ip link set dev swp1.50 master br0 $ ip link set dev br0 up $ ip -d link show 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 08:00:27:db:31:88 brd ff:ff:ff:ff:ff:ff promiscuity 0 addrgenmode eui64 3: swp1: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 0 addrgenmode eui64 10: vxlan42: mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default link/ether 4a:d9:91:42:a2:d2 brd ff:ff:ff:ff:ff:ff promiscuity 1 vxlan id 42 srcport 0 0 dstport 8472 ageing 300 bridge_slave state disabled priority 8 cost 100 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8001 port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge 8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off addrgenmode eui64 11: bond0: mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default link/ether e2:aa:7b:17:c5:14 brd ff:ff:ff:ff:ff:ff promiscuity 1 bond mode 802.3ad miimon 100 updelay 0 downdelay 0 use_carrier 1 arp_interval 0 arp_validate none arp_all_targets any primary_reselect always fail_over_mac none xmit_hash_policy layer3+4 resend_igmp 1 num_grat_arp 1 all_slaves_active 0 min_links 1 lp_interval 1 packets_per_slave 1 lacp_rate fast ad_select stable ad_actor_sys_prio 65535 ad_user_port_key 0 ad_actor_system 00:00:00:00:00:00 bridge_slave state disabled priority 8 cost 100 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8002 port_no 0x2 designated_port 32770 designated_cost 0 designated_bridge 8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off addrgenmode eui64 12: swp1.50@swp1: mtu 1500 qdisc noop master br0 state DOWN mode DEFAULT group default link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 1 vlan protocol 802.1Q id 50 bridge_slave state disabled priority 8 cost 100 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8003 port_no 0x3 designated_port 32771 designated_cost 0 designated_bridge 8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off addrgenmode eui64 13: br0: mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default link/ether 08:00:27:5b:b1:75 brd ff:ff:ff:ff:ff:ff promiscuity 0 bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.8:0:27:5b:b1:75 designated_root 8000.8:0:27:5b:b1:75 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 244.44 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 4096 mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode eui64 // Schema for: ip -brief link show [ { "deleted": { "type": "bool", "attr": "RTM_DELLINK" }, "link": { "type": "string", "attr": "IFLA_LINK" }, "ifname": { "type": "string", "attr": "IFNAME" }, "operstate": { "type": "string", "attr": "IFLA_OPERSTATE", "mutually_exclusive": { "operstate_index": { "type": "uint", "comment": "if state >= ARRAY_SIZE(oper_states)" } } }, "address": { "type": "string", "attr": "IFLA_ADDRESS" }, "flags": { "type": "array", "attr": "IFF_LOOPBACK, IFF_BROADCAST...IFF_*" }, "addr_info": { "type": "array", "array": [ { "deleted": { "type": "bool", "attr": "RTM_DELADDR" }, "family": { "type": "string", "attr": "ifa->ifa_family", "mutually_exclusive": { "family_index": { "type": "uint", "comment": "if family is not known" } } }, "local": { "type": "string", "attr": "IFA_LOCAL" }, "address": { "type": "string", "attr": "IFLA_LOCAL && IFA_ADDRESS" }, "prefixlen": { "type": "int", "attr": "IFLA_LOCAL" } } ] } } ] $ ip -json -brief link show [{ "ifname": "lo", "operstate": "UNKNOWN", "address": "00:00:00:00:00:00", "flags": ["LOOPBACK","UP","LOWER_UP"] },{ "ifname": "eth0", "operstate": "UP", "address": "08:00:27:db:31:88", "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"] },{ "ifname": "swp1", "operstate": "DOWN", "address": "08:00:27:5b:b1:75", "flags": ["BROADCAST","MULTICAST"] },{ "ifname": "vxlan42", "operstate": "DOWN", "address": "4a:d9:91:42:a2:d2", "flags": ["BROADCAST","MULTICAST"] },{ "ifname": "bond0", "operstate": "DOWN", "address": "e2:aa:7b:17:c5:14", "flags": ["BROADCAST","MULTICAST","MASTER"] },{ "link": "swp1", "ifname": "swp1.50", "operstate": "DOWN", "address": "08:00:27:5b:b1:75", "flags": ["BROADCAST","MULTICAST","M-DOWN"] },{ "ifname": "br0", "operstate": "DOWN", "address": "08:00:27:5b:b1:75", "flags": ["NO-CARRIER","BROADCAST","MULTICAST","UP"] } ] Schema for normal plus -details: ip -json -details link show [ { "deleted": { "type": "bool", "attr": "RTM_DELLINK" }, "ifindex": { "type": "int" }, "ifname": { "type": "string", "attr": "IFLA_IFNAME" }, "link": { "type": "string", "attr": "IFLA_LINK", "mutually_exclusive": { "link_index": { "type": "int", "comment": "if IFLA_LINK_NETNSID exists" } } }, "flags": { "type": "array", "attr": "IFF_LOOPBACK, IFF_BROADCAST...IFF_*" }, "mtu": { "type": "int", "attr": "IFLA_MTU" }, "xdp": { "type": "object", "attr": "IFLA_XDP", "object": { "mode": { "type": "utin", "attr": "IFLA_XDP_ATTACHED" }, "prog_id": { "type": "uint", "attr": "IFLA_XDP_PROG_ID" } } }, "qdisc": { "type": "string", "attr": "IFLA_QDISC" }, "master": { "type": "string", "attr": "IFLA_MASTER" }, "operstate": { "type": "string", "attr": "IFLA_OPERSTATE", "mutually_exclusive": { "operstate_index": { "type": "uint", "comment": "if state >= ARRAY_SIZE(oper_states)" } } }, "linkmode": { "type": "string", "attr": "IFLA_LINKMODE", "mutually_exclusive": { "linkmode_index": { "type": "uint", "comment": "if mode >= ARRAY_SIZE(link_modes)" } } }, "group": { "type": "string", "attr": "IFLA_GROUP" }, "txqlen": { "type": "int", "attr": "IFLA_TXQLEN" }, "event": { "type": "string", "attr": "IFLA_EVENT", "mutually_exclusive": { "event_index": { "type": "uint", "attr": "IFLA_OPERSTATE", "comment": "if event >= ARRAY_SIZE(link_events)" } } }, "link_type": { "type": "string", "attr": "ifi_type" }, "address": { "type": "string", "attr": "IFLA_ADDRESS" }, "link_pointtopoint": { "type": "bool", "attr": "IFF_POINTOPOINT" }, "broadcast": { "type": "string", "attr": "IFLA_BROADCAST" }, "link_netnsid": { "type": "int", "attr": "IFLA_LINK_NETNSID" }, "proto_down": { "type": "bool", "attr": "IFLA_PROTO_DOWN" }, // // if -details // "promiscuity": { "type": "uint", "attr": "IFLA_PROMISCUITY" }, "linkinfo": { "type": "dict", "attr": "IFLA_LINKINFO", "dict": { "info_kind": { "type": "string", "attr": "IFLA_INFO_KIND" }, "info_data": { "type": "dict", "attr": "IFLA_INFO_DATA", "dict": {} }, "info_xstats": { "type": "dict", "attr": "IFLA_INFO_XSTATS", "dict": {} }, "info_slave_data": { "type": "dict", "attr": "IFLA_INFO_SLAVE_DATA", "dict": {} } } }, "inet6_addr_gen_mode": { "type": "string", "attr": "IFLA_INET6_ADDR_GEN_MODE" }, "num_tx_queues": { "type": "uint", "attr": "IFLA_NUM_TX_QUEUES" }, "num_rx_queues": { "type": "uint", "attr": "IFLA_NUM_RX_QUEUES" }, "gso_max_size": { "type": "uint", "attr": "IFLA_GSO_MAX_SIZE" }, "gso_max_segs": { "type": "uint", "attr": "IFLA_GSO_MAX_SEGS" }, "phys_port_name": { "type": "string", "attr": "IFLA_PHYS_PORT_NAME" }, "phys_port_id": { "type": "string", "attr": "IFLA_PHYS_PORT_ID" }, "phys_switch_id": { "type": "string", "attr": "IFLA_PHYS_SWITCH_ID" }, "ifalias": { "type": "string", "attr": "IFLA_IFALIAS" }, "stats": { "type": "dict", "attr": "IFLA_STATS", "dict": { "rx": { "type": "dict", "dict": { "bytes": { "type": "uint" }, "packets": { "type": "uint" }, "errors": { "type": "uint" }, "dropped": { "type": "uint" }, "over_errors": { "type": "uint" }, "multicast": { "type": "uint" }, "compressed": { "type": "uint" }, "length_errors": { "type": "uint" }, "crc_errors": { "type": "uint" }, "frame_errors": { "type": "uint" }, "fifo_errors": { "type": "uint" }, "missed_errors": { "type": "uint" }, "nohandler": { "type": "uint" } } }, "tx": { "type": "dict", "dict": { "bytes": { "type": "uint" }, "packets": { "type": "uint" }, "errors": { "type": "uint" }, "dropped": { "type": "uint" }, "carrier_errors": { "type": "uint" }, "collisions": { "type": "uint" }, "compressed": { "type": "uint" }, "aborted_errors": { "type": "uint" }, "fifo_errors": { "type": "uint" }, "window_errors": { "type": "uint" }, "heartbeat_errors": { "type": "uint" }, "carrier_changes": { "type": "uint" } } } } }, "stats64": { "type": "dict", "attr": "IFLA_STATS64", "dict": { "rx": { "type": "dict", "dict": { "bytes": { "type": "uint" }, "packets": { "type": "uint" }, "errors": { "type": "uint" }, "dropped": { "type": "uint" }, "over_errors": { "type": "uint" }, "multicast": { "type": "uint" }, "compressed": { "type": "uint" }, "length_errors": { "type": "uint" }, "crc_errors": { "type": "uint" }, "frame_errors": { "type": "uint" }, "fifo_errors": { "type": "uint" }, "missed_errors": { "type": "uint" }, "nohandler": { "type": "uint" } } }, "tx": { "type": "dict", "dict": { "bytes": { "type": "uint" }, "packets": { "type": "uint" }, "errors": { "type": "uint" }, "dropped": { "type": "uint" }, "carrier_errors": { "type": "uint" }, "collisions": { "type": "uint" }, "compressed": { "type": "uint" }, "aborted_errors": { "type": "uint" }, "fifo_errors": { "type": "uint" }, "window_errors": { "type": "uint" }, "heartbeat_errors": { "type": "uint" }, "carrier_changes": { "type": "uint" } } } } }, "vfinfo_list": { "type": "array", "attr": "IFLA_VFINFO_LIST", "array": [ { "vf": { "type": "int" }, "mac": { "type": "string" }, "vlan_list": { "type": "array", "attr": "IFLA_VF_VLAN_LIST", "array": [ { "vlan": { "type": "int" }, "qos": { "type": "int" }, "protocol": { "type": "string" } } ] }, "vlan": { "type": "int", "attr": "!IFLA_VF_VLAN_LIST && IFLA_VF_VLAN" }, "qos": { "type": "int", "attr": "!IFLA_VF_VLAN_LIST && IFLA_VF_VLAN" }, "tx_rate": { "type": "int" }, "rate": { "type": "dict", "attr": "IFLA_VF_RATE", "dict": { "max_tx": { "type": "int" }, "min_tx": { "type": "int" } } }, "spoofchk": { "type": "bool", "attr": "IFLA_VF_SPOOFCHK" }, "link_state": { "type": "string", "attr": "IFLA_VF_LINK_STATE" }, "trust": { "type": "bool", "attr": "IFLA_VF_TRUST" }, "query_rss_en": { "type": "bool", "attr": "IFLA_VF_RSS_QUERY_EN" }, "stats": { "type": "dict", "attr": "IFLA_VF_STATS", "dict": { "rx": { "type": "dict", "dict": { "bytes": { "type": "uint", "attr": "IFLA_VF_STATS_RX_BYTES" }, "packets": { "type": "uint", "attr": "IFLA_VF_STATS_RX_PACKETS" }, "multicast": { "type": "uint", "attr": "IFLA_VF_STATS_MULTICAST" }, "broadcast": { "type": "uint", "attr": "IFLA_VF_STATS_BROADCAST" } } }, "tx": { "type": "dict", "dict": { "bytes": { "type": "uint", "attr": "IFLA_VF_STATS_TX_BYTES" }, "packets": { "type": "uint", "attr": "IFLA_VF_STATS_TX_PACKETS" } } } } } } ] } } ] Example with the config previously given: Note that here, linkinfo attributes are not populated. The schemas are provided in each link type patches. $ ip -details -json link show [{ "ifindex": 1, "ifname": "lo", "flags": ["LOOPBACK","UP","LOWER_UP"], "mtu": 65536, "qdisc": "noqueue", "operstate": "UNKNOWN", "linkmode": "DEFAULT", "group": "default", "link_type": "loopback", "address": "00:00:00:00:00:00", "broadcast": "00:00:00:00:00:00", "promiscuity": 0, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 2, "ifname": "eth0", "flags": ["BROADCAST","MULTICAST","UP","LOWER_UP"], "mtu": 1500, "qdisc": "pfifo_fast", "operstate": "UP", "linkmode": "DEFAULT", "group": "default", "txqlen": 1000, "link_type": "ether", "address": "08:00:27:db:31:88", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 0, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 3, "ifname": "swp1", "flags": ["BROADCAST","MULTICAST"], "mtu": 1500, "qdisc": "noop", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "txqlen": 1000, "link_type": "ether", "address": "08:00:27:5b:b1:75", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 0, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 10, "ifname": "vxlan42", "flags": ["BROADCAST","MULTICAST"], "mtu": 1500, "qdisc": "noop", "master": "br0", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "link_type": "ether", "address": "4a:d9:91:42:a2:d2", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 1, "linkinfo": { "info_kind": "vxlan", "info_data": {}, "info_slave_kind": "bridge", "info_slave_data": {} }, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 11, "ifname": "bond0", "flags": ["BROADCAST","MULTICAST","MASTER"], "mtu": 1500, "qdisc": "noop", "master": "br0", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "link_type": "ether", "address": "e2:aa:7b:17:c5:14", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 1, "linkinfo": { "info_kind": "bond", "info_data": {}, "info_slave_kind": "bridge", "info_slave_data": {}, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 16, "num_rx_queues": 16, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 12, "ifname": "swp1.50", "link": "swp1", "flags": ["BROADCAST","MULTICAST","M-DOWN"], "mtu": 1500, "qdisc": "noop", "master": "br0", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "link_type": "ether", "address": "08:00:27:5b:b1:75", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 1, "linkinfo": { "info_kind": "vlan", "info_data": {}, "info_slave_kind": "bridge", "info_slave_data": {}, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 },{ "ifindex": 13, "ifname": "br0", "flags": ["NO-CARRIER","BROADCAST","MULTICAST","UP"], "mtu": 1500, "qdisc": "noqueue", "operstate": "DOWN", "linkmode": "DEFAULT", "group": "default", "link_type": "ether", "address": "08:00:27:5b:b1:75", "broadcast": "ff:ff:ff:ff:ff:ff", "promiscuity": 0, "linkinfo": { "info_kind": "bridge", "info_data": {}, "inet6_addr_gen_mode": "eui64", "num_tx_queues": 1, "num_rx_queues": 1, "gso_max_size": 65536, "gso_max_segs": 65535 } ] Signed-off-by: Julien Fortin --- ip/ipaddress.c | 1064 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 753 insertions(+), 311 deletions(-) diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 4d37c5e0..81a5888f 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" @@ -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]); - - 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); + int max_tx = vf_rate->max_tx_rate; + int min_tx = vf_rate->min_tx_rate; + + if (is_json_context()) { + open_json_object("rate"); + print_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"); + + /* 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 */ + 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_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])); + 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, " "); + /* 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])); + 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_); - - 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); + if (is_json_context()) { + open_json_object("stats644"); + + /* 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) { + 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(); + + /* 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); + + /* 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(); - } - fprintf(fp, "%s", _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->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); - /* 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)); + /* 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_); - - - 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_); + 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)); + } - /* 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"); + close_json_object(); + close_json_object(); + } else { + /* RX stats */ + fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", + s->rx_compressed ? "compressed" : "", _SL_); + + + fprintf(fp, " "); + print_num(fp, 10, s->rx_bytes); + print_num(fp, 8, s->rx_packets); + print_num(fp, 7, s->rx_errors); + print_num(fp, 7, s->rx_dropped); + print_num(fp, 7, s->rx_over_errors); + print_num(fp, 7, s->multicast); + if (s->rx_compressed) + print_num(fp, 7, s->rx_compressed); + + /* RX error stats */ + if (show_stats > 1) { + fprintf(fp, "%s", _SL_); + fprintf(fp, " RX errors: length crc frame fifo missed%s%s", + s->rx_nohandler ? " nohandler" : "", _SL_); + fprintf(fp, " "); + print_num(fp, 8, s->rx_length_errors); + print_num(fp, 7, s->rx_crc_errors); + print_num(fp, 7, s->rx_frame_errors); + print_num(fp, 7, s->rx_fifo_errors); + print_num(fp, 7, s->rx_missed_errors); + if (s->rx_nohandler) + print_num(fp, 7, s->rx_nohandler); + } fprintf(fp, "%s", _SL_); - 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 "); - - fprintf(fp, "%d: ", ifi->ifi_index); - color_fprintf(fp, COLOR_IFNAME, "%s", - tb[IFLA_IFNAME] ? rta_getattr_str(tb[IFLA_IFNAME]) : ""); + print_bool(PRINT_ANY, "deleted", "Deleted ", true); + + 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]); 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,50 +1255,68 @@ 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_stats) { - fprintf(fp, "%s", _SL_); + print_string(PRINT_FP, NULL, "%s", _SL_); __print_link_stats(fp, tb); } @@ -966,11 +1324,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; } @@ -1009,7 +1372,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 */ @@ -1042,20 +1405,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); + } + } } @@ -1176,80 +1553,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; @@ -1258,6 +1685,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); @@ -1275,10 +1703,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; @@ -1724,6 +2156,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 @@ -1732,8 +2170,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); } @@ -1755,6 +2195,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) @@ -1763,13 +2204,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); @@ -1777,7 +2219,7 @@ out: if (ainfo) free_nlmsg_chain(ainfo); free_nlmsg_chain(&linfo); - + delete_json_obj(); return 0; }