This is more conventional use of Netlink. For upstreaming, 'u64 attrs' can be changed to u32 and the uses of 1ULL can be changed to 1.
Signed-off-by: Ben Pfaff <b...@nicira.com> --- datapath/flow.c | 484 ++++++++++++++++++++++++++---------------------------- lib/odp-util.c | 418 +++++++++++++++++++++++++----------------------- 2 files changed, 453 insertions(+), 449 deletions(-) diff --git a/datapath/flow.c b/datapath/flow.c index 9786c37..6caca2a 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -862,288 +862,270 @@ const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_TUN_ID] = sizeof(__be64), }; +static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len, + const struct nlattr *a[], u64 *attrs) +{ + const struct ovs_key_icmp *icmp_key; + const struct ovs_key_tcp *tcp_key; + const struct ovs_key_udp *udp_key; + + switch (swkey->ip.proto) { + case IPPROTO_TCP: + if (!(*attrs & (1 << OVS_KEY_ATTR_TCP))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_TCP); + + *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); + swkey->ipv4.tp.src = tcp_key->tcp_src; + swkey->ipv4.tp.dst = tcp_key->tcp_dst; + break; + + case IPPROTO_UDP: + if (!(*attrs & (1 << OVS_KEY_ATTR_UDP))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_UDP); + + *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); + swkey->ipv4.tp.src = udp_key->udp_src; + swkey->ipv4.tp.dst = udp_key->udp_dst; + break; + + case IPPROTO_ICMP: + if (!(*attrs & (1 << OVS_KEY_ATTR_ICMP))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_ICMP); + + *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); + icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); + swkey->ipv4.tp.src = htons(icmp_key->icmp_type); + swkey->ipv4.tp.dst = htons(icmp_key->icmp_code); + break; + } + + return 0; +} + +static int ipv6_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len, + const struct nlattr *a[], u64 *attrs) +{ + const struct ovs_key_icmpv6 *icmpv6_key; + const struct ovs_key_tcp *tcp_key; + const struct ovs_key_udp *udp_key; + + switch (swkey->ip.proto) { + case IPPROTO_TCP: + if (!(*attrs & (1 << OVS_KEY_ATTR_TCP))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_TCP); + + *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); + tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); + swkey->ipv6.tp.src = tcp_key->tcp_src; + swkey->ipv6.tp.dst = tcp_key->tcp_dst; + break; + + case IPPROTO_UDP: + if (!(*attrs & (1 << OVS_KEY_ATTR_UDP))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_UDP); + + *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); + udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); + swkey->ipv6.tp.src = udp_key->udp_src; + swkey->ipv6.tp.dst = udp_key->udp_dst; + break; + + case IPPROTO_ICMPV6: + if (!(*attrs & (1 << OVS_KEY_ATTR_ICMPV6))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); + + *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); + icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); + swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type); + swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code); + + if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || + swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { + const struct ovs_key_nd *nd_key; + + if (!(*attrs & (1 << OVS_KEY_ATTR_ND))) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_ND); + + *key_len = SW_FLOW_KEY_OFFSET(ipv6.nd); + nd_key = nla_data(a[OVS_KEY_ATTR_ND]); + memcpy(&swkey->ipv6.nd.target, nd_key->nd_target, + sizeof(swkey->ipv6.nd.target)); + memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN); + memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN); + } + break; + } + + return 0; +} + /** * flow_from_nlattrs - parses Netlink attributes into a flow key. * @swkey: receives the extracted flow key. * @key_lenp: number of bytes used in @swkey. * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. - * - * This state machine accepts the following forms, with [] for optional - * elements and | for alternatives: - * - * [priority] [tun_id] [in_port] ethernet [8021q] [ethertype \ - * [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6 [ND]] | ARP]] - * - * except that IPv4 or IPv6 terminates the sequence if its @ipv4_frag or - * @ipv6_frag member, respectively, equals %OVS_FRAG_TYPE_LATER. */ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, const struct nlattr *attr) { - int error = 0; + const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; + const struct ovs_key_ethernet *eth_key; const struct nlattr *nla; - u16 prev_type; - int rem; int key_len; + u64 attrs; + int rem; - memset(swkey, 0, sizeof(*swkey)); - swkey->phy.in_port = USHRT_MAX; - swkey->eth.type = htons(ETH_P_802_2); + memset(swkey, 0, sizeof(struct sw_flow_key)); key_len = SW_FLOW_KEY_OFFSET(eth); - prev_type = OVS_KEY_ATTR_UNSPEC; + attrs = 0; nla_for_each_nested(nla, attr, rem) { - const struct ovs_key_ethernet *eth_key; - const struct ovs_key_8021q *q_key; - const struct ovs_key_ipv4 *ipv4_key; - const struct ovs_key_ipv6 *ipv6_key; - const struct ovs_key_tcp *tcp_key; - const struct ovs_key_udp *udp_key; - const struct ovs_key_icmp *icmp_key; - const struct ovs_key_icmpv6 *icmpv6_key; - const struct ovs_key_arp *arp_key; - const struct ovs_key_nd *nd_key; - - int type = nla_type(nla); + u16 type = nla_type(nla); - if (type > OVS_KEY_ATTR_MAX || + if (type > OVS_KEY_ATTR_MAX || attrs & (1ULL << type) || nla_len(nla) != ovs_key_lens[type]) - goto invalid; - -#define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) - switch (TRANSITION(prev_type, type)) { - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_PRIORITY): - swkey->phy.priority = nla_get_u32(nla); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_TUN_ID): - swkey->phy.tun_id = nla_get_be64(nla); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_IN_PORT): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_IN_PORT): - case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_IN_PORT): - if (nla_get_u32(nla) >= DP_MAX_PORTS) - goto invalid; - swkey->phy.in_port = nla_get_u32(nla); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_IN_PORT, OVS_KEY_ATTR_ETHERNET): - eth_key = nla_data(nla); - memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN); - memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN); - break; - - case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_8021Q): - q_key = nla_data(nla); - /* Only standard 0x8100 VLANs currently supported. */ - if (q_key->q_tpid != htons(ETH_P_8021Q)) - goto invalid; - if (q_key->q_tci & htons(VLAN_TAG_PRESENT)) - goto invalid; - swkey->eth.tci = q_key->q_tci | htons(VLAN_TAG_PRESENT); - break; + return -EINVAL; + attrs |= 1ULL << type; + a[type] = nla; + } + if (rem) + return -EINVAL; - case TRANSITION(OVS_KEY_ATTR_8021Q, OVS_KEY_ATTR_ETHERTYPE): - case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_ETHERTYPE): - swkey->eth.type = nla_get_be16(nla); - if (ntohs(swkey->eth.type) < 1536) - goto invalid; - break; + /* Metadata attributes. */ + if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { + swkey->phy.priority = nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]); + attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY); + } + if (attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { + u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); + if (in_port >= DP_MAX_PORTS) + return -EINVAL; + swkey->phy.in_port = in_port; + attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); + } else { + swkey->phy.in_port = USHRT_MAX; + } - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV4): - key_len = SW_FLOW_KEY_OFFSET(ipv4.addr); - if (swkey->eth.type != htons(ETH_P_IP)) - goto invalid; - ipv4_key = nla_data(nla); - if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) - goto invalid; - swkey->ip.proto = ipv4_key->ipv4_proto; - swkey->ip.tos = ipv4_key->ipv4_tos; - swkey->ip.ttl = ipv4_key->ipv4_ttl; - swkey->ip.frag = ipv4_key->ipv4_frag; - swkey->ipv4.addr.src = ipv4_key->ipv4_src; - swkey->ipv4.addr.dst = ipv4_key->ipv4_dst; - break; - - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV6): - key_len = SW_FLOW_KEY_OFFSET(ipv6.label); - if (swkey->eth.type != htons(ETH_P_IPV6)) - goto invalid; - ipv6_key = nla_data(nla); - if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) - goto invalid; - swkey->ipv6.label = ipv6_key->ipv6_label; - swkey->ip.proto = ipv6_key->ipv6_proto; - swkey->ip.tos = ipv6_key->ipv6_tclass; - swkey->ip.ttl = ipv6_key->ipv6_hlimit; - swkey->ip.frag = ipv6_key->ipv6_frag; - memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src, - sizeof(swkey->ipv6.addr.src)); - memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst, - sizeof(swkey->ipv6.addr.dst)); - break; - - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_TCP): - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (swkey->ip.proto != IPPROTO_TCP) - goto invalid; - tcp_key = nla_data(nla); - swkey->ipv4.tp.src = tcp_key->tcp_src; - swkey->ipv4.tp.dst = tcp_key->tcp_dst; - break; + if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID)) { + swkey->phy.tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]); + attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID); + } - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_TCP): - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - if (swkey->ip.proto != IPPROTO_TCP) - goto invalid; - tcp_key = nla_data(nla); - swkey->ipv6.tp.src = tcp_key->tcp_src; - swkey->ipv6.tp.dst = tcp_key->tcp_dst; - break; + /* Data attributes. */ + if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET))) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_UDP): - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (swkey->ip.proto != IPPROTO_UDP) - goto invalid; - udp_key = nla_data(nla); - swkey->ipv4.tp.src = udp_key->udp_src; - swkey->ipv4.tp.dst = udp_key->udp_dst; - break; + eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); + memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN); + memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN); - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_UDP): - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - if (swkey->ip.proto != IPPROTO_UDP) - goto invalid; - udp_key = nla_data(nla); - swkey->ipv6.tp.src = udp_key->udp_src; - swkey->ipv6.tp.dst = udp_key->udp_dst; - break; + if (attrs & (1 << OVS_KEY_ATTR_8021Q)) { + const struct ovs_key_8021q *q_key; - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_ICMP): - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - if (swkey->ip.proto != IPPROTO_ICMP) - goto invalid; - icmp_key = nla_data(nla); - swkey->ipv4.tp.src = htons(icmp_key->icmp_type); - swkey->ipv4.tp.dst = htons(icmp_key->icmp_code); - break; + q_key = nla_data(a[OVS_KEY_ATTR_8021Q]); + /* Only standard 0x8100 VLANs currently supported. */ + if (q_key->q_tpid != htons(ETH_P_8021Q)) + return -EINVAL; + if (q_key->q_tci & htons(VLAN_TAG_PRESENT)) + return -EINVAL; + swkey->eth.tci = q_key->q_tci | htons(VLAN_TAG_PRESENT); - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_ICMPV6): - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - if (swkey->ip.proto != IPPROTO_ICMPV6) - goto invalid; - icmpv6_key = nla_data(nla); - swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type); - swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code); - break; + attrs &= ~(1 << OVS_KEY_ATTR_8021Q); + } - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_ARP): - key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); - if (swkey->eth.type != htons(ETH_P_ARP)) - goto invalid; - arp_key = nla_data(nla); - swkey->ipv4.addr.src = arp_key->arp_sip; - swkey->ipv4.addr.dst = arp_key->arp_tip; - if (arp_key->arp_op & htons(0xff00)) - goto invalid; - swkey->ip.proto = ntohs(arp_key->arp_op); - memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN); - memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN); - break; - - case TRANSITION(OVS_KEY_ATTR_ICMPV6, OVS_KEY_ATTR_ND): - key_len = SW_FLOW_KEY_OFFSET(ipv6.nd); - if (swkey->ipv6.tp.src != htons(NDISC_NEIGHBOUR_SOLICITATION) - && swkey->ipv6.tp.src != htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) - goto invalid; - nd_key = nla_data(nla); - memcpy(&swkey->ipv6.nd.target, nd_key->nd_target, - sizeof(swkey->ipv6.nd.target)); - memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN); - memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN); - break; + if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { + swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + if (ntohs(swkey->eth.type) < 1536) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + } else { + swkey->eth.type = htons(ETH_P_802_2); + } + + + if (swkey->eth.type == htons(ETH_P_IP)) { + const struct ovs_key_ipv4 *ipv4_key; + + if (!(attrs & (1 << OVS_KEY_ATTR_IPV4))) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_IPV4); - default: - goto invalid; + key_len = SW_FLOW_KEY_OFFSET(ipv4.addr); + ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); + if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) + return -EINVAL; + swkey->ip.proto = ipv4_key->ipv4_proto; + swkey->ip.tos = ipv4_key->ipv4_tos; + swkey->ip.ttl = ipv4_key->ipv4_ttl; + swkey->ip.frag = ipv4_key->ipv4_frag; + swkey->ipv4.addr.src = ipv4_key->ipv4_src; + swkey->ipv4.addr.dst = ipv4_key->ipv4_dst; + + if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) { + int err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs); + if (err) + return err; } + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + const struct ovs_key_ipv6 *ipv6_key; - prev_type = type; - } - if (rem) - goto invalid; - - switch (prev_type) { - case OVS_KEY_ATTR_UNSPEC: - goto invalid; - - case OVS_KEY_ATTR_PRIORITY: - case OVS_KEY_ATTR_TUN_ID: - case OVS_KEY_ATTR_IN_PORT: - goto invalid; - - case OVS_KEY_ATTR_ETHERNET: - case OVS_KEY_ATTR_8021Q: - goto ok; - - case OVS_KEY_ATTR_ETHERTYPE: - if (swkey->eth.type == htons(ETH_P_IP) || - swkey->eth.type == htons(ETH_P_IPV6) || - swkey->eth.type == htons(ETH_P_ARP)) - goto invalid; - goto ok; - - case OVS_KEY_ATTR_IPV4: - if (swkey->ip.frag == OVS_FRAG_TYPE_LATER) - goto ok; - if (swkey->ip.proto == IPPROTO_TCP || - swkey->ip.proto == IPPROTO_UDP || - swkey->ip.proto == IPPROTO_ICMP) - goto invalid; - goto ok; - - case OVS_KEY_ATTR_IPV6: - if (swkey->ip.frag == OVS_FRAG_TYPE_LATER) - goto ok; - if (swkey->ip.proto == IPPROTO_TCP || - swkey->ip.proto == IPPROTO_UDP || - swkey->ip.proto == IPPROTO_ICMPV6) - goto invalid; - goto ok; - - case OVS_KEY_ATTR_ICMPV6: - if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || - swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT) || - swkey->ip.frag == OVS_FRAG_TYPE_LATER) - goto invalid; - goto ok; - - case OVS_KEY_ATTR_TCP: - case OVS_KEY_ATTR_UDP: - case OVS_KEY_ATTR_ICMP: - case OVS_KEY_ATTR_ND: - if (swkey->ip.frag == OVS_FRAG_TYPE_LATER) - goto invalid; - goto ok; - - case OVS_KEY_ATTR_ARP: - goto ok; - - default: - WARN_ON_ONCE(1); - } + if (!(attrs & (1 << OVS_KEY_ATTR_IPV6))) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_IPV6); + + key_len = SW_FLOW_KEY_OFFSET(ipv6.label); + ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); + if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) + return -EINVAL; + swkey->ipv6.label = ipv6_key->ipv6_label; + swkey->ip.proto = ipv6_key->ipv6_proto; + swkey->ip.tos = ipv6_key->ipv6_tclass; + swkey->ip.ttl = ipv6_key->ipv6_hlimit; + swkey->ip.frag = ipv6_key->ipv6_frag; + memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src, + sizeof(swkey->ipv6.addr.src)); + memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst, + sizeof(swkey->ipv6.addr.dst)); + + if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) { + int err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs); + if (err) + return err; + } + } else if (swkey->eth.type == htons(ETH_P_ARP)) { + const struct ovs_key_arp *arp_key; -invalid: - error = -EINVAL; + if (!(attrs & (1 << OVS_KEY_ATTR_ARP))) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_ARP); + + key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); + arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); + swkey->ipv4.addr.src = arp_key->arp_sip; + swkey->ipv4.addr.dst = arp_key->arp_tip; + if (arp_key->arp_op & htons(0xff00)) + return -EINVAL; + swkey->ip.proto = ntohs(arp_key->arp_op); + memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN); + memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN); + } -ok: + if (attrs) + return -EINVAL; *key_lenp = key_len; - return error; + + return 0; } /** diff --git a/lib/odp-util.c b/lib/odp-util.c index a8da627..c70ab11 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -33,6 +33,9 @@ #include "packets.h" #include "timeval.h" #include "util.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(odp_util); /* The interface between userspace and kernel uses an "OVS_*" prefix. * Since this is fairly non-specific for the OVS userspace components, @@ -825,8 +828,7 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) * * On success, the attributes appended to 'key' are individually syntactically * valid, but they may not be valid as a sequence. 'key' might, for example, - * be missing an "in_port" key, have duplicated keys, or have keys in the wrong - * order. odp_flow_key_to_flow() will detect those errors. */ + * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */ int odp_flow_key_from_string(const char *s, struct ofpbuf *key) { @@ -983,10 +985,41 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) } } +static void +log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title, + uint32_t attrs, + const struct nlattr *key, size_t key_len) +{ + struct ds s; + int i; + + if (VLOG_DROP_WARN(rl)) { + return; + } + + ds_init(&s); + ds_put_format(&s, "%s:", title); + for (i = 0; i < 32; i++) { + if (attrs & (1u << i)) { + ds_put_format(&s, " %s", ovs_key_attr_to_string(i)); + } + } + + ds_put_cstr(&s, ": "); + odp_flow_key_format(key, key_len, &s); + + VLOG_WARN("%s", ds_cstr(&s)); + ds_destroy(&s); +} + static bool odp_to_ovs_frag(uint8_t odp_frag, struct flow *flow) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + if (odp_frag > OVS_FRAG_TYPE_LATER) { + VLOG_ERR_RL(&rl, "invalid frag %"PRIu8" in flow key", + odp_frag); return false; } @@ -1005,88 +1038,121 @@ int odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, struct flow *flow) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1]; const struct nlattr *nla; - enum ovs_key_attr prev_type; + uint64_t expected_attrs; + uint64_t present_attrs; + uint64_t missing_attrs; + uint64_t extra_attrs; size_t left; memset(flow, 0, sizeof *flow); - flow->dl_type = htons(FLOW_DL_TYPE_NONE); - flow->in_port = OFPP_NONE; - prev_type = OVS_KEY_ATTR_UNSPEC; + memset(attrs, 0, sizeof attrs); + + present_attrs = 0; + expected_attrs = 0; NL_ATTR_FOR_EACH (nla, left, key, key_len) { + uint16_t type = nl_attr_type(nla); + size_t len = nl_attr_get_size(nla); + int expected_len = odp_flow_key_attr_len(type); + + if (len != expected_len) { + if (expected_len == -1) { + VLOG_ERR_RL(&rl, "unknown attribute %"PRIu16" in flow key", + type); + } else { + VLOG_ERR_RL(&rl, "attribute %s has length %zu but should have " + "length %d", ovs_key_attr_to_string(type), + len, expected_len); + } + return EINVAL; + } else if (present_attrs & (UINT64_C(1) << type)) { + VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key", + ovs_key_attr_to_string(type)); + return EINVAL; + } + + present_attrs |= UINT64_C(1) << type; + attrs[type] = nla; + } + if (left) { + VLOG_ERR_RL(&rl, "trailing garbage in flow key"); + return EINVAL; + } + + if (attrs[OVS_KEY_ATTR_PRIORITY]) { + flow->priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]); + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY; + } + + if (attrs[OVS_KEY_ATTR_TUN_ID]) { + flow->tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]); + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID; + } + + if (attrs[OVS_KEY_ATTR_IN_PORT]) { + uint32_t in_port = nl_attr_get_u32(attrs[OVS_KEY_ATTR_IN_PORT]); + if (in_port >= UINT16_MAX || in_port >= OFPP_MAX) { + VLOG_ERR_RL(&rl, "in_port %"PRIu32" out of supported range", + in_port); + return EINVAL; + } + flow->in_port = odp_port_to_ofp_port(in_port); + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT; + } else { + flow->in_port = OFPP_NONE; + } + + if (attrs[OVS_KEY_ATTR_ETHERNET]) { const struct ovs_key_ethernet *eth_key; - const struct ovs_key_8021q *q_key; - const struct ovs_key_ipv4 *ipv4_key; - const struct ovs_key_ipv6 *ipv6_key; - const struct ovs_key_tcp *tcp_key; - const struct ovs_key_udp *udp_key; - const struct ovs_key_icmp *icmp_key; - const struct ovs_key_icmpv6 *icmpv6_key; - const struct ovs_key_arp *arp_key; - const struct ovs_key_nd *nd_key; - uint16_t type = nl_attr_type(nla); - int len = odp_flow_key_attr_len(type); + eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]); + memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN); + memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN); + } else { + VLOG_ERR_RL(&rl, "missing Ethernet attribute in flow key"); + return EINVAL; + } + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; + + if (attrs[OVS_KEY_ATTR_8021Q]) { + const struct ovs_key_8021q *q_key; - if (nl_attr_get_size(nla) != len && len != -1) { + q_key = nl_attr_get(attrs[OVS_KEY_ATTR_8021Q]); + if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { + VLOG_ERR_RL(&rl, "unsupported 802.1Q TPID %"PRIu16" in flow key", + ntohs(q_key->q_tpid)); + return EINVAL; + } + if (q_key->q_tci & htons(VLAN_CFI)) { + VLOG_ERR_RL(&rl, "invalid 802.1Q TCI %"PRIu16" in flow key", + ntohs(q_key->q_tci)); return EINVAL; } + flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_8021Q; + } -#define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE)) - switch (TRANSITION(prev_type, type)) { - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_PRIORITY): - flow->priority = nl_attr_get_u32(nla); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_TUN_ID): - flow->tun_id = nl_attr_get_be64(nla); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_IN_PORT): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_IN_PORT): - case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_IN_PORT): - if (nl_attr_get_u32(nla) >= UINT16_MAX) { - return EINVAL; - } - flow->in_port = odp_port_to_ofp_port(nl_attr_get_u32(nla)); - break; - - case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_PRIORITY, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_TUN_ID, OVS_KEY_ATTR_ETHERNET): - case TRANSITION(OVS_KEY_ATTR_IN_PORT, OVS_KEY_ATTR_ETHERNET): - eth_key = nl_attr_get(nla); - memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN); - memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN); - break; - - case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_8021Q): - q_key = nl_attr_get(nla); - if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { - /* Only standard 0x8100 VLANs currently supported. */ - return EINVAL; - } - if (q_key->q_tci & htons(VLAN_CFI)) { - return EINVAL; - } - flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); - break; + if (attrs[OVS_KEY_ATTR_ETHERTYPE]) { + flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]); + if (ntohs(flow->dl_type) < 1536) { + VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key", + ntohs(flow->dl_type)); + return EINVAL; + } + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE; + } else { + flow->dl_type = htons(FLOW_DL_TYPE_NONE); + } - case TRANSITION(OVS_KEY_ATTR_8021Q, OVS_KEY_ATTR_ETHERTYPE): - case TRANSITION(OVS_KEY_ATTR_ETHERNET, OVS_KEY_ATTR_ETHERTYPE): - flow->dl_type = nl_attr_get_be16(nla); - if (ntohs(flow->dl_type) < 1536) { - return EINVAL; - } - break; + if (flow->dl_type == htons(ETH_TYPE_IP)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4; + if (attrs[OVS_KEY_ATTR_IPV4]) { + const struct ovs_key_ipv4 *ipv4_key; - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV4): - if (flow->dl_type != htons(ETH_TYPE_IP)) { - return EINVAL; - } - ipv4_key = nl_attr_get(nla); + ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]); flow->nw_src = ipv4_key->ipv4_src; flow->nw_dst = ipv4_key->ipv4_dst; flow->nw_proto = ipv4_key->ipv4_proto; @@ -1095,13 +1161,13 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) { return EINVAL; } - break; + } + } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6; + if (attrs[OVS_KEY_ATTR_IPV6]) { + const struct ovs_key_ipv6 *ipv6_key; - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_IPV6): - if (flow->dl_type != htons(ETH_TYPE_IPV6)) { - return EINVAL; - } - ipv6_key = nl_attr_get(nla); + ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]); memcpy(&flow->ipv6_src, ipv6_key->ipv6_src, sizeof flow->ipv6_src); memcpy(&flow->ipv6_dst, ipv6_key->ipv6_dst, sizeof flow->ipv6_dst); flow->ipv6_label = ipv6_key->ipv6_label; @@ -1111,147 +1177,103 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) { return EINVAL; } - break; - - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_TCP): - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_TCP): - if (flow->nw_proto != IPPROTO_TCP) { - return EINVAL; - } - tcp_key = nl_attr_get(nla); - flow->tp_src = tcp_key->tcp_src; - flow->tp_dst = tcp_key->tcp_dst; - break; - - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_UDP): - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_UDP): - if (flow->nw_proto != IPPROTO_UDP) { - return EINVAL; - } - udp_key = nl_attr_get(nla); - flow->tp_src = udp_key->udp_src; - flow->tp_dst = udp_key->udp_dst; - break; - - case TRANSITION(OVS_KEY_ATTR_IPV4, OVS_KEY_ATTR_ICMP): - if (flow->nw_proto != IPPROTO_ICMP) { - return EINVAL; - } - icmp_key = nl_attr_get(nla); - flow->tp_src = htons(icmp_key->icmp_type); - flow->tp_dst = htons(icmp_key->icmp_code); - break; - - case TRANSITION(OVS_KEY_ATTR_IPV6, OVS_KEY_ATTR_ICMPV6): - if (flow->nw_proto != IPPROTO_ICMPV6) { - return EINVAL; - } - icmpv6_key = nl_attr_get(nla); - flow->tp_src = htons(icmpv6_key->icmpv6_type); - flow->tp_dst = htons(icmpv6_key->icmpv6_code); - break; + } + } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP; + if (attrs[OVS_KEY_ATTR_ARP]) { + const struct ovs_key_arp *arp_key; - case TRANSITION(OVS_KEY_ATTR_ETHERTYPE, OVS_KEY_ATTR_ARP): - if (flow->dl_type != htons(ETH_TYPE_ARP)) { - return EINVAL; - } - arp_key = nl_attr_get(nla); + arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]); flow->nw_src = arp_key->arp_sip; flow->nw_dst = arp_key->arp_tip; if (arp_key->arp_op & htons(0xff00)) { + VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow " + "key", ntohs(arp_key->arp_op)); return EINVAL; } flow->nw_proto = ntohs(arp_key->arp_op); memcpy(flow->arp_sha, arp_key->arp_sha, ETH_ADDR_LEN); memcpy(flow->arp_tha, arp_key->arp_tha, ETH_ADDR_LEN); - break; - - case TRANSITION(OVS_KEY_ATTR_ICMPV6, OVS_KEY_ATTR_ND): - if (flow->tp_src != htons(ND_NEIGHBOR_SOLICIT) - && flow->tp_src != htons(ND_NEIGHBOR_ADVERT)) { - return EINVAL; - } - nd_key = nl_attr_get(nla); - memcpy(&flow->nd_target, nd_key->nd_target, sizeof flow->nd_target); - memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN); - memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN); - break; - - default: - return EINVAL; } - - prev_type = type; } - if (left) { - return EINVAL; - } - - switch (prev_type) { - case OVS_KEY_ATTR_UNSPEC: - return EINVAL; - case OVS_KEY_ATTR_PRIORITY: - case OVS_KEY_ATTR_TUN_ID: - case OVS_KEY_ATTR_IN_PORT: - return EINVAL; - - case OVS_KEY_ATTR_ETHERNET: - case OVS_KEY_ATTR_8021Q: - return 0; - - case OVS_KEY_ATTR_ETHERTYPE: - if (flow->dl_type == htons(ETH_TYPE_IP) - || flow->dl_type == htons(ETH_TYPE_IPV6) - || flow->dl_type == htons(ETH_TYPE_ARP)) { - return EINVAL; - } - return 0; - - case OVS_KEY_ATTR_IPV4: - if (flow->nw_frag & FLOW_NW_FRAG_LATER) { - return 0; - } - if (flow->nw_proto == IPPROTO_TCP - || flow->nw_proto == IPPROTO_UDP - || flow->nw_proto == IPPROTO_ICMP) { - return EINVAL; - } - return 0; + if (flow->nw_proto == IPPROTO_TCP + && (flow->dl_type == htons(ETH_TYPE_IP) || + flow->dl_type == htons(ETH_TYPE_IPV6)) + && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP; + if (attrs[OVS_KEY_ATTR_TCP]) { + const struct ovs_key_tcp *tcp_key; - case OVS_KEY_ATTR_IPV6: - if (flow->nw_frag & FLOW_NW_FRAG_LATER) { - return 0; + tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]); + flow->tp_src = tcp_key->tcp_src; + flow->tp_dst = tcp_key->tcp_dst; } - if (flow->nw_proto == IPPROTO_TCP - || flow->nw_proto == IPPROTO_UDP - || flow->nw_proto == IPPROTO_ICMPV6) { - return EINVAL; + } else if (flow->nw_proto == IPPROTO_UDP + && (flow->dl_type == htons(ETH_TYPE_IP) || + flow->dl_type == htons(ETH_TYPE_IPV6)) + && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP; + if (attrs[OVS_KEY_ATTR_UDP]) { + const struct ovs_key_udp *udp_key; + + udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]); + flow->tp_src = udp_key->udp_src; + flow->tp_dst = udp_key->udp_dst; } - return 0; - - case OVS_KEY_ATTR_ICMPV6: - if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) - || flow->tp_src == htons(ND_NEIGHBOR_ADVERT) - || flow->nw_frag & FLOW_NW_FRAG_LATER) { - return EINVAL; + } else if (flow->nw_proto == IPPROTO_ICMP + && flow->dl_type == htons(ETH_TYPE_IP) + && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP; + if (attrs[OVS_KEY_ATTR_ICMP]) { + const struct ovs_key_icmp *icmp_key; + + icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]); + flow->tp_src = htons(icmp_key->icmp_type); + flow->tp_dst = htons(icmp_key->icmp_code); } - return 0; + } else if (flow->nw_proto == IPPROTO_ICMPV6 + && flow->dl_type == htons(ETH_TYPE_IPV6) + && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6; + if (attrs[OVS_KEY_ATTR_ICMPV6]) { + const struct ovs_key_icmpv6 *icmpv6_key; + + icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]); + flow->tp_src = htons(icmpv6_key->icmpv6_type); + flow->tp_dst = htons(icmpv6_key->icmpv6_code); - case OVS_KEY_ATTR_TCP: - case OVS_KEY_ATTR_UDP: - case OVS_KEY_ATTR_ICMP: - case OVS_KEY_ATTR_ND: - if (flow->nw_frag & FLOW_NW_FRAG_LATER) { - return EINVAL; + if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) || + flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) { + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND; + if (attrs[OVS_KEY_ATTR_ND]) { + const struct ovs_key_nd *nd_key; + + nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]); + memcpy(&flow->nd_target, nd_key->nd_target, + sizeof flow->nd_target); + memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN); + memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN); + } + } } - return 0; + } - case OVS_KEY_ATTR_ARP: - return 0; + missing_attrs = expected_attrs & ~present_attrs; + if (missing_attrs) { + static struct vlog_rate_limit miss_rl = VLOG_RATE_LIMIT_INIT(10, 10); + log_odp_key_attributes(&miss_rl, "expected but not present", + missing_attrs, key, key_len); + return EINVAL; + } - case __OVS_KEY_ATTR_MAX: - default: - NOT_REACHED(); + extra_attrs = present_attrs & ~expected_attrs; + if (extra_attrs) { + static struct vlog_rate_limit extra_rl = VLOG_RATE_LIMIT_INIT(10, 10); + log_odp_key_attributes(&extra_rl, "present but not expected", + extra_attrs, key, key_len); + return EINVAL; } + + return 0; } -- 1.7.2.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev