]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduce ip nexthop routing
authorSusant Sahani <ssahani@vmware.com>
Fri, 4 Oct 2019 19:40:51 +0000 (21:40 +0200)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 14 Oct 2019 12:32:48 +0000 (21:32 +0900)
Used to manipulate entries in the kernel's nexthop tables.
Example:
```
[NextHop]
Id=3
Gateway=192.168.5.1
```

21 files changed:
man/systemd.network.xml
src/basic/linux/nexthop.h [new file with mode: 0644]
src/basic/linux/rtnetlink.h
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-netlink/rtnl-message.c
src/libsystemd/sd-netlink/sd-netlink.c
src/network/meson.build
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-nexthop.c [new file with mode: 0644]
src/network/networkd-nexthop.h [new file with mode: 0644]
src/network/networkd.c
src/systemd/sd-netlink.h
test/fuzz/fuzz-network-parser/directives.network

index 657ba662455e16e35a4c3b2bb8e2e0db78f46bdd..a9a9b13d42e12a09a7c62fafb5a25600fa8fa4a5 100644 (file)
           </listitem>
         </varlistentry>
       </variablelist>
+    </refsect1>
+
+    <refsect1>
+      <title>[NextHop] Section Options</title>
+      <para>The <literal>[NextHop]</literal> section accepts the
+      following keys. Specify several <literal>[NextHop]</literal>
+      sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop
+      tables.</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>Gateway=</varname></term>
+          <listitem>
+            <para>As in the <literal>[Network]</literal> section. This is mandatory.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>Id=</varname></term>
+          <listitem>
+            <para>The id of the nexthop (an unsigned integer). If unspecified or '0' then automatically chosen by kernel.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
   </refsect1>
 
   <refsect1>
diff --git a/src/basic/linux/nexthop.h b/src/basic/linux/nexthop.h
new file mode 100644 (file)
index 0000000..d51b6e1
--- /dev/null
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_NEXTHOP_H
+#define _LINUX_NEXTHOP_H
+
+#include <linux/types.h>
+
+struct nhmsg {
+        unsigned char   nh_family;
+        unsigned char   nh_scope;     /* return only */
+        unsigned char   nh_protocol;  /* Routing protocol that installed nh */
+        unsigned char   resvd;
+        unsigned int    nh_flags;     /* RTNH_F flags */
+};
+
+/* entry in a nexthop group */
+struct nexthop_grp {
+        __u32   id;       /* nexthop id - must exist */
+        __u8    weight;   /* weight of this nexthop */
+        __u8    resvd1;
+        __u16   resvd2;
+};
+
+enum {
+      NEXTHOP_GRP_TYPE_MPATH,  /* default type if not specified */
+      __NEXTHOP_GRP_TYPE_MAX,
+};
+
+#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
+
+enum {
+      NHA_UNSPEC,
+      NHA_ID,           /* u32; id for nexthop. id == 0 means auto-assign */
+
+      NHA_GROUP,        /* array of nexthop_grp */
+      NHA_GROUP_TYPE,   /* u16 one of NEXTHOP_GRP_TYPE */
+      /* if NHA_GROUP attribute is added, no other attributes can be set */
+
+      NHA_BLACKHOLE,    /* flag; nexthop used to blackhole packets */
+      /* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */
+
+      NHA_OIF,  /* u32; nexthop device */
+      NHA_GATEWAY,      /* be32 (IPv4) or in6_addr (IPv6) gw address */
+      NHA_ENCAP_TYPE, /* u16; lwt encap type */
+      NHA_ENCAP,        /* lwt encap data */
+
+      /* NHA_OIF can be appended to dump request to return only
+       * nexthops using given device
+       */
+      NHA_GROUPS,       /* flag; only return nexthop groups in dump */
+      NHA_MASTER,       /* u32;  only return nexthops with given master dev */
+
+      __NHA_MAX,
+};
+
+#define NHA_MAX (__NHA_MAX - 1)
+#endif
index 46399367627fcefb0abdf145e374dd82f4de731a..80ad27fcc0c53360acb40d56e8b5b6aa1baac6c4 100644 (file)
@@ -157,6 +157,13 @@ enum {
        RTM_GETCHAIN,
 #define RTM_GETCHAIN RTM_GETCHAIN
 
+        RTM_NEWNEXTHOP = 104,
+#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP
+        RTM_DELNEXTHOP,
+#define RTM_DELNEXTHOP RTM_DELNEXTHOP
+        RTM_GETNEXTHOP,
+#define RTM_GETNEXTHOP RTM_GETNEXTHOP
+
        __RTM_MAX,
 #define RTM_MAX                (((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -165,7 +172,7 @@ enum {
 #define RTM_NR_FAMILIES        (RTM_NR_MSGTYPES >> 2)
 #define RTM_FAM(cmd)   (((cmd) - RTM_BASE) >> 2)
 
-/* 
+/*
    Generic structure for encapsulation of optional route information.
    It is reminiscent of sockaddr, but with sa_family replaced
    with attribute type.
@@ -205,7 +212,7 @@ struct rtmsg {
 
        unsigned char           rtm_table;      /* Routing table id */
        unsigned char           rtm_protocol;   /* Routing protocol; see below  */
-       unsigned char           rtm_scope;      /* See below */ 
+        unsigned char          rtm_scope;      /* See below */
        unsigned char           rtm_type;       /* See below    */
 
        unsigned                rtm_flags;
@@ -342,6 +349,7 @@ enum rtattr_type_t {
        RTA_IP_PROTO,
        RTA_SPORT,
        RTA_DPORT,
+        RTA_NH_ID,
        __RTA_MAX
 };
 
@@ -515,7 +523,7 @@ struct ifinfomsg {
 };
 
 /********************************************************************
- *             prefix information 
+ *             prefix information
  ****/
 
 struct prefixmsg {
@@ -529,7 +537,7 @@ struct prefixmsg {
        unsigned char   prefix_pad3;
 };
 
-enum 
+enum
 {
        PREFIX_UNSPEC,
        PREFIX_ADDRESS,
@@ -704,6 +712,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_IPV4_MROUTE_R  RTNLGRP_IPV4_MROUTE_R
        RTNLGRP_IPV6_MROUTE_R,
 #define RTNLGRP_IPV6_MROUTE_R  RTNLGRP_IPV6_MROUTE_R
+        RTNLGRP_NEXTHOP,
+#define RTNLGRP_NEXTHOP                RTNLGRP_NEXTHOP
        __RTNLGRP_MAX
 };
 #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
index 16964712c91621ac2b8571376e06df2bf9a77f41..58ec1efdd43a5a5c61eaaae0ba0bcefc58c76cef 100644 (file)
@@ -88,7 +88,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
 
-        assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL);
+        assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
+                             RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
 
         SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);
 
