]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #12489 from ssahani/vxlan
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 8 May 2019 10:02:54 +0000 (12:02 +0200)
committerGitHub <noreply@github.com>
Wed, 8 May 2019 10:02:54 +0000 (12:02 +0200)
networkd: VXLAN rename Id to VNI

34 files changed:
man/systemd.network.xml
meson.build
src/basic/macro.h
src/basic/random-util.c
src/basic/random-util.h
src/basic/string-util.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-radv.c
src/network/networkctl.c
src/network/networkd-address-pool.c
src/network/networkd-address.c
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-fdb.c
src/network/networkd-fdb.h
src/network/networkd-ipv4ll.c
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-bus.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/test-network.c
src/network/test-networkd-conf.c
src/shared/utmp-wtmp.c
src/systemd/sd-dhcp-client.h
src/udev/scsi_id/scsi_serial.c
test/fuzz/fuzz-network-parser/directives.network
test/test-network/conf/dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network [moved from test/test-network/conf/dhcp-client-with-ipv4ll-fallback.network with 100% similarity]
test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index b86bcf03ef8c9277e309fb1fa4eae75289a0ea1c..f24bf401595b5c5276a287db79679f416a88bfdc 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>MaxAttempts=</varname></term>
+          <listitem>
+            <para>Specifies how many times the DHCPv4 client configuration should be attempted. Takes a
+            number or <literal>infinity</literal>. Defaults to <literal>infinity</literal>.
+            Note that the time between retries is increased exponentially, so the network will not be
+            overloaded even if this number is high.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>DUIDType=</varname></term>
           <listitem>
             table entry.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>VNI=</varname></term>
+          <listitem>
+            <para>The VXLAN Network Identifier (or VXLAN Segment ID) to use to connect to
+            the remote VXLAN tunnel endpoint. Takes a number in the range 1-16777215.
+            Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
index 24ef643d44e92a399cd6e1fb8c2cb3df35de6713..b5d06e422621e5cac2913bbb63102460e9c5470a 100644 (file)
@@ -411,11 +411,14 @@ endif
 
 cpp = ' '.join(cc.cmd_array()) + ' -E'
 
+has_wstringop_truncation = cc.has_argument('-Wstringop-truncation')
+
 #####################################################################
 # compilation result tests
 
 conf.set('_GNU_SOURCE', true)
 conf.set('__SANE_USERSPACE_TYPES__', true)
+conf.set10('HAVE_WSTRINGOP_TRUNCATION', has_wstringop_truncation)
 
 conf.set('SIZEOF_PID_T', cc.sizeof('pid_t', prefix : '#include <sys/types.h>'))
 conf.set('SIZEOF_UID_T', cc.sizeof('uid_t', prefix : '#include <sys/types.h>'))
index 1971e912db4f497e676af561733f98286397e4ad..ae8907db04bc719ecd13c16cbe64d2a570c77dc6 100644 (file)
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
 
+#if HAVE_WSTRINGOP_TRUNCATION
+#  define DISABLE_WARNING_STRINGOP_TRUNCATION                           \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wstringop-truncation\"")
+#else
+#  define DISABLE_WARNING_STRINGOP_TRUNCATION                           \
+        _Pragma("GCC diagnostic push")
+#endif
+
 #define REENABLE_WARNING                                                \
         _Pragma("GCC diagnostic pop")
 
index ca25fd2420c4de14d1c47e43eebe06b3889cd1d5..7c648575927395ba4bed50d2114a6706eecc7924 100644 (file)
 #include "io-util.h"
 #include "missing.h"
 #include "random-util.h"
+#include "siphash24.h"
 #include "time-util.h"
 
 int rdrand(unsigned long *ret) {
 
 #if defined(__i386__) || defined(__x86_64__)
         static int have_rdrand = -1;
-        unsigned char err;
+        uint8_t success;
 
         if (have_rdrand < 0) {
                 uint32_t eax, ebx, ecx, edx;
@@ -45,7 +46,12 @@ int rdrand(unsigned long *ret) {
                         return -EOPNOTSUPP;
                 }
 
-                have_rdrand = !!(ecx & (1U << 30));
+/* Compat with old gcc where bit_RDRND didn't exist yet */
+#ifndef bit_RDRND
+#define bit_RDRND (1U << 30)
+#endif
+
+                have_rdrand = !!(ecx & bit_RDRND);
         }
 
         if (have_rdrand == 0)
@@ -54,9 +60,9 @@ int rdrand(unsigned long *ret) {
         asm volatile("rdrand %0;"
                      "setc %1"
                      : "=r" (*ret),
-                       "=qm" (err));
-        msan_unpoison(&err, sizeof(err));
-        if (!err)
+                       "=qm" (success));
+        msan_unpoison(&success, sizeof(sucess));
+        if (!success)
                 return -EAGAIN;
 
         return 0;
@@ -71,21 +77,22 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
         bool got_some = false;
         int r;
 
-        /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't
-         * block, unless the RANDOM_BLOCK flag is set. If RANDOM_DONT_DRAIN is set, an error is returned if the random
-         * pool is not initialized. Otherwise it will always return some data from the kernel, regardless of whether
-         * the random pool is fully initialized or not. */
+        /* Gathers some randomness from the kernel (or the CPU if the RANDOM_ALLOW_RDRAND flag is set). This
+         * call won't block, unless the RANDOM_BLOCK flag is set. If RANDOM_MAY_FAIL is set, an error is
+         * returned if the random pool is not initialized. Otherwise it will always return some data from the
+         * kernel, regardless of whether the random pool is fully initialized or not. */
 
         if (n == 0)
                 return 0;
 
         if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND))
-                /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is not
-                 * required, as we don't trust it (who does?). Note that we only do a single iteration of RDRAND here,
-                 * even though the Intel docs suggest calling this in a tight loop of 10 invocations or so. That's
-                 * because we don't really care about the quality here. We generally prefer using RDRAND if the caller
-                 * allows us too, since this way we won't drain the kernel randomness pool if we don't need it, as the
-                 * pool's entropy is scarce. */
+                /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is
+                 * not required, as we don't trust it (who does?). Note that we only do a single iteration of
+                 * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10
+                 * invocations or so. That's because we don't really care about the quality here. We
+                 * generally prefer using RDRAND if the caller allows us to, since this way we won't upset
+                 * the kernel's random subsystem by accessing it before the pool is initialized (after all it
+                 * will kmsg log about every attempt to do so)..*/
                 for (;;) {
                         unsigned long u;
                         size_t m;
@@ -153,12 +160,13 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
                                 break;
 
                         } else if (errno == EAGAIN) {
-                                /* The kernel has no entropy whatsoever. Let's remember to use the syscall the next
-                                 * time again though.
+                                /* The kernel has no entropy whatsoever. Let's remember to use the syscall
+                                 * the next time again though.
                                  *
-                                 * If RANDOM_DONT_DRAIN is set, return an error so that random_bytes() can produce some
-                                 * pseudo-random bytes instead. Otherwise, fall back to /dev/urandom, which we know is empty,
-                                 * but the kernel will produce some bytes for us on a best-effort basis. */
+                                 * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can
+                                 * produce some pseudo-random bytes instead. Otherwise, fall back to
+                                 * /dev/urandom, which we know is empty, but the kernel will produce some
+                                 * bytes for us on a best-effort basis. */
                                 have_syscall = true;
 
                                 if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
@@ -167,7 +175,7 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
                                         return 0;
                                 }
 
