Skip to content

Commit

Permalink
mlxsw: Add support for IP-in-IP with IPv6 underlay for Spectrum-2 and…
Browse files Browse the repository at this point in the history
… above

Currently, mlxsw driver supports IP-in-IP only with IPv4 underlay.
Add support for IPv6 underlay for Spectrum-2 and above.

Most of the configurations are same to IPv4, the main difference between
IPv4 and IPv6 is related to saving IP addresses.
IPv6 addresses are saved as part of KVD and the relevant registers hold
pointer to them.
Add API for that as part of ipip_ops, so then only Spectrum-2 and above
will save IPv6 addresses in this way.

Signed-off-by: Amit Cohen <[email protected]>
Reviewed-by: Petr Machata <[email protected]>
Signed-off-by: Ido Schimmel <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Amit Cohen authored and davem330 committed Sep 24, 2021
1 parent 8d4f104 commit ba1c713
Show file tree
Hide file tree
Showing 3 changed files with 309 additions and 10 deletions.
227 changes: 226 additions & 1 deletion drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,45 @@ static bool mlxsw_sp_ipip_parms4_has_ikey(const struct ip_tunnel_parm *parms)
return !!(parms->i_flags & TUNNEL_KEY);
}

static bool mlxsw_sp_ipip_parms6_has_ikey(const struct __ip6_tnl_parm *parms)
{
return !!(parms->i_flags & TUNNEL_KEY);
}

static bool mlxsw_sp_ipip_parms4_has_okey(const struct ip_tunnel_parm *parms)
{
return !!(parms->o_flags & TUNNEL_KEY);
}

static bool mlxsw_sp_ipip_parms6_has_okey(const struct __ip6_tnl_parm *parms)
{
return !!(parms->o_flags & TUNNEL_KEY);
}

static u32 mlxsw_sp_ipip_parms4_ikey(const struct ip_tunnel_parm *parms)
{
return mlxsw_sp_ipip_parms4_has_ikey(parms) ?
be32_to_cpu(parms->i_key) : 0;
}

static u32 mlxsw_sp_ipip_parms6_ikey(const struct __ip6_tnl_parm *parms)
{
return mlxsw_sp_ipip_parms6_has_ikey(parms) ?
be32_to_cpu(parms->i_key) : 0;
}

static u32 mlxsw_sp_ipip_parms4_okey(const struct ip_tunnel_parm *parms)
{
return mlxsw_sp_ipip_parms4_has_okey(parms) ?
be32_to_cpu(parms->o_key) : 0;
}

static u32 mlxsw_sp_ipip_parms6_okey(const struct __ip6_tnl_parm *parms)
{
return mlxsw_sp_ipip_parms6_has_okey(parms) ?
be32_to_cpu(parms->o_key) : 0;
}

static union mlxsw_sp_l3addr
mlxsw_sp_ipip_parms4_saddr(const struct ip_tunnel_parm *parms)
{
Expand Down Expand Up @@ -313,6 +335,19 @@ mlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp,
&new_parms, extack);
}

static int
mlxsw_sp_ipip_rem_addr_set_gre4(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry)
{
return 0;
}

static void
mlxsw_sp_ipip_rem_addr_unset_gre4(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_ipip_entry *ipip_entry)
{
}

static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = {
.dev_type = ARPHRD_IPGRE,
.ul_proto = MLXSW_SP_L3_PROTO_IPV4,
Expand All @@ -323,6 +358,8 @@ static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = {
.can_offload = mlxsw_sp_ipip_can_offload_gre4,
.ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre4,
.ol_netdev_change = mlxsw_sp_ipip_ol_netdev_change_gre4,
.rem_ip_addr_set = mlxsw_sp_ipip_rem_addr_set_gre4,
.rem_ip_addr_unset = mlxsw_sp_ipip_rem_addr_unset_gre4,
};

static struct mlxsw_sp_ipip_parms
Expand Down Expand Up @@ -377,6 +414,21 @@ mlxsw_sp1_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp,
return -EINVAL;
}

static int
mlxsw_sp1_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry)
{
WARN_ON_ONCE(1);
return -EINVAL;
}

static void
mlxsw_sp1_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_ipip_entry *ipip_entry)
{
WARN_ON_ONCE(1);
}

