3828 lines · 96.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * INET		An implementation of the TCP/IP protocol suite for the LINUX *		operating system.  INET is implemented using the  BSD Socket *		interface as the means of communication with the user level. * *		ROUTE - implementation of the IP router. * * Authors:	Ross Biro *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> *		Alan Cox, <gw4pts@gw4pts.ampr.org> *		Linus Torvalds, <Linus.Torvalds@helsinki.fi> *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * * Fixes: *		Alan Cox	:	Verify area fixes. *		Alan Cox	:	cli() protects routing changes *		Rui Oliveira	:	ICMP routing table updates *		(rco@di.uminho.pt)	Routing table insertion and update *		Linus Torvalds	:	Rewrote bits to be sensible *		Alan Cox	:	Added BSD route gw semantics *		Alan Cox	:	Super /proc >4K *		Alan Cox	:	MTU in route table *		Alan Cox	:	MSS actually. Also added the window *					clamper. *		Sam Lantinga	:	Fixed route matching in rt_del() *		Alan Cox	:	Routing cache support. *		Alan Cox	:	Removed compatibility cruft. *		Alan Cox	:	RTF_REJECT support. *		Alan Cox	:	TCP irtt support. *		Jonathan Naylor	:	Added Metric support. *	Miquel van Smoorenburg	:	BSD API fixes. *	Miquel van Smoorenburg	:	Metrics. *		Alan Cox	:	Use __u32 properly *		Alan Cox	:	Aligned routing errors more closely with BSD *					our system is still very different. *		Alan Cox	:	Faster /proc handling *	Alexey Kuznetsov	:	Massive rework to support tree based routing, *					routing caches and better behaviour. * *		Olaf Erb	:	irtt wasn't being copied right. *		Bjorn Ekwall	:	Kerneld route support. *		Alan Cox	:	Multicast fixed (I hope) *		Pavel Krauz	:	Limited broadcast fixed *		Mike McLagan	:	Routing by source *	Alexey Kuznetsov	:	End of old history. Split to fib.c and *					route.c and rewritten from scratch. *		Andi Kleen	:	Load-limit warning messages. *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma. *	Vitaly E. Lavrov	:	Race condition in ip_route_input_slow. *	Tobias Ringstrom	:	Uninitialized res.type in ip_route_output_slow. *	Vladimir V. Ivanov	:	IP rule info (flowid) is really useful. *		Marc Boucher	:	routing by fwmark *	Robert Olsson		:	Added rt_cache statistics *	Arnaldo C. Melo		:	Convert proc stuff to seq_file *	Eric Dumazet		:	hashed spinlocks and rt_check_expire() fixes. *	Ilia Sotnikov		:	Ignore TOS on PMTUD and Redirect *	Ilia Sotnikov		:	Removed TOS from hash calculations */ #define pr_fmt(fmt) "IPv4: " fmt #include <linux/module.h>#include <linux/bitops.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/memblock.h>#include <linux/socket.h>#include <linux/errno.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/inetdevice.h>#include <linux/igmp.h>#include <linux/pkt_sched.h>#include <linux/mroute.h>#include <linux/netfilter_ipv4.h>#include <linux/random.h>#include <linux/rcupdate.h>#include <linux/slab.h>#include <linux/jhash.h>#include <net/dst.h>#include <net/dst_metadata.h>#include <net/flow.h>#include <net/inet_dscp.h>#include <net/net_namespace.h>#include <net/ip.h>#include <net/route.h>#include <net/inetpeer.h>#include <net/sock.h>#include <net/ip_fib.h>#include <net/nexthop.h>#include <net/tcp.h>#include <net/icmp.h>#include <net/xfrm.h>#include <net/lwtunnel.h>#include <net/netevent.h>#include <net/rtnetlink.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <net/secure_seq.h>#include <net/ip_tunnels.h> #include "fib_lookup.h" #define RT_GC_TIMEOUT (300*HZ) #define DEFAULT_MIN_PMTU (512 + 20 + 20)#define DEFAULT_MTU_EXPIRES (10 * 60 * HZ)#define DEFAULT_MIN_ADVMSS 256static int ip_rt_max_size;static int ip_rt_redirect_number __read_mostly	= 9;static int ip_rt_redirect_load __read_mostly	= HZ / 50;static int ip_rt_redirect_silence __read_mostly	= ((HZ / 50) << (9 + 1));static int ip_rt_error_cost __read_mostly	= HZ;static int ip_rt_error_burst __read_mostly	= 5 * HZ; static int ip_rt_gc_timeout __read_mostly	= RT_GC_TIMEOUT; /* *	Interface to generic destination cache. */ INDIRECT_CALLABLE_SCOPEstruct dst_entry	*ipv4_dst_check(struct dst_entry *dst, u32 cookie);static unsigned int	 ipv4_default_advmss(const struct dst_entry *dst);INDIRECT_CALLABLE_SCOPEunsigned int		ipv4_mtu(const struct dst_entry *dst);static void		ipv4_negative_advice(struct sock *sk,					     struct dst_entry *dst);static void		 ipv4_link_failure(struct sk_buff *skb);static void		 ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,					   struct sk_buff *skb, u32 mtu,					   bool confirm_neigh);static void		 ip_do_redirect(struct dst_entry *dst, struct sock *sk,					struct sk_buff *skb);static void		ipv4_dst_destroy(struct dst_entry *dst); static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old){	WARN_ON(1);	return NULL;} static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,					   struct sk_buff *skb,					   const void *daddr);static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr); static struct dst_ops ipv4_dst_ops = {	.family =		AF_INET,	.check =		ipv4_dst_check,	.default_advmss =	ipv4_default_advmss,	.mtu =			ipv4_mtu,	.cow_metrics =		ipv4_cow_metrics,	.destroy =		ipv4_dst_destroy,	.negative_advice =	ipv4_negative_advice,	.link_failure =		ipv4_link_failure,	.update_pmtu =		ip_rt_update_pmtu,	.redirect =		ip_do_redirect,	.local_out =		__ip_local_out,	.neigh_lookup =		ipv4_neigh_lookup,	.confirm_neigh =	ipv4_confirm_neigh,}; #define ECN_OR_COST(class)	TC_PRIO_##class const __u8 ip_tos2prio[16] = {	TC_PRIO_BESTEFFORT,	ECN_OR_COST(BESTEFFORT),	TC_PRIO_BESTEFFORT,	ECN_OR_COST(BESTEFFORT),	TC_PRIO_BULK,	ECN_OR_COST(BULK),	TC_PRIO_BULK,	ECN_OR_COST(BULK),	TC_PRIO_INTERACTIVE,	ECN_OR_COST(INTERACTIVE),	TC_PRIO_INTERACTIVE,	ECN_OR_COST(INTERACTIVE),	TC_PRIO_INTERACTIVE_BULK,	ECN_OR_COST(INTERACTIVE_BULK),	TC_PRIO_INTERACTIVE_BULK,	ECN_OR_COST(INTERACTIVE_BULK)};EXPORT_SYMBOL(ip_tos2prio); static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat);#ifndef CONFIG_PREEMPT_RT#define RT_CACHE_STAT_INC(field) raw_cpu_inc(rt_cache_stat.field)#else#define RT_CACHE_STAT_INC(field) this_cpu_inc(rt_cache_stat.field)#endif #ifdef CONFIG_PROC_FSstatic void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos){	if (*pos)		return NULL;	return SEQ_START_TOKEN;} static void *rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos){	++*pos;	return NULL;} static void rt_cache_seq_stop(struct seq_file *seq, void *v){} static int rt_cache_seq_show(struct seq_file *seq, void *v){	if (v == SEQ_START_TOKEN)		seq_printf(seq, "%-127s\n",			   "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\t"			   "Metric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\t"			   "HHUptod\tSpecDst");	return 0;} static const struct seq_operations rt_cache_seq_ops = {	.start  = rt_cache_seq_start,	.next   = rt_cache_seq_next,	.stop   = rt_cache_seq_stop,	.show   = rt_cache_seq_show,}; static void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos){	int cpu; 	if (*pos == 0)		return SEQ_START_TOKEN; 	for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {		if (!cpu_possible(cpu))			continue;		*pos = cpu+1;		return &per_cpu(rt_cache_stat, cpu);	}	return NULL;} static void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos){	int cpu; 	for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {		if (!cpu_possible(cpu))			continue;		*pos = cpu+1;		return &per_cpu(rt_cache_stat, cpu);	}	(*pos)++;	return NULL; } static void rt_cpu_seq_stop(struct seq_file *seq, void *v){ } static int rt_cpu_seq_show(struct seq_file *seq, void *v){	struct rt_cache_stat *st = v; 	if (v == SEQ_START_TOKEN) {		seq_puts(seq, "entries  in_hit   in_slow_tot in_slow_mc in_no_route in_brd   in_martian_dst in_martian_src out_hit  out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n");		return 0;	} 	seq_printf(seq, "%08x %08x %08x    %08x   %08x    %08x %08x       "			"%08x       %08x %08x     %08x    %08x %08x   "			"%08x     %08x        %08x        %08x\n",		   dst_entries_get_slow(&ipv4_dst_ops),		   0, /* st->in_hit */		   st->in_slow_tot,		   st->in_slow_mc,		   st->in_no_route,		   st->in_brd,		   st->in_martian_dst,		   st->in_martian_src, 		   0, /* st->out_hit */		   st->out_slow_tot,		   st->out_slow_mc, 		   0, /* st->gc_total */		   0, /* st->gc_ignored */		   0, /* st->gc_goal_miss */		   0, /* st->gc_dst_overflow */		   0, /* st->in_hlist_search */		   0  /* st->out_hlist_search */		);	return 0;} static const struct seq_operations rt_cpu_seq_ops = {	.start  = rt_cpu_seq_start,	.next   = rt_cpu_seq_next,	.stop   = rt_cpu_seq_stop,	.show   = rt_cpu_seq_show,}; #ifdef CONFIG_IP_ROUTE_CLASSIDstatic int rt_acct_proc_show(struct seq_file *m, void *v){	struct ip_rt_acct *dst, *src;	unsigned int i, j; 	dst = kzalloc_objs(struct ip_rt_acct, 256);	if (!dst)		return -ENOMEM; 	for_each_possible_cpu(i) {		src = (struct ip_rt_acct *)per_cpu_ptr(ip_rt_acct, i);		for (j = 0; j < 256; j++) {			dst[j].o_bytes   += src[j].o_bytes;			dst[j].o_packets += src[j].o_packets;			dst[j].i_bytes   += src[j].i_bytes;			dst[j].i_packets += src[j].i_packets;		}	} 	seq_write(m, dst, 256 * sizeof(struct ip_rt_acct));	kfree(dst);	return 0;}#endif static int __net_init ip_rt_do_proc_init(struct net *net){	struct proc_dir_entry *pde; 	pde = proc_create_seq("rt_cache", 0444, net->proc_net,			      &rt_cache_seq_ops);	if (!pde)		goto err1; 	pde = proc_create_seq("rt_cache", 0444, net->proc_net_stat,			      &rt_cpu_seq_ops);	if (!pde)		goto err2; #ifdef CONFIG_IP_ROUTE_CLASSID	pde = proc_create_single("rt_acct", 0, net->proc_net,			rt_acct_proc_show);	if (!pde)		goto err3;#endif	return 0; #ifdef CONFIG_IP_ROUTE_CLASSIDerr3:	remove_proc_entry("rt_cache", net->proc_net_stat);#endiferr2:	remove_proc_entry("rt_cache", net->proc_net);err1:	return -ENOMEM;} static void __net_exit ip_rt_do_proc_exit(struct net *net){	remove_proc_entry("rt_cache", net->proc_net_stat);	remove_proc_entry("rt_cache", net->proc_net);#ifdef CONFIG_IP_ROUTE_CLASSID	remove_proc_entry("rt_acct", net->proc_net);#endif} static struct pernet_operations ip_rt_proc_ops __net_initdata =  {	.init = ip_rt_do_proc_init,	.exit = ip_rt_do_proc_exit,}; static int __init ip_rt_proc_init(void){	return register_pernet_subsys(&ip_rt_proc_ops);} #elsestatic inline int ip_rt_proc_init(void){	return 0;}#endif /* CONFIG_PROC_FS */ static inline bool rt_is_expired(const struct rtable *rth){	bool res; 	rcu_read_lock();	res = rth->rt_genid != rt_genid_ipv4(dev_net_rcu(rth->dst.dev));	rcu_read_unlock(); 	return res;} void rt_cache_flush(struct net *net){	rt_genid_bump_ipv4(net);} static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,					   struct sk_buff *skb,					   const void *daddr){	const struct rtable *rt = container_of(dst, struct rtable, dst);	struct net_device *dev;	struct neighbour *n; 	rcu_read_lock();	dev = dst_dev_rcu(dst);	if (likely(rt->rt_gw_family == AF_INET)) {		n = ip_neigh_gw4(dev, rt->rt_gw4);	} else if (rt->rt_gw_family == AF_INET6) {		n = ip_neigh_gw6(dev, &rt->rt_gw6);        } else {		__be32 pkey; 		pkey = skb ? ip_hdr(skb)->daddr : *((__be32 *) daddr);		n = ip_neigh_gw4(dev, pkey);	} 	if (!IS_ERR(n) && !refcount_inc_not_zero(&n->refcnt))		n = NULL; 	rcu_read_unlock(); 	return n;} static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr){	const struct rtable *rt = container_of(dst, struct rtable, dst);	struct net_device *dev = dst_dev(dst);	const __be32 *pkey = daddr; 	if (rt->rt_gw_family == AF_INET) {		pkey = (const __be32 *)&rt->rt_gw4;	} else if (IS_ENABLED(CONFIG_IPV6) && rt->rt_gw_family == AF_INET6) {		return __ipv6_confirm_neigh(dev, &rt->rt_gw6);	} else if (!daddr ||		 (rt->rt_flags &		  (RTCF_MULTICAST | RTCF_BROADCAST | RTCF_LOCAL))) {		return;	}	__ipv4_confirm_neigh(dev, *(__force u32 *)pkey);} /* Hash tables of size 2048..262144 depending on RAM size. * Each bucket uses 8 bytes. */static u32 ip_idents_mask __read_mostly;static atomic_t *ip_idents __read_mostly;static u32 *ip_tstamps __read_mostly; /* In order to protect privacy, we add a perturbation to identifiers * if one generator is seldom used. This makes hard for an attacker * to infer how many packets were sent between two points in time. */static u32 ip_idents_reserve(u32 hash, int segs){	u32 bucket, old, now = (u32)jiffies;	atomic_t *p_id;	u32 *p_tstamp;	u32 delta = 0; 	bucket = hash & ip_idents_mask;	p_tstamp = ip_tstamps + bucket;	p_id = ip_idents + bucket;	old = READ_ONCE(*p_tstamp); 	if (old != now && cmpxchg(p_tstamp, old, now) == old)		delta = get_random_u32_below(now - old); 	/* If UBSAN reports an error there, please make sure your compiler	 * supports -fno-strict-overflow before reporting it that was a bug	 * in UBSAN, and it has been fixed in GCC-8.	 */	return atomic_add_return(segs + delta, p_id) - segs;} void __ip_select_ident(struct net *net, struct iphdr *iph, int segs){	u32 hash, id; 	/* Note the following code is not safe, but this is okay. */	if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)))		get_random_bytes(&net->ipv4.ip_id_key,				 sizeof(net->ipv4.ip_id_key)); 

Lines 1–500 of 3828.