+ zero_wildcards(&rule->flow, &rule->wc);
+}
+
+void
+cls_rule_set_reg(struct cls_rule *rule, unsigned int reg_idx, uint32_t value)
+{
+ cls_rule_set_reg_masked(rule, reg_idx, value, UINT32_MAX);
+}
+
+void
+cls_rule_set_reg_masked(struct cls_rule *rule, unsigned int reg_idx,
+ uint32_t value, uint32_t mask)
+{
+ assert(reg_idx < FLOW_N_REGS);
+ flow_wildcards_set_reg_mask(&rule->wc, reg_idx, mask);
+ rule->flow.regs[reg_idx] = value & mask;
+}
+
+void
+cls_rule_set_tun_id(struct cls_rule *rule, ovs_be64 tun_id)
+{
+ cls_rule_set_tun_id_masked(rule, tun_id, htonll(UINT64_MAX));
+}
+
+void
+cls_rule_set_tun_id_masked(struct cls_rule *rule,
+ ovs_be64 tun_id, ovs_be64 mask)
+{
+ rule->wc.tun_id_mask = mask;
+ rule->flow.tun_id = tun_id & mask;
+}
+
+void
+cls_rule_set_in_port(struct cls_rule *rule, uint16_t odp_port)
+{
+ rule->wc.wildcards &= ~FWW_IN_PORT;
+ rule->flow.in_port = odp_port;
+}
+
+void
+cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
+{
+ rule->wc.wildcards &= ~FWW_DL_TYPE;
+ rule->flow.dl_type = dl_type;
+}
+
+void
+cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
+{
+ rule->wc.wildcards &= ~FWW_DL_SRC;
+ memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN);
+}
+
+void
+cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
+{
+ rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
+ memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
+}
+
+void
+cls_rule_set_dl_tci(struct cls_rule *rule, ovs_be16 tci)
+{
+ cls_rule_set_dl_tci_masked(rule, tci, htons(0xffff));
+}
+
+void
+cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
+{
+ rule->flow.vlan_tci = tci & mask;
+ rule->wc.vlan_tci_mask = mask;
+}
+
+/* Modifies 'rule' so that the VLAN VID is wildcarded. If the PCP is already
+ * wildcarded, then 'rule' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+cls_rule_set_any_vid(struct cls_rule *rule)
+{
+ if (rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK)) {
+ rule->wc.vlan_tci_mask &= ~htons(VLAN_VID_MASK);
+ rule->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ } else {
+ cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
+ }
+}
+
+/* Modifies 'rule' depending on 'dl_vlan':
+ *
+ * - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'rule' match only packets
+ * without an 802.1Q header.
+ *
+ * - Otherwise, makes 'rule' match only packets with an 802.1Q header whose
+ * VID equals the low 12 bits of 'dl_vlan'.
+ */
+void
+cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
+{
+ if (dl_vlan == htons(OFP_VLAN_NONE)) {
+ cls_rule_set_dl_tci(rule, htons(0));
+ } else {
+ dl_vlan &= htons(VLAN_VID_MASK);
+ rule->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ rule->flow.vlan_tci |= htons(VLAN_CFI) | dl_vlan;
+ rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
+ }
+}
+
+/* Modifies 'rule' so that the VLAN PCP is wildcarded. If the VID is already
+ * wildcarded, then 'rule' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+cls_rule_set_any_pcp(struct cls_rule *rule)
+{
+ if (rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK)) {
+ rule->wc.vlan_tci_mask &= ~htons(VLAN_PCP_MASK);
+ rule->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ } else {
+ cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
+ }
+}
+
+/* Modifies 'rule' so that it matches only packets with an 802.1Q header whose
+ * PCP equals the low 3 bits of 'dl_vlan_pcp'. */
+void
+cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, uint8_t dl_vlan_pcp)
+{
+ dl_vlan_pcp &= 0x07;
+ rule->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ rule->flow.vlan_tci |= htons((dl_vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+ rule->wc.vlan_tci_mask |= htons(VLAN_CFI | VLAN_PCP_MASK);
+}
+
+void
+cls_rule_set_tp_src(struct cls_rule *rule, ovs_be16 tp_src)
+{
+ rule->wc.wildcards &= ~FWW_TP_SRC;
+ rule->flow.tp_src = tp_src;
+}
+
+void
+cls_rule_set_tp_dst(struct cls_rule *rule, ovs_be16 tp_dst)
+{
+ rule->wc.wildcards &= ~FWW_TP_DST;
+ rule->flow.tp_dst = tp_dst;
+}
+
+void
+cls_rule_set_nw_proto(struct cls_rule *rule, uint8_t nw_proto)
+{
+ rule->wc.wildcards &= ~FWW_NW_PROTO;
+ rule->flow.nw_proto = nw_proto;
+}
+
+void
+cls_rule_set_nw_src(struct cls_rule *rule, ovs_be32 nw_src)
+{
+ cls_rule_set_nw_src_masked(rule, nw_src, htonl(UINT32_MAX));
+}
+
+bool
+cls_rule_set_nw_src_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
+{
+ if (flow_wildcards_set_nw_src_mask(&rule->wc, mask)) {
+ rule->flow.nw_src = ip & mask;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+cls_rule_set_nw_dst(struct cls_rule *rule, ovs_be32 nw_dst)
+{
+ cls_rule_set_nw_dst_masked(rule, nw_dst, htonl(UINT32_MAX));
+}
+
+bool
+cls_rule_set_nw_dst_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
+{
+ if (flow_wildcards_set_nw_dst_mask(&rule->wc, mask)) {
+ rule->flow.nw_dst = ip & mask;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+cls_rule_set_nw_tos(struct cls_rule *rule, uint8_t nw_tos)
+{
+ rule->wc.wildcards &= ~FWW_NW_TOS;
+ rule->flow.nw_tos = nw_tos & IP_DSCP_MASK;
+}
+
+void
+cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
+{
+ rule->wc.wildcards &= ~FWW_TP_SRC;
+ rule->flow.icmp_type = htons(icmp_type);
+
+}
+
+void
+cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
+{
+ rule->wc.wildcards &= ~FWW_TP_DST;
+ rule->flow.icmp_code = htons(icmp_code);
+}
+
+/* Returns true if 'a' and 'b' have the same priority, wildcard the same
+ * fields, and have the same values for fixed fields, otherwise false. */
+bool
+cls_rule_equal(const struct cls_rule *a, const struct cls_rule *b)
+{
+ return (a->priority == b->priority
+ && flow_wildcards_equal(&a->wc, &b->wc)
+ && flow_equal(&a->flow, &b->flow));
+}
+
+static void
+format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
+ ovs_be32 netmask)
+{
+ if (netmask) {
+ ds_put_format(s, "%s="IP_FMT, name, IP_ARGS(&ip));
+ if (netmask != htonl(UINT32_MAX)) {
+ if (ip_is_cidr(netmask)) {
+ int wcbits = ofputil_netmask_to_wcbits(netmask);
+ ds_put_format(s, "/%d", 32 - wcbits);
+ } else {
+ ds_put_format(s, "/"IP_FMT, IP_ARGS(&netmask));
+ }
+ }
+ ds_put_char(s, ',');
+ }
+}
+
+void
+cls_rule_format(const struct cls_rule *rule, struct ds *s)
+{
+ const struct flow_wildcards *wc = &rule->wc;
+ size_t start_len = s->length;
+ flow_wildcards_t w = wc->wildcards;
+ const struct flow *f = &rule->flow;
+ bool skip_type = false;
+ bool skip_proto = false;
+
+ int i;
+
+ if (rule->priority != OFP_DEFAULT_PRIORITY) {
+ ds_put_format(s, "priority=%d,", rule->priority);
+ }
+
+ if (!(w & FWW_DL_TYPE)) {
+ skip_type = true;
+ if (f->dl_type == htons(ETH_TYPE_IP)) {
+ if (!(w & FWW_NW_PROTO)) {
+ skip_proto = true;
+ if (f->nw_proto == IP_TYPE_ICMP) {
+ ds_put_cstr(s, "icmp,");
+ } else if (f->nw_proto == IP_TYPE_TCP) {
+ ds_put_cstr(s, "tcp,");
+ } else if (f->nw_proto == IP_TYPE_UDP) {
+ ds_put_cstr(s, "udp,");
+ } else {
+ ds_put_cstr(s, "ip,");
+ skip_proto = false;
+ }
+ } else {
+ ds_put_cstr(s, "ip,");
+ }
+ } else if (f->dl_type == htons(ETH_TYPE_ARP)) {
+ ds_put_cstr(s, "arp,");
+ } else {
+ skip_type = false;
+ }
+ }
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ switch (wc->reg_masks[i]) {
+ case 0:
+ break;
+ case UINT32_MAX:
+ ds_put_format(s, "reg%d=0x%"PRIx32",", i, f->regs[i]);
+ break;
+ default:
+ ds_put_format(s, "reg%d=0x%"PRIx32"/0x%"PRIx32",",
+ i, f->regs[i], wc->reg_masks[i]);
+ break;
+ }
+ }
+ switch (wc->tun_id_mask) {
+ case 0:
+ break;
+ case CONSTANT_HTONLL(UINT64_MAX):
+ ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(f->tun_id));
+ break;
+ default:
+ ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
+ ntohll(f->tun_id), ntohll(wc->tun_id_mask));
+ break;
+ }
+ if (!(w & FWW_IN_PORT)) {
+ ds_put_format(s, "in_port=%"PRIu16",",
+ odp_port_to_ofp_port(f->in_port));
+ }
+ if (wc->vlan_tci_mask) {
+ ovs_be16 vid_mask = wc->vlan_tci_mask & htons(VLAN_VID_MASK);
+ ovs_be16 pcp_mask = wc->vlan_tci_mask & htons(VLAN_PCP_MASK);
+ ovs_be16 cfi = wc->vlan_tci_mask & htons(VLAN_CFI);
+
+ if (cfi && f->vlan_tci & htons(VLAN_CFI)
+ && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
+ && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
+ && (vid_mask || pcp_mask)) {
+ if (vid_mask) {
+ ds_put_format(s, "dl_vlan=%"PRIu16",",
+ vlan_tci_to_vid(f->vlan_tci));
+ }
+ if (pcp_mask) {
+ ds_put_format(s, "dl_vlan_pcp=%d,",
+ vlan_tci_to_pcp(f->vlan_tci));
+ }
+ } else {
+ ds_put_format(s, "vlan_tci=0x%04"PRIx16"/0x%04"PRIx16",",
+ ntohs(f->vlan_tci), ntohs(wc->vlan_tci_mask));
+ }
+ }
+ if (!(w & FWW_DL_SRC)) {
+ ds_put_format(s, "dl_src="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_src));
+ }
+ switch (w & (FWW_DL_DST | FWW_ETH_MCAST)) {
+ case 0:
+ ds_put_format(s, "dl_dst="ETH_ADDR_FMT",", ETH_ADDR_ARGS(f->dl_dst));
+ break;
+ case FWW_DL_DST:
+ ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/01:00:00:00:00:00,",
+ ETH_ADDR_ARGS(f->dl_dst));
+ break;
+ case FWW_ETH_MCAST:
+ ds_put_format(s, "dl_dst="ETH_ADDR_FMT"/fe:ff:ff:ff:ff:ff,",
+ ETH_ADDR_ARGS(f->dl_dst));
+ break;
+ case FWW_DL_DST | FWW_ETH_MCAST:
+ break;
+ }
+ if (!skip_type && !(w & FWW_DL_TYPE)) {
+ ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
+ }
+ format_ip_netmask(s, "nw_src", f->nw_src, wc->nw_src_mask);
+ format_ip_netmask(s, "nw_dst", f->nw_dst, wc->nw_dst_mask);
+ if (!skip_proto && !(w & FWW_NW_PROTO)) {
+ if (f->dl_type == htons(ETH_TYPE_ARP)) {
+ ds_put_format(s, "opcode=%"PRIu8",", f->nw_proto);
+ } else {
+ ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
+ }
+ }
+ if (!(w & FWW_NW_TOS)) {
+ ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos);
+ }
+ if (f->nw_proto == IP_TYPE_ICMP) {
+ if (!(w & FWW_TP_SRC)) {
+ ds_put_format(s, "icmp_type=%"PRIu16",", ntohs(f->tp_src));
+ }
+ if (!(w & FWW_TP_DST)) {
+ ds_put_format(s, "icmp_code=%"PRIu16",", ntohs(f->tp_dst));
+ }
+ } else {
+ if (!(w & FWW_TP_SRC)) {
+ ds_put_format(s, "tp_src=%"PRIu16",", ntohs(f->tp_src));
+ }
+ if (!(w & FWW_TP_DST)) {
+ ds_put_format(s, "tp_dst=%"PRIu16",", ntohs(f->tp_dst));
+ }
+ }
+
+ if (s->length > start_len && ds_last(s) == ',') {
+ s->length--;
+ }