[systemd-commits] 5 commits - Makefile.am src/libsystemd src/network src/systemd

Tom Gundersen tomegun at kemper.freedesktop.org
Sun Feb 8 11:24:47 PST 2015


 Makefile.am                           |    7 +
 src/libsystemd/sd-rtnl/rtnl-message.c |  124 ++++++++++++++++++++++++++++----
 src/libsystemd/sd-rtnl/rtnl-types.c   |   69 ++++++++++++++++--
 src/libsystemd/sd-rtnl/rtnl-types.h   |    7 +
 src/libsystemd/sd-rtnl/test-rtnl.c    |   22 +++++
 src/network/networkd-manager.c        |   10 ++
 src/network/networkd-network-bus.c    |  128 ++++++++++++++++++++++++++++++++++
 src/network/networkd-network.c        |   48 ++++++++++++
 src/network/networkd.h                |   10 ++
 src/systemd/sd-rtnl.h                 |    2 
 10 files changed, 399 insertions(+), 28 deletions(-)

New commits:
commit 3175fcdec5d748e8db85a3e7fe1cb67f5f6a865d
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Feb 8 13:27:56 2015 +0100

    networkd: add basic org.freedesktop.network1.Network interface

diff --git a/Makefile.am b/Makefile.am
index 3dc5fa0..1ab2e79 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5589,6 +5589,7 @@ libsystemd_networkd_core_la_SOURCES = \
 	src/network/networkd-dhcp4.c \
 	src/network/networkd-dhcp6.c \
 	src/network/networkd-network.c \
+	src/network/networkd-network-bus.c \
 	src/network/networkd-address.c \
 	src/network/networkd-route.c \
 	src/network/networkd-manager.c \
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index e2c8a23..4617f11 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -174,6 +174,14 @@ int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add link enumerator: %m");
 
+        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/network", "org.freedesktop.network1.Network", network_vtable, network_object_find, m);
+        if (r < 0)
+               return log_error_errno(r, "Failed to add network object vtable: %m");
+
+        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/network", network_node_enumerator, m);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add network enumerator: %m");
+
         r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to register name: %m");
