+/* Converts a flow into flow wildcards. It sets the wildcard masks based on
+ * the packet headers extracted to 'flow'. It will not set the mask for fields
+ * that do not make sense for the packet type. OpenFlow-only metadata is
+ * wildcarded, but other metadata is unconditionally exact-matched. */
+void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
+ const struct flow *flow)
+{
+ memset(&wc->masks, 0x0, sizeof wc->masks);
+
+ /* Update this function whenever struct flow changes. */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 32);
+
+ if (flow->tunnel.ip_dst) {
+ if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
+ WC_MASK_FIELD(wc, tunnel.tun_id);
+ }
+ WC_MASK_FIELD(wc, tunnel.ip_src);
+ WC_MASK_FIELD(wc, tunnel.ip_dst);
+ WC_MASK_FIELD(wc, tunnel.flags);
+ WC_MASK_FIELD(wc, tunnel.ip_tos);
+ WC_MASK_FIELD(wc, tunnel.ip_ttl);
+ WC_MASK_FIELD(wc, tunnel.tp_src);
+ WC_MASK_FIELD(wc, tunnel.tp_dst);
+ WC_MASK_FIELD(wc, tunnel.gbp_id);
+ WC_MASK_FIELD(wc, tunnel.gbp_flags);
+
+ if (flow->tunnel.metadata.opt_map) {
+ wc->masks.tunnel.metadata.opt_map = flow->tunnel.metadata.opt_map;
+ WC_MASK_FIELD(wc, tunnel.metadata.opts);
+ }
+ } else if (flow->tunnel.tun_id) {
+ WC_MASK_FIELD(wc, tunnel.tun_id);
+ }
+
+ /* metadata, regs, and conj_id wildcarded. */
+
+ WC_MASK_FIELD(wc, skb_priority);
+ WC_MASK_FIELD(wc, pkt_mark);
+ WC_MASK_FIELD(wc, recirc_id);
+ WC_MASK_FIELD(wc, dp_hash);
+ WC_MASK_FIELD(wc, in_port);
+
+ /* actset_output wildcarded. */
+
+ WC_MASK_FIELD(wc, dl_dst);
+ WC_MASK_FIELD(wc, dl_src);
+ WC_MASK_FIELD(wc, dl_type);
+ WC_MASK_FIELD(wc, vlan_tci);
+
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ WC_MASK_FIELD(wc, nw_src);
+ WC_MASK_FIELD(wc, nw_dst);
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ WC_MASK_FIELD(wc, ipv6_src);
+ WC_MASK_FIELD(wc, ipv6_dst);
+ WC_MASK_FIELD(wc, ipv6_label);
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
+ WC_MASK_FIELD(wc, nw_src);
+ WC_MASK_FIELD(wc, nw_dst);
+ WC_MASK_FIELD(wc, nw_proto);
+ WC_MASK_FIELD(wc, arp_sha);
+ WC_MASK_FIELD(wc, arp_tha);
+ return;
+ } else if (eth_type_mpls(flow->dl_type)) {
+ for (int i = 0; i < FLOW_MAX_MPLS_LABELS; i++) {
+ WC_MASK_FIELD(wc, mpls_lse[i]);
+ if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+ break;
+ }
+ }
+ return;
+ } else {
+ return; /* Unknown ethertype. */
+ }
+
+ /* IPv4 or IPv6. */
+ WC_MASK_FIELD(wc, nw_frag);
+ WC_MASK_FIELD(wc, nw_tos);
+ WC_MASK_FIELD(wc, nw_ttl);
+ WC_MASK_FIELD(wc, nw_proto);
+
+ /* No transport layer header in later fragments. */
+ if (!(flow->nw_frag & FLOW_NW_FRAG_LATER) &&
+ (flow->nw_proto == IPPROTO_ICMP ||
+ flow->nw_proto == IPPROTO_ICMPV6 ||
+ flow->nw_proto == IPPROTO_TCP ||
+ flow->nw_proto == IPPROTO_UDP ||
+ flow->nw_proto == IPPROTO_SCTP ||
+ flow->nw_proto == IPPROTO_IGMP)) {
+ WC_MASK_FIELD(wc, tp_src);
+ WC_MASK_FIELD(wc, tp_dst);
+
+ if (flow->nw_proto == IPPROTO_TCP) {
+ WC_MASK_FIELD(wc, tcp_flags);
+ } else if (flow->nw_proto == IPPROTO_ICMPV6) {
+ WC_MASK_FIELD(wc, arp_sha);
+ WC_MASK_FIELD(wc, arp_tha);
+ WC_MASK_FIELD(wc, nd_target);
+ } else if (flow->nw_proto == IPPROTO_IGMP) {
+ WC_MASK_FIELD(wc, igmp_group_ip4);
+ }
+ }
+}
+
+/* Return a map of possible fields for a packet of the same type as 'flow'.
+ * Including extra bits in the returned mask is not wrong, it is just less
+ * optimal.
+ *
+ * This is a less precise version of flow_wildcards_init_for_packet() above. */
+uint64_t
+flow_wc_map(const struct flow *flow)
+{
+ /* Update this function whenever struct flow changes. */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 32);
+
+ uint64_t map = (flow->tunnel.ip_dst) ? MINIFLOW_MAP(tunnel) : 0;
+
+ /* Metadata fields that can appear on packet input. */
+ map |= MINIFLOW_MAP(skb_priority) | MINIFLOW_MAP(pkt_mark)
+ | MINIFLOW_MAP(recirc_id) | MINIFLOW_MAP(dp_hash)
+ | MINIFLOW_MAP(in_port)
+ | MINIFLOW_MAP(dl_dst) | MINIFLOW_MAP(dl_src)
+ | MINIFLOW_MAP(dl_type) | MINIFLOW_MAP(vlan_tci);
+
+ /* Ethertype-dependent fields. */
+ if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) {
+ map |= MINIFLOW_MAP(nw_src) | MINIFLOW_MAP(nw_dst)
+ | MINIFLOW_MAP(nw_proto) | MINIFLOW_MAP(nw_frag)
+ | MINIFLOW_MAP(nw_tos) | MINIFLOW_MAP(nw_ttl);
+ if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) {
+ map |= MINIFLOW_MAP(igmp_group_ip4);
+ } else {
+ map |= MINIFLOW_MAP(tcp_flags)
+ | MINIFLOW_MAP(tp_src) | MINIFLOW_MAP(tp_dst);
+ }
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ map |= MINIFLOW_MAP(ipv6_src) | MINIFLOW_MAP(ipv6_dst)
+ | MINIFLOW_MAP(ipv6_label)
+ | MINIFLOW_MAP(nw_proto) | MINIFLOW_MAP(nw_frag)
+ | MINIFLOW_MAP(nw_tos) | MINIFLOW_MAP(nw_ttl);
+ if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_ICMPV6)) {
+ map |= MINIFLOW_MAP(nd_target)
+ | MINIFLOW_MAP(arp_sha) | MINIFLOW_MAP(arp_tha);
+ } else {
+ map |= MINIFLOW_MAP(tcp_flags)
+ | MINIFLOW_MAP(tp_src) | MINIFLOW_MAP(tp_dst);
+ }
+ } else if (eth_type_mpls(flow->dl_type)) {
+ map |= MINIFLOW_MAP(mpls_lse);
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
+ map |= MINIFLOW_MAP(nw_src) | MINIFLOW_MAP(nw_dst)
+ | MINIFLOW_MAP(nw_proto)
+ | MINIFLOW_MAP(arp_sha) | MINIFLOW_MAP(arp_tha);
+ }
+
+ return map;
+}
+
+/* Clear the metadata and register wildcard masks. They are not packet
+ * header fields. */
+void
+flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
+{
+ /* Update this function whenever struct flow changes. */
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 32);
+
+ memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
+ memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+ wc->masks.actset_output = 0;
+ wc->masks.conj_id = 0;
+}
+