From 4af7b60d428765c2d2c66c46f416f6dae55e9ddb Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Sun, 8 Feb 2015 12:37:05 +0100 Subject: [PATCH] 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...) --- src/libsystemd/sd-rtnl/rtnl-message.c | 124 ++++++++++++++++++++++---- src/libsystemd/sd-rtnl/rtnl-types.c | 41 +++++++-- src/libsystemd/sd-rtnl/rtnl-types.h | 7 ++ src/libsystemd/sd-rtnl/test-rtnl.c | 22 ++++- src/systemd/sd-rtnl.h | 2 + 5 files changed, 172 insertions(+), 24 deletions(-) diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index 14a52dfae..276591f31 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 d4abe4cc3..4ef698a7f 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 9c0dc3033..1ab944498 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 ed60b3cb4..02f7a8e38 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 0eb55470e..be318e58a 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); -- 2.30.2