]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13663 from ssahani/dhcp-send-option-data
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 16 Oct 2019 09:31:22 +0000 (11:31 +0200)
committerGitHub <noreply@github.com>
Wed, 16 Oct 2019 09:31:22 +0000 (11:31 +0200)
network: DHCPv4 client- add support to send arbitary option and data

50 files changed:
hwdb.d/60-sensor.hwdb
man/systemd.network.xml
man/udev.xml
src/basic/linux/nexthop.h [new file with mode: 0644]
src/basic/linux/rtnetlink.h
src/core/automount.c
src/core/cgroup.c
src/core/device.c
src/core/mount.c
src/core/path.c
src/core/scope.c
src/core/service.c
src/core/slice.c
src/core/socket.c
src/core/swap.c
src/core/target.c
src/core/timer.c
src/core/unit.c
src/core/unit.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/login/logind-session.c
src/login/logind-session.h
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-ndisc.c
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/portable/portable.c
src/shared/bus-unit-util.c
src/systemctl/systemctl.c
src/systemd/sd-netlink.h
src/udev/ata_id/ata_id.c
src/udev/cdrom_id/cdrom_id.c
src/udev/fido_id/fido_id.c
src/udev/udev-rules.c
test/fuzz/fuzz-network-parser/directives.network
test/test-network/conf/25-nexthop.network [new file with mode: 0644]
test/test-network/conf/25-veth-peer.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index 4d8e21f169bd8b9df236e32badfb9cae32840a48..0fdd9177213453a888fae9f562f6062f9cabdffe 100644 (file)
@@ -82,6 +82,9 @@ sensor:modalias:acpi:SMO8500:*:dmi:*Acer*:pnOneS1002*
 
 sensor:modalias:acpi:KIOX0009*:dmi:*:svnAcer:pnOneS1003:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnAcer*:pnSwitchSW312-31:*
+ ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
 
 #########################################
 # Archos
index 58bc7e140bb1a8b2ae713b9ad0c1216bc6d8948f..5d3c9383d9c8afe2a34ca25e92840c5ffd0d6022 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>
index 98d17bbb54a8ca606cc542fe9e02ded933ce8b3c..09254f818ec16775e40a06bd4a87bc63d4dfca83 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>CONST{<replaceable>key</replaceable>}</varname></term>
+          <listitem>
+            <para>Match against a system-wide constant. Supported keys are:</para>
+            <variablelist>
+              <varlistentry>
+                <term><literal>arch</literal></term>
+                <listitem>
+                  <para>System's architecture. See <option>ConditionArchitecture=</option> in
+                  <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+                  for possible values.</para>
+                </listitem>
+              </varlistentry>
+              <varlistentry>
+                <term><literal>virt</literal></term>
+                <listitem>
+                  <para>System's virtualization environment. See
+                  <citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                  for possible values.</para>
+                </listitem>
+              </varlistentry>
+            </variablelist>
+            <para>Unknown keys will never match.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>TAG</varname></term>
           <listitem>
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 a54e56c3122ea52344a77673aaf9bd9705cb7d90..0ecacc12c8f604fff703a930e2d5c006cc3ac46d 100644 (file)
@@ -164,9 +164,7 @@ static int automount_verify(Automount *a) {
         int r;
 
         assert(a);
-
-        if (UNIT(a)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(a)->load_state == UNIT_LOADED);
 
         if (path_equal(a->where, "/")) {
                 log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing.");
@@ -201,6 +199,24 @@ static int automount_set_where(Automount *a) {
         return 1;
 }
 
+static int automount_add_extras(Automount *a) {
+        int r;
+
+        r = automount_set_where(a);
+        if (r < 0)
+                return r;
+
+        r = automount_add_trigger_dependencies(a);
+        if (r < 0)
+                return r;
+
+        r = automount_add_mount_dependencies(a);
+        if (r < 0)
+                return r;
+
+        return automount_add_default_dependencies(a);
+}
+
 static int automount_load(Unit *u) {
         Automount *a = AUTOMOUNT(u);
         int r;
@@ -209,27 +225,16 @@ static int automount_load(Unit *u) {
         assert(u->load_state == UNIT_STUB);
 
         /* Load a .automount file */
-        r = unit_load_fragment_and_dropin(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_LOADED) {
-                r = automount_set_where(a);
-                if (r < 0)
-                        return r;
-
-                r = automount_add_trigger_dependencies(a);
-                if (r < 0)
-                        return r;
-
-                r = automount_add_mount_dependencies(a);
-                if (r < 0)
-                        return r;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-                r = automount_add_default_dependencies(a);
-                if (r < 0)
-                        return r;
-        }
+        r = automount_add_extras(a);
+        if (r < 0)
+                return r;
 
         return automount_verify(a);
 }
index c67ecc37c53a08999065fa629f2e1e29ce8e3e02..981aca53cdb416da6f4766021086b7f0d77d1b72 100644 (file)
@@ -970,10 +970,10 @@ static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
                      CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
 }
 
-static void cgroup_apply_unified_cpuset(Unit *u, CPUSet cpus, const char *name) {
+static void cgroup_apply_unified_cpuset(Unit *u, const CPUSet *cpus, const char *name) {
         _cleanup_free_ char *buf = NULL;
 
-        buf = cpu_set_to_range_string(&cpus);
+        buf = cpu_set_to_range_string(cpus);
         if (!buf)
             return;
 
@@ -1221,8 +1221,8 @@ static void cgroup_context_apply(
         }
 
         if ((apply_mask & CGROUP_MASK_CPUSET) && !is_local_root) {
-                cgroup_apply_unified_cpuset(u, c->cpuset_cpus, "cpuset.cpus");
-                cgroup_apply_unified_cpuset(u, c->cpuset_mems, "cpuset.mems");
+                cgroup_apply_unified_cpuset(u, &c->cpuset_cpus, "cpuset.cpus");
+                cgroup_apply_unified_cpuset(u, &c->cpuset_mems, "cpuset.mems");
         }
 
         /* The 'io' controller attributes are not exported on the host's root cgroup (being a pure cgroup v2
index e2abca469a5d892d4a8d14b5ef7adbc940983bba..45149e75554c9243b299f93a6ebf8b7d43d2320f 100644 (file)
@@ -116,7 +116,7 @@ static void device_done(Unit *u) {
 static int device_load(Unit *u) {
         int r;
 
-        r = unit_load_fragment_and_dropin_optional(u);
+        r = unit_load_fragment_and_dropin(u, false);
         if (r < 0)
                 return r;
 
index 09d08f3990fd5523fa469534c6d44ef70eba320e..7697a74b646b190b8c75903ca96aeca4a90a641e 100644 (file)
@@ -513,9 +513,7 @@ static int mount_verify(Mount *m) {
         int r;
 
         assert(m);
-
-        if (UNIT(m)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(m)->load_state == UNIT_LOADED);
 
         if (!m->from_fragment && !m->from_proc_self_mountinfo && !UNIT(m)->perpetual)
                 return -ENOENT;
@@ -606,11 +604,11 @@ static int mount_add_extras(Mount *m) {
         return 0;
 }
 
-static int mount_load_root_mount(Unit *u) {
+static void mount_load_root_mount(Unit *u) {
         assert(u);
 
         if (!unit_has_name(u, SPECIAL_ROOT_MOUNT))
-                return 0;
+                return;
 
         u->perpetual = true;
         u->default_dependencies = false;
@@ -621,39 +619,33 @@ static int mount_load_root_mount(Unit *u) {
 
         if (!u->description)
                 u->description = strdup("Root Mount");
-
-        return 1;
 }
 
 static int mount_load(Unit *u) {
         Mount *m = MOUNT(u);
-        int r, q, w;
+        int r, q = 0;
 
         assert(u);
         assert(u->load_state == UNIT_STUB);
 
-        r = mount_load_root_mount(u);
+        mount_load_root_mount(u);
 
-        if (m->from_proc_self_mountinfo || u->perpetual)
-                q = unit_load_fragment_and_dropin_optional(u);
-        else
-                q = unit_load_fragment_and_dropin(u);
+        bool fragment_optional = m->from_proc_self_mountinfo || u->perpetual;
+        r = unit_load_fragment_and_dropin(u, !fragment_optional);
 
         /* Add in some extras. Note we do this in all cases (even if we failed to load the unit) when announced by the
          * kernel, because we need some things to be set up no matter what when the kernel establishes a mount and thus
          * we need to update the state in our unit to track it. After all, consider that we don't allow changing the
          * 'slice' field for a unit once it is active. */
         if (u->load_state == UNIT_LOADED || m->from_proc_self_mountinfo || u->perpetual)
-                w = mount_add_extras(m);
-        else
-                w = 0;
+                q = mount_add_extras(m);
 
         if (r < 0)
                 return r;
         if (q < 0)
                 return q;
-        if (w < 0)
-                return w;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
         return mount_verify(m);
 }
index aee94ce7f04445fcaa58e8eaed4f1e04666d6589..dff551f37728ec5844a57b47ce31ad4bbb32ae9a 100644 (file)
@@ -284,9 +284,7 @@ static int path_add_mount_dependencies(Path *p) {
 
 static int path_verify(Path *p) {
         assert(p);
-
-        if (UNIT(p)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(p)->load_state == UNIT_LOADED);
 
         if (!p->specs) {
                 log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing.");
@@ -333,6 +331,20 @@ static int path_add_trigger_dependencies(Path *p) {
         return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
 }
 
+static int path_add_extras(Path *p) {
+        int r;
+
+        r = path_add_trigger_dependencies(p);
+        if (r < 0)
+                return r;
+
+        r = path_add_mount_dependencies(p);
+        if (r < 0)
+                return r;
+
+        return path_add_default_dependencies(p);
+}
+
 static int path_load(Unit *u) {
         Path *p = PATH(u);
         int r;
@@ -340,24 +352,16 @@ static int path_load(Unit *u) {
         assert(u);
         assert(u->load_state == UNIT_STUB);
 
-        r = unit_load_fragment_and_dropin(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_LOADED) {
-
-                r = path_add_trigger_dependencies(p);
-                if (r < 0)
-                        return r;
-
-                r = path_add_mount_dependencies(p);
-                if (r < 0)
-                        return r;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-                r = path_add_default_dependencies(p);
-                if (r < 0)
-                        return r;
-        }
+        r = path_add_extras(p);
+        if (r < 0)
+                return r;
 
         return path_verify(p);
 }
index 79470a0a9beb89afe31cde591248405bad1f2af4..094c8979a8ea879791094457092106b251236f21 100644 (file)
@@ -125,9 +125,7 @@ static int scope_add_default_dependencies(Scope *s) {
 
 static int scope_verify(Scope *s) {
         assert(s);
-
-        if (UNIT(s)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(s)->load_state == UNIT_LOADED);
 
         if (set_isempty(UNIT(s)->pids) &&
             !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
@@ -162,6 +160,20 @@ static int scope_load_init_scope(Unit *u) {
         return 1;
 }
 
+static int scope_add_extras(Scope *s) {
+        int r;
+
+        r = unit_patch_contexts(UNIT(s));
+        if (r < 0)
+                return r;
+
+        r = unit_set_default_slice(UNIT(s));
+        if (r < 0)
+                return r;
+
+        return scope_add_default_dependencies(s);
+}
+
 static int scope_load(Unit *u) {
         Scope *s = SCOPE(u);
         int r;
@@ -176,23 +188,17 @@ static int scope_load(Unit *u) {
         r = scope_load_init_scope(u);
         if (r < 0)
                 return r;
-        r = unit_load_fragment_and_dropin_optional(u);
+
+        r = unit_load_fragment_and_dropin(u, false);
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_LOADED) {
-                r = unit_patch_contexts(u);
-                if (r < 0)
-                        return r;
-
-                r = unit_set_default_slice(u);
-                if (r < 0)
-                        return r;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-                r = scope_add_default_dependencies(s);
-                if (r < 0)
-                        return r;
-        }
+        r = scope_add_extras(s);
+        if (r < 0)
+                return r;
 
         return scope_verify(s);
 }
index ada25e634a063f5c169968f134456590948863e8..a9143bcc4e72a9e2f09e7141d1f2ac9483117d46 100644 (file)
@@ -548,9 +548,7 @@ static int service_arm_timer(Service *s, usec_t usec) {
 
 static int service_verify(Service *s) {
         assert(s);
-
-        if (UNIT(s)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(s)->load_state == UNIT_LOADED);
 
         if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
             && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
@@ -760,32 +758,17 @@ static int service_load(Unit *u) {
         Service *s = SERVICE(u);
         int r;
 
-        assert(s);
-
-        /* Load a .service file */
-        r = unit_load_fragment(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        /* Still nothing found? Then let's give up */
-        if (u->load_state == UNIT_STUB)
-                return -ENOENT;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
         /* This is a new unit? Then let's add in some extras */
-        if (u->load_state == UNIT_LOADED) {
-
-                /* We were able to load something, then let's add in
-                 * the dropin directories. */
-                r = unit_load_dropin(u);
-                if (r < 0)
-                        return r;
-
-                /* This is a new unit? Then let's add in some
-                 * extras */
-                r = service_add_extras(s);
-                if (r < 0)
-                        return r;
-        }
+        r = service_add_extras(s);
+        if (r < 0)
+                return r;
 
         return service_verify(s);
 }
index c12328b3b736daad43607709a5aa33f1b544fc07..d97a262786be3d0ad873cd1615256b96bedd83d5 100644 (file)
@@ -91,9 +91,7 @@ static int slice_verify(Slice *s) {
         int r;
 
         assert(s);
-
-        if (UNIT(s)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(s)->load_state == UNIT_LOADED);
 
         if (!slice_name_is_valid(UNIT(s)->id)) {
                 log_unit_error(UNIT(s), "Slice name %s is not valid. Refusing.", UNIT(s)->id);
@@ -170,25 +168,25 @@ static int slice_load(Unit *u) {
         if (r < 0)
                 return r;
 
-        r = unit_load_fragment_and_dropin_optional(u);
+        r = unit_load_fragment_and_dropin(u, false);
         if (r < 0)
                 return r;
 
-        /* This is a new unit? Then let's add in some extras */
-        if (u->load_state == UNIT_LOADED) {
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-                r = unit_patch_contexts(u);
-                if (r < 0)
-                        return r;
+        /* This is a new unit? Then let's add in some extras */
+        r = unit_patch_contexts(u);
+        if (r < 0)
+                return r;
 
-                r = slice_add_parent_slice(s);
-                if (r < 0)
-                        return r;
+        r = slice_add_parent_slice(s);
+        if (r < 0)
+                return r;
 
-                r = slice_add_default_dependencies(s);
-                if (r < 0)
-                        return r;
-        }
+        r = slice_add_default_dependencies(s);
+        if (r < 0)
+                return r;
 
         return slice_verify(s);
 }
index f31d3bd971d64b5e16cc6d9b895b1a09c47fbb57..71ab3c647f95b5047adacbea431ae2ec6865cda6 100644 (file)
@@ -433,9 +433,7 @@ static const char *socket_find_symlink_target(Socket *s) {
 
 static int socket_verify(Socket *s) {
         assert(s);
-
-        if (UNIT(s)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(s)->load_state == UNIT_LOADED);
 
         if (!s->ports) {
                 log_unit_error(UNIT(s), "Unit has no Listen setting (ListenStream=, ListenDatagram=, ListenFIFO=, ...). Refusing.");
@@ -514,16 +512,17 @@ static int socket_load(Unit *u) {
         if (r < 0)
                 return r;
 
-        r = unit_load_fragment_and_dropin(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_LOADED) {
-                /* This is a new unit? Then let's add in some extras */
-                r = socket_add_extras(s);
-                if (r < 0)
-                        return r;
-        }
+        if (u->load_state != UNIT_LOADED)
+                return 0;
+
+        /* This is a new unit? Then let's add in some extras */
+        r = socket_add_extras(s);
+        if (r < 0)
+                return r;
 
         return socket_verify(s);
 }
index ad1da6dddb6f977ed8fe2339c56943c25c3fa1db..726bda2f37b41ef58f61db6ded8828a09dc69e48 100644 (file)
@@ -232,8 +232,7 @@ static int swap_verify(Swap *s) {
         _cleanup_free_ char *e = NULL;
         int r;
 
-        if (UNIT(s)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(s)->load_state == UNIT_LOADED);
 
         r = unit_name_from_path(s->what, ".swap", &e);
         if (r < 0)
@@ -340,28 +339,26 @@ static int swap_add_extras(Swap *s) {
 
 static int swap_load(Unit *u) {
         Swap *s = SWAP(u);
-        int r, q;
+        int r, q = 0;
 
         assert(s);
         assert(u->load_state == UNIT_STUB);
 
         /* Load a .swap file */
-        if (SWAP(u)->from_proc_swaps)
-                r = unit_load_fragment_and_dropin_optional(u);
-        else
-                r = unit_load_fragment_and_dropin(u);
+        bool fragment_optional = s->from_proc_swaps;
+        r = unit_load_fragment_and_dropin(u, !fragment_optional);
 
-        /* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is already
-         * active. */
+        /* Add in some extras, and do so either when we successfully loaded something or when /proc/swaps is
+         * already active. */
         if (u->load_state == UNIT_LOADED || s->from_proc_swaps)
                 q = swap_add_extras(s);
-        else
-                q = 0;
 
         if (r < 0)
                 return r;
         if (q < 0)
                 return q;
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
         return swap_verify(s);
 }
index 421a304c73da421b14a962ab2cb35e962e23a248..357ca70e090c377885cfbcdae62e9a3e42f5df36 100644 (file)
@@ -80,18 +80,15 @@ static int target_load(Unit *u) {
 
         assert(t);
 
-        r = unit_load_fragment_and_dropin(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        /* This is a new unit? Then let's add in some extras */
-        if (u->load_state == UNIT_LOADED) {
-                r = target_add_default_dependencies(t);
-                if (r < 0)
-                        return r;
-        }
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-        return 0;
+        /* This is a new unit? Then let's add in some extras */
+        return target_add_default_dependencies(t);
 }
 
 static int target_coldplug(Unit *u) {
index 7d816856b19d70266675078440e3ec3e17c532f9..47c59ab7e9627afd254da474e5437b0604e7b249 100644 (file)
@@ -73,9 +73,7 @@ static void timer_done(Unit *u) {
 
 static int timer_verify(Timer *t) {
         assert(t);
-
-        if (UNIT(t)->load_state != UNIT_LOADED)
-                return 0;
+        assert(UNIT(t)->load_state == UNIT_LOADED);
 
         if (!t->values && !t->on_clock_change && !t->on_timezone_change) {
                 log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
@@ -178,24 +176,25 @@ static int timer_load(Unit *u) {
         assert(u);
         assert(u->load_state == UNIT_STUB);
 
-        r = unit_load_fragment_and_dropin(u);
+        r = unit_load_fragment_and_dropin(u, true);
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_LOADED) {
+        if (u->load_state != UNIT_LOADED)
+                return 0;
 
-                r = timer_add_trigger_dependencies(t);
-                if (r < 0)
-                        return r;
+        /* This is a new unit? Then let's add in some extras */
+        r = timer_add_trigger_dependencies(t);
+        if (r < 0)
+                return r;
 
-                r = timer_setup_persistent(t);
-                if (r < 0)
-                        return r;
+        r = timer_setup_persistent(t);
+        if (r < 0)
+                return r;
 
-                r = timer_add_default_dependencies(t);
-                if (r < 0)
-                        return r;
-        }
+        r = timer_add_default_dependencies(t);
+        if (r < 0)
+                return r;
 
         return timer_verify(t);
 }
index 6dd075faa775c67e7c003681cbdbb27e307d4f52..58c99d9e073d08a59b486a113319210694906f02 100644 (file)
@@ -1361,7 +1361,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
 }
 
 /* Common implementation for multiple backends */
-int unit_load_fragment_and_dropin(Unit *u) {
+int unit_load_fragment_and_dropin(Unit *u, bool fragment_required) {
         int r;
 
         assert(u);
@@ -1371,8 +1371,12 @@ int unit_load_fragment_and_dropin(Unit *u) {
         if (r < 0)
                 return r;
 
-        if (u->load_state == UNIT_STUB)
-                return -ENOENT;
+        if (u->load_state == UNIT_STUB) {
+                if (fragment_required)
+                        return -ENOENT;
+
+                u->load_state = UNIT_LOADED;
+        }
 
         /* Load drop-in directory data. If u is an alias, we might be reloading the
          * target unit needlessly. But we cannot be sure which drops-ins have already
@@ -1381,27 +1385,6 @@ int unit_load_fragment_and_dropin(Unit *u) {
         return unit_load_dropin(unit_follow_merge(u));
 }
 
-/* Common implementation for multiple backends */
-int unit_load_fragment_and_dropin_optional(Unit *u) {
-        int r;
-
-        assert(u);
-
-        /* Same as unit_load_fragment_and_dropin(), but whether
-         * something can be loaded or not doesn't matter. */
-
-        /* Load a .service/.socket/.slice/… file */
-        r = unit_load_fragment(u);
-        if (r < 0)
-                return r;
-
-        if (u->load_state == UNIT_STUB)
-                u->load_state = UNIT_LOADED;
-
-        /* Load drop-in directory data */
-        return unit_load_dropin(unit_follow_merge(u));
-}
-
 void unit_add_to_target_deps_queue(Unit *u) {
         Manager *m = u->manager;
 
@@ -1559,16 +1542,11 @@ int unit_load(Unit *u) {
                 u->fragment_mtime = now(CLOCK_REALTIME);
         }
 
-        if (UNIT_VTABLE(u)->load) {
-                r = UNIT_VTABLE(u)->load(u);
-                if (r < 0)
-                        goto fail;
-        }
-
-        if (u->load_state == UNIT_STUB) {
-                r = -ENOENT;
+        r = UNIT_VTABLE(u)->load(u);
+        if (r < 0)
                 goto fail;
-        }
+
+        assert(u->load_state != UNIT_STUB);
 
         if (u->load_state == UNIT_LOADED) {
                 unit_add_to_target_deps_queue(u);
index 96f718acdcc638f4587faefd11f88d3a94c31361..5695552471edf5c514d13478fb637b827ce9af9c 100644 (file)
@@ -670,8 +670,7 @@ int unit_merge_by_name(Unit *u, const char *other);
 
 Unit *unit_follow_merge(Unit *u) _pure_;
 
-int unit_load_fragment_and_dropin(Unit *u);
-int unit_load_fragment_and_dropin_optional(Unit *u);
+int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
 int unit_load(Unit *unit);
 
 int unit_set_slice(Unit *u, Unit *slice);
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 7e8025a0ea22b4cbe0faff88f8b5b684cbe77cb5..68468464cd2d1533726769f19d384959b36e0704 100644 (file)
@@ -1183,7 +1183,7 @@ static int session_open_vt(Session *s) {
         return s->vtfd;
 }
 
-int session_prepare_vt(Session *s) {
+static int session_prepare_vt(Session *s) {
         int vt, r;
         struct vt_mode mode = {};
 
index 50fe29e7970cf651253bd848fe24aa1b171f4f5a..28b01d2b9a63a688ee5af0612ced813266ee1c6e 100644 (file)
@@ -161,7 +161,6 @@ KillWho kill_who_from_string(const char *s) _pure_;
 const char* tty_validity_to_string(TTYValidity t) _const_;
 TTYValidity tty_validity_from_string(const char *s) _pure_;
 
-int session_prepare_vt(Session *s);
 void session_leave_vt(Session *s);
 
 bool session_is_controller(Session *s, const char *sender);
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 49ef022e32cfbe4e87954dcb08ba3e0ea35ea838..402d1acd4b2981c892681d240572e34981ec0539 100644 (file)
@@ -546,6 +546,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
         int r;
 
         assert(link);
+        assert(link->network);
         assert(rt);
 
         r = sd_ndisc_router_option_rewind(rt);
@@ -564,8 +565,24 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
                 switch (type) {
 
                 case SD_NDISC_OPTION_PREFIX_INFORMATION: {
+                        union in_addr_union a;
                         uint8_t flags;
 
+                        r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+
+                        if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
+                                if (DEBUG_LOGGING) {
+                                        _cleanup_free_ char *b = NULL;
+
+                                        (void) in_addr_to_string(AF_INET6, &a, &b);
+                                        log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
+                                }
+
+                                break;
+                        }
+
                         r = sd_ndisc_router_prefix_get_flags(rt, &flags);
                         if (r < 0)
                                 return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
@@ -602,46 +619,6 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
         return 0;
 }
 
-static int ndisc_prefix_is_black_listed(Link *link, sd_ndisc_router *rt) {
-        int r;
-
-        assert(link);
-        assert(link->network);
-        assert(rt);
-
-        for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
-                union in_addr_union a;
-                uint8_t type;
-
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
-                if (r == 0) /* EOF */
-                        return false;
-
-                r = sd_ndisc_router_option_get_type(rt, &type);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
-
-                if (type != SD_NDISC_OPTION_PREFIX_INFORMATION)
-                        continue;
-
-                r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to get prefix address: %m");
-
-                if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
-                        if (DEBUG_LOGGING) {
-                                _cleanup_free_ char *b = NULL;
-
-                                (void) in_addr_to_string(AF_INET6, &a, &b);
-                                log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
-                        }
-
-                        return true;
-                }
-        }
-}
-
 static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         uint64_t flags;
         int r;
@@ -666,10 +643,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
                 }
         }
 
-        if (ndisc_prefix_is_black_listed(link, rt) == 0) {
-                (void) ndisc_router_process_default(link, rt);
-                (void) ndisc_router_process_options(link, rt);
-        }
+        (void) ndisc_router_process_default(link, rt);
+        (void) ndisc_router_process_options(link, rt);
 
         return r;
 }
index 220564c5eb407580981a4772ccd0d1b4c1f16ba5..fb80a47cfd7592755432f3f35b26f0e37588a0b9 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 f18d611b36c14ad784923f204ab6f1774cd3fced..cd63bb5f7285ebd86173a91da0891442b0ad369d 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,8 +458,9 @@ int network_load_one(Manager *manager, const char *filename) {
                               "IPv6AddressLabel\0"
                               "RoutingPolicyRule\0"
                               "Route\0"
-                              "DHCP\0"
-                              "DHCPv4\0" /* compat */
+                              "NextHop\0"
+                              "DHCP\0" /* compat */
+                              "DHCPv4\0"
                               "DHCPv6\0"
                               "DHCPServer\0"
                               "IPv6AcceptRA\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 86135c62e5b3d6bdd5c7531444519b255a4f74fe..691e2b3959fb2073789419e0b9e61f8d4463a341 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"
@@ -229,6 +230,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);
@@ -239,6 +241,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;
@@ -249,6 +252,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 d37880cfd1d8fbe27666fdfdb26cd7a6ee2a4aa7..34b123e846925b4a5d62ccc83a22d9712f8fb154 100644 (file)
@@ -1187,7 +1187,7 @@ int portable_detach(
                 r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name);
-                if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME))
+                if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME, UNIT_FILE_LINKED_RUNTIME))
                         return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is in state '%s', can't detach.", de->d_name, unit_file_state_to_string(state));
 
                 r = unit_file_is_active(bus, de->d_name, error);
index 0992ffac3a80e3414dd44eb165663afdfa823f80..cdd30e1e2d3fe9a659cb3ff7066b7c1eca456805 100644 (file)
@@ -419,21 +419,17 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         int r;
 
         if (STR_IN_SET(field, "DevicePolicy", "Slice"))
-
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field,
                        "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting",
                        "TasksAccounting", "IPAccounting"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight"))
-
                 return bus_append_cg_weight_parse(m, field, eq);
 
         if (STR_IN_SET(field, "CPUShares", "StartupCPUShares"))
-
                 return bus_append_cg_cpu_shares_parse(m, field, eq);
 
         if (STR_IN_SET(field, "AllowedCPUs", "AllowedMemoryNodes")) {
@@ -453,15 +449,12 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         }
 
         if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight"))
-
                 return bus_append_cg_blkio_weight_parse(m, field, eq);
 
         if (streq(field, "DisableControllers"))
-
                 return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
 
         if (streq(field, "Delegate")) {
-
                 r = parse_boolean(eq);
                 if (r < 0)
                         return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
@@ -473,7 +466,15 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                 return 1;
         }
 
-        if (STR_IN_SET(field, "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) {
+        if (STR_IN_SET(field, "MemoryMin",
+                              "DefaultMemoryLow",
+                              "DefaultMemoryMin",
+                              "MemoryLow",
+                              "MemoryHigh",
+                              "MemoryMax",
+                              "MemorySwapMax",
+                              "MemoryLimit",
+                              "TasksMax")) {
 
                 if (isempty(eq) || streq(eq, "infinity")) {
                         r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX);
@@ -505,7 +506,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         }
 
         if (streq(field, "CPUQuota")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
                 else {
@@ -540,7 +540,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         }
 
         if (streq(field, "DeviceAllow")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0);
                 else {
@@ -562,7 +561,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         }
 
         if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
                 else {
@@ -596,7 +594,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
         }
 
         if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0);
                 else {
@@ -792,17 +789,13 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
 }
 
 static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) {
-
         if (streq(field, "Where"))
-
                 return bus_append_string(m, field, eq);
 
         if (streq(field, "DirectoryMode"))
-
                 return bus_append_parse_mode(m, field, eq);
 
         if (streq(field, "TimeoutIdleSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         return 0;
@@ -818,7 +811,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                        "WorkingDirectory", "RootDirectory", "SyslogIdentifier",
                        "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage",
                        "RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath"))
-
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field,
@@ -828,7 +820,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                        "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules",
                        "ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality",
                        "ProtectHostname", "RestrictSUIDSGID"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field,
@@ -836,73 +827,56 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                        "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths",
                        "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
                        "SupplementaryGroups", "SystemCallArchitectures"))
-
                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
 
         if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
-
                 return bus_append_log_level_from_string(m, field, eq);
 
         if (streq(field, "SyslogFacility"))
-
                 return bus_append_log_facility_unshifted_from_string(m, field, eq);
 
         if (streq(field, "SecureBits"))
-
                 return bus_append_secure_bits_from_string(m, field, eq);
 
         if (streq(field, "CPUSchedulingPolicy"))
-
                 return bus_append_sched_policy_from_string(m, field, eq);
 
         if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust"))
-
                 return bus_append_safe_atoi(m, field, eq);
 
         if (streq(field, "Nice"))
-
                 return bus_append_parse_nice(m, field, eq);
 
         if (streq(field, "SystemCallErrorNumber"))
-
                 return bus_append_parse_errno(m, field, eq);
 
         if (streq(field, "IOSchedulingClass"))
-
                 return bus_append_ioprio_class_from_string(m, field, eq);
 
         if (streq(field, "IOSchedulingPriority"))
-
                 return bus_append_ioprio_parse_priority(m, field, eq);
 
         if (STR_IN_SET(field,
                        "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode",
                        "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask"))
-
                 return bus_append_parse_mode(m, field, eq);
 
         if (streq(field, "TimerSlackNSec"))
-
                 return bus_append_parse_nsec(m, field, eq);
 
         if (streq(field, "LogRateLimitIntervalSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (streq(field, "LogRateLimitBurst"))
-
                 return bus_append_safe_atou(m, field, eq);
 
         if (streq(field, "MountFlags"))
-
                 return bus_append_mount_propagation_flags_from_string(m, field, eq);
 
         if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
-
                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
 
         if (streq(field, "EnvironmentFile")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0);
                 else
@@ -916,7 +890,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
         }
 
         if (streq(field, "LogExtraFields")) {
-
                 r = sd_bus_message_open_container(m, 'r', "sv");
                 if (r < 0)
                         return bus_log_create_error(r);
@@ -1354,7 +1327,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 }
 
 static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
-
         if (streq(field, "KillMode"))
                 return bus_append_string(m, field, eq);
 
@@ -1370,19 +1342,15 @@ static int bus_append_kill_property(sd_bus_message *m, const char *field, const
 static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) {
 
         if (STR_IN_SET(field, "What", "Where", "Options", "Type"))
-
                 return bus_append_string(m, field, eq);
 
         if (streq(field, "TimeoutSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (streq(field, "DirectoryMode"))
-
                 return bus_append_parse_mode(m, field, eq);
 
         if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         return 0;
@@ -1392,17 +1360,14 @@ static int bus_append_path_property(sd_bus_message *m, const char *field, const
         int r;
 
         if (streq(field, "MakeDirectory"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (streq(field, "DirectoryMode"))
-
                 return bus_append_parse_mode(m, field, eq);
 
         if (STR_IN_SET(field,
                        "PathExists", "PathExistsGlob", "PathChanged",
                        "PathModified", "DirectoryNotEmpty")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0);
                 else
@@ -1422,19 +1387,15 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
         if (STR_IN_SET(field,
                        "PIDFile", "Type", "Restart", "BusName", "NotifyAccess",
                        "USBFunctionDescriptors", "USBFunctionStrings", "OOMPolicy"))
-
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (streq(field, "TimeoutSec")) {
-
                 r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq);
                 if (r < 0)
                         return r;
@@ -1443,7 +1404,6 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
         }
 
         if (streq(field, "FileDescriptorStoreMax"))
-
                 return bus_append_safe_atou(m, field, eq);
 
         if (STR_IN_SET(field,
@@ -1543,60 +1503,47 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
         if (STR_IN_SET(field,
                        "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
                        "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
-
                 return bus_append_safe_atoi(m, field, eq);
 
         if (streq(field, "IPTOS"))
-
                 return bus_append_ip_tos_from_string(m, field, eq);
 
         if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst"))
-
                 return bus_append_safe_atou(m, field, eq);
 
         if (STR_IN_SET(field, "SocketMode", "DirectoryMode"))
-
                 return bus_append_parse_mode(m, field, eq);
 
         if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize"))
-
                 return bus_append_safe_atoi64(m, field, eq);
 
         if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize"))
-
                 return bus_append_parse_size(m, field, eq, 1024);
 
         if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost"))
-
                 return bus_append_exec_command(m, field, eq);
 
         if (STR_IN_SET(field,
                        "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion",
                        "BindToDevice", "BindIPv6Only", "FileDescriptorName",
                        "SocketUser", "SocketGroup"))
-
                 return bus_append_string(m, field, eq);
 
         if (streq(field, "Symlinks"))
-
                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
 
         if (streq(field, "SocketProtocol"))
-
                 return bus_append_parse_ip_protocol(m, field, eq);
 
         if (STR_IN_SET(field,
                        "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink",
                        "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0);
                 else
@@ -1614,17 +1561,14 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
 
         if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent",
                        "OnTimezoneChange", "OnClockChange"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (STR_IN_SET(field,
                        "OnActiveSec", "OnBootSec", "OnStartupSec",
                        "OnUnitActiveSec","OnUnitInactiveSec")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0);
                 else {
@@ -1642,7 +1586,6 @@ static int bus_append_timer_property(sd_bus_message *m, const char *field, const
         }
 
         if (streq(field, "OnCalendar")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0);
                 else
@@ -1666,25 +1609,20 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
                        "JobTimeoutAction", "JobTimeoutRebootArgument",
                        "StartLimitAction", "FailureAction", "SuccessAction",
                        "RebootArgument", "CollectMode"))
-
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field,
                        "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop",
                        "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies"))
-
                 return bus_append_parse_boolean(m, field, eq);
 
         if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec"))
-
                 return bus_append_parse_sec_rename(m, field, eq);
 
         if (streq(field, "StartLimitBurst"))
-
                 return bus_append_safe_atou(m, field, eq);
 
         if (STR_IN_SET(field, "SuccessActionExitStatus", "FailureActionExitStatus")) {
-
                 if (isempty(eq))
                         r = sd_bus_message_append(m, "(sv)", field, "i", -1);
                 else {
@@ -1704,7 +1642,6 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
 
         if (unit_dependency_from_string(field) >= 0 ||
             STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
-
                 return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
 
         t = condition_type_from_string(field);
index 98a71c446019586e83d7ee60a82f178d1c4544bc..738b9af5363edf84436880fba9b809d1b3a73ada 100644 (file)
@@ -7977,7 +7977,7 @@ static void help_states(void) {
 
 static int help_boot_loader_entry(void) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_free_ char **l = NULL;
+        _cleanup_strv_free_ char **l = NULL;
         sd_bus *bus;
         char **i;
         int r;
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 e5a9400edacc00b0bdd69d4f1a8ac7af1c878bbe..deeb939f54a17ed6b28e20e2a473abd012f03b56 100644 (file)
@@ -425,9 +425,10 @@ int main(int argc, char *argv[]) {
                         export = 1;
                         break;
                 case 'h':
-                        printf("Usage: ata_id [--export] [--help] <device>\n"
+                        printf("Usage: %s [--export] [--help] <device>\n"
                                "  -x,--export    print values as environment keys\n"
-                               "  -h,--help      print this help text\n\n");
+                               "  -h,--help      print this help text\n\n",
+                               program_invocation_short_name);
                         return 0;
                 }
         }
index a287901266baad43068fa335843353bcf1f0a3b1..75afe4e7c9e96cfccbd79b6ac29764c7b4c597b3 100644 (file)
@@ -849,12 +849,13 @@ int main(int argc, char *argv[]) {
                         log_open();
                         break;
                 case 'h':
-                        printf("Usage: cdrom_id [options] <device>\n"
+                        printf("Usage: %s [options] <device>\n"
                                "  -l,--lock-media    lock the media (to enable eject request events)\n"
                                "  -u,--unlock-media  unlock the media\n"
                                "  -e,--eject-media   eject the media\n"
                                "  -d,--debug         debug to stderr\n"
-                               "  -h,--help          print this help text\n\n");
+                               "  -h,--help          print this help text\n\n",
+                               program_invocation_short_name);
                         goto exit;
                 default:
                         rc = 1;
index 5afd5ddb80a8cfda3b422e609a7b7b3f5897e55d..83761d72b4534cbd344a93add82ccc6d7cad8b1a 100644 (file)
@@ -44,7 +44,7 @@ static int run(int argc, char **argv) {
         log_open();
 
         if (argc > 2)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Usage: hidraw_id [SYSFS_PATH]");
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Usage: %s [SYSFS_PATH]", program_invocation_short_name);
 
         if (argc == 1) {
                 r = device_new_from_strv(&device, environ);
index efea16e5c5acd4dddd68accb33298626b3cf26e6..ab54067b3f16232c030b906d6e19a01d71737e92 100644 (file)
@@ -3,6 +3,7 @@
 #include <ctype.h>
 
 #include "alloc-util.h"
+#include "architecture.h"
 #include "conf-files.h"
 #include "def.h"
 #include "device-util.h"
@@ -28,6 +29,7 @@
 #include "udev-event.h"
 #include "udev-rules.h"
 #include "user-util.h"
+#include "virt.h"
 
 #define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d")
 
@@ -69,6 +71,7 @@ typedef enum {
         TK_M_DEVLINK,                       /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */
         TK_M_NAME,                          /* string, name of network interface */
         TK_M_ENV,                           /* string, device property, takes key through attribute */
+        TK_M_CONST,                         /* string, system-specific hard-coded constant */
         TK_M_TAG,                           /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */
         TK_M_SUBSYSTEM,                     /* string, sd_device_get_subsystem() */
         TK_M_DRIVER,                        /* string, sd_device_get_driver() */
@@ -618,6 +621,12 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
                 } else
                         r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
+        } else if (streq(key, "CONST")) {
+                if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
+                        return log_token_invalid_attr(rules, key);
+                if (!is_match)
+                        return log_token_invalid_op(rules, key);
+                r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr);
         } else if (streq(key, "TAG")) {
                 if (attr)
                         return log_token_invalid_attr(rules, key);
@@ -1574,6 +1583,17 @@ static int udev_rule_apply_token_to_event(
                         val = hashmap_get(properties_list, token->data);
 
                 return token_match_string(token, val);
+        case TK_M_CONST: {
+                const char *k = token->data;
+
+                if (streq(k, "arch"))
+                        val = architecture_to_string(uname_architecture());
+                else if (streq(k, "virt"))
+                        val = virtualization_to_string(detect_virtualization());
+                else
+                        assert_not_reached("Invalid CONST key");
+                return token_match_string(token, val);
+        }
         case TK_M_TAG:
         case TK_M_PARENTS_TAG:
                 FOREACH_DEVICE_TAG(dev, val)
@@ -2325,7 +2345,7 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod
 
 static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) {
         UdevRuleToken *token;
-        _cleanup_free_ char **tags = NULL;
+        _cleanup_strv_free_ char **tags = NULL;
         uid_t uid = UID_INVALID;
         gid_t gid = GID_INVALID;
         mode_t mode = MODE_INVALID;
index 63dc8bbdf819c278e2a9c04e23b9044007d63c63..ae8af3aba29c0171539cd7dcf4f65cc3016907c6 100644 (file)
@@ -256,3 +256,6 @@ MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
+[NextHop]
+Id=
+Gateway=
diff --git a/test/test-network/conf/25-nexthop.network b/test/test-network/conf/25-nexthop.network
new file mode 100644 (file)
index 0000000..3eea077
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=no
+Address=192.168.5.10/24
+Gateway=192.168.5.1
+
+[NextHop]
+Id=1
+Gateway=192.168.5.1
diff --git a/test/test-network/conf/25-veth-peer.network b/test/test-network/conf/25-veth-peer.network
new file mode 100644 (file)
index 0000000..f24956f
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=veth-peer
+
+[Network]
+IPv6AcceptRA=no
+Address=2600::1/0
+Address=192.168.5.1/24
index 87a76a502c0f25511116e76ad0c6eadacc43350b..d1009217095c18b5a21410894e6bb7f9c3daad37 100755 (executable)
@@ -113,6 +113,16 @@ def expectedFailureIfLinkFileFieldIsNotSet():
 
     return f
 
+def expectedFailureIfNexthopIsNotAvailable():
+    def f(func):
+        rc = call('ip nexthop list')
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
 def setUpModule():
     global running_units
 
@@ -1402,7 +1412,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         'dummy99',
         'gretun97',
         'ip6gretun97',
-        'test1'
+        'test1',
+        'veth99',
     ]
 
     units = [
@@ -1426,6 +1437,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-neighbor-ipv6.network',
         '25-neighbor-ip-dummy.network',
         '25-neighbor-ip.network',
+        '25-nexthop.network',
         '25-link-local-addressing-no.network',
         '25-link-local-addressing-yes.network',
         '25-link-section-unmanaged.network',
@@ -1435,6 +1447,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-gateway-next-static.network',
         '25-sysctl-disable-ipv6.network',
         '25-sysctl.network',
+        '25-veth-peer.network',
+        '25-veth.netdev',
         '26-link-local-addressing-ipv6.network',
         'configure-without-carrier.network',
         'routing-policy-rule-dummy98.network',
@@ -1974,6 +1988,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
         self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
 
+    @expectedFailureIfNexthopIsNotAvailable()
+    def test_nexthop(self):
+        copy_unit_to_networkd_unit_path('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network')
+        start_networkd()
+        self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        output = check_output('ip nexthop list dev veth99')
+        print(output)
+        self.assertRegex(output, '192.168.5.1')
+
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
     links = [
         'dummy98',