-                                if (FLAGS_SET(flags, RANDOM_DONT_DRAIN))
+                                if (FLAGS_SET(flags, RANDOM_MAY_FAIL))
                                         return -ENODATA;
 
                                 /* Use /dev/urandom instead */
@@ -196,14 +204,19 @@ void initialize_srand(void) {
                 return;
 
 #if HAVE_SYS_AUXV_H
-        /* The kernel provides us with 16 bytes of entropy in auxv, so let's
-         * try to make use of that to seed the pseudo-random generator. It's
-         * better than nothing... */
+        /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed
+         * the pseudo-random generator. It's better than nothing... But let's first hash it to make it harder
+         * to recover the original value by watching any pseudo-random bits we generate. After all the
+         * AT_RANDOM data might be used by other stuff too (in particular: ASLR), and we probably shouldn't
+         * leak the seed for that. */
 
-        auxv = (const void*) getauxval(AT_RANDOM);
+        auxv = ULONG_TO_PTR(getauxval(AT_RANDOM));
         if (auxv) {
-                assert_cc(sizeof(x) <= 16);
-                memcpy(&x, auxv, sizeof(x));
+                static const uint8_t auxval_hash_key[16] = {
+                        0x92, 0x6e, 0xfe, 0x1b, 0xcf, 0x00, 0x52, 0x9c, 0xcc, 0x42, 0xcf, 0xdc, 0x94, 0x1f, 0x81, 0x0f
+                };
+
+                x = (unsigned) siphash24(auxv, 16, auxval_hash_key);
         } else
 #endif
                 x = 0;
@@ -250,7 +263,7 @@ void pseudo_random_bytes(void *p, size_t n) {
 
 void random_bytes(void *p, size_t n) {
 
-        if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_DONT_DRAIN|RANDOM_ALLOW_RDRAND) >= 0)
+        if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0)
                 return;
 
         /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
index 3e8c288d3d00f38c077bb205b6218d93f33cecb6..148b6c7813ddd7ae845917f6f8dc9dc95ec50605 100644 (file)
@@ -8,11 +8,11 @@
 typedef enum RandomFlags {
         RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */
         RANDOM_BLOCK              = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
-        RANDOM_DONT_DRAIN         = 1 << 2, /* If we can't get any randomness at all, return early with -EAGAIN */
+        RANDOM_MAY_FAIL           = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */
         RANDOM_ALLOW_RDRAND       = 1 << 3, /* Allow usage of the CPU RNG */
 } RandomFlags;
 
-int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled upwith pseudo random, if not enough is available */
+int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */
 void pseudo_random_bytes(void *p, size_t n);                    /* returns only pseudo-randommess (but possibly seeded from something better) */
 void random_bytes(void *p, size_t n);                           /* returns genuine randomness if cheaply available, and pseudo randomness if not. */
 
index b23f4c834140978b0e6f953721cf72917ebad5a4..a630856236a9a699be23f14a12d7cdbaadd4ff9d 100644 (file)
@@ -212,6 +212,12 @@ static inline size_t strlen_ptr(const char *s) {
         return strlen(s);
 }
 
+DISABLE_WARNING_STRINGOP_TRUNCATION;
+static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
+        strncpy(buf, src, buf_len);
+}
+REENABLE_WARNING;
+
 /* Like startswith(), but operates on arbitrary memory blocks */
 static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
         size_t n;
index f10ae3ed3b553a7cd552b1c26546f6e8d5a8c2c7..33a8ef799975bef06b05b11b1b87d41427efb3f1 100644 (file)
@@ -32,8 +32,6 @@
 #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
 #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
 
-#define MAX_CLIENT_ATTEMPT 64
-
 #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
 #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
 
@@ -90,7 +88,8 @@ struct sd_dhcp_client {
         uint32_t mtu;
         uint32_t xid;
         usec_t start_time;
-        unsigned attempt;
+        uint64_t attempt;
+        uint64_t max_attempts;
         usec_t request_sent;
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
@@ -522,6 +521,14 @@ int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
         return 0;
 }
 