index de9b8b21ab5983e924aef391074a76f49bf9a8d0..6db0ffd13fab88bf5621cb6fab76583dfae9724c 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/if_link.h>
 #include <linux/if_macsec.h>
 #include <linux/if_tunnel.h>
+#include <linux/nexthop.h>
 #include <linux/l2tp.h>
 #include <linux/veth.h>
 #include <linux/wireguard.h>
@@ -716,6 +717,17 @@ static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
         .types = rtnl_routing_policy_rule_types,
 };
 
+static const NLType rtnl_nexthop_types[] = {
+        [NHA_ID]                  = { .type = NETLINK_TYPE_U32 },
+        [NHA_OIF]                 = { .type = NETLINK_TYPE_U32 },
+        [NHA_GATEWAY]             = { .type = NETLINK_TYPE_IN_ADDR },
+};
+
+static const NLTypeSystem rtnl_nexthop_type_system = {
+       .count = ELEMENTSOF(rtnl_nexthop_types),
+       .types = rtnl_nexthop_types,
+};
+
 static const NLType rtnl_types[] = {
         [NLMSG_DONE]       = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
         [NLMSG_ERROR]      = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
@@ -738,6 +750,9 @@ static const NLType rtnl_types[] = {
         [RTM_NEWRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
         [RTM_DELRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
         [RTM_GETRULE]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
+        [RTM_NEWNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
+        [RTM_DELNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
+        [RTM_GETNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
 };
 
 const NLTypeSystem rtnl_type_system_root = {
index 0d01a4bd0e9bc9d1f14e464af601bd75c00171b4..923accb7e304d4575128147d24f992d9b29d4e25 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <linux/rtnetlink.h>
+
 #include "sd-netlink.h"
 
 #include "in-addr-util.h"
@@ -19,6 +21,10 @@ static inline bool rtnl_message_type_is_route(uint16_t type) {
         return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
 }
 
+static inline bool rtnl_message_type_is_nexthop(uint16_t type) {
+        return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
+}
+
 static inline bool rtnl_message_type_is_link(uint16_t type) {
         return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK);
 }
index 751bf53d64e9e49a3e1779c1087ad111726db598..8cba0cc57eaf14826911a620deac84f6c547f8b0 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <netinet/in.h>
 #include <linux/if_addrlabel.h>
+#include <linux/nexthop.h>
 #include <stdbool.h>
 #include <unistd.h>
 
@@ -285,6 +286,70 @@ int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret,
         return 0;
 }
 
+int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
+                                uint16_t nhmsg_type, int nh_family,
+                                unsigned char nh_protocol) {
+        struct nhmsg *nhm;
+        int r;
+
+        assert_return(rtnl_message_type_is_nexthop(nhmsg_type), -EINVAL);
+        assert_return((nhmsg_type == RTM_GETNEXTHOP && nh_family == AF_UNSPEC) ||
+                      IN_SET(nh_family, AF_INET, AF_INET6), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = message_new(rtnl, ret, nhmsg_type);
+        if (r < 0)
+                return r;
+
+        if (nhmsg_type == RTM_NEWNEXTHOP)
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+
+        nhm = NLMSG_DATA((*ret)->hdr);
+
+        nhm->nh_family = nh_family;
+        nhm->nh_scope = RT_SCOPE_UNIVERSE;
+        nhm->nh_protocol = nh_protocol;
+
+        return 0;
+}
+
+int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags) {
+        struct nhmsg *nhm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_nexthop(m->hdr->nlmsg_type), -EINVAL);
+
+        nhm = NLMSG_DATA(m->hdr);
+        nhm->nh_flags |= flags;
+
+        return 0;
+}
+
+int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family) {
+        struct nhmsg *nhm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+
+        nhm = NLMSG_DATA(m->hdr);
+        nhm->nh_family = family;
+
+        return 0;
+}
+
+int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family) {
+        struct nhmsg *nhm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+
+        nhm = NLMSG_DATA(m->hdr);
+        *family = nhm->nh_family ;
+
+        return 0;
+}
+
 int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) {
         struct ndmsg *ndm;
 
@@ -713,6 +778,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) {
 
                 *family = rtm->rtm_family;
 
+                return 0;
+        } else if (rtnl_message_type_is_nexthop(m->hdr->nlmsg_type)) {
+                struct nhmsg *nhm;
+
+                nhm = NLMSG_DATA(m->hdr);
+
+                *family = nhm->nh_family;
+
                 return 0;
         }
 
index 02e9e9a26b9481f72c18300cf5b8ec12c4258b8c..f3366d1bf735d0a0ad0154ae878103f8432e4586 100644 (file)
@@ -896,6 +896,13 @@ int sd_netlink_add_match(
                         if (r < 0)
                                 return r;
                         break;
+                case RTM_NEWNEXTHOP:
+                case RTM_DELNEXTHOP:
+                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
+                        if (r < 0)
+                                return r;
+                break;
+
                 default:
                         return -EOPNOTSUPP;
         }
index 6bed37a1704c7d3c75f447d48b18b58d5e7f4997..c16e095c2c7100727227d2cac745496a6b69d108 100644 (file)
@@ -93,6 +93,8 @@ sources = files('''
         networkd-network-bus.h
         networkd-network.c
         networkd-network.h
+        networkd-nexthop.c
+        networkd-nexthop.h
         networkd-route.c
         networkd-route.h
         networkd-routing-policy-rule.c
index a23bddde9b67675fd3d948f8074f922950b2d166..897a2b2fc957e681b2807f3db400ed551367c9c0 100644 (file)
@@ -672,6 +672,9 @@ static Link *link_free(Link *link) {
         link->routes = set_free_with_destructor(link->routes, route_free);
         link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
 
+        link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free);
+        link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free);
+
         link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
         link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
 
@@ -903,6 +906,58 @@ static int link_request_set_routing_policy_rule(Link *link) {
         return 0;
 }
 
+static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->nexthop_messages > 0);
+        assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
+                      LINK_STATE_FAILED, LINK_STATE_LINGER));
+
+        link->nexthop_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_warning_errno(link, r, "Could not set nexthop: %m");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->nexthop_messages == 0) {
+                log_link_debug(link, "Nexthop set");
+                link->static_nexthops_configured = true;
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+int link_request_set_nexthop(Link *link) {
+        NextHop *nh;
+        int r;
+
+        LIST_FOREACH(nexthops, nh, link->network->static_nexthops) {
+                r = nexthop_configure(nh, link, nexthop_handler);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Could not set nexthop: %m");
+                if (r > 0)
+                        link->nexthop_messages++;
+        }
+
+        if (link->nexthop_messages == 0) {
+                link->static_nexthops_configured = true;
+                link_check_ready(link);
+        } else {
+                log_link_debug(link, "Setting nexthop");
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 1;
+}
+
 static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -948,6 +1003,7 @@ int link_request_set_routes(Link *link) {
         assert(link->state != _LINK_STATE_INVALID);
 
         link->static_routes_configured = false;
+        link->static_routes_ready = false;
 
         if (!link_has_carrier(link) && !link->network->configure_without_carrier)
                 /* During configuring addresses, the link lost its carrier. As networkd is dropping
@@ -1017,6 +1073,17 @@ void link_check_ready(Link *link) {
         if (!link->static_routes_configured)
                 return;
 
+        if (!link->static_routes_ready) {
+                link->static_routes_ready = true;
+                r = link_request_set_nexthop(link);
+                if (r < 0)
+                        link_enter_failed(link);
+                return;
+        }
+
+        if (!link->static_nexthops_configured)
+                return;
+
         if (!link->routing_policy_rules_configured)
                 return;
 
@@ -1134,6 +1201,8 @@ static int link_request_set_addresses(Link *link) {
         link->addresses_ready = false;
         link->neighbors_configured = false;
         link->static_routes_configured = false;
+        link->static_routes_ready = false;
+        link->static_nexthops_configured = false;
         link->routing_policy_rules_configured = false;
 
         r = link_set_bridge_fdb(link);
index 446c042fb9b9d125a8bdb6e561a126a6a150dc90..d6604c91203ac004559380befc0833bf1d80d49b 100644 (file)
@@ -69,6 +69,7 @@ typedef struct Link {
         unsigned address_label_messages;
         unsigned neighbor_messages;
         unsigned route_messages;
+        unsigned nexthop_messages;
         unsigned routing_policy_rule_messages;
         unsigned routing_policy_rule_remove_messages;
         unsigned enslaving;
@@ -79,9 +80,8 @@ typedef struct Link {
         Set *neighbors_foreign;
         Set *routes;
         Set *routes_foreign;
-
-        bool addresses_configured;
-        bool addresses_ready;
+        Set *nexthops;
+        Set *nexthops_foreign;
 
         sd_dhcp_client *dhcp_client;
         sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
@@ -100,8 +100,12 @@ typedef struct Link {
         sd_ipv4ll *ipv4ll;
         bool ipv4ll_address:1;
 
+        bool addresses_configured:1;
+        bool addresses_ready:1;
         bool neighbors_configured:1;
         bool static_routes_configured:1;
+        bool static_routes_ready:1;
+        bool static_nexthops_configured:1;
         bool routing_policy_rules_configured:1;
         bool setting_mtu:1;
 
@@ -198,6 +202,7 @@ uint32_t link_get_vrf_table(Link *link);
 uint32_t link_get_dhcp_route_table(Link *link);
 uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
 int link_request_set_routes(Link *link);
+int link_request_set_nexthop(Link *link);
 
 #define ADDRESS_FMT_VAL(address)                   \
         be32toh((address).s_addr) >> 24,           \
index 546bb2375ce7306650929caed259415aae2127c5..c70194f1f85787a76540c3e249411a83c3119aa3 100644 (file)
@@ -5,6 +5,7 @@
 #include <unistd.h>
 #include <linux/if.h>
 #include <linux/fib_rules.h>
+#include <linux/nexthop.h>
 
 #include "sd-daemon.h"
 #include "sd-netlink.h"
@@ -1153,6 +1154,118 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
         return 1;
 }
 
+int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+        _cleanup_(nexthop_freep) NextHop *tmp = NULL;
+        _cleanup_free_ char *gateway = NULL;
+        NextHop *nexthop = NULL;
+        Manager *m = userdata;
+        Link *link = NULL;
+        uint16_t type;
+        int r;
+
+        assert(rtnl);
+        assert(message);
+        assert(m);
+
+        if (sd_netlink_message_is_error(message)) {
+                r = sd_netlink_message_get_errno(message);
+                if (r < 0)
+                        log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m");
+
+                return 0;
+        }
+
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
+                log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
+                return 0;
+        }
+
+        r = nexthop_new(&tmp);
+        if (r < 0)
+                return log_oom();
+
+        r = sd_rtnl_message_get_family(message, &tmp->family);
+        if (r < 0) {
+                log_warning_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
+                return 0;
+        } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+                log_debug("rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
+                return 0;
+        }
+
+        switch (tmp->family) {
+        case AF_INET:
+                r = sd_netlink_message_read_in_addr(message, NHA_GATEWAY, &tmp->gw.in);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+                        return 0;
+                }
+                break;
+
+        case AF_INET6:
+                r = sd_netlink_message_read_in6_addr(message, NHA_GATEWAY, &tmp->gw.in6);
+                if (r < 0 && r != -ENODATA) {
+                        log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+                        return 0;
+                }
+                break;
+
+        default:
+                assert_not_reached("Received rule message with unsupported address family");
+        }
+
+        r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read_u32(message, NHA_OIF, &tmp->oif);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = link_get(m, tmp->oif, &link);
+        if (r < 0 || !link) {
+                if (!m->enumerating)
+                        log_warning("rtnl: received nexthop message for link (%d) we do not know about, ignoring", tmp->oif);
+                return 0;
+        }
+
+        (void) nexthop_get(link, tmp, &nexthop);
+
+        if (DEBUG_LOGGING)
+                (void) in_addr_to_string(tmp->family, &tmp->gw, &gateway);
+
+        switch (type) {
+        case RTM_NEWNEXTHOP:
+                if (!nexthop) {
+                        log_debug("Remembering foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
+                        r = nexthop_add_foreign(link, tmp, &nexthop);
+                        if (r < 0) {
+                                log_warning_errno(r, "Could not remember foreign nexthop, ignoring: %m");
+                                return 0;
+                        }
+                }
+                break;
+        case RTM_DELNEXTHOP:
+                log_debug("Forgetting foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
+                nexthop_free(nexthop);
+
+                break;
+
+        default:
+                assert_not_reached("Received invalid RTNL message type");
+        }
+
+        return 1;
+}
+
 static int systemd_netlink_fd(void) {
         int n, fd, rtnl_fd = -EINVAL;
 
@@ -1253,6 +1366,14 @@ static int manager_connect_rtnl(Manager *m) {
         if (r < 0)
                 return r;
 
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
+        if (r < 0)
+                return r;
+
         return 0;
 }
 
@@ -1931,6 +2052,47 @@ int manager_rtnl_enumerate_rules(Manager *m) {
         return r;
 }
 
+int manager_rtnl_enumerate_nexthop(Manager *m) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+        sd_netlink_message *nexthop;
+        int r;
+
+        assert(m);
+        assert(m->rtnl);
+
+        r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_request_dump(req, true);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(m->rtnl, req, 0, &reply);
+        if (r < 0) {
+                if (r == -EOPNOTSUPP) {
+                        log_debug("Nexthop are not supported by the kernel. Ignoring.");
+                        return 0;
+                }
+
+                return r;
+        }
+
+        for (nexthop = reply; nexthop; nexthop = sd_netlink_message_next(nexthop)) {
+                int k;
+
+                m->enumerating = true;
+
+                k = manager_rtnl_process_nexthop(m->rtnl, nexthop, m);
+                if (k < 0)
+                        r = k;
+
+                m->enumerating = false;
+        }
+
+        return r;
+}
+
 int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
         AddressPool *p;
         int r;
index d5049df8688d678abfc231b16ecf6b2afd5b8698..f2f309ffb05e93357ad28b5f08b5ad05a61ced62 100644 (file)
@@ -83,11 +83,13 @@ int manager_rtnl_enumerate_addresses(Manager *m);
 int manager_rtnl_enumerate_neighbors(Manager *m);
 int manager_rtnl_enumerate_routes(Manager *m);
 int manager_rtnl_enumerate_rules(Manager *m);
+int manager_rtnl_enumerate_nexthop(Manager *m);
 
 int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
+int manager_rtnl_process_nexthop(sd_netlink *nl, sd_netlink_message *message, void *userdata);
 
 void manager_dirty(Manager *m);
 
index 689b1a123ebbcdfe9e901c3a66b13da62654ad4d..490a0a38a3b8a5d5d2f2a29a9f28b5444a6e635f 100644 (file)
@@ -141,6 +141,8 @@ Route.InitialAdvertisedReceiveWindow,   config_parse_tcp_window,
 Route.QuickAck,                         config_parse_quickack,                           0,                             0
 Route.FastOpenNoCookie,                 config_parse_fast_open_no_cookie,                0,                             0
 Route.TTLPropagate,                     config_parse_route_ttl_propagate,                0,                             0
+NextHop.Id,                             config_parse_nexthop_id,                         0,                             0
+NextHop.Gateway,                        config_parse_nexthop_gateway,                    0,                             0
 DHCPv4.ClientIdentifier,                config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
 DHCPv4.UseDNS,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_dns)
 DHCPv4.RoutesToDNS,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_routes_to_dns)
index 3ea76a034a6edf4d1aa7a1e3d2d01c52e08cdfe6..d949cc0f938ddc47a0f243e18fbf72a706f80820 100644 (file)
@@ -151,6 +151,7 @@ int network_verify(Network *network) {
         AddressLabel *label, *label_next;
         Prefix *prefix, *prefix_next;
         RoutingPolicyRule *rule, *rule_next;
+        NextHop *nexthop, *nextnop_next;
 
         assert(network);
         assert(network->filename);
@@ -282,6 +283,10 @@ int network_verify(Network *network) {
                 if (route_section_verify(route, network) < 0)
                         route_free(route);
 
+        LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops)
+                if (nexthop_section_verify(nexthop) < 0)
+                        nexthop_free(nexthop);
+
         LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
                 if (section_is_invalid(fdb->section))
                         fdb_entry_free(fdb);
@@ -453,6 +458,7 @@ int network_load_one(Manager *manager, const char *filename) {
                               "IPv6AddressLabel\0"
                               "RoutingPolicyRule\0"
                               "Route\0"
+                              "NextHop\0"
                               "DHCP\0"
                               "DHCPv4\0" /* compat */
                               "DHCPv6\0"
@@ -525,8 +531,9 @@ static Network *network_free(Network *network) {
         FdbEntry *fdb_entry;
         Neighbor *neighbor;
         AddressLabel *label;
-        Prefix *prefix;
         Address *address;
+        NextHop *nexthop;
+        Prefix *prefix;
         Route *route;
 
         if (!network)
@@ -573,6 +580,9 @@ static Network *network_free(Network *network) {
         while ((route = network->static_routes))
                 route_free(route);
 
+        while ((nexthop = network->static_nexthops))
+                nexthop_free(nexthop);
+
         while ((address = network->static_addresses))
                 address_free(address);
 
@@ -596,6 +606,7 @@ static Network *network_free(Network *network) {
 
         hashmap_free(network->addresses_by_section);
         hashmap_free(network->routes_by_section);
+        hashmap_free(network->nexthops_by_section);
         hashmap_free(network->fdb_entries_by_section);
         hashmap_free(network->neighbors_by_section);
         hashmap_free(network->address_labels_by_section);
index 35469c05edf25d74c113b39ee309a107cd7fe837..668cc0d348c6258631f42234843cac21518edd75 100644 (file)
@@ -19,6 +19,7 @@
 #include "networkd-lldp-rx.h"
 #include "networkd-lldp-tx.h"
 #include "networkd-neighbor.h"
+#include "networkd-nexthop.h"
 #include "networkd-radv.h"
 #include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
@@ -228,6 +229,7 @@ struct Network {
 
         LIST_HEAD(Address, static_addresses);
         LIST_HEAD(Route, static_routes);
+        LIST_HEAD(NextHop, static_nexthops);
         LIST_HEAD(FdbEntry, static_fdb_entries);
         LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
         LIST_HEAD(Neighbor, neighbors);
@@ -238,6 +240,7 @@ struct Network {
 
         unsigned n_static_addresses;
         unsigned n_static_routes;
+        unsigned n_static_nexthops;
         unsigned n_static_fdb_entries;
         unsigned n_ipv6_proxy_ndp_addresses;
         unsigned n_neighbors;
@@ -248,6 +251,7 @@ struct Network {
 
         Hashmap *addresses_by_section;
         Hashmap *routes_by_section;
+        Hashmap *nexthops_by_section;
         Hashmap *fdb_entries_by_section;
         Hashmap *neighbors_by_section;
         Hashmap *address_labels_by_section;
diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c
new file mode 100644 (file)
index 0000000..9658fe3
--- /dev/null
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc.
+ */
+
+#include <linux/nexthop.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "networkd-nexthop.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "util.h"
+
+int nexthop_new(NextHop **ret) {
+        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+
+        nexthop = new(NextHop, 1);
+        if (!nexthop)
+                return -ENOMEM;
+
+        *nexthop = (NextHop) {
+                .family = AF_UNSPEC,
+        };
+
+        *ret = TAKE_PTR(nexthop);
+
+        return 0;
+}
+
+static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(!!filename == (section_line > 0));
+
+        if (filename) {
+                r = network_config_section_new(filename, section_line, &n);
+                if (r < 0)
+                        return r;
+
+                nexthop = hashmap_get(network->nexthops_by_section, n);
+                if (nexthop) {
+                        *ret = TAKE_PTR(nexthop);
+
+                        return 0;
+                }
+        }
+
+        r = nexthop_new(&nexthop);
+        if (r < 0)
+                return r;
+
+        nexthop->protocol = RTPROT_STATIC;
+        nexthop->network = network;
+        LIST_PREPEND(nexthops, network->static_nexthops, nexthop);
+        network->n_static_nexthops++;
+
+        if (filename) {
+                nexthop->section = TAKE_PTR(n);
+
+                r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
+                if (r < 0)
+                        return r;
+
+                r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(nexthop);
+
+        return 0;
+}
+
+void nexthop_free(NextHop *nexthop) {
+        if (!nexthop)
+                return;
+
+        if (nexthop->network) {
+                LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop);
+
+                assert(nexthop->network->n_static_nexthops > 0);
+                nexthop->network->n_static_nexthops--;
+
+                if (nexthop->section)
+                        hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
+        }
+
+        network_config_section_free(nexthop->section);
+
+        if (nexthop->link) {
+                set_remove(nexthop->link->nexthops, nexthop);
+                set_remove(nexthop->link->nexthops_foreign, nexthop);
+        }
+
+        free(nexthop);
+}
+
+static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
+        assert(nexthop);
+
+        siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
+        siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state);
+        siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
+
+        switch (nexthop->family) {
+        case AF_INET:
+        case AF_INET6:
+                siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
+
+                break;
+        default:
+                /* treat any other address family as AF_UNSPEC */
+                break;
+        }
+}
+
+static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
+        int r;
+
+        r = CMP(a->id, b->id);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->oif, b->oif);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->family, b->family);
+        if (r != 0)
+                return r;
+
+        switch (a->family) {
+        case AF_INET:
+        case AF_INET6:
+
+                r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+                if (r != 0)
+                        return r;
+
+                return 0;
+        default:
+                /* treat any other address family as AF_UNSPEC */
+                return 0;
+        }
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                nexthop_hash_ops,
+                NextHop,
+                nexthop_hash_func,
+                nexthop_compare_func,
+                nexthop_free);
+
+bool nexthop_equal(NextHop *r1, NextHop *r2) {
+        if (r1 == r2)
+                return true;
+
+        if (!r1 || !r2)
+                return false;
+
+        return nexthop_compare_func(r1, r2) == 0;
+}
+
+int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
+        NextHop *existing;
+
+        assert(link);
+        assert(in);
+
+        existing = set_get(link->nexthops, in);
+        if (existing) {
+                if (ret)
+                        *ret = existing;
+                return 1;
+        }
+
+        existing = set_get(link->nexthops_foreign, in);
+        if (existing) {
+                if (ret)
+                        *ret = existing;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop **ret) {
+        _cleanup_(nexthop_freep) NextHop *nexthop = NULL;
+        int r;
+
+        assert(link);
+        assert(nexthops);
+        assert(in);
+
+        r = nexthop_new(&nexthop);
+        if (r < 0)
+                return r;
+
+        nexthop->id = in->id;
+        nexthop->oif = in->oif;
+        nexthop->family = in->family;
+        nexthop->gw = in->gw;
+
+        r = set_ensure_allocated(nexthops, &nexthop_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = set_put(*nexthops, nexthop);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EEXIST;
+
+        nexthop->link = link;
+
+        if (ret)
+                *ret = nexthop;
+
+        nexthop = NULL;
+
+        return 0;
+}
+
+int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
+        return nexthop_add_internal(link, &link->nexthops_foreign, in, ret);
+}
+
+int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
+        NextHop *nexthop;
+        int r;
+
+        r = nexthop_get(link, in, &nexthop);
+        if (r == -ENOENT) {
+                /* NextHop does not exist, create a new one */
+                r = nexthop_add_internal(link, &link->nexthops, in, &nexthop);
+                if (r < 0)
+                        return r;
+        } else if (r == 0) {
+                /* Take over a foreign nexthop */
+                r = set_ensure_allocated(&link->nexthops, &nexthop_hash_ops);
+                if (r < 0)
+                        return r;
+
+                r = set_put(link->nexthops, nexthop);
+                if (r < 0)
+                        return r;
+
+                set_remove(link->nexthops_foreign, nexthop);
+        } else if (r == 1) {
+                /* NextHop exists, do nothing */
+                ;
+        } else
+                return r;
+
+        if (ret)
+                *ret = nexthop;
+
+        return 0;
+}
+
+static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(m);
+        assert(link);
+        assert(link->ifname);
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -ESRCH)
+                log_link_warning_errno(link, r, "Could not drop nexthop: %m");
+
+        return 1;
+}
+
+int nexthop_remove(NextHop *nexthop, Link *link,
+                   link_netlink_message_handler_t callback) {
+
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+        assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
+
+        r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
+                                      RTM_DELNEXTHOP, nexthop->family,
+                                      nexthop->protocol);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *gw = NULL;
+
+                if (!in_addr_is_null(nexthop->family, &nexthop->gw))
+                        (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
+
+                log_link_debug(link, "Removing nexthop: gw: %s", strna(gw));
+        }
+
+        if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
+                r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
+        }
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req,
+                               callback ?: nexthop_remove_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        return 0;
+}
+
+int nexthop_configure(
+                NextHop *nexthop,
+                Link *link,
+                link_netlink_message_handler_t callback) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+        assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
+        assert(callback);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *gw = NULL;
+
+                if (!in_addr_is_null(nexthop->family, &nexthop->gw))
+                        (void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
+
+                log_link_debug(link, "Configuring nexthop: gw: %s", strna(gw));
+        }
+
+        r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
+                                        RTM_NEWNEXTHOP, nexthop->family,
+                                        nexthop->protocol);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m");
+
+        r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
+
+        r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
+
+        if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
+                r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m");
+
+                r = sd_rtnl_message_nexthop_set_family(req, nexthop->family);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set nexthop family: %m");
+        }
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        r = nexthop_add(link, nexthop, &nexthop);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not add nexthop: %m");
+
+        return 1;
+}
+
+int nexthop_section_verify(NextHop *nh) {
+        if (section_is_invalid(nh->section))
+                return -EINVAL;
+
+        if (in_addr_is_null(nh->family, &nh->gw) < 0)
+                return -EINVAL;
+
+        return 0;
+}
+
+int config_parse_nexthop_id(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        Network *network = userdata;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = nexthop_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = safe_atou32(rvalue, &n->id);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(n);
+        return 0;
+}
+
+int config_parse_nexthop_gateway(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+        Network *network = userdata;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = nexthop_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(n);
+        return 0;
+}
diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h
new file mode 100644 (file)
index 0000000..28cbdad
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc.
+ */
+
+#pragma once
+
+#include "conf-parser.h"
+#include "macro.h"
+
+typedef struct NextHop NextHop;
+typedef struct NetworkConfigSection NetworkConfigSection;
+
+#include "networkd-network.h"
+#include "networkd-util.h"
+
+struct NextHop {
+        Network *network;
+        NetworkConfigSection *section;
+
+        Link *link;
+
+        unsigned char protocol;
+
+        int family;
+        uint32_t oif;
+        uint32_t id;
+
+        union in_addr_union gw;
+
+        LIST_FIELDS(NextHop, nexthops);
+};
+
+extern const struct hash_ops nexthop_hash_ops;
+
+int nexthop_new(NextHop **ret);
+void nexthop_free(NextHop *nexthop);
+int nexthop_configure(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
+int nexthop_remove(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
+
+int nexthop_get(Link *link, NextHop *in, NextHop **ret);
+int nexthop_add(Link *link, NextHop *in, NextHop **ret);
+int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret);
+bool nexthop_equal(NextHop *r1, NextHop *r2);
+
+int nexthop_section_verify(NextHop *nexthop);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
+CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);
index 38bd9ff1ffe0b2ce95f5a7120cd1fdbbe28c0590..c7ce64b90b395ab608815fa1386491a689b1d125 100644 (file)
@@ -107,6 +107,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Could not enumerate rules: %m");
 
+        r = manager_rtnl_enumerate_nexthop(m);
+        if (r < 0)
+                return log_error_errno(r, "Could not enumerate nexthop: %m");
+
         r = manager_start(m);
         if (r < 0)
                 return log_error_errno(r, "Could not start manager: %m");
index 33cd82ba5a68b268a0cc065469b0afd9a0e8724b..d5b74e62e1d1315b6d75963f8346996401a1a63b 100644 (file)
@@ -167,6 +167,12 @@ int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char
 int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
 int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type);
 
+int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nhmsg_type, int nh_family, unsigned char nh_protocol);
+
+int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags);
+int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family);
+int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family);
+
 int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags);
 int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state);
 int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);
index 24d94033fc844a3cfe3931c22ac76bfe15ee0817..bd6a127ac5063a759fe03aa53ceed61f10b3fb0b 100644 (file)
@@ -255,3 +255,6 @@ MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
+[NextHop]
+Id=
+Gateway=