162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NET3 IP device support routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from the IP parts of dev.c 1.0.19 662306a36Sopenharmony_ci * Authors: Ross Biro 762306a36Sopenharmony_ci * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 862306a36Sopenharmony_ci * Mark Evans, <evansmp@uhura.aston.ac.uk> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Additional Authors: 1162306a36Sopenharmony_ci * Alan Cox, <gw4pts@gw4pts.ampr.org> 1262306a36Sopenharmony_ci * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Changes: 1562306a36Sopenharmony_ci * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 1662306a36Sopenharmony_ci * lists. 1762306a36Sopenharmony_ci * Cyrus Durgin: updated for kmod 1862306a36Sopenharmony_ci * Matthias Andree: in devinet_ioctl, compare label and 1962306a36Sopenharmony_ci * address (4.4BSD alias style support), 2062306a36Sopenharmony_ci * fall back to comparing just the label 2162306a36Sopenharmony_ci * if no match found. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <linux/bitops.h> 2762306a36Sopenharmony_ci#include <linux/capability.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/types.h> 3062306a36Sopenharmony_ci#include <linux/kernel.h> 3162306a36Sopenharmony_ci#include <linux/sched/signal.h> 3262306a36Sopenharmony_ci#include <linux/string.h> 3362306a36Sopenharmony_ci#include <linux/mm.h> 3462306a36Sopenharmony_ci#include <linux/socket.h> 3562306a36Sopenharmony_ci#include <linux/sockios.h> 3662306a36Sopenharmony_ci#include <linux/in.h> 3762306a36Sopenharmony_ci#include <linux/errno.h> 3862306a36Sopenharmony_ci#include <linux/interrupt.h> 3962306a36Sopenharmony_ci#include <linux/if_addr.h> 4062306a36Sopenharmony_ci#include <linux/if_ether.h> 4162306a36Sopenharmony_ci#include <linux/inet.h> 4262306a36Sopenharmony_ci#include <linux/netdevice.h> 4362306a36Sopenharmony_ci#include <linux/etherdevice.h> 4462306a36Sopenharmony_ci#include <linux/skbuff.h> 4562306a36Sopenharmony_ci#include <linux/init.h> 4662306a36Sopenharmony_ci#include <linux/notifier.h> 4762306a36Sopenharmony_ci#include <linux/inetdevice.h> 4862306a36Sopenharmony_ci#include <linux/igmp.h> 4962306a36Sopenharmony_ci#include <linux/slab.h> 5062306a36Sopenharmony_ci#include <linux/hash.h> 5162306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 5262306a36Sopenharmony_ci#include <linux/sysctl.h> 5362306a36Sopenharmony_ci#endif 5462306a36Sopenharmony_ci#include <linux/kmod.h> 5562306a36Sopenharmony_ci#include <linux/netconf.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <net/arp.h> 5862306a36Sopenharmony_ci#include <net/ip.h> 5962306a36Sopenharmony_ci#include <net/route.h> 6062306a36Sopenharmony_ci#include <net/ip_fib.h> 6162306a36Sopenharmony_ci#include <net/rtnetlink.h> 6262306a36Sopenharmony_ci#include <net/net_namespace.h> 6362306a36Sopenharmony_ci#include <net/addrconf.h> 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define IPV6ONLY_FLAGS \ 6662306a36Sopenharmony_ci (IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \ 6762306a36Sopenharmony_ci IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \ 6862306a36Sopenharmony_ci IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct ipv4_devconf ipv4_devconf = { 7162306a36Sopenharmony_ci .data = { 7262306a36Sopenharmony_ci [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7362306a36Sopenharmony_ci [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7462306a36Sopenharmony_ci [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7562306a36Sopenharmony_ci [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7662306a36Sopenharmony_ci [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 7762306a36Sopenharmony_ci [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 7862306a36Sopenharmony_ci [IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1, 7962306a36Sopenharmony_ci }, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct ipv4_devconf ipv4_devconf_dflt = { 8362306a36Sopenharmony_ci .data = { 8462306a36Sopenharmony_ci [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8562306a36Sopenharmony_ci [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8662306a36Sopenharmony_ci [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8762306a36Sopenharmony_ci [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8862306a36Sopenharmony_ci [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8962306a36Sopenharmony_ci [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 9062306a36Sopenharmony_ci [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 9162306a36Sopenharmony_ci [IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1, 9262306a36Sopenharmony_ci }, 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define IPV4_DEVCONF_DFLT(net, attr) \ 9662306a36Sopenharmony_ci IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 9962306a36Sopenharmony_ci [IFA_LOCAL] = { .type = NLA_U32 }, 10062306a36Sopenharmony_ci [IFA_ADDRESS] = { .type = NLA_U32 }, 10162306a36Sopenharmony_ci [IFA_BROADCAST] = { .type = NLA_U32 }, 10262306a36Sopenharmony_ci [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 10362306a36Sopenharmony_ci [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 10462306a36Sopenharmony_ci [IFA_FLAGS] = { .type = NLA_U32 }, 10562306a36Sopenharmony_ci [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 10662306a36Sopenharmony_ci [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 10762306a36Sopenharmony_ci [IFA_PROTO] = { .type = NLA_U8 }, 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct inet_fill_args { 11162306a36Sopenharmony_ci u32 portid; 11262306a36Sopenharmony_ci u32 seq; 11362306a36Sopenharmony_ci int event; 11462306a36Sopenharmony_ci unsigned int flags; 11562306a36Sopenharmony_ci int netnsid; 11662306a36Sopenharmony_ci int ifindex; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define IN4_ADDR_HSIZE_SHIFT 8 12062306a36Sopenharmony_ci#define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic u32 inet_addr_hash(const struct net *net, __be32 addr) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u32 val = (__force u32) addr ^ net_hash_mix(net); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 hash = inet_addr_hash(net, ifa->ifa_local); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ASSERT_RTNL(); 13662306a36Sopenharmony_ci hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void inet_hash_remove(struct in_ifaddr *ifa) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci ASSERT_RTNL(); 14262306a36Sopenharmony_ci hlist_del_init_rcu(&ifa->hash); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * __ip_dev_find - find the first device with a given source address. 14762306a36Sopenharmony_ci * @net: the net namespace 14862306a36Sopenharmony_ci * @addr: the source address 14962306a36Sopenharmony_ci * @devref: if true, take a reference on the found device 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * If a caller uses devref=false, it should be protected by RCU, or RTNL 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistruct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct net_device *result = NULL; 15662306a36Sopenharmony_ci struct in_ifaddr *ifa; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci rcu_read_lock(); 15962306a36Sopenharmony_ci ifa = inet_lookup_ifaddr_rcu(net, addr); 16062306a36Sopenharmony_ci if (!ifa) { 16162306a36Sopenharmony_ci struct flowi4 fl4 = { .daddr = addr }; 16262306a36Sopenharmony_ci struct fib_result res = { 0 }; 16362306a36Sopenharmony_ci struct fib_table *local; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Fallback to FIB local table so that communication 16662306a36Sopenharmony_ci * over loopback subnets work. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci local = fib_get_table(net, RT_TABLE_LOCAL); 16962306a36Sopenharmony_ci if (local && 17062306a36Sopenharmony_ci !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 17162306a36Sopenharmony_ci res.type == RTN_LOCAL) 17262306a36Sopenharmony_ci result = FIB_RES_DEV(res); 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci result = ifa->ifa_dev->dev; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci if (result && devref) 17762306a36Sopenharmony_ci dev_hold(result); 17862306a36Sopenharmony_ci rcu_read_unlock(); 17962306a36Sopenharmony_ci return result; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(__ip_dev_find); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* called under RCU lock */ 18462306a36Sopenharmony_cistruct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 hash = inet_addr_hash(net, addr); 18762306a36Sopenharmony_ci struct in_ifaddr *ifa; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) 19062306a36Sopenharmony_ci if (ifa->ifa_local == addr && 19162306a36Sopenharmony_ci net_eq(dev_net(ifa->ifa_dev->dev), net)) 19262306a36Sopenharmony_ci return ifa; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 20062306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); 20162306a36Sopenharmony_cistatic void inet_del_ifa(struct in_device *in_dev, 20262306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap, 20362306a36Sopenharmony_ci int destroy); 20462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 20562306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev); 20662306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev); 20762306a36Sopenharmony_ci#else 20862306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci#endif 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* Locks all the inet devices. */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic struct in_ifaddr *inet_alloc_ifa(void) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void inet_rcu_free_ifa(struct rcu_head *head) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 22762306a36Sopenharmony_ci if (ifa->ifa_dev) 22862306a36Sopenharmony_ci in_dev_put(ifa->ifa_dev); 22962306a36Sopenharmony_ci kfree(ifa); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void inet_free_ifa(struct in_ifaddr *ifa) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void in_dev_free_rcu(struct rcu_head *head) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct in_device *idev = container_of(head, struct in_device, rcu_head); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci kfree(rcu_dereference_protected(idev->mc_hash, 1)); 24262306a36Sopenharmony_ci kfree(idev); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_civoid in_dev_finish_destroy(struct in_device *idev) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct net_device *dev = idev->dev; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci WARN_ON(idev->ifa_list); 25062306a36Sopenharmony_ci WARN_ON(idev->mc_list); 25162306a36Sopenharmony_ci#ifdef NET_REFCNT_DEBUG 25262306a36Sopenharmony_ci pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 25362306a36Sopenharmony_ci#endif 25462306a36Sopenharmony_ci netdev_put(dev, &idev->dev_tracker); 25562306a36Sopenharmony_ci if (!idev->dead) 25662306a36Sopenharmony_ci pr_err("Freeing alive in_device %p\n", idev); 25762306a36Sopenharmony_ci else 25862306a36Sopenharmony_ci call_rcu(&idev->rcu_head, in_dev_free_rcu); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL(in_dev_finish_destroy); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic struct in_device *inetdev_init(struct net_device *dev) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct in_device *in_dev; 26562306a36Sopenharmony_ci int err = -ENOMEM; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ASSERT_RTNL(); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 27062306a36Sopenharmony_ci if (!in_dev) 27162306a36Sopenharmony_ci goto out; 27262306a36Sopenharmony_ci memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 27362306a36Sopenharmony_ci sizeof(in_dev->cnf)); 27462306a36Sopenharmony_ci in_dev->cnf.sysctl = NULL; 27562306a36Sopenharmony_ci in_dev->dev = dev; 27662306a36Sopenharmony_ci in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 27762306a36Sopenharmony_ci if (!in_dev->arp_parms) 27862306a36Sopenharmony_ci goto out_kfree; 27962306a36Sopenharmony_ci if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 28062306a36Sopenharmony_ci dev_disable_lro(dev); 28162306a36Sopenharmony_ci /* Reference in_dev->dev */ 28262306a36Sopenharmony_ci netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL); 28362306a36Sopenharmony_ci /* Account for reference dev->ip_ptr (below) */ 28462306a36Sopenharmony_ci refcount_set(&in_dev->refcnt, 1); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci err = devinet_sysctl_register(in_dev); 28762306a36Sopenharmony_ci if (err) { 28862306a36Sopenharmony_ci in_dev->dead = 1; 28962306a36Sopenharmony_ci neigh_parms_release(&arp_tbl, in_dev->arp_parms); 29062306a36Sopenharmony_ci in_dev_put(in_dev); 29162306a36Sopenharmony_ci in_dev = NULL; 29262306a36Sopenharmony_ci goto out; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci ip_mc_init_dev(in_dev); 29562306a36Sopenharmony_ci if (dev->flags & IFF_UP) 29662306a36Sopenharmony_ci ip_mc_up(in_dev); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* we can receive as soon as ip_ptr is set -- do this last */ 29962306a36Sopenharmony_ci rcu_assign_pointer(dev->ip_ptr, in_dev); 30062306a36Sopenharmony_ciout: 30162306a36Sopenharmony_ci return in_dev ?: ERR_PTR(err); 30262306a36Sopenharmony_ciout_kfree: 30362306a36Sopenharmony_ci kfree(in_dev); 30462306a36Sopenharmony_ci in_dev = NULL; 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void inetdev_destroy(struct in_device *in_dev) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct net_device *dev; 31162306a36Sopenharmony_ci struct in_ifaddr *ifa; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ASSERT_RTNL(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev = in_dev->dev; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci in_dev->dead = 1; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ip_mc_destroy_dev(in_dev); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) { 32262306a36Sopenharmony_ci inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 32362306a36Sopenharmony_ci inet_free_ifa(ifa); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci RCU_INIT_POINTER(dev->ip_ptr, NULL); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci devinet_sysctl_unregister(in_dev); 32962306a36Sopenharmony_ci neigh_parms_release(&arp_tbl, in_dev->arp_parms); 33062306a36Sopenharmony_ci arp_ifdown(dev); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci in_dev_put(in_dev); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ciint inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci const struct in_ifaddr *ifa; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci rcu_read_lock(); 34062306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 34162306a36Sopenharmony_ci if (inet_ifa_match(a, ifa)) { 34262306a36Sopenharmony_ci if (!b || inet_ifa_match(b, ifa)) { 34362306a36Sopenharmony_ci rcu_read_unlock(); 34462306a36Sopenharmony_ci return 1; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci rcu_read_unlock(); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void __inet_del_ifa(struct in_device *in_dev, 35362306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap, 35462306a36Sopenharmony_ci int destroy, struct nlmsghdr *nlh, u32 portid) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct in_ifaddr *promote = NULL; 35762306a36Sopenharmony_ci struct in_ifaddr *ifa, *ifa1; 35862306a36Sopenharmony_ci struct in_ifaddr __rcu **last_prim; 35962306a36Sopenharmony_ci struct in_ifaddr *prev_prom = NULL; 36062306a36Sopenharmony_ci int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ASSERT_RTNL(); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ifa1 = rtnl_dereference(*ifap); 36562306a36Sopenharmony_ci last_prim = ifap; 36662306a36Sopenharmony_ci if (in_dev->dead) 36762306a36Sopenharmony_ci goto no_promotions; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 1. Deleting primary ifaddr forces deletion all secondaries 37062306a36Sopenharmony_ci * unless alias promotion is set 37162306a36Sopenharmony_ci **/ 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 37462306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci while ((ifa = rtnl_dereference(*ifap1)) != NULL) { 37762306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 37862306a36Sopenharmony_ci ifa1->ifa_scope <= ifa->ifa_scope) 37962306a36Sopenharmony_ci last_prim = &ifa->ifa_next; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 38262306a36Sopenharmony_ci ifa1->ifa_mask != ifa->ifa_mask || 38362306a36Sopenharmony_ci !inet_ifa_match(ifa1->ifa_address, ifa)) { 38462306a36Sopenharmony_ci ifap1 = &ifa->ifa_next; 38562306a36Sopenharmony_ci prev_prom = ifa; 38662306a36Sopenharmony_ci continue; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!do_promote) { 39062306a36Sopenharmony_ci inet_hash_remove(ifa); 39162306a36Sopenharmony_ci *ifap1 = ifa->ifa_next; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 39462306a36Sopenharmony_ci blocking_notifier_call_chain(&inetaddr_chain, 39562306a36Sopenharmony_ci NETDEV_DOWN, ifa); 39662306a36Sopenharmony_ci inet_free_ifa(ifa); 39762306a36Sopenharmony_ci } else { 39862306a36Sopenharmony_ci promote = ifa; 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* On promotion all secondaries from subnet are changing 40562306a36Sopenharmony_ci * the primary IP, we must remove all their routes silently 40662306a36Sopenharmony_ci * and later to add them back with new prefsrc. Do this 40762306a36Sopenharmony_ci * while all addresses are on the device list. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) { 41062306a36Sopenharmony_ci if (ifa1->ifa_mask == ifa->ifa_mask && 41162306a36Sopenharmony_ci inet_ifa_match(ifa1->ifa_address, ifa)) 41262306a36Sopenharmony_ci fib_del_ifaddr(ifa, ifa1); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cino_promotions: 41662306a36Sopenharmony_ci /* 2. Unlink it */ 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci *ifap = ifa1->ifa_next; 41962306a36Sopenharmony_ci inet_hash_remove(ifa1); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 3. Announce address deletion */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Send message first, then call notifier. 42462306a36Sopenharmony_ci At first sight, FIB update triggered by notifier 42562306a36Sopenharmony_ci will refer to already deleted ifaddr, that could confuse 42662306a36Sopenharmony_ci netlink listeners. It is not true: look, gated sees 42762306a36Sopenharmony_ci that route deleted and if it still thinks that ifaddr 42862306a36Sopenharmony_ci is valid, it will try to restore deleted routes... Grr. 42962306a36Sopenharmony_ci So that, this order is correct. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 43262306a36Sopenharmony_ci blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (promote) { 43562306a36Sopenharmony_ci struct in_ifaddr *next_sec; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci next_sec = rtnl_dereference(promote->ifa_next); 43862306a36Sopenharmony_ci if (prev_prom) { 43962306a36Sopenharmony_ci struct in_ifaddr *last_sec; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci rcu_assign_pointer(prev_prom->ifa_next, next_sec); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci last_sec = rtnl_dereference(*last_prim); 44462306a36Sopenharmony_ci rcu_assign_pointer(promote->ifa_next, last_sec); 44562306a36Sopenharmony_ci rcu_assign_pointer(*last_prim, promote); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci promote->ifa_flags &= ~IFA_F_SECONDARY; 44962306a36Sopenharmony_ci rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 45062306a36Sopenharmony_ci blocking_notifier_call_chain(&inetaddr_chain, 45162306a36Sopenharmony_ci NETDEV_UP, promote); 45262306a36Sopenharmony_ci for (ifa = next_sec; ifa; 45362306a36Sopenharmony_ci ifa = rtnl_dereference(ifa->ifa_next)) { 45462306a36Sopenharmony_ci if (ifa1->ifa_mask != ifa->ifa_mask || 45562306a36Sopenharmony_ci !inet_ifa_match(ifa1->ifa_address, ifa)) 45662306a36Sopenharmony_ci continue; 45762306a36Sopenharmony_ci fib_add_ifaddr(ifa); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci if (destroy) 46262306a36Sopenharmony_ci inet_free_ifa(ifa1); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void inet_del_ifa(struct in_device *in_dev, 46662306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap, 46762306a36Sopenharmony_ci int destroy) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void check_lifetime(struct work_struct *work); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 47762306a36Sopenharmony_ci u32 portid, struct netlink_ext_ack *extack) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct in_ifaddr __rcu **last_primary, **ifap; 48062306a36Sopenharmony_ci struct in_device *in_dev = ifa->ifa_dev; 48162306a36Sopenharmony_ci struct in_validator_info ivi; 48262306a36Sopenharmony_ci struct in_ifaddr *ifa1; 48362306a36Sopenharmony_ci int ret; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ASSERT_RTNL(); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!ifa->ifa_local) { 48862306a36Sopenharmony_ci inet_free_ifa(ifa); 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ifa->ifa_flags &= ~IFA_F_SECONDARY; 49362306a36Sopenharmony_ci last_primary = &in_dev->ifa_list; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Don't set IPv6 only flags to IPv4 addresses */ 49662306a36Sopenharmony_ci ifa->ifa_flags &= ~IPV6ONLY_FLAGS; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ifap = &in_dev->ifa_list; 49962306a36Sopenharmony_ci ifa1 = rtnl_dereference(*ifap); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci while (ifa1) { 50262306a36Sopenharmony_ci if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 50362306a36Sopenharmony_ci ifa->ifa_scope <= ifa1->ifa_scope) 50462306a36Sopenharmony_ci last_primary = &ifa1->ifa_next; 50562306a36Sopenharmony_ci if (ifa1->ifa_mask == ifa->ifa_mask && 50662306a36Sopenharmony_ci inet_ifa_match(ifa1->ifa_address, ifa)) { 50762306a36Sopenharmony_ci if (ifa1->ifa_local == ifa->ifa_local) { 50862306a36Sopenharmony_ci inet_free_ifa(ifa); 50962306a36Sopenharmony_ci return -EEXIST; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci if (ifa1->ifa_scope != ifa->ifa_scope) { 51262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value"); 51362306a36Sopenharmony_ci inet_free_ifa(ifa); 51462306a36Sopenharmony_ci return -EINVAL; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci ifa->ifa_flags |= IFA_F_SECONDARY; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ifap = &ifa1->ifa_next; 52062306a36Sopenharmony_ci ifa1 = rtnl_dereference(*ifap); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Allow any devices that wish to register ifaddr validtors to weigh 52462306a36Sopenharmony_ci * in now, before changes are committed. The rntl lock is serializing 52562306a36Sopenharmony_ci * access here, so the state should not change between a validator call 52662306a36Sopenharmony_ci * and a final notify on commit. This isn't invoked on promotion under 52762306a36Sopenharmony_ci * the assumption that validators are checking the address itself, and 52862306a36Sopenharmony_ci * not the flags. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci ivi.ivi_addr = ifa->ifa_address; 53162306a36Sopenharmony_ci ivi.ivi_dev = ifa->ifa_dev; 53262306a36Sopenharmony_ci ivi.extack = extack; 53362306a36Sopenharmony_ci ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 53462306a36Sopenharmony_ci NETDEV_UP, &ivi); 53562306a36Sopenharmony_ci ret = notifier_to_errno(ret); 53662306a36Sopenharmony_ci if (ret) { 53762306a36Sopenharmony_ci inet_free_ifa(ifa); 53862306a36Sopenharmony_ci return ret; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_SECONDARY)) 54262306a36Sopenharmony_ci ifap = last_primary; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci rcu_assign_pointer(ifa->ifa_next, *ifap); 54562306a36Sopenharmony_ci rcu_assign_pointer(*ifap, ifa); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci inet_hash_insert(dev_net(in_dev->dev), ifa); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci cancel_delayed_work(&check_lifetime_work); 55062306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Send message first, then call notifier. 55362306a36Sopenharmony_ci Notifier will trigger FIB update, so that 55462306a36Sopenharmony_ci listeners of netlink will know about new ifaddr */ 55562306a36Sopenharmony_ci rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 55662306a36Sopenharmony_ci blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int inet_insert_ifa(struct in_ifaddr *ifa) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci return __inet_insert_ifa(ifa, NULL, 0, NULL); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ASSERT_RTNL(); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (!in_dev) { 57362306a36Sopenharmony_ci inet_free_ifa(ifa); 57462306a36Sopenharmony_ci return -ENOBUFS; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci ipv4_devconf_setall(in_dev); 57762306a36Sopenharmony_ci neigh_parms_data_state_setall(in_dev->arp_parms); 57862306a36Sopenharmony_ci if (ifa->ifa_dev != in_dev) { 57962306a36Sopenharmony_ci WARN_ON(ifa->ifa_dev); 58062306a36Sopenharmony_ci in_dev_hold(in_dev); 58162306a36Sopenharmony_ci ifa->ifa_dev = in_dev; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci if (ipv4_is_loopback(ifa->ifa_local)) 58462306a36Sopenharmony_ci ifa->ifa_scope = RT_SCOPE_HOST; 58562306a36Sopenharmony_ci return inet_insert_ifa(ifa); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* Caller must hold RCU or RTNL : 58962306a36Sopenharmony_ci * We dont take a reference on found in_device 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistruct in_device *inetdev_by_index(struct net *net, int ifindex) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct net_device *dev; 59462306a36Sopenharmony_ci struct in_device *in_dev = NULL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci rcu_read_lock(); 59762306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, ifindex); 59862306a36Sopenharmony_ci if (dev) 59962306a36Sopenharmony_ci in_dev = rcu_dereference_rtnl(dev->ip_ptr); 60062306a36Sopenharmony_ci rcu_read_unlock(); 60162306a36Sopenharmony_ci return in_dev; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ciEXPORT_SYMBOL(inetdev_by_index); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* Called only from RTNL semaphored context. No locks. */ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistruct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 60862306a36Sopenharmony_ci __be32 mask) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct in_ifaddr *ifa; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ASSERT_RTNL(); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 61562306a36Sopenharmony_ci if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 61662306a36Sopenharmony_ci return ifa; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci return NULL; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int ip_mc_autojoin_config(struct net *net, bool join, 62262306a36Sopenharmony_ci const struct in_ifaddr *ifa) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci#if defined(CONFIG_IP_MULTICAST) 62562306a36Sopenharmony_ci struct ip_mreqn mreq = { 62662306a36Sopenharmony_ci .imr_multiaddr.s_addr = ifa->ifa_address, 62762306a36Sopenharmony_ci .imr_ifindex = ifa->ifa_dev->dev->ifindex, 62862306a36Sopenharmony_ci }; 62962306a36Sopenharmony_ci struct sock *sk = net->ipv4.mc_autojoin_sk; 63062306a36Sopenharmony_ci int ret; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ASSERT_RTNL(); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci lock_sock(sk); 63562306a36Sopenharmony_ci if (join) 63662306a36Sopenharmony_ci ret = ip_mc_join_group(sk, &mreq); 63762306a36Sopenharmony_ci else 63862306a36Sopenharmony_ci ret = ip_mc_leave_group(sk, &mreq); 63962306a36Sopenharmony_ci release_sock(sk); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci#else 64362306a36Sopenharmony_ci return -EOPNOTSUPP; 64462306a36Sopenharmony_ci#endif 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 64862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 65162306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap; 65262306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 65362306a36Sopenharmony_ci struct in_device *in_dev; 65462306a36Sopenharmony_ci struct ifaddrmsg *ifm; 65562306a36Sopenharmony_ci struct in_ifaddr *ifa; 65662306a36Sopenharmony_ci int err; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci ASSERT_RTNL(); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 66162306a36Sopenharmony_ci ifa_ipv4_policy, extack); 66262306a36Sopenharmony_ci if (err < 0) 66362306a36Sopenharmony_ci goto errout; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 66662306a36Sopenharmony_ci in_dev = inetdev_by_index(net, ifm->ifa_index); 66762306a36Sopenharmony_ci if (!in_dev) { 66862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 66962306a36Sopenharmony_ci err = -ENODEV; 67062306a36Sopenharmony_ci goto errout; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL; 67462306a36Sopenharmony_ci ifap = &ifa->ifa_next) { 67562306a36Sopenharmony_ci if (tb[IFA_LOCAL] && 67662306a36Sopenharmony_ci ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 67762306a36Sopenharmony_ci continue; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 68062306a36Sopenharmony_ci continue; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (tb[IFA_ADDRESS] && 68362306a36Sopenharmony_ci (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 68462306a36Sopenharmony_ci !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 68562306a36Sopenharmony_ci continue; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (ipv4_is_multicast(ifa->ifa_address)) 68862306a36Sopenharmony_ci ip_mc_autojoin_config(net, false, ifa); 68962306a36Sopenharmony_ci __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Address not found"); 69462306a36Sopenharmony_ci err = -EADDRNOTAVAIL; 69562306a36Sopenharmony_cierrout: 69662306a36Sopenharmony_ci return err; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci#define INFINITY_LIFE_TIME 0xFFFFFFFF 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic void check_lifetime(struct work_struct *work) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci unsigned long now, next, next_sec, next_sched; 70462306a36Sopenharmony_ci struct in_ifaddr *ifa; 70562306a36Sopenharmony_ci struct hlist_node *n; 70662306a36Sopenharmony_ci int i; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci now = jiffies; 70962306a36Sopenharmony_ci next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci for (i = 0; i < IN4_ADDR_HSIZE; i++) { 71262306a36Sopenharmony_ci bool change_needed = false; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci rcu_read_lock(); 71562306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 71662306a36Sopenharmony_ci unsigned long age; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_PERMANENT) 71962306a36Sopenharmony_ci continue; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* We try to batch several events at once. */ 72262306a36Sopenharmony_ci age = (now - ifa->ifa_tstamp + 72362306a36Sopenharmony_ci ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 72662306a36Sopenharmony_ci age >= ifa->ifa_valid_lft) { 72762306a36Sopenharmony_ci change_needed = true; 72862306a36Sopenharmony_ci } else if (ifa->ifa_preferred_lft == 72962306a36Sopenharmony_ci INFINITY_LIFE_TIME) { 73062306a36Sopenharmony_ci continue; 73162306a36Sopenharmony_ci } else if (age >= ifa->ifa_preferred_lft) { 73262306a36Sopenharmony_ci if (time_before(ifa->ifa_tstamp + 73362306a36Sopenharmony_ci ifa->ifa_valid_lft * HZ, next)) 73462306a36Sopenharmony_ci next = ifa->ifa_tstamp + 73562306a36Sopenharmony_ci ifa->ifa_valid_lft * HZ; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 73862306a36Sopenharmony_ci change_needed = true; 73962306a36Sopenharmony_ci } else if (time_before(ifa->ifa_tstamp + 74062306a36Sopenharmony_ci ifa->ifa_preferred_lft * HZ, 74162306a36Sopenharmony_ci next)) { 74262306a36Sopenharmony_ci next = ifa->ifa_tstamp + 74362306a36Sopenharmony_ci ifa->ifa_preferred_lft * HZ; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci rcu_read_unlock(); 74762306a36Sopenharmony_ci if (!change_needed) 74862306a36Sopenharmony_ci continue; 74962306a36Sopenharmony_ci rtnl_lock(); 75062306a36Sopenharmony_ci hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 75162306a36Sopenharmony_ci unsigned long age; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_PERMANENT) 75462306a36Sopenharmony_ci continue; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* We try to batch several events at once. */ 75762306a36Sopenharmony_ci age = (now - ifa->ifa_tstamp + 75862306a36Sopenharmony_ci ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 76162306a36Sopenharmony_ci age >= ifa->ifa_valid_lft) { 76262306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap; 76362306a36Sopenharmony_ci struct in_ifaddr *tmp; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci ifap = &ifa->ifa_dev->ifa_list; 76662306a36Sopenharmony_ci tmp = rtnl_dereference(*ifap); 76762306a36Sopenharmony_ci while (tmp) { 76862306a36Sopenharmony_ci if (tmp == ifa) { 76962306a36Sopenharmony_ci inet_del_ifa(ifa->ifa_dev, 77062306a36Sopenharmony_ci ifap, 1); 77162306a36Sopenharmony_ci break; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci ifap = &tmp->ifa_next; 77462306a36Sopenharmony_ci tmp = rtnl_dereference(*ifap); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci } else if (ifa->ifa_preferred_lft != 77762306a36Sopenharmony_ci INFINITY_LIFE_TIME && 77862306a36Sopenharmony_ci age >= ifa->ifa_preferred_lft && 77962306a36Sopenharmony_ci !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 78062306a36Sopenharmony_ci ifa->ifa_flags |= IFA_F_DEPRECATED; 78162306a36Sopenharmony_ci rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci rtnl_unlock(); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci next_sec = round_jiffies_up(next); 78862306a36Sopenharmony_ci next_sched = next; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* If rounded timeout is accurate enough, accept it. */ 79162306a36Sopenharmony_ci if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 79262306a36Sopenharmony_ci next_sched = next_sec; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci now = jiffies; 79562306a36Sopenharmony_ci /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 79662306a36Sopenharmony_ci if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 79762306a36Sopenharmony_ci next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 80062306a36Sopenharmony_ci next_sched - now); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 80462306a36Sopenharmony_ci __u32 prefered_lft) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci unsigned long timeout; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(valid_lft, HZ); 81162306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) 81262306a36Sopenharmony_ci ifa->ifa_valid_lft = timeout; 81362306a36Sopenharmony_ci else 81462306a36Sopenharmony_ci ifa->ifa_flags |= IFA_F_PERMANENT; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(prefered_lft, HZ); 81762306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 81862306a36Sopenharmony_ci if (timeout == 0) 81962306a36Sopenharmony_ci ifa->ifa_flags |= IFA_F_DEPRECATED; 82062306a36Sopenharmony_ci ifa->ifa_preferred_lft = timeout; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci ifa->ifa_tstamp = jiffies; 82362306a36Sopenharmony_ci if (!ifa->ifa_cstamp) 82462306a36Sopenharmony_ci ifa->ifa_cstamp = ifa->ifa_tstamp; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 82862306a36Sopenharmony_ci __u32 *pvalid_lft, __u32 *pprefered_lft, 82962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 83262306a36Sopenharmony_ci struct in_ifaddr *ifa; 83362306a36Sopenharmony_ci struct ifaddrmsg *ifm; 83462306a36Sopenharmony_ci struct net_device *dev; 83562306a36Sopenharmony_ci struct in_device *in_dev; 83662306a36Sopenharmony_ci int err; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 83962306a36Sopenharmony_ci ifa_ipv4_policy, extack); 84062306a36Sopenharmony_ci if (err < 0) 84162306a36Sopenharmony_ci goto errout; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 84462306a36Sopenharmony_ci err = -EINVAL; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (ifm->ifa_prefixlen > 32) { 84762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length"); 84862306a36Sopenharmony_ci goto errout; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (!tb[IFA_LOCAL]) { 85262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied"); 85362306a36Sopenharmony_ci goto errout; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifm->ifa_index); 85762306a36Sopenharmony_ci err = -ENODEV; 85862306a36Sopenharmony_ci if (!dev) { 85962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 86062306a36Sopenharmony_ci goto errout; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 86462306a36Sopenharmony_ci err = -ENOBUFS; 86562306a36Sopenharmony_ci if (!in_dev) 86662306a36Sopenharmony_ci goto errout; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci ifa = inet_alloc_ifa(); 86962306a36Sopenharmony_ci if (!ifa) 87062306a36Sopenharmony_ci /* 87162306a36Sopenharmony_ci * A potential indev allocation can be left alive, it stays 87262306a36Sopenharmony_ci * assigned to its device and is destroy with it. 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_ci goto errout; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci ipv4_devconf_setall(in_dev); 87762306a36Sopenharmony_ci neigh_parms_data_state_setall(in_dev->arp_parms); 87862306a36Sopenharmony_ci in_dev_hold(in_dev); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!tb[IFA_ADDRESS]) 88162306a36Sopenharmony_ci tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci INIT_HLIST_NODE(&ifa->hash); 88462306a36Sopenharmony_ci ifa->ifa_prefixlen = ifm->ifa_prefixlen; 88562306a36Sopenharmony_ci ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 88662306a36Sopenharmony_ci ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 88762306a36Sopenharmony_ci ifm->ifa_flags; 88862306a36Sopenharmony_ci ifa->ifa_scope = ifm->ifa_scope; 88962306a36Sopenharmony_ci ifa->ifa_dev = in_dev; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 89262306a36Sopenharmony_ci ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (tb[IFA_BROADCAST]) 89562306a36Sopenharmony_ci ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (tb[IFA_LABEL]) 89862306a36Sopenharmony_ci nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (tb[IFA_RT_PRIORITY]) 90362306a36Sopenharmony_ci ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (tb[IFA_PROTO]) 90662306a36Sopenharmony_ci ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (tb[IFA_CACHEINFO]) { 90962306a36Sopenharmony_ci struct ifa_cacheinfo *ci; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci ci = nla_data(tb[IFA_CACHEINFO]); 91262306a36Sopenharmony_ci if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 91362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid"); 91462306a36Sopenharmony_ci err = -EINVAL; 91562306a36Sopenharmony_ci goto errout_free; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci *pvalid_lft = ci->ifa_valid; 91862306a36Sopenharmony_ci *pprefered_lft = ci->ifa_prefered; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return ifa; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cierrout_free: 92462306a36Sopenharmony_ci inet_free_ifa(ifa); 92562306a36Sopenharmony_cierrout: 92662306a36Sopenharmony_ci return ERR_PTR(err); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct in_device *in_dev = ifa->ifa_dev; 93262306a36Sopenharmony_ci struct in_ifaddr *ifa1; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (!ifa->ifa_local) 93562306a36Sopenharmony_ci return NULL; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa1, in_dev) { 93862306a36Sopenharmony_ci if (ifa1->ifa_mask == ifa->ifa_mask && 93962306a36Sopenharmony_ci inet_ifa_match(ifa1->ifa_address, ifa) && 94062306a36Sopenharmony_ci ifa1->ifa_local == ifa->ifa_local) 94162306a36Sopenharmony_ci return ifa1; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci return NULL; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 94762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 95062306a36Sopenharmony_ci struct in_ifaddr *ifa; 95162306a36Sopenharmony_ci struct in_ifaddr *ifa_existing; 95262306a36Sopenharmony_ci __u32 valid_lft = INFINITY_LIFE_TIME; 95362306a36Sopenharmony_ci __u32 prefered_lft = INFINITY_LIFE_TIME; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci ASSERT_RTNL(); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack); 95862306a36Sopenharmony_ci if (IS_ERR(ifa)) 95962306a36Sopenharmony_ci return PTR_ERR(ifa); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci ifa_existing = find_matching_ifa(ifa); 96262306a36Sopenharmony_ci if (!ifa_existing) { 96362306a36Sopenharmony_ci /* It would be best to check for !NLM_F_CREATE here but 96462306a36Sopenharmony_ci * userspace already relies on not having to provide this. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_ci set_ifa_lifetime(ifa, valid_lft, prefered_lft); 96762306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 96862306a36Sopenharmony_ci int ret = ip_mc_autojoin_config(net, true, ifa); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (ret < 0) { 97162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed"); 97262306a36Sopenharmony_ci inet_free_ifa(ifa); 97362306a36Sopenharmony_ci return ret; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 97762306a36Sopenharmony_ci extack); 97862306a36Sopenharmony_ci } else { 97962306a36Sopenharmony_ci u32 new_metric = ifa->ifa_rt_priority; 98062306a36Sopenharmony_ci u8 new_proto = ifa->ifa_proto; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci inet_free_ifa(ifa); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_EXCL || 98562306a36Sopenharmony_ci !(nlh->nlmsg_flags & NLM_F_REPLACE)) { 98662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Address already assigned"); 98762306a36Sopenharmony_ci return -EEXIST; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci ifa = ifa_existing; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (ifa->ifa_rt_priority != new_metric) { 99262306a36Sopenharmony_ci fib_modify_prefix_metric(ifa, new_metric); 99362306a36Sopenharmony_ci ifa->ifa_rt_priority = new_metric; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci ifa->ifa_proto = new_proto; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci set_ifa_lifetime(ifa, valid_lft, prefered_lft); 99962306a36Sopenharmony_ci cancel_delayed_work(&check_lifetime_work); 100062306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, 100162306a36Sopenharmony_ci &check_lifetime_work, 0); 100262306a36Sopenharmony_ci rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci return 0; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/* 100862306a36Sopenharmony_ci * Determine a default network mask, based on the IP address. 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int inet_abc_len(__be32 addr) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci int rc = -1; /* Something else, probably a multicast. */ 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) 101662306a36Sopenharmony_ci rc = 0; 101762306a36Sopenharmony_ci else { 101862306a36Sopenharmony_ci __u32 haddr = ntohl(addr); 101962306a36Sopenharmony_ci if (IN_CLASSA(haddr)) 102062306a36Sopenharmony_ci rc = 8; 102162306a36Sopenharmony_ci else if (IN_CLASSB(haddr)) 102262306a36Sopenharmony_ci rc = 16; 102362306a36Sopenharmony_ci else if (IN_CLASSC(haddr)) 102462306a36Sopenharmony_ci rc = 24; 102562306a36Sopenharmony_ci else if (IN_CLASSE(haddr)) 102662306a36Sopenharmony_ci rc = 32; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return rc; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ciint devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct sockaddr_in sin_orig; 103662306a36Sopenharmony_ci struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 103762306a36Sopenharmony_ci struct in_ifaddr __rcu **ifap = NULL; 103862306a36Sopenharmony_ci struct in_device *in_dev; 103962306a36Sopenharmony_ci struct in_ifaddr *ifa = NULL; 104062306a36Sopenharmony_ci struct net_device *dev; 104162306a36Sopenharmony_ci char *colon; 104262306a36Sopenharmony_ci int ret = -EFAULT; 104362306a36Sopenharmony_ci int tryaddrmatch = 0; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci ifr->ifr_name[IFNAMSIZ - 1] = 0; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci /* save original address for comparison */ 104862306a36Sopenharmony_ci memcpy(&sin_orig, sin, sizeof(*sin)); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci colon = strchr(ifr->ifr_name, ':'); 105162306a36Sopenharmony_ci if (colon) 105262306a36Sopenharmony_ci *colon = 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci dev_load(net, ifr->ifr_name); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci switch (cmd) { 105762306a36Sopenharmony_ci case SIOCGIFADDR: /* Get interface address */ 105862306a36Sopenharmony_ci case SIOCGIFBRDADDR: /* Get the broadcast address */ 105962306a36Sopenharmony_ci case SIOCGIFDSTADDR: /* Get the destination address */ 106062306a36Sopenharmony_ci case SIOCGIFNETMASK: /* Get the netmask for the interface */ 106162306a36Sopenharmony_ci /* Note that these ioctls will not sleep, 106262306a36Sopenharmony_ci so that we do not impose a lock. 106362306a36Sopenharmony_ci One day we will be forced to put shlock here (I mean SMP) 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ci tryaddrmatch = (sin_orig.sin_family == AF_INET); 106662306a36Sopenharmony_ci memset(sin, 0, sizeof(*sin)); 106762306a36Sopenharmony_ci sin->sin_family = AF_INET; 106862306a36Sopenharmony_ci break; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci case SIOCSIFFLAGS: 107162306a36Sopenharmony_ci ret = -EPERM; 107262306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 107362306a36Sopenharmony_ci goto out; 107462306a36Sopenharmony_ci break; 107562306a36Sopenharmony_ci case SIOCSIFADDR: /* Set interface address (and family) */ 107662306a36Sopenharmony_ci case SIOCSIFBRDADDR: /* Set the broadcast address */ 107762306a36Sopenharmony_ci case SIOCSIFDSTADDR: /* Set the destination address */ 107862306a36Sopenharmony_ci case SIOCSIFNETMASK: /* Set the netmask for the interface */ 107962306a36Sopenharmony_ci ret = -EPERM; 108062306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 108162306a36Sopenharmony_ci goto out; 108262306a36Sopenharmony_ci ret = -EINVAL; 108362306a36Sopenharmony_ci if (sin->sin_family != AF_INET) 108462306a36Sopenharmony_ci goto out; 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci default: 108762306a36Sopenharmony_ci ret = -EINVAL; 108862306a36Sopenharmony_ci goto out; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci rtnl_lock(); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci ret = -ENODEV; 109462306a36Sopenharmony_ci dev = __dev_get_by_name(net, ifr->ifr_name); 109562306a36Sopenharmony_ci if (!dev) 109662306a36Sopenharmony_ci goto done; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (colon) 109962306a36Sopenharmony_ci *colon = ':'; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 110262306a36Sopenharmony_ci if (in_dev) { 110362306a36Sopenharmony_ci if (tryaddrmatch) { 110462306a36Sopenharmony_ci /* Matthias Andree */ 110562306a36Sopenharmony_ci /* compare label and address (4.4BSD style) */ 110662306a36Sopenharmony_ci /* note: we only do this for a limited set of ioctls 110762306a36Sopenharmony_ci and only if the original address family was AF_INET. 110862306a36Sopenharmony_ci This is checked above. */ 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci for (ifap = &in_dev->ifa_list; 111162306a36Sopenharmony_ci (ifa = rtnl_dereference(*ifap)) != NULL; 111262306a36Sopenharmony_ci ifap = &ifa->ifa_next) { 111362306a36Sopenharmony_ci if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 111462306a36Sopenharmony_ci sin_orig.sin_addr.s_addr == 111562306a36Sopenharmony_ci ifa->ifa_local) { 111662306a36Sopenharmony_ci break; /* found */ 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci /* we didn't get a match, maybe the application is 112162306a36Sopenharmony_ci 4.3BSD-style and passed in junk so we fall back to 112262306a36Sopenharmony_ci comparing just the label */ 112362306a36Sopenharmony_ci if (!ifa) { 112462306a36Sopenharmony_ci for (ifap = &in_dev->ifa_list; 112562306a36Sopenharmony_ci (ifa = rtnl_dereference(*ifap)) != NULL; 112662306a36Sopenharmony_ci ifap = &ifa->ifa_next) 112762306a36Sopenharmony_ci if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 113362306a36Sopenharmony_ci if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 113462306a36Sopenharmony_ci goto done; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci switch (cmd) { 113762306a36Sopenharmony_ci case SIOCGIFADDR: /* Get interface address */ 113862306a36Sopenharmony_ci ret = 0; 113962306a36Sopenharmony_ci sin->sin_addr.s_addr = ifa->ifa_local; 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci case SIOCGIFBRDADDR: /* Get the broadcast address */ 114362306a36Sopenharmony_ci ret = 0; 114462306a36Sopenharmony_ci sin->sin_addr.s_addr = ifa->ifa_broadcast; 114562306a36Sopenharmony_ci break; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci case SIOCGIFDSTADDR: /* Get the destination address */ 114862306a36Sopenharmony_ci ret = 0; 114962306a36Sopenharmony_ci sin->sin_addr.s_addr = ifa->ifa_address; 115062306a36Sopenharmony_ci break; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci case SIOCGIFNETMASK: /* Get the netmask for the interface */ 115362306a36Sopenharmony_ci ret = 0; 115462306a36Sopenharmony_ci sin->sin_addr.s_addr = ifa->ifa_mask; 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci case SIOCSIFFLAGS: 115862306a36Sopenharmony_ci if (colon) { 115962306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 116062306a36Sopenharmony_ci if (!ifa) 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci ret = 0; 116362306a36Sopenharmony_ci if (!(ifr->ifr_flags & IFF_UP)) 116462306a36Sopenharmony_ci inet_del_ifa(in_dev, ifap, 1); 116562306a36Sopenharmony_ci break; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci ret = dev_change_flags(dev, ifr->ifr_flags, NULL); 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci case SIOCSIFADDR: /* Set interface address (and family) */ 117162306a36Sopenharmony_ci ret = -EINVAL; 117262306a36Sopenharmony_ci if (inet_abc_len(sin->sin_addr.s_addr) < 0) 117362306a36Sopenharmony_ci break; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (!ifa) { 117662306a36Sopenharmony_ci ret = -ENOBUFS; 117762306a36Sopenharmony_ci ifa = inet_alloc_ifa(); 117862306a36Sopenharmony_ci if (!ifa) 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci INIT_HLIST_NODE(&ifa->hash); 118162306a36Sopenharmony_ci if (colon) 118262306a36Sopenharmony_ci memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 118362306a36Sopenharmony_ci else 118462306a36Sopenharmony_ci memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 118562306a36Sopenharmony_ci } else { 118662306a36Sopenharmony_ci ret = 0; 118762306a36Sopenharmony_ci if (ifa->ifa_local == sin->sin_addr.s_addr) 118862306a36Sopenharmony_ci break; 118962306a36Sopenharmony_ci inet_del_ifa(in_dev, ifap, 0); 119062306a36Sopenharmony_ci ifa->ifa_broadcast = 0; 119162306a36Sopenharmony_ci ifa->ifa_scope = 0; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (!(dev->flags & IFF_POINTOPOINT)) { 119762306a36Sopenharmony_ci ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 119862306a36Sopenharmony_ci ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 119962306a36Sopenharmony_ci if ((dev->flags & IFF_BROADCAST) && 120062306a36Sopenharmony_ci ifa->ifa_prefixlen < 31) 120162306a36Sopenharmony_ci ifa->ifa_broadcast = ifa->ifa_address | 120262306a36Sopenharmony_ci ~ifa->ifa_mask; 120362306a36Sopenharmony_ci } else { 120462306a36Sopenharmony_ci ifa->ifa_prefixlen = 32; 120562306a36Sopenharmony_ci ifa->ifa_mask = inet_make_mask(32); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 120862306a36Sopenharmony_ci ret = inet_set_ifa(dev, ifa); 120962306a36Sopenharmony_ci break; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci case SIOCSIFBRDADDR: /* Set the broadcast address */ 121262306a36Sopenharmony_ci ret = 0; 121362306a36Sopenharmony_ci if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 121462306a36Sopenharmony_ci inet_del_ifa(in_dev, ifap, 0); 121562306a36Sopenharmony_ci ifa->ifa_broadcast = sin->sin_addr.s_addr; 121662306a36Sopenharmony_ci inet_insert_ifa(ifa); 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci case SIOCSIFDSTADDR: /* Set the destination address */ 122162306a36Sopenharmony_ci ret = 0; 122262306a36Sopenharmony_ci if (ifa->ifa_address == sin->sin_addr.s_addr) 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci ret = -EINVAL; 122562306a36Sopenharmony_ci if (inet_abc_len(sin->sin_addr.s_addr) < 0) 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci ret = 0; 122862306a36Sopenharmony_ci inet_del_ifa(in_dev, ifap, 0); 122962306a36Sopenharmony_ci ifa->ifa_address = sin->sin_addr.s_addr; 123062306a36Sopenharmony_ci inet_insert_ifa(ifa); 123162306a36Sopenharmony_ci break; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci case SIOCSIFNETMASK: /* Set the netmask for the interface */ 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* 123662306a36Sopenharmony_ci * The mask we set must be legal. 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_ci ret = -EINVAL; 123962306a36Sopenharmony_ci if (bad_mask(sin->sin_addr.s_addr, 0)) 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci ret = 0; 124262306a36Sopenharmony_ci if (ifa->ifa_mask != sin->sin_addr.s_addr) { 124362306a36Sopenharmony_ci __be32 old_mask = ifa->ifa_mask; 124462306a36Sopenharmony_ci inet_del_ifa(in_dev, ifap, 0); 124562306a36Sopenharmony_ci ifa->ifa_mask = sin->sin_addr.s_addr; 124662306a36Sopenharmony_ci ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* See if current broadcast address matches 124962306a36Sopenharmony_ci * with current netmask, then recalculate 125062306a36Sopenharmony_ci * the broadcast address. Otherwise it's a 125162306a36Sopenharmony_ci * funny address, so don't touch it since 125262306a36Sopenharmony_ci * the user seems to know what (s)he's doing... 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_ci if ((dev->flags & IFF_BROADCAST) && 125562306a36Sopenharmony_ci (ifa->ifa_prefixlen < 31) && 125662306a36Sopenharmony_ci (ifa->ifa_broadcast == 125762306a36Sopenharmony_ci (ifa->ifa_local|~old_mask))) { 125862306a36Sopenharmony_ci ifa->ifa_broadcast = (ifa->ifa_local | 125962306a36Sopenharmony_ci ~sin->sin_addr.s_addr); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci inet_insert_ifa(ifa); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_cidone: 126662306a36Sopenharmony_ci rtnl_unlock(); 126762306a36Sopenharmony_ciout: 126862306a36Sopenharmony_ci return ret; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ciint inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 127462306a36Sopenharmony_ci const struct in_ifaddr *ifa; 127562306a36Sopenharmony_ci struct ifreq ifr; 127662306a36Sopenharmony_ci int done = 0; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (WARN_ON(size > sizeof(struct ifreq))) 127962306a36Sopenharmony_ci goto out; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (!in_dev) 128262306a36Sopenharmony_ci goto out; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 128562306a36Sopenharmony_ci if (!buf) { 128662306a36Sopenharmony_ci done += size; 128762306a36Sopenharmony_ci continue; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci if (len < size) 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci memset(&ifr, 0, sizeof(struct ifreq)); 129262306a36Sopenharmony_ci strcpy(ifr.ifr_name, ifa->ifa_label); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 129562306a36Sopenharmony_ci (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 129662306a36Sopenharmony_ci ifa->ifa_local; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (copy_to_user(buf + done, &ifr, size)) { 129962306a36Sopenharmony_ci done = -EFAULT; 130062306a36Sopenharmony_ci break; 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci len -= size; 130362306a36Sopenharmony_ci done += size; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ciout: 130662306a36Sopenharmony_ci return done; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic __be32 in_dev_select_addr(const struct in_device *in_dev, 131062306a36Sopenharmony_ci int scope) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci const struct in_ifaddr *ifa; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 131562306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_SECONDARY) 131662306a36Sopenharmony_ci continue; 131762306a36Sopenharmony_ci if (ifa->ifa_scope != RT_SCOPE_LINK && 131862306a36Sopenharmony_ci ifa->ifa_scope <= scope) 131962306a36Sopenharmony_ci return ifa->ifa_local; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci return 0; 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci const struct in_ifaddr *ifa; 132862306a36Sopenharmony_ci __be32 addr = 0; 132962306a36Sopenharmony_ci unsigned char localnet_scope = RT_SCOPE_HOST; 133062306a36Sopenharmony_ci struct in_device *in_dev; 133162306a36Sopenharmony_ci struct net *net = dev_net(dev); 133262306a36Sopenharmony_ci int master_idx; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci rcu_read_lock(); 133562306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 133662306a36Sopenharmony_ci if (!in_dev) 133762306a36Sopenharmony_ci goto no_in_dev; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 134062306a36Sopenharmony_ci localnet_scope = RT_SCOPE_LINK; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 134362306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_SECONDARY) 134462306a36Sopenharmony_ci continue; 134562306a36Sopenharmony_ci if (min(ifa->ifa_scope, localnet_scope) > scope) 134662306a36Sopenharmony_ci continue; 134762306a36Sopenharmony_ci if (!dst || inet_ifa_match(dst, ifa)) { 134862306a36Sopenharmony_ci addr = ifa->ifa_local; 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci if (!addr) 135262306a36Sopenharmony_ci addr = ifa->ifa_local; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (addr) 135662306a36Sopenharmony_ci goto out_unlock; 135762306a36Sopenharmony_cino_in_dev: 135862306a36Sopenharmony_ci master_idx = l3mdev_master_ifindex_rcu(dev); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* For VRFs, the VRF device takes the place of the loopback device, 136162306a36Sopenharmony_ci * with addresses on it being preferred. Note in such cases the 136262306a36Sopenharmony_ci * loopback device will be among the devices that fail the master_idx 136362306a36Sopenharmony_ci * equality check in the loop below. 136462306a36Sopenharmony_ci */ 136562306a36Sopenharmony_ci if (master_idx && 136662306a36Sopenharmony_ci (dev = dev_get_by_index_rcu(net, master_idx)) && 136762306a36Sopenharmony_ci (in_dev = __in_dev_get_rcu(dev))) { 136862306a36Sopenharmony_ci addr = in_dev_select_addr(in_dev, scope); 136962306a36Sopenharmony_ci if (addr) 137062306a36Sopenharmony_ci goto out_unlock; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* Not loopback addresses on loopback should be preferred 137462306a36Sopenharmony_ci in this case. It is important that lo is the first interface 137562306a36Sopenharmony_ci in dev_base list. 137662306a36Sopenharmony_ci */ 137762306a36Sopenharmony_ci for_each_netdev_rcu(net, dev) { 137862306a36Sopenharmony_ci if (l3mdev_master_ifindex_rcu(dev) != master_idx) 137962306a36Sopenharmony_ci continue; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 138262306a36Sopenharmony_ci if (!in_dev) 138362306a36Sopenharmony_ci continue; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci addr = in_dev_select_addr(in_dev, scope); 138662306a36Sopenharmony_ci if (addr) 138762306a36Sopenharmony_ci goto out_unlock; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ciout_unlock: 139062306a36Sopenharmony_ci rcu_read_unlock(); 139162306a36Sopenharmony_ci return addr; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ciEXPORT_SYMBOL(inet_select_addr); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 139662306a36Sopenharmony_ci __be32 local, int scope) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci unsigned char localnet_scope = RT_SCOPE_HOST; 139962306a36Sopenharmony_ci const struct in_ifaddr *ifa; 140062306a36Sopenharmony_ci __be32 addr = 0; 140162306a36Sopenharmony_ci int same = 0; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 140462306a36Sopenharmony_ci localnet_scope = RT_SCOPE_LINK; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 140762306a36Sopenharmony_ci unsigned char min_scope = min(ifa->ifa_scope, localnet_scope); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (!addr && 141062306a36Sopenharmony_ci (local == ifa->ifa_local || !local) && 141162306a36Sopenharmony_ci min_scope <= scope) { 141262306a36Sopenharmony_ci addr = ifa->ifa_local; 141362306a36Sopenharmony_ci if (same) 141462306a36Sopenharmony_ci break; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci if (!same) { 141762306a36Sopenharmony_ci same = (!local || inet_ifa_match(local, ifa)) && 141862306a36Sopenharmony_ci (!dst || inet_ifa_match(dst, ifa)); 141962306a36Sopenharmony_ci if (same && addr) { 142062306a36Sopenharmony_ci if (local || !dst) 142162306a36Sopenharmony_ci break; 142262306a36Sopenharmony_ci /* Is the selected addr into dst subnet? */ 142362306a36Sopenharmony_ci if (inet_ifa_match(addr, ifa)) 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci /* No, then can we use new local src? */ 142662306a36Sopenharmony_ci if (min_scope <= scope) { 142762306a36Sopenharmony_ci addr = ifa->ifa_local; 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci /* search for large dst subnet for addr */ 143162306a36Sopenharmony_ci same = 0; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci return same ? addr : 0; 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci/* 144062306a36Sopenharmony_ci * Confirm that local IP address exists using wildcards: 144162306a36Sopenharmony_ci * - net: netns to check, cannot be NULL 144262306a36Sopenharmony_ci * - in_dev: only on this interface, NULL=any interface 144362306a36Sopenharmony_ci * - dst: only in the same subnet as dst, 0=any dst 144462306a36Sopenharmony_ci * - local: address, 0=autoselect the local address 144562306a36Sopenharmony_ci * - scope: maximum allowed scope value for the local address 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci__be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 144862306a36Sopenharmony_ci __be32 dst, __be32 local, int scope) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci __be32 addr = 0; 145162306a36Sopenharmony_ci struct net_device *dev; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (in_dev) 145462306a36Sopenharmony_ci return confirm_addr_indev(in_dev, dst, local, scope); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci rcu_read_lock(); 145762306a36Sopenharmony_ci for_each_netdev_rcu(net, dev) { 145862306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 145962306a36Sopenharmony_ci if (in_dev) { 146062306a36Sopenharmony_ci addr = confirm_addr_indev(in_dev, dst, local, scope); 146162306a36Sopenharmony_ci if (addr) 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci rcu_read_unlock(); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci return addr; 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ciEXPORT_SYMBOL(inet_confirm_addr); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci/* 147262306a36Sopenharmony_ci * Device notifier 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ciint register_inetaddr_notifier(struct notifier_block *nb) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci return blocking_notifier_chain_register(&inetaddr_chain, nb); 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ciEXPORT_SYMBOL(register_inetaddr_notifier); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ciint unregister_inetaddr_notifier(struct notifier_block *nb) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_inetaddr_notifier); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ciint register_inetaddr_validator_notifier(struct notifier_block *nb) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ciEXPORT_SYMBOL(register_inetaddr_validator_notifier); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ciint unregister_inetaddr_validator_notifier(struct notifier_block *nb) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 149662306a36Sopenharmony_ci nb); 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci/* Rename ifa_labels for a device name change. Make some effort to preserve 150162306a36Sopenharmony_ci * existing alias numbering and to create unique labels if possible. 150262306a36Sopenharmony_ci*/ 150362306a36Sopenharmony_cistatic void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct in_ifaddr *ifa; 150662306a36Sopenharmony_ci int named = 0; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 150962306a36Sopenharmony_ci char old[IFNAMSIZ], *dot; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci memcpy(old, ifa->ifa_label, IFNAMSIZ); 151262306a36Sopenharmony_ci memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 151362306a36Sopenharmony_ci if (named++ == 0) 151462306a36Sopenharmony_ci goto skip; 151562306a36Sopenharmony_ci dot = strchr(old, ':'); 151662306a36Sopenharmony_ci if (!dot) { 151762306a36Sopenharmony_ci sprintf(old, ":%d", named); 151862306a36Sopenharmony_ci dot = old; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 152162306a36Sopenharmony_ci strcat(ifa->ifa_label, dot); 152262306a36Sopenharmony_ci else 152362306a36Sopenharmony_ci strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 152462306a36Sopenharmony_ciskip: 152562306a36Sopenharmony_ci rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 152662306a36Sopenharmony_ci } 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_cistatic void inetdev_send_gratuitous_arp(struct net_device *dev, 153062306a36Sopenharmony_ci struct in_device *in_dev) 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci const struct in_ifaddr *ifa; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 153662306a36Sopenharmony_ci arp_send(ARPOP_REQUEST, ETH_P_ARP, 153762306a36Sopenharmony_ci ifa->ifa_local, dev, 153862306a36Sopenharmony_ci ifa->ifa_local, NULL, 153962306a36Sopenharmony_ci dev->dev_addr, NULL); 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci/* Called only under RTNL semaphore */ 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic int inetdev_event(struct notifier_block *this, unsigned long event, 154662306a36Sopenharmony_ci void *ptr) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 154962306a36Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci ASSERT_RTNL(); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (!in_dev) { 155462306a36Sopenharmony_ci if (event == NETDEV_REGISTER) { 155562306a36Sopenharmony_ci in_dev = inetdev_init(dev); 155662306a36Sopenharmony_ci if (IS_ERR(in_dev)) 155762306a36Sopenharmony_ci return notifier_from_errno(PTR_ERR(in_dev)); 155862306a36Sopenharmony_ci if (dev->flags & IFF_LOOPBACK) { 155962306a36Sopenharmony_ci IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 156062306a36Sopenharmony_ci IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci } else if (event == NETDEV_CHANGEMTU) { 156362306a36Sopenharmony_ci /* Re-enabling IP */ 156462306a36Sopenharmony_ci if (inetdev_valid_mtu(dev->mtu)) 156562306a36Sopenharmony_ci in_dev = inetdev_init(dev); 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci goto out; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci switch (event) { 157162306a36Sopenharmony_ci case NETDEV_REGISTER: 157262306a36Sopenharmony_ci pr_debug("%s: bug\n", __func__); 157362306a36Sopenharmony_ci RCU_INIT_POINTER(dev->ip_ptr, NULL); 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case NETDEV_UP: 157662306a36Sopenharmony_ci if (!inetdev_valid_mtu(dev->mtu)) 157762306a36Sopenharmony_ci break; 157862306a36Sopenharmony_ci if (dev->flags & IFF_LOOPBACK) { 157962306a36Sopenharmony_ci struct in_ifaddr *ifa = inet_alloc_ifa(); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (ifa) { 158262306a36Sopenharmony_ci INIT_HLIST_NODE(&ifa->hash); 158362306a36Sopenharmony_ci ifa->ifa_local = 158462306a36Sopenharmony_ci ifa->ifa_address = htonl(INADDR_LOOPBACK); 158562306a36Sopenharmony_ci ifa->ifa_prefixlen = 8; 158662306a36Sopenharmony_ci ifa->ifa_mask = inet_make_mask(8); 158762306a36Sopenharmony_ci in_dev_hold(in_dev); 158862306a36Sopenharmony_ci ifa->ifa_dev = in_dev; 158962306a36Sopenharmony_ci ifa->ifa_scope = RT_SCOPE_HOST; 159062306a36Sopenharmony_ci memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 159162306a36Sopenharmony_ci set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 159262306a36Sopenharmony_ci INFINITY_LIFE_TIME); 159362306a36Sopenharmony_ci ipv4_devconf_setall(in_dev); 159462306a36Sopenharmony_ci neigh_parms_data_state_setall(in_dev->arp_parms); 159562306a36Sopenharmony_ci inet_insert_ifa(ifa); 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci ip_mc_up(in_dev); 159962306a36Sopenharmony_ci fallthrough; 160062306a36Sopenharmony_ci case NETDEV_CHANGEADDR: 160162306a36Sopenharmony_ci if (!IN_DEV_ARP_NOTIFY(in_dev)) 160262306a36Sopenharmony_ci break; 160362306a36Sopenharmony_ci fallthrough; 160462306a36Sopenharmony_ci case NETDEV_NOTIFY_PEERS: 160562306a36Sopenharmony_ci /* Send gratuitous ARP to notify of link change */ 160662306a36Sopenharmony_ci inetdev_send_gratuitous_arp(dev, in_dev); 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci case NETDEV_DOWN: 160962306a36Sopenharmony_ci ip_mc_down(in_dev); 161062306a36Sopenharmony_ci break; 161162306a36Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 161262306a36Sopenharmony_ci ip_mc_unmap(in_dev); 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci case NETDEV_POST_TYPE_CHANGE: 161562306a36Sopenharmony_ci ip_mc_remap(in_dev); 161662306a36Sopenharmony_ci break; 161762306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 161862306a36Sopenharmony_ci if (inetdev_valid_mtu(dev->mtu)) 161962306a36Sopenharmony_ci break; 162062306a36Sopenharmony_ci /* disable IP when MTU is not enough */ 162162306a36Sopenharmony_ci fallthrough; 162262306a36Sopenharmony_ci case NETDEV_UNREGISTER: 162362306a36Sopenharmony_ci inetdev_destroy(in_dev); 162462306a36Sopenharmony_ci break; 162562306a36Sopenharmony_ci case NETDEV_CHANGENAME: 162662306a36Sopenharmony_ci /* Do not notify about label change, this event is 162762306a36Sopenharmony_ci * not interesting to applications using netlink. 162862306a36Sopenharmony_ci */ 162962306a36Sopenharmony_ci inetdev_changename(dev, in_dev); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci devinet_sysctl_unregister(in_dev); 163262306a36Sopenharmony_ci devinet_sysctl_register(in_dev); 163362306a36Sopenharmony_ci break; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ciout: 163662306a36Sopenharmony_ci return NOTIFY_DONE; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic struct notifier_block ip_netdev_notifier = { 164062306a36Sopenharmony_ci .notifier_call = inetdev_event, 164162306a36Sopenharmony_ci}; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_cistatic size_t inet_nlmsg_size(void) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 164662306a36Sopenharmony_ci + nla_total_size(4) /* IFA_ADDRESS */ 164762306a36Sopenharmony_ci + nla_total_size(4) /* IFA_LOCAL */ 164862306a36Sopenharmony_ci + nla_total_size(4) /* IFA_BROADCAST */ 164962306a36Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 165062306a36Sopenharmony_ci + nla_total_size(4) /* IFA_FLAGS */ 165162306a36Sopenharmony_ci + nla_total_size(1) /* IFA_PROTO */ 165262306a36Sopenharmony_ci + nla_total_size(4) /* IFA_RT_PRIORITY */ 165362306a36Sopenharmony_ci + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic inline u32 cstamp_delta(unsigned long cstamp) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistatic int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 166262306a36Sopenharmony_ci unsigned long tstamp, u32 preferred, u32 valid) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci struct ifa_cacheinfo ci; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ci.cstamp = cstamp_delta(cstamp); 166762306a36Sopenharmony_ci ci.tstamp = cstamp_delta(tstamp); 166862306a36Sopenharmony_ci ci.ifa_prefered = preferred; 166962306a36Sopenharmony_ci ci.ifa_valid = valid; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cistatic int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 167562306a36Sopenharmony_ci struct inet_fill_args *args) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci struct ifaddrmsg *ifm; 167862306a36Sopenharmony_ci struct nlmsghdr *nlh; 167962306a36Sopenharmony_ci u32 preferred, valid; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm), 168262306a36Sopenharmony_ci args->flags); 168362306a36Sopenharmony_ci if (!nlh) 168462306a36Sopenharmony_ci return -EMSGSIZE; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 168762306a36Sopenharmony_ci ifm->ifa_family = AF_INET; 168862306a36Sopenharmony_ci ifm->ifa_prefixlen = ifa->ifa_prefixlen; 168962306a36Sopenharmony_ci ifm->ifa_flags = ifa->ifa_flags; 169062306a36Sopenharmony_ci ifm->ifa_scope = ifa->ifa_scope; 169162306a36Sopenharmony_ci ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (args->netnsid >= 0 && 169462306a36Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 169562306a36Sopenharmony_ci goto nla_put_failure; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 169862306a36Sopenharmony_ci preferred = ifa->ifa_preferred_lft; 169962306a36Sopenharmony_ci valid = ifa->ifa_valid_lft; 170062306a36Sopenharmony_ci if (preferred != INFINITY_LIFE_TIME) { 170162306a36Sopenharmony_ci long tval = (jiffies - ifa->ifa_tstamp) / HZ; 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (preferred > tval) 170462306a36Sopenharmony_ci preferred -= tval; 170562306a36Sopenharmony_ci else 170662306a36Sopenharmony_ci preferred = 0; 170762306a36Sopenharmony_ci if (valid != INFINITY_LIFE_TIME) { 170862306a36Sopenharmony_ci if (valid > tval) 170962306a36Sopenharmony_ci valid -= tval; 171062306a36Sopenharmony_ci else 171162306a36Sopenharmony_ci valid = 0; 171262306a36Sopenharmony_ci } 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci } else { 171562306a36Sopenharmony_ci preferred = INFINITY_LIFE_TIME; 171662306a36Sopenharmony_ci valid = INFINITY_LIFE_TIME; 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci if ((ifa->ifa_address && 171962306a36Sopenharmony_ci nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 172062306a36Sopenharmony_ci (ifa->ifa_local && 172162306a36Sopenharmony_ci nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 172262306a36Sopenharmony_ci (ifa->ifa_broadcast && 172362306a36Sopenharmony_ci nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 172462306a36Sopenharmony_ci (ifa->ifa_label[0] && 172562306a36Sopenharmony_ci nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 172662306a36Sopenharmony_ci (ifa->ifa_proto && 172762306a36Sopenharmony_ci nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) || 172862306a36Sopenharmony_ci nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 172962306a36Sopenharmony_ci (ifa->ifa_rt_priority && 173062306a36Sopenharmony_ci nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 173162306a36Sopenharmony_ci put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 173262306a36Sopenharmony_ci preferred, valid)) 173362306a36Sopenharmony_ci goto nla_put_failure; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci nlmsg_end(skb, nlh); 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_cinla_put_failure: 173962306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 174062306a36Sopenharmony_ci return -EMSGSIZE; 174162306a36Sopenharmony_ci} 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_cistatic int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 174462306a36Sopenharmony_ci struct inet_fill_args *fillargs, 174562306a36Sopenharmony_ci struct net **tgt_net, struct sock *sk, 174662306a36Sopenharmony_ci struct netlink_callback *cb) 174762306a36Sopenharmony_ci{ 174862306a36Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 174962306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 175062306a36Sopenharmony_ci struct ifaddrmsg *ifm; 175162306a36Sopenharmony_ci int err, i; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 175462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request"); 175562306a36Sopenharmony_ci return -EINVAL; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 175962306a36Sopenharmony_ci if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 176062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request"); 176162306a36Sopenharmony_ci return -EINVAL; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci fillargs->ifindex = ifm->ifa_index; 176562306a36Sopenharmony_ci if (fillargs->ifindex) { 176662306a36Sopenharmony_ci cb->answer_flags |= NLM_F_DUMP_FILTERED; 176762306a36Sopenharmony_ci fillargs->flags |= NLM_F_DUMP_FILTERED; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 177162306a36Sopenharmony_ci ifa_ipv4_policy, extack); 177262306a36Sopenharmony_ci if (err < 0) 177362306a36Sopenharmony_ci return err; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci for (i = 0; i <= IFA_MAX; ++i) { 177662306a36Sopenharmony_ci if (!tb[i]) 177762306a36Sopenharmony_ci continue; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if (i == IFA_TARGET_NETNSID) { 178062306a36Sopenharmony_ci struct net *net; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci fillargs->netnsid = nla_get_s32(tb[i]); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 178562306a36Sopenharmony_ci if (IS_ERR(net)) { 178662306a36Sopenharmony_ci fillargs->netnsid = -1; 178762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id"); 178862306a36Sopenharmony_ci return PTR_ERR(net); 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci *tgt_net = net; 179162306a36Sopenharmony_ci } else { 179262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request"); 179362306a36Sopenharmony_ci return -EINVAL; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci return 0; 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 180162306a36Sopenharmony_ci struct netlink_callback *cb, int s_ip_idx, 180262306a36Sopenharmony_ci struct inet_fill_args *fillargs) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci struct in_ifaddr *ifa; 180562306a36Sopenharmony_ci int ip_idx = 0; 180662306a36Sopenharmony_ci int err; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 180962306a36Sopenharmony_ci if (ip_idx < s_ip_idx) { 181062306a36Sopenharmony_ci ip_idx++; 181162306a36Sopenharmony_ci continue; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci err = inet_fill_ifaddr(skb, ifa, fillargs); 181462306a36Sopenharmony_ci if (err < 0) 181562306a36Sopenharmony_ci goto done; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 181862306a36Sopenharmony_ci ip_idx++; 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci err = 0; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cidone: 182362306a36Sopenharmony_ci cb->args[2] = ip_idx; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci return err; 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci/* Combine dev_addr_genid and dev_base_seq to detect changes. 182962306a36Sopenharmony_ci */ 183062306a36Sopenharmony_cistatic u32 inet_base_seq(const struct net *net) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci u32 res = atomic_read(&net->ipv4.dev_addr_genid) + 183362306a36Sopenharmony_ci net->dev_base_seq; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci /* Must not return 0 (see nl_dump_check_consistent()). 183662306a36Sopenharmony_ci * Chose a value far away from 0. 183762306a36Sopenharmony_ci */ 183862306a36Sopenharmony_ci if (!res) 183962306a36Sopenharmony_ci res = 0x80000000; 184062306a36Sopenharmony_ci return res; 184162306a36Sopenharmony_ci} 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_cistatic int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 184662306a36Sopenharmony_ci struct inet_fill_args fillargs = { 184762306a36Sopenharmony_ci .portid = NETLINK_CB(cb->skb).portid, 184862306a36Sopenharmony_ci .seq = nlh->nlmsg_seq, 184962306a36Sopenharmony_ci .event = RTM_NEWADDR, 185062306a36Sopenharmony_ci .flags = NLM_F_MULTI, 185162306a36Sopenharmony_ci .netnsid = -1, 185262306a36Sopenharmony_ci }; 185362306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 185462306a36Sopenharmony_ci struct net *tgt_net = net; 185562306a36Sopenharmony_ci int h, s_h; 185662306a36Sopenharmony_ci int idx, s_idx; 185762306a36Sopenharmony_ci int s_ip_idx; 185862306a36Sopenharmony_ci struct net_device *dev; 185962306a36Sopenharmony_ci struct in_device *in_dev; 186062306a36Sopenharmony_ci struct hlist_head *head; 186162306a36Sopenharmony_ci int err = 0; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci s_h = cb->args[0]; 186462306a36Sopenharmony_ci s_idx = idx = cb->args[1]; 186562306a36Sopenharmony_ci s_ip_idx = cb->args[2]; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci if (cb->strict_check) { 186862306a36Sopenharmony_ci err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 186962306a36Sopenharmony_ci skb->sk, cb); 187062306a36Sopenharmony_ci if (err < 0) 187162306a36Sopenharmony_ci goto put_tgt_net; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci err = 0; 187462306a36Sopenharmony_ci if (fillargs.ifindex) { 187562306a36Sopenharmony_ci dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 187662306a36Sopenharmony_ci if (!dev) { 187762306a36Sopenharmony_ci err = -ENODEV; 187862306a36Sopenharmony_ci goto put_tgt_net; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 188262306a36Sopenharmony_ci if (in_dev) { 188362306a36Sopenharmony_ci err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 188462306a36Sopenharmony_ci &fillargs); 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci goto put_tgt_net; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 189162306a36Sopenharmony_ci idx = 0; 189262306a36Sopenharmony_ci head = &tgt_net->dev_index_head[h]; 189362306a36Sopenharmony_ci rcu_read_lock(); 189462306a36Sopenharmony_ci cb->seq = inet_base_seq(tgt_net); 189562306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 189662306a36Sopenharmony_ci if (idx < s_idx) 189762306a36Sopenharmony_ci goto cont; 189862306a36Sopenharmony_ci if (h > s_h || idx > s_idx) 189962306a36Sopenharmony_ci s_ip_idx = 0; 190062306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 190162306a36Sopenharmony_ci if (!in_dev) 190262306a36Sopenharmony_ci goto cont; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 190562306a36Sopenharmony_ci &fillargs); 190662306a36Sopenharmony_ci if (err < 0) { 190762306a36Sopenharmony_ci rcu_read_unlock(); 190862306a36Sopenharmony_ci goto done; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_cicont: 191162306a36Sopenharmony_ci idx++; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci rcu_read_unlock(); 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cidone: 191762306a36Sopenharmony_ci cb->args[0] = h; 191862306a36Sopenharmony_ci cb->args[1] = idx; 191962306a36Sopenharmony_ciput_tgt_net: 192062306a36Sopenharmony_ci if (fillargs.netnsid >= 0) 192162306a36Sopenharmony_ci put_net(tgt_net); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci return skb->len ? : err; 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_cistatic void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 192762306a36Sopenharmony_ci u32 portid) 192862306a36Sopenharmony_ci{ 192962306a36Sopenharmony_ci struct inet_fill_args fillargs = { 193062306a36Sopenharmony_ci .portid = portid, 193162306a36Sopenharmony_ci .seq = nlh ? nlh->nlmsg_seq : 0, 193262306a36Sopenharmony_ci .event = event, 193362306a36Sopenharmony_ci .flags = 0, 193462306a36Sopenharmony_ci .netnsid = -1, 193562306a36Sopenharmony_ci }; 193662306a36Sopenharmony_ci struct sk_buff *skb; 193762306a36Sopenharmony_ci int err = -ENOBUFS; 193862306a36Sopenharmony_ci struct net *net; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci net = dev_net(ifa->ifa_dev->dev); 194162306a36Sopenharmony_ci skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 194262306a36Sopenharmony_ci if (!skb) 194362306a36Sopenharmony_ci goto errout; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci err = inet_fill_ifaddr(skb, ifa, &fillargs); 194662306a36Sopenharmony_ci if (err < 0) { 194762306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 194862306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 194962306a36Sopenharmony_ci kfree_skb(skb); 195062306a36Sopenharmony_ci goto errout; 195162306a36Sopenharmony_ci } 195262306a36Sopenharmony_ci rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 195362306a36Sopenharmony_ci return; 195462306a36Sopenharmony_cierrout: 195562306a36Sopenharmony_ci if (err < 0) 195662306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cistatic size_t inet_get_link_af_size(const struct net_device *dev, 196062306a36Sopenharmony_ci u32 ext_filter_mask) 196162306a36Sopenharmony_ci{ 196262306a36Sopenharmony_ci struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci if (!in_dev) 196562306a36Sopenharmony_ci return 0; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 197162306a36Sopenharmony_ci u32 ext_filter_mask) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 197462306a36Sopenharmony_ci struct nlattr *nla; 197562306a36Sopenharmony_ci int i; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (!in_dev) 197862306a36Sopenharmony_ci return -ENODATA; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 198162306a36Sopenharmony_ci if (!nla) 198262306a36Sopenharmony_ci return -EMSGSIZE; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci for (i = 0; i < IPV4_DEVCONF_MAX; i++) 198562306a36Sopenharmony_ci ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cistatic const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 199162306a36Sopenharmony_ci [IFLA_INET_CONF] = { .type = NLA_NESTED }, 199262306a36Sopenharmony_ci}; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_cistatic int inet_validate_link_af(const struct net_device *dev, 199562306a36Sopenharmony_ci const struct nlattr *nla, 199662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci struct nlattr *a, *tb[IFLA_INET_MAX+1]; 199962306a36Sopenharmony_ci int err, rem; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci if (dev && !__in_dev_get_rtnl(dev)) 200262306a36Sopenharmony_ci return -EAFNOSUPPORT; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, 200562306a36Sopenharmony_ci inet_af_policy, extack); 200662306a36Sopenharmony_ci if (err < 0) 200762306a36Sopenharmony_ci return err; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci if (tb[IFLA_INET_CONF]) { 201062306a36Sopenharmony_ci nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 201162306a36Sopenharmony_ci int cfgid = nla_type(a); 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci if (nla_len(a) < 4) 201462306a36Sopenharmony_ci return -EINVAL; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 201762306a36Sopenharmony_ci return -EINVAL; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci return 0; 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, 202562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 202662306a36Sopenharmony_ci{ 202762306a36Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 202862306a36Sopenharmony_ci struct nlattr *a, *tb[IFLA_INET_MAX+1]; 202962306a36Sopenharmony_ci int rem; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci if (!in_dev) 203262306a36Sopenharmony_ci return -EAFNOSUPPORT; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 203562306a36Sopenharmony_ci return -EINVAL; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (tb[IFLA_INET_CONF]) { 203862306a36Sopenharmony_ci nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 203962306a36Sopenharmony_ci ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci return 0; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic int inet_netconf_msgsize_devconf(int type) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 204862306a36Sopenharmony_ci + nla_total_size(4); /* NETCONFA_IFINDEX */ 204962306a36Sopenharmony_ci bool all = false; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (type == NETCONFA_ALL) 205262306a36Sopenharmony_ci all = true; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci if (all || type == NETCONFA_FORWARDING) 205562306a36Sopenharmony_ci size += nla_total_size(4); 205662306a36Sopenharmony_ci if (all || type == NETCONFA_RP_FILTER) 205762306a36Sopenharmony_ci size += nla_total_size(4); 205862306a36Sopenharmony_ci if (all || type == NETCONFA_MC_FORWARDING) 205962306a36Sopenharmony_ci size += nla_total_size(4); 206062306a36Sopenharmony_ci if (all || type == NETCONFA_BC_FORWARDING) 206162306a36Sopenharmony_ci size += nla_total_size(4); 206262306a36Sopenharmony_ci if (all || type == NETCONFA_PROXY_NEIGH) 206362306a36Sopenharmony_ci size += nla_total_size(4); 206462306a36Sopenharmony_ci if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 206562306a36Sopenharmony_ci size += nla_total_size(4); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci return size; 206862306a36Sopenharmony_ci} 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_cistatic int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 207162306a36Sopenharmony_ci struct ipv4_devconf *devconf, u32 portid, 207262306a36Sopenharmony_ci u32 seq, int event, unsigned int flags, 207362306a36Sopenharmony_ci int type) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct nlmsghdr *nlh; 207662306a36Sopenharmony_ci struct netconfmsg *ncm; 207762306a36Sopenharmony_ci bool all = false; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 208062306a36Sopenharmony_ci flags); 208162306a36Sopenharmony_ci if (!nlh) 208262306a36Sopenharmony_ci return -EMSGSIZE; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (type == NETCONFA_ALL) 208562306a36Sopenharmony_ci all = true; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci ncm = nlmsg_data(nlh); 208862306a36Sopenharmony_ci ncm->ncm_family = AF_INET; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 209162306a36Sopenharmony_ci goto nla_put_failure; 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci if (!devconf) 209462306a36Sopenharmony_ci goto out; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci if ((all || type == NETCONFA_FORWARDING) && 209762306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_FORWARDING, 209862306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 209962306a36Sopenharmony_ci goto nla_put_failure; 210062306a36Sopenharmony_ci if ((all || type == NETCONFA_RP_FILTER) && 210162306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_RP_FILTER, 210262306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 210362306a36Sopenharmony_ci goto nla_put_failure; 210462306a36Sopenharmony_ci if ((all || type == NETCONFA_MC_FORWARDING) && 210562306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_MC_FORWARDING, 210662306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 210762306a36Sopenharmony_ci goto nla_put_failure; 210862306a36Sopenharmony_ci if ((all || type == NETCONFA_BC_FORWARDING) && 210962306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_BC_FORWARDING, 211062306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0) 211162306a36Sopenharmony_ci goto nla_put_failure; 211262306a36Sopenharmony_ci if ((all || type == NETCONFA_PROXY_NEIGH) && 211362306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 211462306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 211562306a36Sopenharmony_ci goto nla_put_failure; 211662306a36Sopenharmony_ci if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 211762306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 211862306a36Sopenharmony_ci IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 211962306a36Sopenharmony_ci goto nla_put_failure; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ciout: 212262306a36Sopenharmony_ci nlmsg_end(skb, nlh); 212362306a36Sopenharmony_ci return 0; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_cinla_put_failure: 212662306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 212762306a36Sopenharmony_ci return -EMSGSIZE; 212862306a36Sopenharmony_ci} 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_civoid inet_netconf_notify_devconf(struct net *net, int event, int type, 213162306a36Sopenharmony_ci int ifindex, struct ipv4_devconf *devconf) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci struct sk_buff *skb; 213462306a36Sopenharmony_ci int err = -ENOBUFS; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 213762306a36Sopenharmony_ci if (!skb) 213862306a36Sopenharmony_ci goto errout; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 214162306a36Sopenharmony_ci event, 0, type); 214262306a36Sopenharmony_ci if (err < 0) { 214362306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 214462306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 214562306a36Sopenharmony_ci kfree_skb(skb); 214662306a36Sopenharmony_ci goto errout; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 214962306a36Sopenharmony_ci return; 215062306a36Sopenharmony_cierrout: 215162306a36Sopenharmony_ci if (err < 0) 215262306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 215362306a36Sopenharmony_ci} 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_cistatic const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 215662306a36Sopenharmony_ci [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 215762306a36Sopenharmony_ci [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 215862306a36Sopenharmony_ci [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 215962306a36Sopenharmony_ci [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 216062306a36Sopenharmony_ci [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 216162306a36Sopenharmony_ci}; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic int inet_netconf_valid_get_req(struct sk_buff *skb, 216462306a36Sopenharmony_ci const struct nlmsghdr *nlh, 216562306a36Sopenharmony_ci struct nlattr **tb, 216662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 216762306a36Sopenharmony_ci{ 216862306a36Sopenharmony_ci int i, err; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 217162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request"); 217262306a36Sopenharmony_ci return -EINVAL; 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 217662306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 217762306a36Sopenharmony_ci tb, NETCONFA_MAX, 217862306a36Sopenharmony_ci devconf_ipv4_policy, extack); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 218162306a36Sopenharmony_ci tb, NETCONFA_MAX, 218262306a36Sopenharmony_ci devconf_ipv4_policy, extack); 218362306a36Sopenharmony_ci if (err) 218462306a36Sopenharmony_ci return err; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci for (i = 0; i <= NETCONFA_MAX; i++) { 218762306a36Sopenharmony_ci if (!tb[i]) 218862306a36Sopenharmony_ci continue; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci switch (i) { 219162306a36Sopenharmony_ci case NETCONFA_IFINDEX: 219262306a36Sopenharmony_ci break; 219362306a36Sopenharmony_ci default: 219462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request"); 219562306a36Sopenharmony_ci return -EINVAL; 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci return 0; 220062306a36Sopenharmony_ci} 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_cistatic int inet_netconf_get_devconf(struct sk_buff *in_skb, 220362306a36Sopenharmony_ci struct nlmsghdr *nlh, 220462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 220562306a36Sopenharmony_ci{ 220662306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 220762306a36Sopenharmony_ci struct nlattr *tb[NETCONFA_MAX+1]; 220862306a36Sopenharmony_ci struct sk_buff *skb; 220962306a36Sopenharmony_ci struct ipv4_devconf *devconf; 221062306a36Sopenharmony_ci struct in_device *in_dev; 221162306a36Sopenharmony_ci struct net_device *dev; 221262306a36Sopenharmony_ci int ifindex; 221362306a36Sopenharmony_ci int err; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack); 221662306a36Sopenharmony_ci if (err) 221762306a36Sopenharmony_ci goto errout; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci err = -EINVAL; 222062306a36Sopenharmony_ci if (!tb[NETCONFA_IFINDEX]) 222162306a36Sopenharmony_ci goto errout; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 222462306a36Sopenharmony_ci switch (ifindex) { 222562306a36Sopenharmony_ci case NETCONFA_IFINDEX_ALL: 222662306a36Sopenharmony_ci devconf = net->ipv4.devconf_all; 222762306a36Sopenharmony_ci break; 222862306a36Sopenharmony_ci case NETCONFA_IFINDEX_DEFAULT: 222962306a36Sopenharmony_ci devconf = net->ipv4.devconf_dflt; 223062306a36Sopenharmony_ci break; 223162306a36Sopenharmony_ci default: 223262306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 223362306a36Sopenharmony_ci if (!dev) 223462306a36Sopenharmony_ci goto errout; 223562306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 223662306a36Sopenharmony_ci if (!in_dev) 223762306a36Sopenharmony_ci goto errout; 223862306a36Sopenharmony_ci devconf = &in_dev->cnf; 223962306a36Sopenharmony_ci break; 224062306a36Sopenharmony_ci } 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci err = -ENOBUFS; 224362306a36Sopenharmony_ci skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 224462306a36Sopenharmony_ci if (!skb) 224562306a36Sopenharmony_ci goto errout; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci err = inet_netconf_fill_devconf(skb, ifindex, devconf, 224862306a36Sopenharmony_ci NETLINK_CB(in_skb).portid, 224962306a36Sopenharmony_ci nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 225062306a36Sopenharmony_ci NETCONFA_ALL); 225162306a36Sopenharmony_ci if (err < 0) { 225262306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 225362306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 225462306a36Sopenharmony_ci kfree_skb(skb); 225562306a36Sopenharmony_ci goto errout; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 225862306a36Sopenharmony_cierrout: 225962306a36Sopenharmony_ci return err; 226062306a36Sopenharmony_ci} 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_cistatic int inet_netconf_dump_devconf(struct sk_buff *skb, 226362306a36Sopenharmony_ci struct netlink_callback *cb) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 226662306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 226762306a36Sopenharmony_ci int h, s_h; 226862306a36Sopenharmony_ci int idx, s_idx; 226962306a36Sopenharmony_ci struct net_device *dev; 227062306a36Sopenharmony_ci struct in_device *in_dev; 227162306a36Sopenharmony_ci struct hlist_head *head; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci if (cb->strict_check) { 227462306a36Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 227562306a36Sopenharmony_ci struct netconfmsg *ncm; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 227862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request"); 227962306a36Sopenharmony_ci return -EINVAL; 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 228362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request"); 228462306a36Sopenharmony_ci return -EINVAL; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci s_h = cb->args[0]; 228962306a36Sopenharmony_ci s_idx = idx = cb->args[1]; 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 229262306a36Sopenharmony_ci idx = 0; 229362306a36Sopenharmony_ci head = &net->dev_index_head[h]; 229462306a36Sopenharmony_ci rcu_read_lock(); 229562306a36Sopenharmony_ci cb->seq = inet_base_seq(net); 229662306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 229762306a36Sopenharmony_ci if (idx < s_idx) 229862306a36Sopenharmony_ci goto cont; 229962306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 230062306a36Sopenharmony_ci if (!in_dev) 230162306a36Sopenharmony_ci goto cont; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci if (inet_netconf_fill_devconf(skb, dev->ifindex, 230462306a36Sopenharmony_ci &in_dev->cnf, 230562306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 230662306a36Sopenharmony_ci nlh->nlmsg_seq, 230762306a36Sopenharmony_ci RTM_NEWNETCONF, 230862306a36Sopenharmony_ci NLM_F_MULTI, 230962306a36Sopenharmony_ci NETCONFA_ALL) < 0) { 231062306a36Sopenharmony_ci rcu_read_unlock(); 231162306a36Sopenharmony_ci goto done; 231262306a36Sopenharmony_ci } 231362306a36Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 231462306a36Sopenharmony_cicont: 231562306a36Sopenharmony_ci idx++; 231662306a36Sopenharmony_ci } 231762306a36Sopenharmony_ci rcu_read_unlock(); 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci if (h == NETDEV_HASHENTRIES) { 232062306a36Sopenharmony_ci if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 232162306a36Sopenharmony_ci net->ipv4.devconf_all, 232262306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 232362306a36Sopenharmony_ci nlh->nlmsg_seq, 232462306a36Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 232562306a36Sopenharmony_ci NETCONFA_ALL) < 0) 232662306a36Sopenharmony_ci goto done; 232762306a36Sopenharmony_ci else 232862306a36Sopenharmony_ci h++; 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci if (h == NETDEV_HASHENTRIES + 1) { 233162306a36Sopenharmony_ci if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 233262306a36Sopenharmony_ci net->ipv4.devconf_dflt, 233362306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 233462306a36Sopenharmony_ci nlh->nlmsg_seq, 233562306a36Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 233662306a36Sopenharmony_ci NETCONFA_ALL) < 0) 233762306a36Sopenharmony_ci goto done; 233862306a36Sopenharmony_ci else 233962306a36Sopenharmony_ci h++; 234062306a36Sopenharmony_ci } 234162306a36Sopenharmony_cidone: 234262306a36Sopenharmony_ci cb->args[0] = h; 234362306a36Sopenharmony_ci cb->args[1] = idx; 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci return skb->len; 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_cistatic void devinet_copy_dflt_conf(struct net *net, int i) 235162306a36Sopenharmony_ci{ 235262306a36Sopenharmony_ci struct net_device *dev; 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci rcu_read_lock(); 235562306a36Sopenharmony_ci for_each_netdev_rcu(net, dev) { 235662306a36Sopenharmony_ci struct in_device *in_dev; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 235962306a36Sopenharmony_ci if (in_dev && !test_bit(i, in_dev->cnf.state)) 236062306a36Sopenharmony_ci in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci rcu_read_unlock(); 236362306a36Sopenharmony_ci} 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci/* called with RTNL locked */ 236662306a36Sopenharmony_cistatic void inet_forward_change(struct net *net) 236762306a36Sopenharmony_ci{ 236862306a36Sopenharmony_ci struct net_device *dev; 236962306a36Sopenharmony_ci int on = IPV4_DEVCONF_ALL(net, FORWARDING); 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 237262306a36Sopenharmony_ci IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 237362306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 237462306a36Sopenharmony_ci NETCONFA_FORWARDING, 237562306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 237662306a36Sopenharmony_ci net->ipv4.devconf_all); 237762306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 237862306a36Sopenharmony_ci NETCONFA_FORWARDING, 237962306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 238062306a36Sopenharmony_ci net->ipv4.devconf_dflt); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci for_each_netdev(net, dev) { 238362306a36Sopenharmony_ci struct in_device *in_dev; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci if (on) 238662306a36Sopenharmony_ci dev_disable_lro(dev); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 238962306a36Sopenharmony_ci if (in_dev) { 239062306a36Sopenharmony_ci IN_DEV_CONF_SET(in_dev, FORWARDING, on); 239162306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 239262306a36Sopenharmony_ci NETCONFA_FORWARDING, 239362306a36Sopenharmony_ci dev->ifindex, &in_dev->cnf); 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci} 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_cistatic int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci if (cnf == net->ipv4.devconf_dflt) 240162306a36Sopenharmony_ci return NETCONFA_IFINDEX_DEFAULT; 240262306a36Sopenharmony_ci else if (cnf == net->ipv4.devconf_all) 240362306a36Sopenharmony_ci return NETCONFA_IFINDEX_ALL; 240462306a36Sopenharmony_ci else { 240562306a36Sopenharmony_ci struct in_device *idev 240662306a36Sopenharmony_ci = container_of(cnf, struct in_device, cnf); 240762306a36Sopenharmony_ci return idev->dev->ifindex; 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci} 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_cistatic int devinet_conf_proc(struct ctl_table *ctl, int write, 241262306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 241362306a36Sopenharmony_ci{ 241462306a36Sopenharmony_ci int old_value = *(int *)ctl->data; 241562306a36Sopenharmony_ci int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 241662306a36Sopenharmony_ci int new_value = *(int *)ctl->data; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci if (write) { 241962306a36Sopenharmony_ci struct ipv4_devconf *cnf = ctl->extra1; 242062306a36Sopenharmony_ci struct net *net = ctl->extra2; 242162306a36Sopenharmony_ci int i = (int *)ctl->data - cnf->data; 242262306a36Sopenharmony_ci int ifindex; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci set_bit(i, cnf->state); 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci if (cnf == net->ipv4.devconf_dflt) 242762306a36Sopenharmony_ci devinet_copy_dflt_conf(net, i); 242862306a36Sopenharmony_ci if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 242962306a36Sopenharmony_ci i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 243062306a36Sopenharmony_ci if ((new_value == 0) && (old_value != 0)) 243162306a36Sopenharmony_ci rt_cache_flush(net); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && 243462306a36Sopenharmony_ci new_value != old_value) 243562306a36Sopenharmony_ci rt_cache_flush(net); 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci if (i == IPV4_DEVCONF_RP_FILTER - 1 && 243862306a36Sopenharmony_ci new_value != old_value) { 243962306a36Sopenharmony_ci ifindex = devinet_conf_ifindex(net, cnf); 244062306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 244162306a36Sopenharmony_ci NETCONFA_RP_FILTER, 244262306a36Sopenharmony_ci ifindex, cnf); 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 244562306a36Sopenharmony_ci new_value != old_value) { 244662306a36Sopenharmony_ci ifindex = devinet_conf_ifindex(net, cnf); 244762306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 244862306a36Sopenharmony_ci NETCONFA_PROXY_NEIGH, 244962306a36Sopenharmony_ci ifindex, cnf); 245062306a36Sopenharmony_ci } 245162306a36Sopenharmony_ci if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 245262306a36Sopenharmony_ci new_value != old_value) { 245362306a36Sopenharmony_ci ifindex = devinet_conf_ifindex(net, cnf); 245462306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 245562306a36Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 245662306a36Sopenharmony_ci ifindex, cnf); 245762306a36Sopenharmony_ci } 245862306a36Sopenharmony_ci } 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_ci return ret; 246162306a36Sopenharmony_ci} 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_cistatic int devinet_sysctl_forward(struct ctl_table *ctl, int write, 246462306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 246562306a36Sopenharmony_ci{ 246662306a36Sopenharmony_ci int *valp = ctl->data; 246762306a36Sopenharmony_ci int val = *valp; 246862306a36Sopenharmony_ci loff_t pos = *ppos; 246962306a36Sopenharmony_ci struct net *net = ctl->extra2; 247062306a36Sopenharmony_ci int ret; 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN)) 247362306a36Sopenharmony_ci return -EPERM; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci if (write && *valp != val) { 247862306a36Sopenharmony_ci if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 247962306a36Sopenharmony_ci if (!rtnl_trylock()) { 248062306a36Sopenharmony_ci /* Restore the original values before restarting */ 248162306a36Sopenharmony_ci *valp = val; 248262306a36Sopenharmony_ci *ppos = pos; 248362306a36Sopenharmony_ci return restart_syscall(); 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 248662306a36Sopenharmony_ci inet_forward_change(net); 248762306a36Sopenharmony_ci } else { 248862306a36Sopenharmony_ci struct ipv4_devconf *cnf = ctl->extra1; 248962306a36Sopenharmony_ci struct in_device *idev = 249062306a36Sopenharmony_ci container_of(cnf, struct in_device, cnf); 249162306a36Sopenharmony_ci if (*valp) 249262306a36Sopenharmony_ci dev_disable_lro(idev->dev); 249362306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 249462306a36Sopenharmony_ci NETCONFA_FORWARDING, 249562306a36Sopenharmony_ci idev->dev->ifindex, 249662306a36Sopenharmony_ci cnf); 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci rtnl_unlock(); 249962306a36Sopenharmony_ci rt_cache_flush(net); 250062306a36Sopenharmony_ci } else 250162306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 250262306a36Sopenharmony_ci NETCONFA_FORWARDING, 250362306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 250462306a36Sopenharmony_ci net->ipv4.devconf_dflt); 250562306a36Sopenharmony_ci } 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci return ret; 250862306a36Sopenharmony_ci} 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_cistatic int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 251162306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 251262306a36Sopenharmony_ci{ 251362306a36Sopenharmony_ci int *valp = ctl->data; 251462306a36Sopenharmony_ci int val = *valp; 251562306a36Sopenharmony_ci int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 251662306a36Sopenharmony_ci struct net *net = ctl->extra2; 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci if (write && *valp != val) 251962306a36Sopenharmony_ci rt_cache_flush(net); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci return ret; 252262306a36Sopenharmony_ci} 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 252562306a36Sopenharmony_ci { \ 252662306a36Sopenharmony_ci .procname = name, \ 252762306a36Sopenharmony_ci .data = ipv4_devconf.data + \ 252862306a36Sopenharmony_ci IPV4_DEVCONF_ ## attr - 1, \ 252962306a36Sopenharmony_ci .maxlen = sizeof(int), \ 253062306a36Sopenharmony_ci .mode = mval, \ 253162306a36Sopenharmony_ci .proc_handler = proc, \ 253262306a36Sopenharmony_ci .extra1 = &ipv4_devconf, \ 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 253662306a36Sopenharmony_ci DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 253962306a36Sopenharmony_ci DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 254262306a36Sopenharmony_ci DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 254562306a36Sopenharmony_ci DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_cistatic struct devinet_sysctl_table { 254862306a36Sopenharmony_ci struct ctl_table_header *sysctl_header; 254962306a36Sopenharmony_ci struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 255062306a36Sopenharmony_ci} devinet_sysctl = { 255162306a36Sopenharmony_ci .devinet_vars = { 255262306a36Sopenharmony_ci DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 255362306a36Sopenharmony_ci devinet_sysctl_forward), 255462306a36Sopenharmony_ci DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 255562306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"), 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 255862306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 255962306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 256062306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 256162306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 256262306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 256362306a36Sopenharmony_ci "accept_source_route"), 256462306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 256562306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 256662306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 256762306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 256862306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 256962306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 257062306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 257162306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 257262306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 257362306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 257462306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 257562306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 257662306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER, 257762306a36Sopenharmony_ci "arp_evict_nocarrier"), 257862306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 257962306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 258062306a36Sopenharmony_ci "force_igmp_version"), 258162306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 258262306a36Sopenharmony_ci "igmpv2_unsolicited_report_interval"), 258362306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 258462306a36Sopenharmony_ci "igmpv3_unsolicited_report_interval"), 258562306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 258662306a36Sopenharmony_ci "ignore_routes_with_linkdown"), 258762306a36Sopenharmony_ci DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 258862306a36Sopenharmony_ci "drop_gratuitous_arp"), 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 259162306a36Sopenharmony_ci DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 259262306a36Sopenharmony_ci DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 259362306a36Sopenharmony_ci "promote_secondaries"), 259462306a36Sopenharmony_ci DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 259562306a36Sopenharmony_ci "route_localnet"), 259662306a36Sopenharmony_ci DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 259762306a36Sopenharmony_ci "drop_unicast_in_l2_multicast"), 259862306a36Sopenharmony_ci }, 259962306a36Sopenharmony_ci}; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic int __devinet_sysctl_register(struct net *net, char *dev_name, 260262306a36Sopenharmony_ci int ifindex, struct ipv4_devconf *p) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci int i; 260562306a36Sopenharmony_ci struct devinet_sysctl_table *t; 260662306a36Sopenharmony_ci char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT); 260962306a36Sopenharmony_ci if (!t) 261062306a36Sopenharmony_ci goto out; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 261362306a36Sopenharmony_ci t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 261462306a36Sopenharmony_ci t->devinet_vars[i].extra1 = p; 261562306a36Sopenharmony_ci t->devinet_vars[i].extra2 = net; 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 262162306a36Sopenharmony_ci if (!t->sysctl_header) 262262306a36Sopenharmony_ci goto free; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci p->sysctl = t; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 262762306a36Sopenharmony_ci ifindex, p); 262862306a36Sopenharmony_ci return 0; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_cifree: 263162306a36Sopenharmony_ci kfree(t); 263262306a36Sopenharmony_ciout: 263362306a36Sopenharmony_ci return -ENOMEM; 263462306a36Sopenharmony_ci} 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_cistatic void __devinet_sysctl_unregister(struct net *net, 263762306a36Sopenharmony_ci struct ipv4_devconf *cnf, int ifindex) 263862306a36Sopenharmony_ci{ 263962306a36Sopenharmony_ci struct devinet_sysctl_table *t = cnf->sysctl; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci if (t) { 264262306a36Sopenharmony_ci cnf->sysctl = NULL; 264362306a36Sopenharmony_ci unregister_net_sysctl_table(t->sysctl_header); 264462306a36Sopenharmony_ci kfree(t); 264562306a36Sopenharmony_ci } 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 264862306a36Sopenharmony_ci} 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_cistatic int devinet_sysctl_register(struct in_device *idev) 265162306a36Sopenharmony_ci{ 265262306a36Sopenharmony_ci int err; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci if (!sysctl_dev_name_is_allowed(idev->dev->name)) 265562306a36Sopenharmony_ci return -EINVAL; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 265862306a36Sopenharmony_ci if (err) 265962306a36Sopenharmony_ci return err; 266062306a36Sopenharmony_ci err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 266162306a36Sopenharmony_ci idev->dev->ifindex, &idev->cnf); 266262306a36Sopenharmony_ci if (err) 266362306a36Sopenharmony_ci neigh_sysctl_unregister(idev->arp_parms); 266462306a36Sopenharmony_ci return err; 266562306a36Sopenharmony_ci} 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_cistatic void devinet_sysctl_unregister(struct in_device *idev) 266862306a36Sopenharmony_ci{ 266962306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 267262306a36Sopenharmony_ci neigh_sysctl_unregister(idev->arp_parms); 267362306a36Sopenharmony_ci} 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_cistatic struct ctl_table ctl_forward_entry[] = { 267662306a36Sopenharmony_ci { 267762306a36Sopenharmony_ci .procname = "ip_forward", 267862306a36Sopenharmony_ci .data = &ipv4_devconf.data[ 267962306a36Sopenharmony_ci IPV4_DEVCONF_FORWARDING - 1], 268062306a36Sopenharmony_ci .maxlen = sizeof(int), 268162306a36Sopenharmony_ci .mode = 0644, 268262306a36Sopenharmony_ci .proc_handler = devinet_sysctl_forward, 268362306a36Sopenharmony_ci .extra1 = &ipv4_devconf, 268462306a36Sopenharmony_ci .extra2 = &init_net, 268562306a36Sopenharmony_ci }, 268662306a36Sopenharmony_ci { }, 268762306a36Sopenharmony_ci}; 268862306a36Sopenharmony_ci#endif 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_cistatic __net_init int devinet_init_net(struct net *net) 269162306a36Sopenharmony_ci{ 269262306a36Sopenharmony_ci int err; 269362306a36Sopenharmony_ci struct ipv4_devconf *all, *dflt; 269462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 269562306a36Sopenharmony_ci struct ctl_table *tbl; 269662306a36Sopenharmony_ci struct ctl_table_header *forw_hdr; 269762306a36Sopenharmony_ci#endif 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci err = -ENOMEM; 270062306a36Sopenharmony_ci all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL); 270162306a36Sopenharmony_ci if (!all) 270262306a36Sopenharmony_ci goto err_alloc_all; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 270562306a36Sopenharmony_ci if (!dflt) 270662306a36Sopenharmony_ci goto err_alloc_dflt; 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 270962306a36Sopenharmony_ci tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL); 271062306a36Sopenharmony_ci if (!tbl) 271162306a36Sopenharmony_ci goto err_alloc_ctl; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 271462306a36Sopenharmony_ci tbl[0].extra1 = all; 271562306a36Sopenharmony_ci tbl[0].extra2 = net; 271662306a36Sopenharmony_ci#endif 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci if (!net_eq(net, &init_net)) { 271962306a36Sopenharmony_ci switch (net_inherit_devconf()) { 272062306a36Sopenharmony_ci case 3: 272162306a36Sopenharmony_ci /* copy from the current netns */ 272262306a36Sopenharmony_ci memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all, 272362306a36Sopenharmony_ci sizeof(ipv4_devconf)); 272462306a36Sopenharmony_ci memcpy(dflt, 272562306a36Sopenharmony_ci current->nsproxy->net_ns->ipv4.devconf_dflt, 272662306a36Sopenharmony_ci sizeof(ipv4_devconf_dflt)); 272762306a36Sopenharmony_ci break; 272862306a36Sopenharmony_ci case 0: 272962306a36Sopenharmony_ci case 1: 273062306a36Sopenharmony_ci /* copy from init_net */ 273162306a36Sopenharmony_ci memcpy(all, init_net.ipv4.devconf_all, 273262306a36Sopenharmony_ci sizeof(ipv4_devconf)); 273362306a36Sopenharmony_ci memcpy(dflt, init_net.ipv4.devconf_dflt, 273462306a36Sopenharmony_ci sizeof(ipv4_devconf_dflt)); 273562306a36Sopenharmony_ci break; 273662306a36Sopenharmony_ci case 2: 273762306a36Sopenharmony_ci /* use compiled values */ 273862306a36Sopenharmony_ci break; 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 274362306a36Sopenharmony_ci err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 274462306a36Sopenharmony_ci if (err < 0) 274562306a36Sopenharmony_ci goto err_reg_all; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci err = __devinet_sysctl_register(net, "default", 274862306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, dflt); 274962306a36Sopenharmony_ci if (err < 0) 275062306a36Sopenharmony_ci goto err_reg_dflt; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci err = -ENOMEM; 275362306a36Sopenharmony_ci forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl, 275462306a36Sopenharmony_ci ARRAY_SIZE(ctl_forward_entry)); 275562306a36Sopenharmony_ci if (!forw_hdr) 275662306a36Sopenharmony_ci goto err_reg_ctl; 275762306a36Sopenharmony_ci net->ipv4.forw_hdr = forw_hdr; 275862306a36Sopenharmony_ci#endif 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci net->ipv4.devconf_all = all; 276162306a36Sopenharmony_ci net->ipv4.devconf_dflt = dflt; 276262306a36Sopenharmony_ci return 0; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 276562306a36Sopenharmony_cierr_reg_ctl: 276662306a36Sopenharmony_ci __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 276762306a36Sopenharmony_cierr_reg_dflt: 276862306a36Sopenharmony_ci __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 276962306a36Sopenharmony_cierr_reg_all: 277062306a36Sopenharmony_ci kfree(tbl); 277162306a36Sopenharmony_cierr_alloc_ctl: 277262306a36Sopenharmony_ci#endif 277362306a36Sopenharmony_ci kfree(dflt); 277462306a36Sopenharmony_cierr_alloc_dflt: 277562306a36Sopenharmony_ci kfree(all); 277662306a36Sopenharmony_cierr_alloc_all: 277762306a36Sopenharmony_ci return err; 277862306a36Sopenharmony_ci} 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_cistatic __net_exit void devinet_exit_net(struct net *net) 278162306a36Sopenharmony_ci{ 278262306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 278362306a36Sopenharmony_ci struct ctl_table *tbl; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci tbl = net->ipv4.forw_hdr->ctl_table_arg; 278662306a36Sopenharmony_ci unregister_net_sysctl_table(net->ipv4.forw_hdr); 278762306a36Sopenharmony_ci __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 278862306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT); 278962306a36Sopenharmony_ci __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 279062306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL); 279162306a36Sopenharmony_ci kfree(tbl); 279262306a36Sopenharmony_ci#endif 279362306a36Sopenharmony_ci kfree(net->ipv4.devconf_dflt); 279462306a36Sopenharmony_ci kfree(net->ipv4.devconf_all); 279562306a36Sopenharmony_ci} 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_cistatic __net_initdata struct pernet_operations devinet_ops = { 279862306a36Sopenharmony_ci .init = devinet_init_net, 279962306a36Sopenharmony_ci .exit = devinet_exit_net, 280062306a36Sopenharmony_ci}; 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_cistatic struct rtnl_af_ops inet_af_ops __read_mostly = { 280362306a36Sopenharmony_ci .family = AF_INET, 280462306a36Sopenharmony_ci .fill_link_af = inet_fill_link_af, 280562306a36Sopenharmony_ci .get_link_af_size = inet_get_link_af_size, 280662306a36Sopenharmony_ci .validate_link_af = inet_validate_link_af, 280762306a36Sopenharmony_ci .set_link_af = inet_set_link_af, 280862306a36Sopenharmony_ci}; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_civoid __init devinet_init(void) 281162306a36Sopenharmony_ci{ 281262306a36Sopenharmony_ci int i; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci for (i = 0; i < IN4_ADDR_HSIZE; i++) 281562306a36Sopenharmony_ci INIT_HLIST_HEAD(&inet_addr_lst[i]); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci register_pernet_subsys(&devinet_ops); 281862306a36Sopenharmony_ci register_netdevice_notifier(&ip_netdev_notifier); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci rtnl_af_register(&inet_af_ops); 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 282562306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 282662306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); 282762306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 282862306a36Sopenharmony_ci inet_netconf_dump_devconf, 0); 282962306a36Sopenharmony_ci} 2830