+int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
+        assert_return(client, -EINVAL);
+
+        client->max_attempts = max_attempts;
+
+        return 0;
+}
+
 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -553,7 +560,7 @@ static int client_initialize(sd_dhcp_client *client) {
         (void) event_source_disable(client->timeout_t2);
         (void) event_source_disable(client->timeout_expire);
 
-        client->attempt = 1;
+        client->attempt = 0;
 
         client->state = DHCP_STATE_INIT;
         client->xid = 0;
@@ -1052,12 +1059,12 @@ static int client_timeout_resend(
         case DHCP_STATE_REQUESTING:
         case DHCP_STATE_BOUND:
 
-                if (client->attempt < MAX_CLIENT_ATTEMPT)
-                        client->attempt *= 2;
+                if (client->attempt < client->max_attempts)
+                        client->attempt++;
                 else
                         goto error;
 
-                next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
+                next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC;
 
                 break;
 
@@ -1081,17 +1088,15 @@ static int client_timeout_resend(
                 r = client_send_discover(client);
                 if (r >= 0) {
                         client->state = DHCP_STATE_SELECTING;
-                        client->attempt = 1;
-                } else {
-                        if (client->attempt >= MAX_CLIENT_ATTEMPT)
-                                goto error;
-                }
+                        client->attempt = 0;
+                } else if (client->attempt >= client->max_attempts)
+                        goto error;
 
                 break;
 
         case DHCP_STATE_SELECTING:
                 r = client_send_discover(client);
-                if (r < 0 && client->attempt >= MAX_CLIENT_ATTEMPT)
+                if (r < 0 && client->attempt >= client->max_attempts)
                         goto error;
 
                 break;
@@ -1101,7 +1106,7 @@ static int client_timeout_resend(
         case DHCP_STATE_RENEWING:
         case DHCP_STATE_REBINDING:
                 r = client_send_request(client);
-                if (r < 0 && client->attempt >= MAX_CLIENT_ATTEMPT)
+                if (r < 0 && client->attempt >= client->max_attempts)
                          goto error;
 
                 if (client->state == DHCP_STATE_INIT_REBOOT)
@@ -1253,7 +1258,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
         client->fd = asynchronous_close(client->fd);
 
         client->state = DHCP_STATE_REBINDING;
-        client->attempt = 1;
+        client->attempt = 0;
 
         r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
                                          client->xid, client->mac_addr,
@@ -1273,7 +1278,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
         DHCP_CLIENT_DONT_DESTROY(client);
 
         client->state = DHCP_STATE_RENEWING;
-        client->attempt = 1;
+        client->attempt = 0;
 
         return client_initialize_time_events(client);
 }
@@ -1557,7 +1562,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
                 if (r >= 0) {
 
                         client->state = DHCP_STATE_REQUESTING;
-                        client->attempt = 1;
+                        client->attempt = 0;
 
                         r = event_reset_time(client->event, &client->timeout_resend,
                                              clock_boottime_or_monotonic(),
@@ -1592,7 +1597,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
                                 notify_event = r;
 
                         client->state = DHCP_STATE_BOUND;
-                        client->attempt = 1;
+                        client->attempt = 0;
 
                         client->last_addr = client->lease->address;
 
@@ -1931,10 +1936,10 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
                 .state = DHCP_STATE_INIT,
                 .ifindex = -1,
                 .fd = -1,
-                .attempt = 1,
                 .mtu = DHCP_DEFAULT_MIN_SIZE,
                 .port = DHCP_PORT_CLIENT,
                 .anonymize = !!anonymize,
+                .max_attempts = (uint64_t) -1,
         };
         /* NOTE: this could be moved to a function. */
         if (anonymize) {
index 08433adb25c911c5aa2ea63b5cdac3e1b5096fb7..185b55e1c56ca35754fa1d1f16cb27f24399e2b6 100644 (file)
@@ -598,6 +598,7 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra,
 
                 LIST_REMOVE(prefix, ra->prefixes, cur);
                 ra->n_prefixes--;
+                sd_radv_prefix_unref(cur);
 
                 break;
         }
index 3881aaa42b577b43c4d484f448445fb7cce9ff50..eb3521b7940ecd2b4b9e0d6e4e912a416c99c5ff 100644 (file)
@@ -320,8 +320,8 @@ static int get_gateway_description(
                 return r;
 
         for (m = reply; m; m = sd_netlink_message_next(m)) {
-                union in_addr_union gw = {};
-                struct ether_addr mac = {};
+                union in_addr_union gw = IN_ADDR_NULL;
+                struct ether_addr mac = ETHER_ADDR_NULL;
                 uint16_t type;
                 int ifi, fam;
 
@@ -514,7 +514,7 @@ static int dump_address_labels(sd_netlink *rtnl) {
 
         for (m = reply; m; m = sd_netlink_message_next(m)) {
                 _cleanup_free_ char *pretty = NULL;
-                union in_addr_union prefix = {};
+                union in_addr_union prefix = IN_ADDR_NULL;
                 uint8_t prefixlen;
                 uint32_t label;
 
index eaf056d11864d2625ff1c7cc3002448ee10da4d3..db6c1456dc7e5c20b70783ffae1a29b26b8a8e58 100644 (file)
@@ -106,7 +106,7 @@ static bool address_pool_prefix_is_taken(
         }
 
         /* And don't clash with configured but un-assigned addresses either */
-        LIST_FOREACH(networks, n, p->manager->networks) {
+        ORDERED_HASHMAP_FOREACH(n, p->manager->networks, i) {
                 Address *a;
 
                 LIST_FOREACH(addresses, a, n->static_addresses) {
index 42d61cc0e50569eb5afcf93a18b9137ee914584f..600bad474dcf507d18289cff93f64a525da9cac2 100644 (file)
@@ -490,7 +490,7 @@ int address_remove(
 }
 
 static int address_acquire(Link *link, Address *original, Address **ret) {
-        union in_addr_union in_addr = {};
+        union in_addr_union in_addr = IN_ADDR_NULL;
         struct in_addr broadcast = {};
         _cleanup_(address_freep) Address *na = NULL;
         int r;
index c630359027bce8285a3903dc7caa456aaddf68c0..01f4e955c85e9b899cf4e4b1c0e45866de9ec3b7 100644 (file)
@@ -533,7 +533,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
 
                                 r = sd_ipv4ll_start(link->ipv4ll);
                                 if (r < 0) {
-                                        log_link_warning(link, "Could not acquire IPv4 link-local address: %m");
+                                        log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
                                         return;
                                 }
                         }
@@ -833,5 +833,11 @@ int dhcp4_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m");
         }
 
+        if (link->network->dhcp_max_attempts > 0) {
+                r = sd_dhcp_client_set_max_attempts(link->dhcp_client, link->network->dhcp_max_attempts);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m");
+        }
+
         return dhcp4_set_client_identifier(link);
 }
index ba795decfecf61fb99cfa5fc6ddb425248d253ce..7db59d3acad6176deff08ece300aa23357797d8e 100644 (file)
@@ -90,7 +90,7 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
         if (r < 0 && r != -EEXIST)
                 return r;
 
-        r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link);
+        r = manager_dhcp6_prefix_add(link->manager, prefix, link);
         if (r < 0)
                 return r;
 
@@ -126,47 +126,33 @@ int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
                                      &lifetime_preferred,
                                      &lifetime_valid) >= 0) {
                 _cleanup_free_ char *buf = NULL;
-                _cleanup_free_ Route *route = NULL;
+                Route *route;
 
-                if (pd_prefix_len > 64)
+                if (pd_prefix_len >= 64)
                         continue;
 
                 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
 
-                if (pd_prefix_len < 64) {
-                        r = route_new(&route);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
-                                                       strnull(buf),
-                                                       pd_prefix_len);
-                                continue;
-                        }
-
-                        r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
-                                                       strnull(buf),
-                                                       pd_prefix_len);
-                                continue;
-                        }
-
-                        route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
-
-                        r = route_remove(route, link, dhcp6_route_remove_handler);
-                        if (r < 0) {
-                                (void) in_addr_to_string(AF_INET6,
-                                                         &pd_prefix, &buf);
-
-                                log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
-                                                       strnull(buf),
-                                                       pd_prefix_len);
+                r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
+                                               strnull(buf),
+                                               pd_prefix_len);
+                        continue;
+                }
 
-                                continue;
-                        }
+                route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
 
-                        log_link_debug(link, "Removing unreachable route %s/%u",
-                                       strnull(buf), pd_prefix_len);
+                r = route_remove(route, link, dhcp6_route_remove_handler);
+                if (r < 0) {
+                        log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
+                                               strnull(buf),
+                                               pd_prefix_len);
+                        continue;
                 }
+
+                log_link_debug(link, "Removing unreachable route %s/%u",
+                               strnull(buf), pd_prefix_len);
         }
 
         return 0;
@@ -217,7 +203,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
                         continue;
 
                 assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
-                if (assigned_link != NULL && assigned_link != link)
+                if (assigned_link && assigned_link != link)
                         continue;
 
                 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
@@ -266,7 +252,6 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
         union in_addr_union pd_prefix;
         uint8_t pd_prefix_len;
         uint32_t lifetime_preferred, lifetime_valid;
-        _cleanup_free_ char *buf = NULL;
         Iterator i = ITERATOR_FIRST;
 
         r = sd_dhcp6_client_get_lease(client, &lease);
