@@ -6322,6 +6322,240 @@ parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
return true;
}
+static bool
+parse_l2_5(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, uint64_t *expected_attrs,
+ struct flow *flow, const struct flow *src_flow)
+{
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ bool is_mask = src_flow != flow;
+ const void *check_start = NULL;
+ size_t check_len = 0;
+ enum ovs_key_attr expected_bit = 0xff;
+
+ if (eth_type_mpls(src_flow->dl_type)) {
+ if (!is_mask || present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
+ *expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
+ size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
+ const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
+ int n = size / sizeof(ovs_be32);
+ int i;
+
+ if (!size || size % sizeof(ovs_be32)) {
+ return false;
+ }
+ if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) {
+ return false;
+ }
+
+ for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
+ flow->mpls_lse[i] = mpls_lse[i];
+ }
+ if (n > FLOW_MAX_MPLS_LABELS) {
+ return false;
+ }
+
+ if (!is_mask) {
+ /* BOS may be set only in the innermost label. */
+ for (i = 0; i < n - 1; i++) {
+ if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+ return false;
+ }
+ }
+
+ /* BOS must be set in the innermost label. */
+ if (n < FLOW_MAX_MPLS_LABELS
+ && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ } else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ put_ipv4_key(ipv4_key, flow, is_mask);
+ if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
+ return false;
+ }
+ if (is_mask) {
+ check_start = ipv4_key;
+ check_len = sizeof *ipv4_key;
+ expected_bit = OVS_KEY_ATTR_IPV4;
+ }
+ }
+ } else if (src_flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ put_ipv6_key(ipv6_key, flow, is_mask);
+ if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
+ return false;
+ }
+ if (is_mask) {
+ check_start = ipv6_key;
+ check_len = sizeof *ipv6_key;
+ expected_bit = OVS_KEY_ATTR_IPV6;
+ }
+ }
+ } else if (src_flow->dl_type == htons(ETH_TYPE_ARP) ||
+ src_flow->dl_type == htons(ETH_TYPE_RARP)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
+ VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow "
+ "key", ntohs(arp_key->arp_op));
+ return false;
+ }
+ put_arp_key(arp_key, flow);
+ if (is_mask) {
+ check_start = arp_key;
+ check_len = sizeof *arp_key;
+ expected_bit = OVS_KEY_ATTR_ARP;
+ }
+ }
+ } else {
+ return true;
+ }
+ if (check_len > 0) { /* Happens only when 'is_mask'. */
+ if (!is_all_zeros(check_start, check_len) &&
+ flow->dl_type != htons(0xffff)) {
+ return false;
+ } else {
+ *expected_attrs |= UINT64_C(1) << expected_bit;
+ }
+ }
+
+ expected_bit = OVS_KEY_ATTR_UNSPEC;
+ if (src_flow->nw_proto == IPPROTO_TCP
+ && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+ src_flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
+ const union ovs_key_tp *tcp_key;
+
+ tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
+ put_tp_key(tcp_key, flow);
+ expected_bit = OVS_KEY_ATTR_TCP;
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
+ *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
+ flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
+ }
+ } else if (src_flow->nw_proto == IPPROTO_UDP
+ && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+ src_flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
+ const union ovs_key_tp *udp_key;
+
+ udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]);
+ put_tp_key(udp_key, flow);
+ expected_bit = OVS_KEY_ATTR_UDP;
+ }
+ } else if (src_flow->nw_proto == IPPROTO_SCTP
+ && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
+ src_flow->dl_type == htons(ETH_TYPE_IPV6))
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) {
+ const union ovs_key_tp *sctp_key;
+
+ sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]);
+ put_tp_key(sctp_key, flow);
+ expected_bit = OVS_KEY_ATTR_SCTP;
+ }
+ } else if (src_flow->nw_proto == IPPROTO_ICMP
+ && src_flow->dl_type == htons(ETH_TYPE_IP)
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ flow->tp_src = htons(icmp_key->icmp_type);
+ flow->tp_dst = htons(icmp_key->icmp_code);
+ expected_bit = OVS_KEY_ATTR_ICMP;
+ }
+ } else if (src_flow->nw_proto == IPPROTO_ICMPV6
+ && src_flow->dl_type == htons(ETH_TYPE_IPV6)
+ && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ flow->tp_src = htons(icmpv6_key->icmpv6_type);
+ flow->tp_dst = htons(icmpv6_key->icmpv6_code);
+ expected_bit = OVS_KEY_ATTR_ICMPV6;
+ if (is_nd(src_flow, NULL)) {
+ if (!is_mask) {
+ *expected_attrs |= UINT64_C(1) << 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]);
+ flow->nd_target = nd_key->nd_target;
+ flow->arp_sha = nd_key->nd_sll;
+ flow->arp_tha = nd_key->nd_tll;
+ if (is_mask) {
+ /* Even though 'tp_src' and 'tp_dst' are 16 bits wide,
+ * ICMP type and code are 8 bits wide. Therefore, an
+ * exact match looks like htons(0xff), not
+ * htons(0xffff). See xlate_wc_finish() for details.
+ * */
+ if (!is_all_zeros(nd_key, sizeof *nd_key) &&
+ (flow->tp_src != htons(0xff) ||
+ flow->tp_dst != htons(0xff))) {
+ return false;
+ } else {
+ *expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
+ if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
+ return false;
+ } else {
+ *expected_attrs |= UINT64_C(1) << expected_bit;
+ }
+ }
+ return true;
+}
+
static enum odp_key_fitness
parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
uint64_t present_attrs, int out_of_range_attr,
@@ -6658,7 +6892,10 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
flow, src_flow)) {
return ODP_FIT_ERROR;
}
-
+ if (!parse_l2_5(attrs, present_attrs, &expected_attrs,
+ flow, src_flow)) {
+ return ODP_FIT_ERROR;
+ }
encaps++;
}