+/*
+ * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include "match.h"
+#include <assert.h>
+#include <stdlib.h>
+#include "byte-order.h"
+#include "dynamic-string.h"
+#include "packets.h"
+
+/* Converts the flow in 'flow' into a match in 'match', with the given
+ * 'wildcards'. */
+void
+match_init(struct match *match,
+ const struct flow *flow, const struct flow_wildcards *wc)
+{
+ match->flow = *flow;
+ match->wc = *wc;
+ match_zero_wildcarded_fields(match);
+}
+
+/* Converts the flow in 'flow' into an exact-match match in 'match'. */
+void
+match_init_exact(struct match *match, const struct flow *flow)
+{
+ match->flow = *flow;
+ match->flow.skb_priority = 0;
+ flow_wildcards_init_exact(&match->wc);
+}
+
+/* Initializes 'match' as a "catch-all" match that matches every packet. */
+void
+match_init_catchall(struct match *match)
+{
+ memset(&match->flow, 0, sizeof match->flow);
+ flow_wildcards_init_catchall(&match->wc);
+}
+
+/* For each bit or field wildcarded in 'match', sets the corresponding bit or
+ * field in 'flow' to all-0-bits. It is important to maintain this invariant
+ * in a match that might be inserted into a classifier.
+ *
+ * It is never necessary to call this function directly for a match that is
+ * initialized or modified only by match_*() functions. It is useful to
+ * restore the invariant in a match whose 'wc' member is modified by hand.
+ */
+void
+match_zero_wildcarded_fields(struct match *match)
+{
+ flow_zero_wildcards(&match->flow, &match->wc);
+}
+
+void
+match_set_reg(struct match *match, unsigned int reg_idx, uint32_t value)
+{
+ match_set_reg_masked(match, reg_idx, value, UINT32_MAX);
+}
+
+void
+match_set_reg_masked(struct match *match, unsigned int reg_idx,
+ uint32_t value, uint32_t mask)
+{
+ assert(reg_idx < FLOW_N_REGS);
+ flow_wildcards_set_reg_mask(&match->wc, reg_idx, mask);
+ match->flow.regs[reg_idx] = value & mask;
+}
+
+void
+match_set_metadata(struct match *match, ovs_be64 metadata)
+{
+ match_set_metadata_masked(match, metadata, htonll(UINT64_MAX));
+}
+
+void
+match_set_metadata_masked(struct match *match,
+ ovs_be64 metadata, ovs_be64 mask)
+{
+ match->wc.masks.metadata = mask;
+ match->flow.metadata = metadata & mask;
+}
+
+void
+match_set_tun_id(struct match *match, ovs_be64 tun_id)
+{
+ match_set_tun_id_masked(match, tun_id, htonll(UINT64_MAX));
+}
+
+void
+match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
+{
+ match->wc.masks.tun_id = mask;
+ match->flow.tun_id = tun_id & mask;
+}
+
+void
+match_set_in_port(struct match *match, uint16_t ofp_port)
+{
+ match->wc.masks.in_port = UINT16_MAX;
+ match->flow.in_port = ofp_port;
+}
+
+void
+match_set_dl_type(struct match *match, ovs_be16 dl_type)
+{
+ match->wc.masks.dl_type = htons(UINT16_MAX);
+ match->flow.dl_type = dl_type;
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match 'value_dst'
+ * exactly. 'mask_dst' is set to all 1s. */
+static void
+set_eth(const uint8_t value_src[ETH_ADDR_LEN],
+ uint8_t value_dst[ETH_ADDR_LEN],
+ uint8_t mask_dst[ETH_ADDR_LEN])
+{
+ memcpy(value_dst, value_src, ETH_ADDR_LEN);
+ memset(mask_dst, 0xff, ETH_ADDR_LEN);
+}
+
+/* Modifies 'value_src' so that the Ethernet address must match 'value_src'
+ * after each byte is ANDed with the appropriate byte in 'mask_src'.
+ * 'mask_dst' is set to 'mask_src' */
+static void
+set_eth_masked(const uint8_t value_src[ETH_ADDR_LEN],
+ const uint8_t mask_src[ETH_ADDR_LEN],
+ uint8_t value_dst[ETH_ADDR_LEN],
+ uint8_t mask_dst[ETH_ADDR_LEN])
+{
+ size_t i;
+
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ value_dst[i] = value_src[i] & mask_src[i];
+ mask_dst[i] = mask_src[i];
+ }
+}
+
+/* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
+ * exactly. */
+void
+match_set_dl_src(struct match *match, const uint8_t dl_src[ETH_ADDR_LEN])
+{
+ set_eth(dl_src, match->flow.dl_src, match->wc.masks.dl_src);
+}
+
+/* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
+ * after each byte is ANDed with the appropriate byte in 'mask'. */
+void
+match_set_dl_src_masked(struct match *match,
+ const uint8_t dl_src[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ set_eth_masked(dl_src, mask, match->flow.dl_src, match->wc.masks.dl_src);
+}
+
+/* Modifies 'match' so that the Ethernet address must match 'dl_dst'
+ * exactly. */
+void
+match_set_dl_dst(struct match *match, const uint8_t dl_dst[ETH_ADDR_LEN])
+{
+ set_eth(dl_dst, match->flow.dl_dst, match->wc.masks.dl_dst);
+}
+
+/* Modifies 'match' so that the Ethernet address must match 'dl_dst' after each
+ * byte is ANDed with the appropriate byte in 'mask'.
+ *
+ * This function will assert-fail if 'mask' is invalid. Only 'mask' values
+ * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+void
+match_set_dl_dst_masked(struct match *match,
+ const uint8_t dl_dst[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ set_eth_masked(dl_dst, mask, match->flow.dl_dst, match->wc.masks.dl_dst);
+}
+
+void
+match_set_dl_tci(struct match *match, ovs_be16 tci)
+{
+ match_set_dl_tci_masked(match, tci, htons(0xffff));
+}
+
+void
+match_set_dl_tci_masked(struct match *match, ovs_be16 tci, ovs_be16 mask)
+{
+ match->flow.vlan_tci = tci & mask;
+ match->wc.masks.vlan_tci = mask;
+}
+
+/* Modifies 'match' so that the VLAN VID is wildcarded. If the PCP is already
+ * wildcarded, then 'match' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+match_set_any_vid(struct match *match)
+{
+ if (match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK)) {
+ match->wc.masks.vlan_tci &= ~htons(VLAN_VID_MASK);
+ match->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+ } else {
+ match_set_dl_tci_masked(match, htons(0), htons(0));
+ }
+}
+
+/* Modifies 'match' depending on 'dl_vlan':
+ *
+ * - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'match' match only packets
+ * without an 802.1Q header.
+ *
+ * - Otherwise, makes 'match' match only packets with an 802.1Q header whose
+ * VID equals the low 12 bits of 'dl_vlan'.
+ */
+void
+match_set_dl_vlan(struct match *match, ovs_be16 dl_vlan)
+{
+ flow_set_dl_vlan(&match->flow, dl_vlan);
+ if (dl_vlan == htons(OFP10_VLAN_NONE)) {
+ match->wc.masks.vlan_tci = htons(UINT16_MAX);
+ } else {
+ match->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ }
+}
+
+/* Sets the VLAN VID that 'match' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI). */
+void
+match_set_vlan_vid(struct match *match, ovs_be16 vid)
+{
+ match_set_vlan_vid_masked(match, vid, htons(VLAN_VID_MASK | VLAN_CFI));
+}
+
+
+/* Sets the VLAN VID that 'flow' matches to 'vid', which is interpreted as an
+ * OpenFlow 1.2 "vlan_vid" value, that is, the low 13 bits of 'vlan_tci' (VID
+ * plus CFI), with the corresponding 'mask'. */
+void
+match_set_vlan_vid_masked(struct match *match, ovs_be16 vid, ovs_be16 mask)
+{
+ ovs_be16 pcp_mask = htons(VLAN_PCP_MASK);
+ ovs_be16 vid_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+
+ mask &= vid_mask;
+ flow_set_vlan_vid(&match->flow, vid & mask);
+ match->wc.masks.vlan_tci = mask | (match->wc.masks.vlan_tci & pcp_mask);
+}
+
+/* Modifies 'match' so that the VLAN PCP is wildcarded. If the VID is already
+ * wildcarded, then 'match' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+match_set_any_pcp(struct match *match)
+{
+ if (match->wc.masks.vlan_tci & htons(VLAN_VID_MASK)) {
+ match->wc.masks.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ match->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+ } else {
+ match_set_dl_tci_masked(match, htons(0), htons(0));
+ }
+}
+
+/* Modifies 'match' so that it matches only packets with an 802.1Q header whose
+ * PCP equals the low 3 bits of 'dl_vlan_pcp'. */
+void
+match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
+{
+ flow_set_vlan_pcp(&match->flow, dl_vlan_pcp);
+ match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
+}
+
+void
+match_set_tp_src(struct match *match, ovs_be16 tp_src)
+{
+ match_set_tp_src_masked(match, tp_src, htons(UINT16_MAX));
+}
+
+void
+match_set_tp_src_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
+{
+ match->flow.tp_src = port & mask;
+ match->wc.masks.tp_src = mask;
+}
+
+void
+match_set_tp_dst(struct match *match, ovs_be16 tp_dst)
+{
+ match_set_tp_dst_masked(match, tp_dst, htons(UINT16_MAX));
+}
+
+void
+match_set_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
+{
+ match->flow.tp_dst = port & mask;
+ match->wc.masks.tp_dst = mask;
+}
+
+void
+match_set_nw_proto(struct match *match, uint8_t nw_proto)
+{
+ match->flow.nw_proto = nw_proto;
+ match->wc.masks.nw_proto = UINT8_MAX;
+}
+
+void
+match_set_nw_src(struct match *match, ovs_be32 nw_src)
+{
+ match->flow.nw_src = nw_src;
+ match->wc.masks.nw_src = htonl(UINT32_MAX);
+}
+
+void
+match_set_nw_src_masked(struct match *match,
+ ovs_be32 nw_src, ovs_be32 mask)
+{
+ match->flow.nw_src = nw_src & mask;
+ match->wc.masks.nw_src = mask;
+}
+
+void
+match_set_nw_dst(struct match *match, ovs_be32 nw_dst)
+{
+ match->flow.nw_dst = nw_dst;
+ match->wc.masks.nw_dst = htonl(UINT32_MAX);
+}
+
+void
+match_set_nw_dst_masked(struct match *match, ovs_be32 ip, ovs_be32 mask)
+{
+ match->flow.nw_dst = ip & mask;
+ match->wc.masks.nw_dst = mask;
+}
+
+void
+match_set_nw_dscp(struct match *match, uint8_t nw_dscp)
+{
+ match->wc.masks.nw_tos |= IP_DSCP_MASK;
+ match->flow.nw_tos &= ~IP_DSCP_MASK;
+ match->flow.nw_tos |= nw_dscp & IP_DSCP_MASK;
+}
+
+void
+match_set_nw_ecn(struct match *match, uint8_t nw_ecn)
+{
+ match->wc.masks.nw_tos |= IP_ECN_MASK;
+ match->flow.nw_tos &= ~IP_ECN_MASK;
+ match->flow.nw_tos |= nw_ecn & IP_ECN_MASK;
+}
+
+void
+match_set_nw_ttl(struct match *match, uint8_t nw_ttl)
+{
+ match->wc.masks.nw_ttl = UINT8_MAX;
+ match->flow.nw_ttl = nw_ttl;
+}
+
+void
+match_set_nw_frag(struct match *match, uint8_t nw_frag)
+{
+ match->wc.masks.nw_frag |= FLOW_NW_FRAG_MASK;
+ match->flow.nw_frag = nw_frag;
+}
+
+void
+match_set_nw_frag_masked(struct match *match,
+ uint8_t nw_frag, uint8_t mask)
+{
+ match->flow.nw_frag = nw_frag & mask;
+ match->wc.masks.nw_frag = mask;
+}
+
+void
+match_set_icmp_type(struct match *match, uint8_t icmp_type)
+{
+ match_set_tp_src(match, htons(icmp_type));
+}
+
+void
+match_set_icmp_code(struct match *match, uint8_t icmp_code)
+{
+ match_set_tp_dst(match, htons(icmp_code));
+}
+
+void
+match_set_arp_sha(struct match *match, const uint8_t sha[ETH_ADDR_LEN])
+{
+ memcpy(match->flow.arp_sha, sha, ETH_ADDR_LEN);
+ memset(match->wc.masks.arp_sha, UINT8_MAX, ETH_ADDR_LEN);
+}
+
+void
+match_set_arp_sha_masked(struct match *match,
+ const uint8_t arp_sha[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ set_eth_masked(arp_sha, mask,
+ match->flow.arp_sha, match->wc.masks.arp_sha);
+}
+
+void
+match_set_arp_tha(struct match *match, const uint8_t tha[ETH_ADDR_LEN])
+{
+ memcpy(match->flow.arp_tha, tha, ETH_ADDR_LEN);
+ memset(match->wc.masks.arp_tha, UINT8_MAX, ETH_ADDR_LEN);
+}
+
+void
+match_set_arp_tha_masked(struct match *match,
+ const uint8_t arp_tha[ETH_ADDR_LEN],
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ set_eth_masked(arp_tha, mask,
+ match->flow.arp_tha, match->wc.masks.arp_tha);
+}
+
+void
+match_set_ipv6_src(struct match *match, const struct in6_addr *src)
+{
+ match->flow.ipv6_src = *src;
+ match->wc.masks.ipv6_src = in6addr_exact;
+}
+
+void
+match_set_ipv6_src_masked(struct match *match, const struct in6_addr *src,
+ const struct in6_addr *mask)
+{
+ match->flow.ipv6_src = ipv6_addr_bitand(src, mask);
+ match->wc.masks.ipv6_src = *mask;
+}
+
+void
+match_set_ipv6_dst(struct match *match, const struct in6_addr *dst)
+{
+ match->flow.ipv6_dst = *dst;
+ match->wc.masks.ipv6_dst = in6addr_exact;
+}
+
+void
+match_set_ipv6_dst_masked(struct match *match, const struct in6_addr *dst,
+ const struct in6_addr *mask)
+{
+ match->flow.ipv6_dst = ipv6_addr_bitand(dst, mask);
+ match->wc.masks.ipv6_dst = *mask;
+}
+
+void
+match_set_ipv6_label(struct match *match, ovs_be32 ipv6_label)
+{
+ match->wc.masks.ipv6_label = htonl(UINT32_MAX);
+ match->flow.ipv6_label = ipv6_label;
+}
+
+
+void
+match_set_ipv6_label_masked(struct match *match, ovs_be32 ipv6_label,
+ ovs_be32 mask)
+{
+ match->flow.ipv6_label = ipv6_label & mask;
+ match->wc.masks.ipv6_label = mask;
+}
+
+void
+match_set_nd_target(struct match *match, const struct in6_addr *target)
+{
+ match->flow.nd_target = *target;
+ match->wc.masks.nd_target = in6addr_exact;
+}
+
+void
+match_set_nd_target_masked(struct match *match,
+ const struct in6_addr *target,
+ const struct in6_addr *mask)
+{
+ match->flow.nd_target = ipv6_addr_bitand(target, mask);
+ match->wc.masks.nd_target = *mask;
+}
+
+/* Returns true if 'a' and 'b' wildcard the same fields and have the same
+ * values for fixed fields, otherwise false. */
+bool
+match_equal(const struct match *a, const struct match *b)
+{
+ return (flow_wildcards_equal(&a->wc, &b->wc)
+ && flow_equal(&a->flow, &b->flow));
+}
+
+/* Returns a hash value for the flow and wildcards in 'match', starting from
+ * 'basis'. */
+uint32_t
+match_hash(const struct match *match, uint32_t basis)
+{
+ return flow_wildcards_hash(&match->wc, flow_hash(&match->flow, basis));
+}
+
+static void
+format_eth_masked(struct ds *s, const char *name, const uint8_t eth[6],
+ const uint8_t mask[6])
+{
+ if (!eth_addr_is_zero(mask)) {
+ ds_put_format(s, "%s=", name);
+ eth_format_masked(eth, mask, s);
+ ds_put_char(s, ',');
+ }
+}
+
+static void
+format_ip_netmask(struct ds *s, const char *name, ovs_be32 ip,
+ ovs_be32 netmask)
+{
+ if (netmask) {
+ ds_put_format(s, "%s=", name);
+ ip_format_masked(ip, netmask, s);
+ ds_put_char(s, ',');
+ }
+}
+
+static void
+format_ipv6_netmask(struct ds *s, const char *name,
+ const struct in6_addr *addr,
+ const struct in6_addr *netmask)
+{
+ if (!ipv6_mask_is_any(netmask)) {
+ ds_put_format(s, "%s=", name);
+ print_ipv6_masked(s, addr, netmask);
+ ds_put_char(s, ',');
+ }
+}
+
+
+static void
+format_be16_masked(struct ds *s, const char *name,
+ ovs_be16 value, ovs_be16 mask)
+{
+ if (mask != htons(0)) {
+ ds_put_format(s, "%s=", name);
+ if (mask == htons(UINT16_MAX)) {
+ ds_put_format(s, "%"PRIu16, ntohs(value));
+ } else {
+ ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16,
+ ntohs(value), ntohs(mask));
+ }
+ ds_put_char(s, ',');
+ }
+}
+
+/* Appends a string representation of 'match' to 's'. If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
+void
+match_format(const struct match *match, struct ds *s, unsigned int priority)
+{
+ const struct flow_wildcards *wc = &match->wc;
+ size_t start_len = s->length;
+ const struct flow *f = &match->flow;
+ bool skip_type = false;
+ bool skip_proto = false;
+
+ int i;
+
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+
+ if (priority != OFP_DEFAULT_PRIORITY) {
+ ds_put_format(s, "priority=%u,", priority);
+ }
+
+ if (wc->masks.dl_type) {
+ skip_type = true;
+ if (f->dl_type == htons(ETH_TYPE_IP)) {
+ if (wc->masks.nw_proto) {
+ skip_proto = true;
+ if (f->nw_proto == IPPROTO_ICMP) {
+ ds_put_cstr(s, "icmp,");
+ } else if (f->nw_proto == IPPROTO_TCP) {
+ ds_put_cstr(s, "tcp,");
+ } else if (f->nw_proto == IPPROTO_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_IPV6)) {
+ if (wc->masks.nw_proto) {
+ skip_proto = true;
+ if (f->nw_proto == IPPROTO_ICMPV6) {
+ ds_put_cstr(s, "icmp6,");
+ } else if (f->nw_proto == IPPROTO_TCP) {
+ ds_put_cstr(s, "tcp6,");
+ } else if (f->nw_proto == IPPROTO_UDP) {
+ ds_put_cstr(s, "udp6,");
+ } else {
+ ds_put_cstr(s, "ipv6,");
+ skip_proto = false;
+ }
+ } else {
+ ds_put_cstr(s, "ipv6,");
+ }
+ } 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->masks.regs[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->masks.regs[i]);
+ break;
+ }
+ }
+ switch (wc->masks.tun_id) {
+ 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->masks.tun_id));
+ break;
+ }
+ switch (wc->masks.metadata) {
+ case 0:
+ break;
+ case CONSTANT_HTONLL(UINT64_MAX):
+ ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
+ break;
+ default:
+ ds_put_format(s, "metadata=%#"PRIx64"/%#"PRIx64",",
+ ntohll(f->metadata), ntohll(wc->masks.metadata));
+ break;
+ }
+ if (wc->masks.in_port) {
+ ds_put_format(s, "in_port=%"PRIu16",", f->in_port);
+ }
+ if (wc->masks.vlan_tci) {
+ ovs_be16 vid_mask = wc->masks.vlan_tci & htons(VLAN_VID_MASK);
+ ovs_be16 pcp_mask = wc->masks.vlan_tci & htons(VLAN_PCP_MASK);
+ ovs_be16 cfi = wc->masks.vlan_tci & 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 if (wc->masks.vlan_tci == htons(0xffff)) {
+ ds_put_format(s, "vlan_tci=0x%04"PRIx16",", ntohs(f->vlan_tci));
+ } else {
+ ds_put_format(s, "vlan_tci=0x%04"PRIx16"/0x%04"PRIx16",",
+ ntohs(f->vlan_tci), ntohs(wc->masks.vlan_tci));
+ }
+ }
+ format_eth_masked(s, "dl_src", f->dl_src, wc->masks.dl_src);
+ format_eth_masked(s, "dl_dst", f->dl_dst, wc->masks.dl_dst);
+ if (!skip_type && wc->masks.dl_type) {
+ ds_put_format(s, "dl_type=0x%04"PRIx16",", ntohs(f->dl_type));
+ }
+ if (f->dl_type == htons(ETH_TYPE_IPV6)) {
+ format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->masks.ipv6_src);
+ format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->masks.ipv6_dst);
+ if (wc->masks.ipv6_label) {
+ if (wc->masks.ipv6_label == htonl(UINT32_MAX)) {
+ ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
+ ntohl(f->ipv6_label));
+ } else {
+ ds_put_format(s, "ipv6_label=0x%05"PRIx32"/0x%05"PRIx32",",
+ ntohl(f->ipv6_label),
+ ntohl(wc->masks.ipv6_label));
+ }
+ }
+ } else {
+ format_ip_netmask(s, "nw_src", f->nw_src, wc->masks.nw_src);
+ format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst);
+ }
+ if (!skip_proto && wc->masks.nw_proto) {
+ if (f->dl_type == htons(ETH_TYPE_ARP)) {
+ ds_put_format(s, "arp_op=%"PRIu8",", f->nw_proto);
+ } else {
+ ds_put_format(s, "nw_proto=%"PRIu8",", f->nw_proto);
+ }
+ }
+ if (f->dl_type == htons(ETH_TYPE_ARP)) {
+ format_eth_masked(s, "arp_sha", f->arp_sha, wc->masks.arp_sha);
+ format_eth_masked(s, "arp_tha", f->arp_tha, wc->masks.arp_tha);
+ }
+ if (wc->masks.nw_tos & IP_DSCP_MASK) {
+ ds_put_format(s, "nw_tos=%"PRIu8",", f->nw_tos & IP_DSCP_MASK);
+ }
+ if (wc->masks.nw_tos & IP_ECN_MASK) {
+ ds_put_format(s, "nw_ecn=%"PRIu8",", f->nw_tos & IP_ECN_MASK);
+ }
+ if (wc->masks.nw_ttl) {
+ ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
+ }
+ switch (wc->masks.nw_frag) {
+ case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
+ ds_put_format(s, "nw_frag=%s,",
+ f->nw_frag & FLOW_NW_FRAG_ANY
+ ? (f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "first")
+ : (f->nw_frag & FLOW_NW_FRAG_LATER ? "<error>" : "no"));
+ break;
+
+ case FLOW_NW_FRAG_ANY:
+ ds_put_format(s, "nw_frag=%s,",
+ f->nw_frag & FLOW_NW_FRAG_ANY ? "yes" : "no");
+ break;
+
+ case FLOW_NW_FRAG_LATER:
+ ds_put_format(s, "nw_frag=%s,",
+ f->nw_frag & FLOW_NW_FRAG_LATER ? "later" : "not_later");
+ break;
+ }
+ if (f->nw_proto == IPPROTO_ICMP) {
+ format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
+ format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
+ } else if (f->nw_proto == IPPROTO_ICMPV6) {
+ format_be16_masked(s, "icmp_type", f->tp_src, wc->masks.tp_src);
+ format_be16_masked(s, "icmp_code", f->tp_dst, wc->masks.tp_dst);
+ format_ipv6_netmask(s, "nd_target", &f->nd_target,
+ &wc->masks.nd_target);
+ format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha);
+ format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha);
+ } else {
+ format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
+ format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
+ }
+
+ if (s->length > start_len && ds_last(s) == ',') {
+ s->length--;
+ }
+}
+
+/* Converts 'match' to a string and returns the string. If 'priority' is
+ * different from OFP_DEFAULT_PRIORITY, includes it in the string. The caller
+ * must free the string (with free()). */
+char *
+match_to_string(const struct match *match, unsigned int priority)
+{
+ struct ds s = DS_EMPTY_INITIALIZER;
+ match_format(match, &s, priority);
+ return ds_steal_cstr(&s);
+}
+
+void
+match_print(const struct match *match)
+{
+ char *s = match_to_string(match, OFP_DEFAULT_PRIORITY);
+ puts(s);
+ free(s);
+}