mirror of
https://github.com/openvswitch/ovs
synced 2025-10-21 14:49:41 +00:00
This patch adds a new 128-bit metadata field to the connection tracking interface. When a label is specified as part of the ct action and the connection is committed, the value is saved with the current connection. Subsequent ct lookups with the table specified will expose this metadata as the "ct_label" field in the flow. For example, to allow new TCP connections from port 1->2 and only allow established connections from port 2->1, and to associate a label with those connections: table=0,priority=1,action=drop table=0,arp,action=normal table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2 table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1) table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1 Signed-off-by: Joe Stringer <joestringer@nicira.com> Acked-by: Jarno Rajahalme <jrajahalme@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
1404 lines
41 KiB
C
1404 lines
41 KiB
C
/*
|
||
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 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 <stdlib.h>
|
||
#include "byte-order.h"
|
||
#include "dynamic-string.h"
|
||
#include "ofp-util.h"
|
||
#include "packets.h"
|
||
#include "tun-metadata.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);
|
||
memset(&match->tun_md, 0, sizeof match->tun_md);
|
||
}
|
||
|
||
/* Converts a flow into a match. It sets the wildcard masks based on
|
||
* the packet contents. It will not set the mask for fields that do not
|
||
* make sense for the packet type. */
|
||
void
|
||
match_wc_init(struct match *match, const struct flow *flow)
|
||
{
|
||
match->flow = *flow;
|
||
|
||
flow_wildcards_init_for_packet(&match->wc, flow);
|
||
WC_MASK_FIELD(&match->wc, regs);
|
||
WC_MASK_FIELD(&match->wc, metadata);
|
||
|
||
memset(&match->tun_md, 0, sizeof match->tun_md);
|
||
}
|
||
|
||
/* 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);
|
||
memset(&match->tun_md, 0, sizeof match->tun_md);
|
||
}
|
||
|
||
/* 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_dp_hash(struct match *match, uint32_t value)
|
||
{
|
||
match_set_dp_hash_masked(match, value, UINT32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_dp_hash_masked(struct match *match, uint32_t value, uint32_t mask)
|
||
{
|
||
match->wc.masks.dp_hash = mask;
|
||
match->flow.dp_hash = value & mask;
|
||
}
|
||
|
||
void
|
||
match_set_recirc_id(struct match *match, uint32_t value)
|
||
{
|
||
match->flow.recirc_id = value;
|
||
match->wc.masks.recirc_id = UINT32_MAX;
|
||
}
|
||
|
||
void
|
||
match_set_conj_id(struct match *match, uint32_t value)
|
||
{
|
||
match->flow.conj_id = value;
|
||
match->wc.masks.conj_id = UINT32_MAX;
|
||
}
|
||
|
||
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)
|
||
{
|
||
ovs_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_xreg(struct match *match, unsigned int xreg_idx, uint64_t value)
|
||
{
|
||
match_set_xreg_masked(match, xreg_idx, value, UINT64_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_xreg_masked(struct match *match, unsigned int xreg_idx,
|
||
uint64_t value, uint64_t mask)
|
||
{
|
||
ovs_assert(xreg_idx < FLOW_N_XREGS);
|
||
flow_wildcards_set_xreg_mask(&match->wc, xreg_idx, mask);
|
||
flow_set_xreg(&match->flow, xreg_idx, value & mask);
|
||
}
|
||
|
||
void
|
||
match_set_actset_output(struct match *match, ofp_port_t actset_output)
|
||
{
|
||
match->wc.masks.actset_output = u16_to_ofp(UINT16_MAX);
|
||
match->flow.actset_output = actset_output;
|
||
}
|
||
|
||
void
|
||
match_set_metadata(struct match *match, ovs_be64 metadata)
|
||
{
|
||
match_set_metadata_masked(match, metadata, OVS_BE64_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, OVS_BE64_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
|
||
{
|
||
match->wc.masks.tunnel.tun_id = mask;
|
||
match->flow.tunnel.tun_id = tun_id & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_src(struct match *match, ovs_be32 src)
|
||
{
|
||
match_set_tun_src_masked(match, src, OVS_BE32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask)
|
||
{
|
||
match->wc.masks.tunnel.ip_src = mask;
|
||
match->flow.tunnel.ip_src = src & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_dst(struct match *match, ovs_be32 dst)
|
||
{
|
||
match_set_tun_dst_masked(match, dst, OVS_BE32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask)
|
||
{
|
||
match->wc.masks.tunnel.ip_dst = mask;
|
||
match->flow.tunnel.ip_dst = dst & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_ttl(struct match *match, uint8_t ttl)
|
||
{
|
||
match_set_tun_ttl_masked(match, ttl, UINT8_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask)
|
||
{
|
||
match->wc.masks.tunnel.ip_ttl = mask;
|
||
match->flow.tunnel.ip_ttl = ttl & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_tos(struct match *match, uint8_t tos)
|
||
{
|
||
match_set_tun_tos_masked(match, tos, UINT8_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask)
|
||
{
|
||
match->wc.masks.tunnel.ip_tos = mask;
|
||
match->flow.tunnel.ip_tos = tos & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_flags(struct match *match, uint16_t flags)
|
||
{
|
||
match_set_tun_flags_masked(match, flags, UINT16_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask)
|
||
{
|
||
mask &= FLOW_TNL_PUB_F_MASK;
|
||
|
||
match->wc.masks.tunnel.flags = mask;
|
||
match->flow.tunnel.flags = flags & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 mask)
|
||
{
|
||
match->wc.masks.tunnel.gbp_id = mask;
|
||
match->flow.tunnel.gbp_id = gbp_id & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id)
|
||
{
|
||
match_set_tun_gbp_id_masked(match, gbp_id, OVS_BE16_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask)
|
||
{
|
||
match->wc.masks.tunnel.gbp_flags = mask;
|
||
match->flow.tunnel.gbp_flags = flags & mask;
|
||
}
|
||
|
||
void
|
||
match_set_tun_gbp_flags(struct match *match, uint8_t flags)
|
||
{
|
||
match_set_tun_gbp_flags_masked(match, flags, UINT8_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_in_port(struct match *match, ofp_port_t ofp_port)
|
||
{
|
||
match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
|
||
match->flow.in_port.ofp_port = ofp_port;
|
||
}
|
||
|
||
void
|
||
match_set_skb_priority(struct match *match, uint32_t skb_priority)
|
||
{
|
||
match->wc.masks.skb_priority = UINT32_MAX;
|
||
match->flow.skb_priority = skb_priority;
|
||
}
|
||
|
||
void
|
||
match_set_pkt_mark(struct match *match, uint32_t pkt_mark)
|
||
{
|
||
match_set_pkt_mark_masked(match, pkt_mark, UINT32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_pkt_mark_masked(struct match *match, uint32_t pkt_mark, uint32_t mask)
|
||
{
|
||
match->flow.pkt_mark = pkt_mark & mask;
|
||
match->wc.masks.pkt_mark = mask;
|
||
}
|
||
|
||
void
|
||
match_set_ct_state(struct match *match, uint32_t ct_state)
|
||
{
|
||
match_set_ct_state_masked(match, ct_state, UINT32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_ct_state_masked(struct match *match, uint32_t ct_state, uint32_t mask)
|
||
{
|
||
match->flow.ct_state = ct_state & mask & UINT16_MAX;
|
||
match->wc.masks.ct_state = mask & UINT16_MAX;
|
||
}
|
||
|
||
void
|
||
match_set_ct_zone(struct match *match, uint16_t ct_zone)
|
||
{
|
||
match->flow.ct_zone = ct_zone;
|
||
match->wc.masks.ct_zone = UINT16_MAX;
|
||
}
|
||
|
||
void
|
||
match_set_ct_mark(struct match *match, uint32_t ct_mark)
|
||
{
|
||
match_set_ct_mark_masked(match, ct_mark, UINT32_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_ct_mark_masked(struct match *match, uint32_t ct_mark,
|
||
uint32_t mask)
|
||
{
|
||
match->flow.ct_mark = ct_mark & mask;
|
||
match->wc.masks.ct_mark = mask;
|
||
}
|
||
|
||
void
|
||
match_set_ct_label(struct match *match, ovs_u128 ct_label)
|
||
{
|
||
ovs_u128 mask;
|
||
|
||
mask.u64.lo = UINT64_MAX;
|
||
mask.u64.hi = UINT64_MAX;
|
||
match_set_ct_label_masked(match, ct_label, mask);
|
||
}
|
||
|
||
void
|
||
match_set_ct_label_masked(struct match *match, ovs_u128 value, ovs_u128 mask)
|
||
{
|
||
match->flow.ct_label.u64.lo = value.u64.lo & mask.u64.lo;
|
||
match->flow.ct_label.u64.hi = value.u64.hi & mask.u64.hi;
|
||
match->wc.masks.ct_label = mask;
|
||
}
|
||
|
||
void
|
||
match_set_dl_type(struct match *match, ovs_be16 dl_type)
|
||
{
|
||
match->wc.masks.dl_type = OVS_BE16_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 struct eth_addr value_src,
|
||
struct eth_addr *value_dst,
|
||
struct eth_addr *mask_dst)
|
||
{
|
||
*value_dst = value_src;
|
||
*mask_dst = eth_addr_exact;
|
||
}
|
||
|
||
/* 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 struct eth_addr value_src,
|
||
const struct eth_addr mask_src,
|
||
struct eth_addr *value_dst, struct eth_addr *mask_dst)
|
||
{
|
||
size_t i;
|
||
|
||
for (i = 0; i < ARRAY_SIZE(value_dst->be16); i++) {
|
||
value_dst->be16[i] = value_src.be16[i] & mask_src.be16[i];
|
||
}
|
||
*mask_dst = mask_src;
|
||
}
|
||
|
||
/* Modifies 'rule' so that the source Ethernet address must match 'dl_src'
|
||
* exactly. */
|
||
void
|
||
match_set_dl_src(struct match *match, const struct eth_addr dl_src)
|
||
{
|
||
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 struct eth_addr dl_src,
|
||
const struct eth_addr mask)
|
||
{
|
||
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 struct eth_addr dl_dst)
|
||
{
|
||
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 struct eth_addr dl_dst,
|
||
const struct eth_addr mask)
|
||
{
|
||
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 = OVS_BE16_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);
|
||
}
|
||
|
||
/* Modifies 'match' so that the MPLS label 'idx' matches 'lse' exactly. */
|
||
void
|
||
match_set_mpls_lse(struct match *match, int idx, ovs_be32 lse)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] = OVS_BE32_MAX;
|
||
match->flow.mpls_lse[idx] = lse;
|
||
}
|
||
|
||
/* Modifies 'match' so that the MPLS label is wildcarded. */
|
||
void
|
||
match_set_any_mpls_label(struct match *match, int idx)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_LABEL_MASK);
|
||
flow_set_mpls_label(&match->flow, idx, htonl(0));
|
||
}
|
||
|
||
/* Modifies 'match' so that it matches only packets with an MPLS header whose
|
||
* label equals the low 20 bits of 'mpls_label'. */
|
||
void
|
||
match_set_mpls_label(struct match *match, int idx, ovs_be32 mpls_label)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] |= htonl(MPLS_LABEL_MASK);
|
||
flow_set_mpls_label(&match->flow, idx, mpls_label);
|
||
}
|
||
|
||
/* Modifies 'match' so that the MPLS TC is wildcarded. */
|
||
void
|
||
match_set_any_mpls_tc(struct match *match, int idx)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_TC_MASK);
|
||
flow_set_mpls_tc(&match->flow, idx, 0);
|
||
}
|
||
|
||
/* Modifies 'match' so that it matches only packets with an MPLS header whose
|
||
* Traffic Class equals the low 3 bits of 'mpls_tc'. */
|
||
void
|
||
match_set_mpls_tc(struct match *match, int idx, uint8_t mpls_tc)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] |= htonl(MPLS_TC_MASK);
|
||
flow_set_mpls_tc(&match->flow, idx, mpls_tc);
|
||
}
|
||
|
||
/* Modifies 'match' so that the MPLS stack flag is wildcarded. */
|
||
void
|
||
match_set_any_mpls_bos(struct match *match, int idx)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] &= ~htonl(MPLS_BOS_MASK);
|
||
flow_set_mpls_bos(&match->flow, idx, 0);
|
||
}
|
||
|
||
/* Modifies 'match' so that it matches only packets with an MPLS header whose
|
||
* Stack Flag equals the lower bit of 'mpls_bos' */
|
||
void
|
||
match_set_mpls_bos(struct match *match, int idx, uint8_t mpls_bos)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] |= htonl(MPLS_BOS_MASK);
|
||
flow_set_mpls_bos(&match->flow, idx, mpls_bos);
|
||
}
|
||
|
||
/* Modifies 'match' so that the MPLS LSE is wildcarded. */
|
||
void
|
||
match_set_any_mpls_lse(struct match *match, int idx)
|
||
{
|
||
match->wc.masks.mpls_lse[idx] = htonl(0);
|
||
flow_set_mpls_lse(&match->flow, idx, htonl(0));
|
||
}
|
||
|
||
void
|
||
match_set_tp_src(struct match *match, ovs_be16 tp_src)
|
||
{
|
||
match_set_tp_src_masked(match, tp_src, OVS_BE16_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, OVS_BE16_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_tcp_flags(struct match *match, ovs_be16 flags)
|
||
{
|
||
match_set_tcp_flags_masked(match, flags, OVS_BE16_MAX);
|
||
}
|
||
|
||
void
|
||
match_set_tcp_flags_masked(struct match *match, ovs_be16 flags, ovs_be16 mask)
|
||
{
|
||
match->flow.tcp_flags = flags & mask;
|
||
match->wc.masks.tcp_flags = 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 = OVS_BE32_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 = OVS_BE32_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 struct eth_addr sha)
|
||
{
|
||
match->flow.arp_sha = sha;
|
||
match->wc.masks.arp_sha = eth_addr_exact;
|
||
}
|
||
|
||
void
|
||
match_set_arp_sha_masked(struct match *match,
|
||
const struct eth_addr arp_sha,
|
||
const struct eth_addr mask)
|
||
{
|
||
set_eth_masked(arp_sha, mask,
|
||
&match->flow.arp_sha, &match->wc.masks.arp_sha);
|
||
}
|
||
|
||
void
|
||
match_set_arp_tha(struct match *match, const struct eth_addr tha)
|
||
{
|
||
match->flow.arp_tha = tha;
|
||
match->wc.masks.arp_tha = eth_addr_exact;
|
||
}
|
||
|
||
void
|
||
match_set_arp_tha_masked(struct match *match,
|
||
const struct eth_addr arp_tha,
|
||
const struct eth_addr mask)
|
||
{
|
||
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 = OVS_BE32_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 bool
|
||
match_has_default_recirc_id(const struct match *m)
|
||
{
|
||
return m->flow.recirc_id == 0 && (m->wc.masks.recirc_id == UINT32_MAX ||
|
||
m->wc.masks.recirc_id == 0);
|
||
}
|
||
|
||
static bool
|
||
match_has_default_dp_hash(const struct match *m)
|
||
{
|
||
return ((m->flow.dp_hash | m->wc.masks.dp_hash) == 0);
|
||
}
|
||
|
||
/* Return true if the hidden fields of the match are set to the default values.
|
||
* The default values equals to those set up by match_init_hidden_fields(). */
|
||
bool
|
||
match_has_default_hidden_fields(const struct match *m)
|
||
{
|
||
return match_has_default_recirc_id(m) && match_has_default_dp_hash(m);
|
||
}
|
||
|
||
void
|
||
match_init_hidden_fields(struct match *m)
|
||
{
|
||
match_set_recirc_id(m, 0);
|
||
match_set_dp_hash_masked(m, 0, 0);
|
||
}
|
||
|
||
static void
|
||
format_eth_masked(struct ds *s, const char *name,
|
||
const struct eth_addr eth, const struct eth_addr mask)
|
||
{
|
||
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_uint16_masked(struct ds *s, const char *name,
|
||
uint16_t value, uint16_t mask)
|
||
{
|
||
if (mask != 0) {
|
||
ds_put_format(s, "%s=", name);
|
||
if (mask == UINT16_MAX) {
|
||
ds_put_format(s, "%"PRIu16, value);
|
||
} else {
|
||
ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16, value, mask);
|
||
}
|
||
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 == OVS_BE16_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, ',');
|
||
}
|
||
}
|
||
|
||
static void
|
||
format_be32_masked(struct ds *s, const char *name,
|
||
ovs_be32 value, ovs_be32 mask)
|
||
{
|
||
if (mask != htonl(0)) {
|
||
ds_put_format(s, "%s=", name);
|
||
if (mask == OVS_BE32_MAX) {
|
||
ds_put_format(s, "%"PRIu32, ntohl(value));
|
||
} else {
|
||
ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
|
||
ntohl(value), ntohl(mask));
|
||
}
|
||
ds_put_char(s, ',');
|
||
}
|
||
}
|
||
|
||
static void
|
||
format_uint32_masked(struct ds *s, const char *name,
|
||
uint32_t value, uint32_t mask)
|
||
{
|
||
if (mask) {
|
||
ds_put_format(s, "%s=%#"PRIx32, name, value);
|
||
if (mask != UINT32_MAX) {
|
||
ds_put_format(s, "/%#"PRIx32, mask);
|
||
}
|
||
ds_put_char(s, ',');
|
||
}
|
||
}
|
||
|
||
static void
|
||
format_be64_masked(struct ds *s, const char *name,
|
||
ovs_be64 value, ovs_be64 mask)
|
||
{
|
||
if (mask != htonll(0)) {
|
||
ds_put_format(s, "%s=%#"PRIx64, name, ntohll(value));
|
||
if (mask != OVS_BE64_MAX) {
|
||
ds_put_format(s, "/%#"PRIx64, ntohll(mask));
|
||
}
|
||
ds_put_char(s, ',');
|
||
}
|
||
}
|
||
|
||
static void
|
||
format_flow_tunnel(struct ds *s, const struct match *match)
|
||
{
|
||
const struct flow_wildcards *wc = &match->wc;
|
||
const struct flow_tnl *tnl = &match->flow.tunnel;
|
||
|
||
format_be64_masked(s, "tun_id", tnl->tun_id, wc->masks.tunnel.tun_id);
|
||
format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
|
||
format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);
|
||
|
||
if (wc->masks.tunnel.gbp_id) {
|
||
format_be16_masked(s, "tun_gbp_id", tnl->gbp_id,
|
||
wc->masks.tunnel.gbp_id);
|
||
}
|
||
|
||
if (wc->masks.tunnel.gbp_flags) {
|
||
ds_put_format(s, "tun_gbp_flags=%#"PRIx8",", tnl->gbp_flags);
|
||
}
|
||
|
||
if (wc->masks.tunnel.ip_tos) {
|
||
ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
|
||
}
|
||
if (wc->masks.tunnel.ip_ttl) {
|
||
ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl);
|
||
}
|
||
if (wc->masks.tunnel.flags) {
|
||
format_flags_masked(s, "tun_flags", flow_tun_flag_to_string,
|
||
tnl->flags,
|
||
wc->masks.tunnel.flags & FLOW_TNL_F_MASK,
|
||
FLOW_TNL_F_MASK);
|
||
ds_put_char(s, ',');
|
||
}
|
||
tun_metadata_match_format(s, match);
|
||
}
|
||
|
||
static void
|
||
format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
|
||
{
|
||
if (!ovs_u128_is_zero(mask)) {
|
||
ovs_be128 value;
|
||
|
||
hton128(key, &value);
|
||
ds_put_format(s, "ct_label=");
|
||
ds_put_hex(s, &value, sizeof value);
|
||
if (!is_all_ones(mask, sizeof(*mask))) {
|
||
hton128(mask, &value);
|
||
ds_put_char(s, '/');
|
||
ds_put_hex(s, &value, sizeof value);
|
||
}
|
||
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, 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 == 34);
|
||
|
||
if (priority != OFP_DEFAULT_PRIORITY) {
|
||
ds_put_format(s, "priority=%d,", priority);
|
||
}
|
||
|
||
format_uint32_masked(s, "pkt_mark", f->pkt_mark, wc->masks.pkt_mark);
|
||
|
||
if (wc->masks.recirc_id) {
|
||
format_uint32_masked(s, "recirc_id", f->recirc_id,
|
||
wc->masks.recirc_id);
|
||
}
|
||
|
||
if (wc->masks.dp_hash) {
|
||
format_uint32_masked(s, "dp_hash", f->dp_hash,
|
||
wc->masks.dp_hash);
|
||
}
|
||
|
||
if (wc->masks.conj_id) {
|
||
ds_put_format(s, "conj_id=%"PRIu32",", f->conj_id);
|
||
}
|
||
|
||
if (wc->masks.skb_priority) {
|
||
ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority);
|
||
}
|
||
|
||
if (wc->masks.actset_output) {
|
||
ds_put_cstr(s, "actset_output=");
|
||
ofputil_format_port(f->actset_output, s);
|
||
ds_put_char(s, ',');
|
||
}
|
||
|
||
if (wc->masks.ct_state) {
|
||
if (wc->masks.ct_state == UINT16_MAX) {
|
||
ds_put_cstr(s, "ct_state=");
|
||
if (f->ct_state) {
|
||
format_flags(s, ct_state_to_string, f->ct_state, '|');
|
||
} else {
|
||
ds_put_cstr(s, "0"); /* No state. */
|
||
}
|
||
} else {
|
||
format_flags_masked(s, "ct_state", ct_state_to_string,
|
||
f->ct_state, wc->masks.ct_state, UINT16_MAX);
|
||
}
|
||
ds_put_char(s, ',');
|
||
}
|
||
|
||
if (wc->masks.ct_zone) {
|
||
format_uint16_masked(s, "ct_zone", f->ct_zone, wc->masks.ct_zone);
|
||
}
|
||
|
||
if (wc->masks.ct_mark) {
|
||
format_uint32_masked(s, "ct_mark", f->ct_mark, wc->masks.ct_mark);
|
||
}
|
||
|
||
if (!ovs_u128_is_zero(&wc->masks.ct_label)) {
|
||
format_ct_label_masked(s, &f->ct_label, &wc->masks.ct_label);
|
||
}
|
||
|
||
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_IGMP) {
|
||
ds_put_cstr(s, "igmp,");
|
||
} 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 if (f->nw_proto == IPPROTO_SCTP) {
|
||
ds_put_cstr(s, "sctp,");
|
||
} 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 if (f->nw_proto == IPPROTO_SCTP) {
|
||
ds_put_cstr(s, "sctp6,");
|
||
} 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 if (f->dl_type == htons(ETH_TYPE_RARP)) {
|
||
ds_put_cstr(s, "rarp,");
|
||
} else if (f->dl_type == htons(ETH_TYPE_MPLS)) {
|
||
ds_put_cstr(s, "mpls,");
|
||
} else if (f->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
|
||
ds_put_cstr(s, "mplsm,");
|
||
} else {
|
||
skip_type = false;
|
||
}
|
||
}
|
||
for (i = 0; i < FLOW_N_REGS; i++) {
|
||
#define REGNAME_LEN 20
|
||
char regname[REGNAME_LEN];
|
||
if (snprintf(regname, REGNAME_LEN, "reg%d", i) >= REGNAME_LEN) {
|
||
strcpy(regname, "reg?");
|
||
}
|
||
format_uint32_masked(s, regname, f->regs[i], wc->masks.regs[i]);
|
||
}
|
||
|
||
format_flow_tunnel(s, match);
|
||
|
||
format_be64_masked(s, "metadata", f->metadata, wc->masks.metadata);
|
||
|
||
if (wc->masks.in_port.ofp_port) {
|
||
ds_put_cstr(s, "in_port=");
|
||
ofputil_format_port(f->in_port.ofp_port, s);
|
||
ds_put_char(s, ',');
|
||
}
|
||
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 == OVS_BE32_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 if (f->dl_type == htons(ETH_TYPE_ARP) ||
|
||
f->dl_type == htons(ETH_TYPE_RARP)) {
|
||
format_ip_netmask(s, "arp_spa", f->nw_src, wc->masks.nw_src);
|
||
format_ip_netmask(s, "arp_tpa", f->nw_dst, wc->masks.nw_dst);
|
||
} 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) ||
|
||
f->dl_type == htons(ETH_TYPE_RARP)) {
|
||
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) ||
|
||
f->dl_type == htons(ETH_TYPE_RARP)) {
|
||
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);
|
||
}
|
||
if (wc->masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
|
||
ds_put_format(s, "mpls_label=%"PRIu32",",
|
||
mpls_lse_to_label(f->mpls_lse[0]));
|
||
}
|
||
if (wc->masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
|
||
ds_put_format(s, "mpls_tc=%"PRIu8",",
|
||
mpls_lse_to_tc(f->mpls_lse[0]));
|
||
}
|
||
if (wc->masks.mpls_lse[0] & htonl(MPLS_TTL_MASK)) {
|
||
ds_put_format(s, "mpls_ttl=%"PRIu8",",
|
||
mpls_lse_to_ttl(f->mpls_lse[0]));
|
||
}
|
||
if (wc->masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
|
||
ds_put_format(s, "mpls_bos=%"PRIu8",",
|
||
mpls_lse_to_bos(f->mpls_lse[0]));
|
||
}
|
||
format_be32_masked(s, "mpls_lse1", f->mpls_lse[1], wc->masks.mpls_lse[1]);
|
||
format_be32_masked(s, "mpls_lse2", f->mpls_lse[2], wc->masks.mpls_lse[2]);
|
||
|
||
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->dl_type == htons(ETH_TYPE_IP) &&
|
||
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->dl_type == htons(ETH_TYPE_IP) &&
|
||
f->nw_proto == IPPROTO_IGMP) {
|
||
format_be16_masked(s, "igmp_type", f->tp_src, wc->masks.tp_src);
|
||
format_be16_masked(s, "igmp_code", f->tp_dst, wc->masks.tp_dst);
|
||
} else if (f->dl_type == htons(ETH_TYPE_IPV6) &&
|
||
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 (is_ip_any(f) && f->nw_proto == IPPROTO_TCP && wc->masks.tcp_flags) {
|
||
format_flags_masked(s, "tcp_flags", packet_tcp_flag_to_string,
|
||
ntohs(f->tcp_flags), TCP_FLAGS(wc->masks.tcp_flags),
|
||
TCP_FLAGS(OVS_BE16_MAX));
|
||
}
|
||
|
||
if (s->length > start_len) {
|
||
ds_chomp(s, ',');
|
||
}
|
||
}
|
||
|
||
/* 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, 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);
|
||
}
|
||
|
||
/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst'
|
||
* with minimatch_destroy(). */
|
||
void
|
||
minimatch_init(struct minimatch *dst, const struct match *src)
|
||
{
|
||
struct miniflow tmp;
|
||
|
||
miniflow_map_init(&tmp, &src->wc.masks);
|
||
/* Allocate two consecutive miniflows. */
|
||
miniflow_alloc(dst->flows, 2, &tmp);
|
||
miniflow_init(dst->flow, &src->flow);
|
||
minimask_init(dst->mask, &src->wc);
|
||
}
|
||
|
||
/* Initializes 'dst' as a copy of 'src'. The caller must eventually free 'dst'
|
||
* with minimatch_destroy(). */
|
||
void
|
||
minimatch_clone(struct minimatch *dst, const struct minimatch *src)
|
||
{
|
||
/* Allocate two consecutive miniflows. */
|
||
size_t data_size = miniflow_alloc(dst->flows, 2, &src->mask->masks);
|
||
|
||
memcpy(miniflow_values(dst->flow),
|
||
miniflow_get_values(src->flow), data_size);
|
||
memcpy(miniflow_values(&dst->mask->masks),
|
||
miniflow_get_values(&src->mask->masks), data_size);
|
||
}
|
||
|
||
/* Initializes 'dst' with the data in 'src', destroying 'src'. The caller must
|
||
* eventually free 'dst' with minimatch_destroy(). */
|
||
void
|
||
minimatch_move(struct minimatch *dst, struct minimatch *src)
|
||
{
|
||
dst->flow = src->flow;
|
||
dst->mask = src->mask;
|
||
}
|
||
|
||
/* Frees any memory owned by 'match'. Does not free the storage in which
|
||
* 'match' itself resides; the caller is responsible for that. */
|
||
void
|
||
minimatch_destroy(struct minimatch *match)
|
||
{
|
||
free(match->flow);
|
||
}
|
||
|
||
/* Initializes 'dst' as a copy of 'src'. */
|
||
void
|
||
minimatch_expand(const struct minimatch *src, struct match *dst)
|
||
{
|
||
miniflow_expand(src->flow, &dst->flow);
|
||
minimask_expand(src->mask, &dst->wc);
|
||
memset(&dst->tun_md, 0, sizeof dst->tun_md);
|
||
}
|
||
|
||
/* Returns true if 'a' and 'b' match the same packets, false otherwise. */
|
||
bool
|
||
minimatch_equal(const struct minimatch *a, const struct minimatch *b)
|
||
{
|
||
return minimask_equal(a->mask, b->mask)
|
||
&& miniflow_equal(a->flow, b->flow);
|
||
}
|
||
|
||
/* Returns true if 'target' satisifies 'match', that is, if each bit for which
|
||
* 'match' specifies a particular value has the correct value in 'target'.
|
||
*
|
||
* This function is equivalent to miniflow_equal_flow_in_minimask(&match->flow,
|
||
* target, &match->mask) but it is faster because of the invariant that
|
||
* match->flow.map and match->mask.map are the same. */
|
||
bool
|
||
minimatch_matches_flow(const struct minimatch *match,
|
||
const struct flow *target)
|
||
{
|
||
const uint64_t *flowp = miniflow_get_values(match->flow);
|
||
const uint64_t *maskp = miniflow_get_values(&match->mask->masks);
|
||
size_t idx;
|
||
|
||
FLOWMAP_FOR_EACH_INDEX(idx, match->flow->map) {
|
||
if ((*flowp++ ^ flow_u64_value(target, idx)) & *maskp++) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Appends a string representation of 'match' to 's'. If 'priority' is
|
||
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
|
||
void
|
||
minimatch_format(const struct minimatch *match, struct ds *s, int priority)
|
||
{
|
||
struct match megamatch;
|
||
|
||
minimatch_expand(match, &megamatch);
|
||
match_format(&megamatch, s, priority);
|
||
}
|
||
|
||
/* 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 *
|
||
minimatch_to_string(const struct minimatch *match, int priority)
|
||
{
|
||
struct match megamatch;
|
||
|
||
minimatch_expand(match, &megamatch);
|
||
return match_to_string(&megamatch, priority);
|
||
}
|