+ * 1. If the type of level N is known, then only the valid fields for that
+ * level may be specified. For example, ARP does not have a TOS field,
+ * so nw_tos must be wildcarded if 'rule' specifies an ARP flow.
+ * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ * ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an
+ * IPv4 flow.
+ *
+ * 2. If the type of level N is not known (or not understood by Open
+ * vSwitch), then no fields at all for that level may be specified. For
+ * example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ * L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an
+ * SCTP flow.
+ *
+ * 'flow_format' specifies the format of the flow as received or as intended to
+ * be sent. This is important for IPv6 and ARP, for which NXM supports more
+ * detailed matching. */
+void
+ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
+{
+ enum {
+ MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
+ MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */
+ MAY_NW_PROTO = 1 << 2, /* nw_proto */
+ MAY_NW_TOS = 1 << 3, /* nw_tos */
+ MAY_ARP_SHA = 1 << 4, /* arp_sha */
+ MAY_ARP_THA = 1 << 5, /* arp_tha */
+ MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */
+ MAY_ND_TARGET = 1 << 7 /* nd_target */
+ } may_match;
+
+ struct flow_wildcards wc;
+
+ /* Figure out what fields may be matched. */
+ if (rule->flow.dl_type == htons(ETH_TYPE_IP)) {
+ may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_NW_ADDR;
+ if (rule->flow.nw_proto == IPPROTO_TCP ||
+ rule->flow.nw_proto == IPPROTO_UDP ||
+ rule->flow.nw_proto == IPPROTO_ICMP) {
+ may_match |= MAY_TP_ADDR;
+ }
+ } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)
+ && flow_format == NXFF_NXM) {
+ may_match = MAY_NW_PROTO | MAY_NW_TOS | MAY_IPV6_ADDR;
+ if (rule->flow.nw_proto == IPPROTO_TCP ||
+ rule->flow.nw_proto == IPPROTO_UDP) {
+ may_match |= MAY_TP_ADDR;
+ } else if (rule->flow.nw_proto == IPPROTO_ICMPV6) {
+ may_match |= MAY_TP_ADDR;
+ if (rule->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
+ } else if (rule->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_THA;
+ }
+ }
+ } else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) {
+ may_match = MAY_NW_PROTO | MAY_NW_ADDR;
+ if (flow_format == NXFF_NXM) {
+ may_match |= MAY_ARP_SHA | MAY_ARP_THA;
+ }
+ } else {
+ may_match = 0;
+ }
+
+ /* Clear the fields that may not be matched. */
+ wc = rule->wc;
+ if (!(may_match & MAY_NW_ADDR)) {
+ wc.nw_src_mask = wc.nw_dst_mask = htonl(0);
+ }
+ if (!(may_match & MAY_TP_ADDR)) {
+ wc.wildcards |= FWW_TP_SRC | FWW_TP_DST;
+ }
+ if (!(may_match & MAY_NW_PROTO)) {
+ wc.wildcards |= FWW_NW_PROTO;
+ }
+ if (!(may_match & MAY_NW_TOS)) {
+ wc.wildcards |= FWW_NW_TOS;
+ }
+ if (!(may_match & MAY_ARP_SHA)) {
+ wc.wildcards |= FWW_ARP_SHA;
+ }
+ if (!(may_match & MAY_ARP_THA)) {
+ wc.wildcards |= FWW_ARP_THA;
+ }
+ if (!(may_match & MAY_IPV6_ADDR)) {
+ wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
+ }
+ if (!(may_match & MAY_ND_TARGET)) {
+ wc.wildcards |= FWW_ND_TARGET;
+ }
+
+ /* Log any changes. */
+ if (!flow_wildcards_equal(&wc, &rule->wc)) {
+ bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl);
+ char *pre = log ? cls_rule_to_string(rule) : NULL;
+
+ rule->wc = wc;
+ cls_rule_zero_wildcarded_fields(rule);
+
+ if (log) {
+ char *post = cls_rule_to_string(rule);
+ VLOG_INFO("normalization changed ofp_match, details:");
+ VLOG_INFO(" pre: %s", pre);
+ VLOG_INFO("post: %s", post);
+ free(pre);
+ free(post);
+ }
+ }