@@ -279,32 +264,23 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
                                      &lifetime_preferred,
                                      &lifetime_valid) >= 0) {
 
+                _cleanup_free_ char *buf = NULL;
+
+                (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+
                 if (pd_prefix_len > 64) {
-                        (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
                         log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
                                        strnull(buf), pd_prefix_len);
                         continue;
                 }
 
-                if (pd_prefix_len < 48) {
-                        (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+                if (pd_prefix_len < 48)
                         log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
                                        strnull(buf), pd_prefix_len);
-                }
 
                 if (pd_prefix_len < 64) {
-                        _cleanup_(route_freep) Route *route = NULL;
                         uint32_t table;
-
-                        (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
-
-                        r = route_new(&route);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m",
-                                                       strnull(buf),
-                                                       pd_prefix_len);
-                                continue;
-                        }
+                        Route *route;
 
                         table = link_get_dhcp_route_table(link);
 
@@ -328,7 +304,6 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
 
                         log_link_debug(link, "Configuring unreachable route for %s/%u",
                                        strnull(buf), pd_prefix_len);
-
                 } else
                         log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
 
index 6ebc45267fcc1a4077e282be10448f7cf240035b..fdac574d74fce76abbb247f49776b35610ed51ce 100644 (file)
@@ -9,9 +9,12 @@
 #include "alloc-util.h"
 #include "conf-parser.h"
 #include "netdev/bridge.h"
+#include "netdev/vxlan.h"
 #include "netlink-util.h"
 #include "networkd-fdb.h"
 #include "networkd-manager.h"
+#include "parse-util.h"
+#include "string-util.h"
 #include "util.h"
 #include "vlan-util.h"
 
@@ -64,6 +67,7 @@ static int fdb_entry_new_static(
         *fdb_entry = (FdbEntry) {
                 .network = network,
                 .mac_addr = TAKE_PTR(mac_addr),
+                .vni = VXLAN_VID_MAX + 1,
         };
 
         LIST_PREPEND(static_fdb_entries, network->static_fdb_entries, fdb_entry);
@@ -103,9 +107,9 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
 int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         sd_netlink *rtnl;
-        int r;
-        uint8_t flags;
         Bridge *bridge;
+        uint8_t flags;
+        int r;
 
         assert(link);
         assert(link->network);
@@ -151,6 +155,12 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
                         return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
         }
 
+        if (fdb_entry->vni <= VXLAN_VID_MAX) {
+                r = sd_netlink_message_append_u32(req, NDA_VNI, fdb_entry->vni);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m");
+        }
+
         /* send message to the kernel to update its internal static MAC table. */
         r = netlink_call_async(rtnl, NULL, req, set_fdb_handler,
                                link_netlink_destroy_callback, link);
@@ -301,3 +311,51 @@ int config_parse_fdb_destination(
 
         return 0;
 }
+
+int config_parse_fdb_vxlan_vni(
+                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_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL;
+        Network *network = userdata;
+        uint32_t vni;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = fdb_entry_new_static(network, filename, section_line, &fdb_entry);
+        if (r < 0)
+                return log_oom();
+
+        r = safe_atou32(rvalue, &vni);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        if (vni > VXLAN_VID_MAX) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        fdb_entry->vni = vni;
+        fdb_entry = NULL;
+
+        return 0;
+}
index e3c45acd72d0406550fd3a281e2731485ccf73f0..6954e555fa413186d4cfc24b1244e3a280e6c252 100644 (file)
@@ -19,6 +19,8 @@ struct FdbEntry {
         Network *network;
         NetworkConfigSection *section;
 
+        uint32_t vni;
+
         int family;
         uint16_t vlan_id;
 
@@ -36,3 +38,4 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free);
 CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr);
 CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id);
 CONFIG_PARSER_PROTOTYPE(config_parse_fdb_destination);
+CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vxlan_vni);
index fb4bf266a6e124e977598014c76d6ef2008163d2..79e90f2f5af640b254857688144efd2cf92f8888 100644 (file)
@@ -175,7 +175,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
 
                         r = sd_ipv4ll_restart(ll);
                         if (r < 0)
-                                log_link_warning(link, "Could not acquire IPv4 link-local address");
+                                log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
                         break;
                 case SD_IPV4LL_EVENT_BIND:
                         r = ipv4ll_address_claimed(ll, link);
index 533193ac9327b397a16173b344eef06169c60c51..3c8b5c5cb436e76d2eda1f258572fb63853579ad 100644 (file)
@@ -338,8 +338,7 @@ static int link_enable_ipv6(Link *link) {
 
         r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled);
         if (r < 0)
-                log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m",
-                                       enable_disable(!disabled), link->ifname);
+                log_link_warning_errno(link, r, "Cannot %s IPv6: %m", enable_disable(!disabled));
         else
                 log_link_info(link, "IPv6 successfully %sd", enable_disable(!disabled));
 
@@ -524,8 +523,6 @@ static int link_update_flags(Link *link, sd_netlink_message *m) {
         return 0;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
-
 static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         _cleanup_(link_unrefp) Link *link = NULL;
         uint16_t type;
@@ -624,30 +621,15 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
 }
 
 static Link *link_free(Link *link) {
-        Link *carrier;
         Address *address;
-        Route *route;
-        Iterator i;
 
         assert(link);
 
-        while ((route = set_first(link->routes)))
-                route_free(route);
-
-        while ((route = set_first(link->routes_foreign)))
-                route_free(route);
-
-        link->routes = set_free(link->routes);
-        link->routes_foreign = set_free(link->routes_foreign);
+        link->routes = set_free_with_destructor(link->routes, route_free);
+        link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
 
-        while ((address = set_first(link->addresses)))
-                address_free(address);
-
-        while ((address = set_first(link->addresses_foreign)))
-                address_free(address);
-
-        link->addresses = set_free(link->addresses);
-        link->addresses_foreign = set_free(link->addresses_foreign);
+        link->addresses = set_free_with_destructor(link->addresses, address_free);
+        link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
 
         while ((address = link->pool_addresses)) {
                 LIST_REMOVE(addresses, link->pool_addresses, address);
@@ -680,16 +662,13 @@ static Link *link_free(Link *link) {
 
         sd_device_unref(link->sd_device);
 
-        HASHMAP_FOREACH (carrier, link->bound_to_links, i)
-                hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
         hashmap_free(link->bound_to_links);
-
-        HASHMAP_FOREACH (carrier, link->bound_by_links, i)
-                hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
         hashmap_free(link->bound_by_links);
 
         set_free_with_destructor(link->slaves, link_unref);
 
+        network_unref(link->network);
+
         return mfree(link);
 }
 
index 1366a299242b0947d90aa258a70df9bb75dfb841..930dd25f924d52a5a5d18c0c698da7917f83496d 100644 (file)
@@ -132,6 +132,7 @@ int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *re
 
 Link *link_unref(Link *link);
 Link *link_ref(Link *link);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
 DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref);
 
 int link_get(Manager *m, int ifindex, Link **ret);
index 8c461a89e047001c110d0ae68336b9515be64852..a842d131a53f809861e690a034855234d41a207c 100644 (file)
@@ -266,7 +266,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         unsigned char protocol, scope, tos, table, rt_type;
         int family;
         unsigned char dst_prefixlen, src_prefixlen;
-        union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
+        union in_addr_union dst = IN_ADDR_NULL, gw = IN_ADDR_NULL, src = IN_ADDR_NULL, prefsrc = IN_ADDR_NULL;
         Route *route = NULL;
         int r;
 
@@ -484,7 +484,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         int family;
         unsigned char prefixlen;
         unsigned char scope;
-        union in_addr_union in_addr;
+        union in_addr_union in_addr = IN_ADDR_NULL;
         struct ifa_cacheinfo cinfo;
         Address *address = NULL;
         char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
@@ -728,7 +728,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
 int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
         uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0;
         struct fib_rule_port_range sport = {}, dport = {};
-        union in_addr_union to = {}, from = {};
+        union in_addr_union to = IN_ADDR_NULL, from = IN_ADDR_NULL;
         RoutingPolicyRule *rule = NULL;
         uint32_t fwmark = 0, table = 0;
         const char *iif = NULL, *oif = NULL;
@@ -1279,7 +1279,9 @@ static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in
 DEFINE_PRIVATE_HASH_OPS(dhcp6_prefixes_hash_ops, struct in6_addr, dhcp6_prefixes_hash_func, dhcp6_prefixes_compare_func);
 
 int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
+        _cleanup_free_ struct in6_addr *a = NULL;
         _cleanup_free_ char *buf = NULL;
+        Link *assigned_link;
         Route *route;
         int r;
 
@@ -1298,11 +1300,27 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
         log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
 
+        assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
+        if (assigned_link) {
+                assert(assigned_link == link);
+                return 0;
+        }
+
+        a = newdup(struct in6_addr, addr, 1);
+        if (!a)
+                return -ENOMEM;
+
         r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops);
         if (r < 0)
                 return r;
 