diff --git a/src/network/networkd-network-bus.c b/src/network/networkd-network-bus.c
new file mode 100644
index 0000000..a167857
--- /dev/null
+++ b/src/network/networkd-network-bus.c
@@ -0,0 +1,128 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Tom Gundersen
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-util.h"
+#include "bus-label.h"
+#include "strv.h"
+
+#include "networkd.h"
+
+const sd_bus_vtable network_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Description", "s", NULL, offsetof(Network, description), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Network, filename), SD_BUS_VTABLE_PROPERTY_CONST),
+/*        SD_BUS_PROPERTY("MatchMAC", "s", NULL TODO, offsetof(Network, match_mac), SD_BUS_VTABLE_PROPERTY_CONST), */
+        SD_BUS_PROPERTY("MatchPath", "s", NULL, offsetof(Network, match_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("MatchDriver", "s", NULL, offsetof(Network, match_driver), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("MatchType", "s", NULL, offsetof(Network, match_type), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("MatchName", "s", NULL, offsetof(Network, match_name), SD_BUS_VTABLE_PROPERTY_CONST),
+
+        SD_BUS_VTABLE_END
+};
+
+static char *network_bus_path(Network *network) {
+        _cleanup_free_ char *e = NULL;
+        _cleanup_free_ char *name = NULL;
+        char *networkname;
+        char *d;
+
+        assert(network);
+        assert(network->filename);
+
+        name = strdup(network->filename);
+        if (!name)
+                return NULL;
+
+        networkname = basename(name);
+
+        d = strrchr(networkname, '.');
+        if (!d)
+                return NULL;
+
+        assert(streq(d, ".network"));
+
+        *d = '\0';
+
+        e = bus_label_escape(networkname);
+        if (!e)
+                return NULL;
+
+        return strappend("/org/freedesktop/network1/network/", e);
+}
+
+int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+        _cleanup_strv_free_ char **l = NULL;
+        Manager *m = userdata;
+        Network *network;
+        int r;
+
+        assert(bus);
+        assert(path);
+        assert(m);
+        assert(nodes);
+
+        LIST_FOREACH(networks, network, m->networks) {
+                char *p;
+
+                p = network_bus_path(network);
+                if (!p)
+                        return -ENOMEM;
+
+                r = strv_consume(&l, p);
+                if (r < 0)
+                        return r;
+        }
+
+        *nodes = l;
+        l = NULL;
+
+        return 1;
+}
+
+int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+        Manager *m = userdata;
+        Network *network;
+        _cleanup_free_ char *name = NULL;
+        _cleanup_free_ char *e = NULL;
+        int r;
+
+        assert(bus);
+        assert(path);
+        assert(interface);
+        assert(m);
+        assert(found);
+
+        if (sscanf(path, "/org/freedesktop/network1/network/%ms", &name) != 1)
+                return 0;
+
+        e = bus_label_unescape(name);
+        if (!e)
+                return -ENOMEM;
+
+        r = network_get_by_name(m, e, &network);
+        if (r < 0)
+                return 0;
+
+        *found = network;
+
+        return 1;
+}
diff --git a/src/network/networkd.h b/src/network/networkd.h
index d144c11..147b21a 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -104,7 +104,6 @@ struct Network {
         char *match_driver;
         char *match_type;
         char *match_name;
-        char *dhcp_vendor_class_identifier;
 
         Condition *match_host;
         Condition *match_virt;
@@ -116,6 +115,7 @@ struct Network {
         NetDev *bond;
         Hashmap *stacked_netdevs;
         AddressFamilyBoolean dhcp;
+        char *dhcp_vendor_class_identifier;
         bool dhcp_dns;
         bool dhcp_ntp;
         bool dhcp_mtu;
@@ -321,6 +321,11 @@ int config_parse_vxlan_group_address(const char *unit,
                                      void *data,
                                      void *userdata);
 
+extern const sd_bus_vtable network_vtable[];
+
+int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+
 /* gperf */
 const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length);
 

commit dbffab87f1504abc9f189dd253111693c99fbd9a
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Feb 8 13:29:35 2015 +0100

    networkd: add network_get_by_name

diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 8dfe437..e2c8a23 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -481,6 +481,8 @@ void manager_free(Manager *m) {
         while ((network = m->networks))
                 network_free(network);
 
+        hashmap_free(m->networks_by_name);
+
         while ((netdev = hashmap_first(m->netdevs)))
                 netdev_unref(netdev);
         hashmap_free(m->netdevs);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 4589162..504419c 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -34,6 +34,7 @@
 static int network_load_one(Manager *manager, const char *filename) {
         _cleanup_network_free_ Network *network = NULL;
         _cleanup_fclose_ FILE *file = NULL;
+        char *d;
         Route *route;
         Address *address;
         int r;
@@ -84,6 +85,18 @@ static int network_load_one(Manager *manager, const char *filename) {
         if (!network->filename)
                 return log_oom();
 
+        network->name = strdup(basename(filename));
+        if (!network->name)
+                return log_oom();
+
+        d = strrchr(network->name, '.');
+        if (!d)
+                return -EINVAL;
+
+        assert(streq(d, ".network"));
+
+        *d = '\0';
+
         network->dhcp = ADDRESS_FAMILY_NO;
         network->dhcp_ntp = true;
         network->dhcp_dns = true;
@@ -115,6 +128,14 @@ static int network_load_one(Manager *manager, const char *filename) {
 
         LIST_PREPEND(networks, manager->networks, network);
 
+        r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = hashmap_put(manager->networks_by_name, network->name, network);
+        if (r < 0)
+                return r;
+
         LIST_FOREACH(routes, route, network->static_routes) {
                 if (!route->family) {
                         log_warning("Route section without Gateway field configured in %s. "
@@ -210,8 +231,15 @@ void network_free(Network *network) {
         hashmap_free(network->routes_by_section);
         hashmap_free(network->fdb_entries_by_section);
 
-        if (network->manager && network->manager->networks)
-                LIST_REMOVE(networks, network->manager->networks, network);
+        if (network->manager) {
+                if (network->manager->networks)
+                        LIST_REMOVE(networks, network->manager->networks, network);
+
+                if (network->manager->networks_by_name)
+                        hashmap_remove(network->manager->networks_by_name, network->name);
+        }
+
+        free(network->name);
 
         condition_free_list(network->match_host);
         condition_free_list(network->match_virt);
@@ -221,6 +249,22 @@ void network_free(Network *network) {
         free(network);
 }
 
+int network_get_by_name(Manager *manager, const char *name, Network **ret) {
+        Network *network;
+
+        assert(manager);
+        assert(name);
+        assert(ret);
+
+        network = hashmap_get(manager->networks_by_name, name);
+        if (!network)
+                return -ENOENT;
+
+        *ret = network;
+
+        return 0;
+}
+
 int network_get(Manager *manager, struct udev_device *device,
                 const char *ifname, const struct ether_addr *address,
                 Network **ret) {
diff --git a/src/network/networkd.h b/src/network/networkd.h
index cd54e9b..d144c11 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -97,6 +97,7 @@ struct Network {
         Manager *manager;
 
         char *filename;
+        char *name;
 
         struct ether_addr *match_mac;
         char *match_path;
@@ -224,6 +225,7 @@ struct Manager {
 
         Hashmap *links;
         Hashmap *netdevs;
+        Hashmap *networks_by_name;
         LIST_HEAD(Network, networks);
         LIST_HEAD(AddressPool, address_pools);
 
@@ -265,6 +267,7 @@ void network_free(Network *network);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
 #define _cleanup_network_free_ _cleanup_(network_freep)
 
+int network_get_by_name(Manager *manager, const char *name, Network **ret);
 int network_get(Manager *manager, struct udev_device *device,
                 const char *ifname, const struct ether_addr *mac,
                 Network **ret);

commit 23cb977ac382931a0b5c1385cdbb7fe5c4c92b1a
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Feb 8 19:43:01 2015 +0100

    build-sys: properly enable the networkd dbus activation

diff --git a/Makefile.am b/Makefile.am
index d289914..3dc5fa0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5688,6 +5688,12 @@ GENERAL_ALIASES += \
 	$(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service \
 	$(systemunitdir)/systemd-networkd-wait-online.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service
 
+SYSTEM_UNIT_ALIASES += \
+	systemd-networkd.service dbus-org.freedesktop.network1.service
+
+BUSNAMES_TARGET_WANTS += \
+	org.freedesktop.network1.busname
+
 EXTRA_DIST += \
 	src/network/networkd-network-gperf.gperf \
 	src/network/networkd-netdev-gperf.gperf \

commit c149ae08ccbc63279c0b2d146b6879163dfffce3
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Feb 8 19:31:46 2015 +0100

    sd-rtnl: add support for IFLA_INET6_*

diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c
index 4ef698a..f2d5c8d 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.c
+++ b/src/libsystemd/sd-rtnl/rtnl-types.c
@@ -265,6 +265,30 @@ static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
         .match_type = NL_MATCH_PROTOCOL,
 };
 
+static const struct NLType rtnl_af_spec_inet6_types[IFLA_INET6_MAX + 1] = {
+        [IFLA_INET6_FLAGS]              = { .type = NLA_U32 },
+/*
+        IFLA_INET6_CONF,
+        IFLA_INET6_STATS,
+        IFLA_INET6_MCAST,
+        IFLA_INET6_CACHEINFO,
+        IFLA_INET6_ICMP6STATS,
+*/
+        [IFLA_INET6_TOKEN]              = { .type = NLA_IN_ADDR },
+        [IFLA_INET6_ADDR_GEN_MODE]      = { .type = NLA_U8 },
+};
+
+static const NLTypeSystem rtnl_af_spec_type_systems[AF_MAX] = {
+        [AF_INET6] =    { .max = ELEMENTSOF(rtnl_af_spec_inet6_types) - 1,
+                          .types = rtnl_af_spec_inet6_types },
+};
+
+static const NLTypeSystemUnion rtnl_af_spec_type_system_union = {
+        .num = AF_MAX,
+        .type_systems = rtnl_af_spec_type_systems,
+        .match_type = NL_MATCH_PROTOCOL,
+};
+
 static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
         [IFLA_ADDRESS]          = { .type = NLA_ETHER_ADDR, },
         [IFLA_BROADCAST]        = { .type = NLA_ETHER_ADDR, },
@@ -298,7 +322,9 @@ static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
         [IFLA_STATS64],
         [IFLA_VF_PORTS]         = { .type = NLA_NESTED },
         [IFLA_PORT_SELF]        = { .type = NLA_NESTED },
-        [IFLA_AF_SPEC]          = { .type = NLA_NESTED },
+*/
+        [IFLA_AF_SPEC]          = { .type = NLA_NESTED, .type_system_union = &rtnl_af_spec_type_system_union },
+/*
         [IFLA_VF_PORTS],
         [IFLA_PORT_SELF],
         [IFLA_AF_SPEC],

commit 4af7b60d428765c2d2c66c46f416f6dae55e9ddb
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Feb 8 12:37:05 2015 +0100

    sd-rtnl: extend type system to allow address-family to decide the union members
    
    So far we only supported selecting them by sibling attributes.
    
    (This stuff is all a bit crazy, but there seems to be no other way...)

diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
index 14a52df..276591f 100644
--- a/src/libsystemd/sd-rtnl/rtnl-message.c
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -609,6 +609,49 @@ int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type) {
         return 0;
 }
 
+int sd_rtnl_message_get_family(sd_rtnl_message *m, int *family) {
+        assert_return(m, -EINVAL);
+        assert_return(family, -EINVAL);
+
+        assert(m->hdr);
+
+        if (rtnl_message_type_is_link(m->hdr->nlmsg_type)) {
+                struct ifinfomsg *ifi;
+
+                ifi = NLMSG_DATA(m->hdr);
+
+                *family = ifi->ifi_family;
+
+                return 0;
+        } else if (rtnl_message_type_is_route(m->hdr->nlmsg_type)) {
+                struct rtmsg *rtm;
+
+                rtm = NLMSG_DATA(m->hdr);
+
+                *family = rtm->rtm_family;
+
+                return 0;
+        } else if (rtnl_message_type_is_neigh(m->hdr->nlmsg_type)) {
+                struct ndmsg *ndm;
+
+                ndm = NLMSG_DATA(m->hdr);
+
+                *family = ndm->ndm_family;
+
+                return 0;
+        } else if (rtnl_message_type_is_addr(m->hdr->nlmsg_type)) {
+                struct ifaddrmsg *ifa;
+
+                ifa = NLMSG_DATA(m->hdr);
+
+                *family = ifa->ifa_family;
+
+                return 0;
+        }
+
+        return -ENOTSUP;
+}
+
 int sd_rtnl_message_is_broadcast(sd_rtnl_message *m) {
         assert_return(m, -EINVAL);
 
@@ -898,16 +941,37 @@ int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
         assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -ERANGE);
 
         r = message_attribute_has_type(m, type, NLA_NESTED);
-        if (r < 0)
-                return r;
-        else
+        if (r < 0) {
+                const NLTypeSystemUnion *type_system_union;
+                int family;
+
+                r = message_attribute_has_type(m, type, NLA_UNION);
+                if (r < 0)
+                        return r;
+                size = (size_t) r;
+
+                r = sd_rtnl_message_get_family(m, &family);
+                if (r < 0)
+                        return r;
+
+                r = type_system_get_type_system_union(m->container_type_system[m->n_containers], &type_system_union, type);
+                if (r < 0)
+                        return r;
+
+                r = type_system_union_protocol_get_type_system(type_system_union,
+                                                               &m->container_type_system[m->n_containers + 1],
+                                                               family);
+                if (r < 0)
+                        return r;
+        } else {
                 size = (size_t)r;
 
-        r = type_system_get_type_system(m->container_type_system[m->n_containers],
-                                        &m->container_type_system[m->n_containers + 1],
-                                        type);
-        if (r < 0)
-                return r;
+                r = type_system_get_type_system(m->container_type_system[m->n_containers],
+                                                &m->container_type_system[m->n_containers + 1],
+                                                type);
+                if (r < 0)
+                        return r;
+        }
 
         r = add_rtattr(m, type | NLA_F_NESTED, NULL, size);
         if (r < 0)
@@ -1181,7 +1245,6 @@ int sd_rtnl_message_enter_container(sd_rtnl_message *m, unsigned short type) {
                         return r;
         } else if (nl_type->type == NLA_UNION) {
                 const NLTypeSystemUnion *type_system_union;
-                const char *key;
 
                 r = type_system_get_type_system_union(m->container_type_system[m->n_containers],
                                                       &type_system_union,
@@ -1189,15 +1252,42 @@ int sd_rtnl_message_enter_container(sd_rtnl_message *m, unsigned short type) {
                 if (r < 0)
                         return r;
 
-                r = sd_rtnl_message_read_string(m, type_system_union->match, &key);
-                if (r < 0)
-                        return r;
+                switch (type_system_union->match_type) {
+                case NL_MATCH_SIBLING:
+                {
+                        const char *key;
 
-                r = type_system_union_get_type_system(type_system_union,
-                                                      &type_system,
-                                                      key);
-                if (r < 0)
-                        return r;
+                        r = sd_rtnl_message_read_string(m, type_system_union->match, &key);
+                        if (r < 0)
+                                return r;
+
+                        r = type_system_union_get_type_system(type_system_union,
+                                                              &type_system,
+                                                              key);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+                case NL_MATCH_PROTOCOL:
+                {
+                        int family;
+
+                        r = sd_rtnl_message_get_family(m, &family);
+                        if (r < 0)
+                                return r;
+
+                        r = type_system_union_protocol_get_type_system(type_system_union,
+                                                                       &type_system,
+                                                                       family);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+                default:
+                        assert_not_reached("sd-rtnl: invalid type system union type");
+                }
         } else
                 return -EINVAL;
 
diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c
index d4abe4c..4ef698a 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.c
+++ b/src/libsystemd/sd-rtnl/rtnl-types.c
@@ -224,6 +224,7 @@ static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
         .num = _NL_UNION_LINK_INFO_DATA_MAX,
         .lookup = nl_union_link_info_data_from_string,
         .type_systems = rtnl_link_info_data_type_systems,
+        .match_type = NL_MATCH_SIBLING,
         .match = IFLA_INFO_KIND,
 };
 
@@ -242,7 +243,7 @@ static const NLTypeSystem rtnl_link_info_type_system = {
         .types = rtnl_link_info_types,
 };
 
-static const struct NLType rtnl_bridge_port_types[IFLA_BRPORT_MAX + 1] = {
+static const struct NLType rtnl_prot_info_bridge_port_types[IFLA_BRPORT_MAX + 1] = {
         [IFLA_BRPORT_STATE]     = { .type = NLA_U8 },
         [IFLA_BRPORT_COST]      = { .type = NLA_U32 },
         [IFLA_BRPORT_PRIORITY]  = { .type = NLA_U16 },
@@ -253,9 +254,15 @@ static const struct NLType rtnl_bridge_port_types[IFLA_BRPORT_MAX + 1] = {
         [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
 };
 
-static const NLTypeSystem rtnl_bridge_port_type_system = {
-        .max = ELEMENTSOF(rtnl_bridge_port_types) - 1,
-        .types = rtnl_bridge_port_types,
+static const NLTypeSystem rtnl_prot_info_type_systems[AF_MAX] = {
+        [AF_BRIDGE] =   { .max = ELEMENTSOF(rtnl_prot_info_bridge_port_types) - 1,
+                          .types = rtnl_prot_info_bridge_port_types },
+};
+
+static const NLTypeSystemUnion rtnl_prot_info_type_system_union = {
+        .num = AF_MAX,
+        .type_systems = rtnl_prot_info_type_systems,
+        .match_type = NL_MATCH_PROTOCOL,
 };
 
 static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
@@ -273,9 +280,8 @@ static const NLType rtnl_link_types[IFLA_MAX + 1 ] = {
         [IFLA_MASTER]           = { .type = NLA_U32 },
 /*
         [IFLA_WIRELESS],
-        [IFLA_PROTINFO],
 */
-        [IFLA_PROTINFO]         = { .type = NLA_NESTED, .type_system = &rtnl_bridge_port_type_system },
+        [IFLA_PROTINFO]         = { .type = NLA_UNION, .type_system_union = &rtnl_prot_info_type_system_union },
         [IFLA_TXQLEN]           = { .type = NLA_U32 },
 /*
         [IFLA_MAP]              = { .len = sizeof(struct rtnl_link_ifmap) },
@@ -463,6 +469,7 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union
         int type;
 
         assert(type_system_union);
+        assert_return(type_system_union->match_type == NL_MATCH_SIBLING, -EINVAL);
         assert(type_system_union->lookup);
         assert(type_system_union->type_systems);
         assert(ret);
@@ -478,3 +485,25 @@ int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union
 
         return 0;
 }
+
+int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol) {
+        const NLTypeSystem *type_system;
+
+        assert(type_system_union);
+        assert(type_system_union->type_systems);
+        assert(ret);
+        assert_return(type_system_union->match_type == NL_MATCH_PROTOCOL, -EINVAL);
+        assert_return(protocol < type_system_union->num, -EINVAL);
+
+        if (protocol >= type_system_union->num)
+                return -ENOTSUP;
+
+        type_system = &type_system_union->type_systems[protocol];
+
+        if (!type_system)
+                return -ENOTSUP;
+
+        *ret = type_system;
+
+        return 0;
+}
diff --git a/src/libsystemd/sd-rtnl/rtnl-types.h b/src/libsystemd/sd-rtnl/rtnl-types.h
index 9c0dc30..1ab9444 100644
--- a/src/libsystemd/sd-rtnl/rtnl-types.h
+++ b/src/libsystemd/sd-rtnl/rtnl-types.h
@@ -36,12 +36,18 @@ enum {
         NLA_UNION,
 };
 
+typedef enum NLMatchType {
+        NL_MATCH_SIBLING,
+        NL_MATCH_PROTOCOL,
+} NLMatchType;
+
 typedef struct NLTypeSystemUnion NLTypeSystemUnion;
 typedef struct NLTypeSystem NLTypeSystem;
 typedef struct NLType NLType;
 
 struct NLTypeSystemUnion {
         int num;
+        NLMatchType match_type;
         uint16_t match;
         int (*lookup)(const char *);
         const NLTypeSystem *type_systems;
@@ -63,6 +69,7 @@ int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, ui
 int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type);
 int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type);
 int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key);
+int type_system_union_protocol_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, uint16_t protocol);
 
 typedef enum NLUnionLinkInfoData {
         NL_UNION_LINK_INFO_DATA_BOND,
diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
index ed60b3c..02f7a8e 100644
--- a/src/libsystemd/sd-rtnl/test-rtnl.c
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -31,8 +31,26 @@
 #include "missing.h"
 #include "rtnl-internal.h"
 
+static void test_message_link_bridge(sd_rtnl *rtnl) {
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL;
+        uint32_t cost;
+
+        assert_se(sd_rtnl_message_new_link(rtnl, &message, RTM_NEWLINK, 1) >= 0);
+        assert_se(sd_rtnl_message_link_set_family(message, PF_BRIDGE) >= 0);
+        assert_se(sd_rtnl_message_open_container(message, IFLA_PROTINFO) >= 0);
+        assert_se(sd_rtnl_message_append_u32(message, IFLA_BRPORT_COST, 10) >= 0);
+        assert_se(sd_rtnl_message_close_container(message) >= 0);
+
+        assert_se(sd_rtnl_message_rewind(message) >= 0);
+
+        assert_se(sd_rtnl_message_enter_container(message, IFLA_PROTINFO) >= 0);
+        assert_se(sd_rtnl_message_read_u32(message, IFLA_BRPORT_COST, &cost) >= 0);
+        assert_se(cost == 10);
+        assert_se(sd_rtnl_message_exit_container(message) >= 0);
+}
+
 static void test_link_configure(sd_rtnl *rtnl, int ifindex) {
-        _cleanup_rtnl_message_unref_ sd_rtnl_message *message;
+        _cleanup_rtnl_message_unref_ sd_rtnl_message *message = NULL;
         const char *mac = "98:fe:94:3f:c6:18", *name = "test";
         char buffer[ETHER_ADDR_TO_STRING_MAX];
         unsigned int mtu = 1450, mtu_out;
@@ -394,6 +412,8 @@ int main(void) {
 
         test_get_addresses(rtnl);
 
+        test_message_link_bridge(rtnl);
+
         assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, if_loopback) >= 0);
         assert_se(m);
 
diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h
index 0eb5547..be318e5 100644
--- a/src/systemd/sd-rtnl.h
+++ b/src/systemd/sd-rtnl.h
@@ -88,6 +88,8 @@ int sd_rtnl_message_get_errno(sd_rtnl_message *m);
 int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type);
 int sd_rtnl_message_is_broadcast(sd_rtnl_message *m);
 
+int sd_rtnl_message_get_family(sd_rtnl_message *m, int *family);
+
 int sd_rtnl_message_addr_set_prefixlen(sd_rtnl_message *m, unsigned char prefixlen);
 int sd_rtnl_message_addr_set_scope(sd_rtnl_message *m, unsigned char scope);
 int sd_rtnl_message_addr_set_flags(sd_rtnl_message *m, unsigned char flags);



More information about the systemd-commits mailing list