diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-internal.h | 5 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-message.c | 653 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-types.c | 356 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/rtnl-types.h | 64 | ||||
-rw-r--r-- | src/libsystemd/sd-rtnl/test-rtnl.c | 7 | ||||
-rw-r--r-- | src/network/networkd-netdev.c | 52 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 16 | ||||
-rw-r--r-- | src/systemd/sd-rtnl.h | 1 |
9 files changed, 673 insertions, 483 deletions
diff --git a/Makefile.am b/Makefile.am index 88371a2df4..6c7d6e54d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2107,6 +2107,8 @@ libsystemd_internal_la_SOURCES = \ src/libsystemd/sd-rtnl/sd-rtnl.c \ src/libsystemd/sd-rtnl/rtnl-internal.h \ src/libsystemd/sd-rtnl/rtnl-message.c \ + src/libsystemd/sd-rtnl/rtnl-types.h \ + src/libsystemd/sd-rtnl/rtnl-types.c \ src/libsystemd/sd-rtnl/rtnl-util.h \ src/libsystemd/sd-rtnl/rtnl-util.c \ src/libsystemd/sd-id128/sd-id128.c \ diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h index 21a270aecd..8aa2300835 100644 --- a/src/libsystemd/sd-rtnl/rtnl-internal.h +++ b/src/libsystemd/sd-rtnl/rtnl-internal.h @@ -29,6 +29,8 @@ #include "sd-rtnl.h" +#include "rtnl-types.h" + #define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) #define RTNL_WQUEUE_MAX 1024 @@ -91,6 +93,7 @@ struct sd_rtnl_message { sd_rtnl *rtnl; struct nlmsghdr *hdr; + const struct NLTypeSystem *(container_type_system[RTNL_CONTAINER_DEPTH]); /* the type of the container and all its parents */ size_t container_offsets[RTNL_CONTAINER_DEPTH]; /* offset from hdr to each container's start */ unsigned n_containers; /* number of containers */ size_t next_rta_offset; /* offset from hdr to next rta */ @@ -99,7 +102,7 @@ struct sd_rtnl_message { bool sealed:1; }; -int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initial_size); +int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t type); int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m); int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret); diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index b4421af000..4c746f9d8a 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -23,6 +23,7 @@ #include <netinet/ether.h> #include <stdbool.h> #include <unistd.h> +#include <linux/netlink.h> #include <linux/veth.h> #include <linux/if.h> #include <linux/ip.h> @@ -36,11 +37,12 @@ #include "sd-rtnl.h" #include "rtnl-util.h" #include "rtnl-internal.h" +#include "rtnl-types.h" #define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offsets[i]) : NULL) #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; -int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initial_size) { +static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initial_size) { sd_rtnl_message *m; assert_return(ret, -EINVAL); @@ -71,6 +73,30 @@ int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, size_t initial_size) { return 0; } +int message_new(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t type) { + const NLType *nl_type; + size_t size; + int r; + + r = type_system_get_type(NULL, &nl_type, type); + if (r < 0) + return r; + + assert(nl_type->type == NLA_NESTED); + + size = NLMSG_SPACE(nl_type->size); + + r = message_new_empty(rtnl, ret, size); + if (r < 0) + return r; + + (*ret)->container_type_system[0] = nl_type->type_system; + (*ret)->hdr->nlmsg_len = size; + (*ret)->hdr->nlmsg_type = type; + + return 0; +} + int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) { struct rtmsg *rtm; @@ -112,12 +138,10 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); assert_return(ret, -EINVAL); - r = message_new(rtnl, ret, NLMSG_SPACE(sizeof(struct rtmsg))); + r = message_new(rtnl, ret, nlmsg_type); if (r < 0) return r; - (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - (*ret)->hdr->nlmsg_type = nlmsg_type; if (nlmsg_type == RTM_NEWROUTE) (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; @@ -171,12 +195,10 @@ int sd_rtnl_message_new_link(sd_rtnl *rtnl, sd_rtnl_message **ret, assert_return(nlmsg_type != RTM_DELLINK || index > 0, -EINVAL); assert_return(ret, -EINVAL); - r = message_new(rtnl, ret, NLMSG_SPACE(sizeof(struct ifinfomsg))); + r = message_new(rtnl, ret, nlmsg_type); if (r < 0) return r; - (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); - (*ret)->hdr->nlmsg_type = nlmsg_type; if (nlmsg_type == RTM_NEWLINK) (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; @@ -245,12 +267,10 @@ int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, assert_return(family == AF_INET || family == AF_INET6, -EINVAL); assert_return(ret, -EINVAL); - r = message_new(rtnl, ret, NLMSG_SPACE(sizeof(struct ifaddrmsg))); + r = message_new(rtnl, ret, nlmsg_type); if (r < 0) return r; - (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - (*ret)->hdr->nlmsg_type = nlmsg_type; if (nlmsg_type == RTM_GETADDR && family == AF_INET) (*ret)->hdr->nlmsg_flags |= NLM_F_DUMP; @@ -372,6 +392,7 @@ static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, rta->rta_type = type; rta->rta_len = rta_length; if (!data) { + //TODO: simply return this value rather than check for !data /* this is the start of a new container */ m->container_offsets[m->n_containers ++] = m->hdr->nlmsg_len; } else { @@ -390,50 +411,42 @@ static int add_rtattr(sd_rtnl_message *m, unsigned short type, const void *data, return 0; } +static int message_attribute_has_type(sd_rtnl_message *m, uint16_t attribute_type, uint16_t data_type) { + const NLType *type; + int r; + + r = type_system_get_type(m->container_type_system[m->n_containers], &type, attribute_type); + if (r < 0) + return r; + + if (type->type != data_type) + return -EINVAL; + + return type->size; +} + int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data) { - uint16_t rtm_type; + size_t length, size; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_STRING); if (r < 0) return r; + else + size = (size_t)r; - /* check that the type is correct */ - switch (rtm_type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - if (m->n_containers == 1) { - if (GET_CONTAINER(m, 0)->rta_type != IFLA_LINKINFO || - type != IFLA_INFO_KIND) - return -ENOTSUP; - } else { - switch (type) { - case IFLA_IFNAME: - case IFLA_IFALIAS: - case IFLA_QDISC: - break; - default: - return -ENOTSUP; - } - } - break; - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - if (type != IFA_LABEL) - return -ENOTSUP; - break; - default: - return -ENOTSUP; - } + if (size) { + length = strnlen(data, size); + if (length >= size) + return -EINVAL; + } else + length = strlen(data); - r = add_rtattr(m, type, data, strlen(data) + 1); + r = add_rtattr(m, type, data, length + 1); if (r < 0) return r; @@ -441,41 +454,15 @@ int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const } int sd_rtnl_message_append_u8(sd_rtnl_message *m, unsigned short type, uint8_t data) { - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_U8); if (r < 0) return r; - switch (rtm_type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - switch (type) { - case IFLA_CARRIER: - case IFLA_OPERSTATE: - case IFLA_LINKMODE: - case IFLA_IPTUN_TTL: - case IFLA_IPTUN_TOS: - case IFLA_IPTUN_PROTO: - case IFLA_IPTUN_PMTUDISC: - case IFLA_IPTUN_ENCAP_LIMIT: - case IFLA_GRE_TTL: - break; - default: - return -ENOTSUP; - } - - break; - default: - return -ENOTSUP; - } - r = add_rtattr(m, type, &data, sizeof(uint8_t)); if (r < 0) return r; @@ -485,44 +472,15 @@ int sd_rtnl_message_append_u8(sd_rtnl_message *m, unsigned short type, uint8_t d int sd_rtnl_message_append_u16(sd_rtnl_message *m, unsigned short type, uint16_t data) { - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_U16); if (r < 0) return r; - /* check that the type is correct */ - switch (rtm_type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - if (m->n_containers == 2 && - GET_CONTAINER(m, 0)->rta_type == IFLA_LINKINFO && - GET_CONTAINER(m, 1)->rta_type == IFLA_INFO_DATA) { - switch (type) { - case IFLA_VLAN_ID: - case IFLA_IPTUN_FLAGS: - case IFLA_GRE_IFLAGS: - case IFLA_GRE_OFLAGS: - case IFLA_IPTUN_6RD_PREFIXLEN: - case IFLA_IPTUN_6RD_RELAY_PREFIXLEN: - break; - default: - return -ENOTSUP; - } - } else - return -ENOTSUP; - - break; - default: - return -ENOTSUP; - } - r = add_rtattr(m, type, &data, sizeof(uint16_t)); if (r < 0) return r; @@ -531,63 +489,15 @@ int sd_rtnl_message_append_u16(sd_rtnl_message *m, unsigned short type, uint16_t } int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t data) { - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_U32); if (r < 0) return r; - /* check that the type is correct */ - switch (rtm_type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - switch (type) { - case IFLA_MASTER: - case IFLA_MTU: - case IFLA_LINK: - case IFLA_GROUP: - case IFLA_TXQLEN: - case IFLA_WEIGHT: - case IFLA_NET_NS_FD: - case IFLA_NET_NS_PID: - case IFLA_PROMISCUITY: - case IFLA_NUM_TX_QUEUES: - case IFLA_NUM_RX_QUEUES: - case IFLA_IPTUN_LOCAL: - case IFLA_IPTUN_REMOTE: - case IFLA_MACVLAN_MODE: - case IFLA_IPTUN_FLAGS: - case IFLA_IPTUN_FLOWINFO: - case IFLA_GRE_FLOWINFO: - break; - default: - return -ENOTSUP; - } - break; - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - switch (type) { - case RTA_TABLE: - case RTA_PRIORITY: - case RTA_IIF: - case RTA_OIF: - case RTA_MARK: - break; - default: - return -ENOTSUP; - } - break; - default: - return -ENOTSUP; - } - r = add_rtattr(m, type, &data, sizeof(uint32_t)); if (r < 0) return r; @@ -596,62 +506,16 @@ int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t } int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, const struct in_addr *data) { - struct ifaddrmsg *ifa; - struct rtmsg *rtm; - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; - /* check that the type is correct */ - switch (rtm_type) { - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - switch (type) { - case IFA_ADDRESS: - case IFA_LOCAL: - case IFA_BROADCAST: - case IFA_ANYCAST: - case IFLA_GRE_LOCAL: - case IFLA_GRE_REMOTE: - ifa = NLMSG_DATA(m->hdr); - - if (ifa->ifa_family != AF_INET) - return -EINVAL; - - break; - default: - return -ENOTSUP; - } - break; - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - switch (type) { - case RTA_DST: - case RTA_SRC: - case RTA_GATEWAY: - rtm = NLMSG_DATA(m->hdr); - - if (rtm->rtm_family != AF_INET) - return -EINVAL; - - break; - default: - return -ENOTSUP; - } - break; - default: - return -ENOTSUP; - } - r = add_rtattr(m, type, data, sizeof(struct in_addr)); if (r < 0) return r; @@ -660,62 +524,16 @@ int sd_rtnl_message_append_in_addr(sd_rtnl_message *m, unsigned short type, cons } int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, const struct in6_addr *data) { - struct ifaddrmsg *ifa; - struct rtmsg *rtm; - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); - r = sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; - /* check that the type is correct */ - switch (rtm_type) { - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - switch (type) { - case IFA_ADDRESS: - case IFA_LOCAL: - case IFA_BROADCAST: - case IFA_ANYCAST: - case IFLA_GRE_LOCAL: - case IFLA_GRE_REMOTE: - case IFLA_IPTUN_6RD_PREFIX: - ifa = NLMSG_DATA(m->hdr); - - if (ifa->ifa_family != AF_INET6) - return -EINVAL; - - break; - default: - return -ENOTSUP; - } - break; - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - switch (type) { - case RTA_DST: - case RTA_SRC: - case RTA_GATEWAY: - rtm = NLMSG_DATA(m->hdr); - - if (rtm->rtm_family != AF_INET6) - return -EINVAL; - - break; - default: - return -ENOTSUP; - } - default: - return -ENOTSUP; - } - r = add_rtattr(m, type, data, sizeof(struct in6_addr)); if (r < 0) return r; @@ -724,31 +542,15 @@ int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, con } int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data) { - uint16_t rtm_type; int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); - sd_rtnl_message_get_type(m, &rtm_type); - - switch (rtm_type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_DELLINK: - case RTM_GETLINK: - switch (type) { - case IFLA_ADDRESS: - case IFLA_BROADCAST: - break; - default: - return -ENOTSUP; - } - break; - default: - return -ENOTSUP; - } + r = message_attribute_has_type(m, type, NLA_ETHER_ADDR); + if (r < 0) + return r; r = add_rtattr(m, type, data, ETH_ALEN); if (r < 0) @@ -758,33 +560,67 @@ int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, c } int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) { - uint16_t rtm_type; + size_t size; + int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - sd_rtnl_message_get_type(m, &rtm_type); + r = message_attribute_has_type(m, type, NLA_NESTED); + if (r < 0) + return r; + else + size = (size_t)r; - if (rtnl_message_type_is_link(rtm_type)) { + 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; - if ((type == IFLA_LINKINFO && m->n_containers == 0) || - (type == IFLA_INFO_DATA && m->n_containers == 1 && - GET_CONTAINER(m, 0)->rta_type == IFLA_LINKINFO)) - return add_rtattr(m, type, NULL, 0); - else if (type == VETH_INFO_PEER && m->n_containers == 2 && - GET_CONTAINER(m, 1)->rta_type == IFLA_INFO_DATA && - GET_CONTAINER(m, 0)->rta_type == IFLA_LINKINFO) - return add_rtattr(m, type, NULL, sizeof(struct ifinfomsg)); - } + r = add_rtattr(m, type, NULL, size); + if (r < 0) + return r; + + return 0; +} + +int sd_rtnl_message_open_container_union(sd_rtnl_message *m, unsigned short type, const char *key) { + const NLTypeSystemUnion *type_system_union; + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + + 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_get_type_system(type_system_union, + &m->container_type_system[m->n_containers + 1], + key); + if (r < 0) + return r; - return -ENOTSUP; + r = sd_rtnl_message_append_string(m, type_system_union->match, key); + if (r < 0) + return r; + + /* do we evere need non-null size */ + r = add_rtattr(m, type, NULL, 0); + if (r < 0) + return r; + + return 0; } + int sd_rtnl_message_close_container(sd_rtnl_message *m) { assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(m->n_containers > 0, -EINVAL); + m->container_type_system[m->n_containers] = NULL; m->n_containers --; return 0; @@ -796,8 +632,9 @@ int rtnl_message_read_internal(sd_rtnl_message *m, unsigned short type, void **d assert_return(m, -EINVAL); assert_return(m->sealed, -EPERM); assert_return(data, -EINVAL); - assert_return(m->rta_offset_tb[m->n_containers], -EINVAL); - assert_return(type < m->rta_tb_size[m->n_containers], -EINVAL); + assert(m->n_containers <= RTNL_CONTAINER_DEPTH); + assert(m->rta_offset_tb[m->n_containers]); + assert(type < m->rta_tb_size[m->n_containers]); if(!m->rta_offset_tb[m->n_containers][type]) return -ENODATA; @@ -813,7 +650,9 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, char ** int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_STRING); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -830,7 +669,9 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U8); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -847,7 +688,9 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t * int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U16); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -864,7 +707,9 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t * int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U32); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -881,7 +726,9 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_ETHER_ADDR); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -898,7 +745,9 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -915,7 +764,9 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc int r; void *attr_data; - assert_return(data, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); + if (r < 0) + return r; r = rtnl_message_read_internal(m, type, &attr_data); if (r < 0) @@ -929,84 +780,69 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc } int sd_rtnl_message_enter_container(sd_rtnl_message *m, unsigned short type) { - uint16_t rtm_type; - unsigned short parent_type; + const NLType *nl_type; + const NLTypeSystem *type_system; void *container; - size_t container_length; - int max, r; + size_t size; + int r; assert_return(m, -EINVAL); assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL); - r = rtnl_message_read_internal(m, type, &container); + r = type_system_get_type(m->container_type_system[m->n_containers], + &nl_type, + type); if (r < 0) return r; - else - container_length = r; - r = sd_rtnl_message_get_type(m, &rtm_type); + if (nl_type->type == NLA_NESTED) { + r = type_system_get_type_system(m->container_type_system[m->n_containers], + &type_system, + type); + if (r < 0) + return r; + } else if (nl_type->type == NLA_UNION) { + const NLTypeSystemUnion *type_system_union; + char *key; + + r = type_system_get_type_system_union(m->container_type_system[m->n_containers], + &type_system_union, + type); + 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; + } else + return -EINVAL; + + r = rtnl_message_read_internal(m, type, &container); if (r < 0) return r; + else + size = (size_t)r; - if (rtnl_message_type_is_link(rtm_type)) { - switch (m->n_containers) { - case 0: - switch (type) { - case IFLA_LINKINFO: - max = IFLA_INFO_MAX; - break; - default: - return -ENOTSUP; - } - break; - case 1: - parent_type = GET_CONTAINER(m, 0)->rta_type; - switch (parent_type) { - case IFLA_LINKINFO: - switch (type) { - case IFLA_INFO_DATA: { - char *kind; - - r = sd_rtnl_message_read_string(m, IFLA_INFO_KIND, &kind); - if (r < 0) - return r; - - if (streq(kind, "vlan")) { - max = IFLA_VLAN_MAX; - } else if (streq(kind, "bridge")) { - max = IFLA_BRIDGE_MAX; - } else if (streq(kind, "veth")) { - max = VETH_INFO_MAX; - container = IFLA_RTA(container); - } else - return -ENOTSUP; - - break; - } - default: - return -ENOTSUP; - } - break; - default: - return -ENOTSUP; - } - break; - default: - return -ENOTSUP; - } - } else - return -ENOTSUP; + m->n_containers ++; r = rtnl_message_parse(m, - &m->rta_offset_tb[m->n_containers + 1], - &m->rta_tb_size[m->n_containers + 1], - max, + &m->rta_offset_tb[m->n_containers], + &m->rta_tb_size[m->n_containers], + type_system->max, container, - container_length); - if (r < 0) + size); + if (r < 0) { + m->n_containers --; return r; + } - m->n_containers ++; + m->container_type_system[m->n_containers] = type_system; return 0; } @@ -1018,6 +854,7 @@ int sd_rtnl_message_exit_container(sd_rtnl_message *m) { free(m->rta_offset_tb[m->n_containers]); m->rta_offset_tb[m->n_containers] = NULL; + m->container_type_system[m->n_containers] = NULL; m->n_containers --; @@ -1128,6 +965,7 @@ int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m) { */ int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; + const NLType *nl_type; struct nlmsghdr *new_hdr; union { struct sockaddr sa; @@ -1144,7 +982,7 @@ int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) { if (r < 0) return r; - r = message_new(nl, &m, need); + r = message_new_empty(nl, &m, need); if (r < 0) return r; @@ -1169,41 +1007,24 @@ int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) { else len = (size_t) r; - /* check that the size matches the message type */ - switch (m->hdr->nlmsg_type) { - - case NLMSG_ERROR: - if (len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) - return -EIO; - break; + /* silently drop noop messages */ + if (m->hdr->nlmsg_type == NLMSG_NOOP) + return 0; - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_DELLINK: - case RTM_GETLINK: - if (len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) - return -EIO; - break; + /* check that we support this message type */ + r = type_system_get_type(NULL, &nl_type, m->hdr->nlmsg_type); + if (r < 0) { + if (r == -ENOTSUP) + log_debug("sd-rtnl: ignored message with unknown type: %u", + m->hdr->nlmsg_type); - case RTM_NEWADDR: - case RTM_DELADDR: - case RTM_GETADDR: - if (len < NLMSG_LENGTH(sizeof(struct ifaddrmsg))) - return -EIO; - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - case RTM_GETROUTE: - if (len < NLMSG_LENGTH(sizeof(struct rtmsg))) - return -EIO; - break; - case NLMSG_NOOP: - return 0; - default: - log_debug("sd-rtnl: ignored message with unknown type"); return 0; } + /* check that the size matches the message type */ + if (len < NLMSG_LENGTH(nl_type->size)) + return -EIO; + /* we probably allocated way too much memory, give it back */ new_hdr = realloc(m->hdr, len); if (!new_hdr) @@ -1222,14 +1043,11 @@ int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret) { } int sd_rtnl_message_rewind(sd_rtnl_message *m) { - struct ifinfomsg *ifi; - struct ifaddrmsg *ifa; - struct rtmsg *rtm; + const NLType *type; unsigned i; int r; assert_return(m, -EINVAL); - assert_return(m->hdr, -EINVAL); /* don't allow appending to message once parsed */ if (!m->sealed) @@ -1239,6 +1057,7 @@ int sd_rtnl_message_rewind(sd_rtnl_message *m) { free(m->rta_offset_tb[i]); m->rta_offset_tb[i] = NULL; m->rta_tb_size[i] = 0; + m->container_type_system[i] = NULL; } m->n_containers = 0; @@ -1248,57 +1067,27 @@ int sd_rtnl_message_rewind(sd_rtnl_message *m) { return 0; } - /* parse top-level attributes */ - switch(m->hdr->nlmsg_type) { - case NLMSG_NOOP: - case NLMSG_ERROR: - break; - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - ifi = NLMSG_DATA(m->hdr); - - r = rtnl_message_parse(m, - &m->rta_offset_tb[0], - &m->rta_tb_size[0], - IFLA_MAX, - IFLA_RTA(ifi), - IFLA_PAYLOAD(m->hdr)); - if (r < 0) - return r; - - break; - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - ifa = NLMSG_DATA(m->hdr); - - r = rtnl_message_parse(m, - &m->rta_offset_tb[0], - &m->rta_tb_size[0], - IFA_MAX, - IFA_RTA(ifa), - IFA_PAYLOAD(m->hdr)); - if (r < 0) - return r; - - break; - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - rtm = NLMSG_DATA(m->hdr); - - r = rtnl_message_parse(m, - &m->rta_offset_tb[0], - &m->rta_tb_size[0], - RTA_MAX, - RTM_RTA(rtm), - RTM_PAYLOAD(m->hdr)); - - break; - default: - return -ENOTSUP; + assert(m->hdr); + + r = type_system_get_type(NULL, &type, m->hdr->nlmsg_type); + if (r < 0) + return r; + + if (type->type == NLA_NESTED) { + const NLTypeSystem *type_system = type->type_system; + + assert(type_system); + + m->container_type_system[0] = type_system; + + r = rtnl_message_parse(m, + &m->rta_offset_tb[m->n_containers], + &m->rta_tb_size[m->n_containers], + type_system->max, + (char*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(type->size), + NLMSG_PAYLOAD(m->hdr, type->size)); + if (r < 0) + return r; } return 0; diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c new file mode 100644 index 0000000000..936073e49c --- /dev/null +++ b/src/libsystemd/sd-rtnl/rtnl-types.c @@ -0,0 +1,356 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen <teg@jklm.no> + + 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 <stddef.h> +#include <stdint.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <linux/in6.h> +#include <linux/veth.h> +#include <linux/if_bridge.h> +#include <linux/if_addr.h> +#include <linux/if.h> + +#include "macro.h" +#include "util.h" + +#include "rtnl-types.h" + +static const NLTypeSystem rtnl_link_type_system; + +static const NLType rtnl_link_info_data_veth_types[VETH_INFO_MAX + 1] = { + [VETH_INFO_PEER] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, +}; + + +static const NLType rtnl_link_info_data_macvlan_types[IFLA_MACVLAN_MAX + 1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, +}; + +static const NLType rtnl_link_info_data_bridge_types[IFLA_BRIDGE_MAX + 1] = { + [IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, + [IFLA_BRIDGE_MODE] = { .type = NLA_U16 }, +/* + [IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY, + .len = sizeof(struct bridge_vlan_info), }, +*/ +}; + +static const NLType rtnl_link_info_data_vlan_types[IFLA_VLAN_MAX + 1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, +/* + [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, +*/ + [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, +}; + +static const NLType rtnl_link_info_data_bond_types[IFLA_BOND_MAX + 1] = { + [IFLA_BOND_MODE] = { .type = NLA_U8 }, + [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, +#ifdef IFLA_BOND_MIIMON + [IFLA_BOND_MIIMON] = { .type = NLA_U32 }, + [IFLA_BOND_UPDELAY] = { .type = NLA_U32 }, + [IFLA_BOND_DOWNDELAY] = { .type = NLA_U32 }, + [IFLA_BOND_USE_CARRIER] = { .type = NLA_U8 }, + [IFLA_BOND_ARP_INTERVAL] = { .type = NLA_U32 }, +/* + [IFLA_BOND_ARP_IP_TARGET] = { .type = NLA_NESTED }, +*/ + [IFLA_BOND_ARP_VALIDATE] = { .type = NLA_U32 }, + [IFLA_BOND_ARP_ALL_TARGETS] = { .type = NLA_U32 }, + [IFLA_BOND_PRIMARY] = { .type = NLA_U32 }, + [IFLA_BOND_PRIMARY_RESELECT] = { .type = NLA_U8 }, + [IFLA_BOND_FAIL_OVER_MAC] = { .type = NLA_U8 }, + [IFLA_BOND_XMIT_HASH_POLICY] = { .type = NLA_U8 }, + [IFLA_BOND_RESEND_IGMP] = { .type = NLA_U32 }, + [IFLA_BOND_NUM_PEER_NOTIF] = { .type = NLA_U8 }, + [IFLA_BOND_ALL_SLAVES_ACTIVE] = { .type = NLA_U8 }, + [IFLA_BOND_MIN_LINKS] = { .type = NLA_U32 }, + [IFLA_BOND_LP_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BOND_PACKETS_PER_SLAVE] = { .type = NLA_U32 }, + [IFLA_BOND_AD_LACP_RATE] = { .type = NLA_U8 }, + [IFLA_BOND_AD_SELECT] = { .type = NLA_U8 }, +/* + [IFLA_BOND_AD_INFO] = { .type = NLA_NESTED }, +*/ +#endif +}; + +typedef enum NLUnionLinkInfoData { + NL_UNION_LINK_INFO_DATA_BOND, + NL_UNION_LINK_INFO_DATA_BRIDGE, + NL_UNION_LINK_INFO_DATA_VLAN, + NL_UNION_LINK_INFO_DATA_VETH, + NL_UNION_LINK_INFO_DATA_MACVLAN, + _NL_UNION_LINK_INFO_DATA_MAX, + _NL_UNION_LINK_INFO_DATA_INVALID = -1 +} NLUnionLinkInfoData; + +const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_; +NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_; + +static const char* const nl_union_link_info_data_table[_NL_UNION_LINK_INFO_DATA_MAX] = { + [NL_UNION_LINK_INFO_DATA_BOND] = "bond", + [NL_UNION_LINK_INFO_DATA_BRIDGE] = "bridge", + [NL_UNION_LINK_INFO_DATA_VLAN] = "vlan", + [NL_UNION_LINK_INFO_DATA_VETH] = "veth", + [NL_UNION_LINK_INFO_DATA_MACVLAN] = "macvlan", +}; + +DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); + +static const NLTypeSystem rtnl_link_info_data_type_systems[_NL_UNION_LINK_INFO_DATA_MAX] = { + [NL_UNION_LINK_INFO_DATA_BRIDGE] = { .max = ELEMENTSOF(rtnl_link_info_data_bridge_types) - 1, + .types = rtnl_link_info_data_bridge_types }, + [NL_UNION_LINK_INFO_DATA_VLAN] = { .max = ELEMENTSOF(rtnl_link_info_data_vlan_types) - 1, + .types = rtnl_link_info_data_vlan_types }, + [NL_UNION_LINK_INFO_DATA_VETH] = { .max = ELEMENTSOF(rtnl_link_info_data_veth_types) - 1, + .types = rtnl_link_info_data_veth_types }, + [NL_UNION_LINK_INFO_DATA_MACVLAN] = { .max = ELEMENTSOF(rtnl_link_info_data_macvlan_types) - 1, + .types = rtnl_link_info_data_macvlan_types }, +}; + +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 = IFLA_INFO_KIND, +}; + +static const NLType rtnl_link_info_types[IFLA_INFO_MAX + 1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_UNION, .type_system_union = &rtnl_link_info_data_type_system_union}, +/* + [IFLA_INFO_XSTATS], + [IFLA_INFO_SLAVE_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_SLAVE_DATA] = { .type = NLA_NESTED }, +*/ +}; + +static const NLTypeSystem rtnl_link_info_type_system = { + .max = ELEMENTSOF(rtnl_link_info_types) - 1, + .types = rtnl_link_info_types, +}; + +static const NLType rtnl_link_types[IFLA_MAX + 1] = { + [IFLA_ADDRESS] = { .type = NLA_ETHER_ADDR, }, + [IFLA_BROADCAST] = { .type = NLA_ETHER_ADDR, }, + [IFLA_IFNAME] = { .type = NLA_STRING, .size = IFNAMSIZ - 1, }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, +/* + [IFLA_QDISC], + [IFLA_STATS], + [IFLA_COST], + [IFLA_PRIORITY], +*/ + [IFLA_MASTER] = { .type = NLA_U32 }, +/* + [IFLA_WIRELESS], + [IFLA_PROTINFO], +*/ + [IFLA_TXQLEN] = { .type = NLA_U32 }, +/* + [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, +*/ + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED, .type_system = &rtnl_link_info_type_system }, + [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .size = IFALIASZ - 1 }, +/* + [IFLA_NUM_VF], + [IFLA_VFINFO_LIST] = {. type = NLA_NESTED, }, + [IFLA_STATS64], + [IFLA_VF_PORTS] = { .type = NLA_NESTED }, + [IFLA_PORT_SELF] = { .type = NLA_NESTED }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_VF_PORTS], + [IFLA_PORT_SELF], + [IFLA_AF_SPEC], +*/ + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, + [IFLA_EXT_MASK] = { .type = NLA_U32 }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, +/* + [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, +*/ +}; + +static const NLTypeSystem rtnl_link_type_system = { + .max = ELEMENTSOF(rtnl_link_types) - 1, + .types = rtnl_link_types, +}; + +static const NLType rtnl_address_types[IFA_MAX + 1] = { + [IFA_ADDRESS] = { .type = NLA_IN_ADDR }, + [IFA_LOCAL] = { .type = NLA_IN_ADDR }, + [IFA_LABEL] = { .type = NLA_STRING, .size = IFNAMSIZ - 1 }, + [IFA_BROADCAST] = { .type = NLA_IN_ADDR }, /* 6? */ +/* + [IFA_ANYCAST], + [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, + [IFA_MULTICAST], +*/ +#ifdef IFA_FLAGS + [IFA_FLAGS] = { .type = NLA_U32 }, +#endif +}; + +static const NLTypeSystem rtnl_address_type_system = { + .max = ELEMENTSOF(rtnl_address_types) - 1, + .types = rtnl_address_types, +}; + +static const NLType rtnl_route_types[RTA_MAX + 1] = { + [RTA_DST] = { .type = NLA_IN_ADDR }, /* 6? */ + [RTA_SRC] = { .type = NLA_IN_ADDR }, /* 6? */ + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_GATEWAY] = { .type = NLA_IN_ADDR }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_PREFSRC] = { .type = NLA_IN_ADDR }, /* 6? */ +/* + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, +*/ + [RTA_FLOW] = { .type = NLA_U32 }, /* 6? */ +/* + RTA_CACHEINFO, + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, +*/ +}; + +static const NLTypeSystem rtnl_route_type_system = { + .max = ELEMENTSOF(rtnl_route_types) - 1, + .types = rtnl_route_types, +}; + +static const NLType rtnl_types[RTM_MAX + 1] = { + [NLMSG_ERROR] = { .type = NLA_META, .size = sizeof(struct nlmsgerr) }, + [RTM_NEWLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_DELLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_GETLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_SETLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_NEWADDR] = { .type = NLA_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_DELADDR] = { .type = NLA_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_GETADDR] = { .type = NLA_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_NEWROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_DELROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_GETROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, +}; + +const NLTypeSystem rtnl_type_system = { + .max = ELEMENTSOF(rtnl_types) - 1, + .types = rtnl_types, +}; + +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { + const NLType *nl_type; + + assert(ret); + + if (!type_system) + type_system = &rtnl_type_system; + + assert(type_system->types); + + if (type > type_system->max) + return -ENOTSUP; + + nl_type = &type_system->types[type]; + + if (nl_type->type == NLA_UNSPEC) + return -ENOTSUP; + + *ret = nl_type; + + return 0; +} + +int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + assert_return(nl_type->type == NLA_NESTED, -EINVAL); + + assert(nl_type->type_system); + + *ret = nl_type->type_system; + + return 0; +} + +int type_system_get_type_system_union(const NLTypeSystem *type_system, const NLTypeSystemUnion **ret, uint16_t type) { + const NLType *nl_type; + int r; + + assert(ret); + + r = type_system_get_type(type_system, &nl_type, type); + if (r < 0) + return r; + + assert_return(nl_type->type == NLA_UNION, -EINVAL); + + assert(nl_type->type_system_union); + + *ret = nl_type->type_system_union; + + return 0; +} + +int type_system_union_get_type_system(const NLTypeSystemUnion *type_system_union, const NLTypeSystem **ret, const char *key) { + int type; + + assert(type_system_union); + assert(type_system_union->lookup); + assert(type_system_union->type_systems); + assert(ret); + assert(key); + + type = type_system_union->lookup(key); + if (type < 0) + return -ENOTSUP; + + assert(type < type_system_union->num); + + *ret = &type_system_union->type_systems[type]; + + return 0; +} diff --git a/src/libsystemd/sd-rtnl/rtnl-types.h b/src/libsystemd/sd-rtnl/rtnl-types.h new file mode 100644 index 0000000000..2425dc92a3 --- /dev/null +++ b/src/libsystemd/sd-rtnl/rtnl-types.h @@ -0,0 +1,64 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen <teg@jklm.no> + + 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/>. +***/ + +enum { + NLA_UNSPEC, + NLA_META, + NLA_U8, + NLA_U16, + NLA_U32, + NLA_U64, + NLA_STRING, + NLA_IN_ADDR, + NLA_ETHER_ADDR, + NLA_NESTED, + NLA_UNION, +}; + +typedef struct NLTypeSystemUnion NLTypeSystemUnion; +typedef struct NLTypeSystem NLTypeSystem; +typedef struct NLType NLType; + +struct NLTypeSystemUnion { + int num; + uint16_t match; + int (*lookup)(const char *); + const NLTypeSystem *type_systems; +}; + +struct NLTypeSystem { + uint16_t max; + const NLType *types; +}; + +struct NLType { + uint16_t type; + size_t size; + const NLTypeSystem *type_system; + const NLTypeSystemUnion *type_system_union; +}; + +int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); +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); diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c index 8f1f95353f..44369628f4 100644 --- a/src/libsystemd/sd-rtnl/test-rtnl.c +++ b/src/libsystemd/sd-rtnl/test-rtnl.c @@ -279,10 +279,7 @@ static void test_container(void) { assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); assert_se(sd_rtnl_message_open_container(m, IFLA_LINKINFO) >= 0); - assert_se(sd_rtnl_message_open_container(m, IFLA_LINKINFO) == -ENOTSUP); - assert_se(sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); - assert_se(sd_rtnl_message_open_container(m, IFLA_INFO_DATA) >= 0); - assert_se(sd_rtnl_message_open_container(m, IFLA_INFO_DATA) == -ENOTSUP); + assert_se(sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); assert_se(sd_rtnl_message_append_u16(m, IFLA_VLAN_ID, 100) >= 0); assert_se(sd_rtnl_message_close_container(m) >= 0); assert_se(sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "vlan") >= 0); @@ -303,7 +300,7 @@ static void test_container(void) { assert_se(streq("vlan", string_data)); assert_se(sd_rtnl_message_exit_container(m) >= 0); - assert_se(sd_rtnl_message_read_u32(m, IFLA_LINKINFO, &u32_data) == 0); + assert_se(sd_rtnl_message_read_u32(m, IFLA_LINKINFO, &u32_data) < 0); assert_se(sd_rtnl_message_exit_container(m) == -EINVAL); } diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index e333c47b1f..15a5d7cdff 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -291,48 +291,30 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c return -EINVAL; } - r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind); + r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind); if (r < 0) { log_error_netdev(netdev, - "Could not append IFLA_INFO_KIND attribute: %s", - strerror(-r)); + "Could not open IFLA_INFO_DATA container: %s", + strerror(-r)); return r; } - if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { - r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA); + if (netdev->vlanid <= VLANID_MAX) { + r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid); if (r < 0) { log_error_netdev(netdev, - "Could not open IFLA_INFO_DATA container: %s", + "Could not append IFLA_VLAN_ID attribute: %s", strerror(-r)); return r; } + } - if (netdev->vlanid <= VLANID_MAX) { - r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid); - if (r < 0) { - log_error_netdev(netdev, - "Could not append IFLA_VLAN_ID attribute: %s", - strerror(-r)); - return r; - } - } - - if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { - r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode); - if (r < 0) { - log_error_netdev(netdev, - "Could not append IFLA_MACVLAN_MODE attribute: %s", - strerror(-r)); - return r; - } - } - - r = sd_rtnl_message_close_container(req); - if (r < 0) { - log_error_netdev(netdev, - "Could not close IFLA_INFO_DATA container %s", - strerror(-r)); + if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) { + r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode); + if (r < 0) { + log_error_netdev(netdev, + "Could not append IFLA_MACVLAN_MODE attribute: %s", + strerror(-r)); return r; } } @@ -340,6 +322,14 @@ static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t c r = sd_rtnl_message_close_container(req); if (r < 0) { log_error_netdev(netdev, + "Could not close IFLA_INFO_DATA container %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_close_container(req); + if (r < 0) { + log_error_netdev(netdev, "Could not close IFLA_LINKINFO container %s", strerror(-r)); return r; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 9a9ed9dc6e..84724d59c9 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1486,13 +1486,7 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ]) { return r; } - r = sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "veth"); - if (r < 0) { - log_error("Failed to append netlink kind: %s", strerror(-r)); - return r; - } - - r = sd_rtnl_message_open_container(m, IFLA_INFO_DATA); + r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "veth"); if (r < 0) { log_error("Failed to open netlink container: %s", strerror(-r)); return r; @@ -1757,13 +1751,7 @@ static int setup_macvlan(pid_t pid) { return r; } - r = sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "macvlan"); - if (r < 0) { - log_error("Failed to append netlink kind: %s", strerror(-r)); - return r; - } - - r = sd_rtnl_message_open_container(m, IFLA_INFO_DATA); + r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); if (r < 0) { log_error("Failed to open netlink container: %s", strerror(-r)); return r; diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h index 54e5142456..80e88e3838 100644 --- a/src/systemd/sd-rtnl.h +++ b/src/systemd/sd-rtnl.h @@ -101,6 +101,7 @@ int sd_rtnl_message_append_in6_addr(sd_rtnl_message *m, unsigned short type, con int sd_rtnl_message_append_ether_addr(sd_rtnl_message *m, unsigned short type, const struct ether_addr *data); int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type); +int sd_rtnl_message_open_container_union(sd_rtnl_message *m, unsigned short type, const char *key); int sd_rtnl_message_close_container(sd_rtnl_message *m); int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, char **data); |