]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/iplink_bond.c
ip route: get: allow zero-length subnet mask
[mirror_iproute2.git] / ip / iplink_bond.c
index 3fb7f4f4ca0f0a7a78dba39519f7767716346ee2..f906e7f1b323239f63162748dbc34b77ef44fbfd 100644 (file)
  *              2 of the License, or (at your option) any later version.
  *
  * Authors:     Jiri Pirko <jiri@resnulli.us>
+ *              Scott Feldman <sfeldma@cumulusnetworks.com>
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <linux/if_link.h>
+#include <linux/if_ether.h>
 #include <net/if.h>
 
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
 
-static void explain(void)
+#define BOND_MAX_ARP_TARGETS    16
+
+static const char *mode_tbl[] = {
+       "balance-rr",
+       "active-backup",
+       "balance-xor",
+       "broadcast",
+       "802.3ad",
+       "balance-tlb",
+       "balance-alb",
+       NULL,
+};
+
+static const char *arp_validate_tbl[] = {
+       "none",
+       "active",
+       "backup",
+       "all",
+       NULL,
+};
+
+static const char *arp_all_targets_tbl[] = {
+       "any",
+       "all",
+       NULL,
+};
+
+static const char *primary_reselect_tbl[] = {
+       "always",
+       "better",
+       "failure",
+       NULL,
+};
+
+static const char *fail_over_mac_tbl[] = {
+       "none",
+       "active",
+       "follow",
+       NULL,
+};
+
+static const char *xmit_hash_policy_tbl[] = {
+       "layer2",
+       "layer3+4",
+       "layer2+3",
+       "encap2+3",
+       "encap3+4",
+       NULL,
+};
+
+static const char *lacp_rate_tbl[] = {
+       "slow",
+       "fast",
+       NULL,
+};
+
+static const char *ad_select_tbl[] = {
+       "stable",
+       "bandwidth",
+       "count",
+       NULL,
+};
+
+static const char *get_name(const char **tbl, int index)
 {
-       fprintf(stderr,
+       int i;
+
+       for (i = 0; tbl[i]; i++)
+               if (i == index)
+                       return tbl[i];
+
+       return "UNKNOWN";
+}
+
+static int get_index(const char **tbl, char *name)
+{
+       int i, index;
+
+       /* check for integer index passed in instead of name */
+       if (get_integer(&index, name, 10) == 0)
+               for (i = 0; tbl[i]; i++)
+                       if (i == index)
+                               return i;
+
+       for (i = 0; tbl[i]; i++)
+               if (strcmp(tbl[i], name) == 0)
+                       return i;
+
+       return -1;
+}
+
+static void print_explain(FILE *f)
+{
+       fprintf(f,
                "Usage: ... bond [ mode BONDMODE ] [ active_slave SLAVE_DEV ]\n"
-               "                [ clear_active_slave ]\n"
+               "                [ clear_active_slave ] [ miimon MIIMON ]\n"
+               "                [ updelay UPDELAY ] [ downdelay DOWNDELAY ]\n"
+               "                [ use_carrier USE_CARRIER ]\n"
+               "                [ arp_interval ARP_INTERVAL ]\n"
+               "                [ arp_validate ARP_VALIDATE ]\n"
+               "                [ arp_all_targets ARP_ALL_TARGETS ]\n"
+               "                [ arp_ip_target [ ARP_IP_TARGET, ... ] ]\n"
+               "                [ primary SLAVE_DEV ]\n"
+               "                [ primary_reselect PRIMARY_RESELECT ]\n"
+               "                [ fail_over_mac FAIL_OVER_MAC ]\n"
+               "                [ xmit_hash_policy XMIT_HASH_POLICY ]\n"
+               "                [ resend_igmp RESEND_IGMP ]\n"
+               "                [ num_grat_arp|num_unsol_na NUM_GRAT_ARP|NUM_UNSOL_NA ]\n"
+               "                [ all_slaves_active ALL_SLAVES_ACTIVE ]\n"
+               "                [ min_links MIN_LINKS ]\n"
+               "                [ lp_interval LP_INTERVAL ]\n"
+               "                [ packets_per_slave PACKETS_PER_SLAVE ]\n"
+               "                [ tlb_dynamic_lb TLB_DYNAMIC_LB ]\n"
+               "                [ lacp_rate LACP_RATE ]\n"
+               "                [ ad_select AD_SELECT ]\n"
+               "                [ ad_user_port_key PORTKEY ]\n"
+               "                [ ad_actor_sys_prio SYSPRIO ]\n"
+               "                [ ad_actor_system LLADDR ]\n"
                "\n"
-               "BONDMODE := 0-6\n"
+               "BONDMODE := balance-rr|active-backup|balance-xor|broadcast|802.3ad|balance-tlb|balance-alb\n"
+               "ARP_VALIDATE := none|active|backup|all\n"
+               "ARP_ALL_TARGETS := any|all\n"
+               "PRIMARY_RESELECT := always|better|failure\n"
+               "FAIL_OVER_MAC := none|active|follow\n"
+               "XMIT_HASH_POLICY := layer2|layer2+3|layer3+4|encap2+3|encap3+4\n"
+               "LACP_RATE := slow|fast\n"
+               "AD_SELECT := stable|bandwidth|count\n"
        );
 }
 
+static void explain(void)
+{
+       print_explain(stderr);
+}
+
 static int bond_parse_opt(struct link_util *lu, int argc, char **argv,
                          struct nlmsghdr *n)
 {
-       __u8 mode;
-       unsigned ifindex;
+       __u8 mode, use_carrier, primary_reselect, fail_over_mac;
+       __u8 xmit_hash_policy, num_peer_notif, all_slaves_active;
+       __u8 lacp_rate, ad_select, tlb_dynamic_lb;
+       __u16 ad_user_port_key, ad_actor_sys_prio;
+       __u32 miimon, updelay, downdelay, arp_interval, arp_validate;
+       __u32 arp_all_targets, resend_igmp, min_links, lp_interval;
+       __u32 packets_per_slave;
+       unsigned int ifindex;
 
        while (argc > 0) {
                if (matches(*argv, "mode") == 0) {
                        NEXT_ARG();
-                       if (get_u8(&mode, *argv, 0)) {
-                               invarg("mode %s is invalid", *argv);
-                               return -1;
-                       }
+                       if (get_index(mode_tbl, *argv) < 0)
+                               invarg("invalid mode", *argv);
+                       mode = get_index(mode_tbl, *argv);
                        addattr8(n, 1024, IFLA_BOND_MODE, mode);
                } else if (matches(*argv, "active_slave") == 0) {
                        NEXT_ARG();
-                       ifindex = if_nametoindex(*argv);
+                       ifindex = ll_name_to_index(*argv);
                        if (!ifindex)
-                               return -1;
+                               return nodev(*argv);
                        addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, ifindex);
                } else if (matches(*argv, "clear_active_slave") == 0) {
                        addattr32(n, 1024, IFLA_BOND_ACTIVE_SLAVE, 0);
+               } else if (matches(*argv, "miimon") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&miimon, *argv, 0))
+                               invarg("invalid miimon", *argv);
+                       addattr32(n, 1024, IFLA_BOND_MIIMON, miimon);
+               } else if (matches(*argv, "updelay") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&updelay, *argv, 0))
+                               invarg("invalid updelay", *argv);
+                       addattr32(n, 1024, IFLA_BOND_UPDELAY, updelay);
+               } else if (matches(*argv, "downdelay") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&downdelay, *argv, 0))
+                               invarg("invalid downdelay", *argv);
+                       addattr32(n, 1024, IFLA_BOND_DOWNDELAY, downdelay);
+               } else if (matches(*argv, "use_carrier") == 0) {
+                       NEXT_ARG();
+                       if (get_u8(&use_carrier, *argv, 0))
+                               invarg("invalid use_carrier", *argv);
+                       addattr8(n, 1024, IFLA_BOND_USE_CARRIER, use_carrier);
+               } else if (matches(*argv, "arp_interval") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&arp_interval, *argv, 0))
+                               invarg("invalid arp_interval", *argv);
+                       addattr32(n, 1024, IFLA_BOND_ARP_INTERVAL, arp_interval);
+               } else if (matches(*argv, "arp_ip_target") == 0) {
+                       struct rtattr *nest = addattr_nest(n, 1024,
+                               IFLA_BOND_ARP_IP_TARGET);
+                       if (NEXT_ARG_OK()) {
+                               NEXT_ARG();
+                               char *targets = strdupa(*argv);
+                               char *target = strtok(targets, ",");
+                               int i;
+
+                               for (i = 0; target && i < BOND_MAX_ARP_TARGETS; i++) {
+                                       __u32 addr = get_addr32(target);
+
+                                       addattr32(n, 1024, i, addr);
+                                       target = strtok(NULL, ",");
+                               }
+                               addattr_nest_end(n, nest);
+                       }
+                       addattr_nest_end(n, nest);
+               } else if (matches(*argv, "arp_validate") == 0) {
+                       NEXT_ARG();
+                       if (get_index(arp_validate_tbl, *argv) < 0)
+                               invarg("invalid arp_validate", *argv);
+                       arp_validate = get_index(arp_validate_tbl, *argv);
+                       addattr32(n, 1024, IFLA_BOND_ARP_VALIDATE, arp_validate);
+               } else if (matches(*argv, "arp_all_targets") == 0) {
+                       NEXT_ARG();
+                       if (get_index(arp_all_targets_tbl, *argv) < 0)
+                               invarg("invalid arp_all_targets", *argv);
+                       arp_all_targets = get_index(arp_all_targets_tbl, *argv);
+                       addattr32(n, 1024, IFLA_BOND_ARP_ALL_TARGETS, arp_all_targets);
+               } else if (matches(*argv, "primary") == 0) {
+                       NEXT_ARG();
+                       ifindex = ll_name_to_index(*argv);
+                       if (!ifindex)
+                               return nodev(*argv);
+                       addattr32(n, 1024, IFLA_BOND_PRIMARY, ifindex);
+               } else if (matches(*argv, "primary_reselect") == 0) {
+                       NEXT_ARG();
+                       if (get_index(primary_reselect_tbl, *argv) < 0)
+                               invarg("invalid primary_reselect", *argv);
+                       primary_reselect = get_index(primary_reselect_tbl, *argv);
+                       addattr8(n, 1024, IFLA_BOND_PRIMARY_RESELECT,
+                                primary_reselect);
+               } else if (matches(*argv, "fail_over_mac") == 0) {
+                       NEXT_ARG();
+                       if (get_index(fail_over_mac_tbl, *argv) < 0)
+                               invarg("invalid fail_over_mac", *argv);
+                       fail_over_mac = get_index(fail_over_mac_tbl, *argv);
+                       addattr8(n, 1024, IFLA_BOND_FAIL_OVER_MAC,
+                                fail_over_mac);
+               } else if (matches(*argv, "xmit_hash_policy") == 0) {
+                       NEXT_ARG();
+                       if (get_index(xmit_hash_policy_tbl, *argv) < 0)
+                               invarg("invalid xmit_hash_policy", *argv);
+
+                       xmit_hash_policy = get_index(xmit_hash_policy_tbl, *argv);
+                       addattr8(n, 1024, IFLA_BOND_XMIT_HASH_POLICY,
+                                xmit_hash_policy);
+               } else if (matches(*argv, "resend_igmp") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&resend_igmp, *argv, 0))
+                               invarg("invalid resend_igmp", *argv);
+
+                       addattr32(n, 1024, IFLA_BOND_RESEND_IGMP, resend_igmp);
+               } else if (matches(*argv, "num_grat_arp") == 0 ||
+                          matches(*argv, "num_unsol_na") == 0) {
+                       NEXT_ARG();
+                       if (get_u8(&num_peer_notif, *argv, 0))
+                               invarg("invalid num_grat_arp|num_unsol_na",
+                                      *argv);
+
+                       addattr8(n, 1024, IFLA_BOND_NUM_PEER_NOTIF,
+                                num_peer_notif);
+               } else if (matches(*argv, "all_slaves_active") == 0) {
+                       NEXT_ARG();
+                       if (get_u8(&all_slaves_active, *argv, 0))
+                               invarg("invalid all_slaves_active", *argv);
+
+                       addattr8(n, 1024, IFLA_BOND_ALL_SLAVES_ACTIVE,
+                                all_slaves_active);
+               } else if (matches(*argv, "min_links") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&min_links, *argv, 0))
+                               invarg("invalid min_links", *argv);
+
+                       addattr32(n, 1024, IFLA_BOND_MIN_LINKS, min_links);
+               } else if (matches(*argv, "lp_interval") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&lp_interval, *argv, 0))
+                               invarg("invalid lp_interval", *argv);
+
+                       addattr32(n, 1024, IFLA_BOND_LP_INTERVAL, lp_interval);
+               } else if (matches(*argv, "packets_per_slave") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&packets_per_slave, *argv, 0))
+                               invarg("invalid packets_per_slave", *argv);
+
+                       addattr32(n, 1024, IFLA_BOND_PACKETS_PER_SLAVE,
+                                 packets_per_slave);
+               } else if (matches(*argv, "lacp_rate") == 0) {
+                       NEXT_ARG();
+                       if (get_index(lacp_rate_tbl, *argv) < 0)
+                               invarg("invalid lacp_rate", *argv);
+
+                       lacp_rate = get_index(lacp_rate_tbl, *argv);
+                       addattr8(n, 1024, IFLA_BOND_AD_LACP_RATE, lacp_rate);
+               } else if (matches(*argv, "ad_select") == 0) {
+                       NEXT_ARG();
+                       if (get_index(ad_select_tbl, *argv) < 0)
+                               invarg("invalid ad_select", *argv);
+
+                       ad_select = get_index(ad_select_tbl, *argv);
+                       addattr8(n, 1024, IFLA_BOND_AD_SELECT, ad_select);
+               } else if (matches(*argv, "ad_user_port_key") == 0) {
+                       NEXT_ARG();
+                       if (get_u16(&ad_user_port_key, *argv, 0))
+                               invarg("invalid ad_user_port_key", *argv);
+
+                       addattr16(n, 1024, IFLA_BOND_AD_USER_PORT_KEY,
+                                 ad_user_port_key);
+               } else if (matches(*argv, "ad_actor_sys_prio") == 0) {
+                       NEXT_ARG();
+                       if (get_u16(&ad_actor_sys_prio, *argv, 0))
+                               invarg("invalid ad_actor_sys_prio", *argv);
+
+                       addattr16(n, 1024, IFLA_BOND_AD_ACTOR_SYS_PRIO,
+                                 ad_actor_sys_prio);
+               } else if (matches(*argv, "ad_actor_system") == 0) {
+                       int len;
+                       char abuf[32];
+
+                       NEXT_ARG();
+                       len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+                       if (len < 0)
+                               return -1;
+                       addattr_l(n, 1024, IFLA_BOND_AD_ACTOR_SYSTEM,
+                                 abuf, len);
+               } else if (matches(*argv, "tlb_dynamic_lb") == 0) {
+                       NEXT_ARG();
+                       if (get_u8(&tlb_dynamic_lb, *argv, 0)) {
+                               invarg("invalid tlb_dynamic_lb", *argv);
+                               return -1;
+                       }
+                       addattr8(n, 1024, IFLA_BOND_TLB_DYNAMIC_LB,
+                                tlb_dynamic_lb);
+               } else if (matches(*argv, "help") == 0) {
+                       explain();
+                       return -1;
                } else {
                        fprintf(stderr, "bond: unknown command \"%s\"?\n", *argv);
                        explain();
@@ -64,24 +369,284 @@ static int bond_parse_opt(struct link_util *lu, int argc, char **argv,
 
 static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
-       unsigned ifindex;
-
        if (!tb)
                return;
 
-       if (tb[IFLA_BOND_MODE])
-               fprintf(f, "mode %u ", rta_getattr_u8(tb[IFLA_BOND_MODE]));
+       if (tb[IFLA_BOND_MODE]) {
+               const char *mode = get_name(mode_tbl,
+                                           rta_getattr_u8(tb[IFLA_BOND_MODE]));
+               print_string(PRINT_ANY, "mode", "mode %s ", mode);
+       }
+
+       if (tb[IFLA_BOND_ACTIVE_SLAVE]) {
+               unsigned int ifindex =
+                       rta_getattr_u32(tb[IFLA_BOND_ACTIVE_SLAVE]);
+
+               if (ifindex) {
+                       print_string(PRINT_ANY,
+                                    "active_slave",
+                                    "active_slave %s ",
+                                    ll_index_to_name(ifindex));
+               }
+       }
+
+       if (tb[IFLA_BOND_MIIMON])
+               print_uint(PRINT_ANY,
+                          "miimon",
+                          "miimon %u ",
+                          rta_getattr_u32(tb[IFLA_BOND_MIIMON]));
+
+       if (tb[IFLA_BOND_UPDELAY])
+               print_uint(PRINT_ANY,
+                          "updelay",
+                          "updelay %u ",
+                          rta_getattr_u32(tb[IFLA_BOND_UPDELAY]));
+
+       if (tb[IFLA_BOND_DOWNDELAY])
+               print_uint(PRINT_ANY,
+                          "downdelay",
+                          "downdelay %u ",
+                          rta_getattr_u32(tb[IFLA_BOND_DOWNDELAY]));
+
+       if (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])
+               print_uint(PRINT_ANY,
+                          "arp_interval",
+                          "arp_interval %u ",
+                          rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
 
-       if (tb[IFLA_BOND_ACTIVE_SLAVE] &&
-           (ifindex = rta_getattr_u32(tb[IFLA_BOND_ACTIVE_SLAVE]))) {
-               char buf[IFNAMSIZ];
-               const char *n = if_indextoname(ifindex, buf);
+       if (tb[IFLA_BOND_ARP_IP_TARGET]) {
+               struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1];
+               int i;
 
-               if (n)
-                       fprintf(f, "active_slave %s ", n);
+               parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS,
+                                   tb[IFLA_BOND_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])
+                               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]) {
+                       print_string(PRINT_FP, NULL, " ", NULL);
+                       close_json_array(PRINT_JSON, NULL);
+               }
+       }
+
+       if (tb[IFLA_BOND_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
-                       fprintf(f, "active_slave %u ", ifindex);
+                       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]));
+               print_string(PRINT_ANY,
+                            "arp_all_targets",
+                            "arp_all_targets %s ",
+                            arp_all_targets);
+       }
+
+       if (tb[IFLA_BOND_PRIMARY]) {
+               unsigned int ifindex = rta_getattr_u32(tb[IFLA_BOND_PRIMARY]);
+
+               if (ifindex) {
+                       print_string(PRINT_ANY,
+                                    "primary",
+                                    "primary %s ",
+                                    ll_index_to_name(ifindex));
+               }
+       }
+
+       if (tb[IFLA_BOND_PRIMARY_RESELECT]) {
+               const char *primary_reselect = get_name(primary_reselect_tbl,
+                                                       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]));
+               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]));
+               print_string(PRINT_ANY,
+                            "xmit_hash_policy",
+                            "xmit_hash_policy %s ",
+                            xmit_hash_policy);
+       }
+
+       if (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])
+               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])
+               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])
+               print_uint(PRINT_ANY,
+                          "min_links",
+                          "min_links %u ",
+                          rta_getattr_u32(tb[IFLA_BOND_MIN_LINKS]));
+
+       if (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])
+               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]));
+               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]));
+               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]);
+
+               open_json_object("ad_info");
+
+               if (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])
+                       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])
+                       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])
+                       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);
+                       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]) {
+               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]) {
+               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);
+
+               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]) {
+               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)
+{
+       print_explain(f);
 }
 
 struct link_util bond_link_util = {
@@ -89,4 +654,5 @@ struct link_util bond_link_util = {
        .maxattr        = IFLA_BOND_MAX,
        .parse_opt      = bond_parse_opt,
        .print_opt      = bond_print_opt,
+       .print_help     = bond_print_help,
 };