about summary refs log tree commit diff
path: root/src/libnm-platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-platform')
-rw-r--r--src/libnm-platform/nm-linux-platform.c106
-rw-r--r--src/libnm-platform/nm-platform.c121
-rw-r--r--src/libnm-platform/nm-platform.h44
3 files changed, 256 insertions, 15 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index 049095b2..484087b9 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -177,6 +177,8 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1));
 
 /*****************************************************************************/
 
+#define IFLA_BOND_SLAVE_PRIO 9
+
 #define IFLA_BOND_PEER_NOTIF_DELAY 28
 
 #undef IFLA_BOND_MAX
@@ -3347,9 +3349,11 @@ _new_from_nl_link(NMPlatform            *platform,
 
     if (tb[IFLA_LINKINFO]) {
         static const struct nla_policy policy_link_info[] = {
-            [IFLA_INFO_KIND]   = {.type = NLA_STRING},
-            [IFLA_INFO_DATA]   = {.type = NLA_NESTED},
-            [IFLA_INFO_XSTATS] = {.type = NLA_NESTED},
+            [IFLA_INFO_KIND]       = {.type = NLA_STRING},
+            [IFLA_INFO_DATA]       = {.type = NLA_NESTED},
+            [IFLA_INFO_XSTATS]     = {.type = NLA_NESTED},
+            [IFLA_INFO_SLAVE_KIND] = {.type = NLA_STRING},
+            [IFLA_INFO_SLAVE_DATA] = {.type = NLA_NESTED},
         };
         struct nlattr *li[G_N_ELEMENTS(policy_link_info)];
 
@@ -3360,6 +3364,49 @@ _new_from_nl_link(NMPlatform            *platform,
             nl_info_kind = nla_get_string(li[IFLA_INFO_KIND]);
 
         nl_info_data = li[IFLA_INFO_DATA];
+
+        if (li[IFLA_INFO_SLAVE_KIND]) {
+            const char *s = nla_get_string(li[IFLA_INFO_SLAVE_KIND]);
+
+            if (nm_streq(s, "bond"))
+                obj->link.port_kind = NM_PORT_KIND_BOND;
+        }
+
+        if (li[IFLA_INFO_SLAVE_DATA]) {
+            static const struct nla_policy policy_bond_port[] = {
+                [IFLA_BOND_SLAVE_QUEUE_ID] = {.type = NLA_U16},
+                [IFLA_BOND_SLAVE_PRIO]     = {.type = NLA_S32},
+            };
+            struct nlattr *bp[G_N_ELEMENTS(policy_bond_port)];
+
+            switch (obj->link.port_kind) {
+            case NM_PORT_KIND_BOND:
+                if (nla_parse_nested_arr(bp, li[IFLA_INFO_SLAVE_DATA], policy_bond_port) < 0)
+                    return NULL;
+
+                if (bp[IFLA_BOND_SLAVE_QUEUE_ID])
+                    obj->link.port_data.bond.queue_id = nla_get_u16(bp[IFLA_BOND_SLAVE_QUEUE_ID]);
+
+                if (bp[IFLA_BOND_SLAVE_PRIO]) {
+                    obj->link.port_data.bond.prio     = nla_get_s32(bp[IFLA_BOND_SLAVE_PRIO]);
+                    obj->link.port_data.bond.prio_has = TRUE;
+                    if (!_nm_platform_kernel_support_detected(
+                            NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO)) {
+                        /* support for IFLA_BOND_SLAVE_PRIO was added in 0a2ff7cc8ad48a86939a91bd3457f38e59e741a1,
+                         * kernel 6.0, 2 October 2022.
+                         *
+                         * We can only detect support if the attribute is present. A missing attribute
+                         * is not conclusive. */
+                        _nm_platform_kernel_support_init(
+                            NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO,
+                            1);
+                    }
+                }
+                break;
+            case NM_PORT_KIND_NONE:
+                break;
+            }
+        }
     }
 
     if (tb[IFLA_STATS64]) {
@@ -8277,7 +8324,7 @@ out:
 }
 
 static int
-link_change(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data)
+link_change_extra(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data)
 {
     nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 
@@ -8355,6 +8402,51 @@ link_delete(NMPlatform *platform, int ifindex)
 }
 
 static gboolean
+link_change(NMPlatform                   *platform,
+            int                           ifindex,
+            NMPortKind                    port_kind,
+            const NMPlatformLinkPortData *port_data)
+{
+    nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+    struct nlattr               *nl_info;
+    struct nlattr               *nl_port_data;
+
+    nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, NULL);
+    if (!nlmsg)
+        return FALSE;
+
+    switch (port_kind) {
+    case NM_PORT_KIND_BOND:
+
+        nm_assert(port_data);
+
+        if (!(nl_info = nla_nest_start(nlmsg, IFLA_LINKINFO)))
+            goto nla_put_failure;
+
+        nm_assert(nm_streq0("bond", nm_link_type_to_rtnl_type_string(NM_LINK_TYPE_BOND)));
+        NLA_PUT_STRING(nlmsg, IFLA_INFO_SLAVE_KIND, "bond");
+
+        if (!(nl_port_data = nla_nest_start(nlmsg, IFLA_INFO_SLAVE_DATA)))
+            goto nla_put_failure;
+
+        NLA_PUT_U16(nlmsg, IFLA_BOND_SLAVE_QUEUE_ID, port_data->bond.queue_id);
+
+        if (port_data->bond.prio_has)
+            NLA_PUT_S32(nlmsg, IFLA_BOND_SLAVE_PRIO, port_data->bond.prio);
+
+        nla_nest_end(nlmsg, nl_port_data);
+        nla_nest_end(nlmsg, nl_info);
+        break;
+    case NM_PORT_KIND_NONE:
+        break;
+    }
+
+    return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == 0;
+nla_put_failure:
+    g_return_val_if_reached(FALSE);
+}
+
+static gboolean
 link_refresh(NMPlatform *platform, int ifindex)
 {
     do_request_link(platform, ifindex, NULL);
@@ -11121,9 +11213,11 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
     platform_class->sysctl_set_async = sysctl_set_async;
     platform_class->sysctl_get       = sysctl_get;
 
-    platform_class->link_add    = link_add;
+    platform_class->link_add          = link_add;
+    platform_class->link_change_extra = link_change_extra;
+    platform_class->link_delete       = link_delete;
+
     platform_class->link_change = link_change;
-    platform_class->link_delete = link_delete;
 
     platform_class->link_refresh = link_refresh;
 
diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c
index dc643ed2..1c495d60 100644
--- a/src/libnm-platform/nm-platform.c
+++ b/src/libnm-platform/nm-platform.c
@@ -62,6 +62,41 @@ G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_perm_address.data) == _NM_UT
 G_STATIC_ASSERT(sizeof(((NMPlatformLink *) NULL)->l_broadcast.data) == _NM_UTILS_HWADDR_LEN_MAX);
 
 static const char *
+_nmp_link_port_data_to_string(NMPortKind                    port_kind,
+                              const NMPlatformLinkPortData *port_data,
+                              char                         *sbuf,
+                              gsize                         sbuf_len)
+{
+    const char *sbuf0 = sbuf;
+    char        s0[120];
+
+    nm_assert(port_data);
+
+    switch (port_kind) {
+    case NM_PORT_KIND_NONE:
+        nm_strbuf_append_c(&sbuf, &sbuf_len, '\0');
+        goto out;
+    case NM_PORT_KIND_BOND:
+        nm_strbuf_append(&sbuf,
+                         &sbuf_len,
+                         "port bond queue-id %u%s",
+                         port_data->bond.queue_id,
+                         port_data->bond.prio_has || port_data->bond.prio != 0
+                             ? nm_sprintf_buf(s0,
+                                              " prio%s %u",
+                                              port_data->bond.prio_has ? "" : "?",
+                                              port_data->bond.prio)
+                             : "");
+        goto out;
+    }
+
+    nm_strbuf_append(&sbuf, &sbuf_len, "invalid-port-type %d", (int) port_kind);
+
+out:
+    return sbuf0;
+}
+
+static const char *
 _nmp_link_address_to_string(const NMPLinkAddress *addr,
                             char                  buf[static(_NM_UTILS_HWADDR_LEN_MAX * 3)])
 {
@@ -1406,7 +1441,10 @@ nm_platform_link_add(NMPlatform            *self,
 }
 
 int
-nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data)
+nm_platform_link_change_extra(NMPlatform   *self,
+                              NMLinkType    type,
+                              int           ifindex,
+                              gconstpointer extra_data)
 {
     char        buf[512];
     const char *name = nm_platform_link_get_name(self, ifindex);
@@ -1447,7 +1485,7 @@ nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpo
                buf;
            }));
 
