162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 462306a36Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 562306a36Sopenharmony_ci * interface as the means of communication with the user level. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * IPv4 Forwarding Information Base: FIB frontend. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/uaccess.h> 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/capability.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/socket.h> 2162306a36Sopenharmony_ci#include <linux/sockios.h> 2262306a36Sopenharmony_ci#include <linux/errno.h> 2362306a36Sopenharmony_ci#include <linux/in.h> 2462306a36Sopenharmony_ci#include <linux/inet.h> 2562306a36Sopenharmony_ci#include <linux/inetdevice.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/if_addr.h> 2862306a36Sopenharmony_ci#include <linux/if_arp.h> 2962306a36Sopenharmony_ci#include <linux/skbuff.h> 3062306a36Sopenharmony_ci#include <linux/cache.h> 3162306a36Sopenharmony_ci#include <linux/init.h> 3262306a36Sopenharmony_ci#include <linux/list.h> 3362306a36Sopenharmony_ci#include <linux/slab.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <net/inet_dscp.h> 3662306a36Sopenharmony_ci#include <net/ip.h> 3762306a36Sopenharmony_ci#include <net/protocol.h> 3862306a36Sopenharmony_ci#include <net/route.h> 3962306a36Sopenharmony_ci#include <net/tcp.h> 4062306a36Sopenharmony_ci#include <net/sock.h> 4162306a36Sopenharmony_ci#include <net/arp.h> 4262306a36Sopenharmony_ci#include <net/ip_fib.h> 4362306a36Sopenharmony_ci#include <net/nexthop.h> 4462306a36Sopenharmony_ci#include <net/rtnetlink.h> 4562306a36Sopenharmony_ci#include <net/xfrm.h> 4662306a36Sopenharmony_ci#include <net/l3mdev.h> 4762306a36Sopenharmony_ci#include <net/lwtunnel.h> 4862306a36Sopenharmony_ci#include <trace/events/fib.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#ifndef CONFIG_IP_MULTIPLE_TABLES 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int __net_init fib4_rules_init(struct net *net) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct fib_table *local_table, *main_table; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci main_table = fib_trie_table(RT_TABLE_MAIN, NULL); 5762306a36Sopenharmony_ci if (!main_table) 5862306a36Sopenharmony_ci return -ENOMEM; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci local_table = fib_trie_table(RT_TABLE_LOCAL, main_table); 6162306a36Sopenharmony_ci if (!local_table) 6262306a36Sopenharmony_ci goto fail; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci hlist_add_head_rcu(&local_table->tb_hlist, 6562306a36Sopenharmony_ci &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]); 6662306a36Sopenharmony_ci hlist_add_head_rcu(&main_table->tb_hlist, 6762306a36Sopenharmony_ci &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]); 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cifail: 7162306a36Sopenharmony_ci fib_free_table(main_table); 7262306a36Sopenharmony_ci return -ENOMEM; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci#else 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct fib_table *fib_new_table(struct net *net, u32 id) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct fib_table *tb, *alias = NULL; 7962306a36Sopenharmony_ci unsigned int h; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (id == 0) 8262306a36Sopenharmony_ci id = RT_TABLE_MAIN; 8362306a36Sopenharmony_ci tb = fib_get_table(net, id); 8462306a36Sopenharmony_ci if (tb) 8562306a36Sopenharmony_ci return tb; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules) 8862306a36Sopenharmony_ci alias = fib_new_table(net, RT_TABLE_MAIN); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci tb = fib_trie_table(id, alias); 9162306a36Sopenharmony_ci if (!tb) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci switch (id) { 9562306a36Sopenharmony_ci case RT_TABLE_MAIN: 9662306a36Sopenharmony_ci rcu_assign_pointer(net->ipv4.fib_main, tb); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case RT_TABLE_DEFAULT: 9962306a36Sopenharmony_ci rcu_assign_pointer(net->ipv4.fib_default, tb); 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci h = id & (FIB_TABLE_HASHSZ - 1); 10662306a36Sopenharmony_ci hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); 10762306a36Sopenharmony_ci return tb; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_new_table); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* caller must hold either rtnl or rcu read lock */ 11262306a36Sopenharmony_cistruct fib_table *fib_get_table(struct net *net, u32 id) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct fib_table *tb; 11562306a36Sopenharmony_ci struct hlist_head *head; 11662306a36Sopenharmony_ci unsigned int h; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (id == 0) 11962306a36Sopenharmony_ci id = RT_TABLE_MAIN; 12062306a36Sopenharmony_ci h = id & (FIB_TABLE_HASHSZ - 1); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci head = &net->ipv4.fib_table_hash[h]; 12362306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist, 12462306a36Sopenharmony_ci lockdep_rtnl_is_held()) { 12562306a36Sopenharmony_ci if (tb->tb_id == id) 12662306a36Sopenharmony_ci return tb; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci return NULL; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci#endif /* CONFIG_IP_MULTIPLE_TABLES */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void fib_replace_table(struct net *net, struct fib_table *old, 13362306a36Sopenharmony_ci struct fib_table *new) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci#ifdef CONFIG_IP_MULTIPLE_TABLES 13662306a36Sopenharmony_ci switch (new->tb_id) { 13762306a36Sopenharmony_ci case RT_TABLE_MAIN: 13862306a36Sopenharmony_ci rcu_assign_pointer(net->ipv4.fib_main, new); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case RT_TABLE_DEFAULT: 14162306a36Sopenharmony_ci rcu_assign_pointer(net->ipv4.fib_default, new); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci default: 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#endif 14862306a36Sopenharmony_ci /* replace the old table in the hlist */ 14962306a36Sopenharmony_ci hlist_replace_rcu(&old->tb_hlist, &new->tb_hlist); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciint fib_unmerge(struct net *net) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct fib_table *old, *new, *main_table; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* attempt to fetch local table if it has been allocated */ 15762306a36Sopenharmony_ci old = fib_get_table(net, RT_TABLE_LOCAL); 15862306a36Sopenharmony_ci if (!old) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci new = fib_trie_unmerge(old); 16262306a36Sopenharmony_ci if (!new) 16362306a36Sopenharmony_ci return -ENOMEM; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* table is already unmerged */ 16662306a36Sopenharmony_ci if (new == old) 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* replace merged table with clean table */ 17062306a36Sopenharmony_ci fib_replace_table(net, old, new); 17162306a36Sopenharmony_ci fib_free_table(old); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* attempt to fetch main table if it has been allocated */ 17462306a36Sopenharmony_ci main_table = fib_get_table(net, RT_TABLE_MAIN); 17562306a36Sopenharmony_ci if (!main_table) 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* flush local entries from main table */ 17962306a36Sopenharmony_ci fib_table_flush_external(main_table); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_civoid fib_flush(struct net *net) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int flushed = 0; 18762306a36Sopenharmony_ci unsigned int h; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci for (h = 0; h < FIB_TABLE_HASHSZ; h++) { 19062306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[h]; 19162306a36Sopenharmony_ci struct hlist_node *tmp; 19262306a36Sopenharmony_ci struct fib_table *tb; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) 19562306a36Sopenharmony_ci flushed += fib_table_flush(net, tb, false); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (flushed) 19962306a36Sopenharmony_ci rt_cache_flush(net); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Find address type as if only "dev" was present in the system. If 20462306a36Sopenharmony_ci * on_dev is NULL then all interfaces are taken into consideration. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic inline unsigned int __inet_dev_addr_type(struct net *net, 20762306a36Sopenharmony_ci const struct net_device *dev, 20862306a36Sopenharmony_ci __be32 addr, u32 tb_id) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct flowi4 fl4 = { .daddr = addr }; 21162306a36Sopenharmony_ci struct fib_result res; 21262306a36Sopenharmony_ci unsigned int ret = RTN_BROADCAST; 21362306a36Sopenharmony_ci struct fib_table *table; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) 21662306a36Sopenharmony_ci return RTN_BROADCAST; 21762306a36Sopenharmony_ci if (ipv4_is_multicast(addr)) 21862306a36Sopenharmony_ci return RTN_MULTICAST; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci rcu_read_lock(); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci table = fib_get_table(net, tb_id); 22362306a36Sopenharmony_ci if (table) { 22462306a36Sopenharmony_ci ret = RTN_UNICAST; 22562306a36Sopenharmony_ci if (!fib_table_lookup(table, &fl4, &res, FIB_LOOKUP_NOREF)) { 22662306a36Sopenharmony_ci struct fib_nh_common *nhc = fib_info_nhc(res.fi, 0); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!dev || dev == nhc->nhc_dev) 22962306a36Sopenharmony_ci ret = res.type; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci rcu_read_unlock(); 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ciunsigned int inet_addr_type_table(struct net *net, __be32 addr, u32 tb_id) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci return __inet_dev_addr_type(net, NULL, addr, tb_id); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ciEXPORT_SYMBOL(inet_addr_type_table); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciunsigned int inet_addr_type(struct net *net, __be32 addr) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return __inet_dev_addr_type(net, NULL, addr, RT_TABLE_LOCAL); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL(inet_addr_type); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciunsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, 25062306a36Sopenharmony_ci __be32 addr) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return __inet_dev_addr_type(net, dev, addr, rt_table); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ciEXPORT_SYMBOL(inet_dev_addr_type); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* inet_addr_type with dev == NULL but using the table from a dev 25962306a36Sopenharmony_ci * if one is associated 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ciunsigned int inet_addr_type_dev_table(struct net *net, 26262306a36Sopenharmony_ci const struct net_device *dev, 26362306a36Sopenharmony_ci __be32 addr) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci u32 rt_table = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return __inet_dev_addr_type(net, NULL, addr, rt_table); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ciEXPORT_SYMBOL(inet_addr_type_dev_table); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci__be32 fib_compute_spec_dst(struct sk_buff *skb) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct net_device *dev = skb->dev; 27462306a36Sopenharmony_ci struct in_device *in_dev; 27562306a36Sopenharmony_ci struct fib_result res; 27662306a36Sopenharmony_ci struct rtable *rt; 27762306a36Sopenharmony_ci struct net *net; 27862306a36Sopenharmony_ci int scope; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci rt = skb_rtable(skb); 28162306a36Sopenharmony_ci if ((rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST | RTCF_LOCAL)) == 28262306a36Sopenharmony_ci RTCF_LOCAL) 28362306a36Sopenharmony_ci return ip_hdr(skb)->daddr; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci net = dev_net(dev); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci scope = RT_SCOPE_UNIVERSE; 29062306a36Sopenharmony_ci if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) { 29162306a36Sopenharmony_ci bool vmark = in_dev && IN_DEV_SRC_VMARK(in_dev); 29262306a36Sopenharmony_ci struct flowi4 fl4 = { 29362306a36Sopenharmony_ci .flowi4_iif = LOOPBACK_IFINDEX, 29462306a36Sopenharmony_ci .flowi4_l3mdev = l3mdev_master_ifindex_rcu(dev), 29562306a36Sopenharmony_ci .daddr = ip_hdr(skb)->saddr, 29662306a36Sopenharmony_ci .flowi4_tos = ip_hdr(skb)->tos & IPTOS_RT_MASK, 29762306a36Sopenharmony_ci .flowi4_scope = scope, 29862306a36Sopenharmony_ci .flowi4_mark = vmark ? skb->mark : 0, 29962306a36Sopenharmony_ci }; 30062306a36Sopenharmony_ci if (!fib_lookup(net, &fl4, &res, 0)) 30162306a36Sopenharmony_ci return fib_result_prefsrc(net, &res); 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci scope = RT_SCOPE_LINK; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cibool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci bool dev_match = false; 31262306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 31362306a36Sopenharmony_ci if (unlikely(fi->nh)) { 31462306a36Sopenharmony_ci dev_match = nexthop_uses_dev(fi->nh, dev); 31562306a36Sopenharmony_ci } else { 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci for (ret = 0; ret < fib_info_num_path(fi); ret++) { 31962306a36Sopenharmony_ci const struct fib_nh_common *nhc = fib_info_nhc(fi, ret); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (nhc_l3mdev_matches_dev(nhc, dev)) { 32262306a36Sopenharmony_ci dev_match = true; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci#else 32862306a36Sopenharmony_ci if (fib_info_nhc(fi, 0)->nhc_dev == dev) 32962306a36Sopenharmony_ci dev_match = true; 33062306a36Sopenharmony_ci#endif 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return dev_match; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fib_info_nh_uses_dev); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci/* Given (packet source, input interface) and optional (dst, oif, tos): 33762306a36Sopenharmony_ci * - (main) check, that source is valid i.e. not broadcast or our local 33862306a36Sopenharmony_ci * address. 33962306a36Sopenharmony_ci * - figure out what "logical" interface this packet arrived 34062306a36Sopenharmony_ci * and calculate "specific destination" address. 34162306a36Sopenharmony_ci * - check, that packet arrived from expected physical interface. 34262306a36Sopenharmony_ci * called with rcu_read_lock() 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_cistatic int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, 34562306a36Sopenharmony_ci u8 tos, int oif, struct net_device *dev, 34662306a36Sopenharmony_ci int rpf, struct in_device *idev, u32 *itag) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct net *net = dev_net(dev); 34962306a36Sopenharmony_ci struct flow_keys flkeys; 35062306a36Sopenharmony_ci int ret, no_addr; 35162306a36Sopenharmony_ci struct fib_result res; 35262306a36Sopenharmony_ci struct flowi4 fl4; 35362306a36Sopenharmony_ci bool dev_match; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci fl4.flowi4_oif = 0; 35662306a36Sopenharmony_ci fl4.flowi4_l3mdev = l3mdev_master_ifindex_rcu(dev); 35762306a36Sopenharmony_ci fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX; 35862306a36Sopenharmony_ci fl4.daddr = src; 35962306a36Sopenharmony_ci fl4.saddr = dst; 36062306a36Sopenharmony_ci fl4.flowi4_tos = tos; 36162306a36Sopenharmony_ci fl4.flowi4_scope = RT_SCOPE_UNIVERSE; 36262306a36Sopenharmony_ci fl4.flowi4_tun_key.tun_id = 0; 36362306a36Sopenharmony_ci fl4.flowi4_flags = 0; 36462306a36Sopenharmony_ci fl4.flowi4_uid = sock_net_uid(net, NULL); 36562306a36Sopenharmony_ci fl4.flowi4_multipath_hash = 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci no_addr = idev->ifa_list == NULL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; 37062306a36Sopenharmony_ci if (!fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys)) { 37162306a36Sopenharmony_ci fl4.flowi4_proto = 0; 37262306a36Sopenharmony_ci fl4.fl4_sport = 0; 37362306a36Sopenharmony_ci fl4.fl4_dport = 0; 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci swap(fl4.fl4_sport, fl4.fl4_dport); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (fib_lookup(net, &fl4, &res, 0)) 37962306a36Sopenharmony_ci goto last_resort; 38062306a36Sopenharmony_ci if (res.type != RTN_UNICAST && 38162306a36Sopenharmony_ci (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev))) 38262306a36Sopenharmony_ci goto e_inval; 38362306a36Sopenharmony_ci fib_combine_itag(itag, &res); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci dev_match = fib_info_nh_uses_dev(res.fi, dev); 38662306a36Sopenharmony_ci /* This is not common, loopback packets retain skb_dst so normally they 38762306a36Sopenharmony_ci * would not even hit this slow path. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci dev_match = dev_match || (res.type == RTN_LOCAL && 39062306a36Sopenharmony_ci dev == net->loopback_dev); 39162306a36Sopenharmony_ci if (dev_match) { 39262306a36Sopenharmony_ci ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST; 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci if (no_addr) 39662306a36Sopenharmony_ci goto last_resort; 39762306a36Sopenharmony_ci if (rpf == 1) 39862306a36Sopenharmony_ci goto e_rpf; 39962306a36Sopenharmony_ci fl4.flowi4_oif = dev->ifindex; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = 0; 40262306a36Sopenharmony_ci if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) { 40362306a36Sopenharmony_ci if (res.type == RTN_UNICAST) 40462306a36Sopenharmony_ci ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci return ret; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cilast_resort: 40962306a36Sopenharmony_ci if (rpf) 41062306a36Sopenharmony_ci goto e_rpf; 41162306a36Sopenharmony_ci *itag = 0; 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cie_inval: 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_cie_rpf: 41762306a36Sopenharmony_ci return -EXDEV; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* Ignore rp_filter for packets protected by IPsec. */ 42162306a36Sopenharmony_ciint fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, 42262306a36Sopenharmony_ci u8 tos, int oif, struct net_device *dev, 42362306a36Sopenharmony_ci struct in_device *idev, u32 *itag) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); 42662306a36Sopenharmony_ci struct net *net = dev_net(dev); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!r && !fib_num_tclassid_users(net) && 42962306a36Sopenharmony_ci (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) { 43062306a36Sopenharmony_ci if (IN_DEV_ACCEPT_LOCAL(idev)) 43162306a36Sopenharmony_ci goto ok; 43262306a36Sopenharmony_ci /* with custom local routes in place, checking local addresses 43362306a36Sopenharmony_ci * only will be too optimistic, with custom rules, checking 43462306a36Sopenharmony_ci * local addresses only can be too strict, e.g. due to vrf 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (net->ipv4.fib_has_custom_local_routes || 43762306a36Sopenharmony_ci fib4_has_custom_rules(net)) 43862306a36Sopenharmony_ci goto full_check; 43962306a36Sopenharmony_ci /* Within the same container, it is regarded as a martian source, 44062306a36Sopenharmony_ci * and the same host but different containers are not. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci if (inet_lookup_ifaddr_rcu(net, src)) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ciok: 44662306a36Sopenharmony_ci *itag = 0; 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cifull_check: 45162306a36Sopenharmony_ci return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic inline __be32 sk_extract_addr(struct sockaddr *addr) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci return ((struct sockaddr_in *) addr)->sin_addr.s_addr; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic int put_rtax(struct nlattr *mx, int len, int type, u32 value) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct nlattr *nla; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci nla = (struct nlattr *) ((char *) mx + len); 46462306a36Sopenharmony_ci nla->nla_type = type; 46562306a36Sopenharmony_ci nla->nla_len = nla_attr_size(4); 46662306a36Sopenharmony_ci *(u32 *) nla_data(nla) = value; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return len + nla_total_size(4); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt, 47262306a36Sopenharmony_ci struct fib_config *cfg) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci __be32 addr; 47562306a36Sopenharmony_ci int plen; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci memset(cfg, 0, sizeof(*cfg)); 47862306a36Sopenharmony_ci cfg->fc_nlinfo.nl_net = net; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (rt->rt_dst.sa_family != AF_INET) 48162306a36Sopenharmony_ci return -EAFNOSUPPORT; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * Check mask for validity: 48562306a36Sopenharmony_ci * a) it must be contiguous. 48662306a36Sopenharmony_ci * b) destination must have all host bits clear. 48762306a36Sopenharmony_ci * c) if application forgot to set correct family (AF_INET), 48862306a36Sopenharmony_ci * reject request unless it is absolutely clear i.e. 48962306a36Sopenharmony_ci * both family and mask are zero. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci plen = 32; 49262306a36Sopenharmony_ci addr = sk_extract_addr(&rt->rt_dst); 49362306a36Sopenharmony_ci if (!(rt->rt_flags & RTF_HOST)) { 49462306a36Sopenharmony_ci __be32 mask = sk_extract_addr(&rt->rt_genmask); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (rt->rt_genmask.sa_family != AF_INET) { 49762306a36Sopenharmony_ci if (mask || rt->rt_genmask.sa_family) 49862306a36Sopenharmony_ci return -EAFNOSUPPORT; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (bad_mask(mask, addr)) 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci plen = inet_mask_len(mask); 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci cfg->fc_dst_len = plen; 50862306a36Sopenharmony_ci cfg->fc_dst = addr; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (cmd != SIOCDELRT) { 51162306a36Sopenharmony_ci cfg->fc_nlflags = NLM_F_CREATE; 51262306a36Sopenharmony_ci cfg->fc_protocol = RTPROT_BOOT; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (rt->rt_metric) 51662306a36Sopenharmony_ci cfg->fc_priority = rt->rt_metric - 1; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (rt->rt_flags & RTF_REJECT) { 51962306a36Sopenharmony_ci cfg->fc_scope = RT_SCOPE_HOST; 52062306a36Sopenharmony_ci cfg->fc_type = RTN_UNREACHABLE; 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci cfg->fc_scope = RT_SCOPE_NOWHERE; 52562306a36Sopenharmony_ci cfg->fc_type = RTN_UNICAST; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (rt->rt_dev) { 52862306a36Sopenharmony_ci char *colon; 52962306a36Sopenharmony_ci struct net_device *dev; 53062306a36Sopenharmony_ci char devname[IFNAMSIZ]; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (copy_from_user(devname, rt->rt_dev, IFNAMSIZ-1)) 53362306a36Sopenharmony_ci return -EFAULT; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci devname[IFNAMSIZ-1] = 0; 53662306a36Sopenharmony_ci colon = strchr(devname, ':'); 53762306a36Sopenharmony_ci if (colon) 53862306a36Sopenharmony_ci *colon = 0; 53962306a36Sopenharmony_ci dev = __dev_get_by_name(net, devname); 54062306a36Sopenharmony_ci if (!dev) 54162306a36Sopenharmony_ci return -ENODEV; 54262306a36Sopenharmony_ci cfg->fc_oif = dev->ifindex; 54362306a36Sopenharmony_ci cfg->fc_table = l3mdev_fib_table(dev); 54462306a36Sopenharmony_ci if (colon) { 54562306a36Sopenharmony_ci const struct in_ifaddr *ifa; 54662306a36Sopenharmony_ci struct in_device *in_dev; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 54962306a36Sopenharmony_ci if (!in_dev) 55062306a36Sopenharmony_ci return -ENODEV; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci *colon = ':'; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci rcu_read_lock(); 55562306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa, in_dev) { 55662306a36Sopenharmony_ci if (strcmp(ifa->ifa_label, devname) == 0) 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci rcu_read_unlock(); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!ifa) 56262306a36Sopenharmony_ci return -ENODEV; 56362306a36Sopenharmony_ci cfg->fc_prefsrc = ifa->ifa_local; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci addr = sk_extract_addr(&rt->rt_gateway); 56862306a36Sopenharmony_ci if (rt->rt_gateway.sa_family == AF_INET && addr) { 56962306a36Sopenharmony_ci unsigned int addr_type; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci cfg->fc_gw4 = addr; 57262306a36Sopenharmony_ci cfg->fc_gw_family = AF_INET; 57362306a36Sopenharmony_ci addr_type = inet_addr_type_table(net, addr, cfg->fc_table); 57462306a36Sopenharmony_ci if (rt->rt_flags & RTF_GATEWAY && 57562306a36Sopenharmony_ci addr_type == RTN_UNICAST) 57662306a36Sopenharmony_ci cfg->fc_scope = RT_SCOPE_UNIVERSE; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (!cfg->fc_table) 58062306a36Sopenharmony_ci cfg->fc_table = RT_TABLE_MAIN; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (cmd == SIOCDELRT) 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw_family) 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (cfg->fc_scope == RT_SCOPE_NOWHERE) 58962306a36Sopenharmony_ci cfg->fc_scope = RT_SCOPE_LINK; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (rt->rt_flags & (RTF_MTU | RTF_WINDOW | RTF_IRTT)) { 59262306a36Sopenharmony_ci struct nlattr *mx; 59362306a36Sopenharmony_ci int len = 0; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci mx = kcalloc(3, nla_total_size(4), GFP_KERNEL); 59662306a36Sopenharmony_ci if (!mx) 59762306a36Sopenharmony_ci return -ENOMEM; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (rt->rt_flags & RTF_MTU) 60062306a36Sopenharmony_ci len = put_rtax(mx, len, RTAX_ADVMSS, rt->rt_mtu - 40); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (rt->rt_flags & RTF_WINDOW) 60362306a36Sopenharmony_ci len = put_rtax(mx, len, RTAX_WINDOW, rt->rt_window); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (rt->rt_flags & RTF_IRTT) 60662306a36Sopenharmony_ci len = put_rtax(mx, len, RTAX_RTT, rt->rt_irtt << 3); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci cfg->fc_mx = mx; 60962306a36Sopenharmony_ci cfg->fc_mx_len = len; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * Handle IP routing ioctl calls. 61762306a36Sopenharmony_ci * These are used to manipulate the routing tables 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ciint ip_rt_ioctl(struct net *net, unsigned int cmd, struct rtentry *rt) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct fib_config cfg; 62262306a36Sopenharmony_ci int err; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci switch (cmd) { 62562306a36Sopenharmony_ci case SIOCADDRT: /* Add a route */ 62662306a36Sopenharmony_ci case SIOCDELRT: /* Delete a route */ 62762306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 62862306a36Sopenharmony_ci return -EPERM; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci rtnl_lock(); 63162306a36Sopenharmony_ci err = rtentry_to_fib_config(net, cmd, rt, &cfg); 63262306a36Sopenharmony_ci if (err == 0) { 63362306a36Sopenharmony_ci struct fib_table *tb; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (cmd == SIOCDELRT) { 63662306a36Sopenharmony_ci tb = fib_get_table(net, cfg.fc_table); 63762306a36Sopenharmony_ci if (tb) 63862306a36Sopenharmony_ci err = fib_table_delete(net, tb, &cfg, 63962306a36Sopenharmony_ci NULL); 64062306a36Sopenharmony_ci else 64162306a36Sopenharmony_ci err = -ESRCH; 64262306a36Sopenharmony_ci } else { 64362306a36Sopenharmony_ci tb = fib_new_table(net, cfg.fc_table); 64462306a36Sopenharmony_ci if (tb) 64562306a36Sopenharmony_ci err = fib_table_insert(net, tb, 64662306a36Sopenharmony_ci &cfg, NULL); 64762306a36Sopenharmony_ci else 64862306a36Sopenharmony_ci err = -ENOBUFS; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* allocated by rtentry_to_fib_config() */ 65262306a36Sopenharmony_ci kfree(cfg.fc_mx); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci rtnl_unlock(); 65562306a36Sopenharmony_ci return err; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci return -EINVAL; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ciconst struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { 66162306a36Sopenharmony_ci [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 }, 66262306a36Sopenharmony_ci [RTA_DST] = { .type = NLA_U32 }, 66362306a36Sopenharmony_ci [RTA_SRC] = { .type = NLA_U32 }, 66462306a36Sopenharmony_ci [RTA_IIF] = { .type = NLA_U32 }, 66562306a36Sopenharmony_ci [RTA_OIF] = { .type = NLA_U32 }, 66662306a36Sopenharmony_ci [RTA_GATEWAY] = { .type = NLA_U32 }, 66762306a36Sopenharmony_ci [RTA_PRIORITY] = { .type = NLA_U32 }, 66862306a36Sopenharmony_ci [RTA_PREFSRC] = { .type = NLA_U32 }, 66962306a36Sopenharmony_ci [RTA_METRICS] = { .type = NLA_NESTED }, 67062306a36Sopenharmony_ci [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 67162306a36Sopenharmony_ci [RTA_FLOW] = { .type = NLA_U32 }, 67262306a36Sopenharmony_ci [RTA_ENCAP_TYPE] = { .type = NLA_U16 }, 67362306a36Sopenharmony_ci [RTA_ENCAP] = { .type = NLA_NESTED }, 67462306a36Sopenharmony_ci [RTA_UID] = { .type = NLA_U32 }, 67562306a36Sopenharmony_ci [RTA_MARK] = { .type = NLA_U32 }, 67662306a36Sopenharmony_ci [RTA_TABLE] = { .type = NLA_U32 }, 67762306a36Sopenharmony_ci [RTA_IP_PROTO] = { .type = NLA_U8 }, 67862306a36Sopenharmony_ci [RTA_SPORT] = { .type = NLA_U16 }, 67962306a36Sopenharmony_ci [RTA_DPORT] = { .type = NLA_U16 }, 68062306a36Sopenharmony_ci [RTA_NH_ID] = { .type = NLA_U32 }, 68162306a36Sopenharmony_ci}; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ciint fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla, 68462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct rtvia *via; 68762306a36Sopenharmony_ci int alen; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) { 69062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA"); 69162306a36Sopenharmony_ci return -EINVAL; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci via = nla_data(nla); 69562306a36Sopenharmony_ci alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci switch (via->rtvia_family) { 69862306a36Sopenharmony_ci case AF_INET: 69962306a36Sopenharmony_ci if (alen != sizeof(__be32)) { 70062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA"); 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci cfg->fc_gw_family = AF_INET; 70462306a36Sopenharmony_ci cfg->fc_gw4 = *((__be32 *)via->rtvia_addr); 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case AF_INET6: 70762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 70862306a36Sopenharmony_ci if (alen != sizeof(struct in6_addr)) { 70962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA"); 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci cfg->fc_gw_family = AF_INET6; 71362306a36Sopenharmony_ci cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr); 71462306a36Sopenharmony_ci#else 71562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel"); 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci#endif 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci default: 72062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA"); 72162306a36Sopenharmony_ci return -EINVAL; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int rtm_to_fib_config(struct net *net, struct sk_buff *skb, 72862306a36Sopenharmony_ci struct nlmsghdr *nlh, struct fib_config *cfg, 72962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci bool has_gw = false, has_via = false; 73262306a36Sopenharmony_ci struct nlattr *attr; 73362306a36Sopenharmony_ci int err, remaining; 73462306a36Sopenharmony_ci struct rtmsg *rtm; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci err = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, 73762306a36Sopenharmony_ci rtm_ipv4_policy, extack); 73862306a36Sopenharmony_ci if (err < 0) 73962306a36Sopenharmony_ci goto errout; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci memset(cfg, 0, sizeof(*cfg)); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (!inet_validate_dscp(rtm->rtm_tos)) { 74662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 74762306a36Sopenharmony_ci "Invalid dsfield (tos): ECN bits must be 0"); 74862306a36Sopenharmony_ci err = -EINVAL; 74962306a36Sopenharmony_ci goto errout; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci cfg->fc_dscp = inet_dsfield_to_dscp(rtm->rtm_tos); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci cfg->fc_dst_len = rtm->rtm_dst_len; 75462306a36Sopenharmony_ci cfg->fc_table = rtm->rtm_table; 75562306a36Sopenharmony_ci cfg->fc_protocol = rtm->rtm_protocol; 75662306a36Sopenharmony_ci cfg->fc_scope = rtm->rtm_scope; 75762306a36Sopenharmony_ci cfg->fc_type = rtm->rtm_type; 75862306a36Sopenharmony_ci cfg->fc_flags = rtm->rtm_flags; 75962306a36Sopenharmony_ci cfg->fc_nlflags = nlh->nlmsg_flags; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; 76262306a36Sopenharmony_ci cfg->fc_nlinfo.nlh = nlh; 76362306a36Sopenharmony_ci cfg->fc_nlinfo.nl_net = net; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (cfg->fc_type > RTN_MAX) { 76662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid route type"); 76762306a36Sopenharmony_ci err = -EINVAL; 76862306a36Sopenharmony_ci goto errout; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), remaining) { 77262306a36Sopenharmony_ci switch (nla_type(attr)) { 77362306a36Sopenharmony_ci case RTA_DST: 77462306a36Sopenharmony_ci cfg->fc_dst = nla_get_be32(attr); 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci case RTA_OIF: 77762306a36Sopenharmony_ci cfg->fc_oif = nla_get_u32(attr); 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case RTA_GATEWAY: 78062306a36Sopenharmony_ci has_gw = true; 78162306a36Sopenharmony_ci cfg->fc_gw4 = nla_get_be32(attr); 78262306a36Sopenharmony_ci if (cfg->fc_gw4) 78362306a36Sopenharmony_ci cfg->fc_gw_family = AF_INET; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case RTA_VIA: 78662306a36Sopenharmony_ci has_via = true; 78762306a36Sopenharmony_ci err = fib_gw_from_via(cfg, attr, extack); 78862306a36Sopenharmony_ci if (err) 78962306a36Sopenharmony_ci goto errout; 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci case RTA_PRIORITY: 79262306a36Sopenharmony_ci cfg->fc_priority = nla_get_u32(attr); 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci case RTA_PREFSRC: 79562306a36Sopenharmony_ci cfg->fc_prefsrc = nla_get_be32(attr); 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case RTA_METRICS: 79862306a36Sopenharmony_ci cfg->fc_mx = nla_data(attr); 79962306a36Sopenharmony_ci cfg->fc_mx_len = nla_len(attr); 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case RTA_MULTIPATH: 80262306a36Sopenharmony_ci err = lwtunnel_valid_encap_type_attr(nla_data(attr), 80362306a36Sopenharmony_ci nla_len(attr), 80462306a36Sopenharmony_ci extack); 80562306a36Sopenharmony_ci if (err < 0) 80662306a36Sopenharmony_ci goto errout; 80762306a36Sopenharmony_ci cfg->fc_mp = nla_data(attr); 80862306a36Sopenharmony_ci cfg->fc_mp_len = nla_len(attr); 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci case RTA_FLOW: 81162306a36Sopenharmony_ci cfg->fc_flow = nla_get_u32(attr); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case RTA_TABLE: 81462306a36Sopenharmony_ci cfg->fc_table = nla_get_u32(attr); 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci case RTA_ENCAP: 81762306a36Sopenharmony_ci cfg->fc_encap = attr; 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case RTA_ENCAP_TYPE: 82062306a36Sopenharmony_ci cfg->fc_encap_type = nla_get_u16(attr); 82162306a36Sopenharmony_ci err = lwtunnel_valid_encap_type(cfg->fc_encap_type, 82262306a36Sopenharmony_ci extack); 82362306a36Sopenharmony_ci if (err < 0) 82462306a36Sopenharmony_ci goto errout; 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci case RTA_NH_ID: 82762306a36Sopenharmony_ci cfg->fc_nh_id = nla_get_u32(attr); 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (cfg->fc_nh_id) { 83362306a36Sopenharmony_ci if (cfg->fc_oif || cfg->fc_gw_family || 83462306a36Sopenharmony_ci cfg->fc_encap || cfg->fc_mp) { 83562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 83662306a36Sopenharmony_ci "Nexthop specification and nexthop id are mutually exclusive"); 83762306a36Sopenharmony_ci return -EINVAL; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (has_gw && has_via) { 84262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 84362306a36Sopenharmony_ci "Nexthop configuration can not contain both GATEWAY and VIA"); 84462306a36Sopenharmony_ci return -EINVAL; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (!cfg->fc_table) 84862306a36Sopenharmony_ci cfg->fc_table = RT_TABLE_MAIN; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_cierrout: 85262306a36Sopenharmony_ci return err; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 85662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 85962306a36Sopenharmony_ci struct fib_config cfg; 86062306a36Sopenharmony_ci struct fib_table *tb; 86162306a36Sopenharmony_ci int err; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci err = rtm_to_fib_config(net, skb, nlh, &cfg, extack); 86462306a36Sopenharmony_ci if (err < 0) 86562306a36Sopenharmony_ci goto errout; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (cfg.fc_nh_id && !nexthop_find_by_id(net, cfg.fc_nh_id)) { 86862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); 86962306a36Sopenharmony_ci err = -EINVAL; 87062306a36Sopenharmony_ci goto errout; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci tb = fib_get_table(net, cfg.fc_table); 87462306a36Sopenharmony_ci if (!tb) { 87562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "FIB table does not exist"); 87662306a36Sopenharmony_ci err = -ESRCH; 87762306a36Sopenharmony_ci goto errout; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci err = fib_table_delete(net, tb, &cfg, extack); 88162306a36Sopenharmony_cierrout: 88262306a36Sopenharmony_ci return err; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 88662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 88962306a36Sopenharmony_ci struct fib_config cfg; 89062306a36Sopenharmony_ci struct fib_table *tb; 89162306a36Sopenharmony_ci int err; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci err = rtm_to_fib_config(net, skb, nlh, &cfg, extack); 89462306a36Sopenharmony_ci if (err < 0) 89562306a36Sopenharmony_ci goto errout; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci tb = fib_new_table(net, cfg.fc_table); 89862306a36Sopenharmony_ci if (!tb) { 89962306a36Sopenharmony_ci err = -ENOBUFS; 90062306a36Sopenharmony_ci goto errout; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci err = fib_table_insert(net, tb, &cfg, extack); 90462306a36Sopenharmony_ci if (!err && cfg.fc_type == RTN_LOCAL) 90562306a36Sopenharmony_ci net->ipv4.fib_has_custom_local_routes = true; 90662306a36Sopenharmony_cierrout: 90762306a36Sopenharmony_ci return err; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ciint ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, 91162306a36Sopenharmony_ci struct fib_dump_filter *filter, 91262306a36Sopenharmony_ci struct netlink_callback *cb) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 91562306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX + 1]; 91662306a36Sopenharmony_ci struct rtmsg *rtm; 91762306a36Sopenharmony_ci int err, i; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ASSERT_RTNL(); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 92262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request"); 92362306a36Sopenharmony_ci return -EINVAL; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 92762306a36Sopenharmony_ci if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || 92862306a36Sopenharmony_ci rtm->rtm_scope) { 92962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request"); 93062306a36Sopenharmony_ci return -EINVAL; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (rtm->rtm_flags & ~(RTM_F_CLONED | RTM_F_PREFIX)) { 93462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid flags for FIB dump request"); 93562306a36Sopenharmony_ci return -EINVAL; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci if (rtm->rtm_flags & RTM_F_CLONED) 93862306a36Sopenharmony_ci filter->dump_routes = false; 93962306a36Sopenharmony_ci else 94062306a36Sopenharmony_ci filter->dump_exceptions = false; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci filter->flags = rtm->rtm_flags; 94362306a36Sopenharmony_ci filter->protocol = rtm->rtm_protocol; 94462306a36Sopenharmony_ci filter->rt_type = rtm->rtm_type; 94562306a36Sopenharmony_ci filter->table_id = rtm->rtm_table; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 94862306a36Sopenharmony_ci rtm_ipv4_policy, extack); 94962306a36Sopenharmony_ci if (err < 0) 95062306a36Sopenharmony_ci return err; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci for (i = 0; i <= RTA_MAX; ++i) { 95362306a36Sopenharmony_ci int ifindex; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (!tb[i]) 95662306a36Sopenharmony_ci continue; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci switch (i) { 95962306a36Sopenharmony_ci case RTA_TABLE: 96062306a36Sopenharmony_ci filter->table_id = nla_get_u32(tb[i]); 96162306a36Sopenharmony_ci break; 96262306a36Sopenharmony_ci case RTA_OIF: 96362306a36Sopenharmony_ci ifindex = nla_get_u32(tb[i]); 96462306a36Sopenharmony_ci filter->dev = __dev_get_by_index(net, ifindex); 96562306a36Sopenharmony_ci if (!filter->dev) 96662306a36Sopenharmony_ci return -ENODEV; 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci default: 96962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request"); 97062306a36Sopenharmony_ci return -EINVAL; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (filter->flags || filter->protocol || filter->rt_type || 97562306a36Sopenharmony_ci filter->table_id || filter->dev) { 97662306a36Sopenharmony_ci filter->filter_set = 1; 97762306a36Sopenharmony_ci cb->answer_flags = NLM_F_DUMP_FILTERED; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_valid_fib_dump_req); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct fib_dump_filter filter = { .dump_routes = true, 98762306a36Sopenharmony_ci .dump_exceptions = true }; 98862306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 98962306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 99062306a36Sopenharmony_ci unsigned int h, s_h; 99162306a36Sopenharmony_ci unsigned int e = 0, s_e; 99262306a36Sopenharmony_ci struct fib_table *tb; 99362306a36Sopenharmony_ci struct hlist_head *head; 99462306a36Sopenharmony_ci int dumped = 0, err; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (cb->strict_check) { 99762306a36Sopenharmony_ci err = ip_valid_fib_dump_req(net, nlh, &filter, cb); 99862306a36Sopenharmony_ci if (err < 0) 99962306a36Sopenharmony_ci return err; 100062306a36Sopenharmony_ci } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { 100162306a36Sopenharmony_ci struct rtmsg *rtm = nlmsg_data(nlh); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci filter.flags = rtm->rtm_flags & (RTM_F_PREFIX | RTM_F_CLONED); 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* ipv4 does not use prefix flag */ 100762306a36Sopenharmony_ci if (filter.flags & RTM_F_PREFIX) 100862306a36Sopenharmony_ci return skb->len; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (filter.table_id) { 101162306a36Sopenharmony_ci tb = fib_get_table(net, filter.table_id); 101262306a36Sopenharmony_ci if (!tb) { 101362306a36Sopenharmony_ci if (rtnl_msg_family(cb->nlh) != PF_INET) 101462306a36Sopenharmony_ci return skb->len; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist"); 101762306a36Sopenharmony_ci return -ENOENT; 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci rcu_read_lock(); 102162306a36Sopenharmony_ci err = fib_table_dump(tb, skb, cb, &filter); 102262306a36Sopenharmony_ci rcu_read_unlock(); 102362306a36Sopenharmony_ci return skb->len ? : err; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci s_h = cb->args[0]; 102762306a36Sopenharmony_ci s_e = cb->args[1]; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci rcu_read_lock(); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) { 103262306a36Sopenharmony_ci e = 0; 103362306a36Sopenharmony_ci head = &net->ipv4.fib_table_hash[h]; 103462306a36Sopenharmony_ci hlist_for_each_entry_rcu(tb, head, tb_hlist) { 103562306a36Sopenharmony_ci if (e < s_e) 103662306a36Sopenharmony_ci goto next; 103762306a36Sopenharmony_ci if (dumped) 103862306a36Sopenharmony_ci memset(&cb->args[2], 0, sizeof(cb->args) - 103962306a36Sopenharmony_ci 2 * sizeof(cb->args[0])); 104062306a36Sopenharmony_ci err = fib_table_dump(tb, skb, cb, &filter); 104162306a36Sopenharmony_ci if (err < 0) { 104262306a36Sopenharmony_ci if (likely(skb->len)) 104362306a36Sopenharmony_ci goto out; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci goto out_err; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci dumped = 1; 104862306a36Sopenharmony_cinext: 104962306a36Sopenharmony_ci e++; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ciout: 105362306a36Sopenharmony_ci err = skb->len; 105462306a36Sopenharmony_ciout_err: 105562306a36Sopenharmony_ci rcu_read_unlock(); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci cb->args[1] = e; 105862306a36Sopenharmony_ci cb->args[0] = h; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci return err; 106162306a36Sopenharmony_ci} 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci/* Prepare and feed intra-kernel routing request. 106462306a36Sopenharmony_ci * Really, it should be netlink message, but :-( netlink 106562306a36Sopenharmony_ci * can be not configured, so that we feed it directly 106662306a36Sopenharmony_ci * to fib engine. It is legal, because all events occur 106762306a36Sopenharmony_ci * only when netlink is already locked. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_cistatic void fib_magic(int cmd, int type, __be32 dst, int dst_len, 107062306a36Sopenharmony_ci struct in_ifaddr *ifa, u32 rt_priority) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct net *net = dev_net(ifa->ifa_dev->dev); 107362306a36Sopenharmony_ci u32 tb_id = l3mdev_fib_table(ifa->ifa_dev->dev); 107462306a36Sopenharmony_ci struct fib_table *tb; 107562306a36Sopenharmony_ci struct fib_config cfg = { 107662306a36Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 107762306a36Sopenharmony_ci .fc_type = type, 107862306a36Sopenharmony_ci .fc_dst = dst, 107962306a36Sopenharmony_ci .fc_dst_len = dst_len, 108062306a36Sopenharmony_ci .fc_priority = rt_priority, 108162306a36Sopenharmony_ci .fc_prefsrc = ifa->ifa_local, 108262306a36Sopenharmony_ci .fc_oif = ifa->ifa_dev->dev->ifindex, 108362306a36Sopenharmony_ci .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND, 108462306a36Sopenharmony_ci .fc_nlinfo = { 108562306a36Sopenharmony_ci .nl_net = net, 108662306a36Sopenharmony_ci }, 108762306a36Sopenharmony_ci }; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (!tb_id) 109062306a36Sopenharmony_ci tb_id = (type == RTN_UNICAST) ? RT_TABLE_MAIN : RT_TABLE_LOCAL; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci tb = fib_new_table(net, tb_id); 109362306a36Sopenharmony_ci if (!tb) 109462306a36Sopenharmony_ci return; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci cfg.fc_table = tb->tb_id; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (type != RTN_LOCAL) 109962306a36Sopenharmony_ci cfg.fc_scope = RT_SCOPE_LINK; 110062306a36Sopenharmony_ci else 110162306a36Sopenharmony_ci cfg.fc_scope = RT_SCOPE_HOST; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (cmd == RTM_NEWROUTE) 110462306a36Sopenharmony_ci fib_table_insert(net, tb, &cfg, NULL); 110562306a36Sopenharmony_ci else 110662306a36Sopenharmony_ci fib_table_delete(net, tb, &cfg, NULL); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_civoid fib_add_ifaddr(struct in_ifaddr *ifa) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct in_device *in_dev = ifa->ifa_dev; 111262306a36Sopenharmony_ci struct net_device *dev = in_dev->dev; 111362306a36Sopenharmony_ci struct in_ifaddr *prim = ifa; 111462306a36Sopenharmony_ci __be32 mask = ifa->ifa_mask; 111562306a36Sopenharmony_ci __be32 addr = ifa->ifa_local; 111662306a36Sopenharmony_ci __be32 prefix = ifa->ifa_address & mask; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_SECONDARY) { 111962306a36Sopenharmony_ci prim = inet_ifa_byprefix(in_dev, prefix, mask); 112062306a36Sopenharmony_ci if (!prim) { 112162306a36Sopenharmony_ci pr_warn("%s: bug: prim == NULL\n", __func__); 112262306a36Sopenharmony_ci return; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim, 0); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (!(dev->flags & IFF_UP)) 112962306a36Sopenharmony_ci return; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* Add broadcast address, if it is explicitly assigned. */ 113262306a36Sopenharmony_ci if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) { 113362306a36Sopenharmony_ci fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, 113462306a36Sopenharmony_ci prim, 0); 113562306a36Sopenharmony_ci arp_invalidate(dev, ifa->ifa_broadcast, false); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) && 113962306a36Sopenharmony_ci (prefix != addr || ifa->ifa_prefixlen < 32)) { 114062306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE)) 114162306a36Sopenharmony_ci fib_magic(RTM_NEWROUTE, 114262306a36Sopenharmony_ci dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, 114362306a36Sopenharmony_ci prefix, ifa->ifa_prefixlen, prim, 114462306a36Sopenharmony_ci ifa->ifa_rt_priority); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci /* Add the network broadcast address, when it makes sense */ 114762306a36Sopenharmony_ci if (ifa->ifa_prefixlen < 31) { 114862306a36Sopenharmony_ci fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask, 114962306a36Sopenharmony_ci 32, prim, 0); 115062306a36Sopenharmony_ci arp_invalidate(dev, prefix | ~mask, false); 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_civoid fib_modify_prefix_metric(struct in_ifaddr *ifa, u32 new_metric) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci __be32 prefix = ifa->ifa_address & ifa->ifa_mask; 115862306a36Sopenharmony_ci struct in_device *in_dev = ifa->ifa_dev; 115962306a36Sopenharmony_ci struct net_device *dev = in_dev->dev; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (!(dev->flags & IFF_UP) || 116262306a36Sopenharmony_ci ifa->ifa_flags & (IFA_F_SECONDARY | IFA_F_NOPREFIXROUTE) || 116362306a36Sopenharmony_ci ipv4_is_zeronet(prefix) || 116462306a36Sopenharmony_ci (prefix == ifa->ifa_local && ifa->ifa_prefixlen == 32)) 116562306a36Sopenharmony_ci return; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* add the new */ 116862306a36Sopenharmony_ci fib_magic(RTM_NEWROUTE, 116962306a36Sopenharmony_ci dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, 117062306a36Sopenharmony_ci prefix, ifa->ifa_prefixlen, ifa, new_metric); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* delete the old */ 117362306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, 117462306a36Sopenharmony_ci dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, 117562306a36Sopenharmony_ci prefix, ifa->ifa_prefixlen, ifa, ifa->ifa_rt_priority); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/* Delete primary or secondary address. 117962306a36Sopenharmony_ci * Optionally, on secondary address promotion consider the addresses 118062306a36Sopenharmony_ci * from subnet iprim as deleted, even if they are in device list. 118162306a36Sopenharmony_ci * In this case the secondary ifa can be in device list. 118262306a36Sopenharmony_ci */ 118362306a36Sopenharmony_civoid fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct in_device *in_dev = ifa->ifa_dev; 118662306a36Sopenharmony_ci struct net_device *dev = in_dev->dev; 118762306a36Sopenharmony_ci struct in_ifaddr *ifa1; 118862306a36Sopenharmony_ci struct in_ifaddr *prim = ifa, *prim1 = NULL; 118962306a36Sopenharmony_ci __be32 brd = ifa->ifa_address | ~ifa->ifa_mask; 119062306a36Sopenharmony_ci __be32 any = ifa->ifa_address & ifa->ifa_mask; 119162306a36Sopenharmony_ci#define LOCAL_OK 1 119262306a36Sopenharmony_ci#define BRD_OK 2 119362306a36Sopenharmony_ci#define BRD0_OK 4 119462306a36Sopenharmony_ci#define BRD1_OK 8 119562306a36Sopenharmony_ci unsigned int ok = 0; 119662306a36Sopenharmony_ci int subnet = 0; /* Primary network */ 119762306a36Sopenharmony_ci int gone = 1; /* Address is missing */ 119862306a36Sopenharmony_ci int same_prefsrc = 0; /* Another primary with same IP */ 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (ifa->ifa_flags & IFA_F_SECONDARY) { 120162306a36Sopenharmony_ci prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask); 120262306a36Sopenharmony_ci if (!prim) { 120362306a36Sopenharmony_ci /* if the device has been deleted, we don't perform 120462306a36Sopenharmony_ci * address promotion 120562306a36Sopenharmony_ci */ 120662306a36Sopenharmony_ci if (!in_dev->dead) 120762306a36Sopenharmony_ci pr_warn("%s: bug: prim == NULL\n", __func__); 120862306a36Sopenharmony_ci return; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci if (iprim && iprim != prim) { 121162306a36Sopenharmony_ci pr_warn("%s: bug: iprim != prim\n", __func__); 121262306a36Sopenharmony_ci return; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci } else if (!ipv4_is_zeronet(any) && 121562306a36Sopenharmony_ci (any != ifa->ifa_local || ifa->ifa_prefixlen < 32)) { 121662306a36Sopenharmony_ci if (!(ifa->ifa_flags & IFA_F_NOPREFIXROUTE)) 121762306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, 121862306a36Sopenharmony_ci dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST, 121962306a36Sopenharmony_ci any, ifa->ifa_prefixlen, prim, 0); 122062306a36Sopenharmony_ci subnet = 1; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (in_dev->dead) 122462306a36Sopenharmony_ci goto no_promotions; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* Deletion is more complicated than add. 122762306a36Sopenharmony_ci * We should take care of not to delete too much :-) 122862306a36Sopenharmony_ci * 122962306a36Sopenharmony_ci * Scan address list to be sure that addresses are really gone. 123062306a36Sopenharmony_ci */ 123162306a36Sopenharmony_ci rcu_read_lock(); 123262306a36Sopenharmony_ci in_dev_for_each_ifa_rcu(ifa1, in_dev) { 123362306a36Sopenharmony_ci if (ifa1 == ifa) { 123462306a36Sopenharmony_ci /* promotion, keep the IP */ 123562306a36Sopenharmony_ci gone = 0; 123662306a36Sopenharmony_ci continue; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci /* Ignore IFAs from our subnet */ 123962306a36Sopenharmony_ci if (iprim && ifa1->ifa_mask == iprim->ifa_mask && 124062306a36Sopenharmony_ci inet_ifa_match(ifa1->ifa_address, iprim)) 124162306a36Sopenharmony_ci continue; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* Ignore ifa1 if it uses different primary IP (prefsrc) */ 124462306a36Sopenharmony_ci if (ifa1->ifa_flags & IFA_F_SECONDARY) { 124562306a36Sopenharmony_ci /* Another address from our subnet? */ 124662306a36Sopenharmony_ci if (ifa1->ifa_mask == prim->ifa_mask && 124762306a36Sopenharmony_ci inet_ifa_match(ifa1->ifa_address, prim)) 124862306a36Sopenharmony_ci prim1 = prim; 124962306a36Sopenharmony_ci else { 125062306a36Sopenharmony_ci /* We reached the secondaries, so 125162306a36Sopenharmony_ci * same_prefsrc should be determined. 125262306a36Sopenharmony_ci */ 125362306a36Sopenharmony_ci if (!same_prefsrc) 125462306a36Sopenharmony_ci continue; 125562306a36Sopenharmony_ci /* Search new prim1 if ifa1 is not 125662306a36Sopenharmony_ci * using the current prim1 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci if (!prim1 || 125962306a36Sopenharmony_ci ifa1->ifa_mask != prim1->ifa_mask || 126062306a36Sopenharmony_ci !inet_ifa_match(ifa1->ifa_address, prim1)) 126162306a36Sopenharmony_ci prim1 = inet_ifa_byprefix(in_dev, 126262306a36Sopenharmony_ci ifa1->ifa_address, 126362306a36Sopenharmony_ci ifa1->ifa_mask); 126462306a36Sopenharmony_ci if (!prim1) 126562306a36Sopenharmony_ci continue; 126662306a36Sopenharmony_ci if (prim1->ifa_local != prim->ifa_local) 126762306a36Sopenharmony_ci continue; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci } else { 127062306a36Sopenharmony_ci if (prim->ifa_local != ifa1->ifa_local) 127162306a36Sopenharmony_ci continue; 127262306a36Sopenharmony_ci prim1 = ifa1; 127362306a36Sopenharmony_ci if (prim != prim1) 127462306a36Sopenharmony_ci same_prefsrc = 1; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci if (ifa->ifa_local == ifa1->ifa_local) 127762306a36Sopenharmony_ci ok |= LOCAL_OK; 127862306a36Sopenharmony_ci if (ifa->ifa_broadcast == ifa1->ifa_broadcast) 127962306a36Sopenharmony_ci ok |= BRD_OK; 128062306a36Sopenharmony_ci if (brd == ifa1->ifa_broadcast) 128162306a36Sopenharmony_ci ok |= BRD1_OK; 128262306a36Sopenharmony_ci if (any == ifa1->ifa_broadcast) 128362306a36Sopenharmony_ci ok |= BRD0_OK; 128462306a36Sopenharmony_ci /* primary has network specific broadcasts */ 128562306a36Sopenharmony_ci if (prim1 == ifa1 && ifa1->ifa_prefixlen < 31) { 128662306a36Sopenharmony_ci __be32 brd1 = ifa1->ifa_address | ~ifa1->ifa_mask; 128762306a36Sopenharmony_ci __be32 any1 = ifa1->ifa_address & ifa1->ifa_mask; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!ipv4_is_zeronet(any1)) { 129062306a36Sopenharmony_ci if (ifa->ifa_broadcast == brd1 || 129162306a36Sopenharmony_ci ifa->ifa_broadcast == any1) 129262306a36Sopenharmony_ci ok |= BRD_OK; 129362306a36Sopenharmony_ci if (brd == brd1 || brd == any1) 129462306a36Sopenharmony_ci ok |= BRD1_OK; 129562306a36Sopenharmony_ci if (any == brd1 || any == any1) 129662306a36Sopenharmony_ci ok |= BRD0_OK; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci rcu_read_unlock(); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cino_promotions: 130362306a36Sopenharmony_ci if (!(ok & BRD_OK)) 130462306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, 130562306a36Sopenharmony_ci prim, 0); 130662306a36Sopenharmony_ci if (subnet && ifa->ifa_prefixlen < 31) { 130762306a36Sopenharmony_ci if (!(ok & BRD1_OK)) 130862306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, 130962306a36Sopenharmony_ci prim, 0); 131062306a36Sopenharmony_ci if (!(ok & BRD0_OK)) 131162306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, 131262306a36Sopenharmony_ci prim, 0); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci if (!(ok & LOCAL_OK)) { 131562306a36Sopenharmony_ci unsigned int addr_type; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim, 0); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* Check, that this local address finally disappeared. */ 132062306a36Sopenharmony_ci addr_type = inet_addr_type_dev_table(dev_net(dev), dev, 132162306a36Sopenharmony_ci ifa->ifa_local); 132262306a36Sopenharmony_ci if (gone && addr_type != RTN_LOCAL) { 132362306a36Sopenharmony_ci /* And the last, but not the least thing. 132462306a36Sopenharmony_ci * We must flush stray FIB entries. 132562306a36Sopenharmony_ci * 132662306a36Sopenharmony_ci * First of all, we scan fib_info list searching 132762306a36Sopenharmony_ci * for stray nexthop entries, then ignite fib_flush. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci if (fib_sync_down_addr(dev, ifa->ifa_local)) 133062306a36Sopenharmony_ci fib_flush(dev_net(dev)); 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci#undef LOCAL_OK 133462306a36Sopenharmony_ci#undef BRD_OK 133562306a36Sopenharmony_ci#undef BRD0_OK 133662306a36Sopenharmony_ci#undef BRD1_OK 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic void nl_fib_lookup(struct net *net, struct fib_result_nl *frn) 134062306a36Sopenharmony_ci{ 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci struct fib_result res; 134362306a36Sopenharmony_ci struct flowi4 fl4 = { 134462306a36Sopenharmony_ci .flowi4_mark = frn->fl_mark, 134562306a36Sopenharmony_ci .daddr = frn->fl_addr, 134662306a36Sopenharmony_ci .flowi4_tos = frn->fl_tos, 134762306a36Sopenharmony_ci .flowi4_scope = frn->fl_scope, 134862306a36Sopenharmony_ci }; 134962306a36Sopenharmony_ci struct fib_table *tb; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci rcu_read_lock(); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci tb = fib_get_table(net, frn->tb_id_in); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci frn->err = -ENOENT; 135662306a36Sopenharmony_ci if (tb) { 135762306a36Sopenharmony_ci local_bh_disable(); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci frn->tb_id = tb->tb_id; 136062306a36Sopenharmony_ci frn->err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (!frn->err) { 136362306a36Sopenharmony_ci frn->prefixlen = res.prefixlen; 136462306a36Sopenharmony_ci frn->nh_sel = res.nh_sel; 136562306a36Sopenharmony_ci frn->type = res.type; 136662306a36Sopenharmony_ci frn->scope = res.scope; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci local_bh_enable(); 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci rcu_read_unlock(); 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic void nl_fib_input(struct sk_buff *skb) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct net *net; 137762306a36Sopenharmony_ci struct fib_result_nl *frn; 137862306a36Sopenharmony_ci struct nlmsghdr *nlh; 137962306a36Sopenharmony_ci u32 portid; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci net = sock_net(skb->sk); 138262306a36Sopenharmony_ci nlh = nlmsg_hdr(skb); 138362306a36Sopenharmony_ci if (skb->len < nlmsg_total_size(sizeof(*frn)) || 138462306a36Sopenharmony_ci skb->len < nlh->nlmsg_len || 138562306a36Sopenharmony_ci nlmsg_len(nlh) < sizeof(*frn)) 138662306a36Sopenharmony_ci return; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci skb = netlink_skb_clone(skb, GFP_KERNEL); 138962306a36Sopenharmony_ci if (!skb) 139062306a36Sopenharmony_ci return; 139162306a36Sopenharmony_ci nlh = nlmsg_hdr(skb); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci frn = nlmsg_data(nlh); 139462306a36Sopenharmony_ci nl_fib_lookup(net, frn); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci portid = NETLINK_CB(skb).portid; /* netlink portid */ 139762306a36Sopenharmony_ci NETLINK_CB(skb).portid = 0; /* from kernel */ 139862306a36Sopenharmony_ci NETLINK_CB(skb).dst_group = 0; /* unicast */ 139962306a36Sopenharmony_ci nlmsg_unicast(net->ipv4.fibnl, skb, portid); 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic int __net_init nl_fib_lookup_init(struct net *net) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci struct sock *sk; 140562306a36Sopenharmony_ci struct netlink_kernel_cfg cfg = { 140662306a36Sopenharmony_ci .input = nl_fib_input, 140762306a36Sopenharmony_ci }; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg); 141062306a36Sopenharmony_ci if (!sk) 141162306a36Sopenharmony_ci return -EAFNOSUPPORT; 141262306a36Sopenharmony_ci net->ipv4.fibnl = sk; 141362306a36Sopenharmony_ci return 0; 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic void nl_fib_lookup_exit(struct net *net) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci netlink_kernel_release(net->ipv4.fibnl); 141962306a36Sopenharmony_ci net->ipv4.fibnl = NULL; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic void fib_disable_ip(struct net_device *dev, unsigned long event, 142362306a36Sopenharmony_ci bool force) 142462306a36Sopenharmony_ci{ 142562306a36Sopenharmony_ci if (fib_sync_down_dev(dev, event, force)) 142662306a36Sopenharmony_ci fib_flush(dev_net(dev)); 142762306a36Sopenharmony_ci else 142862306a36Sopenharmony_ci rt_cache_flush(dev_net(dev)); 142962306a36Sopenharmony_ci arp_ifdown(dev); 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr) 143362306a36Sopenharmony_ci{ 143462306a36Sopenharmony_ci struct in_ifaddr *ifa = ptr; 143562306a36Sopenharmony_ci struct net_device *dev = ifa->ifa_dev->dev; 143662306a36Sopenharmony_ci struct net *net = dev_net(dev); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci switch (event) { 143962306a36Sopenharmony_ci case NETDEV_UP: 144062306a36Sopenharmony_ci fib_add_ifaddr(ifa); 144162306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 144262306a36Sopenharmony_ci fib_sync_up(dev, RTNH_F_DEAD); 144362306a36Sopenharmony_ci#endif 144462306a36Sopenharmony_ci atomic_inc(&net->ipv4.dev_addr_genid); 144562306a36Sopenharmony_ci rt_cache_flush(dev_net(dev)); 144662306a36Sopenharmony_ci break; 144762306a36Sopenharmony_ci case NETDEV_DOWN: 144862306a36Sopenharmony_ci fib_del_ifaddr(ifa, NULL); 144962306a36Sopenharmony_ci atomic_inc(&net->ipv4.dev_addr_genid); 145062306a36Sopenharmony_ci if (!ifa->ifa_dev->ifa_list) { 145162306a36Sopenharmony_ci /* Last address was deleted from this interface. 145262306a36Sopenharmony_ci * Disable IP. 145362306a36Sopenharmony_ci */ 145462306a36Sopenharmony_ci fib_disable_ip(dev, event, true); 145562306a36Sopenharmony_ci } else { 145662306a36Sopenharmony_ci rt_cache_flush(dev_net(dev)); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci break; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci return NOTIFY_DONE; 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) 146462306a36Sopenharmony_ci{ 146562306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 146662306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *upper_info = ptr; 146762306a36Sopenharmony_ci struct netdev_notifier_info_ext *info_ext = ptr; 146862306a36Sopenharmony_ci struct in_device *in_dev; 146962306a36Sopenharmony_ci struct net *net = dev_net(dev); 147062306a36Sopenharmony_ci struct in_ifaddr *ifa; 147162306a36Sopenharmony_ci unsigned int flags; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (event == NETDEV_UNREGISTER) { 147462306a36Sopenharmony_ci fib_disable_ip(dev, event, true); 147562306a36Sopenharmony_ci rt_flush_dev(dev); 147662306a36Sopenharmony_ci return NOTIFY_DONE; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 148062306a36Sopenharmony_ci if (!in_dev) 148162306a36Sopenharmony_ci return NOTIFY_DONE; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci switch (event) { 148462306a36Sopenharmony_ci case NETDEV_UP: 148562306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 148662306a36Sopenharmony_ci fib_add_ifaddr(ifa); 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 148962306a36Sopenharmony_ci fib_sync_up(dev, RTNH_F_DEAD); 149062306a36Sopenharmony_ci#endif 149162306a36Sopenharmony_ci atomic_inc(&net->ipv4.dev_addr_genid); 149262306a36Sopenharmony_ci rt_cache_flush(net); 149362306a36Sopenharmony_ci break; 149462306a36Sopenharmony_ci case NETDEV_DOWN: 149562306a36Sopenharmony_ci fib_disable_ip(dev, event, false); 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci case NETDEV_CHANGE: 149862306a36Sopenharmony_ci flags = dev_get_flags(dev); 149962306a36Sopenharmony_ci if (flags & (IFF_RUNNING | IFF_LOWER_UP)) 150062306a36Sopenharmony_ci fib_sync_up(dev, RTNH_F_LINKDOWN); 150162306a36Sopenharmony_ci else 150262306a36Sopenharmony_ci fib_sync_down_dev(dev, event, false); 150362306a36Sopenharmony_ci rt_cache_flush(net); 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 150662306a36Sopenharmony_ci fib_sync_mtu(dev, info_ext->ext.mtu); 150762306a36Sopenharmony_ci rt_cache_flush(net); 150862306a36Sopenharmony_ci break; 150962306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 151062306a36Sopenharmony_ci upper_info = ptr; 151162306a36Sopenharmony_ci /* flush all routes if dev is linked to or unlinked from 151262306a36Sopenharmony_ci * an L3 master device (e.g., VRF) 151362306a36Sopenharmony_ci */ 151462306a36Sopenharmony_ci if (upper_info->upper_dev && 151562306a36Sopenharmony_ci netif_is_l3_master(upper_info->upper_dev)) 151662306a36Sopenharmony_ci fib_disable_ip(dev, NETDEV_DOWN, true); 151762306a36Sopenharmony_ci break; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci return NOTIFY_DONE; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic struct notifier_block fib_inetaddr_notifier = { 152362306a36Sopenharmony_ci .notifier_call = fib_inetaddr_event, 152462306a36Sopenharmony_ci}; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic struct notifier_block fib_netdev_notifier = { 152762306a36Sopenharmony_ci .notifier_call = fib_netdev_event, 152862306a36Sopenharmony_ci}; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic int __net_init ip_fib_net_init(struct net *net) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci int err; 153362306a36Sopenharmony_ci size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci err = fib4_notifier_init(net); 153662306a36Sopenharmony_ci if (err) 153762306a36Sopenharmony_ci return err; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 154062306a36Sopenharmony_ci /* Default to 3-tuple */ 154162306a36Sopenharmony_ci net->ipv4.sysctl_fib_multipath_hash_fields = 154262306a36Sopenharmony_ci FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK; 154362306a36Sopenharmony_ci#endif 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* Avoid false sharing : Use at least a full cache line */ 154662306a36Sopenharmony_ci size = max_t(size_t, size, L1_CACHE_BYTES); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL); 154962306a36Sopenharmony_ci if (!net->ipv4.fib_table_hash) { 155062306a36Sopenharmony_ci err = -ENOMEM; 155162306a36Sopenharmony_ci goto err_table_hash_alloc; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci err = fib4_rules_init(net); 155562306a36Sopenharmony_ci if (err < 0) 155662306a36Sopenharmony_ci goto err_rules_init; 155762306a36Sopenharmony_ci return 0; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cierr_rules_init: 156062306a36Sopenharmony_ci kfree(net->ipv4.fib_table_hash); 156162306a36Sopenharmony_cierr_table_hash_alloc: 156262306a36Sopenharmony_ci fib4_notifier_exit(net); 156362306a36Sopenharmony_ci return err; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_cistatic void ip_fib_net_exit(struct net *net) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci int i; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci ASSERT_RTNL(); 157162306a36Sopenharmony_ci#ifdef CONFIG_IP_MULTIPLE_TABLES 157262306a36Sopenharmony_ci RCU_INIT_POINTER(net->ipv4.fib_main, NULL); 157362306a36Sopenharmony_ci RCU_INIT_POINTER(net->ipv4.fib_default, NULL); 157462306a36Sopenharmony_ci#endif 157562306a36Sopenharmony_ci /* Destroy the tables in reverse order to guarantee that the 157662306a36Sopenharmony_ci * local table, ID 255, is destroyed before the main table, ID 157762306a36Sopenharmony_ci * 254. This is necessary as the local table may contain 157862306a36Sopenharmony_ci * references to data contained in the main table. 157962306a36Sopenharmony_ci */ 158062306a36Sopenharmony_ci for (i = FIB_TABLE_HASHSZ - 1; i >= 0; i--) { 158162306a36Sopenharmony_ci struct hlist_head *head = &net->ipv4.fib_table_hash[i]; 158262306a36Sopenharmony_ci struct hlist_node *tmp; 158362306a36Sopenharmony_ci struct fib_table *tb; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) { 158662306a36Sopenharmony_ci hlist_del(&tb->tb_hlist); 158762306a36Sopenharmony_ci fib_table_flush(net, tb, true); 158862306a36Sopenharmony_ci fib_free_table(tb); 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci#ifdef CONFIG_IP_MULTIPLE_TABLES 159362306a36Sopenharmony_ci fib4_rules_exit(net); 159462306a36Sopenharmony_ci#endif 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci kfree(net->ipv4.fib_table_hash); 159762306a36Sopenharmony_ci fib4_notifier_exit(net); 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic int __net_init fib_net_init(struct net *net) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci int error; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 160562306a36Sopenharmony_ci atomic_set(&net->ipv4.fib_num_tclassid_users, 0); 160662306a36Sopenharmony_ci#endif 160762306a36Sopenharmony_ci error = ip_fib_net_init(net); 160862306a36Sopenharmony_ci if (error < 0) 160962306a36Sopenharmony_ci goto out; 161062306a36Sopenharmony_ci error = nl_fib_lookup_init(net); 161162306a36Sopenharmony_ci if (error < 0) 161262306a36Sopenharmony_ci goto out_nlfl; 161362306a36Sopenharmony_ci error = fib_proc_init(net); 161462306a36Sopenharmony_ci if (error < 0) 161562306a36Sopenharmony_ci goto out_proc; 161662306a36Sopenharmony_ciout: 161762306a36Sopenharmony_ci return error; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ciout_proc: 162062306a36Sopenharmony_ci nl_fib_lookup_exit(net); 162162306a36Sopenharmony_ciout_nlfl: 162262306a36Sopenharmony_ci rtnl_lock(); 162362306a36Sopenharmony_ci ip_fib_net_exit(net); 162462306a36Sopenharmony_ci rtnl_unlock(); 162562306a36Sopenharmony_ci goto out; 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic void __net_exit fib_net_exit(struct net *net) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci fib_proc_exit(net); 163162306a36Sopenharmony_ci nl_fib_lookup_exit(net); 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic void __net_exit fib_net_exit_batch(struct list_head *net_list) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct net *net; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci rtnl_lock(); 163962306a36Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) 164062306a36Sopenharmony_ci ip_fib_net_exit(net); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci rtnl_unlock(); 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_cistatic struct pernet_operations fib_net_ops = { 164662306a36Sopenharmony_ci .init = fib_net_init, 164762306a36Sopenharmony_ci .exit = fib_net_exit, 164862306a36Sopenharmony_ci .exit_batch = fib_net_exit_batch, 164962306a36Sopenharmony_ci}; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_civoid __init ip_fib_init(void) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci fib_trie_init(); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci register_pernet_subsys(&fib_net_ops); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci register_netdevice_notifier(&fib_netdev_notifier); 165862306a36Sopenharmony_ci register_inetaddr_notifier(&fib_inetaddr_notifier); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0); 166162306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0); 166262306a36Sopenharmony_ci rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, 0); 166362306a36Sopenharmony_ci} 1664