2017-06-13 18:03:24 +03:00
|
|
|
/*
|
2017-06-19 14:33:23 -07:00
|
|
|
* Copyright (c) 2009-2017 Nicira, Inc.
|
2017-06-13 18:03:27 +03:00
|
|
|
* Copyright (c) 2016 Mellanox Technologies, Ltd.
|
2017-06-13 18:03:24 +03:00
|
|
|
*
|
|
|
|
* 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 "tc.h"
|
2017-06-19 14:33:23 -07:00
|
|
|
|
2017-06-13 18:03:24 +03:00
|
|
|
#include <errno.h>
|
2017-06-13 18:03:27 +03:00
|
|
|
#include <linux/if_ether.h>
|
2019-04-09 15:36:12 +01:00
|
|
|
#include <linux/if_packet.h>
|
2017-06-13 18:03:27 +03:00
|
|
|
#include <linux/rtnetlink.h>
|
2017-09-18 07:16:03 +03:00
|
|
|
#include <linux/tc_act/tc_csum.h>
|
2017-06-13 18:03:27 +03:00
|
|
|
#include <linux/tc_act/tc_gact.h>
|
|
|
|
#include <linux/tc_act/tc_mirred.h>
|
2019-07-30 12:05:15 +01:00
|
|
|
#include <linux/tc_act/tc_mpls.h>
|
2017-09-18 07:16:03 +03:00
|
|
|
#include <linux/tc_act/tc_pedit.h>
|
2019-04-09 15:36:12 +01:00
|
|
|
#include <linux/tc_act/tc_skbedit.h>
|
2017-06-13 18:03:27 +03:00
|
|
|
#include <linux/tc_act/tc_tunnel_key.h>
|
|
|
|
#include <linux/tc_act/tc_vlan.h>
|
2019-12-22 12:16:41 +02:00
|
|
|
#include <linux/tc_act/tc_ct.h>
|
2017-06-13 18:03:27 +03:00
|
|
|
#include <linux/gen_stats.h>
|
|
|
|
#include <net/if.h>
|
2017-07-30 08:01:52 +03:00
|
|
|
#include <unistd.h>
|
2017-06-19 14:33:23 -07:00
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
#include "byte-order.h"
|
2023-06-06 20:33:35 +02:00
|
|
|
#include "coverage.h"
|
2017-06-13 18:03:24 +03:00
|
|
|
#include "netlink-socket.h"
|
|
|
|
#include "netlink.h"
|
2023-06-27 13:48:10 +03:00
|
|
|
#include "odp-util.h"
|
2017-06-13 18:03:24 +03:00
|
|
|
#include "openvswitch/ofpbuf.h"
|
2017-09-18 07:16:03 +03:00
|
|
|
#include "openvswitch/util.h"
|
2017-06-13 18:03:24 +03:00
|
|
|
#include "openvswitch/vlog.h"
|
2017-06-13 18:03:27 +03:00
|
|
|
#include "packets.h"
|
|
|
|
#include "timeval.h"
|
2017-06-19 14:33:23 -07:00
|
|
|
#include "unaligned.h"
|
2017-06-13 18:03:24 +03:00
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
#define MAX_PEDIT_OFFSETS 32
|
|
|
|
|
2018-06-28 17:03:02 +01:00
|
|
|
#ifndef TCM_IFINDEX_MAGIC_BLOCK
|
|
|
|
#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
|
|
|
|
#endif
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
#ifndef TCA_DUMP_FLAGS_TERSE
|
|
|
|
#define TCA_DUMP_FLAGS_TERSE (1 << 0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TCA_MAX < 15
|
2019-12-22 12:16:40 +02:00
|
|
|
#define TCA_CHAIN 11
|
2018-06-28 17:03:02 +01:00
|
|
|
#define TCA_INGRESS_BLOCK 13
|
2020-06-04 13:47:01 +03:00
|
|
|
#define TCA_DUMP_FLAGS 15
|
2018-06-28 17:03:02 +01:00
|
|
|
#endif
|
|
|
|
|
2021-01-26 09:17:46 +02:00
|
|
|
#ifndef RTM_GETCHAIN
|
|
|
|
#define RTM_GETCHAIN 102
|
|
|
|
#endif
|
|
|
|
|
2017-06-13 18:03:24 +03:00
|
|
|
VLOG_DEFINE_THIS_MODULE(tc);
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
COVERAGE_DEFINE(tc_netlink_malformed_reply);
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
|
|
|
|
|
2017-06-13 18:03:30 +03:00
|
|
|
static enum tc_offload_policy tc_policy = TC_POLICY_NONE;
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
struct tc_pedit_key_ex {
|
|
|
|
enum pedit_header_type htype;
|
|
|
|
enum pedit_cmd cmd;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct flower_key_to_pedit {
|
|
|
|
enum pedit_header_type htype;
|
|
|
|
int offset;
|
2017-11-21 14:40:41 +02:00
|
|
|
int flower_offset;
|
2017-09-18 07:16:03 +03:00
|
|
|
int size;
|
2019-01-28 12:29:10 +00:00
|
|
|
int boundary_shift;
|
2017-09-18 07:16:03 +03:00
|
|
|
};
|
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
struct tc_flow_stats {
|
|
|
|
uint64_t n_packets;
|
|
|
|
uint64_t n_bytes;
|
|
|
|
};
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static struct flower_key_to_pedit flower_pedit_map[] = {
|
|
|
|
{
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
|
|
|
|
12,
|
|
|
|
offsetof(struct tc_flower_key, ipv4.ipv4_src),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_src),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
|
|
|
|
16,
|
|
|
|
offsetof(struct tc_flower_key, ipv4.ipv4_dst),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_dst),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
|
|
|
|
8,
|
|
|
|
offsetof(struct tc_flower_key, ipv4.rewrite_ttl),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_ttl),
|
|
|
|
0
|
2019-01-28 12:29:09 +00:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP4,
|
|
|
|
1,
|
|
|
|
offsetof(struct tc_flower_key, ipv4.rewrite_tos),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_tos),
|
|
|
|
0
|
2018-12-26 09:36:22 +02:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP6,
|
|
|
|
7,
|
|
|
|
offsetof(struct tc_flower_key, ipv6.rewrite_hlimit),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_hlimit),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP6,
|
|
|
|
8,
|
|
|
|
offsetof(struct tc_flower_key, ipv6.ipv6_src),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_src),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP6,
|
|
|
|
24,
|
|
|
|
offsetof(struct tc_flower_key, ipv6.ipv6_dst),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_dst),
|
|
|
|
0
|
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_IP6,
|
|
|
|
0,
|
|
|
|
offsetof(struct tc_flower_key, ipv6.rewrite_tclass),
|
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_tclass),
|
|
|
|
4
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
|
|
|
|
6,
|
|
|
|
offsetof(struct tc_flower_key, src_mac),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, src_mac),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
|
|
|
|
0,
|
|
|
|
offsetof(struct tc_flower_key, dst_mac),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, dst_mac),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_ETH,
|
|
|
|
12,
|
|
|
|
offsetof(struct tc_flower_key, eth_type),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, eth_type),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_TCP,
|
|
|
|
0,
|
|
|
|
offsetof(struct tc_flower_key, tcp_src),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, tcp_src),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_TCP,
|
|
|
|
2,
|
|
|
|
offsetof(struct tc_flower_key, tcp_dst),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, tcp_dst),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_UDP,
|
|
|
|
0,
|
|
|
|
offsetof(struct tc_flower_key, udp_src),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, udp_src),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
}, {
|
|
|
|
TCA_PEDIT_KEY_EX_HDR_TYPE_UDP,
|
|
|
|
2,
|
|
|
|
offsetof(struct tc_flower_key, udp_dst),
|
2019-01-28 12:29:10 +00:00
|
|
|
MEMBER_SIZEOF(struct tc_flower_key, udp_dst),
|
|
|
|
0
|
2017-09-18 07:16:03 +03:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
static inline int
|
|
|
|
csum_update_flag(struct tc_flower *flower,
|
|
|
|
enum pedit_header_type htype);
|
|
|
|
|
2017-06-13 18:03:24 +03:00
|
|
|
struct tcmsg *
|
|
|
|
tc_make_request(int ifindex, int type, unsigned int flags,
|
|
|
|
struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
struct tcmsg *tcmsg;
|
|
|
|
|
|
|
|
ofpbuf_init(request, 512);
|
|
|
|
nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags);
|
|
|
|
tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg);
|
|
|
|
tcmsg->tcm_family = AF_UNSPEC;
|
|
|
|
tcmsg->tcm_ifindex = ifindex;
|
|
|
|
/* Caller should fill in tcmsg->tcm_handle. */
|
|
|
|
/* Caller should fill in tcmsg->tcm_parent. */
|
|
|
|
|
|
|
|
return tcmsg;
|
|
|
|
}
|
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
struct tcamsg *
|
|
|
|
tc_make_action_request(int type, unsigned int flags,
|
|
|
|
struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
struct tcamsg *tcamsg;
|
|
|
|
|
|
|
|
ofpbuf_init(request, 512);
|
|
|
|
nl_msg_put_nlmsghdr(request, sizeof *tcamsg, type, NLM_F_REQUEST | flags);
|
|
|
|
tcamsg = ofpbuf_put_zeros(request, sizeof *tcamsg);
|
|
|
|
tcamsg->tca_family = AF_UNSPEC;
|
|
|
|
|
|
|
|
return tcamsg;
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
static void request_from_tcf_id(struct tcf_id *id, uint16_t eth_type,
|
|
|
|
int type, unsigned int flags,
|
|
|
|
struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
int ifindex = id->block_id ? TCM_IFINDEX_MAGIC_BLOCK : id->ifindex;
|
|
|
|
uint32_t ingress_parent = id->block_id ? : TC_INGRESS_PARENT;
|
|
|
|
struct tcmsg *tcmsg;
|
|
|
|
|
|
|
|
tcmsg = tc_make_request(ifindex, type, flags, request);
|
|
|
|
tcmsg->tcm_parent = (id->hook == TC_EGRESS) ?
|
|
|
|
TC_EGRESS_PARENT : ingress_parent;
|
|
|
|
tcmsg->tcm_info = tc_make_handle(id->prio, eth_type);
|
|
|
|
tcmsg->tcm_handle = id->handle;
|
2019-12-22 12:16:40 +02:00
|
|
|
|
|
|
|
if (id->chain) {
|
|
|
|
nl_msg_put_u32(request, TCA_CHAIN, id->chain);
|
|
|
|
}
|
2019-12-22 12:16:36 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:24 +03:00
|
|
|
int
|
|
|
|
tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
|
|
|
|
{
|
|
|
|
int error = nl_transact(NETLINK_ROUTE, request, replyp);
|
|
|
|
ofpbuf_uninit(request);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:36:13 +01:00
|
|
|
/* Adds or deletes a root qdisc on device with specified ifindex.
|
2017-06-13 18:03:24 +03:00
|
|
|
*
|
2019-04-09 15:36:13 +01:00
|
|
|
* The tc_qdisc_hook parameter determines if the qdisc is added on device
|
|
|
|
* ingress or egress.
|
|
|
|
*
|
|
|
|
* If tc_qdisc_hook is TC_INGRESS, this function is equivalent to running the
|
|
|
|
* following when 'add' is true:
|
2017-06-13 18:03:24 +03:00
|
|
|
* /sbin/tc qdisc add dev <devname> handle ffff: ingress
|
|
|
|
*
|
|
|
|
* This function is equivalent to running the following when 'add' is false:
|
|
|
|
* /sbin/tc qdisc del dev <devname> handle ffff: ingress
|
|
|
|
*
|
2019-04-09 15:36:13 +01:00
|
|
|
* If tc_qdisc_hook is TC_EGRESS, this function is equivalent to:
|
|
|
|
* /sbin/tc qdisc (add|del) dev <devname> handle ffff: clsact
|
|
|
|
*
|
2017-06-13 18:03:24 +03:00
|
|
|
* Where dev <devname> is the device with specified ifindex name.
|
|
|
|
*
|
|
|
|
* The configuration and stats may be seen with the following command:
|
|
|
|
* /sbin/tc -s qdisc show dev <devname>
|
|
|
|
*
|
2018-06-28 17:03:02 +01:00
|
|
|
* If block_id is greater than 0, then the ingress qdisc is added to a block.
|
|
|
|
* In this case, it is equivalent to running (when 'add' is true):
|
|
|
|
* /sbin/tc qdisc add dev <devname> ingress_block <block_id> ingress
|
|
|
|
*
|
2017-06-13 18:03:24 +03:00
|
|
|
* Returns 0 if successful, otherwise a positive errno value.
|
|
|
|
*/
|
|
|
|
int
|
2019-04-09 15:36:13 +01:00
|
|
|
tc_add_del_qdisc(int ifindex, bool add, uint32_t block_id,
|
|
|
|
enum tc_qdisc_hook hook)
|
2017-06-13 18:03:24 +03:00
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
struct tcmsg *tcmsg;
|
|
|
|
int error;
|
|
|
|
int type = add ? RTM_NEWQDISC : RTM_DELQDISC;
|
|
|
|
int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
|
|
|
|
|
|
|
|
tcmsg = tc_make_request(ifindex, type, flags, &request);
|
2019-04-09 15:36:13 +01:00
|
|
|
|
|
|
|
if (hook == TC_EGRESS) {
|
|
|
|
tcmsg->tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0);
|
|
|
|
tcmsg->tcm_parent = TC_H_CLSACT;
|
|
|
|
nl_msg_put_string(&request, TCA_KIND, "clsact");
|
|
|
|
} else {
|
|
|
|
tcmsg->tcm_handle = TC_H_MAKE(TC_H_INGRESS, 0);
|
|
|
|
tcmsg->tcm_parent = TC_H_INGRESS;
|
|
|
|
nl_msg_put_string(&request, TCA_KIND, "ingress");
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:24 +03:00
|
|
|
nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
|
2019-04-09 15:36:13 +01:00
|
|
|
if (hook == TC_INGRESS && block_id) {
|
2018-06-28 17:03:02 +01:00
|
|
|
nl_msg_put_u32(&request, TCA_INGRESS_BLOCK, block_id);
|
|
|
|
}
|
2017-06-13 18:03:24 +03:00
|
|
|
|
|
|
|
error = tc_transact(&request, NULL);
|
|
|
|
if (error) {
|
|
|
|
/* If we're deleting the qdisc, don't worry about some of the
|
|
|
|
* error conditions. */
|
|
|
|
if (!add && (error == ENOENT || error == EINVAL)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
static const struct nl_policy tca_policy[] = {
|
|
|
|
[TCA_KIND] = { .type = NL_A_STRING, .optional = false, },
|
|
|
|
[TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
|
2019-12-22 12:16:40 +02:00
|
|
|
[TCA_CHAIN] = { .type = NL_A_U32, .optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
[TCA_STATS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_stats), .optional = true, },
|
|
|
|
[TCA_STATS2] = { .type = NL_A_NESTED, .optional = true, },
|
|
|
|
};
|
|
|
|
|
2021-01-26 09:17:46 +02:00
|
|
|
static const struct nl_policy tca_chain_policy[] = {
|
|
|
|
[TCA_CHAIN] = { .type = NL_A_U32, .optional = false, },
|
|
|
|
};
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static const struct nl_policy tca_flower_policy[] = {
|
|
|
|
[TCA_FLOWER_CLASSID] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_INDEV] = { .type = NL_A_STRING, .max_len = IFNAMSIZ,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ETH_SRC] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ETH_DST] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ETH_SRC_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ETH_DST_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ETH_TYPE] = { .type = NL_A_U16, .optional = false, },
|
2020-06-05 21:17:29 +08:00
|
|
|
[TCA_FLOWER_KEY_ARP_SIP] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_TIP] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_SHA] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_THA] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_OP] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_SHA_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_THA_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = ETH_ALEN,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NL_A_U8, .optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
[TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, },
|
|
|
|
[TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, },
|
|
|
|
[TCA_FLOWER_KEY_IP_PROTO] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV4_DST] = {.type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV6_SRC] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV6_DST] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_TCP_SRC] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_TCP_DST] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_UDP_SRC] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_UDP_DST] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
|
2017-07-25 14:39:51 +03:00
|
|
|
[TCA_FLOWER_KEY_SCTP_SRC] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_SCTP_DST] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
|
2018-09-02 14:07:49 +01:00
|
|
|
[TCA_FLOWER_KEY_MPLS_TTL] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_MPLS_BOS] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
[TCA_FLOWER_KEY_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
2022-08-14 16:46:03 +02:00
|
|
|
[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2022-08-14 16:46:00 +02:00
|
|
|
[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2018-03-12 14:58:47 +02:00
|
|
|
[TCA_FLOWER_KEY_FLAGS] = { .type = NL_A_BE32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NL_A_BE32, .optional = true, },
|
2017-08-07 18:19:06 +03:00
|
|
|
[TCA_FLOWER_KEY_IP_TTL] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
2018-07-31 13:40:37 +03:00
|
|
|
[TCA_FLOWER_KEY_IP_TOS] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
2017-08-07 18:19:10 +03:00
|
|
|
[TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2018-07-17 02:01:57 +00:00
|
|
|
[TCA_FLOWER_KEY_CVLAN_ID] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
|
2018-07-31 13:40:39 +03:00
|
|
|
[TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
2018-09-18 09:36:20 +01:00
|
|
|
[TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED,
|
|
|
|
.optional = true, },
|
2024-10-14 14:09:02 +02:00
|
|
|
[TCA_FLOWER_KEY_ENC_FLAGS] = { .type = NL_A_BE32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ENC_FLAGS_MASK] = { .type = NL_A_BE32,
|
|
|
|
.optional = true, },
|
2019-12-22 12:16:41 +02:00
|
|
|
[TCA_FLOWER_KEY_CT_STATE] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_ZONE] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NL_A_U16, .optional = true, },
|
2019-12-22 12:16:42 +02:00
|
|
|
[TCA_FLOWER_KEY_CT_MARK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_LABELS] = { .type = NL_A_U128, .optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NL_A_U128,
|
|
|
|
.optional = true, },
|
2021-01-28 15:51:08 +02:00
|
|
|
[TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NL_A_U8,
|
|
|
|
.optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
};
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
static const struct nl_policy tca_flower_terse_policy[] = {
|
|
|
|
[TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, },
|
|
|
|
[TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, },
|
|
|
|
};
|
|
|
|
|
2020-06-05 21:17:29 +08:00
|
|
|
static void
|
|
|
|
nl_parse_flower_arp(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
const struct eth_addr *eth;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ARP_SIP_MASK]) {
|
|
|
|
flower->key.arp.spa =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_SIP]);
|
|
|
|
flower->mask.arp.spa =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_SIP_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ARP_TIP_MASK]) {
|
|
|
|
flower->key.arp.tpa =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_TIP]);
|
|
|
|
flower->mask.arp.tpa =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ARP_TIP_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ARP_SHA_MASK]) {
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_SHA], ETH_ALEN);
|
|
|
|
memcpy(&flower->key.arp.sha, eth, sizeof flower->key.arp.sha);
|
|
|
|
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_SHA_MASK], ETH_ALEN);
|
|
|
|
memcpy(&flower->mask.arp.sha, eth, sizeof flower->mask.arp.sha);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ARP_THA_MASK]) {
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_THA], ETH_ALEN);
|
|
|
|
memcpy(&flower->key.arp.tha, eth, sizeof flower->key.arp.tha);
|
|
|
|
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ARP_THA_MASK], ETH_ALEN);
|
|
|
|
memcpy(&flower->mask.arp.tha, eth, sizeof flower->mask.arp.tha);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ARP_OP_MASK]) {
|
|
|
|
flower->key.arp.opcode =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ARP_OP]);
|
|
|
|
flower->mask.arp.opcode =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ARP_OP_MASK]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
|
|
|
nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
const struct eth_addr *eth;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ETH_SRC_MASK]) {
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN);
|
|
|
|
memcpy(&flower->key.src_mac, eth, sizeof flower->key.src_mac);
|
|
|
|
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC_MASK], ETH_ALEN);
|
|
|
|
memcpy(&flower->mask.src_mac, eth, sizeof flower->mask.src_mac);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ETH_DST_MASK]) {
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN);
|
|
|
|
memcpy(&flower->key.dst_mac, eth, sizeof flower->key.dst_mac);
|
|
|
|
|
|
|
|
eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST_MASK], ETH_ALEN);
|
|
|
|
memcpy(&flower->mask.dst_mac, eth, sizeof flower->mask.dst_mac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-02 14:07:49 +01:00
|
|
|
static void
|
|
|
|
nl_parse_flower_mpls(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
uint8_t ttl, tc, bos;
|
|
|
|
uint32_t label;
|
|
|
|
|
|
|
|
if (!eth_type_mpls(flower->key.eth_type)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
flower->key.encap_eth_type[0] =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
|
|
|
|
flower->key.mpls_lse = 0;
|
|
|
|
flower->mask.mpls_lse = 0;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_MPLS_TTL]) {
|
|
|
|
ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_TTL]);
|
|
|
|
set_mpls_lse_ttl(&flower->key.mpls_lse, ttl);
|
|
|
|
set_mpls_lse_ttl(&flower->mask.mpls_lse, 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_MPLS_BOS]) {
|
|
|
|
bos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_BOS]);
|
|
|
|
set_mpls_lse_bos(&flower->key.mpls_lse, bos);
|
2020-11-25 10:51:57 +01:00
|
|
|
set_mpls_lse_bos(&flower->mask.mpls_lse, 0xff);
|
2018-09-02 14:07:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_MPLS_TC]) {
|
|
|
|
tc = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_MPLS_TC]);
|
|
|
|
set_mpls_lse_tc(&flower->key.mpls_lse, tc);
|
|
|
|
set_mpls_lse_tc(&flower->mask.mpls_lse, 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_MPLS_LABEL]) {
|
|
|
|
label = nl_attr_get_u32(attrs[TCA_FLOWER_KEY_MPLS_LABEL]);
|
|
|
|
set_mpls_lse_label(&flower->key.mpls_lse, htonl(label));
|
|
|
|
set_mpls_lse_label(&flower->mask.mpls_lse, OVS_BE32_MAX);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
|
|
|
nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
2018-07-17 02:01:57 +00:00
|
|
|
ovs_be16 encap_ethtype;
|
|
|
|
|
2018-07-17 02:01:55 +00:00
|
|
|
if (!eth_type_vlan(flower->key.eth_type)) {
|
2017-06-13 18:03:27 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.encap_eth_type[0] =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.encap_eth_type[0] = CONSTANT_HTONS(0xffff);
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.vlan_id[0] =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.vlan_id[0] = VLAN_VID_MASK >> VLAN_VID_SHIFT;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.vlan_prio[0] =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.vlan_prio[0] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2018-07-17 02:01:57 +00:00
|
|
|
|
|
|
|
if (!attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
encap_ethtype = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_VLAN_ETH_TYPE]);
|
|
|
|
if (!eth_type_vlan(encap_ethtype)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
flower->key.encap_eth_type[1] = flower->key.encap_eth_type[0];
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.encap_eth_type[1] = CONSTANT_HTONS(0xffff);
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.encap_eth_type[0] = encap_ethtype;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_CVLAN_ID]) {
|
|
|
|
flower->key.vlan_id[1] =
|
|
|
|
nl_attr_get_u16(attrs[TCA_FLOWER_KEY_CVLAN_ID]);
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.vlan_id[1] = VLAN_VID_MASK >> VLAN_VID_SHIFT;
|
2018-07-17 02:01:57 +00:00
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_CVLAN_PRIO]) {
|
|
|
|
flower->key.vlan_prio[1] =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_CVLAN_PRIO]);
|
2022-02-22 16:21:42 +01:00
|
|
|
flower->mask.vlan_prio[1] = VLAN_PCP_MASK >> VLAN_PCP_SHIFT;
|
2018-07-17 02:01:57 +00:00
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
static int
|
|
|
|
nl_parse_geneve_key(const struct nlattr *in_nlattr,
|
|
|
|
struct tun_metadata *metadata)
|
|
|
|
{
|
|
|
|
struct geneve_opt *opt = NULL;
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
uint16_t last_opt_type;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
size_t left;
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
nl_attr_get_nested(in_nlattr, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC;
|
|
|
|
cnt = 0;
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS:
|
|
|
|
if (cnt && last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tun options class");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = &metadata->opts.gnv[cnt];
|
|
|
|
opt->opt_class = nl_attr_get_be16(nla);
|
|
|
|
cnt += sizeof(struct geneve_opt) / 4;
|
|
|
|
metadata->present.len += sizeof(struct geneve_opt);
|
|
|
|
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS;
|
|
|
|
break;
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE:
|
|
|
|
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tun options type");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->type = nl_attr_get_u8(nla);
|
|
|
|
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE;
|
|
|
|
break;
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA:
|
|
|
|
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tun options data");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->length = nl_attr_get_size(nla) / 4;
|
|
|
|
memcpy(opt + 1, nl_attr_get_unspec(nla, 1), opt->length * 4);
|
|
|
|
cnt += opt->length;
|
|
|
|
metadata->present.len += opt->length * 4;
|
|
|
|
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tun options without data");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-27 13:48:10 +03:00
|
|
|
static int
|
|
|
|
nl_parse_vxlan_key(const struct nlattr *in_nlattr,
|
|
|
|
struct tc_flower_tunnel *tunnel)
|
|
|
|
{
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
uint32_t gbp_raw;
|
|
|
|
size_t left;
|
|
|
|
|
|
|
|
nl_attr_get_nested(in_nlattr, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
|
|
|
|
gbp_raw = nl_attr_get_u32(nla);
|
|
|
|
odp_decode_gbp_raw(gbp_raw, &tunnel->gbp.id,
|
|
|
|
&tunnel->gbp.flags);
|
|
|
|
tunnel->gbp.id_present = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
VLOG_WARN_RL(&error_rl, "failed to parse vxlan tun options");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
static int
|
|
|
|
nl_parse_flower_tunnel_opts(struct nlattr *options,
|
2023-06-27 13:48:06 +03:00
|
|
|
struct tc_flower_tunnel *tunnel)
|
2018-09-18 09:36:20 +01:00
|
|
|
{
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
size_t left;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
nl_attr_get_nested(options, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
switch (type) {
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPTS_GENEVE:
|
2023-06-27 13:48:06 +03:00
|
|
|
err = nl_parse_geneve_key(nla, &tunnel->metadata);
|
2018-09-18 09:36:20 +01:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2023-06-27 13:48:10 +03:00
|
|
|
break;
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPTS_VXLAN:
|
|
|
|
err = nl_parse_vxlan_key(nla, tunnel);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
flower_tun_geneve_opt_check_len(struct tun_metadata *key,
|
|
|
|
struct tun_metadata *mask)
|
|
|
|
{
|
|
|
|
const struct geneve_opt *opt, *opt_mask;
|
|
|
|
int len, cnt = 0;
|
|
|
|
|
2022-08-14 16:45:59 +02:00
|
|
|
if (key->present.len != mask->present.len) {
|
|
|
|
goto bad_length;
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
len = key->present.len;
|
|
|
|
while (len) {
|
|
|
|
opt = &key->opts.gnv[cnt];
|
|
|
|
opt_mask = &mask->opts.gnv[cnt];
|
|
|
|
|
|
|
|
if (opt->length != opt_mask->length) {
|
2022-08-14 16:45:59 +02:00
|
|
|
goto bad_length;
|
2018-09-18 09:36:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
|
|
|
|
len -= sizeof(struct geneve_opt) + opt->length * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2022-08-14 16:45:59 +02:00
|
|
|
|
|
|
|
bad_length:
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse tun options; key/mask length differ");
|
|
|
|
return EINVAL;
|
2018-09-18 09:36:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
2018-09-18 09:36:20 +01:00
|
|
|
int err;
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) {
|
|
|
|
ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]);
|
|
|
|
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.id = be32_to_be64(id);
|
2019-01-17 17:41:36 +02:00
|
|
|
flower->mask.tunnel.id = OVS_BE64_MAX;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]) {
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
flower->mask.tunnel.ipv4.ipv4_src =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]);
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.ipv4.ipv4_src =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]) {
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
flower->mask.tunnel.ipv4.ipv4_dst =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]);
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.ipv4.ipv4_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]) {
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
flower->mask.tunnel.ipv6.ipv6_src =
|
|
|
|
nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]);
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.ipv6.ipv6_src =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]) {
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
flower->mask.tunnel.ipv6.ipv6_dst =
|
|
|
|
nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]);
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.ipv6.ipv6_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST]);
|
|
|
|
}
|
2022-08-14 16:46:03 +02:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK]) {
|
|
|
|
flower->mask.tunnel.tp_src =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK]);
|
|
|
|
flower->key.tunnel.tp_src =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT]);
|
|
|
|
}
|
2022-08-14 16:46:00 +02:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK]) {
|
|
|
|
flower->mask.tunnel.tp_dst =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK]);
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.tp_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]);
|
|
|
|
}
|
2018-09-06 13:52:26 +03:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]) {
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.tos =
|
2018-07-31 13:40:39 +03:00
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TOS]);
|
2018-09-06 13:52:26 +03:00
|
|
|
flower->mask.tunnel.tos =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TOS_MASK]);
|
2018-07-31 13:40:39 +03:00
|
|
|
}
|
2018-09-06 13:52:26 +03:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]) {
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->key.tunnel.ttl =
|
2018-07-31 13:40:39 +03:00
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TTL]);
|
2018-09-06 13:52:26 +03:00
|
|
|
flower->mask.tunnel.ttl =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]);
|
2018-07-31 13:40:39 +03:00
|
|
|
}
|
2019-12-22 12:16:39 +02:00
|
|
|
|
|
|
|
if (!is_all_zeros(&flower->mask.tunnel, sizeof flower->mask.tunnel) ||
|
|
|
|
!is_all_zeros(&flower->key.tunnel, sizeof flower->key.tunnel)) {
|
|
|
|
flower->tunnel = true;
|
|
|
|
}
|
|
|
|
|
2024-10-14 14:09:02 +02:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_FLAGS_MASK]) {
|
|
|
|
flower->key.tunnel.tc_enc_flags = ntohl(
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_FLAGS]));
|
|
|
|
flower->mask.tunnel.tc_enc_flags = ntohl(
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_FLAGS_MASK]));
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
if (attrs[TCA_FLOWER_KEY_ENC_OPTS] &&
|
|
|
|
attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
|
|
|
|
err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS],
|
2023-06-27 13:48:06 +03:00
|
|
|
&flower->key.tunnel);
|
2018-09-18 09:36:20 +01:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK],
|
2023-06-27 13:48:06 +03:00
|
|
|
&flower->mask.tunnel);
|
2018-09-18 09:36:20 +01:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = flower_tun_geneve_opt_check_len(&flower->key.tunnel.metadata,
|
|
|
|
&flower->mask.tunnel.metadata);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
} else if (attrs[TCA_FLOWER_KEY_ENC_OPTS]) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse tun options; no mask supplied");
|
|
|
|
return EINVAL;
|
|
|
|
} else if (attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tun options; no key supplied");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:41 +02:00
|
|
|
static void
|
|
|
|
nl_parse_flower_ct_match(struct nlattr **attrs, struct tc_flower *flower) {
|
|
|
|
struct tc_flower_key *key = &flower->key;
|
|
|
|
struct tc_flower_key *mask = &flower->mask;
|
|
|
|
struct nlattr *attr_key, *attr_mask;
|
|
|
|
|
|
|
|
attr_key = attrs[TCA_FLOWER_KEY_CT_STATE];
|
|
|
|
attr_mask = attrs[TCA_FLOWER_KEY_CT_STATE_MASK];
|
|
|
|
if (attr_mask) {
|
|
|
|
key->ct_state = nl_attr_get_u16(attr_key);
|
|
|
|
mask->ct_state = nl_attr_get_u16(attr_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_key = attrs[TCA_FLOWER_KEY_CT_ZONE];
|
|
|
|
attr_mask = attrs[TCA_FLOWER_KEY_CT_ZONE_MASK];
|
|
|
|
if (attrs[TCA_FLOWER_KEY_CT_ZONE_MASK]) {
|
|
|
|
key->ct_zone = nl_attr_get_u16(attr_key);
|
|
|
|
mask->ct_zone = nl_attr_get_u16(attr_mask);
|
|
|
|
}
|
2019-12-22 12:16:42 +02:00
|
|
|
|
|
|
|
attr_key = attrs[TCA_FLOWER_KEY_CT_MARK];
|
|
|
|
attr_mask = attrs[TCA_FLOWER_KEY_CT_MARK_MASK];
|
|
|
|
if (attrs[TCA_FLOWER_KEY_CT_MARK_MASK]) {
|
|
|
|
key->ct_mark = nl_attr_get_u32(attr_key);
|
|
|
|
mask->ct_mark = nl_attr_get_u32(attr_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_key = attrs[TCA_FLOWER_KEY_CT_LABELS];
|
|
|
|
attr_mask = attrs[TCA_FLOWER_KEY_CT_LABELS_MASK];
|
|
|
|
if (attrs[TCA_FLOWER_KEY_CT_LABELS_MASK]) {
|
|
|
|
key->ct_label = nl_attr_get_u128(attr_key);
|
|
|
|
mask->ct_label = nl_attr_get_u128(attr_mask);
|
|
|
|
}
|
2019-12-22 12:16:41 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
|
|
|
nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
|
|
|
|
uint8_t ip_proto = 0;
|
|
|
|
struct tc_flower_key *key = &flower->key;
|
|
|
|
struct tc_flower_key *mask = &flower->mask;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IP_PROTO]) {
|
|
|
|
ip_proto = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_PROTO]);
|
|
|
|
key->ip_proto = ip_proto;
|
|
|
|
mask->ip_proto = UINT8_MAX;
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:58:47 +02:00
|
|
|
if (attrs[TCA_FLOWER_KEY_FLAGS_MASK]) {
|
2018-03-21 20:11:22 +00:00
|
|
|
key->flags = ntohl(nl_attr_get_be32(attrs[TCA_FLOWER_KEY_FLAGS]));
|
|
|
|
mask->flags =
|
|
|
|
ntohl(nl_attr_get_be32(attrs[TCA_FLOWER_KEY_FLAGS_MASK]));
|
2018-03-12 14:58:47 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]) {
|
|
|
|
key->ipv4.ipv4_src =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC]);
|
|
|
|
mask->ipv4.ipv4_src =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]) {
|
|
|
|
key->ipv4.ipv4_dst =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST]);
|
|
|
|
mask->ipv4.ipv4_dst =
|
|
|
|
nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK]) {
|
|
|
|
struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_SRC];
|
|
|
|
struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK];
|
|
|
|
|
|
|
|
key->ipv6.ipv6_src = nl_attr_get_in6_addr(attr);
|
|
|
|
mask->ipv6.ipv6_src = nl_attr_get_in6_addr(attr_mask);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IPV6_DST_MASK]) {
|
|
|
|
struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_DST];
|
|
|
|
struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_DST_MASK];
|
|
|
|
|
|
|
|
key->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr);
|
|
|
|
mask->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ip_proto == IPPROTO_TCP) {
|
|
|
|
if (attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->tcp_src =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC]);
|
2017-08-03 16:38:24 +03:00
|
|
|
mask->tcp_src =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_TCP_DST_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->tcp_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST]);
|
2017-08-03 16:38:24 +03:00
|
|
|
mask->tcp_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST_MASK]);
|
|
|
|
}
|
2017-08-07 18:19:10 +03:00
|
|
|
if (attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]) {
|
|
|
|
key->tcp_flags =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS]);
|
|
|
|
mask->tcp_flags =
|
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_FLAGS_MASK]);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (ip_proto == IPPROTO_UDP) {
|
|
|
|
if (attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->udp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC]);
|
|
|
|
mask->udp_src =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_UDP_DST_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->udp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST]);
|
|
|
|
mask->udp_dst =
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST_MASK]);
|
|
|
|
}
|
2017-07-25 14:39:51 +03:00
|
|
|
} else if (ip_proto == IPPROTO_SCTP) {
|
|
|
|
if (attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->sctp_src = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC]);
|
|
|
|
mask->sctp_src =
|
2017-07-25 14:39:51 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_SRC_MASK]);
|
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]) {
|
2017-08-03 16:38:24 +03:00
|
|
|
key->sctp_dst = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST]);
|
|
|
|
mask->sctp_dst =
|
2017-07-25 14:39:51 +03:00
|
|
|
nl_attr_get_be16(attrs[TCA_FLOWER_KEY_SCTP_DST_MASK]);
|
|
|
|
}
|
2021-01-28 15:51:08 +02:00
|
|
|
} else if (ip_proto == IPPROTO_ICMP) {
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ICMPV4_CODE_MASK]) {
|
|
|
|
key->icmp_code =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE]);
|
|
|
|
mask->icmp_code =
|
2022-02-22 16:24:46 +01:00
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_CODE_MASK]);
|
2021-01-28 15:51:08 +02:00
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]) {
|
2022-02-22 16:24:46 +01:00
|
|
|
key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE]);
|
2021-01-28 15:51:08 +02:00
|
|
|
mask->icmp_type =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK]);
|
|
|
|
}
|
|
|
|
} else if (ip_proto == IPPROTO_ICMPV6) {
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]) {
|
2022-02-22 16:24:46 +01:00
|
|
|
key->icmp_code = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE]);
|
2021-01-28 15:51:08 +02:00
|
|
|
mask->icmp_code =
|
2022-02-22 16:24:46 +01:00
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_CODE_MASK]);
|
2021-01-28 15:51:08 +02:00
|
|
|
}
|
|
|
|
if (attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]) {
|
2022-02-22 16:24:46 +01:00
|
|
|
key->icmp_type = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE]);
|
2021-01-28 15:51:08 +02:00
|
|
|
mask->icmp_type =
|
|
|
|
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK]);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2017-08-07 18:19:06 +03:00
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IP_TTL_MASK]) {
|
|
|
|
key->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL]);
|
|
|
|
mask->ip_ttl = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TTL_MASK]);
|
|
|
|
}
|
2018-07-31 13:40:37 +03:00
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_KEY_IP_TOS_MASK]) {
|
|
|
|
key->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS]);
|
|
|
|
mask->ip_tos = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_TOS_MASK]);
|
|
|
|
}
|
2019-12-22 12:16:41 +02:00
|
|
|
|
|
|
|
nl_parse_flower_ct_match(attrs, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
dpctl: Properly reflect a rule's offloaded to HW state
Previously, any rule that is offloaded via a netdev, not necessarily
to the HW, would be reported as "offloaded". This patch fixes this
misalignment, and introduces the 'dp' state, as follows:
rule is in HW via TC offload -> offloaded=yes dp:tc
rule is in not HW over TC DP -> offloaded=no dp:tc
rule is in not HW over OVS DP -> offloaded=no dp:ovs
To achieve this, the flows's 'offloaded' flag was encapsulated in a new
attrs struct, which contains the offloaded state of the flow and the
DP layer the flow is handled in, and instead of setting the flow's
'offloaded' state based solely on the type of dump it was acquired
via, for netdev flows it now sends the new attrs struct to be
collected along with the rest of the flow via the netdev, allowing
it to be set per flow.
For TC offloads, the offloaded state is set based on the 'in_hw' and
'not_in_hw' flags received from the TC as part of the flower. If no
such flag was received, due to lack of kernel support, it defaults
to true.
Signed-off-by: Gavi Teitz <gavi@mellanox.com>
Acked-by: Roi Dayan <roid@mellanox.com>
[simon: resolved conflict in lib/dpctl.man]
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2018-06-07 09:36:59 +03:00
|
|
|
static enum tc_offloaded_state
|
|
|
|
nl_get_flower_offloaded_state(struct nlattr **attrs)
|
|
|
|
{
|
|
|
|
uint32_t flower_flags = 0;
|
|
|
|
|
|
|
|
if (attrs[TCA_FLOWER_FLAGS]) {
|
|
|
|
flower_flags = nl_attr_get_u32(attrs[TCA_FLOWER_FLAGS]);
|
|
|
|
if (flower_flags & TCA_CLS_FLAGS_NOT_IN_HW) {
|
|
|
|
return TC_OFFLOADED_STATE_NOT_IN_HW;
|
|
|
|
} else if (flower_flags & TCA_CLS_FLAGS_IN_HW) {
|
|
|
|
return TC_OFFLOADED_STATE_IN_HW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TC_OFFLOADED_STATE_UNDEFINED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
|
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
static void
|
|
|
|
nl_parse_action_pc(uint32_t action_pc, struct tc_action *action)
|
|
|
|
{
|
|
|
|
if (action_pc == TC_ACT_STOLEN) {
|
|
|
|
action->jump_action = JUMP_ACTION_STOP;
|
|
|
|
} else if (action_pc & TC_ACT_JUMP) {
|
|
|
|
action->jump_action = action_pc & TC_ACT_EXT_VAL_MASK;
|
|
|
|
} else {
|
|
|
|
action->jump_action = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static const struct nl_policy pedit_policy[] = {
|
|
|
|
[TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_pedit),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED,
|
|
|
|
.optional = false, },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
2022-02-20 16:47:36 +02:00
|
|
|
struct tc_action *action = &flower->actions[flower->action_count++];
|
2017-09-18 07:16:03 +03:00
|
|
|
struct nlattr *pe_attrs[ARRAY_SIZE(pedit_policy)];
|
|
|
|
const struct tc_pedit *pe;
|
|
|
|
const struct tc_pedit_key *keys;
|
|
|
|
const struct nlattr *nla, *keys_ex, *ex_type;
|
|
|
|
const void *keys_attr;
|
2022-02-20 16:47:36 +02:00
|
|
|
char *rewrite_key = (void *) &action->rewrite.key;
|
|
|
|
char *rewrite_mask = (void *) &action->rewrite.mask;
|
2017-09-18 07:16:03 +03:00
|
|
|
size_t keys_ex_size, left;
|
2017-11-21 14:40:38 +02:00
|
|
|
int type, i = 0, err;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, pedit_policy, pe_attrs,
|
|
|
|
ARRAY_SIZE(pedit_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse pedit action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
pe = nl_attr_get_unspec(pe_attrs[TCA_PEDIT_PARMS_EX], sizeof *pe);
|
|
|
|
keys = pe->keys;
|
|
|
|
keys_attr = pe_attrs[TCA_PEDIT_KEYS_EX];
|
|
|
|
keys_ex = nl_attr_get(keys_attr);
|
|
|
|
keys_ex_size = nl_attr_get_size(keys_attr);
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, keys_ex, keys_ex_size) {
|
|
|
|
if (i >= pe->nkeys) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-11-21 14:40:37 +02:00
|
|
|
if (nl_attr_type(nla) != TCA_PEDIT_KEY_EX) {
|
2017-09-18 07:16:03 +03:00
|
|
|
VLOG_ERR_RL(&error_rl, "unable to parse legacy pedit type: %d",
|
|
|
|
nl_attr_type(nla));
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
ex_type = nl_attr_find_nested(nla, TCA_PEDIT_KEY_EX_HTYPE);
|
2022-11-04 15:06:04 +02:00
|
|
|
if (!ex_type) {
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
type = nl_attr_get_u16(ex_type);
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
err = csum_update_flag(flower, type);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
for (int j = 0; j < ARRAY_SIZE(flower_pedit_map); j++) {
|
|
|
|
struct flower_key_to_pedit *m = &flower_pedit_map[j];
|
|
|
|
int flower_off = m->flower_offset;
|
|
|
|
int sz = m->size;
|
|
|
|
int mf = m->offset;
|
2020-11-24 11:01:09 +08:00
|
|
|
int ef = ROUND_UP(mf, 4);
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
if (m->htype != type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check overlap between current pedit key, which is always
|
|
|
|
* 4 bytes (range [off, off + 3]), and a map entry in
|
2020-11-24 11:01:09 +08:00
|
|
|
* flower_pedit_map sf = ROUND_DOWN(mf, 4)
|
|
|
|
* (range [sf|mf, (mf + sz - 1)|ef]) */
|
2017-09-18 07:16:03 +03:00
|
|
|
if ((keys->off >= mf && keys->off < mf + sz)
|
2020-11-24 11:01:09 +08:00
|
|
|
|| (keys->off + 3 >= mf && keys->off + 3 < ef)) {
|
2017-09-18 07:16:03 +03:00
|
|
|
int diff = flower_off + (keys->off - mf);
|
2019-01-28 12:29:07 +00:00
|
|
|
ovs_be32 *dst = (void *) (rewrite_key + diff);
|
|
|
|
ovs_be32 *dst_m = (void *) (rewrite_mask + diff);
|
2022-10-26 15:40:28 +02:00
|
|
|
ovs_be32 mask, mask_word, data_word, val;
|
2017-09-18 07:16:03 +03:00
|
|
|
uint32_t zero_bits;
|
|
|
|
|
2019-01-28 12:29:10 +00:00
|
|
|
mask_word = htonl(ntohl(keys->mask) << m->boundary_shift);
|
|
|
|
data_word = htonl(ntohl(keys->val) << m->boundary_shift);
|
|
|
|
mask = ~(mask_word);
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
if (keys->off < mf) {
|
|
|
|
zero_bits = 8 * (mf - keys->off);
|
2019-01-28 12:29:07 +00:00
|
|
|
mask &= htonl(UINT32_MAX >> zero_bits);
|
2017-09-18 07:16:03 +03:00
|
|
|
} else if (keys->off + 4 > mf + m->size) {
|
|
|
|
zero_bits = 8 * (keys->off + 4 - mf - m->size);
|
2019-01-28 12:29:07 +00:00
|
|
|
mask &= htonl(UINT32_MAX << zero_bits);
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
|
|
|
|
2022-10-26 15:40:28 +02:00
|
|
|
val = get_unaligned_be32(dst_m);
|
|
|
|
val |= mask;
|
|
|
|
put_unaligned_be32(dst_m, val);
|
|
|
|
|
|
|
|
val = get_unaligned_be32(dst);
|
|
|
|
val |= data_word & mask;
|
|
|
|
put_unaligned_be32(dst, val);
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
keys++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
action->type = TC_ACT_PEDIT;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(pe->action, action);
|
2017-09-18 07:16:03 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static const struct nl_policy tunnel_key_policy[] = {
|
|
|
|
[TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_tunnel_key),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct in6_addr),
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_DST_PORT] = { .type = NL_A_U16, .optional = true, },
|
2018-07-31 13:40:38 +03:00
|
|
|
[TCA_TUNNEL_KEY_ENC_TOS] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_TUNNEL_KEY_ENC_TTL] = { .type = NL_A_U8, .optional = true, },
|
2018-09-18 09:36:19 +01:00
|
|
|
[TCA_TUNNEL_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, },
|
2018-10-11 10:06:43 +03:00
|
|
|
[TCA_TUNNEL_KEY_NO_CSUM] = { .type = NL_A_U8, .optional = true, },
|
2024-10-14 14:09:04 +02:00
|
|
|
[TCA_TUNNEL_KEY_NO_FRAG] = { .type = NL_A_FLAG, .optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
};
|
|
|
|
|
2018-09-18 09:36:19 +01:00
|
|
|
static int
|
|
|
|
nl_parse_act_geneve_opts(const struct nlattr *in_nlattr,
|
|
|
|
struct tc_action *action)
|
|
|
|
{
|
|
|
|
struct geneve_opt *opt = NULL;
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
uint16_t last_opt_type;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
size_t left;
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
nl_attr_get_nested(in_nlattr, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_UNSPEC;
|
|
|
|
cnt = 0;
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS:
|
|
|
|
if (cnt && last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse action geneve options class");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = &action->encap.data.opts.gnv[cnt];
|
|
|
|
opt->opt_class = nl_attr_get_be16(nla);
|
|
|
|
cnt += sizeof(struct geneve_opt) / 4;
|
|
|
|
action->encap.data.present.len += sizeof(struct geneve_opt);
|
|
|
|
last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS;
|
|
|
|
break;
|
|
|
|
case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE:
|
|
|
|
if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse action geneve options type");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->type = nl_attr_get_u8(nla);
|
|
|
|
last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE;
|
|
|
|
break;
|
|
|
|
case TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA:
|
|
|
|
if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse action geneve options data");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->length = nl_attr_get_size(nla) / 4;
|
|
|
|
memcpy(opt + 1, nl_attr_get_unspec(nla, 1), opt->length * 4);
|
|
|
|
cnt += opt->length;
|
|
|
|
action->encap.data.present.len += opt->length * 4;
|
|
|
|
last_opt_type = TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_opt_type != TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"failed to parse action geneve options without data");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-27 13:48:12 +03:00
|
|
|
static int
|
|
|
|
nl_parse_act_vxlan_opts(struct nlattr *in_nlattr, struct tc_action *action)
|
|
|
|
{
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
size_t left;
|
|
|
|
|
|
|
|
nl_attr_get_nested(in_nlattr, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
int32_t gbp_raw;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP:
|
|
|
|
gbp_raw = nl_attr_get_u32(nla);
|
|
|
|
odp_decode_gbp_raw(gbp_raw, &action->encap.gbp.id,
|
|
|
|
&action->encap.gbp.flags);
|
|
|
|
action->encap.gbp.id_present = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:19 +01:00
|
|
|
static int
|
|
|
|
nl_parse_act_tunnel_opts(struct nlattr *options, struct tc_action *action)
|
|
|
|
{
|
|
|
|
const struct ofpbuf *msg;
|
|
|
|
struct nlattr *nla;
|
|
|
|
struct ofpbuf buf;
|
|
|
|
size_t left;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!options) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_attr_get_nested(options, &buf);
|
|
|
|
msg = &buf;
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
switch (type) {
|
|
|
|
case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
|
|
|
|
err = nl_parse_act_geneve_opts(nla, action);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2023-06-27 13:48:12 +03:00
|
|
|
break;
|
|
|
|
case TCA_TUNNEL_KEY_ENC_OPTS_VXLAN:
|
|
|
|
err = nl_parse_act_vxlan_opts(nla, action);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2018-09-18 09:36:19 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static int
|
|
|
|
nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)];
|
|
|
|
const struct nlattr *tun_parms;
|
|
|
|
const struct tc_tunnel_key *tun;
|
2018-04-10 14:18:08 +09:00
|
|
|
struct tc_action *action;
|
2018-09-18 09:36:19 +01:00
|
|
|
int err;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, tunnel_key_policy, tun_attrs,
|
|
|
|
ARRAY_SIZE(tunnel_key_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tunnel_key action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
tun_parms = tun_attrs[TCA_TUNNEL_KEY_PARMS];
|
|
|
|
tun = nl_attr_get_unspec(tun_parms, sizeof *tun);
|
|
|
|
if (tun->t_action == TCA_TUNNEL_KEY_ACT_SET) {
|
|
|
|
struct nlattr *id = tun_attrs[TCA_TUNNEL_KEY_ENC_KEY_ID];
|
|
|
|
struct nlattr *dst_port = tun_attrs[TCA_TUNNEL_KEY_ENC_DST_PORT];
|
|
|
|
struct nlattr *ipv4_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_SRC];
|
|
|
|
struct nlattr *ipv4_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_DST];
|
|
|
|
struct nlattr *ipv6_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_SRC];
|
|
|
|
struct nlattr *ipv6_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_DST];
|
2018-07-31 13:40:38 +03:00
|
|
|
struct nlattr *tos = tun_attrs[TCA_TUNNEL_KEY_ENC_TOS];
|
|
|
|
struct nlattr *ttl = tun_attrs[TCA_TUNNEL_KEY_ENC_TTL];
|
2018-09-18 09:36:19 +01:00
|
|
|
struct nlattr *tun_opt = tun_attrs[TCA_TUNNEL_KEY_ENC_OPTS];
|
2018-10-11 10:06:43 +03:00
|
|
|
struct nlattr *no_csum = tun_attrs[TCA_TUNNEL_KEY_NO_CSUM];
|
2024-10-14 14:09:04 +02:00
|
|
|
struct nlattr *no_frag = tun_attrs[TCA_TUNNEL_KEY_NO_FRAG];
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
action = &flower->actions[flower->action_count++];
|
|
|
|
action->type = TC_ACT_ENCAP;
|
|
|
|
action->encap.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 0;
|
|
|
|
action->encap.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
if (ipv6_src) {
|
2018-04-10 14:18:08 +09:00
|
|
|
action->encap.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
if (ipv6_dst) {
|
2018-04-10 14:18:08 +09:00
|
|
|
action->encap.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2018-04-10 14:18:08 +09:00
|
|
|
action->encap.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0;
|
2019-01-17 17:41:36 +02:00
|
|
|
action->encap.id_present = id ? true : false;
|
2018-04-10 14:18:08 +09:00
|
|
|
action->encap.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0;
|
2018-07-31 13:40:38 +03:00
|
|
|
action->encap.tos = tos ? nl_attr_get_u8(tos) : 0;
|
|
|
|
action->encap.ttl = ttl ? nl_attr_get_u8(ttl) : 0;
|
2018-10-11 10:06:43 +03:00
|
|
|
action->encap.no_csum = no_csum ? nl_attr_get_u8(no_csum) : 0;
|
2024-10-14 14:09:04 +02:00
|
|
|
action->encap.dont_fragment = no_frag ? true : false;
|
2018-09-18 09:36:19 +01:00
|
|
|
|
|
|
|
err = nl_parse_act_tunnel_opts(tun_opt, action);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(tun->action, action);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
|
2018-09-06 13:52:25 +03:00
|
|
|
flower->tunnel = true;
|
2017-06-13 18:03:27 +03:00
|
|
|
} else {
|
|
|
|
VLOG_ERR_RL(&error_rl, "unknown tunnel actions: %d, %d",
|
|
|
|
tun->action, tun->t_action);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct nl_policy gact_policy[] = {
|
|
|
|
[TCA_GACT_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_gact),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_GACT_TM] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tcf_t),
|
|
|
|
.optional = false, },
|
|
|
|
};
|
|
|
|
|
2017-07-30 08:01:52 +03:00
|
|
|
static int
|
|
|
|
get_user_hz(void)
|
|
|
|
{
|
|
|
|
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
|
|
|
static int user_hz = 100;
|
|
|
|
|
|
|
|
if (ovsthread_once_start(&once)) {
|
|
|
|
user_hz = sysconf(_SC_CLK_TCK);
|
|
|
|
ovsthread_once_done(&once);
|
|
|
|
}
|
|
|
|
|
|
|
|
return user_hz;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
static void
|
|
|
|
nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
|
|
|
|
{
|
2023-02-07 15:06:23 +01:00
|
|
|
uint64_t lastused;
|
|
|
|
|
|
|
|
/* On creation both tm->install and tm->lastuse are set to jiffies
|
|
|
|
* by the kernel. So if both values are the same, the flow has not been
|
|
|
|
* used yet.
|
|
|
|
*
|
|
|
|
* Note that tm->firstuse can not be used due to some kernel bug, i.e.,
|
|
|
|
* hardware offloaded flows do not update tm->firstuse. */
|
|
|
|
if (tm->lastuse == tm->install) {
|
|
|
|
lastused = 0;
|
|
|
|
} else {
|
|
|
|
lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
|
|
|
|
if (flower->lastused < lastused) {
|
|
|
|
flower->lastused = lastused;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-12-22 12:16:40 +02:00
|
|
|
nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)];
|
|
|
|
const struct tc_gact *p;
|
|
|
|
struct nlattr *gact_parms;
|
2019-12-22 12:16:40 +02:00
|
|
|
struct tc_action *action;
|
2022-06-24 15:18:58 +02:00
|
|
|
struct tcf_t tm;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, gact_policy, gact_attrs,
|
|
|
|
ARRAY_SIZE(gact_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse gact action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
gact_parms = gact_attrs[TCA_GACT_PARMS];
|
|
|
|
p = nl_attr_get_unspec(gact_parms, sizeof *p);
|
|
|
|
|
2019-12-22 12:16:40 +02:00
|
|
|
if (TC_ACT_EXT_CMP(p->action, TC_ACT_GOTO_CHAIN)) {
|
|
|
|
action = &flower->actions[flower->action_count++];
|
|
|
|
action->chain = p->action & TC_ACT_EXT_VAL_MASK;
|
|
|
|
action->type = TC_ACT_GOTO;
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(p->action, action);
|
2019-12-22 12:16:40 +02:00
|
|
|
} else if (p->action != TC_ACT_SHOT) {
|
2017-06-13 18:03:27 +03:00
|
|
|
VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-06-24 15:18:58 +02:00
|
|
|
memcpy(&tm, nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof tm),
|
|
|
|
sizeof tm);
|
|
|
|
nl_parse_tcf(&tm, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-08 09:55:26 +00:00
|
|
|
static const struct nl_policy police_policy[] = {
|
|
|
|
[TCA_POLICE_TBF] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_police),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_POLICE_RATE] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = 1024,
|
|
|
|
.optional = true, },
|
2023-07-17 10:08:17 +02:00
|
|
|
[TCA_POLICE_RATE64] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
2022-07-08 09:55:26 +00:00
|
|
|
[TCA_POLICE_PEAKRATE] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = 1024,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_POLICE_AVRATE] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_POLICE_RESULT] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_POLICE_TM] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tcf_t),
|
|
|
|
.optional = true, },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_police(const struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *police_attrs[ARRAY_SIZE(police_policy)] = {};
|
|
|
|
const struct tc_police *police;
|
2022-07-13 10:20:44 +02:00
|
|
|
struct nlattr *police_result;
|
2022-07-08 09:55:26 +00:00
|
|
|
struct tc_action *action;
|
|
|
|
struct nlattr *police_tm;
|
2022-07-13 16:32:44 +02:00
|
|
|
struct tcf_t tm;
|
2022-07-08 09:55:26 +00:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, police_policy, police_attrs,
|
|
|
|
ARRAY_SIZE(police_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "Failed to parse police action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
police = nl_attr_get_unspec(police_attrs[TCA_POLICE_TBF], sizeof *police);
|
|
|
|
action = &flower->actions[flower->action_count++];
|
2022-07-13 10:20:44 +02:00
|
|
|
|
|
|
|
police_result = police_attrs[TCA_POLICE_RESULT];
|
|
|
|
if (police_result && !tc_is_meter_index(police->index)) {
|
|
|
|
action->type = TC_ACT_POLICE_MTU;
|
|
|
|
action->police.mtu = police->mtu;
|
|
|
|
|
|
|
|
uint32_t action_pc = nl_attr_get_u32(police_result);
|
|
|
|
if (action_pc & TC_ACT_JUMP) {
|
|
|
|
action->police.result_jump = action_pc & TC_ACT_EXT_VAL_MASK;
|
|
|
|
} else {
|
|
|
|
action->police.result_jump = JUMP_ACTION_STOP;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
action->type = TC_ACT_POLICE;
|
|
|
|
action->police.index = police->index;
|
|
|
|
}
|
2022-07-08 09:55:26 +00:00
|
|
|
|
|
|
|
police_tm = police_attrs[TCA_POLICE_TM];
|
|
|
|
if (police_tm) {
|
2022-07-13 16:32:44 +02:00
|
|
|
memcpy(&tm, nl_attr_get_unspec(police_tm, sizeof tm), sizeof tm);
|
|
|
|
nl_parse_tcf(&tm, flower);
|
2022-07-08 09:55:26 +00:00
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(police->action, action);
|
2022-07-08 09:55:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static const struct nl_policy mirred_policy[] = {
|
|
|
|
[TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_mirred),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_MIRRED_TM] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tcf_t),
|
|
|
|
.optional = false, },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct nlattr *mirred_attrs[ARRAY_SIZE(mirred_policy)];
|
|
|
|
const struct tc_mirred *m;
|
|
|
|
const struct nlattr *mirred_parms;
|
|
|
|
struct nlattr *mirred_tm;
|
2018-04-10 14:18:08 +09:00
|
|
|
struct tc_action *action;
|
2022-06-24 15:18:58 +02:00
|
|
|
struct tcf_t tm;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, mirred_policy, mirred_attrs,
|
|
|
|
ARRAY_SIZE(mirred_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse mirred action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
mirred_parms = mirred_attrs[TCA_MIRRED_PARMS];
|
|
|
|
m = nl_attr_get_unspec(mirred_parms, sizeof *m);
|
|
|
|
|
2019-04-09 15:36:12 +01:00
|
|
|
if (m->eaction != TCA_EGRESS_REDIR && m->eaction != TCA_EGRESS_MIRROR &&
|
|
|
|
m->eaction != TCA_INGRESS_REDIR && m->eaction != TCA_INGRESS_MIRROR) {
|
2017-06-13 18:03:27 +03:00
|
|
|
VLOG_ERR_RL(&error_rl, "unknown mirred action: %d, %d, %d",
|
2018-04-10 14:18:08 +09:00
|
|
|
m->action, m->eaction, m->ifindex);
|
2017-06-13 18:03:27 +03:00
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
action = &flower->actions[flower->action_count++];
|
2019-04-09 15:36:12 +01:00
|
|
|
action->out.ifindex_out = m->ifindex;
|
|
|
|
if (m->eaction == TCA_INGRESS_REDIR || m->eaction == TCA_INGRESS_MIRROR) {
|
|
|
|
action->out.ingress = true;
|
|
|
|
} else {
|
|
|
|
action->out.ingress = false;
|
|
|
|
}
|
2018-04-10 14:18:08 +09:00
|
|
|
action->type = TC_ACT_OUTPUT;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
mirred_tm = mirred_attrs[TCA_MIRRED_TM];
|
2022-06-24 15:18:58 +02:00
|
|
|
memcpy(&tm, nl_attr_get_unspec(mirred_tm, sizeof tm), sizeof tm);
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_tcf(&tm, flower);
|
|
|
|
nl_parse_action_pc(m->action, action);
|
2017-06-13 18:03:27 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:41 +02:00
|
|
|
static const struct nl_policy ct_policy[] = {
|
|
|
|
[TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_ct),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_CT_ACTION] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_ZONE] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2019-12-22 12:16:42 +02:00
|
|
|
[TCA_CT_MARK] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_MARK_MASK] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_LABELS] = { .type = NL_A_UNSPEC,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
|
|
|
|
.optional = true, },
|
2019-12-22 12:16:43 +02:00
|
|
|
[TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_NAT_IPV4_MAX] = { .type = NL_A_U32,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_NAT_IPV6_MIN] = { .min_len = sizeof(struct in6_addr),
|
|
|
|
.type = NL_A_UNSPEC,
|
|
|
|
.optional = true },
|
|
|
|
[TCA_CT_NAT_IPV6_MAX] = { .min_len = sizeof(struct in6_addr),
|
|
|
|
.type = NL_A_UNSPEC,
|
|
|
|
.optional = true },
|
|
|
|
[TCA_CT_NAT_PORT_MIN] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
|
|
|
[TCA_CT_NAT_PORT_MAX] = { .type = NL_A_U16,
|
|
|
|
.optional = true, },
|
2022-10-23 09:27:10 +03:00
|
|
|
[TCA_CT_TM] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tcf_t),
|
|
|
|
.optional = true, },
|
2019-12-22 12:16:41 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *ct_attrs[ARRAY_SIZE(ct_policy)];
|
|
|
|
const struct nlattr *ct_parms;
|
|
|
|
struct tc_action *action;
|
|
|
|
const struct tc_ct *ct;
|
|
|
|
uint16_t ct_action = 0;
|
2022-10-23 09:27:10 +03:00
|
|
|
struct tcf_t tm;
|
2019-12-22 12:16:41 +02:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, ct_policy, ct_attrs,
|
|
|
|
ARRAY_SIZE(ct_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse ct action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ct_parms = ct_attrs[TCA_CT_PARMS];
|
|
|
|
ct = nl_attr_get_unspec(ct_parms, sizeof *ct);
|
|
|
|
|
|
|
|
if (ct_attrs[TCA_CT_ACTION]) {
|
|
|
|
ct_action = nl_attr_get_u16(ct_attrs[TCA_CT_ACTION]);
|
|
|
|
}
|
|
|
|
|
|
|
|
action = &flower->actions[flower->action_count++];
|
|
|
|
action->ct.clear = ct_action & TCA_CT_ACT_CLEAR;
|
|
|
|
if (!action->ct.clear) {
|
|
|
|
struct nlattr *zone = ct_attrs[TCA_CT_ZONE];
|
2019-12-22 12:16:42 +02:00
|
|
|
struct nlattr *mark = ct_attrs[TCA_CT_MARK];
|
|
|
|
struct nlattr *mark_mask = ct_attrs[TCA_CT_MARK_MASK];
|
|
|
|
struct nlattr *label = ct_attrs[TCA_CT_LABELS];
|
|
|
|
struct nlattr *label_mask = ct_attrs[TCA_CT_LABELS_MASK];
|
2019-12-22 12:16:41 +02:00
|
|
|
|
|
|
|
action->ct.commit = ct_action & TCA_CT_ACT_COMMIT;
|
|
|
|
action->ct.force = ct_action & TCA_CT_ACT_FORCE;
|
|
|
|
|
|
|
|
action->ct.zone = zone ? nl_attr_get_u16(zone) : 0;
|
2019-12-22 12:16:42 +02:00
|
|
|
action->ct.mark = mark ? nl_attr_get_u32(mark) : 0;
|
|
|
|
action->ct.mark_mask = mark_mask ? nl_attr_get_u32(mark_mask) : 0;
|
|
|
|
action->ct.label = label? nl_attr_get_u128(label) : OVS_U128_ZERO;
|
|
|
|
action->ct.label_mask = label_mask ?
|
|
|
|
nl_attr_get_u128(label_mask) : OVS_U128_ZERO;
|
2019-12-22 12:16:41 +02:00
|
|
|
|
2019-12-22 12:16:43 +02:00
|
|
|
if (ct_action & TCA_CT_ACT_NAT) {
|
|
|
|
struct nlattr *ipv4_min = ct_attrs[TCA_CT_NAT_IPV4_MIN];
|
|
|
|
struct nlattr *ipv4_max = ct_attrs[TCA_CT_NAT_IPV4_MAX];
|
|
|
|
struct nlattr *ipv6_min = ct_attrs[TCA_CT_NAT_IPV6_MIN];
|
|
|
|
struct nlattr *ipv6_max = ct_attrs[TCA_CT_NAT_IPV6_MAX];
|
|
|
|
struct nlattr *port_min = ct_attrs[TCA_CT_NAT_PORT_MIN];
|
|
|
|
struct nlattr *port_max = ct_attrs[TCA_CT_NAT_PORT_MAX];
|
|
|
|
|
|
|
|
action->ct.nat_type = TC_NAT_RESTORE;
|
|
|
|
if (ct_action & TCA_CT_ACT_NAT_SRC) {
|
|
|
|
action->ct.nat_type = TC_NAT_SRC;
|
|
|
|
} else if (ct_action & TCA_CT_ACT_NAT_DST) {
|
|
|
|
action->ct.nat_type = TC_NAT_DST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv4_min) {
|
|
|
|
action->ct.range.ip_family = AF_INET;
|
|
|
|
action->ct.range.ipv4.min = nl_attr_get_be32(ipv4_min);
|
|
|
|
if (ipv4_max) {
|
|
|
|
ovs_be32 addr = nl_attr_get_be32(ipv4_max);
|
|
|
|
|
2022-02-22 16:25:28 +01:00
|
|
|
if (action->ct.range.ipv4.min != addr) {
|
|
|
|
action->ct.range.ipv4.max = addr;
|
|
|
|
}
|
2019-12-22 12:16:43 +02:00
|
|
|
}
|
|
|
|
} else if (ipv6_min) {
|
|
|
|
action->ct.range.ip_family = AF_INET6;
|
|
|
|
action->ct.range.ipv6.min
|
|
|
|
= nl_attr_get_in6_addr(ipv6_min);
|
|
|
|
if (ipv6_max) {
|
|
|
|
struct in6_addr addr = nl_attr_get_in6_addr(ipv6_max);
|
|
|
|
|
2022-02-22 16:25:28 +01:00
|
|
|
if (!ipv6_addr_equals(&action->ct.range.ipv6.min, &addr)) {
|
|
|
|
action->ct.range.ipv6.max = addr;
|
|
|
|
}
|
2019-12-22 12:16:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port_min) {
|
|
|
|
action->ct.range.port.min = nl_attr_get_be16(port_min);
|
|
|
|
if (port_max) {
|
|
|
|
action->ct.range.port.max = nl_attr_get_be16(port_max);
|
2022-02-22 16:25:28 +01:00
|
|
|
if (action->ct.range.port.min ==
|
|
|
|
action->ct.range.port.max) {
|
|
|
|
action->ct.range.port.max = 0;
|
|
|
|
}
|
2019-12-22 12:16:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 12:16:41 +02:00
|
|
|
}
|
|
|
|
action->type = TC_ACT_CT;
|
|
|
|
|
2022-10-23 09:27:10 +03:00
|
|
|
if (ct_attrs[TCA_CT_TM]) {
|
|
|
|
memcpy(&tm, nl_attr_get_unspec(ct_attrs[TCA_CT_TM], sizeof tm),
|
|
|
|
sizeof tm);
|
|
|
|
nl_parse_tcf(&tm, flower);
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(ct->action, action);
|
2019-12-22 12:16:41 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static const struct nl_policy vlan_policy[] = {
|
|
|
|
[TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_vlan),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
[TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)];
|
|
|
|
const struct tc_vlan *v;
|
|
|
|
const struct nlattr *vlan_parms;
|
2018-04-10 14:18:08 +09:00
|
|
|
struct tc_action *action;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, vlan_policy, vlan_attrs,
|
|
|
|
ARRAY_SIZE(vlan_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse vlan action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
action = &flower->actions[flower->action_count++];
|
2017-06-13 18:03:27 +03:00
|
|
|
vlan_parms = vlan_attrs[TCA_VLAN_PARMS];
|
|
|
|
v = nl_attr_get_unspec(vlan_parms, sizeof *v);
|
|
|
|
if (v->v_action == TCA_VLAN_ACT_PUSH) {
|
2018-07-17 02:01:54 +00:00
|
|
|
struct nlattr *vlan_tpid = vlan_attrs[TCA_VLAN_PUSH_VLAN_PROTOCOL];
|
2017-06-13 18:03:27 +03:00
|
|
|
struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
|
|
|
|
struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
|
|
|
|
|
2018-07-25 11:06:50 -07:00
|
|
|
action->vlan.vlan_push_tpid = nl_attr_get_be16(vlan_tpid);
|
2018-04-10 14:18:08 +09:00
|
|
|
action->vlan.vlan_push_id = nl_attr_get_u16(vlan_id);
|
|
|
|
action->vlan.vlan_push_prio = vlan_prio ? nl_attr_get_u8(vlan_prio) : 0;
|
|
|
|
action->type = TC_ACT_VLAN_PUSH;
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (v->v_action == TCA_VLAN_ACT_POP) {
|
2018-04-10 14:18:08 +09:00
|
|
|
action->type = TC_ACT_VLAN_POP;
|
2017-06-13 18:03:27 +03:00
|
|
|
} else {
|
|
|
|
VLOG_ERR_RL(&error_rl, "unknown vlan action: %d, %d",
|
|
|
|
v->action, v->v_action);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
|
|
|
|
nl_parse_action_pc(v->action, action);
|
2017-06-13 18:03:27 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-30 12:05:15 +01:00
|
|
|
static const struct nl_policy mpls_policy[] = {
|
|
|
|
[TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_mpls),
|
|
|
|
.optional = false, },
|
|
|
|
[TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
|
2019-07-30 12:05:16 +01:00
|
|
|
[TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
|
|
|
|
[TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_MPLS_TTL] = { .type = NL_A_U8, .optional = true, },
|
|
|
|
[TCA_MPLS_BOS] = { .type = NL_A_U8, .optional = true, },
|
2019-07-30 12:05:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *mpls_attrs[ARRAY_SIZE(mpls_policy)];
|
|
|
|
const struct nlattr *mpls_parms;
|
|
|
|
struct nlattr *mpls_proto;
|
2019-07-30 12:05:16 +01:00
|
|
|
struct nlattr *mpls_label;
|
2019-07-30 12:05:15 +01:00
|
|
|
struct tc_action *action;
|
|
|
|
const struct tc_mpls *m;
|
2019-07-30 12:05:16 +01:00
|
|
|
struct nlattr *mpls_ttl;
|
|
|
|
struct nlattr *mpls_bos;
|
|
|
|
struct nlattr *mpls_tc;
|
2019-07-30 12:05:15 +01:00
|
|
|
|
|
|
|
if (!nl_parse_nested(options, mpls_policy, mpls_attrs,
|
|
|
|
ARRAY_SIZE(mpls_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse mpls action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
action = &flower->actions[flower->action_count++];
|
|
|
|
mpls_parms = mpls_attrs[TCA_MPLS_PARMS];
|
|
|
|
m = nl_attr_get_unspec(mpls_parms, sizeof *m);
|
|
|
|
|
|
|
|
switch (m->m_action) {
|
|
|
|
case TCA_MPLS_ACT_POP:
|
|
|
|
mpls_proto = mpls_attrs[TCA_MPLS_PROTO];
|
|
|
|
if (mpls_proto) {
|
|
|
|
action->mpls.proto = nl_attr_get_be16(mpls_proto);
|
|
|
|
}
|
|
|
|
action->type = TC_ACT_MPLS_POP;
|
|
|
|
break;
|
2019-07-30 12:05:16 +01:00
|
|
|
case TCA_MPLS_ACT_PUSH:
|
|
|
|
mpls_proto = mpls_attrs[TCA_MPLS_PROTO];
|
|
|
|
if (mpls_proto) {
|
|
|
|
action->mpls.proto = nl_attr_get_be16(mpls_proto);
|
|
|
|
}
|
|
|
|
mpls_label = mpls_attrs[TCA_MPLS_LABEL];
|
|
|
|
if (mpls_label) {
|
|
|
|
action->mpls.label = nl_attr_get_u32(mpls_label);
|
|
|
|
}
|
|
|
|
mpls_tc = mpls_attrs[TCA_MPLS_TC];
|
|
|
|
if (mpls_tc) {
|
|
|
|
action->mpls.tc = nl_attr_get_u8(mpls_tc);
|
|
|
|
}
|
|
|
|
mpls_ttl = mpls_attrs[TCA_MPLS_TTL];
|
|
|
|
if (mpls_ttl) {
|
|
|
|
action->mpls.ttl = nl_attr_get_u8(mpls_ttl);
|
|
|
|
}
|
|
|
|
mpls_bos = mpls_attrs[TCA_MPLS_BOS];
|
|
|
|
if (mpls_bos) {
|
|
|
|
action->mpls.bos = nl_attr_get_u8(mpls_bos);
|
|
|
|
}
|
|
|
|
action->type = TC_ACT_MPLS_PUSH;
|
|
|
|
break;
|
2019-07-30 12:05:17 +01:00
|
|
|
case TCA_MPLS_ACT_MODIFY:
|
|
|
|
mpls_label = mpls_attrs[TCA_MPLS_LABEL];
|
|
|
|
if (mpls_label) {
|
|
|
|
action->mpls.label = nl_attr_get_u32(mpls_label);
|
|
|
|
}
|
|
|
|
mpls_tc = mpls_attrs[TCA_MPLS_TC];
|
|
|
|
if (mpls_tc) {
|
|
|
|
action->mpls.tc = nl_attr_get_u8(mpls_tc);
|
|
|
|
}
|
|
|
|
mpls_ttl = mpls_attrs[TCA_MPLS_TTL];
|
|
|
|
if (mpls_ttl) {
|
|
|
|
action->mpls.ttl = nl_attr_get_u8(mpls_ttl);
|
|
|
|
}
|
|
|
|
mpls_bos = mpls_attrs[TCA_MPLS_BOS];
|
|
|
|
if (mpls_bos) {
|
|
|
|
action->mpls.bos = nl_attr_get_u8(mpls_bos);
|
|
|
|
}
|
|
|
|
action->type = TC_ACT_MPLS_SET;
|
|
|
|
break;
|
2019-07-30 12:05:15 +01:00
|
|
|
default:
|
|
|
|
VLOG_ERR_RL(&error_rl, "unknown mpls action: %d, %d",
|
|
|
|
m->action, m->m_action);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_parse_action_pc(m->action, action);
|
2019-07-30 12:05:15 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
static const struct nl_policy csum_policy[] = {
|
|
|
|
[TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC,
|
|
|
|
.min_len = sizeof(struct tc_csum),
|
|
|
|
.optional = false, },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
|
|
|
|
{
|
|
|
|
struct nlattr *csum_attrs[ARRAY_SIZE(csum_policy)];
|
|
|
|
const struct tc_csum *c;
|
|
|
|
const struct nlattr *csum_parms;
|
|
|
|
|
|
|
|
if (!nl_parse_nested(options, csum_policy, csum_attrs,
|
|
|
|
ARRAY_SIZE(csum_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse csum action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
csum_parms = csum_attrs[TCA_CSUM_PARMS];
|
|
|
|
c = nl_attr_get_unspec(csum_parms, sizeof *c);
|
|
|
|
|
|
|
|
/* sanity checks */
|
|
|
|
if (c->update_flags != flower->csum_update_flags) {
|
|
|
|
VLOG_WARN_RL(&error_rl,
|
|
|
|
"expected different act csum flags: 0x%x != 0x%x",
|
|
|
|
flower->csum_update_flags, c->update_flags);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
flower->csum_update_flags = 0; /* so we know csum was handled */
|
|
|
|
|
|
|
|
if (flower->needs_full_ip_proto_mask
|
|
|
|
&& flower->mask.ip_proto != UINT8_MAX) {
|
|
|
|
VLOG_WARN_RL(&error_rl, "expected full matching on flower ip_proto");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
/* The action_pc should be set on the previous action. */
|
|
|
|
if (flower->action_count < TCA_ACT_MAX_NUM) {
|
|
|
|
struct tc_action *action = &flower->actions[flower->action_count];
|
|
|
|
|
|
|
|
nl_parse_action_pc(c->action, action);
|
|
|
|
}
|
2017-11-21 14:40:38 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static const struct nl_policy act_policy[] = {
|
|
|
|
[TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, },
|
|
|
|
[TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, },
|
2020-06-04 13:47:01 +03:00
|
|
|
[TCA_ACT_OPTIONS] = { .type = NL_A_NESTED, .optional = true, },
|
2017-06-13 18:03:27 +03:00
|
|
|
[TCA_ACT_STATS] = { .type = NL_A_NESTED, .optional = false, },
|
|
|
|
};
|
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
static int
|
|
|
|
nl_parse_action_stats(struct nlattr *act_stats,
|
|
|
|
struct ovs_flow_stats *stats_sw,
|
|
|
|
struct ovs_flow_stats *stats_hw,
|
|
|
|
struct ovs_flow_stats *stats_dropped)
|
|
|
|
{
|
2023-01-05 15:24:53 -05:00
|
|
|
struct tc_flow_stats s_sw = {0}, s_hw = {0};
|
|
|
|
const struct gnet_stats_queue *qs = NULL;
|
|
|
|
uint16_t prev_type = __TCA_STATS_MAX;
|
|
|
|
const struct nlattr *nla;
|
|
|
|
unsigned int seen = 0;
|
|
|
|
size_t left;
|
2022-07-08 09:55:28 +00:00
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
/* Cannot use nl_parse_nested due to duplicate attributes. */
|
|
|
|
NL_NESTED_FOR_EACH (nla, left, act_stats) {
|
|
|
|
struct gnet_stats_basic stats_basic;
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
2022-07-08 09:55:28 +00:00
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
seen |= 1 << type;
|
2022-07-08 09:55:28 +00:00
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
switch (type) {
|
|
|
|
case TCA_STATS_BASIC:
|
|
|
|
memcpy(&stats_basic, nl_attr_get_unspec(nla, sizeof stats_basic),
|
|
|
|
sizeof stats_basic);
|
|
|
|
s_sw.n_packets = stats_basic.packets;
|
|
|
|
s_sw.n_bytes = stats_basic.bytes;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCA_STATS_BASIC_HW:
|
|
|
|
memcpy(&stats_basic, nl_attr_get_unspec(nla, sizeof stats_basic),
|
|
|
|
sizeof stats_basic);
|
|
|
|
s_hw.n_packets = stats_basic.packets;
|
|
|
|
s_hw.n_bytes = stats_basic.bytes;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCA_STATS_QUEUE:
|
|
|
|
qs = nl_attr_get_unspec(nla, sizeof *qs);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TCA_STATS_PKT64:
|
|
|
|
if (prev_type == TCA_STATS_BASIC) {
|
|
|
|
s_sw.n_packets = nl_attr_get_u64(nla);
|
|
|
|
} else if (prev_type == TCA_STATS_BASIC_HW) {
|
|
|
|
s_hw.n_packets = nl_attr_get_u64(nla);
|
|
|
|
} else {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev_type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(seen & (1 << TCA_STATS_BASIC))) {
|
|
|
|
goto err;
|
2022-07-08 09:55:28 +00:00
|
|
|
}
|
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
if (seen & (1 << TCA_STATS_BASIC_HW)) {
|
|
|
|
s_sw.n_packets = s_sw.n_packets - s_hw.n_packets;
|
|
|
|
s_sw.n_bytes = s_sw.n_bytes - s_hw.n_bytes;
|
|
|
|
|
|
|
|
if (s_hw.n_packets > get_32aligned_u64(&stats_hw->n_packets)) {
|
|
|
|
put_32aligned_u64(&stats_hw->n_packets, s_hw.n_packets);
|
|
|
|
put_32aligned_u64(&stats_hw->n_bytes, s_hw.n_bytes);
|
|
|
|
}
|
2022-07-08 09:55:28 +00:00
|
|
|
}
|
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
if (s_sw.n_packets > get_32aligned_u64(&stats_sw->n_packets)) {
|
|
|
|
put_32aligned_u64(&stats_sw->n_packets, s_sw.n_packets);
|
|
|
|
put_32aligned_u64(&stats_sw->n_bytes, s_sw.n_bytes);
|
2022-07-08 09:55:28 +00:00
|
|
|
}
|
|
|
|
|
2023-01-05 15:24:53 -05:00
|
|
|
if (stats_dropped && qs) {
|
2022-07-08 09:55:28 +00:00
|
|
|
put_32aligned_u64(&stats_dropped->n_packets, qs->drops);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2023-01-05 15:24:53 -05:00
|
|
|
|
|
|
|
err:
|
|
|
|
VLOG_ERR_RL(&error_rl, "Failed to parse action stats policy");
|
|
|
|
return EPROTO;
|
2022-07-08 09:55:28 +00:00
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static int
|
2020-06-04 13:47:01 +03:00
|
|
|
nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
|
2022-07-13 10:20:44 +02:00
|
|
|
bool terse, bool *csum)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct nlattr *act_options;
|
|
|
|
struct nlattr *act_cookie;
|
|
|
|
const char *act_kind;
|
|
|
|
struct nlattr *action_attrs[ARRAY_SIZE(act_policy)];
|
2018-03-12 14:58:46 +02:00
|
|
|
int err = 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (!nl_parse_nested(action, act_policy, action_attrs,
|
2020-06-04 13:47:01 +03:00
|
|
|
ARRAY_SIZE(act_policy)) ||
|
|
|
|
(!terse && !action_attrs[TCA_ACT_OPTIONS])) {
|
2017-06-13 18:03:27 +03:00
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse single action options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
*csum = false;
|
2017-06-13 18:03:27 +03:00
|
|
|
act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]);
|
|
|
|
act_options = action_attrs[TCA_ACT_OPTIONS];
|
|
|
|
act_cookie = action_attrs[TCA_ACT_COOKIE];
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
if (terse) {
|
|
|
|
/* Terse dump doesn't provide act options attribute. */
|
|
|
|
} else if (!strcmp(act_kind, "gact")) {
|
2019-12-22 12:16:40 +02:00
|
|
|
err = nl_parse_act_gact(act_options, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (!strcmp(act_kind, "mirred")) {
|
2018-03-12 14:58:46 +02:00
|
|
|
err = nl_parse_act_mirred(act_options, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (!strcmp(act_kind, "vlan")) {
|
2018-03-12 14:58:46 +02:00
|
|
|
err = nl_parse_act_vlan(act_options, flower);
|
2019-07-30 12:05:15 +01:00
|
|
|
} else if (!strcmp(act_kind, "mpls")) {
|
|
|
|
err = nl_parse_act_mpls(act_options, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (!strcmp(act_kind, "tunnel_key")) {
|
2018-03-12 14:58:46 +02:00
|
|
|
err = nl_parse_act_tunnel_key(act_options, flower);
|
2017-09-18 07:16:03 +03:00
|
|
|
} else if (!strcmp(act_kind, "pedit")) {
|
2018-03-12 14:58:46 +02:00
|
|
|
err = nl_parse_act_pedit(act_options, flower);
|
2017-09-18 07:16:03 +03:00
|
|
|
} else if (!strcmp(act_kind, "csum")) {
|
2017-11-21 14:40:38 +02:00
|
|
|
nl_parse_act_csum(act_options, flower);
|
2022-07-13 10:20:44 +02:00
|
|
|
*csum = true;
|
2019-04-09 15:36:12 +01:00
|
|
|
} else if (!strcmp(act_kind, "skbedit")) {
|
|
|
|
/* Added for TC rule only (not in OvS rule) so ignore. */
|
2019-12-22 12:16:41 +02:00
|
|
|
} else if (!strcmp(act_kind, "ct")) {
|
|
|
|
nl_parse_act_ct(act_options, flower);
|
2022-07-08 09:55:26 +00:00
|
|
|
} else if (!strcmp(act_kind, "police")) {
|
|
|
|
nl_parse_act_police(act_options, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else {
|
|
|
|
VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
|
2018-03-12 14:58:46 +02:00
|
|
|
err = EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return err;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (act_cookie) {
|
|
|
|
flower->act_cookie.data = nl_attr_get(act_cookie);
|
|
|
|
flower->act_cookie.len = nl_attr_get_size(act_cookie);
|
|
|
|
}
|
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
return nl_parse_action_stats(action_attrs[TCA_ACT_STATS],
|
|
|
|
&flower->stats_sw, &flower->stats_hw, NULL);
|
|
|
|
}
|
tc: Fix stats byte count on fragmented packets.
Fragmented packets with offset=0 are defragmented by tc act_ct, and
only when assembled pass to next action, in ovs offload case,
a goto action. Since stats are overwritten on each action dump,
only the stats for last action in the tc filter action priority
list is taken, the stats on the goto action, which count
only the assembled packets. See below for example.
Hardware updates just part of the actions (gact, ct, mirred) - those
that support stats_update() operation. Since datapath rules end
with either an output (mirred) or recirc/drop (both gact), tc rule
will at least have one action that supports it. For software packets,
the first action will have the max software packets count.
Tc dumps total packets (hw + sw) and hardware packets, then
software packets needs to be calculated from this (total - hw).
To fix the above, get hardware packets and calculate software packets
for each action, take the max of each set, then combine back
to get the total packets that went through software and hardware.
Example by running ping above MTU (ping <IP> -s 2000):
ct_state(-trk),recirc_id(0),...,ipv4(proto=1,frag=first),
packets:14, bytes:19544,..., actions:ct(zone=1),recirc(0x1)
ct_state(-trk),recirc_id(0),...,ipv4(proto=1,frag=later),
packets:14, bytes:28392,..., actions:ct(zone=1),recirc(0x1)
Second rule should have had bytes=14*<size of 'later' frag>, but instead
it's bytes=14*<size of assembled packets - size of 'first' + 'later'
frags>.
Fixes: 576126a931cd ("netdev-offload-tc: Add conntrack support")
Signed-off-by: Paul Blakey <paulb@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-11-07 14:12:35 +02:00
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
int
|
|
|
|
tc_parse_action_stats(struct nlattr *action, struct ovs_flow_stats *stats_sw,
|
|
|
|
struct ovs_flow_stats *stats_hw,
|
|
|
|
struct ovs_flow_stats *stats_dropped)
|
|
|
|
{
|
|
|
|
struct nlattr *action_attrs[ARRAY_SIZE(act_policy)];
|
tc: Fix stats byte count on fragmented packets.
Fragmented packets with offset=0 are defragmented by tc act_ct, and
only when assembled pass to next action, in ovs offload case,
a goto action. Since stats are overwritten on each action dump,
only the stats for last action in the tc filter action priority
list is taken, the stats on the goto action, which count
only the assembled packets. See below for example.
Hardware updates just part of the actions (gact, ct, mirred) - those
that support stats_update() operation. Since datapath rules end
with either an output (mirred) or recirc/drop (both gact), tc rule
will at least have one action that supports it. For software packets,
the first action will have the max software packets count.
Tc dumps total packets (hw + sw) and hardware packets, then
software packets needs to be calculated from this (total - hw).
To fix the above, get hardware packets and calculate software packets
for each action, take the max of each set, then combine back
to get the total packets that went through software and hardware.
Example by running ping above MTU (ping <IP> -s 2000):
ct_state(-trk),recirc_id(0),...,ipv4(proto=1,frag=first),
packets:14, bytes:19544,..., actions:ct(zone=1),recirc(0x1)
ct_state(-trk),recirc_id(0),...,ipv4(proto=1,frag=later),
packets:14, bytes:28392,..., actions:ct(zone=1),recirc(0x1)
Second rule should have had bytes=14*<size of 'later' frag>, but instead
it's bytes=14*<size of assembled packets - size of 'first' + 'later'
frags>.
Fixes: 576126a931cd ("netdev-offload-tc: Add conntrack support")
Signed-off-by: Paul Blakey <paulb@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-11-07 14:12:35 +02:00
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
if (!nl_parse_nested(action, act_policy, action_attrs,
|
|
|
|
ARRAY_SIZE(act_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "Failed to parse single action options");
|
|
|
|
return EPROTO;
|
lib/tc: only update the stats for non-empty counter
A packet with first frag and execute act_ct action.
The packet will stole by defrag. So the stats counter
for "gact action goto chain" will always 0. The openvswitch
update each action in order. So the flower stats finally
alway be zero. The rule will be delete adter max-idle time
even there are packet executing the action.
ovs-appctl dpctl/dump-flows
recirc_id(0),in_port(1),eth_type(0x0800),ipv4(dst=11.0.0.7,frag=first), packets:0, bytes:0, used:5.390s, actions:ct(zone=1,nat),recirc(0x4)
filter protocol ip pref 2 flower chain 0 handle 0x2
eth_type ipv4
dst_ip 1.1.1.1
ip_flags frag/firstfrag
skip_hw
not_in_hw
action order 1: ct zone 1 nat pipe
index 2 ref 1 bind 1 installed 11 sec used 1 sec
Action statistics:
Sent 15000 bytes 11 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
cookie e04106c2ac41769b278edaa9b5309960
action order 2: gact action goto chain 1
random type none pass val 0
index 2 ref 1 bind 1 installed 11 sec used 11 sec
Action statistics:
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
cookie e04106c2ac41769b278edaa9b5309960
Signed-off-by: wenxu <wenxu@ucloud.cn>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-29 17:31:18 +08:00
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2022-07-08 09:55:28 +00:00
|
|
|
return nl_parse_action_stats(action_attrs[TCA_ACT_STATS], stats_sw,
|
|
|
|
stats_hw, stats_dropped);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#define TCA_ACT_MIN_PRIO 1
|
|
|
|
|
|
|
|
static int
|
2020-06-04 13:47:01 +03:00
|
|
|
nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
|
|
|
|
bool terse)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
const struct nlattr *actions = attrs[TCA_FLOWER_ACT];
|
2019-10-16 11:37:14 +03:00
|
|
|
static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {};
|
2017-06-13 18:03:27 +03:00
|
|
|
struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
|
|
|
|
const int max_size = ARRAY_SIZE(actions_orders_policy);
|
2022-07-13 10:20:44 +02:00
|
|
|
int previous_action_count = 0;
|
|
|
|
bool need_jump_adjust = false;
|
|
|
|
int jump_adjust = 0;
|
|
|
|
bool csum = false;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
|
|
|
|
actions_orders_policy[i].type = NL_A_NESTED;
|
|
|
|
actions_orders_policy[i].optional = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nl_parse_nested(actions, actions_orders_policy, actions_orders,
|
|
|
|
ARRAY_SIZE(actions_orders_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse flower order of actions");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
|
|
|
|
if (actions_orders[i]) {
|
2018-04-10 14:18:08 +09:00
|
|
|
int err;
|
|
|
|
|
2019-10-16 11:37:14 +03:00
|
|
|
if (flower->action_count >= TCA_ACT_MAX_NUM) {
|
|
|
|
VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
|
2018-04-10 14:18:08 +09:00
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
err = nl_parse_single_action(actions_orders[i], flower, terse,
|
|
|
|
&csum);
|
|
|
|
|
|
|
|
if (flower->action_count == previous_action_count) {
|
|
|
|
|
|
|
|
struct tc_action *action;
|
|
|
|
|
|
|
|
/* We had no update on the TC action count, which means
|
|
|
|
* we had a none TC type action. So need to adjust existing
|
|
|
|
* jump offsets. */
|
|
|
|
jump_adjust++;
|
|
|
|
|
|
|
|
if (need_jump_adjust || (csum && flower->action_count > 0)) {
|
|
|
|
|
|
|
|
if (csum && flower->action_count > 0) {
|
|
|
|
/* The csum action is special as it might carry
|
|
|
|
* a jump count for the previous TC_ACT and therefore
|
|
|
|
* should be adjusted with jump_adjust as it got
|
|
|
|
* copied. */
|
|
|
|
action = &flower->actions[flower->action_count - 1];
|
|
|
|
if (action->jump_action
|
|
|
|
&& action->jump_action != JUMP_ACTION_STOP) {
|
|
|
|
action->jump_action -= (jump_adjust - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < flower->action_count; j++) {
|
|
|
|
action = &flower->actions[j];
|
|
|
|
|
|
|
|
if (action->type == TC_ACT_POLICE_MTU
|
|
|
|
&& action->police.result_jump != JUMP_ACTION_STOP
|
|
|
|
&& (action->police.result_jump - 1) >
|
|
|
|
flower->action_count) {
|
|
|
|
|
|
|
|
action->police.result_jump--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->jump_action
|
|
|
|
&& action->jump_action != JUMP_ACTION_STOP
|
|
|
|
&& (action->jump_action - 1) >
|
|
|
|
flower->action_count) {
|
|
|
|
|
|
|
|
action->jump_action--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct tc_action *action;
|
|
|
|
|
|
|
|
action = &flower->actions[previous_action_count];
|
|
|
|
if (action->type == TC_ACT_POLICE_MTU &&
|
|
|
|
action->police.result_jump != JUMP_ACTION_STOP) {
|
|
|
|
action->police.result_jump -= jump_adjust;
|
|
|
|
need_jump_adjust = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->jump_action
|
|
|
|
&& action->jump_action != JUMP_ACTION_STOP) {
|
|
|
|
action->jump_action -= jump_adjust;
|
|
|
|
need_jump_adjust = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
previous_action_count = flower->action_count;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
if (flower->csum_update_flags) {
|
|
|
|
VLOG_WARN_RL(&error_rl,
|
|
|
|
"expected act csum with flags: 0x%x",
|
|
|
|
flower->csum_update_flags);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2020-06-04 13:47:01 +03:00
|
|
|
nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower,
|
|
|
|
bool terse)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)];
|
2018-09-18 09:36:20 +01:00
|
|
|
int err;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
if (terse) {
|
|
|
|
if (!nl_parse_nested(nl_options, tca_flower_terse_policy,
|
|
|
|
attrs, ARRAY_SIZE(tca_flower_terse_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse flower classifier terse options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
goto skip_flower_opts;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (!nl_parse_nested(nl_options, tca_flower_policy,
|
|
|
|
attrs, ARRAY_SIZE(tca_flower_policy))) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse flower classifier options");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_parse_flower_eth(attrs, flower);
|
2020-06-05 21:17:29 +08:00
|
|
|
nl_parse_flower_arp(attrs, flower);
|
2018-09-02 14:07:49 +01:00
|
|
|
nl_parse_flower_mpls(attrs, flower);
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_parse_flower_vlan(attrs, flower);
|
|
|
|
nl_parse_flower_ip(attrs, flower);
|
2018-09-18 09:36:20 +01:00
|
|
|
err = nl_parse_flower_tunnel(attrs, flower);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
skip_flower_opts:
|
dpctl: Properly reflect a rule's offloaded to HW state
Previously, any rule that is offloaded via a netdev, not necessarily
to the HW, would be reported as "offloaded". This patch fixes this
misalignment, and introduces the 'dp' state, as follows:
rule is in HW via TC offload -> offloaded=yes dp:tc
rule is in not HW over TC DP -> offloaded=no dp:tc
rule is in not HW over OVS DP -> offloaded=no dp:ovs
To achieve this, the flows's 'offloaded' flag was encapsulated in a new
attrs struct, which contains the offloaded state of the flow and the
DP layer the flow is handled in, and instead of setting the flow's
'offloaded' state based solely on the type of dump it was acquired
via, for netdev flows it now sends the new attrs struct to be
collected along with the rest of the flow via the netdev, allowing
it to be set per flow.
For TC offloads, the offloaded state is set based on the 'in_hw' and
'not_in_hw' flags received from the TC as part of the flower. If no
such flag was received, due to lack of kernel support, it defaults
to true.
Signed-off-by: Gavi Teitz <gavi@mellanox.com>
Acked-by: Roi Dayan <roid@mellanox.com>
[simon: resolved conflict in lib/dpctl.man]
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2018-06-07 09:36:59 +03:00
|
|
|
nl_parse_flower_flags(attrs, flower);
|
2020-06-04 13:47:01 +03:00
|
|
|
return nl_parse_flower_actions(attrs, flower, terse);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2019-12-22 12:16:36 +02:00
|
|
|
parse_netlink_to_tc_flower(struct ofpbuf *reply, struct tcf_id *id,
|
2020-06-04 13:47:01 +03:00
|
|
|
struct tc_flower *flower, bool terse)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
2023-06-06 20:33:35 +02:00
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
|
|
|
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
|
|
|
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
2017-06-13 18:03:27 +03:00
|
|
|
struct nlattr *ta[ARRAY_SIZE(tca_policy)];
|
|
|
|
const char *kind;
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
if (!nlmsg || !tc) {
|
|
|
|
COVERAGE_INC(tc_netlink_malformed_reply);
|
2017-06-13 18:03:27 +03:00
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(flower, 0, sizeof *flower);
|
|
|
|
|
|
|
|
flower->key.eth_type = (OVS_FORCE ovs_be16) tc_get_minor(tc->tcm_info);
|
|
|
|
flower->mask.eth_type = OVS_BE16_MAX;
|
2019-12-22 12:16:36 +02:00
|
|
|
id->prio = tc_get_major(tc->tcm_info);
|
|
|
|
id->handle = tc->tcm_handle;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
if (id->prio == TC_RESERVED_PRIORITY_POLICE) {
|
2019-02-01 10:19:32 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
if (!id->handle) {
|
2017-06-13 18:03:27 +03:00
|
|
|
return EAGAIN;
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
if (!nl_policy_parse(&b, 0, tca_policy, ta, ARRAY_SIZE(ta))) {
|
2017-06-13 18:03:27 +03:00
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tca policy");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:40 +02:00
|
|
|
if (ta[TCA_CHAIN]) {
|
|
|
|
id->chain = nl_attr_get_u32(ta[TCA_CHAIN]);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
kind = nl_attr_get_string(ta[TCA_KIND]);
|
|
|
|
if (strcmp(kind, "flower")) {
|
2018-04-11 10:57:30 +03:00
|
|
|
VLOG_DBG_ONCE("Unsupported filter: %s", kind);
|
2017-06-13 18:03:27 +03:00
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
return nl_parse_flower_options(ta[TCA_OPTIONS], flower, terse);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2021-01-26 09:17:46 +02:00
|
|
|
int
|
|
|
|
parse_netlink_to_tc_chain(struct ofpbuf *reply, uint32_t *chain)
|
|
|
|
{
|
2023-06-06 20:33:35 +02:00
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
|
|
|
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
|
|
|
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
2021-01-26 09:17:46 +02:00
|
|
|
struct nlattr *ta[ARRAY_SIZE(tca_chain_policy)];
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
if (!nlmsg || !tc) {
|
|
|
|
COVERAGE_INC(tc_netlink_malformed_reply);
|
|
|
|
return EPROTO;
|
|
|
|
}
|
2021-01-26 09:17:46 +02:00
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
if (!nl_policy_parse(&b, 0, tca_chain_policy, ta, ARRAY_SIZE(ta))) {
|
2021-01-26 09:17:46 +02:00
|
|
|
VLOG_ERR_RL(&error_rl, "failed to parse tca chain policy");
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*chain = nl_attr_get_u32(ta[TCA_CHAIN]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
int
|
2020-06-04 13:47:01 +03:00
|
|
|
tc_dump_flower_start(struct tcf_id *id, struct nl_dump *dump, bool terse)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
request_from_tcf_id(id, 0, RTM_GETTFILTER, NLM_F_DUMP, &request);
|
2020-06-04 13:47:01 +03:00
|
|
|
if (terse) {
|
|
|
|
struct nla_bitfield32 dump_flags = { TCA_DUMP_FLAGS_TERSE,
|
|
|
|
TCA_DUMP_FLAGS_TERSE };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(&request, TCA_DUMP_FLAGS, &dump_flags,
|
|
|
|
sizeof dump_flags);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_dump_start(dump, NETLINK_ROUTE, &request);
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-26 09:17:46 +02:00
|
|
|
int
|
|
|
|
tc_dump_tc_chain_start(struct tcf_id *id, struct nl_dump *dump)
|
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
|
|
|
|
request_from_tcf_id(id, 0, RTM_GETCHAIN, NLM_F_DUMP, &request);
|
|
|
|
nl_dump_start(dump, NETLINK_ROUTE, &request);
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-08 03:06:27 +00:00
|
|
|
int
|
|
|
|
tc_dump_tc_action_start(char *name, struct nl_dump *dump)
|
|
|
|
{
|
|
|
|
size_t offset, root_offset;
|
|
|
|
struct ofpbuf request;
|
|
|
|
|
|
|
|
tc_make_action_request(RTM_GETACTION, NLM_F_DUMP, &request);
|
|
|
|
root_offset = nl_msg_start_nested(&request, TCA_ACT_TAB);
|
|
|
|
offset = nl_msg_start_nested(&request, 1);
|
|
|
|
nl_msg_put_string(&request, TCA_ACT_KIND, name);
|
|
|
|
nl_msg_end_nested(&request, offset);
|
|
|
|
nl_msg_end_nested(&request, root_offset);
|
|
|
|
|
|
|
|
nl_dump_start(dump, NETLINK_ROUTE, &request);
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
parse_netlink_to_tc_policer(struct ofpbuf *reply, uint32_t police_idx[])
|
|
|
|
{
|
|
|
|
static struct nl_policy actions_orders_policy[TCA_ACT_MAX_PRIO] = {};
|
2023-06-06 20:33:35 +02:00
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
2022-07-08 03:06:27 +00:00
|
|
|
struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
|
2023-06-06 20:33:35 +02:00
|
|
|
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
2022-07-08 03:06:27 +00:00
|
|
|
const int max_size = ARRAY_SIZE(actions_orders_policy);
|
2023-06-06 20:33:35 +02:00
|
|
|
struct tcamsg *tca = ofpbuf_try_pull(&b, sizeof *tca);
|
2022-07-08 03:06:27 +00:00
|
|
|
const struct nlattr *actions;
|
|
|
|
struct tc_flower flower;
|
|
|
|
int i, cnt = 0;
|
|
|
|
int err;
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
if (!nlmsg || !tca) {
|
|
|
|
COVERAGE_INC(tc_netlink_malformed_reply);
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
2022-07-08 03:06:27 +00:00
|
|
|
for (i = 0; i < max_size; i++) {
|
|
|
|
actions_orders_policy[i].type = NL_A_NESTED;
|
|
|
|
actions_orders_policy[i].optional = true;
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:33:35 +02:00
|
|
|
actions = nl_attr_find(&b, 0, TCA_ACT_TAB);
|
2022-07-08 03:06:27 +00:00
|
|
|
if (!actions || !nl_parse_nested(actions, actions_orders_policy,
|
|
|
|
actions_orders, max_size)) {
|
|
|
|
VLOG_ERR_RL(&error_rl,
|
|
|
|
"Failed to parse actions in netlink to policer");
|
|
|
|
return EPROTO;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
|
|
|
|
if (actions_orders[i]) {
|
2022-07-13 10:20:44 +02:00
|
|
|
bool csum;
|
|
|
|
|
2022-07-08 03:06:27 +00:00
|
|
|
memset(&flower, 0, sizeof(struct tc_flower));
|
2022-07-13 10:20:44 +02:00
|
|
|
err = nl_parse_single_action(actions_orders[i], &flower, false,
|
|
|
|
&csum);
|
2022-07-08 03:06:27 +00:00
|
|
|
if (err || flower.actions[0].type != TC_ACT_POLICE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (flower.actions[0].police.index) {
|
|
|
|
police_idx[cnt++] = flower.actions[0].police.index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
int
|
2023-01-27 12:16:36 +01:00
|
|
|
tc_del_filter(struct tcf_id *id, const char *kind)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
request_from_tcf_id(id, 0, RTM_DELTFILTER, NLM_F_ACK, &request);
|
2023-04-27 14:32:58 +03:00
|
|
|
if (kind) {
|
|
|
|
nl_msg_put_string(&request, TCA_KIND, kind);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
return tc_transact(&request, NULL);
|
|
|
|
}
|
|
|
|
|
2023-01-27 12:16:36 +01:00
|
|
|
int
|
|
|
|
tc_del_flower_filter(struct tcf_id *id)
|
|
|
|
{
|
|
|
|
return tc_del_filter(id, "flower");
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
int
|
2019-12-22 12:16:36 +02:00
|
|
|
tc_get_flower(struct tcf_id *id, struct tc_flower *flower)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
int error;
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
request_from_tcf_id(id, 0, RTM_GETTFILTER, NLM_F_ECHO, &request);
|
2023-01-27 12:16:36 +01:00
|
|
|
nl_msg_put_string(&request, TCA_KIND, "flower");
|
2017-06-13 18:03:27 +03:00
|
|
|
error = tc_transact(&request, &reply);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2020-06-04 13:47:01 +03:00
|
|
|
error = parse_netlink_to_tc_flower(reply, id, flower, false);
|
2017-06-13 18:03:27 +03:00
|
|
|
ofpbuf_delete(reply);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:30 +03:00
|
|
|
static int
|
|
|
|
tc_get_tc_cls_policy(enum tc_offload_policy policy)
|
|
|
|
{
|
|
|
|
if (policy == TC_POLICY_SKIP_HW) {
|
|
|
|
return TCA_CLS_FLAGS_SKIP_HW;
|
|
|
|
} else if (policy == TC_POLICY_SKIP_SW) {
|
|
|
|
return TCA_CLS_FLAGS_SKIP_SW;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static void
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags, uint32_t action_pc)
|
2017-09-18 07:16:03 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "csum");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_csum parm = { .action = action_pc,
|
2017-09-18 07:16:03 +03:00
|
|
|
.update_flags = flags };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_CSUM_PARMS, &parm, sizeof parm);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_pedit_key_ex *ex, uint32_t action_pc)
|
2017-09-18 07:16:03 +03:00
|
|
|
{
|
2017-11-21 14:40:39 +02:00
|
|
|
size_t ksize = sizeof *parm + parm->nkeys * sizeof(struct tc_pedit_key);
|
2017-09-18 07:16:03 +03:00
|
|
|
size_t offset, offset_keys_ex, offset_key;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "pedit");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
parm->action = action_pc;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_PEDIT_PARMS_EX, parm, ksize);
|
|
|
|
offset_keys_ex = nl_msg_start_nested(request, TCA_PEDIT_KEYS_EX);
|
|
|
|
for (i = 0; i < parm->nkeys; i++, ex++) {
|
|
|
|
offset_key = nl_msg_start_nested(request, TCA_PEDIT_KEY_EX);
|
|
|
|
nl_msg_put_u16(request, TCA_PEDIT_KEY_EX_HTYPE, ex->htype);
|
|
|
|
nl_msg_put_u16(request, TCA_PEDIT_KEY_EX_CMD, ex->cmd);
|
|
|
|
nl_msg_end_nested(request, offset_key);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset_keys_ex);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
2018-07-25 11:06:50 -07:00
|
|
|
nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
|
2022-07-13 10:20:44 +02:00
|
|
|
uint16_t vid, uint8_t prio, uint32_t action_pc)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_vlan parm = { .action = action_pc,
|
2017-06-13 18:03:27 +03:00
|
|
|
.v_action = TCA_VLAN_ACT_PUSH };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
|
2018-07-25 11:06:50 -07:00
|
|
|
nl_msg_put_be16(request, TCA_VLAN_PUSH_VLAN_PROTOCOL, tpid);
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_u16(request, TCA_VLAN_PUSH_VLAN_ID, vid);
|
|
|
|
nl_msg_put_u8(request, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_pop_vlan(struct ofpbuf *request, uint32_t action_pc)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_vlan parm = { .action = action_pc,
|
2017-06-13 18:03:27 +03:00
|
|
|
.v_action = TCA_VLAN_ACT_POP };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2019-07-30 12:05:15 +01:00
|
|
|
static void
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto,
|
|
|
|
uint32_t action_pc)
|
2019-07-30 12:05:15 +01:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_mpls parm = { .action = action_pc,
|
2019-07-30 12:05:15 +01:00
|
|
|
.m_action = TCA_MPLS_ACT_POP };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
|
|
|
|
nl_msg_put_be16(request, TCA_MPLS_PROTO, proto);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2019-07-30 12:05:16 +01:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
|
2022-07-13 10:20:44 +02:00
|
|
|
uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos,
|
|
|
|
uint32_t action_pc)
|
2019-07-30 12:05:16 +01:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_mpls parm = { .action = action_pc,
|
2019-07-30 12:05:16 +01:00
|
|
|
.m_action = TCA_MPLS_ACT_PUSH };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
|
|
|
|
nl_msg_put_be16(request, TCA_MPLS_PROTO, proto);
|
|
|
|
nl_msg_put_u32(request, TCA_MPLS_LABEL, label);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_TC, tc);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_TTL, ttl);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_BOS, bos);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2019-07-30 12:05:17 +01:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_set_mpls(struct ofpbuf *request, uint32_t label, uint8_t tc,
|
2022-07-13 10:20:44 +02:00
|
|
|
uint8_t ttl, uint8_t bos, uint32_t action_pc)
|
2019-07-30 12:05:17 +01:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_mpls parm = { .action = action_pc,
|
2019-07-30 12:05:17 +01:00
|
|
|
.m_action = TCA_MPLS_ACT_MODIFY };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
|
|
|
|
nl_msg_put_u32(request, TCA_MPLS_LABEL, label);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_TC, tc);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_TTL, ttl);
|
|
|
|
nl_msg_put_u8(request, TCA_MPLS_BOS, bos);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_tunnel_key_release(struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
|
|
|
struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
|
|
|
|
.t_action = TCA_TUNNEL_KEY_ACT_RELEASE };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:19 +01:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_tunnel_geneve_option(struct ofpbuf *request,
|
2022-11-04 15:06:03 +02:00
|
|
|
struct tun_metadata *tun_metadata)
|
2018-09-18 09:36:19 +01:00
|
|
|
{
|
|
|
|
const struct geneve_opt *opt;
|
|
|
|
size_t outer, inner;
|
|
|
|
int len, cnt = 0;
|
|
|
|
|
2022-11-04 15:06:03 +02:00
|
|
|
len = tun_metadata->present.len;
|
2018-09-18 09:36:19 +01:00
|
|
|
if (!len) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
outer = nl_msg_start_nested(request, TCA_TUNNEL_KEY_ENC_OPTS);
|
|
|
|
|
|
|
|
while (len) {
|
2022-11-04 15:06:03 +02:00
|
|
|
opt = &tun_metadata->opts.gnv[cnt];
|
2018-09-18 09:36:19 +01:00
|
|
|
inner = nl_msg_start_nested(request, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE);
|
|
|
|
|
|
|
|
nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS,
|
|
|
|
opt->opt_class);
|
|
|
|
nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE, opt->type);
|
|
|
|
nl_msg_put_unspec(request, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA, opt + 1,
|
|
|
|
opt->length * 4);
|
|
|
|
|
|
|
|
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
|
|
|
|
len -= sizeof(struct geneve_opt) + opt->length * 4;
|
|
|
|
|
|
|
|
nl_msg_end_nested(request, inner);
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_end_nested(request, outer);
|
|
|
|
}
|
|
|
|
|
2023-06-27 13:48:12 +03:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_tunnel_vxlan_opts(struct ofpbuf *request,
|
|
|
|
struct tc_action_encap *encap)
|
|
|
|
{
|
|
|
|
size_t outer, inner;
|
|
|
|
uint32_t gbp_raw;
|
|
|
|
|
|
|
|
if (!encap->gbp.id_present) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gbp_raw = odp_encode_gbp_raw(encap->gbp.flags,
|
|
|
|
encap->gbp.id);
|
|
|
|
outer = nl_msg_start_nested_with_flag(request, TCA_TUNNEL_KEY_ENC_OPTS);
|
|
|
|
inner = nl_msg_start_nested_with_flag(request,
|
|
|
|
TCA_TUNNEL_KEY_ENC_OPTS_VXLAN);
|
|
|
|
nl_msg_put_u32(request, TCA_TUNNEL_KEY_ENC_OPT_VXLAN_GBP, gbp_raw);
|
|
|
|
nl_msg_end_nested(request, inner);
|
|
|
|
nl_msg_end_nested(request, outer);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
2023-06-27 13:48:11 +03:00
|
|
|
nl_msg_put_act_tunnel_key_set(struct ofpbuf *request,
|
|
|
|
struct tc_action_encap *encap,
|
|
|
|
uint32_t action_pc)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_tunnel_key tun = { .action = action_pc,
|
2017-06-13 18:03:27 +03:00
|
|
|
.t_action = TCA_TUNNEL_KEY_ACT_SET };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
|
|
|
|
|
2023-06-27 13:48:11 +03:00
|
|
|
ovs_be32 id32 = be64_to_be32(encap->id);
|
|
|
|
if (encap->id_present) {
|
2019-01-17 17:41:36 +02:00
|
|
|
nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_KEY_ID, id32);
|
|
|
|
}
|
2023-06-27 13:48:11 +03:00
|
|
|
if (encap->ipv4.ipv4_dst) {
|
|
|
|
nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_SRC,
|
|
|
|
encap->ipv4.ipv4_src);
|
|
|
|
nl_msg_put_be32(request, TCA_TUNNEL_KEY_ENC_IPV4_DST,
|
|
|
|
encap->ipv4.ipv4_dst);
|
|
|
|
} else if (ipv6_addr_is_set(&encap->ipv6.ipv6_dst)) {
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_DST,
|
2023-06-27 13:48:11 +03:00
|
|
|
&encap->ipv6.ipv6_dst);
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_in6_addr(request, TCA_TUNNEL_KEY_ENC_IPV6_SRC,
|
2023-06-27 13:48:11 +03:00
|
|
|
&encap->ipv6.ipv6_src);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2023-06-27 13:48:11 +03:00
|
|
|
if (encap->tos) {
|
|
|
|
nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_TOS, encap->tos);
|
2018-07-31 13:40:38 +03:00
|
|
|
}
|
2023-06-27 13:48:11 +03:00
|
|
|
if (encap->ttl) {
|
|
|
|
nl_msg_put_u8(request, TCA_TUNNEL_KEY_ENC_TTL, encap->ttl);
|
2018-07-31 13:40:38 +03:00
|
|
|
}
|
2023-06-27 13:48:11 +03:00
|
|
|
if (encap->tp_dst) {
|
|
|
|
nl_msg_put_be16(request, TCA_TUNNEL_KEY_ENC_DST_PORT,
|
|
|
|
encap->tp_dst);
|
2018-12-26 09:34:35 +02:00
|
|
|
}
|
2024-10-14 14:09:04 +02:00
|
|
|
if (encap->dont_fragment) {
|
|
|
|
nl_msg_put_flag(request, TCA_TUNNEL_KEY_NO_FRAG);
|
|
|
|
}
|
2023-06-27 13:48:12 +03:00
|
|
|
nl_msg_put_act_tunnel_vxlan_opts(request, encap);
|
2023-06-27 13:48:11 +03:00
|
|
|
nl_msg_put_act_tunnel_geneve_option(request, &encap->data);
|
|
|
|
nl_msg_put_u8(request, TCA_TUNNEL_KEY_NO_CSUM, encap->no_csum);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-22 12:16:40 +02:00
|
|
|
nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "gact");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
|
|
|
struct tc_gact p = { .action = TC_ACT_SHOT };
|
|
|
|
|
2019-12-22 12:16:40 +02:00
|
|
|
if (chain) {
|
|
|
|
p.action = TC_ACT_GOTO_CHAIN | chain;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_unspec(request, TCA_GACT_PARMS, &p, sizeof p);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2022-07-08 03:06:28 +00:00
|
|
|
static void
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_police_index(struct ofpbuf *request, uint32_t police_idx,
|
|
|
|
uint32_t action_pc)
|
2022-07-08 03:06:28 +00:00
|
|
|
{
|
|
|
|
struct tc_police police;
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
memset(&police, 0, sizeof police);
|
|
|
|
police.index = police_idx;
|
2022-07-13 10:20:44 +02:00
|
|
|
police.action = action_pc;
|
2022-07-08 03:06:28 +00:00
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "police");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
nl_msg_put_unspec(request, TCA_POLICE_TBF, &police, sizeof police);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_u32(request, TCA_POLICE_RESULT, action_pc);
|
2022-07-08 03:06:28 +00:00
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:41 +02:00
|
|
|
static void
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action,
|
|
|
|
uint32_t action_pc)
|
2019-12-22 12:16:41 +02:00
|
|
|
{
|
|
|
|
uint16_t ct_action = 0;
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "ct");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
|
|
|
|
{
|
|
|
|
struct tc_ct ct = {
|
2022-07-13 10:20:44 +02:00
|
|
|
.action = action_pc,
|
2019-12-22 12:16:41 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!action->ct.clear) {
|
|
|
|
if (action->ct.zone) {
|
|
|
|
nl_msg_put_u16(request, TCA_CT_ZONE, action->ct.zone);
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:42 +02:00
|
|
|
if (!is_all_zeros(&action->ct.label_mask,
|
|
|
|
sizeof action->ct.label_mask)) {
|
|
|
|
nl_msg_put_u128(request, TCA_CT_LABELS,
|
|
|
|
action->ct.label);
|
|
|
|
nl_msg_put_u128(request, TCA_CT_LABELS_MASK,
|
|
|
|
action->ct.label_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->ct.mark_mask) {
|
|
|
|
nl_msg_put_u32(request, TCA_CT_MARK,
|
|
|
|
action->ct.mark);
|
|
|
|
nl_msg_put_u32(request, TCA_CT_MARK_MASK,
|
|
|
|
action->ct.mark_mask);
|
|
|
|
}
|
|
|
|
|
2019-12-22 12:16:41 +02:00
|
|
|
if (action->ct.commit) {
|
|
|
|
ct_action = TCA_CT_ACT_COMMIT;
|
|
|
|
if (action->ct.force) {
|
|
|
|
ct_action |= TCA_CT_ACT_FORCE;
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 12:16:43 +02:00
|
|
|
|
|
|
|
if (action->ct.nat_type) {
|
|
|
|
ct_action |= TCA_CT_ACT_NAT;
|
|
|
|
|
|
|
|
if (action->ct.nat_type == TC_NAT_SRC) {
|
|
|
|
ct_action |= TCA_CT_ACT_NAT_SRC;
|
|
|
|
} else if (action->ct.nat_type == TC_NAT_DST) {
|
|
|
|
ct_action |= TCA_CT_ACT_NAT_DST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->ct.range.ip_family == AF_INET) {
|
|
|
|
nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MIN,
|
|
|
|
action->ct.range.ipv4.min);
|
|
|
|
if (action->ct.range.ipv4.max) {
|
|
|
|
nl_msg_put_be32(request, TCA_CT_NAT_IPV4_MAX,
|
|
|
|
action->ct.range.ipv4.max);
|
|
|
|
}
|
|
|
|
} else if (action->ct.range.ip_family == AF_INET6) {
|
|
|
|
|
|
|
|
nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MIN,
|
|
|
|
&action->ct.range.ipv6.min);
|
2020-06-02 21:50:23 +08:00
|
|
|
if (ipv6_addr_is_set(&action->ct.range.ipv6.max)) {
|
2019-12-22 12:16:43 +02:00
|
|
|
nl_msg_put_in6_addr(request, TCA_CT_NAT_IPV6_MAX,
|
|
|
|
&action->ct.range.ipv6.max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->ct.range.port.min) {
|
|
|
|
nl_msg_put_be16(request, TCA_CT_NAT_PORT_MIN,
|
|
|
|
action->ct.range.port.min);
|
|
|
|
if (action->ct.range.port.max) {
|
|
|
|
nl_msg_put_be16(request, TCA_CT_NAT_PORT_MAX,
|
|
|
|
action->ct.range.port.max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 12:16:41 +02:00
|
|
|
} else {
|
|
|
|
ct_action = TCA_CT_ACT_CLEAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_put_u16(request, TCA_CT_ACTION, ct_action);
|
|
|
|
nl_msg_put_unspec(request, TCA_CT_PARMS, &ct, sizeof ct);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:36:12 +01:00
|
|
|
static void
|
|
|
|
nl_msg_put_act_skbedit_to_host(struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "skbedit");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
|
|
|
struct tc_skbedit s = { .action = TC_ACT_PIPE };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_SKBEDIT_PARMS, &s, sizeof s);
|
|
|
|
nl_msg_put_be16(request, TCA_SKBEDIT_PTYPE, PACKET_HOST);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
2018-04-10 14:18:09 +09:00
|
|
|
nl_msg_put_act_mirred(struct ofpbuf *request, int ifindex, int action,
|
|
|
|
int eaction)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "mirred");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
2018-04-10 14:18:09 +09:00
|
|
|
struct tc_mirred m = { .action = action,
|
|
|
|
.eaction = eaction,
|
2017-06-13 18:03:27 +03:00
|
|
|
.ifindex = ifindex };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_MIRRED_PARMS, &m, sizeof m);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) {
|
|
|
|
if (ck->len) {
|
|
|
|
nl_msg_put_unspec(request, TCA_ACT_COOKIE, ck->data, ck->len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-04 18:34:49 +02:00
|
|
|
static inline void
|
|
|
|
nl_msg_put_act_flags(struct ofpbuf *request) {
|
|
|
|
struct nla_bitfield32 act_flags = { TCA_ACT_FLAGS_NO_PERCPU_STATS,
|
|
|
|
TCA_ACT_FLAGS_NO_PERCPU_STATS };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_ACT_FLAGS, &act_flags, sizeof act_flags);
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
/* Given flower, a key_to_pedit map entry, calculates the rest,
|
|
|
|
* where:
|
|
|
|
*
|
|
|
|
* mask, data - pointers of where read the first word of flower->key/mask.
|
|
|
|
* current_offset - which offset to use for the first pedit action.
|
|
|
|
* cnt - max pedits actions to use.
|
|
|
|
* first_word_mask/last_word_mask - the mask to use for the first/last read
|
|
|
|
* (as we read entire words). */
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
2022-02-20 16:47:36 +02:00
|
|
|
calc_offsets(struct tc_action *action, struct flower_key_to_pedit *m,
|
2019-01-28 12:29:07 +00:00
|
|
|
int *cur_offset, int *cnt, ovs_be32 *last_word_mask,
|
|
|
|
ovs_be32 *first_word_mask, ovs_be32 **mask, ovs_be32 **data)
|
2017-09-18 07:16:03 +03:00
|
|
|
{
|
|
|
|
int start_offset, max_offset, total_size;
|
|
|
|
int diff, right_zero_bits, left_zero_bits;
|
2022-02-20 16:47:36 +02:00
|
|
|
char *rewrite_key = (void *) &action->rewrite.key;
|
|
|
|
char *rewrite_mask = (void *) &action->rewrite.mask;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
max_offset = m->offset + m->size;
|
|
|
|
start_offset = ROUND_DOWN(m->offset, 4);
|
|
|
|
diff = m->offset - start_offset;
|
|
|
|
total_size = max_offset - start_offset;
|
2019-01-28 12:29:08 +00:00
|
|
|
right_zero_bits = 8 * (4 - ((max_offset % 4) ? : 4));
|
2017-09-18 07:16:03 +03:00
|
|
|
left_zero_bits = 8 * (m->offset - start_offset);
|
|
|
|
|
|
|
|
*cur_offset = start_offset;
|
|
|
|
*cnt = (total_size / 4) + (total_size % 4 ? 1 : 0);
|
2019-01-28 12:29:07 +00:00
|
|
|
*last_word_mask = htonl(UINT32_MAX << right_zero_bits);
|
|
|
|
*first_word_mask = htonl(UINT32_MAX >> left_zero_bits);
|
2017-09-18 07:16:03 +03:00
|
|
|
*data = (void *) (rewrite_key + m->flower_offset - diff);
|
|
|
|
*mask = (void *) (rewrite_mask + m->flower_offset - diff);
|
|
|
|
}
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
static inline int
|
2017-09-18 07:16:03 +03:00
|
|
|
csum_update_flag(struct tc_flower *flower,
|
|
|
|
enum pedit_header_type htype) {
|
2017-11-21 14:40:38 +02:00
|
|
|
/* Explictily specifiy the csum flags so HW can return EOPNOTSUPP
|
|
|
|
* if it doesn't support a checksum recalculation of some headers.
|
|
|
|
* And since OVS allows a flow such as
|
|
|
|
* eth(dst=<mac>),eth_type(0x0800) actions=set(ipv4(src=<new_ip>))
|
|
|
|
* we need to force a more specific flow as this can, for example,
|
|
|
|
* need a recalculation of icmp checksum if the packet that passes
|
2024-12-10 13:42:27 +01:00
|
|
|
* is ICMPv6 and tcp checksum if its tcp.
|
|
|
|
*
|
|
|
|
* This section of the code must be kept in sync with the pre-check
|
|
|
|
* function in netdev-offload-tc.c, tc_will_add_l4_checksum(). */
|
2017-11-21 14:40:38 +02:00
|
|
|
|
|
|
|
switch (htype) {
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
|
2017-09-18 07:16:03 +03:00
|
|
|
flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_IPV4HDR;
|
2017-11-26 16:26:41 -08:00
|
|
|
/* Fall through. */
|
2017-11-21 14:40:38 +02:00
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
|
2017-09-18 07:16:03 +03:00
|
|
|
if (flower->key.ip_proto == IPPROTO_TCP) {
|
2017-11-21 14:40:38 +02:00
|
|
|
flower->needs_full_ip_proto_mask = true;
|
2017-09-18 07:16:03 +03:00
|
|
|
flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_TCP;
|
|
|
|
} else if (flower->key.ip_proto == IPPROTO_UDP) {
|
2017-11-21 14:40:38 +02:00
|
|
|
flower->needs_full_ip_proto_mask = true;
|
2017-09-18 07:16:03 +03:00
|
|
|
flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
|
2023-10-07 11:49:42 +08:00
|
|
|
} else if (flower->key.ip_proto == IPPROTO_ICMP ||
|
|
|
|
flower->key.ip_proto == IPPROTO_IGMP ||
|
2023-10-07 11:49:46 +08:00
|
|
|
flower->key.ip_proto == IPPROTO_SCTP ||
|
|
|
|
flower->key.ip_proto == IPPROTO_IPIP ||
|
|
|
|
flower->key.ip_proto == IPPROTO_GRE) {
|
2018-04-25 08:09:08 +00:00
|
|
|
flower->needs_full_ip_proto_mask = true;
|
|
|
|
} else if (flower->key.ip_proto == IPPROTO_ICMPV6) {
|
2017-11-21 14:40:38 +02:00
|
|
|
flower->needs_full_ip_proto_mask = true;
|
2017-09-18 07:16:03 +03:00
|
|
|
flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_ICMP;
|
2023-10-07 11:49:42 +08:00
|
|
|
} else if (flower->key.ip_proto == IPPROTO_UDPLITE) {
|
|
|
|
flower->needs_full_ip_proto_mask = true;
|
|
|
|
flower->csum_update_flags |= TCA_CSUM_UPDATE_FLAG_UDPLITE;
|
2017-11-21 14:40:38 +02:00
|
|
|
} else {
|
|
|
|
VLOG_WARN_RL(&error_rl,
|
|
|
|
"can't offload rewrite of IP/IPV6 with ip_proto: %d",
|
|
|
|
flower->key.ip_proto);
|
|
|
|
break;
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
2017-11-26 16:26:41 -08:00
|
|
|
/* Fall through. */
|
2017-11-21 14:40:38 +02:00
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
|
|
|
|
return 0; /* success */
|
|
|
|
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
|
|
|
|
case __PEDIT_HDR_TYPE_MAX:
|
|
|
|
default:
|
|
|
|
break;
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
2017-11-21 14:40:38 +02:00
|
|
|
|
|
|
|
return EOPNOTSUPP;
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
static bool
|
|
|
|
rewrite_pedits_need_csum_update(struct tc_action *action)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) {
|
|
|
|
struct flower_key_to_pedit *m = &flower_pedit_map[i];
|
|
|
|
ovs_be32 *mask, *data, first_word_mask, last_word_mask;
|
|
|
|
int cnt = 0, cur_offset = 0;
|
|
|
|
|
|
|
|
if (!m->size) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask,
|
|
|
|
&first_word_mask, &mask, &data);
|
|
|
|
|
|
|
|
for (j = 0; j < cnt; j++, mask++) {
|
|
|
|
ovs_be32 mask_word = get_unaligned_be32(mask);
|
|
|
|
|
|
|
|
if (j == 0) {
|
|
|
|
mask_word &= first_word_mask;
|
|
|
|
}
|
|
|
|
if (j == cnt - 1) {
|
|
|
|
mask_word &= last_word_mask;
|
|
|
|
}
|
|
|
|
if (!mask_word) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m->htype) {
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
|
|
|
|
return true;
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
|
|
|
|
case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
|
|
|
|
case __PEDIT_HDR_TYPE_MAX:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static int
|
|
|
|
nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
|
2022-02-20 16:47:36 +02:00
|
|
|
struct tc_flower *flower,
|
2022-07-13 10:20:44 +02:00
|
|
|
struct tc_action *action,
|
|
|
|
uint32_t action_pc)
|
2017-09-18 07:16:03 +03:00
|
|
|
{
|
2024-04-26 19:44:52 +02:00
|
|
|
union {
|
2017-09-18 07:16:03 +03:00
|
|
|
struct tc_pedit sel;
|
2024-04-26 19:44:52 +02:00
|
|
|
uint8_t buffer[sizeof(struct tc_pedit)
|
|
|
|
+ MAX_PEDIT_OFFSETS * sizeof(struct tc_pedit_key)];
|
|
|
|
} sel;
|
|
|
|
struct tc_pedit_key_ex keys_ex[MAX_PEDIT_OFFSETS];
|
2017-11-21 14:40:38 +02:00
|
|
|
int i, j, err;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
2024-04-26 19:44:52 +02:00
|
|
|
memset(&sel, 0, sizeof sel);
|
|
|
|
memset(keys_ex, 0, sizeof keys_ex);
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) {
|
|
|
|
struct flower_key_to_pedit *m = &flower_pedit_map[i];
|
|
|
|
struct tc_pedit_key *pedit_key = NULL;
|
|
|
|
struct tc_pedit_key_ex *pedit_key_ex = NULL;
|
2019-01-28 12:29:07 +00:00
|
|
|
ovs_be32 *mask, *data, first_word_mask, last_word_mask;
|
2017-09-18 07:16:03 +03:00
|
|
|
int cnt = 0, cur_offset = 0;
|
|
|
|
|
|
|
|
if (!m->size) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-02-20 16:47:36 +02:00
|
|
|
calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask,
|
2017-09-18 07:16:03 +03:00
|
|
|
&first_word_mask, &mask, &data);
|
|
|
|
|
|
|
|
for (j = 0; j < cnt; j++, mask++, data++, cur_offset += 4) {
|
2022-07-13 16:35:31 +02:00
|
|
|
ovs_be32 mask_word = get_unaligned_be32(mask);
|
|
|
|
ovs_be32 data_word = get_unaligned_be32(data);
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
if (j == 0) {
|
|
|
|
mask_word &= first_word_mask;
|
|
|
|
}
|
|
|
|
if (j == cnt - 1) {
|
|
|
|
mask_word &= last_word_mask;
|
|
|
|
}
|
|
|
|
if (!mask_word) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (sel.sel.nkeys == MAX_PEDIT_OFFSETS) {
|
|
|
|
VLOG_WARN_RL(&error_rl, "reached too many pedit offsets: %d",
|
|
|
|
MAX_PEDIT_OFFSETS);
|
|
|
|
return EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2024-04-26 19:44:52 +02:00
|
|
|
pedit_key = &sel.sel.keys[sel.sel.nkeys];
|
|
|
|
pedit_key_ex = &keys_ex[sel.sel.nkeys];
|
2017-09-18 07:16:03 +03:00
|
|
|
pedit_key_ex->cmd = TCA_PEDIT_KEY_EX_CMD_SET;
|
|
|
|
pedit_key_ex->htype = m->htype;
|
|
|
|
pedit_key->off = cur_offset;
|
2019-01-28 12:29:10 +00:00
|
|
|
mask_word = htonl(ntohl(mask_word) >> m->boundary_shift);
|
|
|
|
data_word = htonl(ntohl(data_word) >> m->boundary_shift);
|
2017-09-18 07:16:03 +03:00
|
|
|
pedit_key->mask = ~mask_word;
|
2019-01-28 12:29:10 +00:00
|
|
|
pedit_key->val = data_word & mask_word;
|
2017-09-18 07:16:03 +03:00
|
|
|
sel.sel.nkeys++;
|
2017-11-21 14:40:38 +02:00
|
|
|
|
|
|
|
err = csum_update_flag(flower, m->htype);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flower->needs_full_ip_proto_mask) {
|
|
|
|
flower->mask.ip_proto = UINT8_MAX;
|
|
|
|
}
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
|
|
|
}
|
2024-04-26 19:44:52 +02:00
|
|
|
nl_msg_put_act_pedit(request, &sel.sel, keys_ex,
|
2022-07-13 10:20:44 +02:00
|
|
|
flower->csum_update_flags ? TC_ACT_PIPE : action_pc);
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
tc: Fix incorrect TC rule for decap+encap datapath flow.
A datapath flow generated for traffic from vxlan port to another vxlan port
looks like this:
tunnel(tun_id=0x65,src=10.10.11.3,dst=10.10.11.2,ttl=0/0,tp_dst=4789,flags(+key)),
...,in_port(vxlan_sys_4789),...,
actions:set(tunnel(tun_id=0x66,src=10.10.12.2,dst=10.10.12.3,tp_dst=4789,flags(key))),
vxlan_sys_4789
The generated TC rule with explicit tunnel key unset action added after
tunnel key set action, which is wrong.
filter protocol ip pref 7 flower chain 0 handle 0x1
dst_mac fa:16:3e:2a:4e:23
eth_type ipv4
ip_tos 0x0/3
enc_dst_ip 10.10.11.2
enc_src_ip 10.10.11.3
enc_key_id 101
enc_dst_port 4789
ip_flags nofrag
not_in_hw
action order 1: tunnel_key set
src_ip 10.10.12.2
dst_ip 10.10.12.3
key_id 102
dst_port 4789
nocsum pipe
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 2: tunnel_key unset pipe
index 2 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 3: mirred (Egress Redirect to device vxlan_sys_4789) stolen
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
cookie e0c82bfd504b701428b00db6b08db3b2
Fix it by also adding the the tunnel key unset action before the tunnel
key set action and not only before output port.
Fixes: 7c53bd7839d8 ("tc: Move tunnel_key unset action before output ports")
Signed-off-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Paul Blakey <paulb@nvidia.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-12-02 14:38:32 +02:00
|
|
|
static void
|
|
|
|
nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
|
|
|
|
{
|
|
|
|
size_t act_offset;
|
|
|
|
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index);
|
|
|
|
nl_msg_put_act_tunnel_key_release(request);
|
|
|
|
nl_msg_put_act_flags(request);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
|
2022-02-20 16:47:36 +02:00
|
|
|
/* Aggregates all previous successive pedit actions csum_update_flags
|
|
|
|
* to flower->csum_update_flags. Only append one csum action to the
|
|
|
|
* last pedit action. */
|
|
|
|
static void
|
|
|
|
nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
|
2022-07-13 10:20:44 +02:00
|
|
|
uint32_t action_pc, uint16_t *act_index)
|
2022-02-20 16:47:36 +02:00
|
|
|
{
|
|
|
|
size_t act_offset;
|
|
|
|
|
|
|
|
/* No pedit actions or processed already. */
|
|
|
|
if (!flower->csum_update_flags) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
act_offset = nl_msg_start_nested(request, (*act_index)++);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_csum(request, flower->csum_update_flags, action_pc);
|
2022-02-20 16:47:36 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
|
|
|
|
/* Clear it. So we can have another series of pedit actions. */
|
|
|
|
flower->csum_update_flags = 0;
|
|
|
|
}
|
|
|
|
|
2022-07-13 10:20:44 +02:00
|
|
|
static int
|
|
|
|
get_action_index_for_tc_actions(struct tc_flower *flower, uint16_t act_index,
|
|
|
|
struct tc_action *action, int action_count,
|
|
|
|
bool tunnel_key_released)
|
|
|
|
{
|
|
|
|
bool need_csum = false;
|
|
|
|
|
|
|
|
if (action_count < 0) {
|
|
|
|
/* Only forward jumps are supported */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < action_count; i++, action++) {
|
|
|
|
if (action->type != TC_ACT_PEDIT && need_csum) {
|
|
|
|
need_csum = false;
|
|
|
|
act_index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (action->type) {
|
|
|
|
|
|
|
|
case TC_ACT_OUTPUT:
|
|
|
|
if (!tunnel_key_released && flower->tunnel) {
|
|
|
|
act_index++;
|
|
|
|
tunnel_key_released = true;
|
|
|
|
}
|
|
|
|
if (action->out.ingress) {
|
|
|
|
act_index++;
|
|
|
|
}
|
|
|
|
act_index++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TC_ACT_ENCAP:
|
|
|
|
if (!tunnel_key_released && flower->tunnel) {
|
|
|
|
act_index++;
|
|
|
|
tunnel_key_released = true;
|
|
|
|
}
|
|
|
|
act_index++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TC_ACT_PEDIT:
|
|
|
|
if (!need_csum) {
|
|
|
|
need_csum = rewrite_pedits_need_csum_update(action);
|
|
|
|
}
|
|
|
|
if (i == (action_count - 1) && need_csum) {
|
|
|
|
need_csum = false;
|
|
|
|
act_index++;
|
|
|
|
}
|
|
|
|
act_index++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TC_ACT_POLICE:
|
|
|
|
case TC_ACT_POLICE_MTU:
|
|
|
|
case TC_ACT_VLAN_POP:
|
|
|
|
case TC_ACT_VLAN_PUSH:
|
|
|
|
case TC_ACT_MPLS_POP:
|
|
|
|
case TC_ACT_MPLS_PUSH:
|
|
|
|
case TC_ACT_MPLS_SET:
|
|
|
|
case TC_ACT_GOTO:
|
|
|
|
case TC_ACT_CT:
|
|
|
|
/* Increase act_index by one if we are sure this type of action
|
|
|
|
* will only add one tc action in the kernel. */
|
|
|
|
act_index++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* If we can't determine how many tc actions will be added by the
|
|
|
|
* kernel return -EOPNOTSUPP.
|
|
|
|
*
|
|
|
|
* Please do NOT add a default case. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return act_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
nl_msg_put_act_police_mtu(struct ofpbuf *request, struct tc_flower *flower,
|
|
|
|
struct tc_action *action, uint32_t action_pc,
|
|
|
|
int action_index, uint16_t act_index, bool released)
|
|
|
|
{
|
|
|
|
uint32_t tc_action;
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
if (action->police.result_jump != JUMP_ACTION_STOP) {
|
|
|
|
int jump_index;
|
|
|
|
int action_count = action->police.result_jump - action_index - 1;
|
|
|
|
|
|
|
|
jump_index = get_action_index_for_tc_actions(flower,
|
|
|
|
act_index - 1,
|
|
|
|
action + 1,
|
|
|
|
action_count,
|
|
|
|
released);
|
|
|
|
if (jump_index < 0) {
|
|
|
|
return -jump_index;
|
|
|
|
}
|
|
|
|
tc_action = TC_ACT_JUMP | (jump_index & TC_ACT_EXT_VAL_MASK);
|
|
|
|
} else {
|
|
|
|
tc_action = TC_ACT_STOLEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_put_string(request, TCA_ACT_KIND, "police");
|
|
|
|
offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
|
|
|
|
{
|
|
|
|
struct tc_police p = { .action = action_pc,
|
|
|
|
.mtu = action->police.mtu };
|
|
|
|
|
|
|
|
nl_msg_put_unspec(request, TCA_POLICE_TBF, &p, sizeof p);
|
|
|
|
|
|
|
|
/* The value in jump_action is the total number of TC_ACT_*
|
|
|
|
* we need to jump, not the actual number of TCA_ACT_KIND
|
|
|
|
* (act_index) actions. As certain TC_ACT_* actions can be
|
|
|
|
* translated into multiple TCA_ACT_KIND ones.
|
|
|
|
*
|
|
|
|
* See nl_msg_put_flower_acts() below for more details. */
|
|
|
|
nl_msg_put_u32(request, TCA_POLICE_RESULT, tc_action);
|
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static int
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
|
|
|
|
{
|
2019-12-22 12:16:39 +02:00
|
|
|
bool ingress, released = false;
|
2017-06-13 18:03:27 +03:00
|
|
|
size_t offset;
|
|
|
|
size_t act_offset;
|
2018-04-10 14:18:08 +09:00
|
|
|
uint16_t act_index = 1;
|
|
|
|
struct tc_action *action;
|
|
|
|
int i, ifindex = 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
|
|
|
|
{
|
2017-09-18 07:16:03 +03:00
|
|
|
int error;
|
2022-07-13 10:20:44 +02:00
|
|
|
uint32_t prev_action_pc = TC_ACT_PIPE;
|
2017-09-18 07:16:03 +03:00
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
action = flower->actions;
|
|
|
|
for (i = 0; i < flower->action_count; i++, action++) {
|
2022-07-13 10:20:44 +02:00
|
|
|
uint32_t action_pc; /* Programmatic Control */
|
|
|
|
|
|
|
|
if (!action->jump_action) {
|
2022-10-23 09:27:10 +03:00
|
|
|
if (i == flower->action_count - 1) {
|
|
|
|
action_pc = TC_ACT_SHOT;
|
|
|
|
} else {
|
|
|
|
action_pc = TC_ACT_PIPE;
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
} else if (action->jump_action == JUMP_ACTION_STOP) {
|
|
|
|
action_pc = TC_ACT_STOLEN;
|
|
|
|
} else {
|
|
|
|
/* The value in jump_action is the total number of TC_ACT_*
|
|
|
|
* we need to jump, not the actual number of TCA_ACT_KIND
|
|
|
|
* (act_index) actions. As certain TC_ACT_* actions can be
|
|
|
|
* translated into multiple TCA_ACT_KIND ones.
|
|
|
|
*
|
|
|
|
* If we can determine the number of actual actions being
|
|
|
|
* inserted we will update the count, if not we will return
|
|
|
|
* -EOPNOTSUPP.
|
|
|
|
*/
|
|
|
|
int jump_index;
|
|
|
|
int act_index_start = act_index - 1;
|
|
|
|
int action_count = (action->jump_action &
|
|
|
|
TC_ACT_EXT_VAL_MASK) - i;
|
|
|
|
|
|
|
|
if (flower->csum_update_flags &&
|
|
|
|
(action->type != TC_ACT_PEDIT
|
|
|
|
|| prev_action_pc & TC_ACT_JUMP)) {
|
|
|
|
act_index_start++;
|
|
|
|
}
|
|
|
|
|
|
|
|
jump_index = get_action_index_for_tc_actions(flower,
|
|
|
|
act_index_start,
|
|
|
|
action,
|
|
|
|
action_count,
|
|
|
|
released);
|
|
|
|
if (jump_index < 0) {
|
|
|
|
return -jump_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
action_pc = TC_ACT_JUMP | jump_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (action->type != TC_ACT_PEDIT || prev_action_pc & TC_ACT_JUMP) {
|
|
|
|
nl_msg_put_csum_act(request, flower, prev_action_pc,
|
|
|
|
&act_index);
|
2022-02-20 16:47:36 +02:00
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
switch (action->type) {
|
|
|
|
case TC_ACT_PEDIT: {
|
2017-11-21 14:40:42 +02:00
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2022-02-20 16:47:36 +02:00
|
|
|
error = nl_msg_put_flower_rewrite_pedits(request, flower,
|
2022-07-13 10:20:44 +02:00
|
|
|
action, action_pc);
|
2018-04-10 14:18:08 +09:00
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2017-11-21 14:40:42 +02:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
2018-04-10 14:18:08 +09:00
|
|
|
|
2022-02-20 16:47:36 +02:00
|
|
|
if (i == flower->action_count - 1) {
|
|
|
|
/* If this is the last action check csum calc again. */
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_csum_act(request, flower, action_pc,
|
|
|
|
&act_index);
|
2018-04-10 14:18:08 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TC_ACT_ENCAP: {
|
tc: Fix incorrect TC rule for decap+encap datapath flow.
A datapath flow generated for traffic from vxlan port to another vxlan port
looks like this:
tunnel(tun_id=0x65,src=10.10.11.3,dst=10.10.11.2,ttl=0/0,tp_dst=4789,flags(+key)),
...,in_port(vxlan_sys_4789),...,
actions:set(tunnel(tun_id=0x66,src=10.10.12.2,dst=10.10.12.3,tp_dst=4789,flags(key))),
vxlan_sys_4789
The generated TC rule with explicit tunnel key unset action added after
tunnel key set action, which is wrong.
filter protocol ip pref 7 flower chain 0 handle 0x1
dst_mac fa:16:3e:2a:4e:23
eth_type ipv4
ip_tos 0x0/3
enc_dst_ip 10.10.11.2
enc_src_ip 10.10.11.3
enc_key_id 101
enc_dst_port 4789
ip_flags nofrag
not_in_hw
action order 1: tunnel_key set
src_ip 10.10.12.2
dst_ip 10.10.12.3
key_id 102
dst_port 4789
nocsum pipe
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 2: tunnel_key unset pipe
index 2 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 3: mirred (Egress Redirect to device vxlan_sys_4789) stolen
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
cookie e0c82bfd504b701428b00db6b08db3b2
Fix it by also adding the the tunnel key unset action before the tunnel
key set action and not only before output port.
Fixes: 7c53bd7839d8 ("tc: Move tunnel_key unset action before output ports")
Signed-off-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Paul Blakey <paulb@nvidia.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-12-02 14:38:32 +02:00
|
|
|
if (!released && flower->tunnel) {
|
|
|
|
nl_msg_put_flower_acts_release(request, act_index++);
|
|
|
|
released = true;
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2023-06-27 13:48:11 +03:00
|
|
|
nl_msg_put_act_tunnel_key_set(request, &action->encap,
|
2022-07-13 10:20:44 +02:00
|
|
|
action_pc);
|
2019-11-04 18:34:49 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TC_ACT_VLAN_POP: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_pop_vlan(request, action_pc);
|
2019-11-04 18:34:49 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TC_ACT_VLAN_PUSH: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
nl_msg_put_act_push_vlan(request,
|
2018-07-17 02:01:54 +00:00
|
|
|
action->vlan.vlan_push_tpid,
|
2018-04-10 14:18:08 +09:00
|
|
|
action->vlan.vlan_push_id,
|
2022-07-13 10:20:44 +02:00
|
|
|
action->vlan.vlan_push_prio,
|
|
|
|
action_pc);
|
2019-11-04 18:34:49 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2019-07-30 12:05:15 +01:00
|
|
|
case TC_ACT_MPLS_POP: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_pop_mpls(request, action->mpls.proto,
|
|
|
|
action_pc);
|
2019-07-30 12:05:15 +01:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2019-07-30 12:05:16 +01:00
|
|
|
case TC_ACT_MPLS_PUSH: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
nl_msg_put_act_push_mpls(request, action->mpls.proto,
|
|
|
|
action->mpls.label, action->mpls.tc,
|
2022-07-13 10:20:44 +02:00
|
|
|
action->mpls.ttl, action->mpls.bos,
|
|
|
|
action_pc);
|
2019-07-30 12:05:16 +01:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2019-07-30 12:05:17 +01:00
|
|
|
case TC_ACT_MPLS_SET: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
nl_msg_put_act_set_mpls(request, action->mpls.label,
|
|
|
|
action->mpls.tc, action->mpls.ttl,
|
2022-07-13 10:20:44 +02:00
|
|
|
action->mpls.bos, action_pc);
|
2019-07-30 12:05:17 +01:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2018-04-10 14:18:08 +09:00
|
|
|
case TC_ACT_OUTPUT: {
|
2019-12-22 12:16:39 +02:00
|
|
|
if (!released && flower->tunnel) {
|
tc: Fix incorrect TC rule for decap+encap datapath flow.
A datapath flow generated for traffic from vxlan port to another vxlan port
looks like this:
tunnel(tun_id=0x65,src=10.10.11.3,dst=10.10.11.2,ttl=0/0,tp_dst=4789,flags(+key)),
...,in_port(vxlan_sys_4789),...,
actions:set(tunnel(tun_id=0x66,src=10.10.12.2,dst=10.10.12.3,tp_dst=4789,flags(key))),
vxlan_sys_4789
The generated TC rule with explicit tunnel key unset action added after
tunnel key set action, which is wrong.
filter protocol ip pref 7 flower chain 0 handle 0x1
dst_mac fa:16:3e:2a:4e:23
eth_type ipv4
ip_tos 0x0/3
enc_dst_ip 10.10.11.2
enc_src_ip 10.10.11.3
enc_key_id 101
enc_dst_port 4789
ip_flags nofrag
not_in_hw
action order 1: tunnel_key set
src_ip 10.10.12.2
dst_ip 10.10.12.3
key_id 102
dst_port 4789
nocsum pipe
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 2: tunnel_key unset pipe
index 2 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
action order 3: mirred (Egress Redirect to device vxlan_sys_4789) stolen
index 1 ref 1 bind 1 installed 568 sec used 0 sec
Action statistics:
Sent 46620 bytes 555 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
cookie e0c82bfd504b701428b00db6b08db3b2
Fix it by also adding the the tunnel key unset action before the tunnel
key set action and not only before output port.
Fixes: 7c53bd7839d8 ("tc: Move tunnel_key unset action before output ports")
Signed-off-by: Roi Dayan <roid@nvidia.com>
Reviewed-by: Paul Blakey <paulb@nvidia.com>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2021-12-02 14:38:32 +02:00
|
|
|
nl_msg_put_flower_acts_release(request, act_index++);
|
2019-12-22 12:16:39 +02:00
|
|
|
released = true;
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:36:12 +01:00
|
|
|
ingress = action->out.ingress;
|
|
|
|
ifindex = action->out.ifindex_out;
|
2018-04-10 14:18:08 +09:00
|
|
|
if (ifindex < 1) {
|
|
|
|
VLOG_ERR_RL(&error_rl, "%s: invalid ifindex: %d, type: %d",
|
|
|
|
__func__, ifindex, action->type);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
2019-04-09 15:36:12 +01:00
|
|
|
|
|
|
|
if (ingress) {
|
|
|
|
/* If redirecting to ingress (internal port) ensure
|
|
|
|
* pkt_type on skb is set to PACKET_HOST. */
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
nl_msg_put_act_skbedit_to_host(request);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:18:08 +09:00
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2018-04-10 14:18:09 +09:00
|
|
|
if (i == flower->action_count - 1) {
|
2019-04-09 15:36:12 +01:00
|
|
|
if (ingress) {
|
|
|
|
nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN,
|
|
|
|
TCA_INGRESS_REDIR);
|
|
|
|
} else {
|
|
|
|
nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN,
|
|
|
|
TCA_EGRESS_REDIR);
|
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
action->jump_action = JUMP_ACTION_STOP;
|
2018-04-10 14:18:09 +09:00
|
|
|
} else {
|
2019-04-09 15:36:12 +01:00
|
|
|
if (ingress) {
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_mirred(request, ifindex, action_pc,
|
2019-04-09 15:36:12 +01:00
|
|
|
TCA_INGRESS_MIRROR);
|
|
|
|
} else {
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_mirred(request, ifindex, action_pc,
|
2019-04-09 15:36:12 +01:00
|
|
|
TCA_EGRESS_MIRROR);
|
|
|
|
}
|
2018-04-10 14:18:09 +09:00
|
|
|
}
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_put_act_cookie(request, &flower->act_cookie);
|
2019-11-04 18:34:49 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2019-12-22 12:16:40 +02:00
|
|
|
case TC_ACT_GOTO: {
|
|
|
|
if (released) {
|
|
|
|
/* We don't support tunnel release + output + goto
|
|
|
|
* for now, as next chain by default will try and match
|
|
|
|
* the tunnel metadata that was released/unset.
|
|
|
|
*
|
|
|
|
* This will happen with tunnel + mirror ports.
|
|
|
|
*/
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
nl_msg_put_act_gact(request, action->chain);
|
|
|
|
nl_msg_put_act_cookie(request, &flower->act_cookie);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2019-12-22 12:16:41 +02:00
|
|
|
case TC_ACT_CT: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_ct(request, action, action_pc);
|
2019-12-22 12:16:41 +02:00
|
|
|
nl_msg_put_act_cookie(request, &flower->act_cookie);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
|
|
|
}
|
|
|
|
break;
|
2022-07-08 09:55:26 +00:00
|
|
|
case TC_ACT_POLICE: {
|
2022-07-08 03:06:28 +00:00
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2022-07-13 10:20:44 +02:00
|
|
|
nl_msg_put_act_police_index(request, action->police.index,
|
|
|
|
action_pc);
|
2022-07-08 03:06:28 +00:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
2022-07-08 09:55:26 +00:00
|
|
|
}
|
|
|
|
break;
|
2022-07-13 10:20:44 +02:00
|
|
|
case TC_ACT_POLICE_MTU: {
|
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
|
|
|
if (nl_msg_put_act_police_mtu(request, flower, action,
|
|
|
|
action_pc, i, act_index,
|
|
|
|
released)) {
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
nl_msg_put_act_cookie(request, &flower->act_cookie);
|
|
|
|
nl_msg_put_act_flags(request);
|
|
|
|
nl_msg_end_nested(request, act_offset);
|
2017-11-21 14:40:42 +02:00
|
|
|
}
|
2022-07-13 10:20:44 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
prev_action_pc = action_pc;
|
2017-09-18 07:16:03 +03:00
|
|
|
}
|
2018-04-10 14:18:08 +09:00
|
|
|
}
|
2019-12-22 12:16:40 +02:00
|
|
|
|
|
|
|
if (!flower->action_count) {
|
2018-04-10 14:18:08 +09:00
|
|
|
act_offset = nl_msg_start_nested(request, act_index++);
|
2019-12-22 12:16:40 +02:00
|
|
|
nl_msg_put_act_gact(request, 0);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_put_act_cookie(request, &flower->act_cookie);
|
2019-11-04 18:34:49 +02:00
|
|
|
nl_msg_put_act_flags(request);
|
2018-04-10 14:18:08 +09:00
|
|
|
nl_msg_end_nested(request, act_offset);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
nl_msg_end_nested(request, offset);
|
2017-09-18 07:16:03 +03:00
|
|
|
|
|
|
|
return 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type,
|
|
|
|
uint16_t mask_type, const void *data,
|
|
|
|
const void *mask_data, size_t len)
|
|
|
|
{
|
|
|
|
if (mask_type != TCA_FLOWER_UNSPEC) {
|
|
|
|
if (is_all_zeros(mask_data, len)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nl_msg_put_unspec(request, mask_type, mask_data, len);
|
|
|
|
}
|
|
|
|
nl_msg_put_unspec(request, type, data, len);
|
|
|
|
}
|
|
|
|
|
2018-09-18 09:36:20 +01:00
|
|
|
static void
|
2023-06-27 13:48:10 +03:00
|
|
|
nl_msg_put_flower_geneve(struct ofpbuf *request,
|
|
|
|
const struct tc_flower_tunnel *tunnel)
|
2018-09-18 09:36:20 +01:00
|
|
|
{
|
2023-06-27 13:48:10 +03:00
|
|
|
const struct tun_metadata *metadata = &tunnel->metadata;
|
|
|
|
const struct geneve_opt *opt;
|
2018-09-18 09:36:20 +01:00
|
|
|
int len, cnt = 0;
|
2023-06-27 13:48:10 +03:00
|
|
|
size_t offset;
|
2018-09-18 09:36:20 +01:00
|
|
|
|
2022-11-04 15:06:03 +02:00
|
|
|
len = metadata->present.len;
|
2018-09-18 09:36:20 +01:00
|
|
|
while (len) {
|
2022-11-04 15:06:03 +02:00
|
|
|
opt = &metadata->opts.gnv[cnt];
|
2023-06-27 13:48:10 +03:00
|
|
|
offset = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
|
2018-09-18 09:36:20 +01:00
|
|
|
|
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
|
|
|
|
opt->opt_class);
|
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, opt->type);
|
|
|
|
nl_msg_put_unspec(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, opt + 1,
|
|
|
|
opt->length * 4);
|
|
|
|
|
|
|
|
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
|
|
|
|
len -= sizeof(struct geneve_opt) + opt->length * 4;
|
|
|
|
|
2023-06-27 13:48:10 +03:00
|
|
|
nl_msg_end_nested(request, offset);
|
2018-09-18 09:36:20 +01:00
|
|
|
}
|
2023-06-27 13:48:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl_msg_put_flower_vxlan_tun_opts(struct ofpbuf *request,
|
|
|
|
const struct tc_flower_tunnel *tunnel)
|
|
|
|
{
|
|
|
|
uint32_t gbp_raw;
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
if (!tunnel->gbp.id_present) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gbp_raw = odp_encode_gbp_raw(tunnel->gbp.flags, tunnel->gbp.id);
|
|
|
|
offset = nl_msg_start_nested_with_flag(request,
|
|
|
|
TCA_FLOWER_KEY_ENC_OPTS_VXLAN);
|
|
|
|
nl_msg_put_u32(request, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, gbp_raw);
|
|
|
|
nl_msg_end_nested(request, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
|
|
|
|
struct tc_flower_tunnel *tunnel)
|
|
|
|
{
|
|
|
|
size_t outer;
|
|
|
|
|
|
|
|
if (!tunnel->metadata.present.len && !tunnel->gbp.id_present) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
outer = nl_msg_start_nested(request, type);
|
|
|
|
nl_msg_put_flower_geneve(request, tunnel);
|
|
|
|
nl_msg_put_flower_vxlan_tun_opts(request, tunnel);
|
2018-09-18 09:36:20 +01:00
|
|
|
nl_msg_end_nested(request, outer);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
static void
|
|
|
|
nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
|
|
|
|
{
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
ovs_be32 ipv4_src_mask = flower->mask.tunnel.ipv4.ipv4_src;
|
|
|
|
ovs_be32 ipv4_dst_mask = flower->mask.tunnel.ipv4.ipv4_dst;
|
2018-09-06 13:52:25 +03:00
|
|
|
ovs_be32 ipv4_src = flower->key.tunnel.ipv4.ipv4_src;
|
|
|
|
ovs_be32 ipv4_dst = flower->key.tunnel.ipv4.ipv4_dst;
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
struct in6_addr *ipv6_src_mask = &flower->mask.tunnel.ipv6.ipv6_src;
|
|
|
|
struct in6_addr *ipv6_dst_mask = &flower->mask.tunnel.ipv6.ipv6_dst;
|
2018-09-06 13:52:25 +03:00
|
|
|
struct in6_addr *ipv6_src = &flower->key.tunnel.ipv6.ipv6_src;
|
|
|
|
struct in6_addr *ipv6_dst = &flower->key.tunnel.ipv6.ipv6_dst;
|
|
|
|
ovs_be32 id = be64_to_be32(flower->key.tunnel.id);
|
2024-10-14 14:09:02 +02:00
|
|
|
ovs_be32 enc_flags = htonl(flower->key.tunnel.tc_enc_flags);
|
2022-08-14 16:46:03 +02:00
|
|
|
ovs_be16 tp_src = flower->key.tunnel.tp_src;
|
2022-08-14 16:46:00 +02:00
|
|
|
ovs_be16 tp_dst = flower->key.tunnel.tp_dst;
|
2018-09-06 13:52:25 +03:00
|
|
|
uint8_t tos = flower->key.tunnel.tos;
|
|
|
|
uint8_t ttl = flower->key.tunnel.ttl;
|
2018-09-06 13:52:26 +03:00
|
|
|
uint8_t tos_mask = flower->mask.tunnel.tos;
|
|
|
|
uint8_t ttl_mask = flower->mask.tunnel.ttl;
|
2019-01-17 17:41:36 +02:00
|
|
|
ovs_be64 id_mask = flower->mask.tunnel.id;
|
2024-10-14 14:09:02 +02:00
|
|
|
ovs_be32 enc_flags_mask = htonl(flower->mask.tunnel.tc_enc_flags);
|
2022-08-14 16:46:03 +02:00
|
|
|
ovs_be16 tp_src_mask = flower->mask.tunnel.tp_src;
|
2022-08-14 16:46:00 +02:00
|
|
|
ovs_be16 tp_dst_mask = flower->mask.tunnel.tp_dst;
|
2017-06-13 18:03:27 +03:00
|
|
|
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
if (ipv4_dst_mask || ipv4_src_mask) {
|
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
|
|
|
|
ipv4_dst_mask);
|
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
|
|
|
|
ipv4_src_mask);
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_DST, ipv4_dst);
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_IPV4_SRC, ipv4_src);
|
|
|
|
} else if (ipv6_addr_is_set(ipv6_dst_mask) ||
|
|
|
|
ipv6_addr_is_set(ipv6_src_mask)) {
|
|
|
|
nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
|
|
|
|
ipv6_dst_mask);
|
|
|
|
nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
|
|
|
|
ipv6_src_mask);
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_DST, ipv6_dst);
|
netdev-offload-tc: Allow to match the IP and port mask of tunnel
This patch allows users to offload the TC flower rules with
tunnel mask. This patch allows masked match of the following,
where previously supported an exact match was supported:
* Remote (dst) tunnel endpoint address
* Local (src) tunnel endpoint address
* Remote (dst) tunnel endpoint UDP port
And also allows masked match of the following, where previously
no match was supported:
* Local (src) tunnel endpoint UDP port
In some case, mask is useful as wildcards. For example, DDOS,
in that case, we don’t want to allow specified hosts IPs or
only source Ports to access the targeted host. For example:
$ ovs-appctl dpctl/add-flow "tunnel(dst=2.2.2.100,src=2.2.2.0/255.255.255.0,tp_dst=4789),\
recirc_id(0),in_port(3),eth(),eth_type(0x0800),ipv4()" ""
$ tc filter show dev vxlan_sys_4789 ingress
...
eth_type ipv4
enc_dst_ip 2.2.2.100
enc_src_ip 2.2.2.0/24
enc_dst_port 4789
enc_ttl 64
in_hw in_hw_count 2
action order 1: gact action drop
...
Signed-off-by: Tonghao Zhang <xiangxia.m.yue@gmail.com>
Acked-by: Roi Dayan <roid@mellanox.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
2020-06-02 21:50:24 +08:00
|
|
|
nl_msg_put_in6_addr(request, TCA_FLOWER_KEY_ENC_IPV6_SRC, ipv6_src);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2018-09-06 13:52:26 +03:00
|
|
|
if (tos_mask) {
|
2018-07-31 13:40:39 +03:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TOS, tos);
|
2018-09-06 13:52:26 +03:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TOS_MASK, tos_mask);
|
2018-07-31 13:40:39 +03:00
|
|
|
}
|
2018-09-06 13:52:26 +03:00
|
|
|
if (ttl_mask) {
|
2018-07-31 13:40:39 +03:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL, ttl);
|
2018-09-06 13:52:26 +03:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_IP_TTL_MASK, ttl_mask);
|
2018-07-31 13:40:39 +03:00
|
|
|
}
|
2022-08-14 16:46:03 +02:00
|
|
|
if (tp_src_mask) {
|
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, tp_src);
|
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,
|
|
|
|
tp_src_mask);
|
|
|
|
}
|
2022-08-14 16:46:00 +02:00
|
|
|
if (tp_dst_mask) {
|
2018-12-26 09:34:35 +02:00
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, tp_dst);
|
2022-08-14 16:46:00 +02:00
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,
|
|
|
|
tp_dst_mask);
|
2018-12-26 09:34:35 +02:00
|
|
|
}
|
2024-10-14 14:09:02 +02:00
|
|
|
if (enc_flags_mask) {
|
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_FLAGS, enc_flags);
|
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_FLAGS_MASK,
|
|
|
|
enc_flags_mask);
|
|
|
|
}
|
2019-01-17 17:41:36 +02:00
|
|
|
if (id_mask) {
|
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id);
|
|
|
|
}
|
2018-09-18 09:36:20 +01:00
|
|
|
nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS,
|
2023-06-27 13:48:06 +03:00
|
|
|
&flower->key.tunnel);
|
2018-09-18 09:36:20 +01:00
|
|
|
nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS_MASK,
|
2023-06-27 13:48:06 +03:00
|
|
|
&flower->mask.tunnel);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2017-08-03 16:38:23 +03:00
|
|
|
#define FLOWER_PUT_MASKED_VALUE(member, type) \
|
|
|
|
nl_msg_put_masked_value(request, type, type##_MASK, &flower->key.member, \
|
|
|
|
&flower->mask.member, sizeof flower->key.member)
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
static int
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_flower_options(struct ofpbuf *request, struct tc_flower *flower)
|
|
|
|
{
|
2017-09-18 07:16:03 +03:00
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
uint16_t host_eth_type = ntohs(flower->key.eth_type);
|
2018-07-17 02:01:55 +00:00
|
|
|
bool is_vlan = eth_type_vlan(flower->key.eth_type);
|
2018-07-17 02:01:57 +00:00
|
|
|
bool is_qinq = is_vlan && eth_type_vlan(flower->key.encap_eth_type[0]);
|
2018-09-02 14:07:49 +01:00
|
|
|
bool is_mpls = eth_type_mpls(flower->key.eth_type);
|
2020-08-04 09:37:21 +03:00
|
|
|
enum tc_offload_policy policy = flower->tc_policy;
|
2017-09-18 07:16:03 +03:00
|
|
|
int err;
|
|
|
|
|
2017-11-21 14:40:38 +02:00
|
|
|
/* need to parse acts first as some acts require changing the matching
|
|
|
|
* see csum_update_flag() */
|
2017-09-18 07:16:03 +03:00
|
|
|
err = nl_msg_put_flower_acts(request, flower);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
if (is_vlan) {
|
2018-07-17 02:01:57 +00:00
|
|
|
if (is_qinq) {
|
|
|
|
host_eth_type = ntohs(flower->key.encap_eth_type[1]);
|
|
|
|
} else {
|
|
|
|
host_eth_type = ntohs(flower->key.encap_eth_type[0]);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2018-09-02 14:07:49 +01:00
|
|
|
if (is_mpls) {
|
|
|
|
host_eth_type = ntohs(flower->key.encap_eth_type[0]);
|
|
|
|
}
|
|
|
|
|
2017-08-03 16:38:23 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(dst_mac, TCA_FLOWER_KEY_ETH_DST);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(src_mac, TCA_FLOWER_KEY_ETH_SRC);
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2020-06-05 21:17:29 +08:00
|
|
|
if (host_eth_type == ETH_P_ARP) {
|
|
|
|
FLOWER_PUT_MASKED_VALUE(arp.spa, TCA_FLOWER_KEY_ARP_SIP);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(arp.tpa, TCA_FLOWER_KEY_ARP_TIP);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(arp.sha, TCA_FLOWER_KEY_ARP_SHA);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(arp.tha, TCA_FLOWER_KEY_ARP_THA);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(arp.opcode, TCA_FLOWER_KEY_ARP_OP);
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (host_eth_type == ETH_P_IP || host_eth_type == ETH_P_IPV6) {
|
2018-07-31 13:40:36 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(ip_ttl, TCA_FLOWER_KEY_IP_TTL);
|
2018-07-31 13:40:37 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(ip_tos, TCA_FLOWER_KEY_IP_TOS);
|
2018-07-31 13:40:36 +03:00
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (flower->mask.ip_proto && flower->key.ip_proto) {
|
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_IP_PROTO,
|
|
|
|
flower->key.ip_proto);
|
|
|
|
}
|
|
|
|
|
2018-03-12 14:58:47 +02:00
|
|
|
if (flower->mask.flags) {
|
2018-03-21 20:11:22 +00:00
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_FLAGS,
|
2018-03-12 14:58:47 +02:00
|
|
|
htonl(flower->key.flags));
|
2018-03-21 20:11:22 +00:00
|
|
|
nl_msg_put_be32(request, TCA_FLOWER_KEY_FLAGS_MASK,
|
2018-03-12 14:58:47 +02:00
|
|
|
htonl(flower->mask.flags));
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (flower->key.ip_proto == IPPROTO_UDP) {
|
2017-08-03 16:38:24 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(udp_src, TCA_FLOWER_KEY_UDP_SRC);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(udp_dst, TCA_FLOWER_KEY_UDP_DST);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (flower->key.ip_proto == IPPROTO_TCP) {
|
2017-08-03 16:38:24 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(tcp_src, TCA_FLOWER_KEY_TCP_SRC);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(tcp_dst, TCA_FLOWER_KEY_TCP_DST);
|
2017-08-07 18:19:10 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(tcp_flags, TCA_FLOWER_KEY_TCP_FLAGS);
|
2017-07-25 14:39:51 +03:00
|
|
|
} else if (flower->key.ip_proto == IPPROTO_SCTP) {
|
2017-08-03 16:38:24 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(sctp_src, TCA_FLOWER_KEY_SCTP_SRC);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(sctp_dst, TCA_FLOWER_KEY_SCTP_DST);
|
2021-01-28 15:51:08 +02:00
|
|
|
} else if (flower->key.ip_proto == IPPROTO_ICMP) {
|
|
|
|
FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV4_CODE);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV4_TYPE);
|
|
|
|
} else if (flower->key.ip_proto == IPPROTO_ICMPV6) {
|
|
|
|
FLOWER_PUT_MASKED_VALUE(icmp_code, TCA_FLOWER_KEY_ICMPV6_CODE);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(icmp_type, TCA_FLOWER_KEY_ICMPV6_TYPE);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-22 16:23:43 +01:00
|
|
|
FLOWER_PUT_MASKED_VALUE(ct_state, TCA_FLOWER_KEY_CT_STATE);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(ct_zone, TCA_FLOWER_KEY_CT_ZONE);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(ct_mark, TCA_FLOWER_KEY_CT_MARK);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(ct_label, TCA_FLOWER_KEY_CT_LABELS);
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (host_eth_type == ETH_P_IP) {
|
2017-08-03 16:38:23 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_src, TCA_FLOWER_KEY_IPV4_SRC);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(ipv4.ipv4_dst, TCA_FLOWER_KEY_IPV4_DST);
|
2017-06-13 18:03:27 +03:00
|
|
|
} else if (host_eth_type == ETH_P_IPV6) {
|
2017-08-03 16:38:23 +03:00
|
|
|
FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_src, TCA_FLOWER_KEY_IPV6_SRC);
|
|
|
|
FLOWER_PUT_MASKED_VALUE(ipv6.ipv6_dst, TCA_FLOWER_KEY_IPV6_DST);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_ETH_TYPE, flower->key.eth_type);
|
|
|
|
|
2018-09-02 14:07:49 +01:00
|
|
|
if (is_mpls) {
|
|
|
|
if (mpls_lse_to_ttl(flower->mask.mpls_lse)) {
|
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_TTL,
|
|
|
|
mpls_lse_to_ttl(flower->key.mpls_lse));
|
|
|
|
}
|
|
|
|
if (mpls_lse_to_tc(flower->mask.mpls_lse)) {
|
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_TC,
|
|
|
|
mpls_lse_to_tc(flower->key.mpls_lse));
|
|
|
|
}
|
|
|
|
if (mpls_lse_to_bos(flower->mask.mpls_lse)) {
|
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_MPLS_BOS,
|
|
|
|
mpls_lse_to_bos(flower->key.mpls_lse));
|
|
|
|
}
|
|
|
|
if (mpls_lse_to_label(flower->mask.mpls_lse)) {
|
|
|
|
nl_msg_put_u32(request, TCA_FLOWER_KEY_MPLS_LABEL,
|
|
|
|
mpls_lse_to_label(flower->key.mpls_lse));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
if (is_vlan) {
|
2018-09-10 11:08:25 +01:00
|
|
|
if (flower->mask.vlan_id[0]) {
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_u16(request, TCA_FLOWER_KEY_VLAN_ID,
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.vlan_id[0]);
|
2018-09-10 11:08:25 +01:00
|
|
|
}
|
|
|
|
if (flower->mask.vlan_prio[0]) {
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_VLAN_PRIO,
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.vlan_prio[0]);
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
2018-07-17 02:01:57 +00:00
|
|
|
if (flower->key.encap_eth_type[0]) {
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
|
2018-07-17 02:01:57 +00:00
|
|
|
flower->key.encap_eth_type[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_qinq) {
|
2018-09-10 11:08:25 +01:00
|
|
|
if (flower->mask.vlan_id[1]) {
|
2018-07-17 02:01:57 +00:00
|
|
|
nl_msg_put_u16(request, TCA_FLOWER_KEY_CVLAN_ID,
|
|
|
|
flower->key.vlan_id[1]);
|
2018-09-10 11:08:25 +01:00
|
|
|
}
|
|
|
|
if (flower->mask.vlan_prio[1]) {
|
2018-07-17 02:01:57 +00:00
|
|
|
nl_msg_put_u8(request, TCA_FLOWER_KEY_CVLAN_PRIO,
|
|
|
|
flower->key.vlan_prio[1]);
|
|
|
|
}
|
|
|
|
if (flower->key.encap_eth_type[1]) {
|
|
|
|
nl_msg_put_be16(request, TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
|
|
|
|
flower->key.encap_eth_type[1]);
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 09:37:21 +03:00
|
|
|
if (policy == TC_POLICY_NONE) {
|
|
|
|
policy = tc_policy;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl_msg_put_u32(request, TCA_FLOWER_FLAGS, tc_get_tc_cls_policy(policy));
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2018-09-06 13:52:25 +03:00
|
|
|
if (flower->tunnel) {
|
2017-06-13 18:03:27 +03:00
|
|
|
nl_msg_put_flower_tunnel(request, flower);
|
|
|
|
}
|
|
|
|
|
2017-09-18 07:16:03 +03:00
|
|
|
return 0;
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
|
2022-02-22 16:21:05 +01:00
|
|
|
static void
|
|
|
|
log_tc_flower_match(const char *msg,
|
|
|
|
const struct tc_flower *a,
|
|
|
|
const struct tc_flower *b)
|
|
|
|
{
|
|
|
|
uint8_t key_a[sizeof(struct tc_flower_key)];
|
|
|
|
uint8_t key_b[sizeof(struct tc_flower_key)];
|
|
|
|
struct ds s = DS_EMPTY_INITIALIZER;
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof a->key; i++) {
|
|
|
|
uint8_t mask_a = ((uint8_t *) &a->mask)[i];
|
|
|
|
uint8_t mask_b = ((uint8_t *) &b->mask)[i];
|
|
|
|
|
|
|
|
key_a[i] = ((uint8_t *) &a->key)[i] & mask_a;
|
|
|
|
key_b[i] = ((uint8_t *) &b->key)[i] & mask_b;
|
|
|
|
}
|
|
|
|
ds_put_cstr(&s, "\nExpected Mask:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, &a->mask, sizeof a->mask, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
ds_put_cstr(&s, "\nReceived Mask:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, &b->mask, sizeof b->mask, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
ds_put_cstr(&s, "\nExpected Key:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, &a->key, sizeof a->key, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
ds_put_cstr(&s, "\nReceived Key:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, &b->key, sizeof b->key, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
ds_put_cstr(&s, "\nExpected Masked Key:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, key_a, sizeof key_a, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
ds_put_cstr(&s, "\nReceived Masked Key:\n");
|
2022-07-29 16:30:30 +02:00
|
|
|
ds_put_sparse_hex_dump(&s, key_b, sizeof key_b, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
|
|
|
|
if (a->action_count != b->action_count) {
|
|
|
|
/* If action count is not equal, we print all actions to see which
|
|
|
|
* ones are missing. */
|
|
|
|
const struct tc_action *action;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ds_put_cstr(&s, "\nExpected Actions:\n");
|
|
|
|
for (i = 0, action = a->actions; i < a->action_count; i++, action++) {
|
2023-10-18 22:46:10 +02:00
|
|
|
ds_put_format(&s, " - %d -\n", i);
|
|
|
|
ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
}
|
2023-10-18 22:46:10 +02:00
|
|
|
ds_put_cstr(&s, "\nReceived Actions:\n");
|
2022-02-22 16:21:05 +01:00
|
|
|
for (i = 0, action = b->actions; i < b->action_count; i++, action++) {
|
2023-10-18 22:46:10 +02:00
|
|
|
ds_put_format(&s, " - %d -\n", i);
|
|
|
|
ds_put_sparse_hex_dump(&s, action, sizeof *action, 0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Only dump the delta in actions. */
|
|
|
|
const struct tc_action *action_a = a->actions;
|
|
|
|
const struct tc_action *action_b = b->actions;
|
|
|
|
|
|
|
|
for (int i = 0; i < a->action_count; i++, action_a++, action_b++) {
|
|
|
|
if (memcmp(action_a, action_b, sizeof *action_a)) {
|
2023-10-18 22:46:10 +02:00
|
|
|
ds_put_format(&s, "\nAction %d mismatch:\n"
|
|
|
|
" - Expected Action:\n", i);
|
|
|
|
ds_put_sparse_hex_dump(&s, action_a, sizeof *action_a,
|
|
|
|
0, false);
|
|
|
|
ds_put_cstr(&s, " - Received Action:\n");
|
|
|
|
ds_put_sparse_hex_dump(&s, action_b, sizeof *action_b,
|
|
|
|
0, false);
|
2022-02-22 16:21:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VLOG_DBG_RL(&error_rl, "%s%s", msg, ds_cstr(&s));
|
|
|
|
ds_destroy(&s);
|
|
|
|
}
|
|
|
|
|
2021-05-17 09:20:28 -04:00
|
|
|
static bool
|
|
|
|
cmp_tc_flower_match_action(const struct tc_flower *a,
|
|
|
|
const struct tc_flower *b)
|
|
|
|
{
|
|
|
|
if (memcmp(&a->mask, &b->mask, sizeof a->mask)) {
|
2022-02-22 16:21:05 +01:00
|
|
|
log_tc_flower_match("tc flower compare failed mask compare:", a, b);
|
2021-05-17 09:20:28 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We can not memcmp() the key as some keys might be set while the mask
|
|
|
|
* is not.*/
|
|
|
|
|
|
|
|
for (int i = 0; i < sizeof a->key; i++) {
|
|
|
|
uint8_t mask = ((uint8_t *)&a->mask)[i];
|
|
|
|
uint8_t key_a = ((uint8_t *)&a->key)[i] & mask;
|
|
|
|
uint8_t key_b = ((uint8_t *)&b->key)[i] & mask;
|
|
|
|
|
|
|
|
if (key_a != key_b) {
|
2022-02-22 16:21:05 +01:00
|
|
|
log_tc_flower_match("tc flower compare failed masked key compare:",
|
|
|
|
a, b);
|
2021-05-17 09:20:28 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare the actions. */
|
|
|
|
const struct tc_action *action_a = a->actions;
|
|
|
|
const struct tc_action *action_b = b->actions;
|
|
|
|
|
|
|
|
if (a->action_count != b->action_count) {
|
2022-02-22 16:21:05 +01:00
|
|
|
log_tc_flower_match("tc flower compare failed action length check",
|
|
|
|
a, b);
|
2021-05-17 09:20:28 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < a->action_count; i++, action_a++, action_b++) {
|
|
|
|
if (memcmp(action_a, action_b, sizeof *action_a)) {
|
2022-02-22 16:21:05 +01:00
|
|
|
log_tc_flower_match("tc flower compare failed action compare",
|
|
|
|
a, b);
|
2021-05-17 09:20:28 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-13 18:03:27 +03:00
|
|
|
int
|
2019-12-22 12:16:36 +02:00
|
|
|
tc_replace_flower(struct tcf_id *id, struct tc_flower *flower)
|
2017-06-13 18:03:27 +03:00
|
|
|
{
|
|
|
|
struct ofpbuf request;
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
int error = 0;
|
|
|
|
size_t basic_offset;
|
|
|
|
uint16_t eth_type = (OVS_FORCE uint16_t) flower->key.eth_type;
|
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
request_from_tcf_id(id, eth_type, RTM_NEWTFILTER,
|
|
|
|
NLM_F_CREATE | NLM_F_ECHO, &request);
|
2017-06-13 18:03:27 +03:00
|
|
|
|
|
|
|
nl_msg_put_string(&request, TCA_KIND, "flower");
|
|
|
|
basic_offset = nl_msg_start_nested(&request, TCA_OPTIONS);
|
|
|
|
{
|
2017-09-18 07:16:03 +03:00
|
|
|
error = nl_msg_put_flower_options(&request, flower);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
return error;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
}
|
|
|
|
nl_msg_end_nested(&request, basic_offset);
|
|
|
|
|
|
|
|
error = tc_transact(&request, &reply);
|
|
|
|
if (!error) {
|
2023-06-06 20:33:35 +02:00
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(reply->data, reply->size);
|
|
|
|
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
|
|
|
struct tcmsg *tc = ofpbuf_try_pull(&b, sizeof *tc);
|
2024-10-14 14:09:02 +02:00
|
|
|
bool is_probe = id->prio == TC_RESERVED_PRIORITY_FEATURE_PROBE;
|
2023-06-06 20:33:35 +02:00
|
|
|
|
|
|
|
if (!nlmsg || !tc) {
|
|
|
|
COVERAGE_INC(tc_netlink_malformed_reply);
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
return EPROTO;
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
|
2019-12-22 12:16:36 +02:00
|
|
|
id->prio = tc_get_major(tc->tcm_info);
|
|
|
|
id->handle = tc->tcm_handle;
|
2021-05-17 09:20:28 -04:00
|
|
|
|
|
|
|
if (id->prio != TC_RESERVED_PRIORITY_POLICE) {
|
|
|
|
struct tc_flower flower_out;
|
|
|
|
struct tcf_id id_out;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = parse_netlink_to_tc_flower(reply, &id_out, &flower_out,
|
|
|
|
false);
|
|
|
|
|
|
|
|
if (ret || !cmp_tc_flower_match_action(flower, &flower_out)) {
|
2024-10-14 14:09:02 +02:00
|
|
|
if (is_probe) {
|
|
|
|
error = EINVAL;
|
|
|
|
} else {
|
|
|
|
VLOG_WARN_RL(&error_rl, "Kernel flower acknowledgment "
|
|
|
|
"does not match request! Set "
|
|
|
|
"dpif_netlink to dbg to see "
|
|
|
|
"which rule caused this error.");
|
|
|
|
}
|
2021-05-17 09:20:28 -04:00
|
|
|
}
|
|
|
|
}
|
2017-06-13 18:03:27 +03:00
|
|
|
ofpbuf_delete(reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
2017-06-13 18:03:30 +03:00
|
|
|
|
|
|
|
void
|
|
|
|
tc_set_policy(const char *policy)
|
|
|
|
{
|
|
|
|
if (!policy) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(policy, "skip_sw")) {
|
|
|
|
tc_policy = TC_POLICY_SKIP_SW;
|
|
|
|
} else if (!strcmp(policy, "skip_hw")) {
|
|
|
|
tc_policy = TC_POLICY_SKIP_HW;
|
|
|
|
} else if (!strcmp(policy, "none")) {
|
|
|
|
tc_policy = TC_POLICY_NONE;
|
|
|
|
} else {
|
|
|
|
VLOG_WARN("tc: Invalid policy '%s'", policy);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VLOG_INFO("tc: Using policy '%s'", policy);
|
|
|
|
}
|
2022-09-30 14:07:56 +08:00
|
|
|
|
|
|
|
void
|
|
|
|
nl_msg_put_act_tc_policy_flag(struct ofpbuf *request)
|
|
|
|
{
|
|
|
|
int flag = 0;
|
|
|
|
|
|
|
|
if (!request) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tc_policy == TC_POLICY_SKIP_HW) {
|
|
|
|
flag = TCA_ACT_FLAGS_SKIP_HW;
|
|
|
|
} else if (tc_policy == TC_POLICY_SKIP_SW) {
|
|
|
|
flag = TCA_ACT_FLAGS_SKIP_SW;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flag) {
|
|
|
|
struct nla_bitfield32 flags = { flag, flag };
|
|
|
|
nl_msg_put_unspec(request, TCA_ACT_FLAGS, &flags, sizeof flags);
|
|
|
|
}
|
|
|
|
}
|