18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
58c2ecf20Sopenharmony_ci#include <net/ip6_tunnel.h>
68c2ecf20Sopenharmony_ci#include <net/inet_ecn.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "spectrum_ipip.h"
98c2ecf20Sopenharmony_ci#include "reg.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistruct ip_tunnel_parm
128c2ecf20Sopenharmony_cimlxsw_sp_ipip_netdev_parms4(const struct net_device *ol_dev)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	struct ip_tunnel *tun = netdev_priv(ol_dev);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	return tun->parms;
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct __ip6_tnl_parm
208c2ecf20Sopenharmony_cimlxsw_sp_ipip_netdev_parms6(const struct net_device *ol_dev)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct ip6_tnl *tun = netdev_priv(ol_dev);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	return tun->parms;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic bool mlxsw_sp_ipip_parms4_has_ikey(struct ip_tunnel_parm parms)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	return !!(parms.i_flags & TUNNEL_KEY);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic bool mlxsw_sp_ipip_parms4_has_okey(struct ip_tunnel_parm parms)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return !!(parms.o_flags & TUNNEL_KEY);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic u32 mlxsw_sp_ipip_parms4_ikey(struct ip_tunnel_parm parms)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_parms4_has_ikey(parms) ?
408c2ecf20Sopenharmony_ci		be32_to_cpu(parms.i_key) : 0;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic u32 mlxsw_sp_ipip_parms4_okey(struct ip_tunnel_parm parms)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_parms4_has_okey(parms) ?
468c2ecf20Sopenharmony_ci		be32_to_cpu(parms.o_key) : 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic union mlxsw_sp_l3addr
508c2ecf20Sopenharmony_cimlxsw_sp_ipip_parms4_saddr(struct ip_tunnel_parm parms)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.saddr };
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic union mlxsw_sp_l3addr
568c2ecf20Sopenharmony_cimlxsw_sp_ipip_parms6_saddr(struct __ip6_tnl_parm parms)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) { .addr6 = parms.laddr };
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic union mlxsw_sp_l3addr
628c2ecf20Sopenharmony_cimlxsw_sp_ipip_parms4_daddr(struct ip_tunnel_parm parms)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) { .addr4 = parms.iph.daddr };
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic union mlxsw_sp_l3addr
688c2ecf20Sopenharmony_cimlxsw_sp_ipip_parms6_daddr(struct __ip6_tnl_parm parms)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) { .addr6 = parms.raddr };
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciunion mlxsw_sp_l3addr
748c2ecf20Sopenharmony_cimlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto,
758c2ecf20Sopenharmony_ci			   const struct net_device *ol_dev)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct ip_tunnel_parm parms4;
788c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm parms6;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	switch (proto) {
818c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
828c2ecf20Sopenharmony_ci		parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
838c2ecf20Sopenharmony_ci		return mlxsw_sp_ipip_parms4_saddr(parms4);
848c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
858c2ecf20Sopenharmony_ci		parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
868c2ecf20Sopenharmony_ci		return mlxsw_sp_ipip_parms6_saddr(parms6);
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	WARN_ON(1);
908c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) {0};
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic __be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	struct ip_tunnel_parm parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_parms4_daddr(parms4).addr4;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic union mlxsw_sp_l3addr
1028c2ecf20Sopenharmony_cimlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto,
1038c2ecf20Sopenharmony_ci			   const struct net_device *ol_dev)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct ip_tunnel_parm parms4;
1068c2ecf20Sopenharmony_ci	struct __ip6_tnl_parm parms6;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	switch (proto) {
1098c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
1108c2ecf20Sopenharmony_ci		parms4 = mlxsw_sp_ipip_netdev_parms4(ol_dev);
1118c2ecf20Sopenharmony_ci		return mlxsw_sp_ipip_parms4_daddr(parms4);
1128c2ecf20Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV6:
1138c2ecf20Sopenharmony_ci		parms6 = mlxsw_sp_ipip_netdev_parms6(ol_dev);
1148c2ecf20Sopenharmony_ci		return mlxsw_sp_ipip_parms6_daddr(parms6);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	WARN_ON(1);
1188c2ecf20Sopenharmony_ci	return (union mlxsw_sp_l3addr) {0};
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cibool mlxsw_sp_l3addr_is_zero(union mlxsw_sp_l3addr addr)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr naddr = {0};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return !memcmp(&addr, &naddr, sizeof(naddr));
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int
1298c2ecf20Sopenharmony_cimlxsw_sp_ipip_nexthop_update_gre4(struct mlxsw_sp *mlxsw_sp, u32 adj_index,
1308c2ecf20Sopenharmony_ci				  struct mlxsw_sp_ipip_entry *ipip_entry)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb);
1338c2ecf20Sopenharmony_ci	__be32 daddr4 = mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev);
1348c2ecf20Sopenharmony_ci	char ratr_pl[MLXSW_REG_RATR_LEN];
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY,
1378c2ecf20Sopenharmony_ci			    true, MLXSW_REG_RATR_TYPE_IPIP,
1388c2ecf20Sopenharmony_ci			    adj_index, rif_index);
1398c2ecf20Sopenharmony_ci	mlxsw_reg_ratr_ipip4_entry_pack(ratr_pl, be32_to_cpu(daddr4));
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int
1458c2ecf20Sopenharmony_cimlxsw_sp_ipip_fib_entry_op_gre4_rtdp(struct mlxsw_sp *mlxsw_sp,
1468c2ecf20Sopenharmony_ci				     u32 tunnel_index,
1478c2ecf20Sopenharmony_ci				     struct mlxsw_sp_ipip_entry *ipip_entry)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb);
1508c2ecf20Sopenharmony_ci	u16 ul_rif_id = mlxsw_sp_ipip_lb_ul_rif_id(ipip_entry->ol_lb);
1518c2ecf20Sopenharmony_ci	char rtdp_pl[MLXSW_REG_RTDP_LEN];
1528c2ecf20Sopenharmony_ci	struct ip_tunnel_parm parms;
1538c2ecf20Sopenharmony_ci	unsigned int type_check;
1548c2ecf20Sopenharmony_ci	bool has_ikey;
1558c2ecf20Sopenharmony_ci	u32 daddr4;
1568c2ecf20Sopenharmony_ci	u32 ikey;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev);
1598c2ecf20Sopenharmony_ci	has_ikey = mlxsw_sp_ipip_parms4_has_ikey(parms);
1608c2ecf20Sopenharmony_ci	ikey = mlxsw_sp_ipip_parms4_ikey(parms);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index);
1638c2ecf20Sopenharmony_ci	mlxsw_reg_rtdp_egress_router_interface_set(rtdp_pl, ul_rif_id);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	type_check = has_ikey ?
1668c2ecf20Sopenharmony_ci		MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY :
1678c2ecf20Sopenharmony_ci		MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* Linux demuxes tunnels based on packet SIP (which must match tunnel
1708c2ecf20Sopenharmony_ci	 * remote IP). Thus configure decap so that it filters out packets that
1718c2ecf20Sopenharmony_ci	 * are not IPv4 or have the wrong SIP. IPIP_DECAP_ERROR trap is
1728c2ecf20Sopenharmony_ci	 * generated for packets that fail this criterion. Linux then handles
1738c2ecf20Sopenharmony_ci	 * such packets in slow path and generates ICMP destination unreachable.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	daddr4 = be32_to_cpu(mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev));
1768c2ecf20Sopenharmony_ci	mlxsw_reg_rtdp_ipip4_pack(rtdp_pl, rif_index,
1778c2ecf20Sopenharmony_ci				  MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV4,
1788c2ecf20Sopenharmony_ci				  type_check, has_ikey, daddr4, ikey);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int
1848c2ecf20Sopenharmony_cimlxsw_sp_ipip_fib_entry_op_gre4_ralue(struct mlxsw_sp *mlxsw_sp,
1858c2ecf20Sopenharmony_ci				      u32 dip, u8 prefix_len, u16 ul_vr_id,
1868c2ecf20Sopenharmony_ci				      enum mlxsw_reg_ralue_op op,
1878c2ecf20Sopenharmony_ci				      u32 tunnel_index)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	char ralue_pl[MLXSW_REG_RALUE_LEN];
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_REG_RALXX_PROTOCOL_IPV4, op,
1928c2ecf20Sopenharmony_ci			      ul_vr_id, prefix_len, dip);
1938c2ecf20Sopenharmony_ci	mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl, tunnel_index);
1948c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int mlxsw_sp_ipip_fib_entry_op_gre4(struct mlxsw_sp *mlxsw_sp,
1988c2ecf20Sopenharmony_ci					struct mlxsw_sp_ipip_entry *ipip_entry,
1998c2ecf20Sopenharmony_ci					enum mlxsw_reg_ralue_op op,
2008c2ecf20Sopenharmony_ci					u32 tunnel_index)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	u16 ul_vr_id = mlxsw_sp_ipip_lb_ul_vr_id(ipip_entry->ol_lb);
2038c2ecf20Sopenharmony_ci	__be32 dip;
2048c2ecf20Sopenharmony_ci	int err;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	err = mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(mlxsw_sp, tunnel_index,
2078c2ecf20Sopenharmony_ci						   ipip_entry);
2088c2ecf20Sopenharmony_ci	if (err)
2098c2ecf20Sopenharmony_ci		return err;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	dip = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4,
2128c2ecf20Sopenharmony_ci					 ipip_entry->ol_dev).addr4;
2138c2ecf20Sopenharmony_ci	return mlxsw_sp_ipip_fib_entry_op_gre4_ralue(mlxsw_sp, be32_to_cpu(dip),
2148c2ecf20Sopenharmony_ci						     32, ul_vr_id, op,
2158c2ecf20Sopenharmony_ci						     tunnel_index);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic bool mlxsw_sp_ipip_tunnel_complete(enum mlxsw_sp_l3proto proto,
2198c2ecf20Sopenharmony_ci					  const struct net_device *ol_dev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr saddr = mlxsw_sp_ipip_netdev_saddr(proto, ol_dev);
2228c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr daddr = mlxsw_sp_ipip_netdev_daddr(proto, ol_dev);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Tunnels with unset local or remote address are valid in Linux and
2258c2ecf20Sopenharmony_ci	 * used for lightweight tunnels (LWT) and Non-Broadcast Multi-Access
2268c2ecf20Sopenharmony_ci	 * (NBMA) tunnels. In principle these can be offloaded, but the driver
2278c2ecf20Sopenharmony_ci	 * currently doesn't support this. So punt.
2288c2ecf20Sopenharmony_ci	 */
2298c2ecf20Sopenharmony_ci	return !mlxsw_sp_l3addr_is_zero(saddr) &&
2308c2ecf20Sopenharmony_ci	       !mlxsw_sp_l3addr_is_zero(daddr);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic bool mlxsw_sp_ipip_can_offload_gre4(const struct mlxsw_sp *mlxsw_sp,
2348c2ecf20Sopenharmony_ci					   const struct net_device *ol_dev,
2358c2ecf20Sopenharmony_ci					   enum mlxsw_sp_l3proto ol_proto)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct ip_tunnel *tunnel = netdev_priv(ol_dev);
2388c2ecf20Sopenharmony_ci	__be16 okflags = TUNNEL_KEY; /* We can't offload any other features. */
2398c2ecf20Sopenharmony_ci	bool inherit_ttl = tunnel->parms.iph.ttl == 0;
2408c2ecf20Sopenharmony_ci	bool inherit_tos = tunnel->parms.iph.tos & 0x1;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return (tunnel->parms.i_flags & ~okflags) == 0 &&
2438c2ecf20Sopenharmony_ci	       (tunnel->parms.o_flags & ~okflags) == 0 &&
2448c2ecf20Sopenharmony_ci	       inherit_ttl && inherit_tos &&
2458c2ecf20Sopenharmony_ci	       mlxsw_sp_ipip_tunnel_complete(MLXSW_SP_L3_PROTO_IPV4, ol_dev);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic struct mlxsw_sp_rif_ipip_lb_config
2498c2ecf20Sopenharmony_cimlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp,
2508c2ecf20Sopenharmony_ci				      const struct net_device *ol_dev)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct ip_tunnel_parm parms = mlxsw_sp_ipip_netdev_parms4(ol_dev);
2538c2ecf20Sopenharmony_ci	enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	lb_ipipt = mlxsw_sp_ipip_parms4_has_okey(parms) ?
2568c2ecf20Sopenharmony_ci		MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP :
2578c2ecf20Sopenharmony_ci		MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP;
2588c2ecf20Sopenharmony_ci	return (struct mlxsw_sp_rif_ipip_lb_config){
2598c2ecf20Sopenharmony_ci		.lb_ipipt = lb_ipipt,
2608c2ecf20Sopenharmony_ci		.okey = mlxsw_sp_ipip_parms4_okey(parms),
2618c2ecf20Sopenharmony_ci		.ul_protocol = MLXSW_SP_L3_PROTO_IPV4,
2628c2ecf20Sopenharmony_ci		.saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4,
2638c2ecf20Sopenharmony_ci						    ol_dev),
2648c2ecf20Sopenharmony_ci	};
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int
2688c2ecf20Sopenharmony_cimlxsw_sp_ipip_ol_netdev_change_gre4(struct mlxsw_sp *mlxsw_sp,
2698c2ecf20Sopenharmony_ci				    struct mlxsw_sp_ipip_entry *ipip_entry,
2708c2ecf20Sopenharmony_ci				    struct netlink_ext_ack *extack)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr old_saddr, new_saddr;
2738c2ecf20Sopenharmony_ci	union mlxsw_sp_l3addr old_daddr, new_daddr;
2748c2ecf20Sopenharmony_ci	struct ip_tunnel_parm new_parms;
2758c2ecf20Sopenharmony_ci	bool update_tunnel = false;
2768c2ecf20Sopenharmony_ci	bool update_decap = false;
2778c2ecf20Sopenharmony_ci	bool update_nhs = false;
2788c2ecf20Sopenharmony_ci	int err = 0;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	new_parms = mlxsw_sp_ipip_netdev_parms4(ipip_entry->ol_dev);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	new_saddr = mlxsw_sp_ipip_parms4_saddr(new_parms);
2838c2ecf20Sopenharmony_ci	old_saddr = mlxsw_sp_ipip_parms4_saddr(ipip_entry->parms4);
2848c2ecf20Sopenharmony_ci	new_daddr = mlxsw_sp_ipip_parms4_daddr(new_parms);
2858c2ecf20Sopenharmony_ci	old_daddr = mlxsw_sp_ipip_parms4_daddr(ipip_entry->parms4);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (!mlxsw_sp_l3addr_eq(&new_saddr, &old_saddr)) {
2888c2ecf20Sopenharmony_ci		u16 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		/* Since the local address has changed, if there is another
2918c2ecf20Sopenharmony_ci		 * tunnel with a matching saddr, both need to be demoted.
2928c2ecf20Sopenharmony_ci		 */
2938c2ecf20Sopenharmony_ci		if (mlxsw_sp_ipip_demote_tunnel_by_saddr(mlxsw_sp,
2948c2ecf20Sopenharmony_ci							 MLXSW_SP_L3_PROTO_IPV4,
2958c2ecf20Sopenharmony_ci							 new_saddr, ul_tb_id,
2968c2ecf20Sopenharmony_ci							 ipip_entry)) {
2978c2ecf20Sopenharmony_ci			mlxsw_sp_ipip_entry_demote_tunnel(mlxsw_sp, ipip_entry);
2988c2ecf20Sopenharmony_ci			return 0;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		update_tunnel = true;
3028c2ecf20Sopenharmony_ci	} else if ((mlxsw_sp_ipip_parms4_okey(ipip_entry->parms4) !=
3038c2ecf20Sopenharmony_ci		    mlxsw_sp_ipip_parms4_okey(new_parms)) ||
3048c2ecf20Sopenharmony_ci		   ipip_entry->parms4.link != new_parms.link) {
3058c2ecf20Sopenharmony_ci		update_tunnel = true;
3068c2ecf20Sopenharmony_ci	} else if (!mlxsw_sp_l3addr_eq(&new_daddr, &old_daddr)) {
3078c2ecf20Sopenharmony_ci		update_nhs = true;
3088c2ecf20Sopenharmony_ci	} else if (mlxsw_sp_ipip_parms4_ikey(ipip_entry->parms4) !=
3098c2ecf20Sopenharmony_ci		   mlxsw_sp_ipip_parms4_ikey(new_parms)) {
3108c2ecf20Sopenharmony_ci		update_decap = true;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (update_tunnel)
3148c2ecf20Sopenharmony_ci		err = __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
3158c2ecf20Sopenharmony_ci							  true, true, true,
3168c2ecf20Sopenharmony_ci							  extack);
3178c2ecf20Sopenharmony_ci	else if (update_nhs)
3188c2ecf20Sopenharmony_ci		err = __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
3198c2ecf20Sopenharmony_ci							  false, false, true,
3208c2ecf20Sopenharmony_ci							  extack);
3218c2ecf20Sopenharmony_ci	else if (update_decap)
3228c2ecf20Sopenharmony_ci		err = __mlxsw_sp_ipip_entry_update_tunnel(mlxsw_sp, ipip_entry,
3238c2ecf20Sopenharmony_ci							  false, false, false,
3248c2ecf20Sopenharmony_ci							  extack);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ipip_entry->parms4 = new_parms;
3278c2ecf20Sopenharmony_ci	return err;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = {
3318c2ecf20Sopenharmony_ci	.dev_type = ARPHRD_IPGRE,
3328c2ecf20Sopenharmony_ci	.ul_proto = MLXSW_SP_L3_PROTO_IPV4,
3338c2ecf20Sopenharmony_ci	.nexthop_update = mlxsw_sp_ipip_nexthop_update_gre4,
3348c2ecf20Sopenharmony_ci	.fib_entry_op = mlxsw_sp_ipip_fib_entry_op_gre4,
3358c2ecf20Sopenharmony_ci	.can_offload = mlxsw_sp_ipip_can_offload_gre4,
3368c2ecf20Sopenharmony_ci	.ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre4,
3378c2ecf20Sopenharmony_ci	.ol_netdev_change = mlxsw_sp_ipip_ol_netdev_change_gre4,
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ciconst struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[] = {
3418c2ecf20Sopenharmony_ci	[MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops,
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int mlxsw_sp_ipip_ecn_encap_init_one(struct mlxsw_sp *mlxsw_sp,
3458c2ecf20Sopenharmony_ci					    u8 inner_ecn, u8 outer_ecn)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	char tieem_pl[MLXSW_REG_TIEEM_LEN];
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	mlxsw_reg_tieem_pack(tieem_pl, inner_ecn, outer_ecn);
3508c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tieem), tieem_pl);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ciint mlxsw_sp_ipip_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int i;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* Iterate over inner ECN values */
3588c2ecf20Sopenharmony_ci	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
3598c2ecf20Sopenharmony_ci		u8 outer_ecn = INET_ECN_encapsulate(0, i);
3608c2ecf20Sopenharmony_ci		int err;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		err = mlxsw_sp_ipip_ecn_encap_init_one(mlxsw_sp, i, outer_ecn);
3638c2ecf20Sopenharmony_ci		if (err)
3648c2ecf20Sopenharmony_ci			return err;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int mlxsw_sp_ipip_ecn_decap_init_one(struct mlxsw_sp *mlxsw_sp,
3718c2ecf20Sopenharmony_ci					    u8 inner_ecn, u8 outer_ecn)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	char tidem_pl[MLXSW_REG_TIDEM_LEN];
3748c2ecf20Sopenharmony_ci	u8 new_inner_ecn;
3758c2ecf20Sopenharmony_ci	bool trap_en;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
3788c2ecf20Sopenharmony_ci						  &trap_en);
3798c2ecf20Sopenharmony_ci	mlxsw_reg_tidem_pack(tidem_pl, outer_ecn, inner_ecn, new_inner_ecn,
3808c2ecf20Sopenharmony_ci			     trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
3818c2ecf20Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tidem), tidem_pl);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ciint mlxsw_sp_ipip_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	int i, j, err;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* Iterate over inner ECN values */
3898c2ecf20Sopenharmony_ci	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
3908c2ecf20Sopenharmony_ci		/* Iterate over outer ECN values */
3918c2ecf20Sopenharmony_ci		for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
3928c2ecf20Sopenharmony_ci			err = mlxsw_sp_ipip_ecn_decap_init_one(mlxsw_sp, i, j);
3938c2ecf20Sopenharmony_ci			if (err)
3948c2ecf20Sopenharmony_ci				return err;
3958c2ecf20Sopenharmony_ci		}
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
400