1203 lines · 32.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2013 Nicira, Inc. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/types.h>#include <linux/kernel.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/in.h>#include <linux/if_arp.h>#include <linux/init.h>#include <linux/in6.h>#include <linux/inetdevice.h>#include <linux/netfilter_ipv4.h>#include <linux/etherdevice.h>#include <linux/if_ether.h>#include <linux/if_vlan.h>#include <linux/static_key.h> #include <net/ip.h>#include <net/icmp.h>#include <net/protocol.h>#include <net/ip_tunnels.h>#include <net/ip6_tunnel.h>#include <net/ip6_checksum.h>#include <net/arp.h>#include <net/checksum.h>#include <net/dsfield.h>#include <net/inet_ecn.h>#include <net/xfrm.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/rtnetlink.h>#include <net/dst_metadata.h>#include <net/geneve.h>#include <net/vxlan.h>#include <net/erspan.h> const struct ip_tunnel_encap_ops __rcu *		iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly;EXPORT_SYMBOL(iptun_encaps); const struct ip6_tnl_encap_ops __rcu *		ip6tun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly;EXPORT_SYMBOL(ip6tun_encaps); void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,		   __be32 src, __be32 dst, __u8 proto,		   __u8 tos, __u8 ttl, __be16 df, bool xnet,		   u16 ipcb_flags){	int pkt_len = skb->len - skb_inner_network_offset(skb);	struct net *net = dev_net(rt->dst.dev);	struct net_device *dev = skb->dev;	struct iphdr *iph;	int err; 	if (unlikely(dev_recursion_level() > IP_TUNNEL_RECURSION_LIMIT)) {		if (dev) {			net_crit_ratelimited("Dead loop on virtual device %s (net %llu), fix it urgently!\n",					     dev->name, dev_net(dev)->net_cookie);			DEV_STATS_INC(dev, tx_errors);		}		ip_rt_put(rt);		kfree_skb_reason(skb, SKB_DROP_REASON_RECURSION_LIMIT);		return;	} 	dev_xmit_recursion_inc(); 	skb_scrub_packet(skb, xnet); 	skb_clear_hash_if_not_l4(skb);	skb_dst_set(skb, &rt->dst);	memset(IPCB(skb), 0, sizeof(*IPCB(skb)));	IPCB(skb)->flags = ipcb_flags; 	/* Push down and install the IP header. */	skb_push(skb, sizeof(struct iphdr));	skb_reset_network_header(skb); 	iph = ip_hdr(skb); 	iph->version	=	4;	iph->ihl	=	sizeof(struct iphdr) >> 2;	iph->frag_off	=	ip_mtu_locked(&rt->dst) ? 0 : df;	iph->protocol	=	proto;	iph->tos	=	tos;	iph->daddr	=	dst;	iph->saddr	=	src;	iph->ttl	=	ttl;	__ip_select_ident(net, iph, skb_shinfo(skb)->gso_segs ?: 1); 	err = ip_local_out(net, sk, skb); 	if (dev) {		if (unlikely(net_xmit_eval(err)))			pkt_len = 0;		iptunnel_xmit_stats(dev, pkt_len);	} 	dev_xmit_recursion_dec();}EXPORT_SYMBOL_GPL(iptunnel_xmit); int __iptunnel_pull_header(struct sk_buff *skb, int hdr_len,			   __be16 inner_proto, bool raw_proto, bool xnet){	if (unlikely(!pskb_may_pull(skb, hdr_len)))		return -ENOMEM; 	skb_pull_rcsum(skb, hdr_len); 	if (!raw_proto && inner_proto == htons(ETH_P_TEB)) {		struct ethhdr *eh; 		if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))			return -ENOMEM; 		eh = (struct ethhdr *)skb->data;		if (likely(eth_proto_is_802_3(eh->h_proto)))			skb->protocol = eh->h_proto;		else			skb->protocol = htons(ETH_P_802_2); 	} else {		skb->protocol = inner_proto;	} 	skb_clear_hash_if_not_l4(skb);	__vlan_hwaccel_clear_tag(skb);	skb_set_queue_mapping(skb, 0);	skb_scrub_packet(skb, xnet); 	return iptunnel_pull_offloads(skb);}EXPORT_SYMBOL_GPL(__iptunnel_pull_header); struct metadata_dst *iptunnel_metadata_reply(struct metadata_dst *md,					     gfp_t flags){	IP_TUNNEL_DECLARE_FLAGS(tun_flags) = { };	struct metadata_dst *res;	struct ip_tunnel_info *dst, *src; 	if (!md || md->type != METADATA_IP_TUNNEL ||	    md->u.tun_info.mode & IP_TUNNEL_INFO_TX)		return NULL; 	src = &md->u.tun_info;	res = metadata_dst_alloc(src->options_len, METADATA_IP_TUNNEL, flags);	if (!res)		return NULL; 	dst = &res->u.tun_info;	dst->key.tun_id = src->key.tun_id;	if (src->mode & IP_TUNNEL_INFO_IPV6)		memcpy(&dst->key.u.ipv6.dst, &src->key.u.ipv6.src,		       sizeof(struct in6_addr));	else		dst->key.u.ipv4.dst = src->key.u.ipv4.src;	ip_tunnel_flags_copy(dst->key.tun_flags, src->key.tun_flags);	dst->mode = src->mode | IP_TUNNEL_INFO_TX;	ip_tunnel_info_opts_set(dst, ip_tunnel_info_opts(src),				src->options_len, tun_flags); 	return res;}EXPORT_SYMBOL_GPL(iptunnel_metadata_reply); int iptunnel_handle_offloads(struct sk_buff *skb,			     int gso_type_mask){	int err; 	if (likely(!skb->encapsulation)) {		skb_reset_inner_headers(skb);		skb->encapsulation = 1;	} 	if (skb_is_gso(skb)) {		err = skb_header_unclone(skb, GFP_ATOMIC);		if (unlikely(err))			return err;		skb_shinfo(skb)->gso_type |= gso_type_mask;		return 0;	} 	if (skb->ip_summed != CHECKSUM_PARTIAL) {		skb->ip_summed = CHECKSUM_NONE;		/* We clear encapsulation here to prevent badly-written		 * drivers potentially deciding to offload an inner checksum		 * if we set CHECKSUM_PARTIAL on the outer header.		 * This should go away when the drivers are all fixed.		 */		skb->encapsulation = 0;	} 	return 0;}EXPORT_SYMBOL_GPL(iptunnel_handle_offloads); /** * iptunnel_pmtud_build_icmp() - Build ICMP error message for PMTUD * @skb:	Original packet with L2 header * @mtu:	MTU value for ICMP error * * Return: length on success, negative error code if message couldn't be built. */static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu){	const struct iphdr *iph;	struct icmphdr *icmph;	struct iphdr *niph;	struct ethhdr eh;	int len, err; 	if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct iphdr)))		return -EINVAL; 	if (skb_is_gso(skb))		skb_gso_reset(skb); 	skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);	pskb_pull(skb, ETH_HLEN); 	err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph));	if (err)		return err; 	len = skb->len + sizeof(*icmph);	err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN);	if (err)		return err;	iph = ip_hdr(skb);	icmph = skb_push(skb, sizeof(*icmph));	*icmph = (struct icmphdr) {		.type			= ICMP_DEST_UNREACH,		.code			= ICMP_FRAG_NEEDED,		.checksum		= 0,		.un.frag.__unused	= 0,		.un.frag.mtu		= htons(mtu),	};	icmph->checksum = csum_fold(skb_checksum(skb, 0, len, 0));	skb_reset_transport_header(skb); 	niph = skb_push(skb, sizeof(*niph));	*niph = (struct iphdr) {		.ihl			= sizeof(*niph) / 4u,		.version 		= 4,		.tos 			= 0,		.tot_len		= htons(len + sizeof(*niph)),		.id			= 0,		.frag_off		= htons(IP_DF),		.ttl			= iph->ttl,		.protocol		= IPPROTO_ICMP,		.saddr			= iph->daddr,		.daddr			= iph->saddr,	};	ip_send_check(niph);	skb_reset_network_header(skb); 	skb->ip_summed = CHECKSUM_NONE; 	eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);	skb_reset_mac_header(skb); 	if (skb_valid_dst(skb))		skb_dst_drop(skb); 	return skb->len;} /** * iptunnel_pmtud_check_icmp() - Trigger ICMP reply if needed and allowed * @skb:	Buffer being sent by encapsulation, L2 headers expected * @mtu:	Network MTU for path * * Return: 0 for no ICMP reply, length if built, negative value on error. */static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu){	const struct iphdr *iph = ip_hdr(skb); 	if (mtu < 576 || iph->frag_off != htons(IP_DF))		return 0; 	if (ipv4_is_lbcast(iph->daddr)  || ipv4_is_multicast(iph->daddr) ||	    ipv4_is_zeronet(iph->saddr) || ipv4_is_loopback(iph->saddr)  ||	    ipv4_is_lbcast(iph->saddr)  || ipv4_is_multicast(iph->saddr))		return 0; 	if (iph->protocol == IPPROTO_ICMP) {		const struct icmphdr *icmph; 		if (!pskb_network_may_pull(skb, iph->ihl * 4 +						offsetofend(struct icmphdr, type)))			return 0;		iph = ip_hdr(skb);		icmph = (void *)iph + iph->ihl * 4;		if (icmp_is_err(icmph->type))			return 0;	}	return iptunnel_pmtud_build_icmp(skb, mtu);} #if IS_ENABLED(CONFIG_IPV6)/** * iptunnel_pmtud_build_icmpv6() - Build ICMPv6 error message for PMTUD * @skb:	Original packet with L2 header * @mtu:	MTU value for ICMPv6 error * * Return: length on success, negative error code if message couldn't be built. */static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu){	const struct ipv6hdr *ip6h;	struct icmp6hdr *icmp6h;	struct ipv6hdr *nip6h;	struct ethhdr eh;	int len, err;	__wsum csum; 	if (!pskb_may_pull(skb, ETH_HLEN + sizeof(struct ipv6hdr)))		return -EINVAL; 	if (skb_is_gso(skb))		skb_gso_reset(skb); 	skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN);	pskb_pull(skb, ETH_HLEN); 	err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h));	if (err)		return err; 	len = skb->len + sizeof(*icmp6h);	err = skb_cow(skb, sizeof(*nip6h) + sizeof(*icmp6h) + ETH_HLEN);	if (err)		return err; 	ip6h = ipv6_hdr(skb);	icmp6h = skb_push(skb, sizeof(*icmp6h));	*icmp6h = (struct icmp6hdr) {		.icmp6_type		= ICMPV6_PKT_TOOBIG,		.icmp6_code		= 0,		.icmp6_cksum		= 0,		.icmp6_mtu		= htonl(mtu),	};	skb_reset_transport_header(skb); 	nip6h = skb_push(skb, sizeof(*nip6h));	*nip6h = (struct ipv6hdr) {		.priority		= 0,		.version		= 6,		.flow_lbl		= { 0 },		.payload_len		= htons(len),		.nexthdr		= IPPROTO_ICMPV6,		.hop_limit		= ip6h->hop_limit,		.saddr			= ip6h->daddr,		.daddr			= ip6h->saddr,	};	skb_reset_network_header(skb); 	csum = skb_checksum(skb, skb_transport_offset(skb), len, 0);	icmp6h->icmp6_cksum = csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr, len,					      IPPROTO_ICMPV6, csum); 	skb->ip_summed = CHECKSUM_NONE; 	eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);	skb_reset_mac_header(skb); 	if (skb_valid_dst(skb))		skb_dst_drop(skb); 	return skb->len;} /** * iptunnel_pmtud_check_icmpv6() - Trigger ICMPv6 reply if needed and allowed * @skb:	Buffer being sent by encapsulation, L2 headers expected * @mtu:	Network MTU for path * * Return: 0 for no ICMPv6 reply, length if built, negative value on error. */static int iptunnel_pmtud_check_icmpv6(struct sk_buff *skb, int mtu){	const struct ipv6hdr *ip6h = ipv6_hdr(skb);	int stype = ipv6_addr_type(&ip6h->saddr);	u8 proto = ip6h->nexthdr;	__be16 frag_off;	int offset; 	if (mtu < IPV6_MIN_MTU)		return 0; 	if (stype == IPV6_ADDR_ANY || stype == IPV6_ADDR_MULTICAST ||	    stype == IPV6_ADDR_LOOPBACK)		return 0; 	offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &proto,				  &frag_off);	if (offset < 0 || (frag_off & htons(~0x7)))		return 0; 	if (proto == IPPROTO_ICMPV6) {		struct icmp6hdr *icmp6h; 		if (!pskb_may_pull(skb, skb_network_header(skb) +					offset + 1 - skb->data))			return 0; 		icmp6h = (struct icmp6hdr *)(skb_network_header(skb) + offset);		if (icmpv6_is_err(icmp6h->icmp6_type) ||		    icmp6h->icmp6_type == NDISC_REDIRECT)			return 0;	} 	return iptunnel_pmtud_build_icmpv6(skb, mtu);}#endif /* IS_ENABLED(CONFIG_IPV6) */ /** * skb_tunnel_check_pmtu() - Check, update PMTU and trigger ICMP reply as needed * @skb:	Buffer being sent by encapsulation, L2 headers expected * @encap_dst:	Destination for tunnel encapsulation (outer IP) * @headroom:	Encapsulation header size, bytes * @reply:	Build matching ICMP or ICMPv6 message as a result * * L2 tunnel implementations that can carry IP and can be directly bridged * (currently UDP tunnels) can't always rely on IP forwarding paths to handle * PMTU discovery. In the bridged case, ICMP or ICMPv6 messages need to be built * based on payload and sent back by the encapsulation itself. * * For routable interfaces, we just need to update the PMTU for the destination. * * Return: 0 if ICMP error not needed, length if built, negative value on error */int skb_tunnel_check_pmtu(struct sk_buff *skb, struct dst_entry *encap_dst,			  int headroom, bool reply){	u32 mtu = dst_mtu(encap_dst) - headroom; 	if ((skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) ||	    (!skb_is_gso(skb) && (skb->len - skb_network_offset(skb)) <= mtu))		return 0; 	skb_dst_update_pmtu_no_confirm(skb, mtu); 	if (!reply)		return 0; 	if (skb->protocol == htons(ETH_P_IP))		return iptunnel_pmtud_check_icmp(skb, mtu); #if IS_ENABLED(CONFIG_IPV6)	if (skb->protocol == htons(ETH_P_IPV6))		return iptunnel_pmtud_check_icmpv6(skb, mtu);#endif	return 0;}EXPORT_SYMBOL(skb_tunnel_check_pmtu); static const struct nla_policy ip_tun_policy[LWTUNNEL_IP_MAX + 1] = {	[LWTUNNEL_IP_UNSPEC]	= { .strict_start_type = LWTUNNEL_IP_OPTS },	[LWTUNNEL_IP_ID]	= { .type = NLA_U64 },	[LWTUNNEL_IP_DST]	= { .type = NLA_U32 },	[LWTUNNEL_IP_SRC]	= { .type = NLA_U32 },	[LWTUNNEL_IP_TTL]	= { .type = NLA_U8 },	[LWTUNNEL_IP_TOS]	= { .type = NLA_U8 },	[LWTUNNEL_IP_FLAGS]	= { .type = NLA_U16 },	[LWTUNNEL_IP_OPTS]	= { .type = NLA_NESTED },}; static const struct nla_policy ip_opts_policy[LWTUNNEL_IP_OPTS_MAX + 1] = {	[LWTUNNEL_IP_OPTS_GENEVE]	= { .type = NLA_NESTED },	[LWTUNNEL_IP_OPTS_VXLAN]	= { .type = NLA_NESTED },	[LWTUNNEL_IP_OPTS_ERSPAN]	= { .type = NLA_NESTED },}; static const struct nla_policygeneve_opt_policy[LWTUNNEL_IP_OPT_GENEVE_MAX + 1] = {	[LWTUNNEL_IP_OPT_GENEVE_CLASS]	= { .type = NLA_U16 },	[LWTUNNEL_IP_OPT_GENEVE_TYPE]	= { .type = NLA_U8 },	[LWTUNNEL_IP_OPT_GENEVE_DATA]	= { .type = NLA_BINARY, .len = 127 },}; static const struct nla_policyvxlan_opt_policy[LWTUNNEL_IP_OPT_VXLAN_MAX + 1] = {	[LWTUNNEL_IP_OPT_VXLAN_GBP]	= { .type = NLA_U32 },}; static const struct nla_policyerspan_opt_policy[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1] = {	[LWTUNNEL_IP_OPT_ERSPAN_VER]	= { .type = NLA_U8 },	[LWTUNNEL_IP_OPT_ERSPAN_INDEX]	= { .type = NLA_U32 },

Lines 1–500 of 1203.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t