18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef __NET_DST_METADATA_H
38c2ecf20Sopenharmony_ci#define __NET_DST_METADATA_H 1
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
68c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h>
78c2ecf20Sopenharmony_ci#include <net/dst.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_cienum metadata_type {
108c2ecf20Sopenharmony_ci	METADATA_IP_TUNNEL,
118c2ecf20Sopenharmony_ci	METADATA_HW_PORT_MUX,
128c2ecf20Sopenharmony_ci};
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct hw_port_info {
158c2ecf20Sopenharmony_ci	struct net_device *lower_dev;
168c2ecf20Sopenharmony_ci	u32 port_id;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct metadata_dst {
208c2ecf20Sopenharmony_ci	struct dst_entry		dst;
218c2ecf20Sopenharmony_ci	enum metadata_type		type;
228c2ecf20Sopenharmony_ci	union {
238c2ecf20Sopenharmony_ci		struct ip_tunnel_info	tun_info;
248c2ecf20Sopenharmony_ci		struct hw_port_info	port_info;
258c2ecf20Sopenharmony_ci	} u;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic inline struct metadata_dst *skb_metadata_dst(const struct sk_buff *skb)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct metadata_dst *md_dst = (struct metadata_dst *) skb_dst(skb);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (md_dst && md_dst->dst.flags & DST_METADATA)
338c2ecf20Sopenharmony_ci		return md_dst;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return NULL;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline struct ip_tunnel_info *
398c2ecf20Sopenharmony_ciskb_tunnel_info(const struct sk_buff *skb)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct metadata_dst *md_dst = skb_metadata_dst(skb);
428c2ecf20Sopenharmony_ci	struct dst_entry *dst;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (md_dst && md_dst->type == METADATA_IP_TUNNEL)
458c2ecf20Sopenharmony_ci		return &md_dst->u.tun_info;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	dst = skb_dst(skb);
488c2ecf20Sopenharmony_ci	if (dst && dst->lwtstate &&
498c2ecf20Sopenharmony_ci	    (dst->lwtstate->type == LWTUNNEL_ENCAP_IP ||
508c2ecf20Sopenharmony_ci	     dst->lwtstate->type == LWTUNNEL_ENCAP_IP6))
518c2ecf20Sopenharmony_ci		return lwt_tun_info(dst->lwtstate);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return NULL;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic inline bool skb_valid_dst(const struct sk_buff *skb)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return dst && !(dst->flags & DST_METADATA);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a,
648c2ecf20Sopenharmony_ci				       const struct sk_buff *skb_b)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	const struct metadata_dst *a, *b;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (!(skb_a->_skb_refdst | skb_b->_skb_refdst))
698c2ecf20Sopenharmony_ci		return 0;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	a = (const struct metadata_dst *) skb_dst(skb_a);
728c2ecf20Sopenharmony_ci	b = (const struct metadata_dst *) skb_dst(skb_b);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (!a != !b || a->type != b->type)
758c2ecf20Sopenharmony_ci		return 1;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	switch (a->type) {
788c2ecf20Sopenharmony_ci	case METADATA_HW_PORT_MUX:
798c2ecf20Sopenharmony_ci		return memcmp(&a->u.port_info, &b->u.port_info,
808c2ecf20Sopenharmony_ci			      sizeof(a->u.port_info));
818c2ecf20Sopenharmony_ci	case METADATA_IP_TUNNEL:
828c2ecf20Sopenharmony_ci		return memcmp(&a->u.tun_info, &b->u.tun_info,
838c2ecf20Sopenharmony_ci			      sizeof(a->u.tun_info) +
848c2ecf20Sopenharmony_ci					 a->u.tun_info.options_len);
858c2ecf20Sopenharmony_ci	default:
868c2ecf20Sopenharmony_ci		return 1;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_civoid metadata_dst_free(struct metadata_dst *);
918c2ecf20Sopenharmony_cistruct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type,
928c2ecf20Sopenharmony_ci					gfp_t flags);
938c2ecf20Sopenharmony_civoid metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst);
948c2ecf20Sopenharmony_cistruct metadata_dst __percpu *
958c2ecf20Sopenharmony_cimetadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline struct metadata_dst *tun_rx_dst(int md_size)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct metadata_dst *tun_dst;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	tun_dst = metadata_dst_alloc(md_size, METADATA_IP_TUNNEL, GFP_ATOMIC);
1028c2ecf20Sopenharmony_ci	if (!tun_dst)
1038c2ecf20Sopenharmony_ci		return NULL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	tun_dst->u.tun_info.options_len = 0;
1068c2ecf20Sopenharmony_ci	tun_dst->u.tun_info.mode = 0;
1078c2ecf20Sopenharmony_ci	return tun_dst;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct metadata_dst *md_dst = skb_metadata_dst(skb);
1138c2ecf20Sopenharmony_ci	int md_size;
1148c2ecf20Sopenharmony_ci	struct metadata_dst *new_md;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (!md_dst || md_dst->type != METADATA_IP_TUNNEL)
1178c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	md_size = md_dst->u.tun_info.options_len;
1208c2ecf20Sopenharmony_ci	new_md = metadata_dst_alloc(md_size, METADATA_IP_TUNNEL, GFP_ATOMIC);
1218c2ecf20Sopenharmony_ci	if (!new_md)
1228c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	memcpy(&new_md->u.tun_info, &md_dst->u.tun_info,
1258c2ecf20Sopenharmony_ci	       sizeof(struct ip_tunnel_info) + md_size);
1268c2ecf20Sopenharmony_ci	skb_dst_drop(skb);
1278c2ecf20Sopenharmony_ci	skb_dst_set(skb, &new_md->dst);
1288c2ecf20Sopenharmony_ci	return new_md;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline struct ip_tunnel_info *skb_tunnel_info_unclone(struct sk_buff *skb)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct metadata_dst *dst;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	dst = tun_dst_unclone(skb);
1368c2ecf20Sopenharmony_ci	if (IS_ERR(dst))
1378c2ecf20Sopenharmony_ci		return NULL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return &dst->u.tun_info;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic inline struct metadata_dst *__ip_tun_set_dst(__be32 saddr,
1438c2ecf20Sopenharmony_ci						    __be32 daddr,
1448c2ecf20Sopenharmony_ci						    __u8 tos, __u8 ttl,
1458c2ecf20Sopenharmony_ci						    __be16 tp_dst,
1468c2ecf20Sopenharmony_ci						    __be16 flags,
1478c2ecf20Sopenharmony_ci						    __be64 tunnel_id,
1488c2ecf20Sopenharmony_ci						    int md_size)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct metadata_dst *tun_dst;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	tun_dst = tun_rx_dst(md_size);
1538c2ecf20Sopenharmony_ci	if (!tun_dst)
1548c2ecf20Sopenharmony_ci		return NULL;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ip_tunnel_key_init(&tun_dst->u.tun_info.key,
1578c2ecf20Sopenharmony_ci			   saddr, daddr, tos, ttl,
1588c2ecf20Sopenharmony_ci			   0, 0, tp_dst, tunnel_id, flags);
1598c2ecf20Sopenharmony_ci	return tun_dst;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic inline struct metadata_dst *ip_tun_rx_dst(struct sk_buff *skb,
1638c2ecf20Sopenharmony_ci						 __be16 flags,
1648c2ecf20Sopenharmony_ci						 __be64 tunnel_id,
1658c2ecf20Sopenharmony_ci						 int md_size)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	const struct iphdr *iph = ip_hdr(skb);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return __ip_tun_set_dst(iph->saddr, iph->daddr, iph->tos, iph->ttl,
1708c2ecf20Sopenharmony_ci				0, flags, tunnel_id, md_size);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic inline struct metadata_dst *__ipv6_tun_set_dst(const struct in6_addr *saddr,
1748c2ecf20Sopenharmony_ci						      const struct in6_addr *daddr,
1758c2ecf20Sopenharmony_ci						      __u8 tos, __u8 ttl,
1768c2ecf20Sopenharmony_ci						      __be16 tp_dst,
1778c2ecf20Sopenharmony_ci						      __be32 label,
1788c2ecf20Sopenharmony_ci						      __be16 flags,
1798c2ecf20Sopenharmony_ci						      __be64 tunnel_id,
1808c2ecf20Sopenharmony_ci						      int md_size)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct metadata_dst *tun_dst;
1838c2ecf20Sopenharmony_ci	struct ip_tunnel_info *info;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	tun_dst = tun_rx_dst(md_size);
1868c2ecf20Sopenharmony_ci	if (!tun_dst)
1878c2ecf20Sopenharmony_ci		return NULL;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	info = &tun_dst->u.tun_info;
1908c2ecf20Sopenharmony_ci	info->mode = IP_TUNNEL_INFO_IPV6;
1918c2ecf20Sopenharmony_ci	info->key.tun_flags = flags;
1928c2ecf20Sopenharmony_ci	info->key.tun_id = tunnel_id;
1938c2ecf20Sopenharmony_ci	info->key.tp_src = 0;
1948c2ecf20Sopenharmony_ci	info->key.tp_dst = tp_dst;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	info->key.u.ipv6.src = *saddr;
1978c2ecf20Sopenharmony_ci	info->key.u.ipv6.dst = *daddr;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	info->key.tos = tos;
2008c2ecf20Sopenharmony_ci	info->key.ttl = ttl;
2018c2ecf20Sopenharmony_ci	info->key.label = label;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return tun_dst;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic inline struct metadata_dst *ipv6_tun_rx_dst(struct sk_buff *skb,
2078c2ecf20Sopenharmony_ci						   __be16 flags,
2088c2ecf20Sopenharmony_ci						   __be64 tunnel_id,
2098c2ecf20Sopenharmony_ci						   int md_size)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	const struct ipv6hdr *ip6h = ipv6_hdr(skb);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return __ipv6_tun_set_dst(&ip6h->saddr, &ip6h->daddr,
2148c2ecf20Sopenharmony_ci				  ipv6_get_dsfield(ip6h), ip6h->hop_limit,
2158c2ecf20Sopenharmony_ci				  0, ip6_flowlabel(ip6h), flags, tunnel_id,
2168c2ecf20Sopenharmony_ci				  md_size);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci#endif /* __NET_DST_METADATA_H */
219