-        return hashmap_put(m->dhcp6_prefixes, addr, link);
+        r = hashmap_put(m->dhcp6_prefixes, a, link);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(a);
+        link_ref(link);
+        return 0;
 }
 
 static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
@@ -1318,21 +1336,21 @@ static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Lin
 }
 
 static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+        _cleanup_free_ struct in6_addr *a = NULL;
+        _cleanup_(link_unrefp) Link *l = NULL;
         _cleanup_free_ char *buf = NULL;
         Route *route;
-        Link *l;
         int r;
 
         assert_return(m, -EINVAL);
         assert_return(addr, -EINVAL);
 
-        l = hashmap_remove(m->dhcp6_prefixes, addr);
+        l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
         if (!l)
                 return -EINVAL;
 
         (void) sd_radv_remove_prefix(l->radv, addr, 64);
-        r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64,
-                      0, 0, 0, &route);
+        r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, 0, 0, 0, &route);
         if (r < 0)
                 return r;
 
@@ -1354,12 +1372,9 @@ int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
         assert_return(m, -EINVAL);
         assert_return(link, -EINVAL);
 
-        HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) {
-                if (l != link)
-                        continue;
-
-                (void) manager_dhcp6_prefix_remove(m, addr);
-        }
+        HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
+                if (l == link)
+                        (void) manager_dhcp6_prefix_remove(m, addr);
 
         return 0;
 }
@@ -1402,8 +1417,6 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        LIST_HEAD_INIT(m->networks);
-
         r = sd_resolve_default(&m->resolve);
         if (r < 0)
                 return r;
