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

Merge branch 'mlxsw-ipv6-underlay'

Ido Schimmel says:

====================
mlxsw: Add support for VxLAN with IPv6 underlay

So far, mlxsw only supported VxLAN with IPv4 underlay. This patchset
extends mlxsw to also support VxLAN with IPv6 underlay. The main
difference is related to the way IPv6 addresses are handled by the
device. See patch #1 for a detailed explanation.

Patch #1 creates a common hash table to store the mapping from IPv6
addresses to KVDL indexes. This table is useful for both IP-in-IP and
VxLAN tunnels with an IPv6 underlay.

Patch #2 converts the IP-in-IP code to use the new hash table.

Patches #3-#6 are preparations.

Patch #7 finally adds support for VxLAN with IPv6 underlay.

Patch #8 removes a test case that checked that VxLAN configurations with
IPv6 underlay are vetoed by the driver.

A follow-up patchset will add forwarding selftests.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents f71f1bcb fb488be8
......@@ -504,19 +504,43 @@ static inline void
mlxsw_reg_sfd_uc_tunnel_pack(char *payload, int rec_index,
enum mlxsw_reg_sfd_rec_policy policy,
const char *mac, u16 fid,
enum mlxsw_reg_sfd_rec_action action, u32 uip,
enum mlxsw_reg_sfd_rec_action action,
enum mlxsw_reg_sfd_uc_tunnel_protocol proto)
{
mlxsw_reg_sfd_rec_pack(payload, rec_index,
MLXSW_REG_SFD_REC_TYPE_UNICAST_TUNNEL, mac,
action);
mlxsw_reg_sfd_rec_policy_set(payload, rec_index, policy);
mlxsw_reg_sfd_uc_tunnel_uip_msb_set(payload, rec_index, uip >> 24);
mlxsw_reg_sfd_uc_tunnel_uip_lsb_set(payload, rec_index, uip);
mlxsw_reg_sfd_uc_tunnel_fid_set(payload, rec_index, fid);
mlxsw_reg_sfd_uc_tunnel_protocol_set(payload, rec_index, proto);
}
static inline void
mlxsw_reg_sfd_uc_tunnel_pack4(char *payload, int rec_index,
enum mlxsw_reg_sfd_rec_policy policy,
const char *mac, u16 fid,
enum mlxsw_reg_sfd_rec_action action, u32 uip)
{
mlxsw_reg_sfd_uc_tunnel_uip_msb_set(payload, rec_index, uip >> 24);
mlxsw_reg_sfd_uc_tunnel_uip_lsb_set(payload, rec_index, uip);
mlxsw_reg_sfd_uc_tunnel_pack(payload, rec_index, policy, mac, fid,
action,
MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV4);
}
static inline void
mlxsw_reg_sfd_uc_tunnel_pack6(char *payload, int rec_index, const char *mac,
u16 fid, enum mlxsw_reg_sfd_rec_action action,
u32 uip_ptr)
{
mlxsw_reg_sfd_uc_tunnel_uip_lsb_set(payload, rec_index, uip_ptr);
/* Only static policy is supported for IPv6 unicast tunnel entry. */
mlxsw_reg_sfd_uc_tunnel_pack(payload, rec_index,
MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY,
mac, fid, action,
MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV6);
}
enum mlxsw_reg_tunnel_port {
MLXSW_REG_TUNNEL_PORT_NVE,
MLXSW_REG_TUNNEL_PORT_VPLS,
......
......@@ -2755,6 +2755,140 @@ static void mlxsw_sp_parsing_fini(struct mlxsw_sp *mlxsw_sp)
mutex_destroy(&mlxsw_sp->parsing.lock);
}
struct mlxsw_sp_ipv6_addr_node {
struct in6_addr key;
struct rhash_head ht_node;
u32 kvdl_index;
refcount_t refcount;
};
static const struct rhashtable_params mlxsw_sp_ipv6_addr_ht_params = {
.key_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, key),
.head_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, ht_node),
.key_len = sizeof(struct in6_addr),
.automatic_shrinking = true,
};
static int
mlxsw_sp_ipv6_addr_init(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6,
u32 *p_kvdl_index)
{
struct mlxsw_sp_ipv6_addr_node *node;
char rips_pl[MLXSW_REG_RIPS_LEN];
int err;
err = mlxsw_sp_kvdl_alloc(mlxsw_sp,
MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
p_kvdl_index);
if (err)
return err;
mlxsw_reg_rips_pack(rips_pl, *p_kvdl_index, addr6);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rips), rips_pl);
if (err)
goto err_rips_write;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) {
err = -ENOMEM;
goto err_node_alloc;
}
node->key = *addr6;
node->kvdl_index = *p_kvdl_index;
refcount_set(&node->refcount, 1);
err = rhashtable_insert_fast(&mlxsw_sp->ipv6_addr_ht,
&node->ht_node,
mlxsw_sp_ipv6_addr_ht_params);
if (err)
goto err_rhashtable_insert;
return 0;
err_rhashtable_insert:
kfree(node);
err_node_alloc:
err_rips_write:
mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
*p_kvdl_index);
return err;
}
static void mlxsw_sp_ipv6_addr_fini(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_ipv6_addr_node *node)
{
u32 kvdl_index = node->kvdl_index;
rhashtable_remove_fast(&mlxsw_sp->ipv6_addr_ht, &node->ht_node,
mlxsw_sp_ipv6_addr_ht_params);
kfree(node);
mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
kvdl_index);
}
int mlxsw_sp_ipv6_addr_kvdl_index_get(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index)
{
struct mlxsw_sp_ipv6_addr_node *node;
int err = 0;
mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
mlxsw_sp_ipv6_addr_ht_params);
if (node) {
refcount_inc(&node->refcount);
*p_kvdl_index = node->kvdl_index;
goto out_unlock;
}
err = mlxsw_sp_ipv6_addr_init(mlxsw_sp, addr6, p_kvdl_index);
out_unlock:
mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
return err;
}
void
mlxsw_sp_ipv6_addr_put(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6)
{
struct mlxsw_sp_ipv6_addr_node *node;
mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
mlxsw_sp_ipv6_addr_ht_params);
if (WARN_ON(!node))
goto out_unlock;
if (!refcount_dec_and_test(&node->refcount))
goto out_unlock;
mlxsw_sp_ipv6_addr_fini(mlxsw_sp, node);
out_unlock:
mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
}
static int mlxsw_sp_ipv6_addr_ht_init(struct mlxsw_sp *mlxsw_sp)
{
int err;
err = rhashtable_init(&mlxsw_sp->ipv6_addr_ht,
&mlxsw_sp_ipv6_addr_ht_params);
if (err)
return err;
mutex_init(&mlxsw_sp->ipv6_addr_ht_lock);
return 0;
}
static void mlxsw_sp_ipv6_addr_ht_fini(struct mlxsw_sp *mlxsw_sp)
{
mutex_destroy(&mlxsw_sp->ipv6_addr_ht_lock);
rhashtable_destroy(&mlxsw_sp->ipv6_addr_ht);
}
static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info,
struct netlink_ext_ack *extack)
......@@ -2843,6 +2977,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
goto err_afa_init;
}
err = mlxsw_sp_ipv6_addr_ht_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize hash table for IPv6 addresses\n");
goto err_ipv6_addr_ht_init;
}
err = mlxsw_sp_nve_init(mlxsw_sp);
if (err) {
dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize NVE\n");
......@@ -2944,6 +3084,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
err_acl_init:
mlxsw_sp_nve_fini(mlxsw_sp);
err_nve_init:
mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
err_ipv6_addr_ht_init:
mlxsw_sp_afa_fini(mlxsw_sp);
err_afa_init:
mlxsw_sp_counter_pool_fini(mlxsw_sp);
......@@ -3075,6 +3217,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
mlxsw_sp_router_fini(mlxsw_sp);
mlxsw_sp_acl_fini(mlxsw_sp);
mlxsw_sp_nve_fini(mlxsw_sp);
mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
mlxsw_sp_afa_fini(mlxsw_sp);
mlxsw_sp_counter_pool_fini(mlxsw_sp);
mlxsw_sp_switchdev_fini(mlxsw_sp);
......
......@@ -203,6 +203,8 @@ struct mlxsw_sp {
const struct mlxsw_listener *listeners;
size_t listeners_count;
u32 lowest_shaper_bs;
struct rhashtable ipv6_addr_ht;
struct mutex ipv6_addr_ht_lock; /* Protects ipv6_addr_ht */
};
struct mlxsw_sp_ptp_ops {
......@@ -587,6 +589,11 @@ mlxsw_sp_sample_trigger_params_set(struct mlxsw_sp *mlxsw_sp,
void
mlxsw_sp_sample_trigger_params_unset(struct mlxsw_sp *mlxsw_sp,
const struct mlxsw_sp_sample_trigger *trigger);
int mlxsw_sp_ipv6_addr_kvdl_index_get(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index);
void
mlxsw_sp_ipv6_addr_put(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6);
extern const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals;
extern const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals;
......@@ -1310,6 +1317,17 @@ void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid *fid,
enum mlxsw_sp_l3proto proto,
union mlxsw_sp_l3addr *addr);
int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index);
void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6);
int
mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index,
const struct in6_addr *new_addr6);
void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index);
int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack);
......
......@@ -568,37 +568,21 @@ 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;
return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, &parms6.raddr,
&ipip_entry->dip_kvdl_index);
}
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);
struct __ip6_tnl_parm parms6;
parms6 = mlxsw_sp_ipip_netdev_parms6(ipip_entry->ol_dev);
mlxsw_sp_ipv6_addr_put(mlxsw_sp, &parms6.raddr);
}
static const struct mlxsw_sp_ipip_ops mlxsw_sp2_ipip_gre6_ops = {
......
......@@ -130,15 +130,25 @@ mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
struct mlxsw_sp_nve_mc_entry *mc_entry,
const union mlxsw_sp_l3addr *addr)
{
WARN_ON(1);
u32 kvdl_index;
int err;
err = mlxsw_sp_ipv6_addr_kvdl_index_get(mc_record->mlxsw_sp,
&addr->addr6, &kvdl_index);
if (err)
return err;
return -EINVAL;
mc_entry->ipv6_entry.addr6 = addr->addr6;
mc_entry->ipv6_entry.addr6_kvdl_index = kvdl_index;
return 0;
}
static void
mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
const struct mlxsw_sp_nve_mc_entry *mc_entry)
{
mlxsw_sp_ipv6_addr_put(mc_record->mlxsw_sp,
&mc_entry->ipv6_entry.addr6);
}
static void
......@@ -787,6 +797,142 @@ static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
ops->fdb_clear_offload(nve_dev, vni);
}
struct mlxsw_sp_nve_ipv6_ht_key {
u8 mac[ETH_ALEN];
u16 fid_index;
};
struct mlxsw_sp_nve_ipv6_ht_node {
struct rhash_head ht_node;
struct list_head list;
struct mlxsw_sp_nve_ipv6_ht_key key;
struct in6_addr addr6;
};
static const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = {
.key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key),
.key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key),
.head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node),
};
int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index)
{
return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index);
}
void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6)
{
mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6);
}
static struct mlxsw_sp_nve_ipv6_ht_node *
mlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_key key = {};
ether_addr_copy(key.mac, mac);
key.fid_index = fid_index;
return rhashtable_lookup_fast(&mlxsw_sp->nve->ipv6_ht, &key,
mlxsw_sp_nve_ipv6_ht_params);
}
static int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid_index,
const struct in6_addr *addr6)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
int err;
ipv6_ht_node = kzalloc(sizeof(*ipv6_ht_node), GFP_KERNEL);
if (!ipv6_ht_node)
return -ENOMEM;
ether_addr_copy(ipv6_ht_node->key.mac, mac);
ipv6_ht_node->key.fid_index = fid_index;
ipv6_ht_node->addr6 = *addr6;
err = rhashtable_insert_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
mlxsw_sp_nve_ipv6_ht_params);
if (err)
goto err_rhashtable_insert;
list_add(&ipv6_ht_node->list, &nve->ipv6_addr_list);
return 0;
err_rhashtable_insert:
kfree(ipv6_ht_node);
return err;
}
static void
mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node)
{
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
list_del(&ipv6_ht_node->list);
rhashtable_remove_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
mlxsw_sp_nve_ipv6_ht_params);
kfree(ipv6_ht_node);
}
int
mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index,
const struct in6_addr *new_addr6)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
ASSERT_RTNL();
ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
fid_index);
if (!ipv6_ht_node)
return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index,
new_addr6);
mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
ipv6_ht_node->addr6 = *new_addr6;
return 0;
}
void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
ASSERT_RTNL();
ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
fid_index);
if (WARN_ON(!ipv6_ht_node))
return;
mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
}
static void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp;
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list,
list) {
if (ipv6_ht_node->key.fid_index != fid_index)
continue;
mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
}
}
int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack)
......@@ -845,6 +991,7 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index);
if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
mlxsw_sp_fid_vni(fid, &vni)))
......@@ -981,7 +1128,13 @@ int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
err = rhashtable_init(&nve->mc_list_ht,
&mlxsw_sp_nve_mc_list_ht_params);
if (err)
goto err_rhashtable_init;
goto err_mc_rhashtable_init;
err = rhashtable_init(&nve->ipv6_ht, &mlxsw_sp_nve_ipv6_ht_params);
if (err)
goto err_ipv6_rhashtable_init;
INIT_LIST_HEAD(&nve->ipv6_addr_list);
err = mlxsw_sp_nve_qos_init(mlxsw_sp);
if (err)
......@@ -1000,8 +1153,10 @@ int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
err_nve_resources_query:
err_nve_ecn_init:
err_nve_qos_init:
rhashtable_destroy(&nve->ipv6_ht);
err_ipv6_rhashtable_init:
rhashtable_destroy(&nve->mc_list_ht);
err_rhashtable_init:
err_mc_rhashtable_init:
mlxsw_sp->nve = NULL;
kfree(nve);
return err;
......@@ -1010,6 +1165,8 @@ int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
{
WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list));
rhashtable_destroy(&mlxsw_sp->nve->ipv6_ht);
rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
kfree(mlxsw_sp->nve);
mlxsw_sp->nve = NULL;
......
......@@ -23,6 +23,8 @@ struct mlxsw_sp_nve_config {
struct mlxsw_sp_nve {
struct mlxsw_sp_nve_config config;
struct rhashtable mc_list_ht;
struct rhashtable ipv6_ht;
struct list_head ipv6_addr_list; /* Saves hash table nodes. */
struct mlxsw_sp *mlxsw_sp;
const struct mlxsw_sp_nve_ops **nve_ops_arr;
unsigned int num_nve_tunnels; /* Protected by RTNL */
......
......@@ -10,8 +10,48 @@
#include "spectrum.h"
#include "spectrum_nve.h"
#define MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS (VXLAN_F_UDP_ZERO_CSUM_TX | \
#define MLXSW_SP_NVE_VXLAN_IPV4_SUPPORTED_FLAGS (VXLAN_F_UDP_ZERO_CSUM_TX | \
VXLAN_F_LEARN)
#define MLXSW_SP_NVE_VXLAN_IPV6_SUPPORTED_FLAGS (VXLAN_F_IPV6 | \
VXLAN_F_UDP_ZERO_CSUM6_TX | \
VXLAN_F_UDP_ZERO_CSUM6_RX)
static bool mlxsw_sp_nve_vxlan_ipv4_flags_check(const struct vxlan_config *cfg,
struct netlink_ext_ack *extack)
{
if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Zero UDP checksum must be allowed for TX");
return false;
}
if (cfg->flags & ~MLXSW_SP_NVE_VXLAN_IPV4_SUPPORTED_FLAGS) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Unsupported flag");
return false;
}
return true;
}
static bool mlxsw_sp_nve_vxlan_ipv6_flags_check(const struct vxlan_config *cfg,
struct netlink_ext_ack *extack)
{
if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Zero UDP checksum must be allowed for TX");
return false;
}
if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Zero UDP checksum must be allowed for RX");
return false;
}
if (cfg->flags & ~MLXSW_SP_NVE_VXLAN_IPV6_SUPPORTED_FLAGS) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Unsupported flag");
return false;
}
return true;
}
static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
......@@ -20,11 +60,6 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
struct vxlan_dev *vxlan = netdev_priv(params->dev);
struct vxlan_config *cfg = &vxlan->cfg;
if (cfg->saddr.sa.sa_family != AF_INET) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Only IPv4 underlay is supported");
return false;
}
if (vxlan_addr_multicast(&cfg->remote_ip)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Multicast destination IP is not supported");
return false;
......@@ -55,14 +90,15 @@ static bool mlxsw_sp_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
return false;
}
if (!(cfg->flags & VXLAN_F_UDP_ZERO_CSUM_TX)) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: UDP checksum is not supported");
return false;
}
if (cfg->flags & ~MLXSW_SP_NVE_VXLAN_SUPPORTED_FLAGS) {
NL_SET_ERR_MSG_MOD(extack, "VxLAN: Unsupported flag");
return false;
switch (cfg->saddr.sa.sa_family) {
case AF_INET:
if (!mlxsw_sp_nve_vxlan_ipv4_flags_check(cfg, extack))
return false;
break;
case AF_INET6:
if (!mlxsw_sp_nve_vxlan_ipv6_flags_check(cfg, extack))
return false;
break;
}
if (cfg->ttl == 0) {
......@@ -90,6 +126,22 @@ static bool mlxsw_sp1_nve_vxlan_can_offload(const struct mlxsw_sp_nve *nve,
return mlxsw_sp_nve_vxlan_can_offload(nve, params, extack);
}
static void
mlxsw_sp_nve_vxlan_ul_proto_sip_config(const struct vxlan_config *cfg,
struct mlxsw_sp_nve_config *config)
{
switch (cfg->saddr.sa.sa_family) {
case AF_INET:
config->ul_proto = MLXSW_SP_L3_PROTO_IPV4;
config->ul_sip.addr4 = cfg->saddr.sin.sin_addr.s_addr;
break;
case AF_INET6:
config->ul_proto = MLXSW_SP_L3_PROTO_IPV6;
config->ul_sip.addr6 = cfg->saddr.sin6.sin6_addr;
break;
}
}
static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,
const struct mlxsw_sp_nve_params *params,
struct mlxsw_sp_nve_config *config)
......@@ -102,8 +154,7 @@ static void mlxsw_sp_nve_vxlan_config(const struct mlxsw_sp_nve *nve,
config->flowlabel = cfg->label;
config->learning_en = cfg->flags & VXLAN_F_LEARN ? 1 : 0;
config->ul_tb_id = RT_TABLE_MAIN;
config->ul_proto = MLXSW_SP_L3_PROTO_IPV4;
config->ul_sip.addr4 = cfg->saddr.sin.sin_addr.s_addr;
mlxsw_sp_nve_vxlan_ul_proto_sip_config(cfg, config);
config->udp_dport = cfg->dst_port;
}
......@@ -111,6 +162,7 @@ static void
mlxsw_sp_nve_vxlan_config_prepare(char *tngcr_pl,
const struct mlxsw_sp_nve_config *config)
{
struct in6_addr addr6;
u8 udp_sport;
mlxsw_reg_tngcr_pack(tngcr_pl, MLXSW_REG_TNGCR_TYPE_VXLAN, true,
......@@ -122,7 +174,18 @@ mlxsw_sp_nve_vxlan_config_prepare(char *tngcr_pl,
get_random_bytes(&udp_sport, sizeof(udp_sport));
udp_sport = (udp_sport % (0xee - 0x80 + 1)) + 0x80;
mlxsw_reg_tngcr_nve_udp_sport_prefix_set(tngcr_pl, udp_sport);
mlxsw_reg_tngcr_usipv4_set(tngcr_pl, be32_to_cpu(config->ul_sip.addr4));
switch (config->ul_proto) {
case MLXSW_SP_L3_PROTO_IPV4:
mlxsw_reg_tngcr_usipv4_set(tngcr_pl,
be32_to_cpu(config->ul_sip.addr4));
break;
case MLXSW_SP_L3_PROTO_IPV6:
addr6 = config->ul_sip.addr6;
mlxsw_reg_tngcr_usipv6_memcpy_to(tngcr_pl,
(const char *)&addr6);
break;
}
}
static int
......
......@@ -1307,6 +1307,10 @@ mlxsw_sp_router_ip2me_fib_entry_find(struct mlxsw_sp *mlxsw_sp, u32 tb_id,
addr_prefix_len = 32;
break;
case MLXSW_SP_L3_PROTO_IPV6:
addrp = &addr->addr6;
addr_len = 16;
addr_prefix_len = 128;
break;
default:
WARN_ON(1);
return NULL;
......@@ -7002,6 +7006,8 @@ mlxsw_sp_fib6_entry_type_set_local(struct mlxsw_sp *mlxsw_sp,
{
struct mlxsw_sp_nexthop_group_info *nhgi = fib_entry->nh_group->nhgi;
union mlxsw_sp_l3addr dip = { .addr6 = rt->fib6_dst.addr };
u32 tb_id = mlxsw_sp_fix_tb_id(rt->fib6_table->tb6_id);
struct mlxsw_sp_router *router = mlxsw_sp->router;
int ifindex = nhgi->nexthops[0].ifindex;
struct mlxsw_sp_ipip_entry *ipip_entry;
......@@ -7015,6 +7021,14 @@ mlxsw_sp_fib6_entry_type_set_local(struct mlxsw_sp *mlxsw_sp,
return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, fib_entry,
ipip_entry);
}
if (mlxsw_sp_router_nve_is_decap(mlxsw_sp, tb_id,
MLXSW_SP_L3_PROTO_IPV6, &dip)) {
u32 tunnel_index;
tunnel_index = router->nve_decap_config.tunnel_index;
fib_entry->decap.tunnel_index = tunnel_index;
fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_NVE_DECAP;
}
return 0;
}
......
......@@ -1290,38 +1290,52 @@ static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
MLXSW_REG_SFD_OP_WRITE_REMOVE;
}
static int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid,
enum mlxsw_sp_l3proto proto,
const union mlxsw_sp_l3addr *addr,
bool adding, bool dynamic)
static int
mlxsw_sp_port_fdb_tun_uc_op4(struct mlxsw_sp *mlxsw_sp, bool dynamic,
const char *mac, u16 fid, __be32 addr, bool adding)
{
enum mlxsw_reg_sfd_uc_tunnel_protocol sfd_proto;
char *sfd_pl;
u8 num_rec;
u32 uip;
int err;
switch (proto) {
case MLXSW_SP_L3_PROTO_IPV4:
uip = be32_to_cpu(addr->addr4);
sfd_proto = MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV4;
break;
case MLXSW_SP_L3_PROTO_IPV6:
default:
WARN_ON(1);
return -EOPNOTSUPP;
}
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
if (!sfd_pl)
return -ENOMEM;
uip = be32_to_cpu(addr);
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_uc_tunnel_pack4(sfd_pl, 0,
mlxsw_sp_sfd_rec_policy(dynamic), mac,
fid, MLXSW_REG_SFD_REC_ACTION_NOP, uip);
num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
if (err)
goto out;
if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl))
err = -EBUSY;
out:
kfree(sfd_pl);
return err;
}
static int mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid,
u32 kvdl_index, bool adding)
{
char *sfd_pl;
u8 num_rec;
int err;
sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
if (!sfd_pl)
return -ENOMEM;
mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
mlxsw_reg_sfd_uc_tunnel_pack(sfd_pl, 0,
mlxsw_sp_sfd_rec_policy(dynamic), mac, fid,
MLXSW_REG_SFD_REC_ACTION_NOP, uip,
sfd_proto);
mlxsw_reg_sfd_uc_tunnel_pack6(sfd_pl, 0, mac, fid,
MLXSW_REG_SFD_REC_ACTION_NOP, kvdl_index);
num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
if (err)
......@@ -1335,6 +1349,79 @@ static int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp,
return err;
}
static int mlxsw_sp_port_fdb_tun_uc_op6_add(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid,
const struct in6_addr *addr)
{
u32 kvdl_index;
int err;
err = mlxsw_sp_nve_ipv6_addr_kvdl_set(mlxsw_sp, addr, &kvdl_index);
if (err)
return err;
err = mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid,
kvdl_index, true);
if (err)
goto err_sfd_write;
err = mlxsw_sp_nve_ipv6_addr_map_replace(mlxsw_sp, mac, fid, addr);
if (err)
/* Replace can fail only for creating new mapping, so removing
* the FDB entry in the error path is OK.
*/
goto err_addr_replace;
return 0;
err_addr_replace:
mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, kvdl_index,
false);
err_sfd_write:
mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr);
return err;
}
static void mlxsw_sp_port_fdb_tun_uc_op6_del(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid,
const struct in6_addr *addr)
{
mlxsw_sp_nve_ipv6_addr_map_del(mlxsw_sp, mac, fid);
mlxsw_sp_port_fdb_tun_uc_op6_sfd_write(mlxsw_sp, mac, fid, 0, false);
mlxsw_sp_nve_ipv6_addr_kvdl_unset(mlxsw_sp, addr);
}
static int
mlxsw_sp_port_fdb_tun_uc_op6(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid, const struct in6_addr *addr, bool adding)
{
if (adding)
return mlxsw_sp_port_fdb_tun_uc_op6_add(mlxsw_sp, mac, fid,
addr);
mlxsw_sp_port_fdb_tun_uc_op6_del(mlxsw_sp, mac, fid, addr);
return 0;
}
static int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid,
enum mlxsw_sp_l3proto proto,
const union mlxsw_sp_l3addr *addr,
bool adding, bool dynamic)
{
switch (proto) {
case MLXSW_SP_L3_PROTO_IPV4:
return mlxsw_sp_port_fdb_tun_uc_op4(mlxsw_sp, dynamic, mac, fid,
addr->addr4, adding);
case MLXSW_SP_L3_PROTO_IPV6:
return mlxsw_sp_port_fdb_tun_uc_op6(mlxsw_sp, mac, fid,
&addr->addr6, adding);
default:
WARN_ON(1);
return -EOPNOTSUPP;
}
}
static int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u16 local_port,
const char *mac, u16 fid, bool adding,
enum mlxsw_reg_sfd_rec_action action,
......
......@@ -145,23 +145,6 @@ sanitization_single_dev_no_local_ip_test()
log_test "vxlan device with no local ip"
}
sanitization_single_dev_local_ipv6_test()
{
RET=0
ip link add dev br0 type bridge mcast_snooping 0
ip link add name vxlan0 up type vxlan id 10 nolearning noudpcsum \
ttl 20 tos inherit local 2001:db8::1 dstport 4789
sanitization_single_dev_test_fail
ip link del dev vxlan0
ip link del dev br0
log_test "vxlan device with local ipv6 address"
}
sanitization_single_dev_learning_enabled_test()
{
RET=0
......@@ -276,7 +259,6 @@ sanitization_single_dev_test()
sanitization_single_dev_mcast_enabled_test
sanitization_single_dev_mcast_group_test
sanitization_single_dev_no_local_ip_test
sanitization_single_dev_local_ipv6_test
sanitization_single_dev_learning_enabled_test
sanitization_single_dev_local_interface_test
sanitization_single_dev_port_range_test
......
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