866 lines · 21.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Bareudp: UDP  tunnel encasulation for different Payload types like * MPLS, NSH, IP, etc. * Copyright (c) 2019 Nokia, Inc. * Authors:  Martin Varghese, <martin.varghese@nokia.com> */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/module.h>#include <linux/etherdevice.h>#include <linux/hash.h>#include <net/dst_metadata.h>#include <net/gro_cells.h>#include <net/rtnetlink.h>#include <net/protocol.h>#include <net/ip6_tunnel.h>#include <net/ip_tunnels.h>#include <net/udp_tunnel.h>#include <net/bareudp.h> #define BAREUDP_BASE_HLEN sizeof(struct udphdr)#define BAREUDP_IPV4_HLEN (sizeof(struct iphdr) + \			   sizeof(struct udphdr))#define BAREUDP_IPV6_HLEN (sizeof(struct ipv6hdr) + \			   sizeof(struct udphdr)) static bool log_ecn_error = true;module_param(log_ecn_error, bool, 0644);MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); /* per-network namespace private data for this module */ static unsigned int bareudp_net_id; struct bareudp_net {	struct list_head        bareudp_list;	struct mutex		lock;}; struct bareudp_conf {	__be16 ethertype;	__be16 port;	u16 sport_min;	bool multi_proto_mode;}; /* Pseudo network device */struct bareudp_dev {	struct net         *net;        /* netns for packet i/o */	struct net_device  *dev;        /* netdev for bareudp tunnel */	__be16		   ethertype;	__be16             port;	u16	           sport_min;	bool               multi_proto_mode;	struct sock        __rcu *sk;	struct list_head   next;        /* bareudp node  on namespace list */	struct gro_cells   gro_cells;}; static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb){	struct metadata_dst *tun_dst = NULL;	IP_TUNNEL_DECLARE_FLAGS(key) = { };	struct bareudp_dev *bareudp;	unsigned short family;	unsigned int len;	__be16 proto;	void *oiph;	int err;	int nh; 	bareudp = rcu_dereference_sk_user_data(sk);	if (!bareudp)		goto drop; 	if (skb->protocol ==  htons(ETH_P_IP))		family = AF_INET;	else		family = AF_INET6; 	if (bareudp->ethertype == htons(ETH_P_IP)) {		__u8 ipversion; 		if (skb_copy_bits(skb, BAREUDP_BASE_HLEN, &ipversion,				  sizeof(ipversion))) {			dev_dstats_rx_dropped(bareudp->dev);			goto drop;		}		ipversion >>= 4; 		if (ipversion == 4) {			proto = htons(ETH_P_IP);		} else if (ipversion == 6 && bareudp->multi_proto_mode) {			proto = htons(ETH_P_IPV6);		} else {			dev_dstats_rx_dropped(bareudp->dev);			goto drop;		}	} else if (bareudp->ethertype == htons(ETH_P_MPLS_UC)) {		struct iphdr *tunnel_hdr; 		tunnel_hdr = (struct iphdr *)skb_network_header(skb);		if (tunnel_hdr->version == 4) {			if (!ipv4_is_multicast(tunnel_hdr->daddr)) {				proto = bareudp->ethertype;			} else if (bareudp->multi_proto_mode &&				   ipv4_is_multicast(tunnel_hdr->daddr)) {				proto = htons(ETH_P_MPLS_MC);			} else {				dev_dstats_rx_dropped(bareudp->dev);				goto drop;			}		} else {			int addr_type;			struct ipv6hdr *tunnel_hdr_v6; 			tunnel_hdr_v6 = (struct ipv6hdr *)skb_network_header(skb);			addr_type =			ipv6_addr_type((struct in6_addr *)&tunnel_hdr_v6->daddr);			if (!(addr_type & IPV6_ADDR_MULTICAST)) {				proto = bareudp->ethertype;			} else if (bareudp->multi_proto_mode &&				   (addr_type & IPV6_ADDR_MULTICAST)) {				proto = htons(ETH_P_MPLS_MC);			} else {				dev_dstats_rx_dropped(bareudp->dev);				goto drop;			}		}	} else {		proto = bareudp->ethertype;	} 	if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN,				 proto,				 !net_eq(bareudp->net,				 dev_net(bareudp->dev)))) {		dev_dstats_rx_dropped(bareudp->dev);		goto drop;	} 	__set_bit(IP_TUNNEL_KEY_BIT, key); 	tun_dst = udp_tun_rx_dst(skb, family, key, 0, 0);	if (!tun_dst) {		dev_dstats_rx_dropped(bareudp->dev);		goto drop;	}	skb_dst_set(skb, &tun_dst->dst);	skb->dev = bareudp->dev;	skb_reset_mac_header(skb); 	/* Save offset of outer header relative to skb->head,	 * because we are going to reset the network header to the inner header	 * and might change skb->head.	 */	nh = skb_network_header(skb) - skb->head; 	skb_reset_network_header(skb); 	if (!pskb_inet_may_pull(skb)) {		DEV_STATS_INC(bareudp->dev, rx_length_errors);		DEV_STATS_INC(bareudp->dev, rx_errors);		goto drop;	} 	/* Get the outer header. */	oiph = skb->head + nh; 	if (!ipv6_mod_enabled() || family == AF_INET)		err = IP_ECN_decapsulate(oiph, skb);	else		err = IP6_ECN_decapsulate(oiph, skb); 	if (unlikely(err)) {		if (log_ecn_error) {			if  (!ipv6_mod_enabled() || family == AF_INET)				net_info_ratelimited("non-ECT from %pI4 "						     "with TOS=%#x\n",						     &((struct iphdr *)oiph)->saddr,						     ((struct iphdr *)oiph)->tos);			else				net_info_ratelimited("non-ECT from %pI6\n",						     &((struct ipv6hdr *)oiph)->saddr);		}		if (err > 1) {			DEV_STATS_INC(bareudp->dev, rx_frame_errors);			DEV_STATS_INC(bareudp->dev, rx_errors);			goto drop;		}	} 	len = skb->len;	err = gro_cells_receive(&bareudp->gro_cells, skb);	if (likely(err == NET_RX_SUCCESS))		dev_dstats_rx_add(bareudp->dev, len); 	return 0;drop:	/* Consume bad packet */	kfree_skb(skb); 	return 0;} static int bareudp_err_lookup(struct sock *sk, struct sk_buff *skb){	return 0;} static int bareudp_init(struct net_device *dev){	struct bareudp_dev *bareudp = netdev_priv(dev);	int err; 	err = gro_cells_init(&bareudp->gro_cells, dev);	if (err)		return err; 	return 0;} static void bareudp_uninit(struct net_device *dev){	struct bareudp_dev *bareudp = netdev_priv(dev); 	gro_cells_destroy(&bareudp->gro_cells);} static struct sock *bareudp_create_sock(struct net *net, __be16 port){	struct udp_port_cfg udp_conf;	struct socket *sock;	int err; 	memset(&udp_conf, 0, sizeof(udp_conf)); 	if (ipv6_mod_enabled())		udp_conf.family = AF_INET6;	else		udp_conf.family = AF_INET; 	udp_conf.local_udp_port = port;	/* Open UDP socket */	err = udp_sock_create(net, &udp_conf, &sock);	if (err < 0)		return ERR_PTR(err); 	udp_allow_gso(sock->sk);	return sock->sk;} /* Create new listen socket if needed */static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port){	struct udp_tunnel_sock_cfg tunnel_cfg;	struct sock *sk; 	sk = bareudp_create_sock(bareudp->net, port);	if (IS_ERR(sk))		return PTR_ERR(sk); 	/* Mark socket as an encapsulation socket */	memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));	tunnel_cfg.sk_user_data = bareudp;	tunnel_cfg.encap_type = 1;	tunnel_cfg.encap_rcv = bareudp_udp_encap_recv;	tunnel_cfg.encap_err_lookup = bareudp_err_lookup;	tunnel_cfg.encap_destroy = NULL;	setup_udp_tunnel_sock(bareudp->net, sk, &tunnel_cfg); 	rcu_assign_pointer(bareudp->sk, sk);	return 0;} static int bareudp_open(struct net_device *dev){	struct bareudp_dev *bareudp = netdev_priv(dev); 	return bareudp_socket_create(bareudp, bareudp->port);} static void bareudp_sock_release(struct bareudp_dev *bareudp){	struct sock *sk; 	sk = rtnl_dereference(bareudp->sk);	rcu_assign_pointer(bareudp->sk, NULL);	udp_tunnel_sock_release(sk);} static int bareudp_stop(struct net_device *dev){	struct bareudp_dev *bareudp = netdev_priv(dev); 	bareudp_sock_release(bareudp);	return 0;} static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,			    struct bareudp_dev *bareudp,			    const struct ip_tunnel_info *info){	bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);	bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));	bool use_cache = ip_tunnel_dst_cache_usable(skb, info);	struct sock *sk = rcu_dereference(bareudp->sk);	const struct ip_tunnel_key *key = &info->key;	struct rtable *rt;	__be16 sport, df;	int min_headroom;	__u8 tos, ttl;	__be32 saddr;	int err; 	if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB)))		return -EINVAL; 	if (!sk)		return -ESHUTDOWN; 	sport = udp_flow_src_port(bareudp->net, skb,				  bareudp->sport_min, USHRT_MAX,				  true);	rt = udp_tunnel_dst_lookup(skb, dev, bareudp->net, 0, &saddr, &info->key,				   sport, bareudp->port, key->tos,				   use_cache ?				   (struct dst_cache *)&info->dst_cache : NULL); 	if (IS_ERR(rt))		return PTR_ERR(rt); 	skb_tunnel_check_pmtu(skb, &rt->dst,			      BAREUDP_IPV4_HLEN + info->options_len, false); 	tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);	ttl = key->ttl;	df = test_bit(IP_TUNNEL_DONT_FRAGMENT_BIT, key->tun_flags) ?	     htons(IP_DF) : 0;	skb_scrub_packet(skb, xnet); 	err = -ENOSPC;	if (!skb_pull(skb, skb_network_offset(skb)))		goto free_dst; 	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len +		BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr); 	err = skb_cow_head(skb, min_headroom);	if (unlikely(err))		goto free_dst; 	err = udp_tunnel_handle_offloads(skb, udp_sum);	if (err)		goto free_dst; 	skb_set_inner_protocol(skb, bareudp->ethertype);	udp_tunnel_xmit_skb(rt, sk, skb, saddr, info->key.u.ipv4.dst,			    tos, ttl, df, sport, bareudp->port,			    !net_eq(bareudp->net, dev_net(bareudp->dev)),			    !test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags),			    0);	return 0; free_dst:	dst_release(&rt->dst);	return err;} static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,			     struct bareudp_dev *bareudp,			     const struct ip_tunnel_info *info){	bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);	bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));	bool use_cache = ip_tunnel_dst_cache_usable(skb, info);	struct sock *sk = rcu_dereference(bareudp->sk);	const struct ip_tunnel_key *key = &info->key;	struct dst_entry *dst = NULL;	struct in6_addr saddr, daddr;	int min_headroom;	__u8 prio, ttl;	__be16 sport;	int err; 	if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB)))		return -EINVAL; 	if (!sk)		return -ESHUTDOWN; 	sport = udp_flow_src_port(bareudp->net, skb,				  bareudp->sport_min, USHRT_MAX,				  true);	dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sk, 0, &saddr,				     key, sport, bareudp->port, key->tos,				     use_cache ?				     (struct dst_cache *) &info->dst_cache : NULL);	if (IS_ERR(dst))		return PTR_ERR(dst); 	skb_tunnel_check_pmtu(skb, dst, BAREUDP_IPV6_HLEN + info->options_len,			      false); 	prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);	ttl = key->ttl; 	skb_scrub_packet(skb, xnet); 	err = -ENOSPC;	if (!skb_pull(skb, skb_network_offset(skb)))		goto free_dst; 	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len +		BAREUDP_BASE_HLEN + info->options_len + sizeof(struct ipv6hdr); 	err = skb_cow_head(skb, min_headroom);	if (unlikely(err))		goto free_dst; 	err = udp_tunnel_handle_offloads(skb, udp_sum);	if (err)		goto free_dst; 	daddr = info->key.u.ipv6.dst;	udp_tunnel6_xmit_skb(dst, sk, skb, dev,			     &saddr, &daddr, prio, ttl,			     info->key.label, sport, bareudp->port,			     !test_bit(IP_TUNNEL_CSUM_BIT,				       info->key.tun_flags),			     0);	return 0; free_dst:	dst_release(dst);	return err;} static bool bareudp_proto_valid(struct bareudp_dev *bareudp, __be16 proto){	if (bareudp->ethertype == proto)		return true; 	if (!bareudp->multi_proto_mode)		return false; 	if (bareudp->ethertype == htons(ETH_P_MPLS_UC) &&	    proto == htons(ETH_P_MPLS_MC))		return true; 	if (bareudp->ethertype == htons(ETH_P_IP) &&	    proto == htons(ETH_P_IPV6))		return true; 	return false;} static netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev){	struct bareudp_dev *bareudp = netdev_priv(dev);	struct ip_tunnel_info *info = NULL;	int err; 	if (!bareudp_proto_valid(bareudp, skb->protocol)) {		err = -EINVAL;		goto tx_error;	} 	info = skb_tunnel_info(skb);	if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {		err = -EINVAL;		goto tx_error;	} 	rcu_read_lock();	if (ipv6_mod_enabled() && info->mode & IP_TUNNEL_INFO_IPV6)		err = bareudp6_xmit_skb(skb, dev, bareudp, info);	else		err = bareudp_xmit_skb(skb, dev, bareudp, info); 	rcu_read_unlock(); 	if (likely(!err))		return NETDEV_TX_OK;tx_error:	dev_kfree_skb(skb); 	if (err == -ELOOP)		DEV_STATS_INC(dev, collisions);	else if (err == -ENETUNREACH)		DEV_STATS_INC(dev, tx_carrier_errors); 	DEV_STATS_INC(dev, tx_errors);	return NETDEV_TX_OK;} static int bareudp_fill_metadata_dst(struct net_device *dev,				     struct sk_buff *skb)

Lines 1–500 of 866.