-    return klass->link_change(self, type, ifindex, extra_data);
+    return klass->link_change_extra(self, type, ifindex, extra_data);
 }
 
 /**
@@ -2107,6 +2145,43 @@ nm_platform_link_set_name(NMPlatform *self, int ifindex, const char *name)
     return klass->link_set_name(self, ifindex, name);
 }
 
+gboolean
+nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port)
+{
+    char sbuf_prio[100];
+
+    _CHECK_SELF(self, klass, FALSE);
+
+    g_return_val_if_fail(ifindex >= 0, FALSE);
+
+    if (_LOGD_ENABLED()) {
+        nm_auto_free_gstring GString *str = g_string_new("");
+
+        if (bond_port) {
+            nm_assert(bond_port->prio_has || bond_port->prio == 0);
+            g_string_append_printf(str,
+                                   "bond-port queue-id %d %s",
+                                   bond_port->queue_id,
+                                   bond_port->prio_has || bond_port->prio != 0
+                                       ? nm_sprintf_buf(sbuf_prio,
+                                                        "prio%s %" G_GINT32_FORMAT,
+                                                        !bond_port->prio_has ? "?" : "",
+                                                        bond_port->prio)
+                                       : "");
+        }
+
+        if (str->len > 0 && str->str[str->len - 1] == ' ')
+            g_string_truncate(str, str->len - 1);
+
+        _LOG3D("link: change: %s", str->str);
+    }
+
+    return klass->link_change(self,
+                              ifindex,
+                              bond_port ? NM_PORT_KIND_BOND : NM_PORT_KIND_NONE,
+                              (const NMPlatformLinkPortData *) bond_port);
+}
+
 /**
  * nm_platform_link_get_physical_port_id:
  * @self: platform instance
@@ -5893,6 +5968,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
     char       *s;
     gsize       l;
     char        str_addrmode[30];
+    char        str_port_data[200];
     char        str_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
     char        str_perm_address[_NM_UTILS_HWADDR_LEN_MAX * 3];
     char        str_broadcast[_NM_UTILS_HWADDR_LEN_MAX * 3];
@@ -5936,6 +6012,11 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
     _nmp_link_address_to_string(&link->l_perm_address, str_perm_address);
     _nmp_link_address_to_string(&link->l_broadcast, str_broadcast);
 
+    _nmp_link_port_data_to_string(link->port_kind,
+                                  &link->port_data,
+                                  str_port_data,
+                                  sizeof(str_port_data));
+
     str_link_type = nm_link_type_to_string(link->type);
 
     g_snprintf(
@@ -5957,6 +6038,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
         "%s%s"    /* l_broadcast */
         "%s%s"    /* inet6_token */
         "%s%s"    /* driver */
+        "%s%s"    /* port_data */
         " rx:%" G_GUINT64_FORMAT ",%" G_GUINT64_FORMAT " tx:%" G_GUINT64_FORMAT
         ",%" G_GUINT64_FORMAT,
         link->ifindex,
@@ -5989,6 +6071,7 @@ nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len)
             : "",
         link->driver ? " driver " : "",
         link->driver ?: "",
+        NM_PRINT_FMT_QUOTED2(str_port_data[0] != '\0', " ", str_port_data, ""),
         link->rx_packets,
         link->rx_bytes,
         link->tx_packets,
@@ -7821,6 +7904,7 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
                         obj->arptype,
                         obj->inet6_addr_gen_mode_inv,
                         obj->inet6_token,
+                        obj->port_kind,
                         obj->rx_packets,
                         obj->rx_bytes,
                         obj->tx_packets,
@@ -7839,6 +7923,20 @@ nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h)
     nm_hash_update_mem(h,
                        obj->l_broadcast.data,
                        NM_MIN(obj->l_broadcast.len, sizeof(obj->l_broadcast.data)));
+
+    switch (obj->port_kind) {
+    case NM_PORT_KIND_NONE:
+        break;
+    case NM_PORT_KIND_BOND:
+        nm_platform_link_bond_port_hash_update(&obj->port_data.bond, h);
+        break;
+    }
+}
+
+void
+nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h)
+{
+    nm_hash_update_vals(h, obj->prio, obj->queue_id, NM_HASH_COMBINE_BOOLS(guint8, obj->prio_has));
 }
 
 int
@@ -7868,6 +7966,14 @@ nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b)
     if (a->l_broadcast.len)
         NM_CMP_FIELD_MEMCMP_LEN(a, b, l_broadcast.data, a->l_broadcast.len);
     NM_CMP_FIELD_MEMCMP(a, b, inet6_token);
+    NM_CMP_FIELD(a, b, port_kind);
+    switch (a->port_kind) {
+    case NM_PORT_KIND_NONE:
+        break;
+    case NM_PORT_KIND_BOND:
+        NM_CMP_RETURN(nm_platform_link_bond_port_cmp(&a->port_data.bond, &b->port_data.bond));
+        break;
+    }
     NM_CMP_FIELD(a, b, rx_packets);
     NM_CMP_FIELD(a, b, rx_bytes);
     NM_CMP_FIELD(a, b, tx_packets);
@@ -7948,6 +8054,17 @@ nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h)
 }
 
 int
+nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a, const NMPlatformLinkBondPort *b)
+{
+    NM_CMP_SELF(a, b);
+    NM_CMP_FIELD(a, b, queue_id);
+    NM_CMP_FIELD(a, b, prio);
+    NM_CMP_FIELD_BOOL(a, b, prio_has);
+
+    return 0;
+}
+
+int
 nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b)
 {
     NM_CMP_SELF(a, b);
diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h
index ca6cdff8..d442ad9b 100644
--- a/src/libnm-platform/nm-platform.h
+++ b/src/libnm-platform/nm-platform.h
@@ -154,6 +154,16 @@ struct _NMPlatformObjWithIfindex {
     __NMPlatformObjWithIfindex_COMMON;
 } _nm_alignas(NMPlatformObject);
 
+typedef struct {
+    gint32  prio;
+    guint16 queue_id;
+    bool    prio_has : 1;
+} NMPlatformLinkBondPort;
+
+typedef union {
+    NMPlatformLinkBondPort bond;
+} NMPlatformLinkPortData;
+
 struct _NMPlatformLink {
     __NMPlatformObjWithIfindex_COMMON;
     char       name[NMP_IFNAMSIZ];
@@ -204,6 +214,12 @@ struct _NMPlatformLink {
     guint64 tx_packets;
     guint64 tx_bytes;
 
+    /* an interface can only hold IFLA_INFO_SLAVE_DATA for one link type */
+    NMPlatformLinkPortData port_data;
+
+    /* IFLA_INFO_SLAVE_KIND */
+    NMPortKind port_kind;
+
     /* @connected is mostly identical to (@n_ifi_flags & IFF_UP). Except for bridge/bond masters,
      * where we coerce the link as disconnect if it has no slaves. */
     bool connected : 1;
@@ -990,6 +1006,8 @@ typedef enum {
      * were added at the same time. */
     NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_IP_PROTO,
 
+    NM_PLATFORM_KERNEL_SUPPORT_TYPE_IFLA_BOND_SLAVE_PRIO,
+
     _NM_PLATFORM_KERNEL_SUPPORT_NUM,
 } NMPlatformKernelSupportType;
 
@@ -1086,9 +1104,14 @@ typedef struct {
                     guint32                mtu,
                     gconstpointer          extra_data,
                     const NMPlatformLink **out_link);
-
-    int (*link_change)(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data);
-
+    int (*link_change_extra)(NMPlatform   *self,
+                             NMLinkType    type,
+                             int           ifindex,
+                             gconstpointer extra_data);
+    gboolean (*link_change)(NMPlatform                   *self,
+                            int                           ifindex,
+                            NMPortKind                    port_kind,
+                            const NMPlatformLinkPortData *port_data);
     gboolean (*link_delete)(NMPlatform *self, int ifindex);
     gboolean (*link_refresh)(NMPlatform *self, int ifindex);
     gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd);
@@ -1590,8 +1613,10 @@ int nm_platform_link_add(NMPlatform            *self,
                          gconstpointer          extra_data,
                          const NMPlatformLink **out_link);
 
-int
-nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data);
+int nm_platform_link_change_extra(NMPlatform   *self,
+                                  NMLinkType    type,
+                                  int           ifindex,
+                                  gconstpointer extra_data);
 
 static inline int
 nm_platform_link_veth_add(NMPlatform            *self,
@@ -1631,13 +1656,13 @@ nm_platform_link_bridge_add(NMPlatform                *self,
 static inline int
 nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLnkBridge *props)
 {
-    return nm_platform_link_change(self, NM_LINK_TYPE_BRIDGE, ifindex, props);
+    return nm_platform_link_change_extra(self, NM_LINK_TYPE_BRIDGE, ifindex, props);
 }
 
 static inline int
 nm_platform_link_bond_change(NMPlatform *self, int ifindex, const NMPlatformLnkBond *props)
 {
-    return nm_platform_link_change(self, NM_LINK_TYPE_BOND, ifindex, props);
+    return nm_platform_link_change_extra(self, NM_LINK_TYPE_BOND, ifindex, props);
 }
 
 static inline int
@@ -1914,6 +1939,8 @@ nm_platform_link_change_flags(NMPlatform *self, int ifindex, unsigned value, gbo
     return nm_platform_link_change_flags_full(self, ifindex, value, set ? value : 0u);
 }
 
+gboolean nm_platform_link_change(NMPlatform *self, int ifindex, NMPlatformLinkBondPort *bond_port);
+
 gboolean    nm_platform_link_get_udev_property(NMPlatform  *self,
                                                int          ifindex,
                                                const char  *name,
@@ -2391,6 +2418,9 @@ int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter
 int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b);
 
 void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h);
+void nm_platform_link_bond_port_hash_update(const NMPlatformLinkBondPort *obj, NMHashState *h);
+int  nm_platform_link_bond_port_cmp(const NMPlatformLinkBondPort *a,
+                                    const NMPlatformLinkBondPort *b);
 void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
                                        NMPlatformIPRouteCmpType  cmp_type,
                                        NMHashState              *h);