about summary refs log tree commit diff
path: root/src/libnm-platform/nm-linux-platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-platform/nm-linux-platform.c')
-rw-r--r--src/libnm-platform/nm-linux-platform.c131
1 files changed, 116 insertions, 15 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c
index cc5b99e0..b5df8b17 100644
--- a/src/libnm-platform/nm-linux-platform.c
+++ b/src/libnm-platform/nm-linux-platform.c
@@ -862,6 +862,7 @@ static const LinkDesc link_descs[] = {
 
     [NM_LINK_TYPE_BNEP]        = {"bluetooth", NULL, "bluetooth"},
     [NM_LINK_TYPE_DUMMY]       = {"dummy", "dummy", NULL},
+    [NM_LINK_TYPE_GENEVE]      = {"geneve", "geneve", "geneve"},
     [NM_LINK_TYPE_GRE]         = {"gre", "gre", NULL},
     [NM_LINK_TYPE_GRETAP]      = {"gretap", "gretap", NULL},
     [NM_LINK_TYPE_IFB]         = {"ifb", "ifb", NULL},
@@ -909,6 +910,7 @@ _link_type_from_rtnl_type(const char *name)
         NM_LINK_TYPE_BOND,        /* "bond"        */
         NM_LINK_TYPE_BRIDGE,      /* "bridge"      */
         NM_LINK_TYPE_DUMMY,       /* "dummy"       */
+        NM_LINK_TYPE_GENEVE,      /* "geneve"      */
         NM_LINK_TYPE_GRE,         /* "gre"         */
         NM_LINK_TYPE_GRETAP,      /* "gretap"      */
         NM_LINK_TYPE_HSR,         /* "hsr"         */
@@ -987,6 +989,7 @@ _link_type_from_devtype(const char *name)
         NM_LINK_TYPE_BNEP,      /* "bluetooth" */
         NM_LINK_TYPE_BOND,      /* "bond"      */
         NM_LINK_TYPE_BRIDGE,    /* "bridge"    */
+        NM_LINK_TYPE_GENEVE,    /* "geneve"    */
         NM_LINK_TYPE_HSR,       /* "hsr"       */
         NM_LINK_TYPE_PPP,       /* "ppp"       */
         NM_LINK_TYPE_VLAN,      /* "vlan"      */
@@ -1857,6 +1860,57 @@ _parse_lnk_gre(const char *kind, struct nlattr *info_data)
 /*****************************************************************************/
 
 static NMPObject *
+_parse_lnk_geneve(const char *kind, struct nlattr *info_data)
+{
+    static const struct nla_policy policy[] = {
+        [IFLA_GENEVE_ID]          = {.type = NLA_U32},
+        [IFLA_GENEVE_REMOTE]      = {.type = NLA_U32},
+        [IFLA_GENEVE_REMOTE6]     = {.type = NLA_UNSPEC, .minlen = sizeof(struct in6_addr)},
+        [IFLA_GENEVE_TTL]         = {.type = NLA_U8},
+        [IFLA_GENEVE_TOS]         = {.type = NLA_U8},
+        [IFLA_GENEVE_TTL_INHERIT] = {.type = NLA_U8},
+        [IFLA_GENEVE_PORT]        = {.type = NLA_U16},
+        [IFLA_GENEVE_DF]          = {.type = NLA_U8},
+    };
+
+    struct nlattr       *tb[G_N_ELEMENTS(policy)];
+    NMPObject           *obj;
+    NMPlatformLnkGeneve *props;
+
+    if (!info_data || !nm_streq0(kind, "geneve"))
+        return NULL;
+
+    if (nla_parse_nested_arr(tb, info_data, policy) < 0)
+        return NULL;
+
+    obj   = nmp_object_new(NMP_OBJECT_TYPE_LNK_GENEVE, NULL);
+    props = &obj->lnk_geneve;
+
+    if (tb[IFLA_GENEVE_ID])
+        props->id = nla_get_u32(tb[IFLA_GENEVE_ID]);
+    if (tb[IFLA_GENEVE_REMOTE])
+        props->remote = nla_get_u32(tb[IFLA_GENEVE_REMOTE]);
+    if (tb[IFLA_GENEVE_REMOTE6])
+        props->remote6 = *nla_data_as(struct in6_addr, tb[IFLA_GENEVE_REMOTE6]);
+
+    if (tb[IFLA_GENEVE_TTL_INHERIT] && nla_get_u8(tb[IFLA_GENEVE_TTL_INHERIT]))
+        props->ttl = -1;
+    else if (tb[IFLA_GENEVE_TTL])
+        props->ttl = nla_get_u8(tb[IFLA_GENEVE_TTL]);
+
+    if (tb[IFLA_GENEVE_TOS])
+        props->tos = nla_get_u8(tb[IFLA_GENEVE_TOS]);
+    if (tb[IFLA_GENEVE_PORT])
+        props->dst_port = ntohs(nla_get_u16(tb[IFLA_GENEVE_PORT]));
+    if (tb[IFLA_GENEVE_DF])
+        props->df = nla_get_u8(tb[IFLA_GENEVE_DF]);
+
+    return obj;
+}
+
+/*****************************************************************************/
+
+static NMPObject *
 _parse_lnk_hsr(const char *kind, struct nlattr *info_data)
 {
     static const struct nla_policy policy[] = {
@@ -3694,6 +3748,9 @@ _new_from_nl_link(NMPlatform            *platform,
     case NM_LINK_TYPE_BOND:
         lnk_data = _parse_lnk_bond(nl_info_kind, nl_info_data);
         break;
+    case NM_LINK_TYPE_GENEVE:
+        lnk_data = _parse_lnk_geneve(nl_info_kind, nl_info_data);
+        break;
     case NM_LINK_TYPE_GRE:
     case NM_LINK_TYPE_GRETAP:
         lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data);
@@ -4033,6 +4090,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
         int      ifindex;
         NMIPAddr gateway;
         gboolean is_via;
+        unsigned rtnh_flags;
     } nh = {
         .found    = FALSE,
         .has_more = FALSE,
@@ -4142,9 +4200,10 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
                     v4_nh_extra_nexthops = v4_nh_extra_nexthops_heap;
                 }
                 nm_assert(v4_n_nexthops - 1u < v4_nh_extra_alloc);
-                new_nexthop          = &v4_nh_extra_nexthops[v4_n_nexthops - 1u];
-                new_nexthop->ifindex = rtnh->rtnh_ifindex;
-                new_nexthop->weight  = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
+                new_nexthop             = &v4_nh_extra_nexthops[v4_n_nexthops - 1u];
+                new_nexthop->ifindex    = rtnh->rtnh_ifindex;
+                new_nexthop->weight     = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
+                new_nexthop->rtnh_flags = rtnh->rtnh_flags;
                 if (rtnh->rtnh_len > sizeof(*rtnh)) {
                     struct nlattr *ntb[RTA_MAX + 1];
 
@@ -4159,9 +4218,10 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
                         memcpy(&new_nexthop->gateway, nla_data(ntb[RTA_GATEWAY]), addr_len);
                 }
             } else if (IS_IPv4 || idx == multihop_idx) {
-                nh.found   = TRUE;
-                nh.ifindex = rtnh->rtnh_ifindex;
-                nh.weight  = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
+                nh.found      = TRUE;
+                nh.ifindex    = rtnh->rtnh_ifindex;
+                nh.weight     = NM_MAX(((guint) rtnh->rtnh_hops) + 1u, 1u);
+                nh.rtnh_flags = rtnh->rtnh_flags;
                 if (rtnh->rtnh_len > sizeof(*rtnh)) {
                     struct nlattr *ntb[RTA_MAX + 1];
 
@@ -4409,7 +4469,16 @@ rta_multipath_done:
     }
 
     obj->ip_route.r_rtm_flags = rtm->rtm_flags;
-    obj->ip_route.rt_source   = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol);
+
+    if (IS_IPv4 && v4_n_nexthops > 1u) {
+        /* For multipath routes, rtm_flags at the route level does not contain
+         * RTNH_F_ONLINK. Instead, each nexthop has its own rtnh_flags. Merge the
+         * first nexthop's RTNH_F_ONLINK into r_rtm_flags, since the first nexthop
+         * is embedded in the route struct. */
+        obj->ip_route.r_rtm_flags |= (nh.rtnh_flags & RTNH_F_ONLINK);
+    }
+
+    obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot(rtm->rtm_protocol);
 
     if (nh.has_more) {
         parse_nlmsg_iter->iter_more               = TRUE;
@@ -5182,6 +5251,35 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo
         nla_nest_end(msg, info_peer);
         break;
     }
+    case NM_LINK_TYPE_GENEVE:
+    {
+        const NMPlatformLnkGeneve *props = extra_data;
+
+        nm_assert(props);
+
+        if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+            goto nla_put_failure;
+
+        NLA_PUT_U32(msg, IFLA_GENEVE_ID, props->id);
+
+        if (props->remote) {
+            NLA_PUT_U32(msg, IFLA_GENEVE_REMOTE, props->remote);
+        } else if (!IN6_IS_ADDR_UNSPECIFIED(&props->remote6)) {
+            NLA_PUT(msg, IFLA_GENEVE_REMOTE6, sizeof(props->remote6), &props->remote6);
+        }
+        NLA_PUT_U16(msg, IFLA_GENEVE_PORT, htons(props->dst_port));
+        if (props->ttl == -1) {
+            NLA_PUT_U8(msg, IFLA_GENEVE_TTL_INHERIT, 1);
+        } else {
+            /* When you want to specify a TTL value,
+             * don't add TTL_INHERIT to the message */
+            NLA_PUT_U8(msg, IFLA_GENEVE_TTL, props->ttl & 0xff);
+        }
+        NLA_PUT_U8(msg, IFLA_GENEVE_TOS, props->tos);
+        NLA_PUT_U8(msg, IFLA_GENEVE_DF, props->df);
+        break;
+    }
+
     case NM_LINK_TYPE_GRE:
     case NM_LINK_TYPE_GRETAP:
     {
@@ -5813,15 +5911,18 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob
             }
             NLA_PUT_U32(msg, RTA_GATEWAY, gw);
 
-            rtnh->rtnh_flags = 0;
+            if (i == 0u) {
+                rtnh->rtnh_flags =
+                    (gw != 0 && NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) RTNH_F_ONLINK))
+                        ? RTNH_F_ONLINK
+                        : 0;
+            } else {
+                const NMPlatformIP4RtNextHop *n2 = &obj->_ip4_route.extra_nexthops[i - 1u];
 
-            if (obj->ip4_route.n_nexthops > 1
-                && NM_FLAGS_HAS(obj->ip_route.r_rtm_flags, (unsigned) (RTNH_F_ONLINK)) && gw != 0) {
-                /* Unlike kernel, we only track the onlink flag per NMPlatformIP4Address, and
-                 * not per nexthop. That is fine for NetworkManager configuring addresses.
-                 * It is not fine for tracking addresses from kernel in platform cache,
-                 * because the rtnh_flags of the nexthops need to be part of nmp_object_id_cmp(). */
-                rtnh->rtnh_flags |= RTNH_F_ONLINK;
+                rtnh->rtnh_flags =
+                    (gw != 0 && NM_FLAGS_HAS(n2->rtnh_flags, (unsigned) RTNH_F_ONLINK))
+                        ? RTNH_F_ONLINK
+                        : 0;
             }
 
             rtnh->rtnh_len = (char *) nlmsg_tail(nlmsg_hdr(msg)) - (char *) rtnh;