static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = {
.dev_type = ARPHRD_IP6GRE,
.ul_proto = MLXSW_SP_L3_PROTO_IPV6,
Expand All @@ -387,16 +439,185 @@ static const struct mlxsw_sp_ipip_ops mlxsw_sp1_ipip_gre6_ops = {
.can_offload = mlxsw_sp1_ipip_can_offload_gre6,
.ol_loopback_config = mlxsw_sp1_ipip_ol_loopback_config_gre6,
.ol_netdev_change = mlxsw_sp1_ipip_ol_netdev_change_gre6,
.rem_ip_addr_set = mlxsw_sp1_ipip_rem_addr_set_gre6,
.rem_ip_addr_unset = mlxsw_sp1_ipip_rem_addr_unset_gre6,
};

const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[] = {
[MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops,
[MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp1_ipip_gre6_ops,
};

static struct mlxsw_sp_ipip_parms
mlxsw_sp2_ipip_netdev_parms_init_gre6(const struct net_device *ol_dev)
{
struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev);

return (struct mlxsw_sp_ipip_parms) {
.proto = MLXSW_SP_L3_PROTO_IPV6,
.saddr = mlxsw_sp_ipip_parms6_saddr(&parms),
.daddr = mlxsw_sp_ipip_parms6_daddr(&parms),
.link = parms.link,
.ikey = mlxsw_sp_ipip_parms6_ikey(&parms),
.okey = mlxsw_sp_ipip_parms6_okey(&parms),
};
}

static int
mlxsw_sp2_ipip_nexthop_update_gre6(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
struct mlxsw_sp_ipip_entry *ipip_entry,
bool force, char *ratr_pl)
{
u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb);
enum mlxsw_reg_ratr_op op;

op = force ? MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY :
MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY_ON_ACTIVITY;
mlxsw_reg_ratr_pack(ratr_pl, op, true, MLXSW_REG_RATR_TYPE_IPIP,
adj_index, rif_index);
mlxsw_reg_ratr_ipip6_entry_pack(ratr_pl,
ipip_entry->dip_kvdl_index);

return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
}

static int
mlxsw_sp2_ipip_decap_config_gre6(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry,
u32 tunnel_index)
{
u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb);
u16 ul_rif_id = mlxsw_sp_ipip_lb_ul_rif_id(ipip_entry->ol_lb);
char rtdp_pl[MLXSW_REG_RTDP_LEN];
struct __ip6_tnl_parm parms;
unsigned int type_check;
bool has_ikey;
u32 ikey;

parms = mlxsw_sp_ipip_netdev_parms6(ipip_entry->ol_dev);
has_ikey = mlxsw_sp_ipip_parms6_has_ikey(&parms);
ikey = mlxsw_sp_ipip_parms6_ikey(&parms);

mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index);
mlxsw_reg_rtdp_egress_router_interface_set(rtdp_pl, ul_rif_id);

type_check = has_ikey ?
MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY :
MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE;

/* Linux demuxes tunnels based on packet SIP (which must match tunnel
* remote IP). Thus configure decap so that it filters out packets that
* are not IPv6 or have the wrong SIP. IPIP_DECAP_ERROR trap is
* generated for packets that fail this criterion. Linux then handles
* such packets in slow path and generates ICMP destination unreachable.
*/
mlxsw_reg_rtdp_ipip6_pack(rtdp_pl, rif_index,
MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6,
type_check, has_ikey,
ipip_entry->dip_kvdl_index, ikey);

return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl);
}

static bool mlxsw_sp2_ipip_can_offload_gre6(const struct mlxsw_sp *mlxsw_sp,
const struct net_device *ol_dev)
{
struct __ip6_tnl_parm tparm = mlxsw_sp_ipip_netdev_parms6(ol_dev);
bool inherit_tos = tparm.flags & IP6_TNL_F_USE_ORIG_TCLASS;
bool inherit_ttl = tparm.hop_limit == 0;
__be16 okflags = TUNNEL_KEY; /* We can't offload any other features. */

return (tparm.i_flags & ~okflags) == 0 &&
(tparm.o_flags & ~okflags) == 0 &&
inherit_ttl && inherit_tos &&
mlxsw_sp_ipip_tunnel_complete(MLXSW_SP_L3_PROTO_IPV6, ol_dev);
}

static struct mlxsw_sp_rif_ipip_lb_config
mlxsw_sp2_ipip_ol_loopback_config_gre6(struct mlxsw_sp *mlxsw_sp,
const struct net_device *ol_dev)
{
struct __ip6_tnl_parm parms = mlxsw_sp_ipip_netdev_parms6(ol_dev);
enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt;

lb_ipipt = mlxsw_sp_ipip_parms6_has_okey(&parms) ?
MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP :
MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP;
return (struct mlxsw_sp_rif_ipip_lb_config){
.lb_ipipt = lb_ipipt,
.okey = mlxsw_sp_ipip_parms6_okey(&parms),
.ul_protocol = MLXSW_SP_L3_PROTO_IPV6,
.saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV6,
ol_dev),
};
}