@@ -1426,8 +1439,8 @@ int manager_new(Manager **ret) {
 }
 
 void manager_free(Manager *m) {
+        struct in6_addr *a;
         AddressPool *pool;
-        Network *network;
         Link *link;
 
         if (!m)
@@ -1435,12 +1448,8 @@ void manager_free(Manager *m) {
 
         free(m->state_file);
 
-        sd_netlink_unref(m->rtnl);
-        sd_netlink_unref(m->genl);
-        sd_resolve_unref(m->resolve);
-
-        while ((link = hashmap_first(m->dhcp6_prefixes)))
-                manager_dhcp6_prefix_remove_all(m, link);
+        while ((a = hashmap_first_key(m->dhcp6_prefixes)))
+                (void) manager_dhcp6_prefix_remove(m, a);
         hashmap_free(m->dhcp6_prefixes);
 
         while ((link = hashmap_steal_first(m->links))) {
@@ -1457,9 +1466,7 @@ void manager_free(Manager *m) {
         m->links = hashmap_free_with_destructor(m->links, link_unref);
 
         m->duids_requesting_uuid = set_free(m->duids_requesting_uuid);
-        while ((network = m->networks))
-                network_free(network);
-        hashmap_free(m->networks_by_name);
+        m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
 
         m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
 
@@ -1472,6 +1479,10 @@ void manager_free(Manager *m) {
         m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
         set_free_with_destructor(m->rules_saved, routing_policy_rule_free);
 
+        sd_netlink_unref(m->rtnl);
+        sd_netlink_unref(m->genl);
+        sd_resolve_unref(m->resolve);
+
         sd_event_unref(m->event);
 
         sd_device_monitor_unref(m->device_monitor);
index 35ab6bedb17058718a37a89b3cace8ee98c47f3d..1084a3a60b6301aef1fd6dbdae8b74075a8a6558 100644 (file)
@@ -37,9 +37,8 @@ struct Manager {
 
         Hashmap *links;
         Hashmap *netdevs;
-        Hashmap *networks_by_name;
+        OrderedHashmap *networks;
         Hashmap *dhcp6_prefixes;
-        LIST_HEAD(Network, networks);
         LIST_HEAD(AddressPool, address_pools);
 
         usec_t network_dirs_ts_usec;
index eb470a4d481509b3ccfc2aea3f535c067717bd00..3b546528055b2476ec72457cdaae074576acd020 100644 (file)
@@ -39,7 +39,7 @@ static int ndisc_netlink_message_handler(sd_netlink *rtnl, sd_netlink_message *m
 
 static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = NULL;
-        struct in6_addr gateway;
+        union in_addr_union gateway;
         uint16_t lifetime;
         unsigned preference;
         uint32_t mtu;
@@ -58,12 +58,14 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         if (lifetime == 0) /* not a default router */
                 return 0;
 
-        r = sd_ndisc_router_get_address(rt, &gateway);
+        r = sd_ndisc_router_get_address(rt, &gateway.in6);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
 
-        SET_FOREACH(address, link->addresses, i)
-                if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) {
+        SET_FOREACH(address, link->addresses, i) {
+                if (address->family != AF_INET6)
+                        continue;
+                if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
                         char buffer[INET6_ADDRSTRLEN];
 
                         log_link_debug(link, "No NDisc route added, gateway %s matches local address",
@@ -72,9 +74,12 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                                                  buffer, sizeof(buffer)));
                         return 0;
                 }
+        }
 
-        SET_FOREACH(address, link->addresses_foreign, i)
-                if (!memcmp(&gateway, &address->in_addr.in6, sizeof(address->in_addr.in6))) {
+        SET_FOREACH(address, link->addresses_foreign, i) {
+                if (address->family != AF_INET6)
+                        continue;
+                if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
                         char buffer[INET6_ADDRSTRLEN];
 
                         log_link_debug(link, "No NDisc route added, gateway %s matches local address",
@@ -83,6 +88,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                                                  buffer, sizeof(buffer)));
                         return 0;
                 }
+        }
 
         r = sd_ndisc_router_get_preference(rt, &preference);
         if (r < 0)
@@ -107,7 +113,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         route->priority = link->network->dhcp_route_metric;
         route->protocol = RTPROT_RA;
         route->pref = preference;
-        route->gw.in6 = gateway;
+        route->gw = gateway;
         route->lifetime = time_now + lifetime * USEC_PER_SEC;
         route->mtu = mtu;
 
index 07e453cc8086a143582c93d568b52b1b555f6eb3..e3ba148ce1bf744d171fb5f68145436494bb89bc 100644 (file)
@@ -87,6 +87,7 @@ int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
         _cleanup_strv_free_ char **l = NULL;
         Manager *m = userdata;
         Network *network;
+        Iterator i;
         int r;
 
         assert(bus);
@@ -94,7 +95,7 @@ int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
         assert(m);
         assert(nodes);
 
-        LIST_FOREACH(networks, network, m->networks) {
+        ORDERED_HASHMAP_FOREACH(network, m->networks, i) {
                 char *p;
 
                 p = network_bus_path(network);
index 8232b069a848f9ccad7cdb605dd92a59fef4431a..98c2241fca2ad79c2c74d90468e737e720629448 100644 (file)
@@ -140,6 +140,7 @@ DHCP.Hostname,                          config_parse_hostname,
 DHCP.RequestBroadcast,                  config_parse_bool,                               0,                             offsetof(Network, dhcp_broadcast)
 DHCP.CriticalConnection,                config_parse_bool,                               0,                             offsetof(Network, dhcp_critical)
 DHCP.VendorClassIdentifier,             config_parse_string,                             0,                             offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.MaxAttempts,                       config_parse_dhcp_max_attempts,                  0,                             0
 DHCP.UserClass,                         config_parse_dhcp_user_class,                    0,                             offsetof(Network, dhcp_user_class)
 DHCP.DUIDType,                          config_parse_duid_type,                          0,                             offsetof(Network, duid)
 DHCP.DUIDRawData,                       config_parse_duid_rawdata,                       0,                             offsetof(Network, duid)
@@ -180,6 +181,7 @@ Bridge.Priority,                        config_parse_bridge_port_priority,
 BridgeFDB.MACAddress,                   config_parse_fdb_hwaddr,                         0,                             0
 BridgeFDB.VLANId,                       config_parse_fdb_vlan_id,                        0,                             0
 BridgeFDB.Destination,                  config_parse_fdb_destination,                    0,                             0
+BridgeFDB.VNI,                          config_parse_fdb_vxlan_vni,                      0,                             0
 BridgeVLAN.PVID,                        config_parse_brvlan_pvid,                        0,                             0
 BridgeVLAN.VLAN,                        config_parse_brvlan_vlan,                        0,                             0
 BridgeVLAN.EgressUntagged,              config_parse_brvlan_untagged,                    0,                             0
index a85d5ede28ed2db50e8e2361cc731a769a33880e..6e0e1eae08dfd19c3fa4f73320abe2c9b18d6dbc 100644 (file)
@@ -298,7 +298,7 @@ int network_verify(Network *network) {
 
 int network_load_one(Manager *manager, const char *filename) {
         _cleanup_free_ char *fname = NULL, *name = NULL;
-        _cleanup_(network_freep) Network *network = NULL;
+        _cleanup_(network_unrefp) Network *network = NULL;
         _cleanup_fclose_ FILE *file = NULL;
         const char *dropin_dirname;
         char *d;
@@ -344,6 +344,9 @@ int network_load_one(Manager *manager, const char *filename) {
                 .filename = TAKE_PTR(fname),
                 .name = TAKE_PTR(name),
 
+                .manager = manager,
+                .n_ref = 1,
+
                 .required_for_online = true,
                 .required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
                 .dhcp = ADDRESS_FAMILY_NO,
@@ -356,7 +359,7 @@ int network_load_one(Manager *manager, const char *filename) {
                 /* To enable/disable RFC7844 Anonymity Profiles */
                 .dhcp_anonymize = false,
                 .dhcp_route_metric = DHCP_ROUTE_METRIC,
-                /* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
+                /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
                 .dhcp_client_identifier = DHCP_CLIENT_ID_DUID,
                 .dhcp_route_table = RT_TABLE_MAIN,
                 .dhcp_route_table_set = false,
@@ -446,14 +449,11 @@ int network_load_one(Manager *manager, const char *filename) {
         if (r < 0)
                 log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename);
 
-        LIST_PREPEND(networks, manager->networks, network);
-        network->manager = manager;
-
-        r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
+        r = ordered_hashmap_ensure_allocated(&manager->networks, &string_hash_ops);
         if (r < 0)
                 return r;
 
-        r = hashmap_put(manager->networks_by_name, network->name, network);
+        r = ordered_hashmap_put(manager->networks, network->name, network);
         if (r < 0)
                 return r;
 
@@ -465,21 +465,19 @@ int network_load_one(Manager *manager, const char *filename) {
 }
 
 int network_load(Manager *manager) {
-        Network *network;
         _cleanup_strv_free_ char **files = NULL;
         char **f;
         int r;
 
         assert(manager);
 
-        while ((network = manager->networks))
-                network_free(network);
+        ordered_hashmap_clear_with_destructor(manager->networks, network_unref);
 
         r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate network files: %m");
 
-        STRV_FOREACH_BACKWARDS(f, files) {
+        STRV_FOREACH(f, files) {
                 r = network_load_one(manager, *f);
                 if (r < 0)
                         return r;
@@ -488,7 +486,7 @@ int network_load(Manager *manager) {
         return 0;
 }
 
-void network_free(Network *network) {
+static Network *network_free(Network *network) {
         IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
         RoutingPolicyRule *rule;
         FdbEntry *fdb_entry;
@@ -499,7 +497,7 @@ void network_free(Network *network) {
         Route *route;
 
         if (!network)
-                return;
+                return NULL;
 
         free(network->filename);
 
@@ -568,11 +566,8 @@ void network_free(Network *network) {
         hashmap_free(network->rules_by_section);
 
         if (network->manager) {
-                if (network->manager->networks)
-                        LIST_REMOVE(networks, network->manager->networks, network);
-
-                if (network->manager->networks_by_name && network->name)
-                        hashmap_remove(network->manager->networks_by_name, network->name);
+                if (network->manager->networks && network->name)
+                        ordered_hashmap_remove(network->manager->networks, network->name);
 
                 if (network->manager->duids_requesting_uuid)
                         set_remove(network->manager->duids_requesting_uuid, &network->duid);
@@ -586,9 +581,11 @@ void network_free(Network *network) {
 
         set_free_free(network->dnssec_negative_trust_anchors);
 
-        free(network);
+        return mfree(network);
 }
 
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Network, network, network_free);
+
 int network_get_by_name(Manager *manager, const char *name, Network **ret) {
         Network *network;
 
@@ -596,7 +593,7 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) {
         assert(name);
         assert(ret);
 
-        network = hashmap_get(manager->networks_by_name, name);
+        network = ordered_hashmap_get(manager->networks, name);
         if (!network)
                 return -ENOENT;
 
@@ -610,6 +607,7 @@ int network_get(Manager *manager, sd_device *device,
                 Network **ret) {
         const char *path = NULL, *driver = NULL, *devtype = NULL;
         Network *network;
+        Iterator i;
 
         assert(manager);
         assert(ret);
@@ -622,7 +620,7 @@ int network_get(Manager *manager, sd_device *device,
                 (void) sd_device_get_devtype(device, &devtype);
         }
 
-        LIST_FOREACH(networks, network, manager->networks) {
+        ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
                 if (net_match_config(network->match_mac, network->match_path,
                                      network->match_driver, network->match_type,
                                      network->match_name,
@@ -645,7 +643,6 @@ int network_get(Manager *manager, sd_device *device,
                         *ret = network;
                         return 0;
                 }
-        }
 
         *ret = NULL;
 
@@ -656,7 +653,7 @@ int network_apply(Network *network, Link *link) {
         assert(network);
         assert(link);
 
-        link->network = network;
+        link->network = network_ref(network);
 
         if (network->n_dns > 0 ||
             !strv_isempty(network->ntp) ||
@@ -1575,6 +1572,54 @@ int config_parse_section_route_table(
         return 0;
 }
 
+int config_parse_dhcp_max_attempts(
+                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) {
+
+        Network *network = data;
+        uint64_t a;
+        int r;
+
+        assert(network);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                network->dhcp_max_attempts = 0;
+                return 0;
+        }
+
+        if (streq(rvalue, "infinity")) {
+                network->dhcp_max_attempts = (uint64_t) -1;
+                return 0;
+        }
+
+        r = safe_atou64(rvalue, &a);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (a == 0) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
+                           "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        network->dhcp_max_attempts = a;
+
+        return 0;
+}
+
 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
                          "Failed to parse DHCP use domains setting");
 
index 852144da3c0a0d9d0fbd1803bd59c953170b3dab..9ee8fb72b8d73d52a606f1490cde96559db95cca 100644 (file)
@@ -92,6 +92,8 @@ struct Network {
         char *filename;
         char *name;
 
+        unsigned n_ref;
+
         Set *match_mac;
         char **match_path;
         char **match_driver;
@@ -116,6 +118,7 @@ struct Network {
         char *dhcp_vendor_class_identifier;
         char **dhcp_user_class;
         char *dhcp_hostname;
+        uint64_t dhcp_max_attempts;
         unsigned dhcp_route_metric;
         uint32_t dhcp_route_table;
         uint16_t dhcp_client_port;
@@ -274,13 +277,11 @@ struct Network {
 
         char **ntp;
         char **bind_carrier;
-
-        LIST_FIELDS(Network, networks);
 };
 
-void network_free(Network *network);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
+Network *network_ref(Network *network);
+Network *network_unref(Network *network);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_unref);
 
 int network_load(Manager *manager);
 int network_load_one(Manager *manager, const char *filename);
@@ -315,6 +316,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
 CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
 /* Legacy IPv4LL support */
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
 
index 6e169e0fca00c0d82f9ce56f6d37990c036f0db6..21ee97e84ea9d211051ecad6a34dae6d9cb57c12 100644 (file)
@@ -121,7 +121,7 @@ static int test_load_config(Manager *manager) {
 
 static void test_network_get(Manager *manager, sd_device *loopback) {
         Network *network;
-        const struct ether_addr mac = {};
+        const struct ether_addr mac = ETHER_ADDR_NULL;
 
         /* let's assume that the test machine does not have a .network file
            that applies to the loopback device... */
index 05fc01d048dc7708f817824eb02715c35e22822a..dfb41f801b359d1b65063cc57e06b9c6ef6967a6 100644 (file)
@@ -169,9 +169,10 @@ static void test_config_parse_hwaddr(void) {
 }
 
 static void test_config_parse_address_one(const char *rvalue, int family, unsigned n_addresses, const union in_addr_union *u, unsigned char prefixlen) {
-        _cleanup_(network_freep) Network *network = NULL;
+        _cleanup_(network_unrefp) Network *network = NULL;
 
         assert_se(network = new0(Network, 1));
+        network->n_ref = 1;
         assert_se(network->filename = strdup("hogehoge.network"));
         assert_se(config_parse_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0);
         assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0);
index 4b134b6c8ac6b3547a888498f03ac886148a4e67..646f449821818efe3292b67cddf44a0d8d9c2876 100644 (file)
@@ -183,16 +183,14 @@ int utmp_put_reboot(usec_t t) {
         return write_entry_both(&store);
 }
 
-_pure_ static const char *sanitize_id(const char *id) {
+static void copy_suffix(char *buf, size_t buf_size, const char *src) {
         size_t l;
 
-        assert(id);
-        l = strlen(id);
-
-        if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
-                return id;
-
-        return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
+        l = strlen(src);
+        if (l < buf_size)
+                strncpy(buf, src, buf_size);
+        else
+                memcpy(buf, src + l - buf_size, buf_size);
 }
 
 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
@@ -207,11 +205,11 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line
 
         init_timestamp(&store, 0);
 
-        /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
-        strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
+        /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
+        copy_suffix(store.ut_id, sizeof(store.ut_id), id);
 
         if (line)
-                strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
+                strncpy_exact(store.ut_line, line, sizeof(store.ut_line));
 
         r = write_entry_both(&store);
         if (r < 0)
@@ -244,8 +242,8 @@ int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
 
         setutxent();
 
-        /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
-        strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
+        /* Copy the whole string if it fits, or just the suffix without the terminating NUL. */
+        copy_suffix(store.ut_id, sizeof(store.ut_id), id);
 
         found = getutxid(&lookup);
         if (!found)
index bd0d429df60d657dd09597429919f083799c076f..d9b57e2c734ceb27e47989e79d988e440f9910f0 100644 (file)
@@ -154,6 +154,9 @@ int sd_dhcp_client_get_client_id(
 int sd_dhcp_client_set_mtu(
                 sd_dhcp_client *client,
                 uint32_t mtu);
+int sd_dhcp_client_set_max_attempts(
+                sd_dhcp_client *client,
+                uint64_t attempt);
 int sd_dhcp_client_set_client_port(
                 sd_dhcp_client *client,
                 uint16_t port);
index 481c426618df08cea4ac693fed0a9ba9ba89ffe6..e1940e7d38f63d21f94c75aecad9dc5e2282f383 100644 (file)
@@ -388,7 +388,7 @@ static int do_scsi_page0_inquiry(struct scsi_id_device *dev_scsi, int fd,
                  * If the vendor id appears in the page assume the page is
                  * invalid.
                  */
-                if (strneq((char *)&buffer[VENDOR_LENGTH], dev_scsi->vendor, VENDOR_LENGTH)) {
+                if (strneq((char*) buffer + VENDOR_LENGTH, dev_scsi->vendor, VENDOR_LENGTH)) {
                         log_debug("%s: invalid page0 data", dev_scsi->kernel);
                         return 1;
                 }
@@ -396,27 +396,21 @@ static int do_scsi_page0_inquiry(struct scsi_id_device *dev_scsi, int fd,
         return 0;
 }
 
-/*
- * The caller checks that serial is long enough to include the vendor +
- * model.
- */
-static int prepend_vendor_model(struct scsi_id_device *dev_scsi, char *serial) {
-        int ind;
-
-        strncpy(serial, dev_scsi->vendor, VENDOR_LENGTH);
-        strncat(serial, dev_scsi->model, MODEL_LENGTH);
-        ind = strlen(serial);
+static int append_vendor_model(
+                const struct scsi_id_device *dev_scsi,
+                char buf[static VENDOR_LENGTH + MODEL_LENGTH]) {
 
-        /*
-         * This is not a complete check, since we are using strncat/cpy
-         * above, ind will never be too large.
-         */
-        if (ind != (VENDOR_LENGTH + MODEL_LENGTH))
+        if (strnlen(dev_scsi->vendor, VENDOR_LENGTH) != VENDOR_LENGTH)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "%s: bad vendor string \"%s\"",
+                                       dev_scsi->kernel, dev_scsi->vendor);
+        if (strnlen(dev_scsi->model, MODEL_LENGTH) != MODEL_LENGTH)
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "%s: expected length %d, got length %d",
-                                       dev_scsi->kernel,
-                                       (VENDOR_LENGTH + MODEL_LENGTH), ind);
-        return ind;
+                                       "%s: bad model string \"%s\"",
+                                       dev_scsi->kernel, dev_scsi->model);
+        memcpy(buf, dev_scsi->vendor, VENDOR_LENGTH);
+        memcpy(buf + VENDOR_LENGTH, dev_scsi->model, MODEL_LENGTH);
+        return VENDOR_LENGTH + MODEL_LENGTH;
 }
 
 /*
@@ -497,7 +491,7 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi,
          * included in the identifier.
          */
         if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
-                if (prepend_vendor_model(dev_scsi, &serial[1]) < 0)
+                if (append_vendor_model(dev_scsi, serial + 1) < 0)
                         return 1;
 
         i = 4; /* offset to the start of the identifier */
@@ -520,12 +514,12 @@ static int check_fill_0x83_id(struct scsi_id_device *dev_scsi,
                 }
         }
 
-        strcpy(serial_short, &serial[s]);
+        strcpy(serial_short, serial + s);
 
         if (id_search->id_type == SCSI_ID_NAA && wwn != NULL) {
-                strncpy(wwn, &serial[s], 16);
+                strncpy(wwn, serial + s, 16);
                 if (wwn_vendor_extension)
-                        strncpy(wwn_vendor_extension, &serial[s + 16], 16);
+                        strncpy(wwn_vendor_extension, serial + s + 16, 16);
         }
 
         return 0;
@@ -619,8 +613,8 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd,
                  * one or a small number of descriptors.
                  */
                 for (j = 4; j <= (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) {
-                        retval = check_fill_0x83_id(dev_scsi, &page_83[j],
-                                                    &id_search_list[id_ind],
+                        retval = check_fill_0x83_id(dev_scsi, page_83 + j,
+                                                    id_search_list + id_ind,
                                                     serial, serial_short, len,
                                                     wwn, wwn_vendor_extension,
                                                     tgpt_group);
@@ -731,7 +725,7 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
         len = buf[3];
         if (serial) {
                 serial[0] = 'S';
-                ser_ind = prepend_vendor_model(dev_scsi, &serial[1]);
+                ser_ind = append_vendor_model(dev_scsi, serial + 1);
                 if (ser_ind < 0)
                         return 1;
                 ser_ind++; /* for the leading 'S' */
@@ -739,7 +733,7 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
                         serial[ser_ind] = buf[i];
         }
         if (serial_short) {
-                memcpy(serial_short, &buf[4], len);
+                memcpy(serial_short, buf + 4, len);
                 serial_short[len] = '\0';
         }
         return 0;
index ee49baa9559af8e32d1e2c83d053eae884c35b7c..9296c16c498df2f0606b036b6b47c540458ff5be 100644 (file)
@@ -33,6 +33,7 @@ MACAddress=
 VLANId=
 MACAddress=
 Destination=
+VNI=
 [DHCP]
 UseDomains=
 UseRoutes=
@@ -199,6 +200,7 @@ MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
+MaxAttempts=
 [DHCPv4]
 UseHostname=
 UseMTU=
diff --git a/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network b/test/test-network/conf/dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network
new file mode 100644 (file)
index 0000000..7faff5f
--- /dev/null
@@ -0,0 +1,10 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+LinkLocalAddressing=fallback
+IPv6AcceptRA=no
+
+[DHCP]
+MaxAttempts=1
index 184ab2d03a33d86939c2ddc31fdf7d1f94e7d06b..4301f8c5dc73b36fe625596249309fd1bed45c1b 100755 (executable)
@@ -1880,7 +1880,8 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities):
         'dhcp-client-route-metric.network',
         'dhcp-client-route-table.network',
         'dhcp-client-vrf.network',
-        'dhcp-client-with-ipv4ll-fallback.network',
+        'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
+        'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
         'dhcp-client.network',
         'dhcp-server-veth-peer.network',
         'dhcp-v4-server-veth-peer.network',
@@ -2177,10 +2178,11 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'onlink')
 
-    def test_dhcp_client_with_ipv4ll_fallback(self):
+    def test_dhcp_client_with_ipv4ll_fallback_with_dhcp_server(self):
         self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
-                                             'dhcp-client-with-ipv4ll-fallback.network')
+                                             'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network')
         self.start_networkd(0)
+        self.wait_online(['veth-peer:carrier'])
         self.start_dnsmasq(lease_time='2m')
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
@@ -2213,6 +2215,24 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities):
 
         self.search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
 
+    def test_dhcp_client_with_ipv4ll_fallback_without_dhcp_server(self):
+        self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+                                             'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network')
+        self.start_networkd(0)
+        self.wait_online(['veth99:degraded', 'veth-peer:routable'])
+
+        output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8')
+        print(output)
+
+        output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8')
+        self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
+        output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8')
+        self.assertRegex(output, 'inet6 .* scope link')
+        output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic']).rstrip().decode('utf-8')
+        self.assertNotRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+        output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link']).rstrip().decode('utf-8')
+        self.assertRegex(output, 'inet .* scope link')
+
 if __name__ == '__main__':
     unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
                                                      verbosity=3))