mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 06:15:47 +00:00
GTP, GPRS Tunneling Protocol, is a group of IP-based communications protocols used to carry general packet radio service (GPRS) within GSM, UMTS and LTE networks. GTP protocol has two parts: Signalling (GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used for setting up GTP-U protocol, which is an IP-in-UDP tunneling protocol. Usually GTP is used in connecting between base station for radio, Serving Gateway (S-GW), and PDN Gateway (P-GW). This patch implements GTP-U protocol for userspace datapath, supporting only required header fields and G-PDU message type. See spec in: https://tools.ietf.org/html/draft-hmm-dmm-5g-uplane-analysis-00 Tested-at: https://travis-ci.org/github/williamtu/ovs-travis/builds/666518784 Signed-off-by: Feng Yang <yangfengee04@gmail.com> Co-authored-by: Feng Yang <yangfengee04@gmail.com> Signed-off-by: Yi Yang <yangyi01@inspur.com> Co-authored-by: Yi Yang <yangyi01@inspur.com> Signed-off-by: William Tu <u9012063@gmail.com> Acked-by: Ben Pfaff <blp@ovn.org>
1167 lines
38 KiB
C
1167 lines
38 KiB
C
/*
|
||
* Copyright (c) 2008-2017 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 "openvswitch/ofp-match.h"
|
||
#include "byte-order.h"
|
||
#include "flow.h"
|
||
#include "nx-match.h"
|
||
#include "openvswitch/match.h"
|
||
#include "openvswitch/ofp-errors.h"
|
||
#include "openvswitch/ofp-msgs.h"
|
||
#include "openvswitch/ofp-port.h"
|
||
#include "openvswitch/packets.h"
|
||
#include "openvswitch/vlog.h"
|
||
|
||
VLOG_DEFINE_THIS_MODULE(ofp_match);
|
||
|
||
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
||
|
||
/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns
|
||
* an IP netmask with a 1 in each bit that must match and a 0 in each bit that
|
||
* is wildcarded.
|
||
*
|
||
* The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0
|
||
* is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits,
|
||
* ..., 32 and higher wildcard the entire field. This is the *opposite* of the
|
||
* usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are
|
||
* wildcarded. */
|
||
static ovs_be32
|
||
ofputil_wcbits_to_netmask(int wcbits)
|
||
{
|
||
wcbits &= 0x3f;
|
||
return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0;
|
||
}
|
||
|
||
/* Given the IP netmask 'netmask', returns the number of bits of the IP address
|
||
* that it wildcards, that is, the number of 0-bits in 'netmask', a number
|
||
* between 0 and 32 inclusive.
|
||
*
|
||
* If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will
|
||
* still be in the valid range but isn't otherwise meaningful. */
|
||
static int
|
||
ofputil_netmask_to_wcbits(ovs_be32 netmask)
|
||
{
|
||
return 32 - ip_count_cidr_bits(netmask);
|
||
}
|
||
|
||
/* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a
|
||
* flow_wildcards in 'wc' for use in struct match. It is the caller's
|
||
* responsibility to handle the special case where the flow match's dl_vlan is
|
||
* set to OFP_VLAN_NONE. */
|
||
void
|
||
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
|
||
{
|
||
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
|
||
|
||
/* Initialize most of wc. */
|
||
flow_wildcards_init_catchall(wc);
|
||
|
||
if (!(ofpfw & OFPFW10_IN_PORT)) {
|
||
wc->masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
|
||
}
|
||
|
||
if (!(ofpfw & OFPFW10_NW_TOS)) {
|
||
wc->masks.nw_tos |= IP_DSCP_MASK;
|
||
}
|
||
|
||
if (!(ofpfw & OFPFW10_NW_PROTO)) {
|
||
wc->masks.nw_proto = UINT8_MAX;
|
||
}
|
||
wc->masks.nw_src = ofputil_wcbits_to_netmask(ofpfw
|
||
>> OFPFW10_NW_SRC_SHIFT);
|
||
wc->masks.nw_dst = ofputil_wcbits_to_netmask(ofpfw
|
||
>> OFPFW10_NW_DST_SHIFT);
|
||
|
||
if (!(ofpfw & OFPFW10_TP_SRC)) {
|
||
wc->masks.tp_src = OVS_BE16_MAX;
|
||
}
|
||
if (!(ofpfw & OFPFW10_TP_DST)) {
|
||
wc->masks.tp_dst = OVS_BE16_MAX;
|
||
}
|
||
|
||
if (!(ofpfw & OFPFW10_DL_SRC)) {
|
||
WC_MASK_FIELD(wc, dl_src);
|
||
}
|
||
if (!(ofpfw & OFPFW10_DL_DST)) {
|
||
WC_MASK_FIELD(wc, dl_dst);
|
||
}
|
||
if (!(ofpfw & OFPFW10_DL_TYPE)) {
|
||
wc->masks.dl_type = OVS_BE16_MAX;
|
||
}
|
||
|
||
/* VLAN TCI mask. */
|
||
if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
|
||
wc->masks.vlans[0].tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
|
||
}
|
||
if (!(ofpfw & OFPFW10_DL_VLAN)) {
|
||
wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
|
||
}
|
||
}
|
||
|
||
/* Converts the ofp10_match in 'ofmatch' into a struct match in 'match'. */
|
||
void
|
||
ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
|
||
struct match *match)
|
||
{
|
||
uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL;
|
||
|
||
/* Initialize match->wc. */
|
||
memset(&match->flow, 0, sizeof match->flow);
|
||
ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
|
||
memset(&match->tun_md, 0, sizeof match->tun_md);
|
||
|
||
/* If any fields, except in_port, are matched, then we also need to match
|
||
* on the Ethernet packet_type. */
|
||
const uint32_t ofpfw_data_bits = (OFPFW10_NW_TOS | OFPFW10_NW_PROTO
|
||
| OFPFW10_TP_SRC | OFPFW10_TP_DST
|
||
| OFPFW10_DL_SRC | OFPFW10_DL_DST
|
||
| OFPFW10_DL_TYPE
|
||
| OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP);
|
||
if ((ofpfw & ofpfw_data_bits) != ofpfw_data_bits
|
||
|| ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT)
|
||
|| ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT)) {
|
||
match_set_default_packet_type(match);
|
||
}
|
||
|
||
/* Initialize most of match->flow. */
|
||
match->flow.nw_src = ofmatch->nw_src;
|
||
match->flow.nw_dst = ofmatch->nw_dst;
|
||
match->flow.in_port.ofp_port = u16_to_ofp(ntohs(ofmatch->in_port));
|
||
match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type);
|
||
match->flow.tp_src = ofmatch->tp_src;
|
||
match->flow.tp_dst = ofmatch->tp_dst;
|
||
match->flow.dl_src = ofmatch->dl_src;
|
||
match->flow.dl_dst = ofmatch->dl_dst;
|
||
match->flow.nw_tos = ofmatch->nw_tos & IP_DSCP_MASK;
|
||
match->flow.nw_proto = ofmatch->nw_proto;
|
||
|
||
/* Translate VLANs. */
|
||
if (!(ofpfw & OFPFW10_DL_VLAN) &&
|
||
ofmatch->dl_vlan == htons(OFP10_VLAN_NONE)) {
|
||
/* Match only packets without 802.1Q header.
|
||
*
|
||
* When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct.
|
||
*
|
||
* If OFPFW10_DL_VLAN_PCP is matched, the flow match is contradictory,
|
||
* because we can't have a specific PCP without an 802.1Q header.
|
||
* However, older versions of OVS treated this as matching packets
|
||
* withut an 802.1Q header, so we do here too. */
|
||
match->flow.vlans[0].tci = htons(0);
|
||
match->wc.masks.vlans[0].tci = htons(0xffff);
|
||
} else {
|
||
ovs_be16 vid, pcp, tci;
|
||
uint16_t hpcp;
|
||
|
||
vid = ofmatch->dl_vlan & htons(VLAN_VID_MASK);
|
||
hpcp = (ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK;
|
||
pcp = htons(hpcp);
|
||
tci = vid | pcp | htons(VLAN_CFI);
|
||
match->flow.vlans[0].tci = tci & match->wc.masks.vlans[0].tci;
|
||
}
|
||
|
||
/* Clean up. */
|
||
match_zero_wildcarded_fields(match);
|
||
}
|
||
|
||
/* Convert 'match' into the OpenFlow 1.0 match structure 'ofmatch'. */
|
||
void
|
||
ofputil_match_to_ofp10_match(const struct match *match,
|
||
struct ofp10_match *ofmatch)
|
||
{
|
||
const struct flow_wildcards *wc = &match->wc;
|
||
uint32_t ofpfw;
|
||
|
||
/* Figure out most OpenFlow wildcards. */
|
||
ofpfw = 0;
|
||
if (!wc->masks.in_port.ofp_port) {
|
||
ofpfw |= OFPFW10_IN_PORT;
|
||
}
|
||
if (!wc->masks.dl_type) {
|
||
ofpfw |= OFPFW10_DL_TYPE;
|
||
}
|
||
if (!wc->masks.nw_proto) {
|
||
ofpfw |= OFPFW10_NW_PROTO;
|
||
}
|
||
ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_src)
|
||
<< OFPFW10_NW_SRC_SHIFT);
|
||
ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_dst)
|
||
<< OFPFW10_NW_DST_SHIFT);
|
||
if (!(wc->masks.nw_tos & IP_DSCP_MASK)) {
|
||
ofpfw |= OFPFW10_NW_TOS;
|
||
}
|
||
if (!wc->masks.tp_src) {
|
||
ofpfw |= OFPFW10_TP_SRC;
|
||
}
|
||
if (!wc->masks.tp_dst) {
|
||
ofpfw |= OFPFW10_TP_DST;
|
||
}
|
||
if (eth_addr_is_zero(wc->masks.dl_src)) {
|
||
ofpfw |= OFPFW10_DL_SRC;
|
||
}
|
||
if (eth_addr_is_zero(wc->masks.dl_dst)) {
|
||
ofpfw |= OFPFW10_DL_DST;
|
||
}
|
||
|
||
/* Translate VLANs. */
|
||
ofmatch->dl_vlan = htons(0);
|
||
ofmatch->dl_vlan_pcp = 0;
|
||
if (match->wc.masks.vlans[0].tci == htons(0)) {
|
||
ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
|
||
} else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
|
||
&& !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
|
||
ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
|
||
} else {
|
||
if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
|
||
ofpfw |= OFPFW10_DL_VLAN;
|
||
} else {
|
||
ofmatch->dl_vlan =
|
||
htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
|
||
}
|
||
|
||
if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
|
||
ofpfw |= OFPFW10_DL_VLAN_PCP;
|
||
} else {
|
||
ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
|
||
}
|
||
}
|
||
|
||
/* Compose most of the match structure. */
|
||
ofmatch->wildcards = htonl(ofpfw);
|
||
ofmatch->in_port = htons(ofp_to_u16(match->flow.in_port.ofp_port));
|
||
ofmatch->dl_src = match->flow.dl_src;
|
||
ofmatch->dl_dst = match->flow.dl_dst;
|
||
ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
|
||
ofmatch->nw_src = match->flow.nw_src;
|
||
ofmatch->nw_dst = match->flow.nw_dst;
|
||
ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK;
|
||
ofmatch->nw_proto = match->flow.nw_proto;
|
||
ofmatch->tp_src = match->flow.tp_src;
|
||
ofmatch->tp_dst = match->flow.tp_dst;
|
||
memset(ofmatch->pad1, '\0', sizeof ofmatch->pad1);
|
||
memset(ofmatch->pad2, '\0', sizeof ofmatch->pad2);
|
||
}
|
||
|
||
enum ofperr
|
||
ofputil_pull_ofp11_match(struct ofpbuf *buf, const struct tun_table *tun_table,
|
||
const struct vl_mff_map *vl_mff_map,
|
||
struct match *match, uint16_t *padded_match_len)
|
||
{
|
||
struct ofp11_match_header *omh = buf->data;
|
||
uint16_t match_len;
|
||
|
||
if (buf->size < sizeof *omh) {
|
||
return OFPERR_OFPBMC_BAD_LEN;
|
||
}
|
||
|
||
match_len = ntohs(omh->length);
|
||
|
||
switch (ntohs(omh->type)) {
|
||
case OFPMT_STANDARD: {
|
||
struct ofp11_match *om;
|
||
|
||
if (match_len != sizeof *om || buf->size < sizeof *om) {
|
||
return OFPERR_OFPBMC_BAD_LEN;
|
||
}
|
||
om = ofpbuf_pull(buf, sizeof *om);
|
||
if (padded_match_len) {
|
||
*padded_match_len = match_len;
|
||
}
|
||
return ofputil_match_from_ofp11_match(om, match);
|
||
}
|
||
|
||
case OFPMT_OXM:
|
||
if (padded_match_len) {
|
||
*padded_match_len = ROUND_UP(match_len, 8);
|
||
}
|
||
return oxm_pull_match(buf, false, tun_table, vl_mff_map, match);
|
||
|
||
default:
|
||
return OFPERR_OFPBMC_BAD_TYPE;
|
||
}
|
||
}
|
||
|
||
/* Converts the ofp11_match in 'ofmatch' into a struct match in 'match'.
|
||
* Returns 0 if successful, otherwise an OFPERR_* value. */
|
||
enum ofperr
|
||
ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
|
||
struct match *match)
|
||
{
|
||
uint16_t wc = ntohl(ofmatch->wildcards);
|
||
bool ipv4, arp, rarp;
|
||
|
||
match_init_catchall(match);
|
||
match->flow.tunnel.metadata.tab = NULL;
|
||
|
||
if (!(wc & OFPFW11_IN_PORT)) {
|
||
ofp_port_t ofp_port;
|
||
enum ofperr error;
|
||
|
||
error = ofputil_port_from_ofp11(ofmatch->in_port, &ofp_port);
|
||
if (error) {
|
||
return OFPERR_OFPBMC_BAD_VALUE;
|
||
}
|
||
match_set_in_port(match, ofp_port);
|
||
}
|
||
|
||
struct eth_addr dl_src_mask = eth_addr_invert(ofmatch->dl_src_mask);
|
||
struct eth_addr dl_dst_mask = eth_addr_invert(ofmatch->dl_dst_mask);
|
||
if (!eth_addr_is_zero(dl_src_mask) || !eth_addr_is_zero(dl_dst_mask)) {
|
||
match_set_dl_src_masked(match, ofmatch->dl_src, dl_src_mask);
|
||
match_set_dl_dst_masked(match, ofmatch->dl_dst, dl_dst_mask);
|
||
match_set_default_packet_type(match);
|
||
}
|
||
|
||
if (!(wc & OFPFW11_DL_VLAN)) {
|
||
if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
|
||
/* Match only packets without a VLAN tag. */
|
||
match->flow.vlans[0].tci = htons(0);
|
||
match->wc.masks.vlans[0].tci = OVS_BE16_MAX;
|
||
} else {
|
||
if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
|
||
/* Match any packet with a VLAN tag regardless of VID. */
|
||
match->flow.vlans[0].tci = htons(VLAN_CFI);
|
||
match->wc.masks.vlans[0].tci = htons(VLAN_CFI);
|
||
} else if (ntohs(ofmatch->dl_vlan) < 4096) {
|
||
/* Match only packets with the specified VLAN VID. */
|
||
match->flow.vlans[0].tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
|
||
match->wc.masks.vlans[0].tci = htons(VLAN_CFI | VLAN_VID_MASK);
|
||
} else {
|
||
/* Invalid VID. */
|
||
return OFPERR_OFPBMC_BAD_VALUE;
|
||
}
|
||
|
||
if (!(wc & OFPFW11_DL_VLAN_PCP)) {
|
||
if (ofmatch->dl_vlan_pcp <= 7) {
|
||
match->flow.vlans[0].tci |= htons(ofmatch->dl_vlan_pcp
|
||
<< VLAN_PCP_SHIFT);
|
||
match->wc.masks.vlans[0].tci |= htons(VLAN_PCP_MASK);
|
||
} else {
|
||
/* Invalid PCP. */
|
||
return OFPERR_OFPBMC_BAD_VALUE;
|
||
}
|
||
}
|
||
}
|
||
match_set_default_packet_type(match);
|
||
}
|
||
|
||
if (!(wc & OFPFW11_DL_TYPE)) {
|
||
match_set_dl_type(match,
|
||
ofputil_dl_type_from_openflow(ofmatch->dl_type));
|
||
match_set_default_packet_type(match);
|
||
}
|
||
|
||
ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP);
|
||
arp = match->flow.dl_type == htons(ETH_TYPE_ARP);
|
||
rarp = match->flow.dl_type == htons(ETH_TYPE_RARP);
|
||
|
||
if (ipv4 && !(wc & OFPFW11_NW_TOS)) {
|
||
if (ofmatch->nw_tos & ~IP_DSCP_MASK) {
|
||
/* Invalid TOS. */
|
||
return OFPERR_OFPBMC_BAD_VALUE;
|
||
}
|
||
|
||
match_set_nw_dscp(match, ofmatch->nw_tos);
|
||
}
|
||
|
||
if (ipv4 || arp || rarp) {
|
||
if (!(wc & OFPFW11_NW_PROTO)) {
|
||
match_set_nw_proto(match, ofmatch->nw_proto);
|
||
}
|
||
match_set_nw_src_masked(match, ofmatch->nw_src, ~ofmatch->nw_src_mask);
|
||
match_set_nw_dst_masked(match, ofmatch->nw_dst, ~ofmatch->nw_dst_mask);
|
||
}
|
||
|
||
#define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST)
|
||
if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) {
|
||
switch (match->flow.nw_proto) {
|
||
case IPPROTO_ICMP:
|
||
/* "A.2.3 Flow Match Structures" in OF1.1 says:
|
||
*
|
||
* The tp_src and tp_dst fields will be ignored unless the
|
||
* network protocol specified is as TCP, UDP or SCTP.
|
||
*
|
||
* but I'm pretty sure we should support ICMP too, otherwise
|
||
* that's a regression from OF1.0. */
|
||
if (!(wc & OFPFW11_TP_SRC)) {
|
||
uint16_t icmp_type = ntohs(ofmatch->tp_src);
|
||
if (icmp_type < 0x100) {
|
||
match_set_icmp_type(match, icmp_type);
|
||
} else {
|
||
return OFPERR_OFPBMC_BAD_FIELD;
|
||
}
|
||
}
|
||
if (!(wc & OFPFW11_TP_DST)) {
|
||
uint16_t icmp_code = ntohs(ofmatch->tp_dst);
|
||
if (icmp_code < 0x100) {
|
||
match_set_icmp_code(match, icmp_code);
|
||
} else {
|
||
return OFPERR_OFPBMC_BAD_FIELD;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case IPPROTO_TCP:
|
||
case IPPROTO_UDP:
|
||
case IPPROTO_SCTP:
|
||
if (!(wc & (OFPFW11_TP_SRC))) {
|
||
match_set_tp_src(match, ofmatch->tp_src);
|
||
}
|
||
if (!(wc & (OFPFW11_TP_DST))) {
|
||
match_set_tp_dst(match, ofmatch->tp_dst);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
/* OF1.1 says explicitly to ignore this. */
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (eth_type_mpls(match->flow.dl_type)) {
|
||
if (!(wc & OFPFW11_MPLS_LABEL)) {
|
||
match_set_mpls_label(match, 0, ofmatch->mpls_label);
|
||
}
|
||
if (!(wc & OFPFW11_MPLS_TC)) {
|
||
match_set_mpls_tc(match, 0, ofmatch->mpls_tc);
|
||
}
|
||
}
|
||
|
||
match_set_metadata_masked(match, ofmatch->metadata,
|
||
~ofmatch->metadata_mask);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Convert 'match' into the OpenFlow 1.1 match structure 'ofmatch'. */
|
||
void
|
||
ofputil_match_to_ofp11_match(const struct match *match,
|
||
struct ofp11_match *ofmatch)
|
||
{
|
||
uint32_t wc = 0;
|
||
|
||
memset(ofmatch, 0, sizeof *ofmatch);
|
||
ofmatch->omh.type = htons(OFPMT_STANDARD);
|
||
ofmatch->omh.length = htons(OFPMT11_STANDARD_LENGTH);
|
||
|
||
if (!match->wc.masks.in_port.ofp_port) {
|
||
wc |= OFPFW11_IN_PORT;
|
||
} else {
|
||
ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port.ofp_port);
|
||
}
|
||
|
||
ofmatch->dl_src = match->flow.dl_src;
|
||
ofmatch->dl_src_mask = eth_addr_invert(match->wc.masks.dl_src);
|
||
ofmatch->dl_dst = match->flow.dl_dst;
|
||
ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst);
|
||
|
||
if (match->wc.masks.vlans[0].tci == htons(0)) {
|
||
wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
|
||
} else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
|
||
&& !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
|
||
ofmatch->dl_vlan = htons(OFPVID11_NONE);
|
||
wc |= OFPFW11_DL_VLAN_PCP;
|
||
} else {
|
||
if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
|
||
ofmatch->dl_vlan = htons(OFPVID11_ANY);
|
||
} else {
|
||
ofmatch->dl_vlan =
|
||
htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
|
||
}
|
||
|
||
if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
|
||
wc |= OFPFW11_DL_VLAN_PCP;
|
||
} else {
|
||
ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
|
||
}
|
||
}
|
||
|
||
if (!match->wc.masks.dl_type) {
|
||
wc |= OFPFW11_DL_TYPE;
|
||
} else {
|
||
ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type);
|
||
}
|
||
|
||
if (!(match->wc.masks.nw_tos & IP_DSCP_MASK)) {
|
||
wc |= OFPFW11_NW_TOS;
|
||
} else {
|
||
ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK;
|
||
}
|
||
|
||
if (!match->wc.masks.nw_proto) {
|
||
wc |= OFPFW11_NW_PROTO;
|
||
} else {
|
||
ofmatch->nw_proto = match->flow.nw_proto;
|
||
}
|
||
|
||
ofmatch->nw_src = match->flow.nw_src;
|
||
ofmatch->nw_src_mask = ~match->wc.masks.nw_src;
|
||
ofmatch->nw_dst = match->flow.nw_dst;
|
||
ofmatch->nw_dst_mask = ~match->wc.masks.nw_dst;
|
||
|
||
if (!match->wc.masks.tp_src) {
|
||
wc |= OFPFW11_TP_SRC;
|
||
} else {
|
||
ofmatch->tp_src = match->flow.tp_src;
|
||
}
|
||
|
||
if (!match->wc.masks.tp_dst) {
|
||
wc |= OFPFW11_TP_DST;
|
||
} else {
|
||
ofmatch->tp_dst = match->flow.tp_dst;
|
||
}
|
||
|
||
if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK))) {
|
||
wc |= OFPFW11_MPLS_LABEL;
|
||
} else {
|
||
ofmatch->mpls_label = htonl(mpls_lse_to_label(
|
||
match->flow.mpls_lse[0]));
|
||
}
|
||
|
||
if (!(match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK))) {
|
||
wc |= OFPFW11_MPLS_TC;
|
||
} else {
|
||
ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse[0]);
|
||
}
|
||
|
||
ofmatch->metadata = match->flow.metadata;
|
||
ofmatch->metadata_mask = ~match->wc.masks.metadata;
|
||
|
||
ofmatch->wildcards = htonl(wc);
|
||
}
|
||
|
||
/* Returns the "typical" length of a match for 'protocol', for use in
|
||
* estimating space to preallocate. */
|
||
int
|
||
ofputil_match_typical_len(enum ofputil_protocol protocol)
|
||
{
|
||
switch (protocol) {
|
||
case OFPUTIL_P_OF10_STD:
|
||
case OFPUTIL_P_OF10_STD_TID:
|
||
return sizeof(struct ofp10_match);
|
||
|
||
case OFPUTIL_P_OF10_NXM:
|
||
case OFPUTIL_P_OF10_NXM_TID:
|
||
return NXM_TYPICAL_LEN;
|
||
|
||
case OFPUTIL_P_OF11_STD:
|
||
return sizeof(struct ofp11_match);
|
||
|
||
case OFPUTIL_P_OF12_OXM:
|
||
case OFPUTIL_P_OF13_OXM:
|
||
case OFPUTIL_P_OF14_OXM:
|
||
case OFPUTIL_P_OF15_OXM:
|
||
return NXM_TYPICAL_LEN;
|
||
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|
||
/* Appends to 'b' an struct ofp11_match_header followed by a match that
|
||
* expresses 'match' properly for 'protocol', plus enough zero bytes to pad the
|
||
* data appended out to a multiple of 8. 'protocol' must be one that is usable
|
||
* in OpenFlow 1.1 or later.
|
||
*
|
||
* This function can cause 'b''s data to be reallocated.
|
||
*
|
||
* Returns the number of bytes appended to 'b', excluding the padding. Never
|
||
* returns zero. */
|
||
int
|
||
ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match,
|
||
enum ofputil_protocol protocol)
|
||
{
|
||
switch (protocol) {
|
||
case OFPUTIL_P_OF10_STD:
|
||
case OFPUTIL_P_OF10_STD_TID:
|
||
case OFPUTIL_P_OF10_NXM:
|
||
case OFPUTIL_P_OF10_NXM_TID:
|
||
OVS_NOT_REACHED();
|
||
|
||
case OFPUTIL_P_OF11_STD: {
|
||
struct ofp11_match *om;
|
||
|
||
/* Make sure that no padding is needed. */
|
||
BUILD_ASSERT_DECL(sizeof *om % 8 == 0);
|
||
|
||
om = ofpbuf_put_uninit(b, sizeof *om);
|
||
ofputil_match_to_ofp11_match(match, om);
|
||
return sizeof *om;
|
||
}
|
||
|
||
case OFPUTIL_P_OF12_OXM:
|
||
case OFPUTIL_P_OF13_OXM:
|
||
case OFPUTIL_P_OF14_OXM:
|
||
case OFPUTIL_P_OF15_OXM:
|
||
return oxm_put_match(b, match,
|
||
ofputil_protocol_to_ofp_version(protocol));
|
||
}
|
||
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
/* Given a 'dl_type' value in the format used in struct flow, returns the
|
||
* corresponding 'dl_type' value for use in an ofp10_match or ofp11_match
|
||
* structure. */
|
||
ovs_be16
|
||
ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
|
||
{
|
||
return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
|
||
? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
|
||
: flow_dl_type);
|
||
}
|
||
|
||
/* Given a 'dl_type' value in the format used in an ofp10_match or ofp11_match
|
||
* structure, returns the corresponding 'dl_type' value for use in struct
|
||
* flow. */
|
||
ovs_be16
|
||
ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
|
||
{
|
||
return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
|
||
? htons(FLOW_DL_TYPE_NONE)
|
||
: ofp_dl_type);
|
||
}
|
||
|
||
static void
|
||
encode_tlv_table_mappings(struct ofpbuf *b, struct ovs_list *mappings)
|
||
{
|
||
struct ofputil_tlv_map *map;
|
||
|
||
LIST_FOR_EACH (map, list_node, mappings) {
|
||
struct nx_tlv_map *nx_map;
|
||
|
||
nx_map = ofpbuf_put_zeros(b, sizeof *nx_map);
|
||
nx_map->option_class = htons(map->option_class);
|
||
nx_map->option_type = map->option_type;
|
||
nx_map->option_len = map->option_len;
|
||
nx_map->index = htons(map->index);
|
||
}
|
||
}
|
||
|
||
struct ofpbuf *
|
||
ofputil_encode_tlv_table_mod(enum ofp_version ofp_version,
|
||
struct ofputil_tlv_table_mod *ttm)
|
||
{
|
||
struct ofpbuf *b;
|
||
struct nx_tlv_table_mod *nx_ttm;
|
||
|
||
b = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_MOD, ofp_version, 0);
|
||
nx_ttm = ofpbuf_put_zeros(b, sizeof *nx_ttm);
|
||
nx_ttm->command = htons(ttm->command);
|
||
encode_tlv_table_mappings(b, &ttm->mappings);
|
||
|
||
return b;
|
||
}
|
||
|
||
static enum ofperr
|
||
decode_tlv_table_mappings(struct ofpbuf *msg, unsigned int max_fields,
|
||
struct ovs_list *mappings)
|
||
{
|
||
ovs_list_init(mappings);
|
||
|
||
while (msg->size) {
|
||
struct nx_tlv_map *nx_map;
|
||
struct ofputil_tlv_map *map;
|
||
|
||
nx_map = ofpbuf_pull(msg, sizeof *nx_map);
|
||
map = xmalloc(sizeof *map);
|
||
ovs_list_push_back(mappings, &map->list_node);
|
||
|
||
map->option_class = ntohs(nx_map->option_class);
|
||
map->option_type = nx_map->option_type;
|
||
|
||
map->option_len = nx_map->option_len;
|
||
if (map->option_len % 4 || map->option_len > TLV_MAX_OPT_SIZE) {
|
||
VLOG_WARN_RL(&rl, "tlv table option length (%u) is not a "
|
||
"valid option size", map->option_len);
|
||
ofputil_uninit_tlv_table(mappings);
|
||
return OFPERR_NXTTMFC_BAD_OPT_LEN;
|
||
}
|
||
|
||
map->index = ntohs(nx_map->index);
|
||
if (map->index >= max_fields) {
|
||
VLOG_WARN_RL(&rl, "tlv table field index (%u) is too large "
|
||
"(max %u)", map->index, max_fields - 1);
|
||
ofputil_uninit_tlv_table(mappings);
|
||
return OFPERR_NXTTMFC_BAD_FIELD_IDX;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
enum ofperr
|
||
ofputil_decode_tlv_table_mod(const struct ofp_header *oh,
|
||
struct ofputil_tlv_table_mod *ttm)
|
||
{
|
||
struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
||
ofpraw_pull_assert(&msg);
|
||
|
||
struct nx_tlv_table_mod *nx_ttm = ofpbuf_pull(&msg, sizeof *nx_ttm);
|
||
ttm->command = ntohs(nx_ttm->command);
|
||
if (ttm->command > NXTTMC_CLEAR) {
|
||
VLOG_WARN_RL(&rl, "tlv table mod command (%u) is out of range",
|
||
ttm->command);
|
||
return OFPERR_NXTTMFC_BAD_COMMAND;
|
||
}
|
||
|
||
return decode_tlv_table_mappings(&msg, TUN_METADATA_NUM_OPTS,
|
||
&ttm->mappings);
|
||
}
|
||
|
||
static void
|
||
print_tlv_table(struct ds *s, const struct ovs_list *mappings)
|
||
{
|
||
struct ofputil_tlv_map *map;
|
||
|
||
ds_put_cstr(s, " mapping table:\n");
|
||
ds_put_cstr(s, " class type length match field\n");
|
||
ds_put_cstr(s, " ------ ---- ------ --------------");
|
||
|
||
LIST_FOR_EACH (map, list_node, mappings) {
|
||
ds_put_format(s, "\n %#6"PRIx16" %#4"PRIx8" %6"PRIu8" "
|
||
"tun_metadata%"PRIu16,
|
||
map->option_class, map->option_type, map->option_len,
|
||
map->index);
|
||
}
|
||
}
|
||
|
||
void
|
||
ofputil_format_tlv_table_mod(struct ds *s,
|
||
const struct ofputil_tlv_table_mod *ttm)
|
||
{
|
||
ds_put_cstr(s, "\n ");
|
||
|
||
switch (ttm->command) {
|
||
case NXTTMC_ADD:
|
||
ds_put_cstr(s, "ADD");
|
||
break;
|
||
case NXTTMC_DELETE:
|
||
ds_put_cstr(s, "DEL");
|
||
break;
|
||
case NXTTMC_CLEAR:
|
||
ds_put_cstr(s, "CLEAR");
|
||
break;
|
||
}
|
||
|
||
if (ttm->command != NXTTMC_CLEAR) {
|
||
print_tlv_table(s, &ttm->mappings);
|
||
}
|
||
}
|
||
|
||
struct ofpbuf *
|
||
ofputil_encode_tlv_table_reply(const struct ofp_header *oh,
|
||
struct ofputil_tlv_table_reply *ttr)
|
||
{
|
||
struct ofpbuf *b;
|
||
struct nx_tlv_table_reply *nx_ttr;
|
||
|
||
b = ofpraw_alloc_reply(OFPRAW_NXT_TLV_TABLE_REPLY, oh, 0);
|
||
nx_ttr = ofpbuf_put_zeros(b, sizeof *nx_ttr);
|
||
nx_ttr->max_option_space = htonl(ttr->max_option_space);
|
||
nx_ttr->max_fields = htons(ttr->max_fields);
|
||
|
||
encode_tlv_table_mappings(b, &ttr->mappings);
|
||
|
||
return b;
|
||
}
|
||
|
||
/* Decodes the NXT_TLV_TABLE_REPLY message in 'oh' into '*ttr'. Returns 0
|
||
* if successful, otherwise an ofperr.
|
||
*
|
||
* The decoder verifies that the indexes in 'ttr->mappings' are less than
|
||
* 'ttr->max_fields', but the caller must ensure, if necessary, that they are
|
||
* less than TUN_METADATA_NUM_OPTS. */
|
||
enum ofperr
|
||
ofputil_decode_tlv_table_reply(const struct ofp_header *oh,
|
||
struct ofputil_tlv_table_reply *ttr)
|
||
{
|
||
struct ofpbuf msg = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
||
ofpraw_pull_assert(&msg);
|
||
|
||
struct nx_tlv_table_reply *nx_ttr = ofpbuf_pull(&msg, sizeof *nx_ttr);
|
||
ttr->max_option_space = ntohl(nx_ttr->max_option_space);
|
||
ttr->max_fields = ntohs(nx_ttr->max_fields);
|
||
|
||
return decode_tlv_table_mappings(&msg, ttr->max_fields, &ttr->mappings);
|
||
}
|
||
|
||
char * OVS_WARN_UNUSED_RESULT
|
||
parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm,
|
||
uint16_t command, const char *s,
|
||
enum ofputil_protocol *usable_protocols)
|
||
{
|
||
*usable_protocols = OFPUTIL_P_NXM_OXM_ANY;
|
||
|
||
ttm->command = command;
|
||
ovs_list_init(&ttm->mappings);
|
||
|
||
while (*s) {
|
||
struct ofputil_tlv_map *map = xmalloc(sizeof *map);
|
||
int n;
|
||
|
||
if (*s == ',') {
|
||
s++;
|
||
}
|
||
|
||
ovs_list_push_back(&ttm->mappings, &map->list_node);
|
||
|
||
if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}"
|
||
"->tun_metadata%"SCNi16"%n",
|
||
&map->option_class, &map->option_type, &map->option_len,
|
||
&map->index, &n)) {
|
||
ofputil_uninit_tlv_table(&ttm->mappings);
|
||
return xstrdup("invalid tlv mapping");
|
||
}
|
||
|
||
s += n;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
void
|
||
ofputil_format_tlv_table_reply(struct ds *s,
|
||
const struct ofputil_tlv_table_reply *ttr)
|
||
{
|
||
ds_put_char(s, '\n');
|
||
|
||
const struct ofputil_tlv_map *map;
|
||
int allocated_space = 0;
|
||
LIST_FOR_EACH (map, list_node, &ttr->mappings) {
|
||
allocated_space += map->option_len;
|
||
}
|
||
|
||
ds_put_format(s, " max option space=%"PRIu32" max fields=%"PRIu16"\n",
|
||
ttr->max_option_space, ttr->max_fields);
|
||
ds_put_format(s, " allocated option space=%d\n", allocated_space);
|
||
ds_put_char(s, '\n');
|
||
print_tlv_table(s, &ttr->mappings);
|
||
}
|
||
|
||
void
|
||
ofputil_uninit_tlv_table(struct ovs_list *mappings)
|
||
{
|
||
struct ofputil_tlv_map *map;
|
||
|
||
LIST_FOR_EACH_POP (map, list_node, mappings) {
|
||
free(map);
|
||
}
|
||
}
|
||
|
||
static void
|
||
ofputil_normalize_match__(struct match *match, bool may_log)
|
||
{
|
||
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_IPVx = 1 << 3, /* tos, frag, ttl */
|
||
MAY_ARP_SHA = 1 << 4, /* arp_sha */
|
||
MAY_ARP_THA = 1 << 5, /* arp_tha */
|
||
MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
|
||
MAY_ND_TARGET = 1 << 7, /* nd_target */
|
||
MAY_MPLS = 1 << 8, /* mpls label and tc */
|
||
MAY_ETHER = 1 << 9, /* dl_src, dl_dst */
|
||
} may_match;
|
||
|
||
struct flow_wildcards wc = match->wc;
|
||
ovs_be16 dl_type;
|
||
|
||
/* Figure out what fields may be matched. */
|
||
/* Check the packet_type first and extract dl_type. */
|
||
if (wc.masks.packet_type == 0 || match_has_default_packet_type(match)) {
|
||
may_match = MAY_ETHER;
|
||
dl_type = match->flow.dl_type;
|
||
} else if (wc.masks.packet_type == OVS_BE32_MAX &&
|
||
pt_ns(match->flow.packet_type) == OFPHTN_ETHERTYPE) {
|
||
may_match = 0;
|
||
dl_type = pt_ns_type_be(match->flow.packet_type);
|
||
} else {
|
||
may_match = 0;
|
||
dl_type = 0;
|
||
}
|
||
if (dl_type == htons(ETH_TYPE_IP)) {
|
||
may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
|
||
if (match->flow.nw_proto == IPPROTO_TCP ||
|
||
match->flow.nw_proto == IPPROTO_UDP ||
|
||
match->flow.nw_proto == IPPROTO_SCTP ||
|
||
match->flow.nw_proto == IPPROTO_ICMP) {
|
||
may_match |= MAY_TP_ADDR;
|
||
}
|
||
} else if (dl_type == htons(ETH_TYPE_IPV6)) {
|
||
may_match |= MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
|
||
if (match->flow.nw_proto == IPPROTO_TCP ||
|
||
match->flow.nw_proto == IPPROTO_UDP ||
|
||
match->flow.nw_proto == IPPROTO_SCTP) {
|
||
may_match |= MAY_TP_ADDR;
|
||
} else if (match->flow.nw_proto == IPPROTO_ICMPV6) {
|
||
may_match |= MAY_TP_ADDR;
|
||
if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
|
||
may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
|
||
} else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
|
||
may_match |= MAY_ND_TARGET | MAY_ARP_THA;
|
||
}
|
||
}
|
||
} else if (dl_type == htons(ETH_TYPE_ARP) ||
|
||
dl_type == htons(ETH_TYPE_RARP)) {
|
||
may_match |= MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
|
||
} else if (eth_type_mpls(dl_type)) {
|
||
may_match |= MAY_MPLS;
|
||
}
|
||
|
||
/* Clear the fields that may not be matched. */
|
||
if (!(may_match & MAY_ETHER)) {
|
||
wc.masks.dl_src = wc.masks.dl_dst = eth_addr_zero;
|
||
}
|
||
if (!(may_match & MAY_NW_ADDR)) {
|
||
wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
|
||
}
|
||
if (!(may_match & MAY_TP_ADDR)) {
|
||
wc.masks.tp_src = wc.masks.tp_dst = htons(0);
|
||
}
|
||
if (!(may_match & MAY_NW_PROTO)) {
|
||
wc.masks.nw_proto = 0;
|
||
}
|
||
if (!(may_match & MAY_IPVx)) {
|
||
wc.masks.nw_tos = 0;
|
||
wc.masks.nw_ttl = 0;
|
||
}
|
||
if (!(may_match & MAY_ARP_SHA)) {
|
||
WC_UNMASK_FIELD(&wc, arp_sha);
|
||
}
|
||
if (!(may_match & MAY_ARP_THA)) {
|
||
WC_UNMASK_FIELD(&wc, arp_tha);
|
||
}
|
||
if (!(may_match & MAY_IPV6)) {
|
||
wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any;
|
||
wc.masks.ipv6_label = htonl(0);
|
||
}
|
||
if (!(may_match & MAY_ND_TARGET)) {
|
||
wc.masks.nd_target = in6addr_any;
|
||
}
|
||
if (!(may_match & MAY_MPLS)) {
|
||
memset(wc.masks.mpls_lse, 0, sizeof wc.masks.mpls_lse);
|
||
}
|
||
|
||
/* Log any changes. */
|
||
if (!flow_wildcards_equal(&wc, &match->wc)) {
|
||
bool log = may_log && !VLOG_DROP_INFO(&rl);
|
||
char *pre = (log
|
||
? match_to_string(match, NULL, OFP_DEFAULT_PRIORITY)
|
||
: NULL);
|
||
|
||
match->wc = wc;
|
||
match_zero_wildcarded_fields(match);
|
||
|
||
if (log) {
|
||
char *post = match_to_string(match, NULL, OFP_DEFAULT_PRIORITY);
|
||
VLOG_INFO("normalization changed ofp_match, details:");
|
||
VLOG_INFO(" pre: %s", pre);
|
||
VLOG_INFO("post: %s", post);
|
||
free(pre);
|
||
free(post);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* "Normalizes" the wildcards in 'match'. That means:
|
||
*
|
||
* 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 'match' 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 'match' 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 'match' specifies an
|
||
* SCTP flow.
|
||
*
|
||
* If this function changes 'match', it logs a rate-limited informational
|
||
* message. */
|
||
void
|
||
ofputil_normalize_match(struct match *match)
|
||
{
|
||
ofputil_normalize_match__(match, true);
|
||
}
|
||
|
||
/* Same as ofputil_normalize_match() without the logging. Thus, this function
|
||
* is suitable for a program's internal use, whereas ofputil_normalize_match()
|
||
* sense for use on flows received from elsewhere (so that a bug in the program
|
||
* that sent them can be reported and corrected). */
|
||
void
|
||
ofputil_normalize_match_quiet(struct match *match)
|
||
{
|
||
ofputil_normalize_match__(match, false);
|
||
}
|
||
|
||
static void OVS_PRINTF_FORMAT(5, 6)
|
||
print_wild(struct ds *string, const char *leader, int is_wild,
|
||
int verbosity, const char *format, ...)
|
||
{
|
||
if (is_wild && verbosity < 2) {
|
||
return;
|
||
}
|
||
ds_put_cstr(string, leader);
|
||
if (!is_wild) {
|
||
va_list args;
|
||
|
||
va_start(args, format);
|
||
ds_put_format_valist(string, format, args);
|
||
va_end(args);
|
||
} else {
|
||
ds_put_char(string, '*');
|
||
}
|
||
ds_put_char(string, ',');
|
||
}
|
||
|
||
static void
|
||
print_wild_port(struct ds *string, const char *leader, int is_wild,
|
||
int verbosity, ofp_port_t port,
|
||
const struct ofputil_port_map *port_map)
|
||
{
|
||
if (is_wild && verbosity < 2) {
|
||
return;
|
||
}
|
||
ds_put_cstr(string, leader);
|
||
if (!is_wild) {
|
||
ofputil_format_port(port, port_map, string);
|
||
} else {
|
||
ds_put_char(string, '*');
|
||
}
|
||
ds_put_char(string, ',');
|
||
}
|
||
|
||
static void
|
||
print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
|
||
uint32_t wild_bits, int verbosity)
|
||
{
|
||
if (wild_bits >= 32 && verbosity < 2) {
|
||
return;
|
||
}
|
||
ds_put_cstr(string, leader);
|
||
if (wild_bits < 32) {
|
||
ds_put_format(string, IP_FMT, IP_ARGS(ip));
|
||
if (wild_bits) {
|
||
ds_put_format(string, "/%d", 32 - wild_bits);
|
||
}
|
||
} else {
|
||
ds_put_char(string, '*');
|
||
}
|
||
ds_put_char(string, ',');
|
||
}
|
||
|
||
void
|
||
ofp10_match_print(struct ds *f, const struct ofp10_match *om,
|
||
const struct ofputil_port_map *port_map, int verbosity)
|
||
{
|
||
char *s = ofp10_match_to_string(om, port_map, verbosity);
|
||
ds_put_cstr(f, s);
|
||
free(s);
|
||
}
|
||
|
||
char *
|
||
ofp10_match_to_string(const struct ofp10_match *om,
|
||
const struct ofputil_port_map *port_map, int verbosity)
|
||
{
|
||
struct ds f = DS_EMPTY_INITIALIZER;
|
||
uint32_t w = ntohl(om->wildcards);
|
||
bool skip_type = false;
|
||
bool skip_proto = false;
|
||
|
||
if (!(w & OFPFW10_DL_TYPE)) {
|
||
skip_type = true;
|
||
if (om->dl_type == htons(ETH_TYPE_IP)) {
|
||
if (!(w & OFPFW10_NW_PROTO)) {
|
||
skip_proto = true;
|
||
if (om->nw_proto == IPPROTO_ICMP) {
|
||
ds_put_cstr(&f, "icmp,");
|
||
} else if (om->nw_proto == IPPROTO_TCP) {
|
||
ds_put_cstr(&f, "tcp,");
|
||
} else if (om->nw_proto == IPPROTO_UDP) {
|
||
ds_put_cstr(&f, "udp,");
|
||
} else if (om->nw_proto == IPPROTO_SCTP) {
|
||
ds_put_cstr(&f, "sctp,");
|
||
} else {
|
||
ds_put_cstr(&f, "ip,");
|
||
skip_proto = false;
|
||
}
|
||
} else {
|
||
ds_put_cstr(&f, "ip,");
|
||
}
|
||
} else if (om->dl_type == htons(ETH_TYPE_ARP)) {
|
||
ds_put_cstr(&f, "arp,");
|
||
} else if (om->dl_type == htons(ETH_TYPE_RARP)){
|
||
ds_put_cstr(&f, "rarp,");
|
||
} else if (om->dl_type == htons(ETH_TYPE_MPLS)) {
|
||
ds_put_cstr(&f, "mpls,");
|
||
} else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
|
||
ds_put_cstr(&f, "mplsm,");
|
||
} else {
|
||
skip_type = false;
|
||
}
|
||
}
|
||
print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
|
||
u16_to_ofp(ntohs(om->in_port)), port_map);
|
||
print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
|
||
"%d", ntohs(om->dl_vlan));
|
||
print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
|
||
"%d", om->dl_vlan_pcp);
|
||
print_wild(&f, "dl_src=", w & OFPFW10_DL_SRC, verbosity,
|
||
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src));
|
||
print_wild(&f, "dl_dst=", w & OFPFW10_DL_DST, verbosity,
|
||
ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst));
|
||
if (!skip_type) {
|
||
print_wild(&f, "dl_type=", w & OFPFW10_DL_TYPE, verbosity,
|
||
"0x%04x", ntohs(om->dl_type));
|
||
}
|
||
print_ip_netmask(&f, "nw_src=", om->nw_src,
|
||
(w & OFPFW10_NW_SRC_MASK) >> OFPFW10_NW_SRC_SHIFT,
|
||
verbosity);
|
||
print_ip_netmask(&f, "nw_dst=", om->nw_dst,
|
||
(w & OFPFW10_NW_DST_MASK) >> OFPFW10_NW_DST_SHIFT,
|
||
verbosity);
|
||
if (!skip_proto) {
|
||
if (om->dl_type == htons(ETH_TYPE_ARP) ||
|
||
om->dl_type == htons(ETH_TYPE_RARP)) {
|
||
print_wild(&f, "arp_op=", w & OFPFW10_NW_PROTO, verbosity,
|
||
"%u", om->nw_proto);
|
||
} else {
|
||
print_wild(&f, "nw_proto=", w & OFPFW10_NW_PROTO, verbosity,
|
||
"%u", om->nw_proto);
|
||
}
|
||
}
|
||
print_wild(&f, "nw_tos=", w & OFPFW10_NW_TOS, verbosity,
|
||
"%u", om->nw_tos);
|
||
if (om->nw_proto == IPPROTO_ICMP) {
|
||
print_wild(&f, "icmp_type=", w & OFPFW10_ICMP_TYPE, verbosity,
|
||
"%d", ntohs(om->tp_src));
|
||
print_wild(&f, "icmp_code=", w & OFPFW10_ICMP_CODE, verbosity,
|
||
"%d", ntohs(om->tp_dst));
|
||
} else {
|
||
print_wild(&f, "tp_src=", w & OFPFW10_TP_SRC, verbosity,
|
||
"%d", ntohs(om->tp_src));
|
||
print_wild(&f, "tp_dst=", w & OFPFW10_TP_DST, verbosity,
|
||
"%d", ntohs(om->tp_dst));
|
||
}
|
||
ds_chomp(&f, ',');
|
||
return ds_cstr(&f);
|
||
}
|
||
|