static int
mlxsw_sp2_ipip_ol_netdev_change_gre6(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry,
struct netlink_ext_ack *extack)
{
struct mlxsw_sp_ipip_parms new_parms;

new_parms = mlxsw_sp2_ipip_netdev_parms_init_gre6(ipip_entry->ol_dev);
return mlxsw_sp_ipip_ol_netdev_change_gre(mlxsw_sp, ipip_entry,
&new_parms, extack);
}

static int
mlxsw_sp2_ipip_rem_addr_set_gre6(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry)
{
char rips_pl[MLXSW_REG_RIPS_LEN];
struct __ip6_tnl_parm parms6;
int err;

err = mlxsw_sp_kvdl_alloc(mlxsw_sp,
MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
&ipip_entry->dip_kvdl_index);
if (err)
return err;

parms6 = mlxsw_sp_ipip_netdev_parms6(ipip_entry->ol_dev);
mlxsw_reg_rips_pack(rips_pl, ipip_entry->dip_kvdl_index,
&parms6.raddr);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rips), rips_pl);
if (err)
goto err_rips_write;

return 0;

err_rips_write:
mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
ipip_entry->dip_kvdl_index);
return err;
}

static void
mlxsw_sp2_ipip_rem_addr_unset_gre6(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_ipip_entry *ipip_entry)
{
mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
ipip_entry->dip_kvdl_index);
}

static const struct mlxsw_sp_ipip_ops mlxsw_sp2_ipip_gre6_ops = {
.dev_type = ARPHRD_IP6GRE,
.ul_proto = MLXSW_SP_L3_PROTO_IPV6,
.inc_parsing_depth = true,
.parms_init = mlxsw_sp2_ipip_netdev_parms_init_gre6,
.nexthop_update = mlxsw_sp2_ipip_nexthop_update_gre6,
.decap_config = mlxsw_sp2_ipip_decap_config_gre6,
.can_offload = mlxsw_sp2_ipip_can_offload_gre6,
.ol_loopback_config = mlxsw_sp2_ipip_ol_loopback_config_gre6,
.ol_netdev_change = mlxsw_sp2_ipip_ol_netdev_change_gre6,
.rem_ip_addr_set = mlxsw_sp2_ipip_rem_addr_set_gre6,
.rem_ip_addr_unset = mlxsw_sp2_ipip_rem_addr_unset_gre6,
};

const struct mlxsw_sp_ipip_ops *mlxsw_sp2_ipip_ops_arr[] = {
[MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops,
[MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp1_ipip_gre6_ops,
[MLXSW_SP_IPIP_TYPE_GRE6] = &mlxsw_sp2_ipip_gre6_ops,
};

static int mlxsw_sp_ipip_ecn_encap_init_one(struct mlxsw_sp *mlxsw_sp,
Expand Down Expand Up @@ -461,11 +682,15 @@ mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev)
{
struct net *net = dev_net(ol_dev);
struct ip_tunnel *tun4;
struct ip6_tnl *tun6;

switch (ol_dev->type) {
case ARPHRD_IPGRE:
tun4 = netdev_priv(ol_dev);
return dev_get_by_index_rcu(net, tun4->parms.link);
case ARPHRD_IP6GRE:
tun6 = netdev_priv(ol_dev);
return dev_get_by_index_rcu(net, tun6->parms.link);
default:
return NULL;
}
Expand Down
6 changes: 6 additions & 0 deletions drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "spectrum_router.h"
#include <net/ip_fib.h>
#include <linux/if_tunnel.h>
#include <net/ip6_tunnel.h>

struct ip_tunnel_parm
mlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev);
Expand Down Expand Up @@ -41,6 +42,7 @@ struct mlxsw_sp_ipip_entry {
struct mlxsw_sp_fib_entry *decap_fib_entry;
struct list_head ipip_list_node;
struct mlxsw_sp_ipip_parms parms;
u32 dip_kvdl_index;
};

struct mlxsw_sp_ipip_ops {
Expand Down Expand Up @@ -70,6 +72,10 @@ struct mlxsw_sp_ipip_ops {
int (*ol_netdev_change)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry,
struct netlink_ext_ack *extack);
int (*rem_ip_addr_set)(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipip_entry *ipip_entry);
void (*rem_ip_addr_unset)(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_ipip_entry *ipip_entry);
};

extern const struct mlxsw_sp_ipip_ops *mlxsw_sp1_ipip_ops_arr[];
Expand Down
Loading

0 comments on commit ba1c713

Please sign in to comment.