Commit f485da3c authored by David S. Miller's avatar David S. Miller

Merge branch 'mlxsw-dip-sip-mangling'

Ido Schimmel says:

====================
mlxsw: Add SIP and DIP mangling support

Danielle says:

On Spectrum-2 onwards, it is possible to overwrite SIP and DIP address
of an IPv4 or IPv6 packet in the ACL engine. That corresponds to pedit
munges of, respectively, ip src and ip dst fields, and likewise for ip6.
Offload these munges on the systems where they are supported.

Patchset overview:
Patch #1: introduces SIP_DIP_ACTION and its fields.
Patch #2-#3: adds the new pedit fields, and dispatches on them on
	     Spectrum-2 and above.
Patch #4 adds a selftest.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 47c1a9a4 92ad3828
......@@ -1957,6 +1957,83 @@ int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block,
}
EXPORT_SYMBOL(mlxsw_afa_block_append_mcrouter);
/* SIP DIP Action
* --------------
* The SIP_DIP_ACTION is used for modifying the SIP and DIP fields of the
* packet, e.g. for NAT. The L3 checksum is updated. Also, if the L4 is TCP or
* if the L4 is UDP and the checksum field is not zero, then the L4 checksum is
* updated.
*/
#define MLXSW_AFA_IP_CODE 0x11
#define MLXSW_AFA_IP_SIZE 2
enum mlxsw_afa_ip_s_d {
/* ip refers to dip */
MLXSW_AFA_IP_S_D_DIP,
/* ip refers to sip */
MLXSW_AFA_IP_S_D_SIP,
};
/* afa_ip_s_d
* Source or destination.
*/
MLXSW_ITEM32(afa, ip, s_d, 0x00, 31, 1);
enum mlxsw_afa_ip_m_l {
/* LSB: ip[63:0] refers to ip[63:0] */
MLXSW_AFA_IP_M_L_LSB,
/* MSB: ip[63:0] refers to ip[127:64] */
MLXSW_AFA_IP_M_L_MSB,
};
/* afa_ip_m_l
* MSB or LSB.
*/
MLXSW_ITEM32(afa, ip, m_l, 0x00, 30, 1);
/* afa_ip_ip_63_32
* Bits [63:32] in the IP address to change to.
*/
MLXSW_ITEM32(afa, ip, ip_63_32, 0x08, 0, 32);
/* afa_ip_ip_31_0
* Bits [31:0] in the IP address to change to.
*/
MLXSW_ITEM32(afa, ip, ip_31_0, 0x0C, 0, 32);
static void mlxsw_afa_ip_pack(char *payload, enum mlxsw_afa_ip_s_d s_d,
enum mlxsw_afa_ip_m_l m_l, u32 ip_31_0,
u32 ip_63_32)
{
mlxsw_afa_ip_s_d_set(payload, s_d);
mlxsw_afa_ip_m_l_set(payload, m_l);
mlxsw_afa_ip_ip_31_0_set(payload, ip_31_0);
mlxsw_afa_ip_ip_63_32_set(payload, ip_63_32);
}
int mlxsw_afa_block_append_ip(struct mlxsw_afa_block *block, bool is_dip,
bool is_lsb, u32 val_31_0, u32 val_63_32,
struct netlink_ext_ack *extack)
{
enum mlxsw_afa_ip_s_d s_d = is_dip ? MLXSW_AFA_IP_S_D_DIP :
MLXSW_AFA_IP_S_D_SIP;
enum mlxsw_afa_ip_m_l m_l = is_lsb ? MLXSW_AFA_IP_M_L_LSB :
MLXSW_AFA_IP_M_L_MSB;
char *act = mlxsw_afa_block_append_action(block,
MLXSW_AFA_IP_CODE,
MLXSW_AFA_IP_SIZE);
if (IS_ERR(act)) {
NL_SET_ERR_MSG_MOD(extack, "Cannot append IP action");
return PTR_ERR(act);
}
mlxsw_afa_ip_pack(act, s_d, m_l, val_31_0, val_63_32);
return 0;
}
EXPORT_SYMBOL(mlxsw_afa_block_append_ip);
/* L4 Port Action
* --------------
* The L4_PORT_ACTION is used for modifying the sport and dport fields of the packet, e.g. for NAT.
......
......@@ -92,6 +92,9 @@ int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid,
int mlxsw_afa_block_append_mcrouter(struct mlxsw_afa_block *block,
u16 expected_irif, u16 min_mtu,
bool rmid_valid, u32 kvdl_index);
int mlxsw_afa_block_append_ip(struct mlxsw_afa_block *block, bool is_dip,
bool is_lsb, u32 val_31_0, u32 val_63_32,
struct netlink_ext_ack *extack);
int mlxsw_afa_block_append_l4port(struct mlxsw_afa_block *block, bool is_dport, u16 l4_port,
struct netlink_ext_ack *extack);
int mlxsw_afa_block_append_police(struct mlxsw_afa_block *block,
......
......@@ -820,6 +820,24 @@ int mlxsw_sp1_kvdl_resources_register(struct mlxsw_core *mlxsw_core);
/* spectrum2_kvdl.c */
extern const struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops;
enum mlxsw_sp_acl_mangle_field {
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD,
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP,
MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN,
MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT,
MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT,
MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP,
MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3,
MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4,
};
struct mlxsw_sp_acl_rule_info {
unsigned int priority;
struct mlxsw_afk_element_values values;
......@@ -828,9 +846,14 @@ struct mlxsw_sp_acl_rule_info {
ingress_bind_blocker:1,
egress_bind_blocker:1,
counter_valid:1,
policer_index_valid:1;
policer_index_valid:1,
ipv6_valid:1;
unsigned int counter_index;
u16 policer_index;
struct {
u32 prev_val;
enum mlxsw_sp_acl_mangle_field prev_field;
} ipv6;
};
/* spectrum_flow.c */
......
......@@ -505,14 +505,6 @@ int mlxsw_sp_acl_rulei_act_priority(struct mlxsw_sp *mlxsw_sp,
extack);
}
enum mlxsw_sp_acl_mangle_field {
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSFIELD,
MLXSW_SP_ACL_MANGLE_FIELD_IP_DSCP,
MLXSW_SP_ACL_MANGLE_FIELD_IP_ECN,
MLXSW_SP_ACL_MANGLE_FIELD_IP_SPORT,
MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT,
};
struct mlxsw_sp_acl_mangle_action {
enum flow_action_mangle_base htype;
/* Offset is u32-aligned. */
......@@ -561,6 +553,18 @@ static struct mlxsw_sp_acl_mangle_action mlxsw_sp_acl_mangle_actions[] = {
MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0x0000ffff, 16, IP_SPORT),
MLXSW_SP_ACL_MANGLE_ACTION_UDP(0, 0xffff0000, 0, IP_DPORT),
MLXSW_SP_ACL_MANGLE_ACTION_IP4(12, 0x00000000, 0, IP4_SIP),
MLXSW_SP_ACL_MANGLE_ACTION_IP4(16, 0x00000000, 0, IP4_DIP),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(8, 0x00000000, 0, IP6_SIP_1),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(12, 0x00000000, 0, IP6_SIP_2),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(16, 0x00000000, 0, IP6_SIP_3),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(20, 0x00000000, 0, IP6_SIP_4),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(24, 0x00000000, 0, IP6_DIP_1),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(28, 0x00000000, 0, IP6_DIP_2),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(32, 0x00000000, 0, IP6_DIP_3),
MLXSW_SP_ACL_MANGLE_ACTION_IP6(36, 0x00000000, 0, IP6_DIP_4),
};
static int
......@@ -599,6 +603,22 @@ static int mlxsw_sp1_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
return err;
}
static int
mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(struct mlxsw_sp_acl_rule_info *rulei,
enum mlxsw_sp_acl_mangle_field field,
u32 val, struct netlink_ext_ack *extack)
{
if (!rulei->ipv6_valid) {
rulei->ipv6.prev_val = val;
rulei->ipv6_valid = true;
rulei->ipv6.prev_field = field;
return 0;
}
NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field order");
return -EOPNOTSUPP;
}
static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_acl_rule_info *rulei,
struct mlxsw_sp_acl_mangle_action *mact,
......@@ -615,6 +635,61 @@ static int mlxsw_sp2_acl_rulei_act_mangle_field(struct mlxsw_sp *mlxsw_sp,
return mlxsw_afa_block_append_l4port(rulei->act_block, false, val, extack);
case MLXSW_SP_ACL_MANGLE_FIELD_IP_DPORT:
return mlxsw_afa_block_append_l4port(rulei->act_block, true, val, extack);
/* IPv4 fields */
case MLXSW_SP_ACL_MANGLE_FIELD_IP4_SIP:
return mlxsw_afa_block_append_ip(rulei->act_block, false,
true, val, 0, extack);
case MLXSW_SP_ACL_MANGLE_FIELD_IP4_DIP:
return mlxsw_afa_block_append_ip(rulei->act_block, true,
true, val, 0, extack);
/* IPv6 fields */
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1:
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3:
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1:
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3:
return mlxsw_sp2_acl_rulei_act_mangle_field_ip_odd(rulei,
mact->field,
val, extack);
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_2:
if (rulei->ipv6_valid &&
rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_1) {
rulei->ipv6_valid = false;
return mlxsw_afa_block_append_ip(rulei->act_block,
false, false, val,
rulei->ipv6.prev_val,
extack);
}
break;
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_4:
if (rulei->ipv6_valid &&
rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_SIP_3) {
rulei->ipv6_valid = false;
return mlxsw_afa_block_append_ip(rulei->act_block,
false, true, val,
rulei->ipv6.prev_val,
extack);
}
break;
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_2:
if (rulei->ipv6_valid &&
rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_1) {
rulei->ipv6_valid = false;
return mlxsw_afa_block_append_ip(rulei->act_block,
true, false, val,
rulei->ipv6.prev_val,
extack);
}
break;
case MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_4:
if (rulei->ipv6_valid &&
rulei->ipv6.prev_field == MLXSW_SP_ACL_MANGLE_FIELD_IP6_DIP_3) {
rulei->ipv6_valid = false;
return mlxsw_afa_block_append_ip(rulei->act_block,
true, true, val,
rulei->ipv6.prev_val,
extack);
}
break;
default:
break;
}
......
......@@ -233,6 +233,12 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
return -EOPNOTSUPP;
}
}
if (rulei->ipv6_valid) {
NL_SET_ERR_MSG_MOD(extack, "Unsupported mangle field");
return -EOPNOTSUPP;
}
return 0;
}
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# This test sends traffic from H1 to H2. Either on ingress of $swp1, or on
# egress of $swp2, the traffic is acted upon by a pedit action. An ingress
# filter installed on $h2 verifies that the packet looks like expected.
#
# +----------------------+ +----------------------+
# | H1 | | H2 |
# | + $h1 | | $h2 + |
# | | 192.0.2.1/28 | | 192.0.2.2/28 | |
# +----|-----------------+ +----------------|-----+
# | |
# +----|----------------------------------------------------------------|-----+
# | SW | | |
# | +-|----------------------------------------------------------------|-+ |
# | | + $swp1 BR $swp2 + | |
# | +--------------------------------------------------------------------+ |
# +---------------------------------------------------------------------------+
ALL_TESTS="
ping_ipv4
ping_ipv6
test_ip4_src
test_ip4_dst
test_ip6_src
test_ip6_dst
"
NUM_NETIFS=4
source lib.sh
source tc_common.sh
h1_create()
{
simple_if_init $h1 192.0.2.1/28 2001:db8:1::1/64
}
h1_destroy()
{
simple_if_fini $h1 192.0.2.1/28 2001:db8:1::1/64
}
h2_create()
{
simple_if_init $h2 192.0.2.2/28 2001:db8:1::2/64
tc qdisc add dev $h2 clsact
}
h2_destroy()
{
tc qdisc del dev $h2 clsact
simple_if_fini $h2 192.0.2.2/28 2001:db8:1::2/64
}
switch_create()
{
ip link add name br1 up type bridge vlan_filtering 1
ip link set dev $swp1 master br1
ip link set dev $swp1 up
ip link set dev $swp2 master br1
ip link set dev $swp2 up
tc qdisc add dev $swp1 clsact
tc qdisc add dev $swp2 clsact
}
switch_destroy()
{
tc qdisc del dev $swp2 clsact
tc qdisc del dev $swp1 clsact
ip link set dev $swp2 down
ip link set dev $swp2 nomaster
ip link set dev $swp1 down
ip link set dev $swp1 nomaster
ip link del dev br1
}
setup_prepare()
{
h1=${NETIFS[p1]}
swp1=${NETIFS[p2]}
swp2=${NETIFS[p3]}
h2=${NETIFS[p4]}
h2mac=$(mac_get $h2)
vrf_prepare
h1_create
h2_create
switch_create
}
cleanup()
{
pre_cleanup
switch_destroy
h2_destroy
h1_destroy
vrf_cleanup
}
ping_ipv4()
{
ping_test $h1 192.0.2.2
}
ping_ipv6()
{
ping6_test $h1 2001:db8:1::2
}
do_test_pedit_ip()
{
local pedit_locus=$1; shift
local pedit_action=$1; shift
local match_prot=$1; shift
local match_flower=$1; shift
local mz_flags=$1; shift
tc filter add $pedit_locus handle 101 pref 1 \
flower action pedit ex munge $pedit_action
tc filter add dev $h2 ingress handle 101 pref 1 prot $match_prot \
flower skip_hw $match_flower action pass
RET=0
$MZ $mz_flags $h1 -c 10 -d 20msec -p 100 -a own -b $h2mac -q -t ip
local pkts
pkts=$(busywait "$TC_HIT_TIMEOUT" until_counter_is ">= 10" \
tc_rule_handle_stats_get "dev $h2 ingress" 101)
check_err $? "Expected to get 10 packets, but got $pkts."
pkts=$(tc_rule_handle_stats_get "$pedit_locus" 101)
((pkts >= 10))
check_err $? "Expected to get 10 packets on pedit rule, but got $pkts."
log_test "$pedit_locus pedit $pedit_action"
tc filter del dev $h2 ingress pref 1
tc filter del $pedit_locus pref 1
}
do_test_pedit_ip6()
{
local locus=$1; shift
local pedit_addr=$1; shift
local flower_addr=$1; shift
do_test_pedit_ip "$locus" "$pedit_addr set 2001:db8:2::1" ipv6 \
"$flower_addr 2001:db8:2::1" \
"-6 -A 2001:db8:1::1 -B 2001:db8:1::2"
}
do_test_pedit_ip4()
{
local locus=$1; shift
local pedit_addr=$1; shift
local flower_addr=$1; shift
do_test_pedit_ip "$locus" "$pedit_addr set 198.51.100.1" ip \
"$flower_addr 198.51.100.1" \
"-A 192.0.2.1 -B 192.0.2.2"
}
test_ip4_src()
{
do_test_pedit_ip4 "dev $swp1 ingress" "ip src" src_ip
do_test_pedit_ip4 "dev $swp2 egress" "ip src" src_ip
}
test_ip4_dst()
{
do_test_pedit_ip4 "dev $swp1 ingress" "ip dst" dst_ip
do_test_pedit_ip4 "dev $swp2 egress" "ip dst" dst_ip
}
test_ip6_src()
{
do_test_pedit_ip6 "dev $swp1 ingress" "ip6 src" src_ip
do_test_pedit_ip6 "dev $swp2 egress" "ip6 src" src_ip
}
test_ip6_dst()
{
do_test_pedit_ip6 "dev $swp1 ingress" "ip6 dst" dst_ip
do_test_pedit_ip6 "dev $swp2 egress" "ip6 dst" dst_ip
}
trap cleanup EXIT
setup_prepare
setup_wait
tests_run
exit $EXIT_STATUS
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment