2974 lines · 75.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	NET3	IP device support routines. * *	Derived from the IP parts of dev.c 1.0.19 * 		Authors:	Ross Biro *				Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> *				Mark Evans, <evansmp@uhura.aston.ac.uk> * *	Additional Authors: *		Alan Cox, <gw4pts@gw4pts.ampr.org> *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * *	Changes: *		Alexey Kuznetsov:	pa_* fields are replaced with ifaddr *					lists. *		Cyrus Durgin:		updated for kmod *		Matthias Andree:	in devinet_ioctl, compare label and *					address (4.4BSD alias style support), *					fall back to comparing just the label *					if no match found. */  #include <linux/uaccess.h>#include <linux/bitops.h>#include <linux/capability.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched/signal.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/if_addr.h>#include <linux/if_ether.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/notifier.h>#include <linux/inetdevice.h>#include <linux/igmp.h>#include "igmp_internal.h"#include <linux/slab.h>#include <linux/hash.h>#ifdef CONFIG_SYSCTL#include <linux/sysctl.h>#endif#include <linux/kmod.h>#include <linux/netconf.h> #include <net/arp.h>#include <net/ip.h>#include <net/route.h>#include <net/ip_fib.h>#include <net/rtnetlink.h>#include <net/net_namespace.h>#include <net/addrconf.h> #define IPV6ONLY_FLAGS	\		(IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \		 IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \		 IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY) static struct ipv4_devconf ipv4_devconf = {	.data = {		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,	},}; static struct ipv4_devconf ipv4_devconf_dflt = {	.data = {		[IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,		[IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,		[IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,		[IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/,		[IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] =  1000 /*ms*/,		[IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1,	},}; #define IPV4_DEVCONF_DFLT(net, attr) \	IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {	[IFA_LOCAL]     	= { .type = NLA_U32 },	[IFA_ADDRESS]   	= { .type = NLA_U32 },	[IFA_BROADCAST] 	= { .type = NLA_U32 },	[IFA_LABEL]     	= { .type = NLA_STRING, .len = IFNAMSIZ - 1 },	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },	[IFA_FLAGS]		= { .type = NLA_U32 },	[IFA_RT_PRIORITY]	= { .type = NLA_U32 },	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },	[IFA_PROTO]		= { .type = NLA_U8 },}; #define IN4_ADDR_HSIZE_SHIFT	8#define IN4_ADDR_HSIZE		(1U << IN4_ADDR_HSIZE_SHIFT) static u32 inet_addr_hash(const struct net *net, __be32 addr){	u32 val = __ipv4_addr_hash(addr, net_hash_mix(net)); 	return hash_32(val, IN4_ADDR_HSIZE_SHIFT);} static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa){	u32 hash = inet_addr_hash(net, ifa->ifa_local); 	ASSERT_RTNL();	hlist_add_head_rcu(&ifa->addr_lst, &net->ipv4.inet_addr_lst[hash]);} static void inet_hash_remove(struct in_ifaddr *ifa){	ASSERT_RTNL();	hlist_del_init_rcu(&ifa->addr_lst);} /** * __ip_dev_find - find the first device with a given source address. * @net: the net namespace * @addr: the source address * @devref: if true, take a reference on the found device * * If a caller uses devref=false, it should be protected by RCU, or RTNL */struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref){	struct net_device *result = NULL;	struct in_ifaddr *ifa; 	rcu_read_lock();	ifa = inet_lookup_ifaddr_rcu(net, addr);	if (!ifa) {		struct flowi4 fl4 = { .daddr = addr };		struct fib_result res = { 0 };		struct fib_table *local; 		/* Fallback to FIB local table so that communication		 * over loopback subnets work.		 */		local = fib_get_table(net, RT_TABLE_LOCAL);		if (local &&		    !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&		    res.type == RTN_LOCAL)			result = FIB_RES_DEV(res);	} else {		result = ifa->ifa_dev->dev;	}	if (result && devref)		dev_hold(result);	rcu_read_unlock();	return result;}EXPORT_SYMBOL(__ip_dev_find); /* called under RCU lock */struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr){	u32 hash = inet_addr_hash(net, addr);	struct in_ifaddr *ifa; 	hlist_for_each_entry_rcu(ifa, &net->ipv4.inet_addr_lst[hash], addr_lst)		if (ifa->ifa_local == addr)			return ifa; 	return NULL;} static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);static void inet_del_ifa(struct in_device *in_dev,			 struct in_ifaddr __rcu **ifap,			 int destroy);#ifdef CONFIG_SYSCTLstatic int devinet_sysctl_register(struct in_device *idev);static void devinet_sysctl_unregister(struct in_device *idev);#elsestatic int devinet_sysctl_register(struct in_device *idev){	return 0;}static void devinet_sysctl_unregister(struct in_device *idev){}#endif /* Locks all the inet devices. */ static struct in_ifaddr *inet_alloc_ifa(struct in_device *in_dev){	struct in_ifaddr *ifa; 	ifa = kzalloc_obj(*ifa, GFP_KERNEL_ACCOUNT);	if (!ifa)		return NULL; 	in_dev_hold(in_dev);	ifa->ifa_dev = in_dev; 	INIT_HLIST_NODE(&ifa->addr_lst); 	return ifa;} static void inet_rcu_free_ifa(struct rcu_head *head){	struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 	in_dev_put(ifa->ifa_dev);	kfree(ifa);} static void inet_free_ifa(struct in_ifaddr *ifa){	/* Our reference to ifa->ifa_dev must be freed ASAP	 * to release the reference to the netdev the same way.	 * in_dev_put() -> in_dev_finish_destroy() -> netdev_put()	 */	call_rcu_hurry(&ifa->rcu_head, inet_rcu_free_ifa);} static void in_dev_free_rcu(struct rcu_head *head){	struct in_device *idev = container_of(head, struct in_device, rcu_head); 	kfree(rcu_dereference_protected(idev->mc_hash, 1));	kfree(idev);} void in_dev_finish_destroy(struct in_device *idev){	struct net_device *dev = idev->dev; 	WARN_ON(idev->ifa_list);	WARN_ON(idev->mc_list);#ifdef NET_REFCNT_DEBUG	pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");#endif	netdev_put(dev, &idev->dev_tracker);	if (!idev->dead)		pr_err("Freeing alive in_device %p\n", idev);	else		call_rcu(&idev->rcu_head, in_dev_free_rcu);}EXPORT_SYMBOL(in_dev_finish_destroy); static struct in_device *inetdev_init(struct net_device *dev){	struct in_device *in_dev;	int err = -ENOMEM; 	ASSERT_RTNL(); 	in_dev = kzalloc_obj(*in_dev);	if (!in_dev)		goto out;	memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,			sizeof(in_dev->cnf));	in_dev->cnf.sysctl = NULL;	in_dev->dev = dev;	in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);	if (!in_dev->arp_parms)		goto out_kfree;	if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))		netif_disable_lro(dev);	/* Reference in_dev->dev */	netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL);	/* Account for reference dev->ip_ptr (below) */	refcount_set(&in_dev->refcnt, 1); 	if (dev != blackhole_netdev) {		err = devinet_sysctl_register(in_dev);		if (err) {			in_dev->dead = 1;			neigh_parms_release(&arp_tbl, in_dev->arp_parms);			in_dev_put(in_dev);			in_dev = NULL;			goto out;		}		ip_mc_init_dev(in_dev);		if (dev->flags & IFF_UP)			ip_mc_up(in_dev);	} 	/* we can receive as soon as ip_ptr is set -- do this last */	rcu_assign_pointer(dev->ip_ptr, in_dev);out:	return in_dev ?: ERR_PTR(err);out_kfree:	kfree(in_dev);	in_dev = NULL;	goto out;} static void inetdev_destroy(struct in_device *in_dev){	struct net_device *dev;	struct in_ifaddr *ifa; 	ASSERT_RTNL(); 	dev = in_dev->dev; 	in_dev->dead = 1; 	RCU_INIT_POINTER(dev->ip_ptr, NULL); 	ip_mc_destroy_dev(in_dev); 	while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) {		inet_del_ifa(in_dev, &in_dev->ifa_list, 0);		inet_free_ifa(ifa);	} 	devinet_sysctl_unregister(in_dev);	neigh_parms_release(&arp_tbl, in_dev->arp_parms);	arp_ifdown(dev); 	in_dev_put(in_dev);} static int __init inet_blackhole_dev_init(void){	struct in_device *in_dev; 	rtnl_lock();	in_dev = inetdev_init(blackhole_netdev);	rtnl_unlock(); 	return PTR_ERR_OR_ZERO(in_dev);}late_initcall(inet_blackhole_dev_init); int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b){	const struct in_ifaddr *ifa; 	rcu_read_lock();	in_dev_for_each_ifa_rcu(ifa, in_dev) {		if (inet_ifa_match(a, ifa)) {			if (!b || inet_ifa_match(b, ifa)) {				rcu_read_unlock();				return 1;			}		}	}	rcu_read_unlock();	return 0;} static void __inet_del_ifa(struct in_device *in_dev,			   struct in_ifaddr __rcu **ifap,			   int destroy, struct nlmsghdr *nlh, u32 portid){	struct in_ifaddr *promote = NULL;	struct in_ifaddr *ifa, *ifa1;	struct in_ifaddr __rcu **last_prim;	struct in_ifaddr *prev_prom = NULL;	int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 	ASSERT_RTNL(); 	ifa1 = rtnl_dereference(*ifap);	last_prim = ifap;	if (in_dev->dead)		goto no_promotions; 	/* 1. Deleting primary ifaddr forces deletion all secondaries	 * unless alias promotion is set	 **/ 	if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {		struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; 		while ((ifa = rtnl_dereference(*ifap1)) != NULL) {			if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&			    ifa1->ifa_scope <= ifa->ifa_scope)				last_prim = &ifa->ifa_next; 			if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||			    ifa1->ifa_mask != ifa->ifa_mask ||			    !inet_ifa_match(ifa1->ifa_address, ifa)) {				ifap1 = &ifa->ifa_next;				prev_prom = ifa;				continue;			} 			if (!do_promote) {				inet_hash_remove(ifa);				*ifap1 = ifa->ifa_next; 				rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid);				blocking_notifier_call_chain(&inetaddr_chain,						NETDEV_DOWN, ifa);				inet_free_ifa(ifa);			} else {				promote = ifa;				break;			}		}	} 	/* On promotion all secondaries from subnet are changing	 * the primary IP, we must remove all their routes silently	 * and later to add them back with new prefsrc. Do this	 * while all addresses are on the device list.	 */	for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) {		if (ifa1->ifa_mask == ifa->ifa_mask &&		    inet_ifa_match(ifa1->ifa_address, ifa))			fib_del_ifaddr(ifa, ifa1);	} no_promotions:	/* 2. Unlink it */ 	*ifap = ifa1->ifa_next;	inet_hash_remove(ifa1); 	/* 3. Announce address deletion */ 	/* Send message first, then call notifier.	   At first sight, FIB update triggered by notifier	   will refer to already deleted ifaddr, that could confuse	   netlink listeners. It is not true: look, gated sees	   that route deleted and if it still thinks that ifaddr	   is valid, it will try to restore deleted routes... Grr.	   So that, this order is correct.	 */	rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid);	blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 	if (promote) {		struct in_ifaddr *next_sec; 		next_sec = rtnl_dereference(promote->ifa_next);		if (prev_prom) {			struct in_ifaddr *last_sec; 			rcu_assign_pointer(prev_prom->ifa_next, next_sec); 			last_sec = rtnl_dereference(*last_prim);			rcu_assign_pointer(promote->ifa_next, last_sec);			rcu_assign_pointer(*last_prim, promote);		} 		promote->ifa_flags &= ~IFA_F_SECONDARY;		rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid);		blocking_notifier_call_chain(&inetaddr_chain,				NETDEV_UP, promote);		for (ifa = next_sec; ifa;		     ifa = rtnl_dereference(ifa->ifa_next)) {			if (ifa1->ifa_mask != ifa->ifa_mask ||			    !inet_ifa_match(ifa1->ifa_address, ifa))					continue;			fib_add_ifaddr(ifa);		} 	}	if (destroy)		inet_free_ifa(ifa1);} static void inet_del_ifa(struct in_device *in_dev,			 struct in_ifaddr __rcu **ifap,			 int destroy){	__inet_del_ifa(in_dev, ifap, destroy, NULL, 0);} static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,			     u32 portid, struct netlink_ext_ack *extack){	struct in_ifaddr __rcu **last_primary, **ifap;	struct in_device *in_dev = ifa->ifa_dev;	struct net *net = dev_net(in_dev->dev);	struct in_validator_info ivi;	struct in_ifaddr *ifa1;	int ret; 	ASSERT_RTNL();

Lines 1–500 of 2974.