diff options
Diffstat (limited to 'lib/odp-util.c')
-rw-r--r-- | lib/odp-util.c | 272 |
1 files changed, 171 insertions, 101 deletions
diff --git a/lib/odp-util.c b/lib/odp-util.c index c70ab119..b974dcc4 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -62,10 +62,10 @@ odp_action_len(uint16_t type) } switch ((enum ovs_action_attr) type) { - case OVS_ACTION_ATTR_OUTPUT: return 4; + case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t); case OVS_ACTION_ATTR_USERSPACE: return -2; - case OVS_ACTION_ATTR_PUSH: return -2; - case OVS_ACTION_ATTR_POP: return 2; + case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan); + case OVS_ACTION_ATTR_POP_VLAN: return 0; case OVS_ACTION_ATTR_SET: return -2; case OVS_ACTION_ATTR_SAMPLE: return -2; @@ -84,11 +84,12 @@ ovs_key_attr_to_string(enum ovs_key_attr attr) switch (attr) { case OVS_KEY_ATTR_UNSPEC: return "unspec"; + case OVS_KEY_ATTR_ENCAP: return "encap"; case OVS_KEY_ATTR_PRIORITY: return "priority"; case OVS_KEY_ATTR_TUN_ID: return "tun_id"; case OVS_KEY_ATTR_IN_PORT: return "in_port"; case OVS_KEY_ATTR_ETHERNET: return "eth"; - case OVS_KEY_ATTR_8021Q: return "vlan"; + case OVS_KEY_ATTR_VLAN: return "vlan"; case OVS_KEY_ATTR_ETHERTYPE: return "eth_type"; case OVS_KEY_ATTR_IPV4: return "ipv4"; case OVS_KEY_ATTR_IPV6: return "ipv6"; @@ -201,6 +202,7 @@ format_odp_action(struct ds *ds, const struct nlattr *a) { int expected_len; enum ovs_action_attr type = nl_attr_type(a); + const struct ovs_action_push_vlan *vlan; expected_len = odp_action_len(nl_attr_type(a)); if (expected_len != -2 && nl_attr_get_size(a) != expected_len) { @@ -211,7 +213,6 @@ format_odp_action(struct ds *ds, const struct nlattr *a) } switch (type) { - case OVS_ACTION_ATTR_OUTPUT: ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a)); break; @@ -223,14 +224,18 @@ format_odp_action(struct ds *ds, const struct nlattr *a) format_odp_key_attr(nl_attr_get(a), ds); ds_put_cstr(ds, ")"); break; - case OVS_ACTION_ATTR_PUSH: - ds_put_cstr(ds, "push("); - format_odp_key_attr(nl_attr_get(a), ds); - ds_put_cstr(ds, ")"); + case OVS_ACTION_ATTR_PUSH_VLAN: + vlan = nl_attr_get(a); + ds_put_cstr(ds, "push_vlan("); + if (vlan->vlan_tpid != htons(ETH_TYPE_VLAN)) { + ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(vlan->vlan_tpid)); + } + ds_put_format(ds, "vid=%"PRIu16",pcp=%d)", + vlan_tci_to_vid(vlan->vlan_tci), + vlan_tci_to_pcp(vlan->vlan_tci)); break; - case OVS_ACTION_ATTR_POP: - ds_put_format(ds, "pop(%s)", - ovs_key_attr_to_string(nl_attr_get_u16(a))); + case OVS_ACTION_ATTR_POP_VLAN: + ds_put_cstr(ds, "pop_vlan"); break; case OVS_ACTION_ATTR_SAMPLE: format_odp_sample_action(ds, a); @@ -269,7 +274,8 @@ format_odp_actions(struct ds *ds, const struct nlattr *actions, } /* Returns the correct length of the payload for a flow key attribute of the - * specified 'type', or -1 if 'type' is unknown. */ + * specified 'type', -1 if 'type' is unknown, or -2 if the attribute's payload + * is variable length. */ static int odp_flow_key_attr_len(uint16_t type) { @@ -278,11 +284,12 @@ odp_flow_key_attr_len(uint16_t type) } switch ((enum ovs_key_attr) type) { + case OVS_KEY_ATTR_ENCAP: return -2; case OVS_KEY_ATTR_PRIORITY: return 4; case OVS_KEY_ATTR_TUN_ID: return 8; case OVS_KEY_ATTR_IN_PORT: return 4; case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet); - case OVS_KEY_ATTR_8021Q: return sizeof(struct ovs_key_8021q); + case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16); case OVS_KEY_ATTR_ETHERTYPE: return 2; case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4); case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6); @@ -338,7 +345,6 @@ static void format_odp_key_attr(const struct nlattr *a, struct ds *ds) { 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; @@ -348,9 +354,11 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) const struct ovs_key_arp *arp_key; const struct ovs_key_nd *nd_key; enum ovs_key_attr attr = nl_attr_type(a); + int expected_len; ds_put_cstr(ds, ovs_key_attr_to_string(attr)); - if (nl_attr_get_size(a) != odp_flow_key_attr_len(nl_attr_type(a))) { + expected_len = odp_flow_key_attr_len(nl_attr_type(a)); + if (expected_len != -2 && nl_attr_get_size(a) != expected_len) { ds_put_format(ds, "(bad length %zu, expected %d)", nl_attr_get_size(a), odp_flow_key_attr_len(nl_attr_type(a))); @@ -359,6 +367,14 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) } switch (attr) { + case OVS_KEY_ATTR_ENCAP: + ds_put_cstr(ds, "("); + if (nl_attr_get_size(a)) { + odp_flow_key_format(nl_attr_get(a), nl_attr_get_size(a), ds); + } + ds_put_char(ds, ')'); + break; + case OVS_KEY_ATTR_PRIORITY: ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a)); break; @@ -378,15 +394,10 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) ETH_ADDR_ARGS(eth_key->eth_dst)); break; - case OVS_KEY_ATTR_8021Q: - q_key = nl_attr_get(a); - ds_put_cstr(ds, "("); - if (q_key->q_tpid != htons(ETH_TYPE_VLAN)) { - ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(q_key->q_tpid)); - } - ds_put_format(ds, "vid=%"PRIu16",pcp=%d)", - vlan_tci_to_vid(q_key->q_tci), - vlan_tci_to_pcp(q_key->q_tci)); + case OVS_KEY_ATTR_VLAN: + ds_put_format(ds, "(vid=%"PRIu16",pcp=%d)", + vlan_tci_to_vid(nl_attr_get_be16(a)), + vlan_tci_to_pcp(nl_attr_get_be16(a))); break; case OVS_KEY_ATTR_ETHERTYPE: @@ -603,21 +614,15 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) } { - uint16_t tpid = ETH_TYPE_VLAN; uint16_t vid; int pcp; int n = -1; - if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", - &vid, &pcp, &n) > 0 && n > 0) || - (sscanf(s, "vlan(tpid=%"SCNi16",vid=%"SCNi16",pcp=%i)%n", - &tpid, &vid, &pcp, &n) > 0 && n > 0)) { - struct ovs_key_8021q q_key; - - q_key.q_tpid = htons(tpid); - q_key.q_tci = htons((vid << VLAN_VID_SHIFT) | - (pcp << VLAN_PCP_SHIFT)); - nl_msg_put_unspec(key, OVS_KEY_ATTR_8021Q, &q_key, sizeof q_key); + if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0 + && n > 0)) { + nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN, + htons((vid << VLAN_VID_SHIFT) | + (pcp << VLAN_PCP_SHIFT))); return n; } } @@ -816,6 +821,36 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) } } + if (!strncmp(s, "encap(", 6)) { + const char *start = s; + size_t encap; + + encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP); + + s += 6; + for (;;) { + int retval; + + s += strspn(s, ", \t\r\n"); + if (!*s) { + return -EINVAL; + } else if (*s == ')') { + break; + } + + retval = parse_odp_key_attr(s, key); + if (retval < 0) { + return retval; + } + s += retval; + } + s++; + + nl_msg_end_nested(key, encap); + + return s - start; + } + return -EINVAL; } @@ -865,6 +900,7 @@ void odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) { struct ovs_key_ethernet *eth_key; + size_t encap; if (flow->priority) { nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->priority); @@ -885,16 +921,16 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN); if (flow->vlan_tci != htons(0)) { - struct ovs_key_8021q *q_key; - - q_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_8021Q, - sizeof *q_key); - q_key->q_tpid = htons(ETH_TYPE_VLAN); - q_key->q_tci = flow->vlan_tci & ~htons(VLAN_CFI); + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN)); + nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, + flow->vlan_tci & ~htons(VLAN_CFI)); + encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); + } else { + encap = 0; } if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { - return; + goto unencap; } nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type); @@ -983,6 +1019,11 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) } } } + +unencap: + if (encap) { + nl_msg_end_nested(buf, encap); + } } static void @@ -1032,33 +1073,22 @@ odp_to_ovs_frag(uint8_t odp_frag, struct flow *flow) return true; } -/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow - * structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */ -int -odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, - struct flow *flow) +static int +parse_flow_nlattrs(const struct nlattr *key, size_t key_len, + const struct nlattr *attrs[], uint64_t *present_attrsp) { 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; - uint64_t expected_attrs; uint64_t present_attrs; - uint64_t missing_attrs; - uint64_t extra_attrs; size_t left; - memset(flow, 0, sizeof *flow); - - 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 (len != expected_len && expected_len != -2) { if (expected_len == -1) { VLOG_ERR_RL(&rl, "unknown attribute %"PRIu16" in flow key", type); @@ -1082,17 +1112,68 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, return EINVAL; } - if (attrs[OVS_KEY_ATTR_PRIORITY]) { + *present_attrsp = present_attrs; + return 0; +} + +static int +check_expectations(uint64_t present_attrs, uint64_t expected_attrs, + const struct nlattr *key, size_t key_len) +{ + uint64_t missing_attrs; + uint64_t extra_attrs; + + 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; + } + + 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; +} + +/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow + * structure in 'flow'. Returns 0 if successful, otherwise EINVAL. */ +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]; + uint64_t expected_attrs; + uint64_t present_attrs; + int error; + + memset(flow, 0, sizeof *flow); + + error = parse_flow_nlattrs(key, key_len, attrs, &present_attrs); + if (error) { + return error; + } + + expected_attrs = 0; + + if (present_attrs & (UINT64_C(1) << 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]) { + if (present_attrs & (UINT64_C(1) << 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]) { + if (present_attrs & (UINT64_C(1) << 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", @@ -1105,7 +1186,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, flow->in_port = OFPP_NONE; } - if (attrs[OVS_KEY_ATTR_ETHERNET]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) { const struct ovs_key_ethernet *eth_key; eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]); @@ -1117,25 +1198,30 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; - if (attrs[OVS_KEY_ATTR_8021Q]) { - const struct ovs_key_8021q *q_key; - - 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)); + if ((present_attrs & ~expected_attrs) + == ((UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE) | + (UINT64_C(1) << OVS_KEY_ATTR_VLAN) | + (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) + && (nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]) + == htons(ETH_TYPE_VLAN))) { + const struct nlattr *encap = attrs[OVS_KEY_ATTR_ENCAP]; + const struct nlattr *vlan = attrs[OVS_KEY_ATTR_VLAN]; + + flow->vlan_tci = nl_attr_get_be16(vlan); + if (flow->vlan_tci & htons(VLAN_CFI)) { 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 |= htons(VLAN_CFI); + + error = parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap), + attrs, &present_attrs); + if (error) { + return error; } - flow->vlan_tci = q_key->q_tci | htons(VLAN_CFI); - expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_8021Q; + expected_attrs = 0; } - if (attrs[OVS_KEY_ATTR_ETHERTYPE]) { + if (present_attrs & (UINT64_C(1) << 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", @@ -1149,7 +1235,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, if (flow->dl_type == htons(ETH_TYPE_IP)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4; - if (attrs[OVS_KEY_ATTR_IPV4]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]); @@ -1164,7 +1250,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6; - if (attrs[OVS_KEY_ATTR_IPV6]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) { const struct ovs_key_ipv6 *ipv6_key; ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]); @@ -1180,7 +1266,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP; - if (attrs[OVS_KEY_ATTR_ARP]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]); @@ -1202,7 +1288,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, 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]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) { const struct ovs_key_tcp *tcp_key; tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]); @@ -1214,7 +1300,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, 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]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) { const struct ovs_key_udp *udp_key; udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]); @@ -1225,7 +1311,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, && 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]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) { const struct ovs_key_icmp *icmp_key; icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]); @@ -1236,7 +1322,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, && 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]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) { const struct ovs_key_icmpv6 *icmpv6_key; icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]); @@ -1246,7 +1332,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, 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]) { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) { const struct ovs_key_nd *nd_key; nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]); @@ -1259,21 +1345,5 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, } } - 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; - } - - 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; + return check_expectations(present_attrs, expected_attrs, key, key_len); } |