18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 48c2ecf20Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 58c2ecf20Sopenharmony_ci * interface as the means of communication with the user level. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * ROUTE - implementation of the IP router. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Authors: Ross Biro 108c2ecf20Sopenharmony_ci * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 118c2ecf20Sopenharmony_ci * Alan Cox, <gw4pts@gw4pts.ampr.org> 128c2ecf20Sopenharmony_ci * Linus Torvalds, <Linus.Torvalds@helsinki.fi> 138c2ecf20Sopenharmony_ci * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Fixes: 168c2ecf20Sopenharmony_ci * Alan Cox : Verify area fixes. 178c2ecf20Sopenharmony_ci * Alan Cox : cli() protects routing changes 188c2ecf20Sopenharmony_ci * Rui Oliveira : ICMP routing table updates 198c2ecf20Sopenharmony_ci * (rco@di.uminho.pt) Routing table insertion and update 208c2ecf20Sopenharmony_ci * Linus Torvalds : Rewrote bits to be sensible 218c2ecf20Sopenharmony_ci * Alan Cox : Added BSD route gw semantics 228c2ecf20Sopenharmony_ci * Alan Cox : Super /proc >4K 238c2ecf20Sopenharmony_ci * Alan Cox : MTU in route table 248c2ecf20Sopenharmony_ci * Alan Cox : MSS actually. Also added the window 258c2ecf20Sopenharmony_ci * clamper. 268c2ecf20Sopenharmony_ci * Sam Lantinga : Fixed route matching in rt_del() 278c2ecf20Sopenharmony_ci * Alan Cox : Routing cache support. 288c2ecf20Sopenharmony_ci * Alan Cox : Removed compatibility cruft. 298c2ecf20Sopenharmony_ci * Alan Cox : RTF_REJECT support. 308c2ecf20Sopenharmony_ci * Alan Cox : TCP irtt support. 318c2ecf20Sopenharmony_ci * Jonathan Naylor : Added Metric support. 328c2ecf20Sopenharmony_ci * Miquel van Smoorenburg : BSD API fixes. 338c2ecf20Sopenharmony_ci * Miquel van Smoorenburg : Metrics. 348c2ecf20Sopenharmony_ci * Alan Cox : Use __u32 properly 358c2ecf20Sopenharmony_ci * Alan Cox : Aligned routing errors more closely with BSD 368c2ecf20Sopenharmony_ci * our system is still very different. 378c2ecf20Sopenharmony_ci * Alan Cox : Faster /proc handling 388c2ecf20Sopenharmony_ci * Alexey Kuznetsov : Massive rework to support tree based routing, 398c2ecf20Sopenharmony_ci * routing caches and better behaviour. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Olaf Erb : irtt wasn't being copied right. 428c2ecf20Sopenharmony_ci * Bjorn Ekwall : Kerneld route support. 438c2ecf20Sopenharmony_ci * Alan Cox : Multicast fixed (I hope) 448c2ecf20Sopenharmony_ci * Pavel Krauz : Limited broadcast fixed 458c2ecf20Sopenharmony_ci * Mike McLagan : Routing by source 468c2ecf20Sopenharmony_ci * Alexey Kuznetsov : End of old history. Split to fib.c and 478c2ecf20Sopenharmony_ci * route.c and rewritten from scratch. 488c2ecf20Sopenharmony_ci * Andi Kleen : Load-limit warning messages. 498c2ecf20Sopenharmony_ci * Vitaly E. Lavrov : Transparent proxy revived after year coma. 508c2ecf20Sopenharmony_ci * Vitaly E. Lavrov : Race condition in ip_route_input_slow. 518c2ecf20Sopenharmony_ci * Tobias Ringstrom : Uninitialized res.type in ip_route_output_slow. 528c2ecf20Sopenharmony_ci * Vladimir V. Ivanov : IP rule info (flowid) is really useful. 538c2ecf20Sopenharmony_ci * Marc Boucher : routing by fwmark 548c2ecf20Sopenharmony_ci * Robert Olsson : Added rt_cache statistics 558c2ecf20Sopenharmony_ci * Arnaldo C. Melo : Convert proc stuff to seq_file 568c2ecf20Sopenharmony_ci * Eric Dumazet : hashed spinlocks and rt_check_expire() fixes. 578c2ecf20Sopenharmony_ci * Ilia Sotnikov : Ignore TOS on PMTUD and Redirect 588c2ecf20Sopenharmony_ci * Ilia Sotnikov : Removed TOS from hash calculations 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv4: " fmt 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include <linux/module.h> 648c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 658c2ecf20Sopenharmony_ci#include <linux/bitops.h> 668c2ecf20Sopenharmony_ci#include <linux/types.h> 678c2ecf20Sopenharmony_ci#include <linux/kernel.h> 688c2ecf20Sopenharmony_ci#include <linux/mm.h> 698c2ecf20Sopenharmony_ci#include <linux/memblock.h> 708c2ecf20Sopenharmony_ci#include <linux/string.h> 718c2ecf20Sopenharmony_ci#include <linux/socket.h> 728c2ecf20Sopenharmony_ci#include <linux/sockios.h> 738c2ecf20Sopenharmony_ci#include <linux/errno.h> 748c2ecf20Sopenharmony_ci#include <linux/in.h> 758c2ecf20Sopenharmony_ci#include <linux/inet.h> 768c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 778c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 788c2ecf20Sopenharmony_ci#include <linux/init.h> 798c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 808c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 818c2ecf20Sopenharmony_ci#include <linux/igmp.h> 828c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h> 838c2ecf20Sopenharmony_ci#include <linux/mroute.h> 848c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv4.h> 858c2ecf20Sopenharmony_ci#include <linux/random.h> 868c2ecf20Sopenharmony_ci#include <linux/rcupdate.h> 878c2ecf20Sopenharmony_ci#include <linux/times.h> 888c2ecf20Sopenharmony_ci#include <linux/slab.h> 898c2ecf20Sopenharmony_ci#include <linux/jhash.h> 908c2ecf20Sopenharmony_ci#include <net/dst.h> 918c2ecf20Sopenharmony_ci#include <net/dst_metadata.h> 928c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 938c2ecf20Sopenharmony_ci#include <net/protocol.h> 948c2ecf20Sopenharmony_ci#include <net/ip.h> 958c2ecf20Sopenharmony_ci#include <net/route.h> 968c2ecf20Sopenharmony_ci#include <net/inetpeer.h> 978c2ecf20Sopenharmony_ci#include <net/sock.h> 988c2ecf20Sopenharmony_ci#include <net/ip_fib.h> 998c2ecf20Sopenharmony_ci#include <net/nexthop.h> 1008c2ecf20Sopenharmony_ci#include <net/arp.h> 1018c2ecf20Sopenharmony_ci#include <net/tcp.h> 1028c2ecf20Sopenharmony_ci#include <net/icmp.h> 1038c2ecf20Sopenharmony_ci#include <net/xfrm.h> 1048c2ecf20Sopenharmony_ci#include <net/lwtunnel.h> 1058c2ecf20Sopenharmony_ci#include <net/netevent.h> 1068c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 1078c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 1088c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 1098c2ecf20Sopenharmony_ci#endif 1108c2ecf20Sopenharmony_ci#include <net/secure_seq.h> 1118c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 1128c2ecf20Sopenharmony_ci#include <net/l3mdev.h> 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#include "fib_lookup.h" 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define RT_FL_TOS(oldflp4) \ 1178c2ecf20Sopenharmony_ci ((oldflp4)->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define RT_GC_TIMEOUT (300*HZ) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int ip_rt_max_size; 1228c2ecf20Sopenharmony_cistatic int ip_rt_redirect_number __read_mostly = 9; 1238c2ecf20Sopenharmony_cistatic int ip_rt_redirect_load __read_mostly = HZ / 50; 1248c2ecf20Sopenharmony_cistatic int ip_rt_redirect_silence __read_mostly = ((HZ / 50) << (9 + 1)); 1258c2ecf20Sopenharmony_cistatic int ip_rt_error_cost __read_mostly = HZ; 1268c2ecf20Sopenharmony_cistatic int ip_rt_error_burst __read_mostly = 5 * HZ; 1278c2ecf20Sopenharmony_cistatic int ip_rt_mtu_expires __read_mostly = 10 * 60 * HZ; 1288c2ecf20Sopenharmony_cistatic u32 ip_rt_min_pmtu __read_mostly = 512 + 20 + 20; 1298c2ecf20Sopenharmony_cistatic int ip_rt_min_advmss __read_mostly = 256; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Interface to generic destination cache. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie); 1388c2ecf20Sopenharmony_cistatic unsigned int ipv4_default_advmss(const struct dst_entry *dst); 1398c2ecf20Sopenharmony_cistatic unsigned int ipv4_mtu(const struct dst_entry *dst); 1408c2ecf20Sopenharmony_cistatic void ipv4_negative_advice(struct sock *sk, 1418c2ecf20Sopenharmony_ci struct dst_entry *dst); 1428c2ecf20Sopenharmony_cistatic void ipv4_link_failure(struct sk_buff *skb); 1438c2ecf20Sopenharmony_cistatic void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 1448c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 mtu, 1458c2ecf20Sopenharmony_ci bool confirm_neigh); 1468c2ecf20Sopenharmony_cistatic void ip_do_redirect(struct dst_entry *dst, struct sock *sk, 1478c2ecf20Sopenharmony_ci struct sk_buff *skb); 1488c2ecf20Sopenharmony_cistatic void ipv4_dst_destroy(struct dst_entry *dst); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci WARN_ON(1); 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, 1578c2ecf20Sopenharmony_ci struct sk_buff *skb, 1588c2ecf20Sopenharmony_ci const void *daddr); 1598c2ecf20Sopenharmony_cistatic void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct dst_ops ipv4_dst_ops = { 1628c2ecf20Sopenharmony_ci .family = AF_INET, 1638c2ecf20Sopenharmony_ci .check = ipv4_dst_check, 1648c2ecf20Sopenharmony_ci .default_advmss = ipv4_default_advmss, 1658c2ecf20Sopenharmony_ci .mtu = ipv4_mtu, 1668c2ecf20Sopenharmony_ci .cow_metrics = ipv4_cow_metrics, 1678c2ecf20Sopenharmony_ci .destroy = ipv4_dst_destroy, 1688c2ecf20Sopenharmony_ci .negative_advice = ipv4_negative_advice, 1698c2ecf20Sopenharmony_ci .link_failure = ipv4_link_failure, 1708c2ecf20Sopenharmony_ci .update_pmtu = ip_rt_update_pmtu, 1718c2ecf20Sopenharmony_ci .redirect = ip_do_redirect, 1728c2ecf20Sopenharmony_ci .local_out = __ip_local_out, 1738c2ecf20Sopenharmony_ci .neigh_lookup = ipv4_neigh_lookup, 1748c2ecf20Sopenharmony_ci .confirm_neigh = ipv4_confirm_neigh, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define ECN_OR_COST(class) TC_PRIO_##class 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciconst __u8 ip_tos2prio[16] = { 1808c2ecf20Sopenharmony_ci TC_PRIO_BESTEFFORT, 1818c2ecf20Sopenharmony_ci ECN_OR_COST(BESTEFFORT), 1828c2ecf20Sopenharmony_ci TC_PRIO_BESTEFFORT, 1838c2ecf20Sopenharmony_ci ECN_OR_COST(BESTEFFORT), 1848c2ecf20Sopenharmony_ci TC_PRIO_BULK, 1858c2ecf20Sopenharmony_ci ECN_OR_COST(BULK), 1868c2ecf20Sopenharmony_ci TC_PRIO_BULK, 1878c2ecf20Sopenharmony_ci ECN_OR_COST(BULK), 1888c2ecf20Sopenharmony_ci TC_PRIO_INTERACTIVE, 1898c2ecf20Sopenharmony_ci ECN_OR_COST(INTERACTIVE), 1908c2ecf20Sopenharmony_ci TC_PRIO_INTERACTIVE, 1918c2ecf20Sopenharmony_ci ECN_OR_COST(INTERACTIVE), 1928c2ecf20Sopenharmony_ci TC_PRIO_INTERACTIVE_BULK, 1938c2ecf20Sopenharmony_ci ECN_OR_COST(INTERACTIVE_BULK), 1948c2ecf20Sopenharmony_ci TC_PRIO_INTERACTIVE_BULK, 1958c2ecf20Sopenharmony_ci ECN_OR_COST(INTERACTIVE_BULK) 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_tos2prio); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat); 2008c2ecf20Sopenharmony_ci#define RT_CACHE_STAT_INC(field) raw_cpu_inc(rt_cache_stat.field) 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 2038c2ecf20Sopenharmony_cistatic void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci if (*pos) 2068c2ecf20Sopenharmony_ci return NULL; 2078c2ecf20Sopenharmony_ci return SEQ_START_TOKEN; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void *rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci ++*pos; 2138c2ecf20Sopenharmony_ci return NULL; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void rt_cache_seq_stop(struct seq_file *seq, void *v) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int rt_cache_seq_show(struct seq_file *seq, void *v) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 2238c2ecf20Sopenharmony_ci seq_printf(seq, "%-127s\n", 2248c2ecf20Sopenharmony_ci "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\t" 2258c2ecf20Sopenharmony_ci "Metric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\t" 2268c2ecf20Sopenharmony_ci "HHUptod\tSpecDst"); 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct seq_operations rt_cache_seq_ops = { 2318c2ecf20Sopenharmony_ci .start = rt_cache_seq_start, 2328c2ecf20Sopenharmony_ci .next = rt_cache_seq_next, 2338c2ecf20Sopenharmony_ci .stop = rt_cache_seq_stop, 2348c2ecf20Sopenharmony_ci .show = rt_cache_seq_show, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int rt_cache_seq_open(struct inode *inode, struct file *file) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return seq_open(file, &rt_cache_seq_ops); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct proc_ops rt_cache_proc_ops = { 2438c2ecf20Sopenharmony_ci .proc_open = rt_cache_seq_open, 2448c2ecf20Sopenharmony_ci .proc_read = seq_read, 2458c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 2468c2ecf20Sopenharmony_ci .proc_release = seq_release, 2478c2ecf20Sopenharmony_ci}; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int cpu; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (*pos == 0) 2558c2ecf20Sopenharmony_ci return SEQ_START_TOKEN; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 2588c2ecf20Sopenharmony_ci if (!cpu_possible(cpu)) 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci *pos = cpu+1; 2618c2ecf20Sopenharmony_ci return &per_cpu(rt_cache_stat, cpu); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci return NULL; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int cpu; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 2718c2ecf20Sopenharmony_ci if (!cpu_possible(cpu)) 2728c2ecf20Sopenharmony_ci continue; 2738c2ecf20Sopenharmony_ci *pos = cpu+1; 2748c2ecf20Sopenharmony_ci return &per_cpu(rt_cache_stat, cpu); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci (*pos)++; 2778c2ecf20Sopenharmony_ci return NULL; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void rt_cpu_seq_stop(struct seq_file *seq, void *v) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int rt_cpu_seq_show(struct seq_file *seq, void *v) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct rt_cache_stat *st = v; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 2918c2ecf20Sopenharmony_ci seq_printf(seq, "entries in_hit in_slow_tot in_slow_mc in_no_route in_brd in_martian_dst in_martian_src out_hit out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n"); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci seq_printf(seq,"%08x %08x %08x %08x %08x %08x %08x %08x " 2968c2ecf20Sopenharmony_ci " %08x %08x %08x %08x %08x %08x %08x %08x %08x \n", 2978c2ecf20Sopenharmony_ci dst_entries_get_slow(&ipv4_dst_ops), 2988c2ecf20Sopenharmony_ci 0, /* st->in_hit */ 2998c2ecf20Sopenharmony_ci st->in_slow_tot, 3008c2ecf20Sopenharmony_ci st->in_slow_mc, 3018c2ecf20Sopenharmony_ci st->in_no_route, 3028c2ecf20Sopenharmony_ci st->in_brd, 3038c2ecf20Sopenharmony_ci st->in_martian_dst, 3048c2ecf20Sopenharmony_ci st->in_martian_src, 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci 0, /* st->out_hit */ 3078c2ecf20Sopenharmony_ci st->out_slow_tot, 3088c2ecf20Sopenharmony_ci st->out_slow_mc, 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci 0, /* st->gc_total */ 3118c2ecf20Sopenharmony_ci 0, /* st->gc_ignored */ 3128c2ecf20Sopenharmony_ci 0, /* st->gc_goal_miss */ 3138c2ecf20Sopenharmony_ci 0, /* st->gc_dst_overflow */ 3148c2ecf20Sopenharmony_ci 0, /* st->in_hlist_search */ 3158c2ecf20Sopenharmony_ci 0 /* st->out_hlist_search */ 3168c2ecf20Sopenharmony_ci ); 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic const struct seq_operations rt_cpu_seq_ops = { 3218c2ecf20Sopenharmony_ci .start = rt_cpu_seq_start, 3228c2ecf20Sopenharmony_ci .next = rt_cpu_seq_next, 3238c2ecf20Sopenharmony_ci .stop = rt_cpu_seq_stop, 3248c2ecf20Sopenharmony_ci .show = rt_cpu_seq_show, 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int rt_cpu_seq_open(struct inode *inode, struct file *file) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci return seq_open(file, &rt_cpu_seq_ops); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic const struct proc_ops rt_cpu_proc_ops = { 3348c2ecf20Sopenharmony_ci .proc_open = rt_cpu_seq_open, 3358c2ecf20Sopenharmony_ci .proc_read = seq_read, 3368c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 3378c2ecf20Sopenharmony_ci .proc_release = seq_release, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 3418c2ecf20Sopenharmony_cistatic int rt_acct_proc_show(struct seq_file *m, void *v) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct ip_rt_acct *dst, *src; 3448c2ecf20Sopenharmony_ci unsigned int i, j; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dst = kcalloc(256, sizeof(struct ip_rt_acct), GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (!dst) 3488c2ecf20Sopenharmony_ci return -ENOMEM; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 3518c2ecf20Sopenharmony_ci src = (struct ip_rt_acct *)per_cpu_ptr(ip_rt_acct, i); 3528c2ecf20Sopenharmony_ci for (j = 0; j < 256; j++) { 3538c2ecf20Sopenharmony_ci dst[j].o_bytes += src[j].o_bytes; 3548c2ecf20Sopenharmony_ci dst[j].o_packets += src[j].o_packets; 3558c2ecf20Sopenharmony_ci dst[j].i_bytes += src[j].i_bytes; 3568c2ecf20Sopenharmony_ci dst[j].i_packets += src[j].i_packets; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci seq_write(m, dst, 256 * sizeof(struct ip_rt_acct)); 3618c2ecf20Sopenharmony_ci kfree(dst); 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci#endif 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int __net_init ip_rt_do_proc_init(struct net *net) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct proc_dir_entry *pde; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci pde = proc_create("rt_cache", 0444, net->proc_net, 3718c2ecf20Sopenharmony_ci &rt_cache_proc_ops); 3728c2ecf20Sopenharmony_ci if (!pde) 3738c2ecf20Sopenharmony_ci goto err1; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci pde = proc_create("rt_cache", 0444, 3768c2ecf20Sopenharmony_ci net->proc_net_stat, &rt_cpu_proc_ops); 3778c2ecf20Sopenharmony_ci if (!pde) 3788c2ecf20Sopenharmony_ci goto err2; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 3818c2ecf20Sopenharmony_ci pde = proc_create_single("rt_acct", 0, net->proc_net, 3828c2ecf20Sopenharmony_ci rt_acct_proc_show); 3838c2ecf20Sopenharmony_ci if (!pde) 3848c2ecf20Sopenharmony_ci goto err3; 3858c2ecf20Sopenharmony_ci#endif 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 3898c2ecf20Sopenharmony_cierr3: 3908c2ecf20Sopenharmony_ci remove_proc_entry("rt_cache", net->proc_net_stat); 3918c2ecf20Sopenharmony_ci#endif 3928c2ecf20Sopenharmony_cierr2: 3938c2ecf20Sopenharmony_ci remove_proc_entry("rt_cache", net->proc_net); 3948c2ecf20Sopenharmony_cierr1: 3958c2ecf20Sopenharmony_ci return -ENOMEM; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void __net_exit ip_rt_do_proc_exit(struct net *net) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci remove_proc_entry("rt_cache", net->proc_net_stat); 4018c2ecf20Sopenharmony_ci remove_proc_entry("rt_cache", net->proc_net); 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 4038c2ecf20Sopenharmony_ci remove_proc_entry("rt_acct", net->proc_net); 4048c2ecf20Sopenharmony_ci#endif 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic struct pernet_operations ip_rt_proc_ops __net_initdata = { 4088c2ecf20Sopenharmony_ci .init = ip_rt_do_proc_init, 4098c2ecf20Sopenharmony_ci .exit = ip_rt_do_proc_exit, 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int __init ip_rt_proc_init(void) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci return register_pernet_subsys(&ip_rt_proc_ops); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci#else 4188c2ecf20Sopenharmony_cistatic inline int ip_rt_proc_init(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic inline bool rt_is_expired(const struct rtable *rth) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci return rth->rt_genid != rt_genid_ipv4(dev_net(rth->dst.dev)); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_civoid rt_cache_flush(struct net *net) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci rt_genid_bump_ipv4(net); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, 4358c2ecf20Sopenharmony_ci struct sk_buff *skb, 4368c2ecf20Sopenharmony_ci const void *daddr) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci const struct rtable *rt = container_of(dst, struct rtable, dst); 4398c2ecf20Sopenharmony_ci struct net_device *dev = dst->dev; 4408c2ecf20Sopenharmony_ci struct neighbour *n; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (likely(rt->rt_gw_family == AF_INET)) { 4458c2ecf20Sopenharmony_ci n = ip_neigh_gw4(dev, rt->rt_gw4); 4468c2ecf20Sopenharmony_ci } else if (rt->rt_gw_family == AF_INET6) { 4478c2ecf20Sopenharmony_ci n = ip_neigh_gw6(dev, &rt->rt_gw6); 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci __be32 pkey; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci pkey = skb ? ip_hdr(skb)->daddr : *((__be32 *) daddr); 4528c2ecf20Sopenharmony_ci n = ip_neigh_gw4(dev, pkey); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!IS_ERR(n) && !refcount_inc_not_zero(&n->refcnt)) 4568c2ecf20Sopenharmony_ci n = NULL; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return n; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci const struct rtable *rt = container_of(dst, struct rtable, dst); 4668c2ecf20Sopenharmony_ci struct net_device *dev = dst->dev; 4678c2ecf20Sopenharmony_ci const __be32 *pkey = daddr; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET) { 4708c2ecf20Sopenharmony_ci pkey = (const __be32 *)&rt->rt_gw4; 4718c2ecf20Sopenharmony_ci } else if (rt->rt_gw_family == AF_INET6) { 4728c2ecf20Sopenharmony_ci return __ipv6_confirm_neigh_stub(dev, &rt->rt_gw6); 4738c2ecf20Sopenharmony_ci } else if (!daddr || 4748c2ecf20Sopenharmony_ci (rt->rt_flags & 4758c2ecf20Sopenharmony_ci (RTCF_MULTICAST | RTCF_BROADCAST | RTCF_LOCAL))) { 4768c2ecf20Sopenharmony_ci return; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci __ipv4_confirm_neigh(dev, *(__force u32 *)pkey); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci/* Hash tables of size 2048..262144 depending on RAM size. 4828c2ecf20Sopenharmony_ci * Each bucket uses 8 bytes. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_cistatic u32 ip_idents_mask __read_mostly; 4858c2ecf20Sopenharmony_cistatic atomic_t *ip_idents __read_mostly; 4868c2ecf20Sopenharmony_cistatic u32 *ip_tstamps __read_mostly; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* In order to protect privacy, we add a perturbation to identifiers 4898c2ecf20Sopenharmony_ci * if one generator is seldom used. This makes hard for an attacker 4908c2ecf20Sopenharmony_ci * to infer how many packets were sent between two points in time. 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ciu32 ip_idents_reserve(u32 hash, int segs) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci u32 bucket, old, now = (u32)jiffies; 4958c2ecf20Sopenharmony_ci atomic_t *p_id; 4968c2ecf20Sopenharmony_ci u32 *p_tstamp; 4978c2ecf20Sopenharmony_ci u32 delta = 0; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci bucket = hash & ip_idents_mask; 5008c2ecf20Sopenharmony_ci p_tstamp = ip_tstamps + bucket; 5018c2ecf20Sopenharmony_ci p_id = ip_idents + bucket; 5028c2ecf20Sopenharmony_ci old = READ_ONCE(*p_tstamp); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (old != now && cmpxchg(p_tstamp, old, now) == old) 5058c2ecf20Sopenharmony_ci delta = prandom_u32_max(now - old); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* If UBSAN reports an error there, please make sure your compiler 5088c2ecf20Sopenharmony_ci * supports -fno-strict-overflow before reporting it that was a bug 5098c2ecf20Sopenharmony_ci * in UBSAN, and it has been fixed in GCC-8. 5108c2ecf20Sopenharmony_ci */ 5118c2ecf20Sopenharmony_ci return atomic_add_return(segs + delta, p_id) - segs; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_idents_reserve); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_civoid __ip_select_ident(struct net *net, struct iphdr *iph, int segs) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci u32 hash, id; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Note the following code is not safe, but this is okay. */ 5208c2ecf20Sopenharmony_ci if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key))) 5218c2ecf20Sopenharmony_ci get_random_bytes(&net->ipv4.ip_id_key, 5228c2ecf20Sopenharmony_ci sizeof(net->ipv4.ip_id_key)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci hash = siphash_3u32((__force u32)iph->daddr, 5258c2ecf20Sopenharmony_ci (__force u32)iph->saddr, 5268c2ecf20Sopenharmony_ci iph->protocol, 5278c2ecf20Sopenharmony_ci &net->ipv4.ip_id_key); 5288c2ecf20Sopenharmony_ci id = ip_idents_reserve(hash, segs); 5298c2ecf20Sopenharmony_ci iph->id = htons(id); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ip_select_ident); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void ip_rt_fix_tos(struct flowi4 *fl4) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci __u8 tos = RT_FL_TOS(fl4); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci fl4->flowi4_tos = tos & IPTOS_RT_MASK; 5388c2ecf20Sopenharmony_ci fl4->flowi4_scope = tos & RTO_ONLINK ? 5398c2ecf20Sopenharmony_ci RT_SCOPE_LINK : RT_SCOPE_UNIVERSE; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void __build_flow_key(const struct net *net, struct flowi4 *fl4, 5438c2ecf20Sopenharmony_ci const struct sock *sk, 5448c2ecf20Sopenharmony_ci const struct iphdr *iph, 5458c2ecf20Sopenharmony_ci int oif, u8 tos, 5468c2ecf20Sopenharmony_ci u8 prot, u32 mark, int flow_flags) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci if (sk) { 5498c2ecf20Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci oif = sk->sk_bound_dev_if; 5528c2ecf20Sopenharmony_ci mark = sk->sk_mark; 5538c2ecf20Sopenharmony_ci tos = RT_CONN_FLAGS(sk); 5548c2ecf20Sopenharmony_ci prot = inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci flowi4_init_output(fl4, oif, mark, tos, 5578c2ecf20Sopenharmony_ci RT_SCOPE_UNIVERSE, prot, 5588c2ecf20Sopenharmony_ci flow_flags, 5598c2ecf20Sopenharmony_ci iph->daddr, iph->saddr, 0, 0, 5608c2ecf20Sopenharmony_ci sock_net_uid(net, sk)); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb, 5648c2ecf20Sopenharmony_ci const struct sock *sk) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci const struct net *net = dev_net(skb->dev); 5678c2ecf20Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 5688c2ecf20Sopenharmony_ci int oif = skb->dev->ifindex; 5698c2ecf20Sopenharmony_ci u8 tos = RT_TOS(iph->tos); 5708c2ecf20Sopenharmony_ci u8 prot = iph->protocol; 5718c2ecf20Sopenharmony_ci u32 mark = skb->mark; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 5798c2ecf20Sopenharmony_ci const struct ip_options_rcu *inet_opt; 5808c2ecf20Sopenharmony_ci __be32 daddr = inet->inet_daddr; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci rcu_read_lock(); 5838c2ecf20Sopenharmony_ci inet_opt = rcu_dereference(inet->inet_opt); 5848c2ecf20Sopenharmony_ci if (inet_opt && inet_opt->opt.srr) 5858c2ecf20Sopenharmony_ci daddr = inet_opt->opt.faddr; 5868c2ecf20Sopenharmony_ci flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, 5878c2ecf20Sopenharmony_ci RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, 5888c2ecf20Sopenharmony_ci inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, 5898c2ecf20Sopenharmony_ci inet_sk_flowi_flags(sk), 5908c2ecf20Sopenharmony_ci daddr, inet->inet_saddr, 0, 0, sk->sk_uid); 5918c2ecf20Sopenharmony_ci rcu_read_unlock(); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk, 5958c2ecf20Sopenharmony_ci const struct sk_buff *skb) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci if (skb) 5988c2ecf20Sopenharmony_ci build_skb_flow_key(fl4, skb, sk); 5998c2ecf20Sopenharmony_ci else 6008c2ecf20Sopenharmony_ci build_sk_flow_key(fl4, sk); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(fnhe_lock); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic void fnhe_flush_routes(struct fib_nh_exception *fnhe) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct rtable *rt; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_input); 6108c2ecf20Sopenharmony_ci if (rt) { 6118c2ecf20Sopenharmony_ci RCU_INIT_POINTER(fnhe->fnhe_rth_input, NULL); 6128c2ecf20Sopenharmony_ci dst_dev_put(&rt->dst); 6138c2ecf20Sopenharmony_ci dst_release(&rt->dst); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_output); 6168c2ecf20Sopenharmony_ci if (rt) { 6178c2ecf20Sopenharmony_ci RCU_INIT_POINTER(fnhe->fnhe_rth_output, NULL); 6188c2ecf20Sopenharmony_ci dst_dev_put(&rt->dst); 6198c2ecf20Sopenharmony_ci dst_release(&rt->dst); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic void fnhe_remove_oldest(struct fnhe_hash_bucket *hash) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct fib_nh_exception __rcu **fnhe_p, **oldest_p; 6268c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe, *oldest = NULL; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci for (fnhe_p = &hash->chain; ; fnhe_p = &fnhe->fnhe_next) { 6298c2ecf20Sopenharmony_ci fnhe = rcu_dereference_protected(*fnhe_p, 6308c2ecf20Sopenharmony_ci lockdep_is_held(&fnhe_lock)); 6318c2ecf20Sopenharmony_ci if (!fnhe) 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci if (!oldest || 6348c2ecf20Sopenharmony_ci time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) { 6358c2ecf20Sopenharmony_ci oldest = fnhe; 6368c2ecf20Sopenharmony_ci oldest_p = fnhe_p; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci fnhe_flush_routes(oldest); 6408c2ecf20Sopenharmony_ci *oldest_p = oldest->fnhe_next; 6418c2ecf20Sopenharmony_ci kfree_rcu(oldest, rcu); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic u32 fnhe_hashfun(__be32 daddr) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci static siphash_key_t fnhe_hash_key __read_mostly; 6478c2ecf20Sopenharmony_ci u64 hval; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci net_get_random_once(&fnhe_hash_key, sizeof(fnhe_hash_key)); 6508c2ecf20Sopenharmony_ci hval = siphash_1u32((__force u32)daddr, &fnhe_hash_key); 6518c2ecf20Sopenharmony_ci return hash_64(hval, FNHE_HASH_SHIFT); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void fill_route_from_fnhe(struct rtable *rt, struct fib_nh_exception *fnhe) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci rt->rt_pmtu = fnhe->fnhe_pmtu; 6578c2ecf20Sopenharmony_ci rt->rt_mtu_locked = fnhe->fnhe_mtu_locked; 6588c2ecf20Sopenharmony_ci rt->dst.expires = fnhe->fnhe_expires; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (fnhe->fnhe_gw) { 6618c2ecf20Sopenharmony_ci rt->rt_flags |= RTCF_REDIRECTED; 6628c2ecf20Sopenharmony_ci rt->rt_uses_gateway = 1; 6638c2ecf20Sopenharmony_ci rt->rt_gw_family = AF_INET; 6648c2ecf20Sopenharmony_ci rt->rt_gw4 = fnhe->fnhe_gw; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void update_or_create_fnhe(struct fib_nh_common *nhc, __be32 daddr, 6698c2ecf20Sopenharmony_ci __be32 gw, u32 pmtu, bool lock, 6708c2ecf20Sopenharmony_ci unsigned long expires) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct fnhe_hash_bucket *hash; 6738c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 6748c2ecf20Sopenharmony_ci struct rtable *rt; 6758c2ecf20Sopenharmony_ci u32 genid, hval; 6768c2ecf20Sopenharmony_ci unsigned int i; 6778c2ecf20Sopenharmony_ci int depth; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci genid = fnhe_genid(dev_net(nhc->nhc_dev)); 6808c2ecf20Sopenharmony_ci hval = fnhe_hashfun(daddr); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci spin_lock_bh(&fnhe_lock); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci hash = rcu_dereference(nhc->nhc_exceptions); 6858c2ecf20Sopenharmony_ci if (!hash) { 6868c2ecf20Sopenharmony_ci hash = kcalloc(FNHE_HASH_SIZE, sizeof(*hash), GFP_ATOMIC); 6878c2ecf20Sopenharmony_ci if (!hash) 6888c2ecf20Sopenharmony_ci goto out_unlock; 6898c2ecf20Sopenharmony_ci rcu_assign_pointer(nhc->nhc_exceptions, hash); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci hash += hval; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci depth = 0; 6958c2ecf20Sopenharmony_ci for (fnhe = rcu_dereference(hash->chain); fnhe; 6968c2ecf20Sopenharmony_ci fnhe = rcu_dereference(fnhe->fnhe_next)) { 6978c2ecf20Sopenharmony_ci if (fnhe->fnhe_daddr == daddr) 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci depth++; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (fnhe) { 7038c2ecf20Sopenharmony_ci if (fnhe->fnhe_genid != genid) 7048c2ecf20Sopenharmony_ci fnhe->fnhe_genid = genid; 7058c2ecf20Sopenharmony_ci if (gw) 7068c2ecf20Sopenharmony_ci fnhe->fnhe_gw = gw; 7078c2ecf20Sopenharmony_ci if (pmtu) { 7088c2ecf20Sopenharmony_ci fnhe->fnhe_pmtu = pmtu; 7098c2ecf20Sopenharmony_ci fnhe->fnhe_mtu_locked = lock; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci fnhe->fnhe_expires = max(1UL, expires); 7128c2ecf20Sopenharmony_ci /* Update all cached dsts too */ 7138c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_input); 7148c2ecf20Sopenharmony_ci if (rt) 7158c2ecf20Sopenharmony_ci fill_route_from_fnhe(rt, fnhe); 7168c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_output); 7178c2ecf20Sopenharmony_ci if (rt) 7188c2ecf20Sopenharmony_ci fill_route_from_fnhe(rt, fnhe); 7198c2ecf20Sopenharmony_ci } else { 7208c2ecf20Sopenharmony_ci /* Randomize max depth to avoid some side channels attacks. */ 7218c2ecf20Sopenharmony_ci int max_depth = FNHE_RECLAIM_DEPTH + 7228c2ecf20Sopenharmony_ci prandom_u32_max(FNHE_RECLAIM_DEPTH); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci while (depth > max_depth) { 7258c2ecf20Sopenharmony_ci fnhe_remove_oldest(hash); 7268c2ecf20Sopenharmony_ci depth--; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci fnhe = kzalloc(sizeof(*fnhe), GFP_ATOMIC); 7308c2ecf20Sopenharmony_ci if (!fnhe) 7318c2ecf20Sopenharmony_ci goto out_unlock; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci fnhe->fnhe_next = hash->chain; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci fnhe->fnhe_genid = genid; 7368c2ecf20Sopenharmony_ci fnhe->fnhe_daddr = daddr; 7378c2ecf20Sopenharmony_ci fnhe->fnhe_gw = gw; 7388c2ecf20Sopenharmony_ci fnhe->fnhe_pmtu = pmtu; 7398c2ecf20Sopenharmony_ci fnhe->fnhe_mtu_locked = lock; 7408c2ecf20Sopenharmony_ci fnhe->fnhe_expires = max(1UL, expires); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci rcu_assign_pointer(hash->chain, fnhe); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* Exception created; mark the cached routes for the nexthop 7458c2ecf20Sopenharmony_ci * stale, so anyone caching it rechecks if this exception 7468c2ecf20Sopenharmony_ci * applies to them. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci rt = rcu_dereference(nhc->nhc_rth_input); 7498c2ecf20Sopenharmony_ci if (rt) 7508c2ecf20Sopenharmony_ci rt->dst.obsolete = DST_OBSOLETE_KILL; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 7538c2ecf20Sopenharmony_ci struct rtable __rcu **prt; 7548c2ecf20Sopenharmony_ci prt = per_cpu_ptr(nhc->nhc_pcpu_rth_output, i); 7558c2ecf20Sopenharmony_ci rt = rcu_dereference(*prt); 7568c2ecf20Sopenharmony_ci if (rt) 7578c2ecf20Sopenharmony_ci rt->dst.obsolete = DST_OBSOLETE_KILL; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci fnhe->fnhe_stamp = jiffies; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ciout_unlock: 7648c2ecf20Sopenharmony_ci spin_unlock_bh(&fnhe_lock); 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4, 7688c2ecf20Sopenharmony_ci bool kill_route) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci __be32 new_gw = icmp_hdr(skb)->un.gateway; 7718c2ecf20Sopenharmony_ci __be32 old_gw = ip_hdr(skb)->saddr; 7728c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 7738c2ecf20Sopenharmony_ci struct in_device *in_dev; 7748c2ecf20Sopenharmony_ci struct fib_result res; 7758c2ecf20Sopenharmony_ci struct neighbour *n; 7768c2ecf20Sopenharmony_ci struct net *net; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci switch (icmp_hdr(skb)->code & 7) { 7798c2ecf20Sopenharmony_ci case ICMP_REDIR_NET: 7808c2ecf20Sopenharmony_ci case ICMP_REDIR_NETTOS: 7818c2ecf20Sopenharmony_ci case ICMP_REDIR_HOST: 7828c2ecf20Sopenharmony_ci case ICMP_REDIR_HOSTTOS: 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci default: 7868c2ecf20Sopenharmony_ci return; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (rt->rt_gw_family != AF_INET || rt->rt_gw4 != old_gw) 7908c2ecf20Sopenharmony_ci return; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 7938c2ecf20Sopenharmony_ci if (!in_dev) 7948c2ecf20Sopenharmony_ci return; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci net = dev_net(dev); 7978c2ecf20Sopenharmony_ci if (new_gw == old_gw || !IN_DEV_RX_REDIRECTS(in_dev) || 7988c2ecf20Sopenharmony_ci ipv4_is_multicast(new_gw) || ipv4_is_lbcast(new_gw) || 7998c2ecf20Sopenharmony_ci ipv4_is_zeronet(new_gw)) 8008c2ecf20Sopenharmony_ci goto reject_redirect; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (!IN_DEV_SHARED_MEDIA(in_dev)) { 8038c2ecf20Sopenharmony_ci if (!inet_addr_onlink(in_dev, new_gw, old_gw)) 8048c2ecf20Sopenharmony_ci goto reject_redirect; 8058c2ecf20Sopenharmony_ci if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev)) 8068c2ecf20Sopenharmony_ci goto reject_redirect; 8078c2ecf20Sopenharmony_ci } else { 8088c2ecf20Sopenharmony_ci if (inet_addr_type(net, new_gw) != RTN_UNICAST) 8098c2ecf20Sopenharmony_ci goto reject_redirect; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci n = __ipv4_neigh_lookup(rt->dst.dev, (__force u32)new_gw); 8138c2ecf20Sopenharmony_ci if (!n) 8148c2ecf20Sopenharmony_ci n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev); 8158c2ecf20Sopenharmony_ci if (!IS_ERR(n)) { 8168c2ecf20Sopenharmony_ci if (!(n->nud_state & NUD_VALID)) { 8178c2ecf20Sopenharmony_ci neigh_event_send(n, NULL); 8188c2ecf20Sopenharmony_ci } else { 8198c2ecf20Sopenharmony_ci if (fib_lookup(net, fl4, &res, 0) == 0) { 8208c2ecf20Sopenharmony_ci struct fib_nh_common *nhc; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci fib_select_path(net, &res, fl4, skb); 8238c2ecf20Sopenharmony_ci nhc = FIB_RES_NHC(res); 8248c2ecf20Sopenharmony_ci update_or_create_fnhe(nhc, fl4->daddr, new_gw, 8258c2ecf20Sopenharmony_ci 0, false, 8268c2ecf20Sopenharmony_ci jiffies + ip_rt_gc_timeout); 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci if (kill_route) 8298c2ecf20Sopenharmony_ci rt->dst.obsolete = DST_OBSOLETE_KILL; 8308c2ecf20Sopenharmony_ci call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci neigh_release(n); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci return; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cireject_redirect: 8378c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_VERBOSE 8388c2ecf20Sopenharmony_ci if (IN_DEV_LOG_MARTIANS(in_dev)) { 8398c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *) skb->data; 8408c2ecf20Sopenharmony_ci __be32 daddr = iph->daddr; 8418c2ecf20Sopenharmony_ci __be32 saddr = iph->saddr; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci net_info_ratelimited("Redirect from %pI4 on %s about %pI4 ignored\n" 8448c2ecf20Sopenharmony_ci " Advised path = %pI4 -> %pI4\n", 8458c2ecf20Sopenharmony_ci &old_gw, dev->name, &new_gw, 8468c2ecf20Sopenharmony_ci &saddr, &daddr); 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci#endif 8498c2ecf20Sopenharmony_ci ; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct rtable *rt; 8558c2ecf20Sopenharmony_ci struct flowi4 fl4; 8568c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *) skb->data; 8578c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 8588c2ecf20Sopenharmony_ci int oif = skb->dev->ifindex; 8598c2ecf20Sopenharmony_ci u8 tos = RT_TOS(iph->tos); 8608c2ecf20Sopenharmony_ci u8 prot = iph->protocol; 8618c2ecf20Sopenharmony_ci u32 mark = skb->mark; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci rt = (struct rtable *) dst; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci __build_flow_key(net, &fl4, sk, iph, oif, tos, prot, mark, 0); 8668c2ecf20Sopenharmony_ci ip_rt_fix_tos(&fl4); 8678c2ecf20Sopenharmony_ci __ip_do_redirect(rt, skb, &fl4, true); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic void ipv4_negative_advice(struct sock *sk, 8718c2ecf20Sopenharmony_ci struct dst_entry *dst) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct rtable *rt = (struct rtable *)dst; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if ((dst->obsolete > 0) || 8768c2ecf20Sopenharmony_ci (rt->rt_flags & RTCF_REDIRECTED) || 8778c2ecf20Sopenharmony_ci rt->dst.expires) 8788c2ecf20Sopenharmony_ci sk_dst_reset(sk); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci/* 8828c2ecf20Sopenharmony_ci * Algorithm: 8838c2ecf20Sopenharmony_ci * 1. The first ip_rt_redirect_number redirects are sent 8848c2ecf20Sopenharmony_ci * with exponential backoff, then we stop sending them at all, 8858c2ecf20Sopenharmony_ci * assuming that the host ignores our redirects. 8868c2ecf20Sopenharmony_ci * 2. If we did not see packets requiring redirects 8878c2ecf20Sopenharmony_ci * during ip_rt_redirect_silence, we assume that the host 8888c2ecf20Sopenharmony_ci * forgot redirected route and start to send redirects again. 8898c2ecf20Sopenharmony_ci * 8908c2ecf20Sopenharmony_ci * This algorithm is much cheaper and more intelligent than dumb load limiting 8918c2ecf20Sopenharmony_ci * in icmp.c. 8928c2ecf20Sopenharmony_ci * 8938c2ecf20Sopenharmony_ci * NOTE. Do not forget to inhibit load limiting for redirects (redundant) 8948c2ecf20Sopenharmony_ci * and "frag. need" (breaks PMTU discovery) in icmp.c. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_civoid ip_rt_send_redirect(struct sk_buff *skb) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct rtable *rt = skb_rtable(skb); 9008c2ecf20Sopenharmony_ci struct in_device *in_dev; 9018c2ecf20Sopenharmony_ci struct inet_peer *peer; 9028c2ecf20Sopenharmony_ci struct net *net; 9038c2ecf20Sopenharmony_ci int log_martians; 9048c2ecf20Sopenharmony_ci int vif; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci rcu_read_lock(); 9078c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(rt->dst.dev); 9088c2ecf20Sopenharmony_ci if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) { 9098c2ecf20Sopenharmony_ci rcu_read_unlock(); 9108c2ecf20Sopenharmony_ci return; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci log_martians = IN_DEV_LOG_MARTIANS(in_dev); 9138c2ecf20Sopenharmony_ci vif = l3mdev_master_ifindex_rcu(rt->dst.dev); 9148c2ecf20Sopenharmony_ci rcu_read_unlock(); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci net = dev_net(rt->dst.dev); 9178c2ecf20Sopenharmony_ci peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif, 1); 9188c2ecf20Sopenharmony_ci if (!peer) { 9198c2ecf20Sopenharmony_ci icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, 9208c2ecf20Sopenharmony_ci rt_nexthop(rt, ip_hdr(skb)->daddr)); 9218c2ecf20Sopenharmony_ci return; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* No redirected packets during ip_rt_redirect_silence; 9258c2ecf20Sopenharmony_ci * reset the algorithm. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) { 9288c2ecf20Sopenharmony_ci peer->rate_tokens = 0; 9298c2ecf20Sopenharmony_ci peer->n_redirects = 0; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* Too many ignored redirects; do not send anything 9338c2ecf20Sopenharmony_ci * set dst.rate_last to the last seen redirected packet. 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_ci if (peer->n_redirects >= ip_rt_redirect_number) { 9368c2ecf20Sopenharmony_ci peer->rate_last = jiffies; 9378c2ecf20Sopenharmony_ci goto out_put_peer; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* Check for load limit; set rate_last to the latest sent 9418c2ecf20Sopenharmony_ci * redirect. 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci if (peer->n_redirects == 0 || 9448c2ecf20Sopenharmony_ci time_after(jiffies, 9458c2ecf20Sopenharmony_ci (peer->rate_last + 9468c2ecf20Sopenharmony_ci (ip_rt_redirect_load << peer->n_redirects)))) { 9478c2ecf20Sopenharmony_ci __be32 gw = rt_nexthop(rt, ip_hdr(skb)->daddr); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, gw); 9508c2ecf20Sopenharmony_ci peer->rate_last = jiffies; 9518c2ecf20Sopenharmony_ci ++peer->n_redirects; 9528c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_VERBOSE 9538c2ecf20Sopenharmony_ci if (log_martians && 9548c2ecf20Sopenharmony_ci peer->n_redirects == ip_rt_redirect_number) 9558c2ecf20Sopenharmony_ci net_warn_ratelimited("host %pI4/if%d ignores redirects for %pI4 to %pI4\n", 9568c2ecf20Sopenharmony_ci &ip_hdr(skb)->saddr, inet_iif(skb), 9578c2ecf20Sopenharmony_ci &ip_hdr(skb)->daddr, &gw); 9588c2ecf20Sopenharmony_ci#endif 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ciout_put_peer: 9618c2ecf20Sopenharmony_ci inet_putpeer(peer); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic int ip_error(struct sk_buff *skb) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct rtable *rt = skb_rtable(skb); 9678c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 9688c2ecf20Sopenharmony_ci struct in_device *in_dev; 9698c2ecf20Sopenharmony_ci struct inet_peer *peer; 9708c2ecf20Sopenharmony_ci unsigned long now; 9718c2ecf20Sopenharmony_ci struct net *net; 9728c2ecf20Sopenharmony_ci bool send; 9738c2ecf20Sopenharmony_ci int code; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (netif_is_l3_master(skb->dev)) { 9768c2ecf20Sopenharmony_ci dev = __dev_get_by_index(dev_net(skb->dev), IPCB(skb)->iif); 9778c2ecf20Sopenharmony_ci if (!dev) 9788c2ecf20Sopenharmony_ci goto out; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(dev); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* IP on this device is disabled. */ 9848c2ecf20Sopenharmony_ci if (!in_dev) 9858c2ecf20Sopenharmony_ci goto out; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci net = dev_net(rt->dst.dev); 9888c2ecf20Sopenharmony_ci if (!IN_DEV_FORWARD(in_dev)) { 9898c2ecf20Sopenharmony_ci switch (rt->dst.error) { 9908c2ecf20Sopenharmony_ci case EHOSTUNREACH: 9918c2ecf20Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS); 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci case ENETUNREACH: 9958c2ecf20Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES); 9968c2ecf20Sopenharmony_ci break; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci goto out; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci switch (rt->dst.error) { 10028c2ecf20Sopenharmony_ci case EINVAL: 10038c2ecf20Sopenharmony_ci default: 10048c2ecf20Sopenharmony_ci goto out; 10058c2ecf20Sopenharmony_ci case EHOSTUNREACH: 10068c2ecf20Sopenharmony_ci code = ICMP_HOST_UNREACH; 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci case ENETUNREACH: 10098c2ecf20Sopenharmony_ci code = ICMP_NET_UNREACH; 10108c2ecf20Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES); 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci case EACCES: 10138c2ecf20Sopenharmony_ci code = ICMP_PKT_FILTERED; 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, 10188c2ecf20Sopenharmony_ci l3mdev_master_ifindex(skb->dev), 1); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci send = true; 10218c2ecf20Sopenharmony_ci if (peer) { 10228c2ecf20Sopenharmony_ci now = jiffies; 10238c2ecf20Sopenharmony_ci peer->rate_tokens += now - peer->rate_last; 10248c2ecf20Sopenharmony_ci if (peer->rate_tokens > ip_rt_error_burst) 10258c2ecf20Sopenharmony_ci peer->rate_tokens = ip_rt_error_burst; 10268c2ecf20Sopenharmony_ci peer->rate_last = now; 10278c2ecf20Sopenharmony_ci if (peer->rate_tokens >= ip_rt_error_cost) 10288c2ecf20Sopenharmony_ci peer->rate_tokens -= ip_rt_error_cost; 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci send = false; 10318c2ecf20Sopenharmony_ci inet_putpeer(peer); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci if (send) 10348c2ecf20Sopenharmony_ci icmp_send(skb, ICMP_DEST_UNREACH, code, 0); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ciout: kfree_skb(skb); 10378c2ecf20Sopenharmony_ci return 0; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct dst_entry *dst = &rt->dst; 10438c2ecf20Sopenharmony_ci struct net *net = dev_net(dst->dev); 10448c2ecf20Sopenharmony_ci struct fib_result res; 10458c2ecf20Sopenharmony_ci bool lock = false; 10468c2ecf20Sopenharmony_ci u32 old_mtu; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (ip_mtu_locked(dst)) 10498c2ecf20Sopenharmony_ci return; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci old_mtu = ipv4_mtu(dst); 10528c2ecf20Sopenharmony_ci if (old_mtu < mtu) 10538c2ecf20Sopenharmony_ci return; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (mtu < ip_rt_min_pmtu) { 10568c2ecf20Sopenharmony_ci lock = true; 10578c2ecf20Sopenharmony_ci mtu = min(old_mtu, ip_rt_min_pmtu); 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (rt->rt_pmtu == mtu && !lock && 10618c2ecf20Sopenharmony_ci time_before(jiffies, dst->expires - ip_rt_mtu_expires / 2)) 10628c2ecf20Sopenharmony_ci return; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci rcu_read_lock(); 10658c2ecf20Sopenharmony_ci if (fib_lookup(net, fl4, &res, 0) == 0) { 10668c2ecf20Sopenharmony_ci struct fib_nh_common *nhc; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci fib_select_path(net, &res, fl4, NULL); 10698c2ecf20Sopenharmony_ci nhc = FIB_RES_NHC(res); 10708c2ecf20Sopenharmony_ci update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock, 10718c2ecf20Sopenharmony_ci jiffies + ip_rt_mtu_expires); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci rcu_read_unlock(); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, 10778c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 mtu, 10788c2ecf20Sopenharmony_ci bool confirm_neigh) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct rtable *rt = (struct rtable *) dst; 10818c2ecf20Sopenharmony_ci struct flowi4 fl4; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci ip_rt_build_flow_key(&fl4, sk, skb); 10848c2ecf20Sopenharmony_ci ip_rt_fix_tos(&fl4); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* Don't make lookup fail for bridged encapsulations */ 10878c2ecf20Sopenharmony_ci if (skb && netif_is_any_bridge_port(skb->dev)) 10888c2ecf20Sopenharmony_ci fl4.flowi4_oif = 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci __ip_rt_update_pmtu(rt, &fl4, mtu); 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_civoid ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, 10948c2ecf20Sopenharmony_ci int oif, u8 protocol) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *)skb->data; 10978c2ecf20Sopenharmony_ci struct flowi4 fl4; 10988c2ecf20Sopenharmony_ci struct rtable *rt; 10998c2ecf20Sopenharmony_ci u32 mark = IP4_REPLY_MARK(net, skb->mark); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci __build_flow_key(net, &fl4, NULL, iph, oif, 11028c2ecf20Sopenharmony_ci RT_TOS(iph->tos), protocol, mark, 0); 11038c2ecf20Sopenharmony_ci rt = __ip_route_output_key(net, &fl4); 11048c2ecf20Sopenharmony_ci if (!IS_ERR(rt)) { 11058c2ecf20Sopenharmony_ci __ip_rt_update_pmtu(rt, &fl4, mtu); 11068c2ecf20Sopenharmony_ci ip_rt_put(rt); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv4_update_pmtu); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *)skb->data; 11148c2ecf20Sopenharmony_ci struct flowi4 fl4; 11158c2ecf20Sopenharmony_ci struct rtable *rt; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (!fl4.flowi4_mark) 11208c2ecf20Sopenharmony_ci fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci rt = __ip_route_output_key(sock_net(sk), &fl4); 11238c2ecf20Sopenharmony_ci if (!IS_ERR(rt)) { 11248c2ecf20Sopenharmony_ci __ip_rt_update_pmtu(rt, &fl4, mtu); 11258c2ecf20Sopenharmony_ci ip_rt_put(rt); 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_civoid ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *)skb->data; 11328c2ecf20Sopenharmony_ci struct flowi4 fl4; 11338c2ecf20Sopenharmony_ci struct rtable *rt; 11348c2ecf20Sopenharmony_ci struct dst_entry *odst = NULL; 11358c2ecf20Sopenharmony_ci bool new = false; 11368c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci bh_lock_sock(sk); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (!ip_sk_accept_pmtu(sk)) 11418c2ecf20Sopenharmony_ci goto out; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci odst = sk_dst_get(sk); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (sock_owned_by_user(sk) || !odst) { 11468c2ecf20Sopenharmony_ci __ipv4_sk_update_pmtu(skb, sk, mtu); 11478c2ecf20Sopenharmony_ci goto out; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci rt = (struct rtable *)odst; 11538c2ecf20Sopenharmony_ci if (odst->obsolete && !odst->ops->check(odst, 0)) { 11548c2ecf20Sopenharmony_ci rt = ip_route_output_flow(sock_net(sk), &fl4, sk); 11558c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 11568c2ecf20Sopenharmony_ci goto out; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci new = true; 11598c2ecf20Sopenharmony_ci } else { 11608c2ecf20Sopenharmony_ci ip_rt_fix_tos(&fl4); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci __ip_rt_update_pmtu((struct rtable *)xfrm_dst_path(&rt->dst), &fl4, mtu); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (!dst_check(&rt->dst, 0)) { 11668c2ecf20Sopenharmony_ci if (new) 11678c2ecf20Sopenharmony_ci dst_release(&rt->dst); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci rt = ip_route_output_flow(sock_net(sk), &fl4, sk); 11708c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 11718c2ecf20Sopenharmony_ci goto out; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci new = true; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (new) 11778c2ecf20Sopenharmony_ci sk_dst_set(sk, &rt->dst); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ciout: 11808c2ecf20Sopenharmony_ci bh_unlock_sock(sk); 11818c2ecf20Sopenharmony_ci dst_release(odst); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_civoid ipv4_redirect(struct sk_buff *skb, struct net *net, 11868c2ecf20Sopenharmony_ci int oif, u8 protocol) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *)skb->data; 11898c2ecf20Sopenharmony_ci struct flowi4 fl4; 11908c2ecf20Sopenharmony_ci struct rtable *rt; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci __build_flow_key(net, &fl4, NULL, iph, oif, 11938c2ecf20Sopenharmony_ci RT_TOS(iph->tos), protocol, 0, 0); 11948c2ecf20Sopenharmony_ci rt = __ip_route_output_key(net, &fl4); 11958c2ecf20Sopenharmony_ci if (!IS_ERR(rt)) { 11968c2ecf20Sopenharmony_ci __ip_do_redirect(rt, skb, &fl4, false); 11978c2ecf20Sopenharmony_ci ip_rt_put(rt); 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv4_redirect); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_civoid ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci const struct iphdr *iph = (const struct iphdr *)skb->data; 12058c2ecf20Sopenharmony_ci struct flowi4 fl4; 12068c2ecf20Sopenharmony_ci struct rtable *rt; 12078c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0); 12108c2ecf20Sopenharmony_ci rt = __ip_route_output_key(net, &fl4); 12118c2ecf20Sopenharmony_ci if (!IS_ERR(rt)) { 12128c2ecf20Sopenharmony_ci __ip_do_redirect(rt, skb, &fl4, false); 12138c2ecf20Sopenharmony_ci ip_rt_put(rt); 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ipv4_sk_redirect); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct rtable *rt = (struct rtable *) dst; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* All IPV4 dsts are created with ->obsolete set to the value 12238c2ecf20Sopenharmony_ci * DST_OBSOLETE_FORCE_CHK which forces validation calls down 12248c2ecf20Sopenharmony_ci * into this function always. 12258c2ecf20Sopenharmony_ci * 12268c2ecf20Sopenharmony_ci * When a PMTU/redirect information update invalidates a route, 12278c2ecf20Sopenharmony_ci * this is indicated by setting obsolete to DST_OBSOLETE_KILL or 12288c2ecf20Sopenharmony_ci * DST_OBSOLETE_DEAD. 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci if (dst->obsolete != DST_OBSOLETE_FORCE_CHK || rt_is_expired(rt)) 12318c2ecf20Sopenharmony_ci return NULL; 12328c2ecf20Sopenharmony_ci return dst; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic void ipv4_send_dest_unreach(struct sk_buff *skb) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci struct net_device *dev; 12388c2ecf20Sopenharmony_ci struct ip_options opt; 12398c2ecf20Sopenharmony_ci int res; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* Recompile ip options since IPCB may not be valid anymore. 12428c2ecf20Sopenharmony_ci * Also check we have a reasonable ipv4 header. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ci if (!pskb_network_may_pull(skb, sizeof(struct iphdr)) || 12458c2ecf20Sopenharmony_ci ip_hdr(skb)->version != 4 || ip_hdr(skb)->ihl < 5) 12468c2ecf20Sopenharmony_ci return; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci memset(&opt, 0, sizeof(opt)); 12498c2ecf20Sopenharmony_ci if (ip_hdr(skb)->ihl > 5) { 12508c2ecf20Sopenharmony_ci if (!pskb_network_may_pull(skb, ip_hdr(skb)->ihl * 4)) 12518c2ecf20Sopenharmony_ci return; 12528c2ecf20Sopenharmony_ci opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci rcu_read_lock(); 12558c2ecf20Sopenharmony_ci dev = skb->dev ? skb->dev : skb_rtable(skb)->dst.dev; 12568c2ecf20Sopenharmony_ci res = __ip_options_compile(dev_net(dev), &opt, skb, NULL); 12578c2ecf20Sopenharmony_ci rcu_read_unlock(); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (res) 12608c2ecf20Sopenharmony_ci return; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt); 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic void ipv4_link_failure(struct sk_buff *skb) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci struct rtable *rt; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci ipv4_send_dest_unreach(skb); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci rt = skb_rtable(skb); 12728c2ecf20Sopenharmony_ci if (rt) 12738c2ecf20Sopenharmony_ci dst_set_expires(&rt->dst, 0); 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic int ip_rt_bug(struct net *net, struct sock *sk, struct sk_buff *skb) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci pr_debug("%s: %pI4 -> %pI4, %s\n", 12798c2ecf20Sopenharmony_ci __func__, &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr, 12808c2ecf20Sopenharmony_ci skb->dev ? skb->dev->name : "?"); 12818c2ecf20Sopenharmony_ci kfree_skb(skb); 12828c2ecf20Sopenharmony_ci WARN_ON(1); 12838c2ecf20Sopenharmony_ci return 0; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci/* 12878c2ecf20Sopenharmony_ci We do not cache source address of outgoing interface, 12888c2ecf20Sopenharmony_ci because it is used only by IP RR, TS and SRR options, 12898c2ecf20Sopenharmony_ci so that it out of fast path. 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci BTW remember: "addr" is allowed to be not aligned 12928c2ecf20Sopenharmony_ci in IP options! 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_civoid ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci __be32 src; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (rt_is_output_route(rt)) 13008c2ecf20Sopenharmony_ci src = ip_hdr(skb)->saddr; 13018c2ecf20Sopenharmony_ci else { 13028c2ecf20Sopenharmony_ci struct fib_result res; 13038c2ecf20Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 13048c2ecf20Sopenharmony_ci struct flowi4 fl4 = { 13058c2ecf20Sopenharmony_ci .daddr = iph->daddr, 13068c2ecf20Sopenharmony_ci .saddr = iph->saddr, 13078c2ecf20Sopenharmony_ci .flowi4_tos = RT_TOS(iph->tos), 13088c2ecf20Sopenharmony_ci .flowi4_oif = rt->dst.dev->ifindex, 13098c2ecf20Sopenharmony_ci .flowi4_iif = skb->dev->ifindex, 13108c2ecf20Sopenharmony_ci .flowi4_mark = skb->mark, 13118c2ecf20Sopenharmony_ci }; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci rcu_read_lock(); 13148c2ecf20Sopenharmony_ci if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res, 0) == 0) 13158c2ecf20Sopenharmony_ci src = fib_result_prefsrc(dev_net(rt->dst.dev), &res); 13168c2ecf20Sopenharmony_ci else 13178c2ecf20Sopenharmony_ci src = inet_select_addr(rt->dst.dev, 13188c2ecf20Sopenharmony_ci rt_nexthop(rt, iph->daddr), 13198c2ecf20Sopenharmony_ci RT_SCOPE_UNIVERSE); 13208c2ecf20Sopenharmony_ci rcu_read_unlock(); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci memcpy(addr, &src, 4); 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 13268c2ecf20Sopenharmony_cistatic void set_class_tag(struct rtable *rt, u32 tag) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci if (!(rt->dst.tclassid & 0xFFFF)) 13298c2ecf20Sopenharmony_ci rt->dst.tclassid |= tag & 0xFFFF; 13308c2ecf20Sopenharmony_ci if (!(rt->dst.tclassid & 0xFFFF0000)) 13318c2ecf20Sopenharmony_ci rt->dst.tclassid |= tag & 0xFFFF0000; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci#endif 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic unsigned int ipv4_default_advmss(const struct dst_entry *dst) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr); 13388c2ecf20Sopenharmony_ci unsigned int advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, 13398c2ecf20Sopenharmony_ci ip_rt_min_advmss); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci return min(advmss, IPV4_MAX_PMTU - header_size); 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_cistatic unsigned int ipv4_mtu(const struct dst_entry *dst) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci const struct rtable *rt = (const struct rtable *)dst; 13478c2ecf20Sopenharmony_ci unsigned int mtu = rt->rt_pmtu; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (!mtu || time_after_eq(jiffies, rt->dst.expires)) 13508c2ecf20Sopenharmony_ci mtu = dst_metric_raw(dst, RTAX_MTU); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (mtu) 13538c2ecf20Sopenharmony_ci goto out; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci mtu = READ_ONCE(dst->dev->mtu); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (unlikely(ip_mtu_locked(dst))) { 13588c2ecf20Sopenharmony_ci if (rt->rt_uses_gateway && mtu > 576) 13598c2ecf20Sopenharmony_ci mtu = 576; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ciout: 13638c2ecf20Sopenharmony_ci mtu = min_t(unsigned int, mtu, IP_MAX_MTU); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return mtu - lwtunnel_headroom(dst->lwtstate, mtu); 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic void ip_del_fnhe(struct fib_nh_common *nhc, __be32 daddr) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct fnhe_hash_bucket *hash; 13718c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe, __rcu **fnhe_p; 13728c2ecf20Sopenharmony_ci u32 hval = fnhe_hashfun(daddr); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci spin_lock_bh(&fnhe_lock); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci hash = rcu_dereference_protected(nhc->nhc_exceptions, 13778c2ecf20Sopenharmony_ci lockdep_is_held(&fnhe_lock)); 13788c2ecf20Sopenharmony_ci hash += hval; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci fnhe_p = &hash->chain; 13818c2ecf20Sopenharmony_ci fnhe = rcu_dereference_protected(*fnhe_p, lockdep_is_held(&fnhe_lock)); 13828c2ecf20Sopenharmony_ci while (fnhe) { 13838c2ecf20Sopenharmony_ci if (fnhe->fnhe_daddr == daddr) { 13848c2ecf20Sopenharmony_ci rcu_assign_pointer(*fnhe_p, rcu_dereference_protected( 13858c2ecf20Sopenharmony_ci fnhe->fnhe_next, lockdep_is_held(&fnhe_lock))); 13868c2ecf20Sopenharmony_ci /* set fnhe_daddr to 0 to ensure it won't bind with 13878c2ecf20Sopenharmony_ci * new dsts in rt_bind_exception(). 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci fnhe->fnhe_daddr = 0; 13908c2ecf20Sopenharmony_ci fnhe_flush_routes(fnhe); 13918c2ecf20Sopenharmony_ci kfree_rcu(fnhe, rcu); 13928c2ecf20Sopenharmony_ci break; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci fnhe_p = &fnhe->fnhe_next; 13958c2ecf20Sopenharmony_ci fnhe = rcu_dereference_protected(fnhe->fnhe_next, 13968c2ecf20Sopenharmony_ci lockdep_is_held(&fnhe_lock)); 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci spin_unlock_bh(&fnhe_lock); 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic struct fib_nh_exception *find_exception(struct fib_nh_common *nhc, 14038c2ecf20Sopenharmony_ci __be32 daddr) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci struct fnhe_hash_bucket *hash = rcu_dereference(nhc->nhc_exceptions); 14068c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 14078c2ecf20Sopenharmony_ci u32 hval; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (!hash) 14108c2ecf20Sopenharmony_ci return NULL; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci hval = fnhe_hashfun(daddr); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci for (fnhe = rcu_dereference(hash[hval].chain); fnhe; 14158c2ecf20Sopenharmony_ci fnhe = rcu_dereference(fnhe->fnhe_next)) { 14168c2ecf20Sopenharmony_ci if (fnhe->fnhe_daddr == daddr) { 14178c2ecf20Sopenharmony_ci if (fnhe->fnhe_expires && 14188c2ecf20Sopenharmony_ci time_after(jiffies, fnhe->fnhe_expires)) { 14198c2ecf20Sopenharmony_ci ip_del_fnhe(nhc, daddr); 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci return fnhe; 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci return NULL; 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci/* MTU selection: 14298c2ecf20Sopenharmony_ci * 1. mtu on route is locked - use it 14308c2ecf20Sopenharmony_ci * 2. mtu from nexthop exception 14318c2ecf20Sopenharmony_ci * 3. mtu from egress device 14328c2ecf20Sopenharmony_ci */ 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ciu32 ip_mtu_from_fib_result(struct fib_result *res, __be32 daddr) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = res->nhc; 14378c2ecf20Sopenharmony_ci struct net_device *dev = nhc->nhc_dev; 14388c2ecf20Sopenharmony_ci struct fib_info *fi = res->fi; 14398c2ecf20Sopenharmony_ci u32 mtu = 0; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (READ_ONCE(dev_net(dev)->ipv4.sysctl_ip_fwd_use_pmtu) || 14428c2ecf20Sopenharmony_ci fi->fib_metrics->metrics[RTAX_LOCK - 1] & (1 << RTAX_MTU)) 14438c2ecf20Sopenharmony_ci mtu = fi->fib_mtu; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (likely(!mtu)) { 14468c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci fnhe = find_exception(nhc, daddr); 14498c2ecf20Sopenharmony_ci if (fnhe && !time_after_eq(jiffies, fnhe->fnhe_expires)) 14508c2ecf20Sopenharmony_ci mtu = fnhe->fnhe_pmtu; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (likely(!mtu)) 14548c2ecf20Sopenharmony_ci mtu = min(READ_ONCE(dev->mtu), IP_MAX_MTU); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci return mtu - lwtunnel_headroom(nhc->nhc_lwtstate, mtu); 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, 14608c2ecf20Sopenharmony_ci __be32 daddr, const bool do_cache) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci bool ret = false; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci spin_lock_bh(&fnhe_lock); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (daddr == fnhe->fnhe_daddr) { 14678c2ecf20Sopenharmony_ci struct rtable __rcu **porig; 14688c2ecf20Sopenharmony_ci struct rtable *orig; 14698c2ecf20Sopenharmony_ci int genid = fnhe_genid(dev_net(rt->dst.dev)); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (rt_is_input_route(rt)) 14728c2ecf20Sopenharmony_ci porig = &fnhe->fnhe_rth_input; 14738c2ecf20Sopenharmony_ci else 14748c2ecf20Sopenharmony_ci porig = &fnhe->fnhe_rth_output; 14758c2ecf20Sopenharmony_ci orig = rcu_dereference(*porig); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (fnhe->fnhe_genid != genid) { 14788c2ecf20Sopenharmony_ci fnhe->fnhe_genid = genid; 14798c2ecf20Sopenharmony_ci fnhe->fnhe_gw = 0; 14808c2ecf20Sopenharmony_ci fnhe->fnhe_pmtu = 0; 14818c2ecf20Sopenharmony_ci fnhe->fnhe_expires = 0; 14828c2ecf20Sopenharmony_ci fnhe->fnhe_mtu_locked = false; 14838c2ecf20Sopenharmony_ci fnhe_flush_routes(fnhe); 14848c2ecf20Sopenharmony_ci orig = NULL; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci fill_route_from_fnhe(rt, fnhe); 14878c2ecf20Sopenharmony_ci if (!rt->rt_gw4) { 14888c2ecf20Sopenharmony_ci rt->rt_gw4 = daddr; 14898c2ecf20Sopenharmony_ci rt->rt_gw_family = AF_INET; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (do_cache) { 14938c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 14948c2ecf20Sopenharmony_ci rcu_assign_pointer(*porig, rt); 14958c2ecf20Sopenharmony_ci if (orig) { 14968c2ecf20Sopenharmony_ci dst_dev_put(&orig->dst); 14978c2ecf20Sopenharmony_ci dst_release(&orig->dst); 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci ret = true; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci fnhe->fnhe_stamp = jiffies; 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci spin_unlock_bh(&fnhe_lock); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci return ret; 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cistatic bool rt_cache_route(struct fib_nh_common *nhc, struct rtable *rt) 15108c2ecf20Sopenharmony_ci{ 15118c2ecf20Sopenharmony_ci struct rtable *orig, *prev, **p; 15128c2ecf20Sopenharmony_ci bool ret = true; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (rt_is_input_route(rt)) { 15158c2ecf20Sopenharmony_ci p = (struct rtable **)&nhc->nhc_rth_input; 15168c2ecf20Sopenharmony_ci } else { 15178c2ecf20Sopenharmony_ci p = (struct rtable **)raw_cpu_ptr(nhc->nhc_pcpu_rth_output); 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci orig = *p; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* hold dst before doing cmpxchg() to avoid race condition 15228c2ecf20Sopenharmony_ci * on this dst 15238c2ecf20Sopenharmony_ci */ 15248c2ecf20Sopenharmony_ci dst_hold(&rt->dst); 15258c2ecf20Sopenharmony_ci prev = cmpxchg(p, orig, rt); 15268c2ecf20Sopenharmony_ci if (prev == orig) { 15278c2ecf20Sopenharmony_ci if (orig) { 15288c2ecf20Sopenharmony_ci rt_add_uncached_list(orig); 15298c2ecf20Sopenharmony_ci dst_release(&orig->dst); 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci } else { 15328c2ecf20Sopenharmony_ci dst_release(&rt->dst); 15338c2ecf20Sopenharmony_ci ret = false; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci return ret; 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistruct uncached_list { 15408c2ecf20Sopenharmony_ci spinlock_t lock; 15418c2ecf20Sopenharmony_ci struct list_head head; 15428c2ecf20Sopenharmony_ci}; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt_uncached_list); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_civoid rt_add_uncached_list(struct rtable *rt) 15478c2ecf20Sopenharmony_ci{ 15488c2ecf20Sopenharmony_ci struct uncached_list *ul = raw_cpu_ptr(&rt_uncached_list); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci rt->rt_uncached_list = ul; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 15538c2ecf20Sopenharmony_ci list_add_tail(&rt->rt_uncached, &ul->head); 15548c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_civoid rt_del_uncached_list(struct rtable *rt) 15588c2ecf20Sopenharmony_ci{ 15598c2ecf20Sopenharmony_ci if (!list_empty(&rt->rt_uncached)) { 15608c2ecf20Sopenharmony_ci struct uncached_list *ul = rt->rt_uncached_list; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 15638c2ecf20Sopenharmony_ci list_del(&rt->rt_uncached); 15648c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic void ipv4_dst_destroy(struct dst_entry *dst) 15698c2ecf20Sopenharmony_ci{ 15708c2ecf20Sopenharmony_ci struct rtable *rt = (struct rtable *)dst; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci ip_dst_metrics_put(dst); 15738c2ecf20Sopenharmony_ci rt_del_uncached_list(rt); 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_civoid rt_flush_dev(struct net_device *dev) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct rtable *rt; 15798c2ecf20Sopenharmony_ci int cpu; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 15828c2ecf20Sopenharmony_ci struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci spin_lock_bh(&ul->lock); 15858c2ecf20Sopenharmony_ci list_for_each_entry(rt, &ul->head, rt_uncached) { 15868c2ecf20Sopenharmony_ci if (rt->dst.dev != dev) 15878c2ecf20Sopenharmony_ci continue; 15888c2ecf20Sopenharmony_ci rt->dst.dev = blackhole_netdev; 15898c2ecf20Sopenharmony_ci dev_hold(rt->dst.dev); 15908c2ecf20Sopenharmony_ci dev_put(dev); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci spin_unlock_bh(&ul->lock); 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic bool rt_cache_valid(const struct rtable *rt) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci return rt && 15998c2ecf20Sopenharmony_ci rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && 16008c2ecf20Sopenharmony_ci !rt_is_expired(rt); 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic void rt_set_nexthop(struct rtable *rt, __be32 daddr, 16048c2ecf20Sopenharmony_ci const struct fib_result *res, 16058c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe, 16068c2ecf20Sopenharmony_ci struct fib_info *fi, u16 type, u32 itag, 16078c2ecf20Sopenharmony_ci const bool do_cache) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci bool cached = false; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (fi) { 16128c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = FIB_RES_NHC(*res); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci if (nhc->nhc_gw_family && nhc->nhc_scope == RT_SCOPE_LINK) { 16158c2ecf20Sopenharmony_ci rt->rt_uses_gateway = 1; 16168c2ecf20Sopenharmony_ci rt->rt_gw_family = nhc->nhc_gw_family; 16178c2ecf20Sopenharmony_ci /* only INET and INET6 are supported */ 16188c2ecf20Sopenharmony_ci if (likely(nhc->nhc_gw_family == AF_INET)) 16198c2ecf20Sopenharmony_ci rt->rt_gw4 = nhc->nhc_gw.ipv4; 16208c2ecf20Sopenharmony_ci else 16218c2ecf20Sopenharmony_ci rt->rt_gw6 = nhc->nhc_gw.ipv6; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci ip_dst_init_metrics(&rt->dst, fi->fib_metrics); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 16278c2ecf20Sopenharmony_ci if (nhc->nhc_family == AF_INET) { 16288c2ecf20Sopenharmony_ci struct fib_nh *nh; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci nh = container_of(nhc, struct fib_nh, nh_common); 16318c2ecf20Sopenharmony_ci rt->dst.tclassid = nh->nh_tclassid; 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci#endif 16348c2ecf20Sopenharmony_ci rt->dst.lwtstate = lwtstate_get(nhc->nhc_lwtstate); 16358c2ecf20Sopenharmony_ci if (unlikely(fnhe)) 16368c2ecf20Sopenharmony_ci cached = rt_bind_exception(rt, fnhe, daddr, do_cache); 16378c2ecf20Sopenharmony_ci else if (do_cache) 16388c2ecf20Sopenharmony_ci cached = rt_cache_route(nhc, rt); 16398c2ecf20Sopenharmony_ci if (unlikely(!cached)) { 16408c2ecf20Sopenharmony_ci /* Routes we intend to cache in nexthop exception or 16418c2ecf20Sopenharmony_ci * FIB nexthop have the DST_NOCACHE bit clear. 16428c2ecf20Sopenharmony_ci * However, if we are unsuccessful at storing this 16438c2ecf20Sopenharmony_ci * route into the cache we really need to set it. 16448c2ecf20Sopenharmony_ci */ 16458c2ecf20Sopenharmony_ci if (!rt->rt_gw4) { 16468c2ecf20Sopenharmony_ci rt->rt_gw_family = AF_INET; 16478c2ecf20Sopenharmony_ci rt->rt_gw4 = daddr; 16488c2ecf20Sopenharmony_ci } 16498c2ecf20Sopenharmony_ci rt_add_uncached_list(rt); 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci } else 16528c2ecf20Sopenharmony_ci rt_add_uncached_list(rt); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 16558c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTIPLE_TABLES 16568c2ecf20Sopenharmony_ci set_class_tag(rt, res->tclassid); 16578c2ecf20Sopenharmony_ci#endif 16588c2ecf20Sopenharmony_ci set_class_tag(rt, itag); 16598c2ecf20Sopenharmony_ci#endif 16608c2ecf20Sopenharmony_ci} 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cistruct rtable *rt_dst_alloc(struct net_device *dev, 16638c2ecf20Sopenharmony_ci unsigned int flags, u16 type, 16648c2ecf20Sopenharmony_ci bool nopolicy, bool noxfrm) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct rtable *rt; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK, 16698c2ecf20Sopenharmony_ci (nopolicy ? DST_NOPOLICY : 0) | 16708c2ecf20Sopenharmony_ci (noxfrm ? DST_NOXFRM : 0)); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (rt) { 16738c2ecf20Sopenharmony_ci rt->rt_genid = rt_genid_ipv4(dev_net(dev)); 16748c2ecf20Sopenharmony_ci rt->rt_flags = flags; 16758c2ecf20Sopenharmony_ci rt->rt_type = type; 16768c2ecf20Sopenharmony_ci rt->rt_is_input = 0; 16778c2ecf20Sopenharmony_ci rt->rt_iif = 0; 16788c2ecf20Sopenharmony_ci rt->rt_pmtu = 0; 16798c2ecf20Sopenharmony_ci rt->rt_mtu_locked = 0; 16808c2ecf20Sopenharmony_ci rt->rt_uses_gateway = 0; 16818c2ecf20Sopenharmony_ci rt->rt_gw_family = 0; 16828c2ecf20Sopenharmony_ci rt->rt_gw4 = 0; 16838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rt->rt_uncached); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci rt->dst.output = ip_output; 16868c2ecf20Sopenharmony_ci if (flags & RTCF_LOCAL) 16878c2ecf20Sopenharmony_ci rt->dst.input = ip_local_deliver; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci return rt; 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rt_dst_alloc); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistruct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci struct rtable *new_rt; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci new_rt = dst_alloc(&ipv4_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK, 16998c2ecf20Sopenharmony_ci rt->dst.flags); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci if (new_rt) { 17028c2ecf20Sopenharmony_ci new_rt->rt_genid = rt_genid_ipv4(dev_net(dev)); 17038c2ecf20Sopenharmony_ci new_rt->rt_flags = rt->rt_flags; 17048c2ecf20Sopenharmony_ci new_rt->rt_type = rt->rt_type; 17058c2ecf20Sopenharmony_ci new_rt->rt_is_input = rt->rt_is_input; 17068c2ecf20Sopenharmony_ci new_rt->rt_iif = rt->rt_iif; 17078c2ecf20Sopenharmony_ci new_rt->rt_pmtu = rt->rt_pmtu; 17088c2ecf20Sopenharmony_ci new_rt->rt_mtu_locked = rt->rt_mtu_locked; 17098c2ecf20Sopenharmony_ci new_rt->rt_gw_family = rt->rt_gw_family; 17108c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET) 17118c2ecf20Sopenharmony_ci new_rt->rt_gw4 = rt->rt_gw4; 17128c2ecf20Sopenharmony_ci else if (rt->rt_gw_family == AF_INET6) 17138c2ecf20Sopenharmony_ci new_rt->rt_gw6 = rt->rt_gw6; 17148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&new_rt->rt_uncached); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci new_rt->dst.input = rt->dst.input; 17178c2ecf20Sopenharmony_ci new_rt->dst.output = rt->dst.output; 17188c2ecf20Sopenharmony_ci new_rt->dst.error = rt->dst.error; 17198c2ecf20Sopenharmony_ci new_rt->dst.lastuse = jiffies; 17208c2ecf20Sopenharmony_ci new_rt->dst.lwtstate = lwtstate_get(rt->dst.lwtstate); 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci return new_rt; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rt_dst_clone); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci/* called in rcu_read_lock() section */ 17278c2ecf20Sopenharmony_ciint ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr, 17288c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev, 17298c2ecf20Sopenharmony_ci struct in_device *in_dev, u32 *itag) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci int err; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* Primary sanity checks. */ 17348c2ecf20Sopenharmony_ci if (!in_dev) 17358c2ecf20Sopenharmony_ci return -EINVAL; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) || 17388c2ecf20Sopenharmony_ci skb->protocol != htons(ETH_P_IP)) 17398c2ecf20Sopenharmony_ci return -EINVAL; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev)) 17428c2ecf20Sopenharmony_ci return -EINVAL; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (ipv4_is_zeronet(saddr)) { 17458c2ecf20Sopenharmony_ci if (!ipv4_is_local_multicast(daddr) && 17468c2ecf20Sopenharmony_ci ip_hdr(skb)->protocol != IPPROTO_IGMP) 17478c2ecf20Sopenharmony_ci return -EINVAL; 17488c2ecf20Sopenharmony_ci } else { 17498c2ecf20Sopenharmony_ci err = fib_validate_source(skb, saddr, 0, tos, 0, dev, 17508c2ecf20Sopenharmony_ci in_dev, itag); 17518c2ecf20Sopenharmony_ci if (err < 0) 17528c2ecf20Sopenharmony_ci return err; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci return 0; 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci/* called in rcu_read_lock() section */ 17588c2ecf20Sopenharmony_cistatic int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, 17598c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev, int our) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 17628c2ecf20Sopenharmony_ci unsigned int flags = RTCF_MULTICAST; 17638c2ecf20Sopenharmony_ci struct rtable *rth; 17648c2ecf20Sopenharmony_ci bool no_policy; 17658c2ecf20Sopenharmony_ci u32 itag = 0; 17668c2ecf20Sopenharmony_ci int err; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci err = ip_mc_validate_source(skb, daddr, saddr, tos, dev, in_dev, &itag); 17698c2ecf20Sopenharmony_ci if (err) 17708c2ecf20Sopenharmony_ci return err; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (our) 17738c2ecf20Sopenharmony_ci flags |= RTCF_LOCAL; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); 17768c2ecf20Sopenharmony_ci if (no_policy) 17778c2ecf20Sopenharmony_ci IPCB(skb)->flags |= IPSKB_NOPOLICY; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST, 17808c2ecf20Sopenharmony_ci no_policy, false); 17818c2ecf20Sopenharmony_ci if (!rth) 17828c2ecf20Sopenharmony_ci return -ENOBUFS; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 17858c2ecf20Sopenharmony_ci rth->dst.tclassid = itag; 17868c2ecf20Sopenharmony_ci#endif 17878c2ecf20Sopenharmony_ci rth->dst.output = ip_rt_bug; 17888c2ecf20Sopenharmony_ci rth->rt_is_input= 1; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MROUTE 17918c2ecf20Sopenharmony_ci if (!ipv4_is_local_multicast(daddr) && IN_DEV_MFORWARD(in_dev)) 17928c2ecf20Sopenharmony_ci rth->dst.input = ip_mr_input; 17938c2ecf20Sopenharmony_ci#endif 17948c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_slow_mc); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci skb_dst_drop(skb); 17978c2ecf20Sopenharmony_ci skb_dst_set(skb, &rth->dst); 17988c2ecf20Sopenharmony_ci return 0; 17998c2ecf20Sopenharmony_ci} 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_cistatic void ip_handle_martian_source(struct net_device *dev, 18038c2ecf20Sopenharmony_ci struct in_device *in_dev, 18048c2ecf20Sopenharmony_ci struct sk_buff *skb, 18058c2ecf20Sopenharmony_ci __be32 daddr, 18068c2ecf20Sopenharmony_ci __be32 saddr) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_martian_src); 18098c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_VERBOSE 18108c2ecf20Sopenharmony_ci if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit()) { 18118c2ecf20Sopenharmony_ci /* 18128c2ecf20Sopenharmony_ci * RFC1812 recommendation, if source is martian, 18138c2ecf20Sopenharmony_ci * the only hint is MAC header. 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci pr_warn("martian source %pI4 from %pI4, on dev %s\n", 18168c2ecf20Sopenharmony_ci &daddr, &saddr, dev->name); 18178c2ecf20Sopenharmony_ci if (dev->hard_header_len && skb_mac_header_was_set(skb)) { 18188c2ecf20Sopenharmony_ci print_hex_dump(KERN_WARNING, "ll header: ", 18198c2ecf20Sopenharmony_ci DUMP_PREFIX_OFFSET, 16, 1, 18208c2ecf20Sopenharmony_ci skb_mac_header(skb), 18218c2ecf20Sopenharmony_ci dev->hard_header_len, false); 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci#endif 18258c2ecf20Sopenharmony_ci} 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci/* called in rcu_read_lock() section */ 18288c2ecf20Sopenharmony_cistatic int __mkroute_input(struct sk_buff *skb, 18298c2ecf20Sopenharmony_ci const struct fib_result *res, 18308c2ecf20Sopenharmony_ci struct in_device *in_dev, 18318c2ecf20Sopenharmony_ci __be32 daddr, __be32 saddr, u32 tos) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = FIB_RES_NHC(*res); 18348c2ecf20Sopenharmony_ci struct net_device *dev = nhc->nhc_dev; 18358c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 18368c2ecf20Sopenharmony_ci struct rtable *rth; 18378c2ecf20Sopenharmony_ci int err; 18388c2ecf20Sopenharmony_ci struct in_device *out_dev; 18398c2ecf20Sopenharmony_ci bool do_cache, no_policy; 18408c2ecf20Sopenharmony_ci u32 itag = 0; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci /* get a working reference to the output device */ 18438c2ecf20Sopenharmony_ci out_dev = __in_dev_get_rcu(dev); 18448c2ecf20Sopenharmony_ci if (!out_dev) { 18458c2ecf20Sopenharmony_ci net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n"); 18468c2ecf20Sopenharmony_ci return -EINVAL; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res), 18508c2ecf20Sopenharmony_ci in_dev->dev, in_dev, &itag); 18518c2ecf20Sopenharmony_ci if (err < 0) { 18528c2ecf20Sopenharmony_ci ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr, 18538c2ecf20Sopenharmony_ci saddr); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci goto cleanup; 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci do_cache = res->fi && !itag; 18598c2ecf20Sopenharmony_ci if (out_dev == in_dev && err && IN_DEV_TX_REDIRECTS(out_dev) && 18608c2ecf20Sopenharmony_ci skb->protocol == htons(ETH_P_IP)) { 18618c2ecf20Sopenharmony_ci __be32 gw; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci gw = nhc->nhc_gw_family == AF_INET ? nhc->nhc_gw.ipv4 : 0; 18648c2ecf20Sopenharmony_ci if (IN_DEV_SHARED_MEDIA(out_dev) || 18658c2ecf20Sopenharmony_ci inet_addr_onlink(out_dev, saddr, gw)) 18668c2ecf20Sopenharmony_ci IPCB(skb)->flags |= IPSKB_DOREDIRECT; 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci if (skb->protocol != htons(ETH_P_IP)) { 18708c2ecf20Sopenharmony_ci /* Not IP (i.e. ARP). Do not create route, if it is 18718c2ecf20Sopenharmony_ci * invalid for proxy arp. DNAT routes are always valid. 18728c2ecf20Sopenharmony_ci * 18738c2ecf20Sopenharmony_ci * Proxy arp feature have been extended to allow, ARP 18748c2ecf20Sopenharmony_ci * replies back to the same interface, to support 18758c2ecf20Sopenharmony_ci * Private VLAN switch technologies. See arp.c. 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_ci if (out_dev == in_dev && 18788c2ecf20Sopenharmony_ci IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) { 18798c2ecf20Sopenharmony_ci err = -EINVAL; 18808c2ecf20Sopenharmony_ci goto cleanup; 18818c2ecf20Sopenharmony_ci } 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); 18858c2ecf20Sopenharmony_ci if (no_policy) 18868c2ecf20Sopenharmony_ci IPCB(skb)->flags |= IPSKB_NOPOLICY; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci fnhe = find_exception(nhc, daddr); 18898c2ecf20Sopenharmony_ci if (do_cache) { 18908c2ecf20Sopenharmony_ci if (fnhe) 18918c2ecf20Sopenharmony_ci rth = rcu_dereference(fnhe->fnhe_rth_input); 18928c2ecf20Sopenharmony_ci else 18938c2ecf20Sopenharmony_ci rth = rcu_dereference(nhc->nhc_rth_input); 18948c2ecf20Sopenharmony_ci if (rt_cache_valid(rth)) { 18958c2ecf20Sopenharmony_ci skb_dst_set_noref(skb, &rth->dst); 18968c2ecf20Sopenharmony_ci goto out; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci rth = rt_dst_alloc(out_dev->dev, 0, res->type, no_policy, 19018c2ecf20Sopenharmony_ci IN_DEV_ORCONF(out_dev, NOXFRM)); 19028c2ecf20Sopenharmony_ci if (!rth) { 19038c2ecf20Sopenharmony_ci err = -ENOBUFS; 19048c2ecf20Sopenharmony_ci goto cleanup; 19058c2ecf20Sopenharmony_ci } 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci rth->rt_is_input = 1; 19088c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_slow_tot); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci rth->dst.input = ip_forward; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag, 19138c2ecf20Sopenharmony_ci do_cache); 19148c2ecf20Sopenharmony_ci lwtunnel_set_redirect(&rth->dst); 19158c2ecf20Sopenharmony_ci skb_dst_set(skb, &rth->dst); 19168c2ecf20Sopenharmony_ciout: 19178c2ecf20Sopenharmony_ci err = 0; 19188c2ecf20Sopenharmony_ci cleanup: 19198c2ecf20Sopenharmony_ci return err; 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 19238c2ecf20Sopenharmony_ci/* To make ICMP packets follow the right flow, the multipath hash is 19248c2ecf20Sopenharmony_ci * calculated from the inner IP addresses. 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_cistatic void ip_multipath_l3_keys(const struct sk_buff *skb, 19278c2ecf20Sopenharmony_ci struct flow_keys *hash_keys) 19288c2ecf20Sopenharmony_ci{ 19298c2ecf20Sopenharmony_ci const struct iphdr *outer_iph = ip_hdr(skb); 19308c2ecf20Sopenharmony_ci const struct iphdr *key_iph = outer_iph; 19318c2ecf20Sopenharmony_ci const struct iphdr *inner_iph; 19328c2ecf20Sopenharmony_ci const struct icmphdr *icmph; 19338c2ecf20Sopenharmony_ci struct iphdr _inner_iph; 19348c2ecf20Sopenharmony_ci struct icmphdr _icmph; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (likely(outer_iph->protocol != IPPROTO_ICMP)) 19378c2ecf20Sopenharmony_ci goto out; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0)) 19408c2ecf20Sopenharmony_ci goto out; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph), 19438c2ecf20Sopenharmony_ci &_icmph); 19448c2ecf20Sopenharmony_ci if (!icmph) 19458c2ecf20Sopenharmony_ci goto out; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (!icmp_is_err(icmph->type)) 19488c2ecf20Sopenharmony_ci goto out; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci inner_iph = skb_header_pointer(skb, 19518c2ecf20Sopenharmony_ci outer_iph->ihl * 4 + sizeof(_icmph), 19528c2ecf20Sopenharmony_ci sizeof(_inner_iph), &_inner_iph); 19538c2ecf20Sopenharmony_ci if (!inner_iph) 19548c2ecf20Sopenharmony_ci goto out; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci key_iph = inner_iph; 19578c2ecf20Sopenharmony_ciout: 19588c2ecf20Sopenharmony_ci hash_keys->addrs.v4addrs.src = key_iph->saddr; 19598c2ecf20Sopenharmony_ci hash_keys->addrs.v4addrs.dst = key_iph->daddr; 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci/* if skb is set it will be used and fl4 can be NULL */ 19638c2ecf20Sopenharmony_ciint fib_multipath_hash(const struct net *net, const struct flowi4 *fl4, 19648c2ecf20Sopenharmony_ci const struct sk_buff *skb, struct flow_keys *flkeys) 19658c2ecf20Sopenharmony_ci{ 19668c2ecf20Sopenharmony_ci u32 multipath_hash = fl4 ? fl4->flowi4_multipath_hash : 0; 19678c2ecf20Sopenharmony_ci struct flow_keys hash_keys; 19688c2ecf20Sopenharmony_ci u32 mhash; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci switch (net->ipv4.sysctl_fib_multipath_hash_policy) { 19718c2ecf20Sopenharmony_ci case 0: 19728c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 19738c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 19748c2ecf20Sopenharmony_ci if (skb) { 19758c2ecf20Sopenharmony_ci ip_multipath_l3_keys(skb, &hash_keys); 19768c2ecf20Sopenharmony_ci } else { 19778c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = fl4->saddr; 19788c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = fl4->daddr; 19798c2ecf20Sopenharmony_ci } 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci case 1: 19828c2ecf20Sopenharmony_ci /* skb is currently provided only when forwarding */ 19838c2ecf20Sopenharmony_ci if (skb) { 19848c2ecf20Sopenharmony_ci unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; 19858c2ecf20Sopenharmony_ci struct flow_keys keys; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* short-circuit if we already have L4 hash present */ 19888c2ecf20Sopenharmony_ci if (skb->l4_hash) 19898c2ecf20Sopenharmony_ci return skb_get_hash_raw(skb) >> 1; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci if (!flkeys) { 19948c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, flag); 19958c2ecf20Sopenharmony_ci flkeys = &keys; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 19998c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src; 20008c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst; 20018c2ecf20Sopenharmony_ci hash_keys.ports.src = flkeys->ports.src; 20028c2ecf20Sopenharmony_ci hash_keys.ports.dst = flkeys->ports.dst; 20038c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = flkeys->basic.ip_proto; 20048c2ecf20Sopenharmony_ci } else { 20058c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 20068c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 20078c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = fl4->saddr; 20088c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = fl4->daddr; 20098c2ecf20Sopenharmony_ci hash_keys.ports.src = fl4->fl4_sport; 20108c2ecf20Sopenharmony_ci hash_keys.ports.dst = fl4->fl4_dport; 20118c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = fl4->flowi4_proto; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci break; 20148c2ecf20Sopenharmony_ci case 2: 20158c2ecf20Sopenharmony_ci memset(&hash_keys, 0, sizeof(hash_keys)); 20168c2ecf20Sopenharmony_ci /* skb is currently provided only when forwarding */ 20178c2ecf20Sopenharmony_ci if (skb) { 20188c2ecf20Sopenharmony_ci struct flow_keys keys; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &keys, 0); 20218c2ecf20Sopenharmony_ci /* Inner can be v4 or v6 */ 20228c2ecf20Sopenharmony_ci if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 20238c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 20248c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; 20258c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; 20268c2ecf20Sopenharmony_ci } else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) { 20278c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 20288c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src; 20298c2ecf20Sopenharmony_ci hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst; 20308c2ecf20Sopenharmony_ci hash_keys.tags.flow_label = keys.tags.flow_label; 20318c2ecf20Sopenharmony_ci hash_keys.basic.ip_proto = keys.basic.ip_proto; 20328c2ecf20Sopenharmony_ci } else { 20338c2ecf20Sopenharmony_ci /* Same as case 0 */ 20348c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 20358c2ecf20Sopenharmony_ci ip_multipath_l3_keys(skb, &hash_keys); 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci } else { 20388c2ecf20Sopenharmony_ci /* Same as case 0 */ 20398c2ecf20Sopenharmony_ci hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 20408c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.src = fl4->saddr; 20418c2ecf20Sopenharmony_ci hash_keys.addrs.v4addrs.dst = fl4->daddr; 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci break; 20448c2ecf20Sopenharmony_ci } 20458c2ecf20Sopenharmony_ci mhash = flow_hash_from_keys(&hash_keys); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci if (multipath_hash) 20488c2ecf20Sopenharmony_ci mhash = jhash_2words(mhash, multipath_hash, 0); 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci return mhash >> 1; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_ROUTE_MULTIPATH */ 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_cistatic int ip_mkroute_input(struct sk_buff *skb, 20558c2ecf20Sopenharmony_ci struct fib_result *res, 20568c2ecf20Sopenharmony_ci struct in_device *in_dev, 20578c2ecf20Sopenharmony_ci __be32 daddr, __be32 saddr, u32 tos, 20588c2ecf20Sopenharmony_ci struct flow_keys *hkeys) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_MULTIPATH 20618c2ecf20Sopenharmony_ci if (res->fi && fib_info_num_path(res->fi) > 1) { 20628c2ecf20Sopenharmony_ci int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys); 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci fib_select_multipath(res, h); 20658c2ecf20Sopenharmony_ci IPCB(skb)->flags |= IPSKB_MULTIPATH; 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci#endif 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci /* create a routing cache entry */ 20708c2ecf20Sopenharmony_ci return __mkroute_input(skb, res, in_dev, daddr, saddr, tos); 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci/* Implements all the saddr-related checks as ip_route_input_slow(), 20748c2ecf20Sopenharmony_ci * assuming daddr is valid and the destination is not a local broadcast one. 20758c2ecf20Sopenharmony_ci * Uses the provided hint instead of performing a route lookup. 20768c2ecf20Sopenharmony_ci */ 20778c2ecf20Sopenharmony_ciint ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr, 20788c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev, 20798c2ecf20Sopenharmony_ci const struct sk_buff *hint) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 20828c2ecf20Sopenharmony_ci struct rtable *rt = skb_rtable(hint); 20838c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 20848c2ecf20Sopenharmony_ci int err = -EINVAL; 20858c2ecf20Sopenharmony_ci u32 tag = 0; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci if (!in_dev) 20888c2ecf20Sopenharmony_ci return -EINVAL; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) 20918c2ecf20Sopenharmony_ci goto martian_source; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (ipv4_is_zeronet(saddr)) 20948c2ecf20Sopenharmony_ci goto martian_source; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci if (ipv4_is_loopback(saddr) && !IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) 20978c2ecf20Sopenharmony_ci goto martian_source; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci if (rt->rt_type != RTN_LOCAL) 21008c2ecf20Sopenharmony_ci goto skip_validate_source; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci tos &= IPTOS_RT_MASK; 21038c2ecf20Sopenharmony_ci err = fib_validate_source(skb, saddr, daddr, tos, 0, dev, in_dev, &tag); 21048c2ecf20Sopenharmony_ci if (err < 0) 21058c2ecf20Sopenharmony_ci goto martian_source; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ciskip_validate_source: 21088c2ecf20Sopenharmony_ci skb_dst_copy(skb, hint); 21098c2ecf20Sopenharmony_ci return 0; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_cimartian_source: 21128c2ecf20Sopenharmony_ci ip_handle_martian_source(dev, in_dev, skb, daddr, saddr); 21138c2ecf20Sopenharmony_ci return err; 21148c2ecf20Sopenharmony_ci} 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci/* get device for dst_alloc with local routes */ 21178c2ecf20Sopenharmony_cistatic struct net_device *ip_rt_get_dev(struct net *net, 21188c2ecf20Sopenharmony_ci const struct fib_result *res) 21198c2ecf20Sopenharmony_ci{ 21208c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = res->fi ? res->nhc : NULL; 21218c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci if (nhc) 21248c2ecf20Sopenharmony_ci dev = l3mdev_master_dev_rcu(nhc->nhc_dev); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci return dev ? : net->loopback_dev; 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci/* 21308c2ecf20Sopenharmony_ci * NOTE. We drop all the packets that has local source 21318c2ecf20Sopenharmony_ci * addresses, because every properly looped back packet 21328c2ecf20Sopenharmony_ci * must have correct destination already attached by output routine. 21338c2ecf20Sopenharmony_ci * Changes in the enforced policies must be applied also to 21348c2ecf20Sopenharmony_ci * ip_route_use_hint(). 21358c2ecf20Sopenharmony_ci * 21368c2ecf20Sopenharmony_ci * Such approach solves two big problems: 21378c2ecf20Sopenharmony_ci * 1. Not simplex devices are handled properly. 21388c2ecf20Sopenharmony_ci * 2. IP spoofing attempts are filtered with 100% of guarantee. 21398c2ecf20Sopenharmony_ci * called with rcu_read_lock() 21408c2ecf20Sopenharmony_ci */ 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_cistatic int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, 21438c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev, 21448c2ecf20Sopenharmony_ci struct fib_result *res) 21458c2ecf20Sopenharmony_ci{ 21468c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 21478c2ecf20Sopenharmony_ci struct flow_keys *flkeys = NULL, _flkeys; 21488c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 21498c2ecf20Sopenharmony_ci struct ip_tunnel_info *tun_info; 21508c2ecf20Sopenharmony_ci int err = -EINVAL; 21518c2ecf20Sopenharmony_ci unsigned int flags = 0; 21528c2ecf20Sopenharmony_ci u32 itag = 0; 21538c2ecf20Sopenharmony_ci struct rtable *rth; 21548c2ecf20Sopenharmony_ci struct flowi4 fl4; 21558c2ecf20Sopenharmony_ci bool do_cache = true; 21568c2ecf20Sopenharmony_ci bool no_policy; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci /* IP on this device is disabled. */ 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (!in_dev) 21618c2ecf20Sopenharmony_ci goto out; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* Check for the most weird martians, which can be not detected 21648c2ecf20Sopenharmony_ci by fib_lookup. 21658c2ecf20Sopenharmony_ci */ 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci tun_info = skb_tunnel_info(skb); 21688c2ecf20Sopenharmony_ci if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) 21698c2ecf20Sopenharmony_ci fl4.flowi4_tun_key.tun_id = tun_info->key.tun_id; 21708c2ecf20Sopenharmony_ci else 21718c2ecf20Sopenharmony_ci fl4.flowi4_tun_key.tun_id = 0; 21728c2ecf20Sopenharmony_ci skb_dst_drop(skb); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) 21758c2ecf20Sopenharmony_ci goto martian_source; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci res->fi = NULL; 21788c2ecf20Sopenharmony_ci res->table = NULL; 21798c2ecf20Sopenharmony_ci if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0)) 21808c2ecf20Sopenharmony_ci goto brd_input; 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ci /* Accept zero addresses only to limited broadcast; 21838c2ecf20Sopenharmony_ci * I even do not know to fix it or not. Waiting for complains :-) 21848c2ecf20Sopenharmony_ci */ 21858c2ecf20Sopenharmony_ci if (ipv4_is_zeronet(saddr)) 21868c2ecf20Sopenharmony_ci goto martian_source; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci if (ipv4_is_zeronet(daddr)) 21898c2ecf20Sopenharmony_ci goto martian_destination; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci /* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(), 21928c2ecf20Sopenharmony_ci * and call it once if daddr or/and saddr are loopback addresses 21938c2ecf20Sopenharmony_ci */ 21948c2ecf20Sopenharmony_ci if (ipv4_is_loopback(daddr)) { 21958c2ecf20Sopenharmony_ci if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) 21968c2ecf20Sopenharmony_ci goto martian_destination; 21978c2ecf20Sopenharmony_ci } else if (ipv4_is_loopback(saddr)) { 21988c2ecf20Sopenharmony_ci if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) 21998c2ecf20Sopenharmony_ci goto martian_source; 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci /* 22038c2ecf20Sopenharmony_ci * Now we are ready to route packet. 22048c2ecf20Sopenharmony_ci */ 22058c2ecf20Sopenharmony_ci fl4.flowi4_oif = 0; 22068c2ecf20Sopenharmony_ci fl4.flowi4_iif = dev->ifindex; 22078c2ecf20Sopenharmony_ci fl4.flowi4_mark = skb->mark; 22088c2ecf20Sopenharmony_ci fl4.flowi4_tos = tos; 22098c2ecf20Sopenharmony_ci fl4.flowi4_scope = RT_SCOPE_UNIVERSE; 22108c2ecf20Sopenharmony_ci fl4.flowi4_flags = 0; 22118c2ecf20Sopenharmony_ci fl4.daddr = daddr; 22128c2ecf20Sopenharmony_ci fl4.saddr = saddr; 22138c2ecf20Sopenharmony_ci fl4.flowi4_uid = sock_net_uid(net, NULL); 22148c2ecf20Sopenharmony_ci fl4.flowi4_multipath_hash = 0; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys)) { 22178c2ecf20Sopenharmony_ci flkeys = &_flkeys; 22188c2ecf20Sopenharmony_ci } else { 22198c2ecf20Sopenharmony_ci fl4.flowi4_proto = 0; 22208c2ecf20Sopenharmony_ci fl4.fl4_sport = 0; 22218c2ecf20Sopenharmony_ci fl4.fl4_dport = 0; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci err = fib_lookup(net, &fl4, res, 0); 22258c2ecf20Sopenharmony_ci if (err != 0) { 22268c2ecf20Sopenharmony_ci if (!IN_DEV_FORWARD(in_dev)) 22278c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 22288c2ecf20Sopenharmony_ci goto no_route; 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci if (res->type == RTN_BROADCAST) { 22328c2ecf20Sopenharmony_ci if (IN_DEV_BFORWARD(in_dev)) 22338c2ecf20Sopenharmony_ci goto make_route; 22348c2ecf20Sopenharmony_ci /* not do cache if bc_forwarding is enabled */ 22358c2ecf20Sopenharmony_ci if (IPV4_DEVCONF_ALL(net, BC_FORWARDING)) 22368c2ecf20Sopenharmony_ci do_cache = false; 22378c2ecf20Sopenharmony_ci goto brd_input; 22388c2ecf20Sopenharmony_ci } 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci if (res->type == RTN_LOCAL) { 22418c2ecf20Sopenharmony_ci err = fib_validate_source(skb, saddr, daddr, tos, 22428c2ecf20Sopenharmony_ci 0, dev, in_dev, &itag); 22438c2ecf20Sopenharmony_ci if (err < 0) 22448c2ecf20Sopenharmony_ci goto martian_source; 22458c2ecf20Sopenharmony_ci goto local_input; 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (!IN_DEV_FORWARD(in_dev)) { 22498c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 22508c2ecf20Sopenharmony_ci goto no_route; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci if (res->type != RTN_UNICAST) 22538c2ecf20Sopenharmony_ci goto martian_destination; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cimake_route: 22568c2ecf20Sopenharmony_ci err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos, flkeys); 22578c2ecf20Sopenharmony_ciout: return err; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cibrd_input: 22608c2ecf20Sopenharmony_ci if (skb->protocol != htons(ETH_P_IP)) 22618c2ecf20Sopenharmony_ci goto e_inval; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci if (!ipv4_is_zeronet(saddr)) { 22648c2ecf20Sopenharmony_ci err = fib_validate_source(skb, saddr, 0, tos, 0, dev, 22658c2ecf20Sopenharmony_ci in_dev, &itag); 22668c2ecf20Sopenharmony_ci if (err < 0) 22678c2ecf20Sopenharmony_ci goto martian_source; 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci flags |= RTCF_BROADCAST; 22708c2ecf20Sopenharmony_ci res->type = RTN_BROADCAST; 22718c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_brd); 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_cilocal_input: 22748c2ecf20Sopenharmony_ci no_policy = IN_DEV_ORCONF(in_dev, NOPOLICY); 22758c2ecf20Sopenharmony_ci if (no_policy) 22768c2ecf20Sopenharmony_ci IPCB(skb)->flags |= IPSKB_NOPOLICY; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci do_cache &= res->fi && !itag; 22798c2ecf20Sopenharmony_ci if (do_cache) { 22808c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = FIB_RES_NHC(*res); 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci rth = rcu_dereference(nhc->nhc_rth_input); 22838c2ecf20Sopenharmony_ci if (rt_cache_valid(rth)) { 22848c2ecf20Sopenharmony_ci skb_dst_set_noref(skb, &rth->dst); 22858c2ecf20Sopenharmony_ci err = 0; 22868c2ecf20Sopenharmony_ci goto out; 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci rth = rt_dst_alloc(ip_rt_get_dev(net, res), 22918c2ecf20Sopenharmony_ci flags | RTCF_LOCAL, res->type, 22928c2ecf20Sopenharmony_ci no_policy, false); 22938c2ecf20Sopenharmony_ci if (!rth) 22948c2ecf20Sopenharmony_ci goto e_nobufs; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci rth->dst.output= ip_rt_bug; 22978c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 22988c2ecf20Sopenharmony_ci rth->dst.tclassid = itag; 22998c2ecf20Sopenharmony_ci#endif 23008c2ecf20Sopenharmony_ci rth->rt_is_input = 1; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_slow_tot); 23038c2ecf20Sopenharmony_ci if (res->type == RTN_UNREACHABLE) { 23048c2ecf20Sopenharmony_ci rth->dst.input= ip_error; 23058c2ecf20Sopenharmony_ci rth->dst.error= -err; 23068c2ecf20Sopenharmony_ci rth->rt_flags &= ~RTCF_LOCAL; 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci if (do_cache) { 23108c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = FIB_RES_NHC(*res); 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci rth->dst.lwtstate = lwtstate_get(nhc->nhc_lwtstate); 23138c2ecf20Sopenharmony_ci if (lwtunnel_input_redirect(rth->dst.lwtstate)) { 23148c2ecf20Sopenharmony_ci WARN_ON(rth->dst.input == lwtunnel_input); 23158c2ecf20Sopenharmony_ci rth->dst.lwtstate->orig_input = rth->dst.input; 23168c2ecf20Sopenharmony_ci rth->dst.input = lwtunnel_input; 23178c2ecf20Sopenharmony_ci } 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (unlikely(!rt_cache_route(nhc, rth))) 23208c2ecf20Sopenharmony_ci rt_add_uncached_list(rth); 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci skb_dst_set(skb, &rth->dst); 23238c2ecf20Sopenharmony_ci err = 0; 23248c2ecf20Sopenharmony_ci goto out; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_cino_route: 23278c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_no_route); 23288c2ecf20Sopenharmony_ci res->type = RTN_UNREACHABLE; 23298c2ecf20Sopenharmony_ci res->fi = NULL; 23308c2ecf20Sopenharmony_ci res->table = NULL; 23318c2ecf20Sopenharmony_ci goto local_input; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci /* 23348c2ecf20Sopenharmony_ci * Do not cache martian addresses: they should be logged (RFC1812) 23358c2ecf20Sopenharmony_ci */ 23368c2ecf20Sopenharmony_cimartian_destination: 23378c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(in_martian_dst); 23388c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_VERBOSE 23398c2ecf20Sopenharmony_ci if (IN_DEV_LOG_MARTIANS(in_dev)) 23408c2ecf20Sopenharmony_ci net_warn_ratelimited("martian destination %pI4 from %pI4, dev %s\n", 23418c2ecf20Sopenharmony_ci &daddr, &saddr, dev->name); 23428c2ecf20Sopenharmony_ci#endif 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_cie_inval: 23458c2ecf20Sopenharmony_ci err = -EINVAL; 23468c2ecf20Sopenharmony_ci goto out; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_cie_nobufs: 23498c2ecf20Sopenharmony_ci err = -ENOBUFS; 23508c2ecf20Sopenharmony_ci goto out; 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_cimartian_source: 23538c2ecf20Sopenharmony_ci ip_handle_martian_source(dev, in_dev, skb, daddr, saddr); 23548c2ecf20Sopenharmony_ci goto out; 23558c2ecf20Sopenharmony_ci} 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ciint ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr, 23588c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev) 23598c2ecf20Sopenharmony_ci{ 23608c2ecf20Sopenharmony_ci struct fib_result res; 23618c2ecf20Sopenharmony_ci int err; 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci tos &= IPTOS_RT_MASK; 23648c2ecf20Sopenharmony_ci rcu_read_lock(); 23658c2ecf20Sopenharmony_ci err = ip_route_input_rcu(skb, daddr, saddr, tos, dev, &res); 23668c2ecf20Sopenharmony_ci rcu_read_unlock(); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci return err; 23698c2ecf20Sopenharmony_ci} 23708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip_route_input_noref); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci/* called with rcu_read_lock held */ 23738c2ecf20Sopenharmony_ciint ip_route_input_rcu(struct sk_buff *skb, __be32 daddr, __be32 saddr, 23748c2ecf20Sopenharmony_ci u8 tos, struct net_device *dev, struct fib_result *res) 23758c2ecf20Sopenharmony_ci{ 23768c2ecf20Sopenharmony_ci /* Multicast recognition logic is moved from route cache to here. 23778c2ecf20Sopenharmony_ci The problem was that too many Ethernet cards have broken/missing 23788c2ecf20Sopenharmony_ci hardware multicast filters :-( As result the host on multicasting 23798c2ecf20Sopenharmony_ci network acquires a lot of useless route cache entries, sort of 23808c2ecf20Sopenharmony_ci SDR messages from all the world. Now we try to get rid of them. 23818c2ecf20Sopenharmony_ci Really, provided software IP multicast filter is organized 23828c2ecf20Sopenharmony_ci reasonably (at least, hashed), it does not result in a slowdown 23838c2ecf20Sopenharmony_ci comparing with route cache reject entries. 23848c2ecf20Sopenharmony_ci Note, that multicast routers are not affected, because 23858c2ecf20Sopenharmony_ci route cache entry is created eventually. 23868c2ecf20Sopenharmony_ci */ 23878c2ecf20Sopenharmony_ci if (ipv4_is_multicast(daddr)) { 23888c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rcu(dev); 23898c2ecf20Sopenharmony_ci int our = 0; 23908c2ecf20Sopenharmony_ci int err = -EINVAL; 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (!in_dev) 23938c2ecf20Sopenharmony_ci return err; 23948c2ecf20Sopenharmony_ci our = ip_check_mc_rcu(in_dev, daddr, saddr, 23958c2ecf20Sopenharmony_ci ip_hdr(skb)->protocol); 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci /* check l3 master if no match yet */ 23988c2ecf20Sopenharmony_ci if (!our && netif_is_l3_slave(dev)) { 23998c2ecf20Sopenharmony_ci struct in_device *l3_in_dev; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci l3_in_dev = __in_dev_get_rcu(skb->dev); 24028c2ecf20Sopenharmony_ci if (l3_in_dev) 24038c2ecf20Sopenharmony_ci our = ip_check_mc_rcu(l3_in_dev, daddr, saddr, 24048c2ecf20Sopenharmony_ci ip_hdr(skb)->protocol); 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci if (our 24088c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MROUTE 24098c2ecf20Sopenharmony_ci || 24108c2ecf20Sopenharmony_ci (!ipv4_is_local_multicast(daddr) && 24118c2ecf20Sopenharmony_ci IN_DEV_MFORWARD(in_dev)) 24128c2ecf20Sopenharmony_ci#endif 24138c2ecf20Sopenharmony_ci ) { 24148c2ecf20Sopenharmony_ci err = ip_route_input_mc(skb, daddr, saddr, 24158c2ecf20Sopenharmony_ci tos, dev, our); 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci return err; 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci return ip_route_input_slow(skb, daddr, saddr, tos, dev, res); 24218c2ecf20Sopenharmony_ci} 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 24248c2ecf20Sopenharmony_cistatic struct rtable *__mkroute_output(const struct fib_result *res, 24258c2ecf20Sopenharmony_ci const struct flowi4 *fl4, int orig_oif, 24268c2ecf20Sopenharmony_ci struct net_device *dev_out, 24278c2ecf20Sopenharmony_ci unsigned int flags) 24288c2ecf20Sopenharmony_ci{ 24298c2ecf20Sopenharmony_ci struct fib_info *fi = res->fi; 24308c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 24318c2ecf20Sopenharmony_ci struct in_device *in_dev; 24328c2ecf20Sopenharmony_ci u16 type = res->type; 24338c2ecf20Sopenharmony_ci struct rtable *rth; 24348c2ecf20Sopenharmony_ci bool do_cache; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci in_dev = __in_dev_get_rcu(dev_out); 24378c2ecf20Sopenharmony_ci if (!in_dev) 24388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci if (likely(!IN_DEV_ROUTE_LOCALNET(in_dev))) 24418c2ecf20Sopenharmony_ci if (ipv4_is_loopback(fl4->saddr) && 24428c2ecf20Sopenharmony_ci !(dev_out->flags & IFF_LOOPBACK) && 24438c2ecf20Sopenharmony_ci !netif_is_l3_master(dev_out)) 24448c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci if (ipv4_is_lbcast(fl4->daddr)) 24478c2ecf20Sopenharmony_ci type = RTN_BROADCAST; 24488c2ecf20Sopenharmony_ci else if (ipv4_is_multicast(fl4->daddr)) 24498c2ecf20Sopenharmony_ci type = RTN_MULTICAST; 24508c2ecf20Sopenharmony_ci else if (ipv4_is_zeronet(fl4->daddr)) 24518c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci if (dev_out->flags & IFF_LOOPBACK) 24548c2ecf20Sopenharmony_ci flags |= RTCF_LOCAL; 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci do_cache = true; 24578c2ecf20Sopenharmony_ci if (type == RTN_BROADCAST) { 24588c2ecf20Sopenharmony_ci flags |= RTCF_BROADCAST | RTCF_LOCAL; 24598c2ecf20Sopenharmony_ci fi = NULL; 24608c2ecf20Sopenharmony_ci } else if (type == RTN_MULTICAST) { 24618c2ecf20Sopenharmony_ci flags |= RTCF_MULTICAST | RTCF_LOCAL; 24628c2ecf20Sopenharmony_ci if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr, 24638c2ecf20Sopenharmony_ci fl4->flowi4_proto)) 24648c2ecf20Sopenharmony_ci flags &= ~RTCF_LOCAL; 24658c2ecf20Sopenharmony_ci else 24668c2ecf20Sopenharmony_ci do_cache = false; 24678c2ecf20Sopenharmony_ci /* If multicast route do not exist use 24688c2ecf20Sopenharmony_ci * default one, but do not gateway in this case. 24698c2ecf20Sopenharmony_ci * Yes, it is hack. 24708c2ecf20Sopenharmony_ci */ 24718c2ecf20Sopenharmony_ci if (fi && res->prefixlen < 4) 24728c2ecf20Sopenharmony_ci fi = NULL; 24738c2ecf20Sopenharmony_ci } else if ((type == RTN_LOCAL) && (orig_oif != 0) && 24748c2ecf20Sopenharmony_ci (orig_oif != dev_out->ifindex)) { 24758c2ecf20Sopenharmony_ci /* For local routes that require a particular output interface 24768c2ecf20Sopenharmony_ci * we do not want to cache the result. Caching the result 24778c2ecf20Sopenharmony_ci * causes incorrect behaviour when there are multiple source 24788c2ecf20Sopenharmony_ci * addresses on the interface, the end result being that if the 24798c2ecf20Sopenharmony_ci * intended recipient is waiting on that interface for the 24808c2ecf20Sopenharmony_ci * packet he won't receive it because it will be delivered on 24818c2ecf20Sopenharmony_ci * the loopback interface and the IP_PKTINFO ipi_ifindex will 24828c2ecf20Sopenharmony_ci * be set to the loopback interface as well. 24838c2ecf20Sopenharmony_ci */ 24848c2ecf20Sopenharmony_ci do_cache = false; 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci fnhe = NULL; 24888c2ecf20Sopenharmony_ci do_cache &= fi != NULL; 24898c2ecf20Sopenharmony_ci if (fi) { 24908c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = FIB_RES_NHC(*res); 24918c2ecf20Sopenharmony_ci struct rtable __rcu **prth; 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci fnhe = find_exception(nhc, fl4->daddr); 24948c2ecf20Sopenharmony_ci if (!do_cache) 24958c2ecf20Sopenharmony_ci goto add; 24968c2ecf20Sopenharmony_ci if (fnhe) { 24978c2ecf20Sopenharmony_ci prth = &fnhe->fnhe_rth_output; 24988c2ecf20Sopenharmony_ci } else { 24998c2ecf20Sopenharmony_ci if (unlikely(fl4->flowi4_flags & 25008c2ecf20Sopenharmony_ci FLOWI_FLAG_KNOWN_NH && 25018c2ecf20Sopenharmony_ci !(nhc->nhc_gw_family && 25028c2ecf20Sopenharmony_ci nhc->nhc_scope == RT_SCOPE_LINK))) { 25038c2ecf20Sopenharmony_ci do_cache = false; 25048c2ecf20Sopenharmony_ci goto add; 25058c2ecf20Sopenharmony_ci } 25068c2ecf20Sopenharmony_ci prth = raw_cpu_ptr(nhc->nhc_pcpu_rth_output); 25078c2ecf20Sopenharmony_ci } 25088c2ecf20Sopenharmony_ci rth = rcu_dereference(*prth); 25098c2ecf20Sopenharmony_ci if (rt_cache_valid(rth) && dst_hold_safe(&rth->dst)) 25108c2ecf20Sopenharmony_ci return rth; 25118c2ecf20Sopenharmony_ci } 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ciadd: 25148c2ecf20Sopenharmony_ci rth = rt_dst_alloc(dev_out, flags, type, 25158c2ecf20Sopenharmony_ci IN_DEV_ORCONF(in_dev, NOPOLICY), 25168c2ecf20Sopenharmony_ci IN_DEV_ORCONF(in_dev, NOXFRM)); 25178c2ecf20Sopenharmony_ci if (!rth) 25188c2ecf20Sopenharmony_ci return ERR_PTR(-ENOBUFS); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci rth->rt_iif = orig_oif; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(out_slow_tot); 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci if (flags & (RTCF_BROADCAST | RTCF_MULTICAST)) { 25258c2ecf20Sopenharmony_ci if (flags & RTCF_LOCAL && 25268c2ecf20Sopenharmony_ci !(dev_out->flags & IFF_LOOPBACK)) { 25278c2ecf20Sopenharmony_ci rth->dst.output = ip_mc_output; 25288c2ecf20Sopenharmony_ci RT_CACHE_STAT_INC(out_slow_mc); 25298c2ecf20Sopenharmony_ci } 25308c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MROUTE 25318c2ecf20Sopenharmony_ci if (type == RTN_MULTICAST) { 25328c2ecf20Sopenharmony_ci if (IN_DEV_MFORWARD(in_dev) && 25338c2ecf20Sopenharmony_ci !ipv4_is_local_multicast(fl4->daddr)) { 25348c2ecf20Sopenharmony_ci rth->dst.input = ip_mr_input; 25358c2ecf20Sopenharmony_ci rth->dst.output = ip_mc_output; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci#endif 25398c2ecf20Sopenharmony_ci } 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0, do_cache); 25428c2ecf20Sopenharmony_ci lwtunnel_set_redirect(&rth->dst); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci return rth; 25458c2ecf20Sopenharmony_ci} 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci/* 25488c2ecf20Sopenharmony_ci * Major route resolver routine. 25498c2ecf20Sopenharmony_ci */ 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_cistruct rtable *ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, 25528c2ecf20Sopenharmony_ci const struct sk_buff *skb) 25538c2ecf20Sopenharmony_ci{ 25548c2ecf20Sopenharmony_ci struct fib_result res = { 25558c2ecf20Sopenharmony_ci .type = RTN_UNSPEC, 25568c2ecf20Sopenharmony_ci .fi = NULL, 25578c2ecf20Sopenharmony_ci .table = NULL, 25588c2ecf20Sopenharmony_ci .tclassid = 0, 25598c2ecf20Sopenharmony_ci }; 25608c2ecf20Sopenharmony_ci struct rtable *rth; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci fl4->flowi4_iif = LOOPBACK_IFINDEX; 25638c2ecf20Sopenharmony_ci ip_rt_fix_tos(fl4); 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci rcu_read_lock(); 25668c2ecf20Sopenharmony_ci rth = ip_route_output_key_hash_rcu(net, fl4, &res, skb); 25678c2ecf20Sopenharmony_ci rcu_read_unlock(); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci return rth; 25708c2ecf20Sopenharmony_ci} 25718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_route_output_key_hash); 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_cistruct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, 25748c2ecf20Sopenharmony_ci struct fib_result *res, 25758c2ecf20Sopenharmony_ci const struct sk_buff *skb) 25768c2ecf20Sopenharmony_ci{ 25778c2ecf20Sopenharmony_ci struct net_device *dev_out = NULL; 25788c2ecf20Sopenharmony_ci int orig_oif = fl4->flowi4_oif; 25798c2ecf20Sopenharmony_ci unsigned int flags = 0; 25808c2ecf20Sopenharmony_ci struct rtable *rth; 25818c2ecf20Sopenharmony_ci int err; 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci if (fl4->saddr) { 25848c2ecf20Sopenharmony_ci if (ipv4_is_multicast(fl4->saddr) || 25858c2ecf20Sopenharmony_ci ipv4_is_lbcast(fl4->saddr) || 25868c2ecf20Sopenharmony_ci ipv4_is_zeronet(fl4->saddr)) { 25878c2ecf20Sopenharmony_ci rth = ERR_PTR(-EINVAL); 25888c2ecf20Sopenharmony_ci goto out; 25898c2ecf20Sopenharmony_ci } 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci rth = ERR_PTR(-ENETUNREACH); 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci /* I removed check for oif == dev_out->oif here. 25948c2ecf20Sopenharmony_ci It was wrong for two reasons: 25958c2ecf20Sopenharmony_ci 1. ip_dev_find(net, saddr) can return wrong iface, if saddr 25968c2ecf20Sopenharmony_ci is assigned to multiple interfaces. 25978c2ecf20Sopenharmony_ci 2. Moreover, we are allowed to send packets with saddr 25988c2ecf20Sopenharmony_ci of another iface. --ANK 25998c2ecf20Sopenharmony_ci */ 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci if (fl4->flowi4_oif == 0 && 26028c2ecf20Sopenharmony_ci (ipv4_is_multicast(fl4->daddr) || 26038c2ecf20Sopenharmony_ci ipv4_is_lbcast(fl4->daddr))) { 26048c2ecf20Sopenharmony_ci /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ 26058c2ecf20Sopenharmony_ci dev_out = __ip_dev_find(net, fl4->saddr, false); 26068c2ecf20Sopenharmony_ci if (!dev_out) 26078c2ecf20Sopenharmony_ci goto out; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci /* Special hack: user can direct multicasts 26108c2ecf20Sopenharmony_ci and limited broadcast via necessary interface 26118c2ecf20Sopenharmony_ci without fiddling with IP_MULTICAST_IF or IP_PKTINFO. 26128c2ecf20Sopenharmony_ci This hack is not just for fun, it allows 26138c2ecf20Sopenharmony_ci vic,vat and friends to work. 26148c2ecf20Sopenharmony_ci They bind socket to loopback, set ttl to zero 26158c2ecf20Sopenharmony_ci and expect that it will work. 26168c2ecf20Sopenharmony_ci From the viewpoint of routing cache they are broken, 26178c2ecf20Sopenharmony_ci because we are not allowed to build multicast path 26188c2ecf20Sopenharmony_ci with loopback source addr (look, routing cache 26198c2ecf20Sopenharmony_ci cannot know, that ttl is zero, so that packet 26208c2ecf20Sopenharmony_ci will not leave this host and route is valid). 26218c2ecf20Sopenharmony_ci Luckily, this hack is good workaround. 26228c2ecf20Sopenharmony_ci */ 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci fl4->flowi4_oif = dev_out->ifindex; 26258c2ecf20Sopenharmony_ci goto make_route; 26268c2ecf20Sopenharmony_ci } 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci if (!(fl4->flowi4_flags & FLOWI_FLAG_ANYSRC)) { 26298c2ecf20Sopenharmony_ci /* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */ 26308c2ecf20Sopenharmony_ci if (!__ip_dev_find(net, fl4->saddr, false)) 26318c2ecf20Sopenharmony_ci goto out; 26328c2ecf20Sopenharmony_ci } 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci if (fl4->flowi4_oif) { 26378c2ecf20Sopenharmony_ci dev_out = dev_get_by_index_rcu(net, fl4->flowi4_oif); 26388c2ecf20Sopenharmony_ci rth = ERR_PTR(-ENODEV); 26398c2ecf20Sopenharmony_ci if (!dev_out) 26408c2ecf20Sopenharmony_ci goto out; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci /* RACE: Check return value of inet_select_addr instead. */ 26438c2ecf20Sopenharmony_ci if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) { 26448c2ecf20Sopenharmony_ci rth = ERR_PTR(-ENETUNREACH); 26458c2ecf20Sopenharmony_ci goto out; 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci if (ipv4_is_local_multicast(fl4->daddr) || 26488c2ecf20Sopenharmony_ci ipv4_is_lbcast(fl4->daddr) || 26498c2ecf20Sopenharmony_ci fl4->flowi4_proto == IPPROTO_IGMP) { 26508c2ecf20Sopenharmony_ci if (!fl4->saddr) 26518c2ecf20Sopenharmony_ci fl4->saddr = inet_select_addr(dev_out, 0, 26528c2ecf20Sopenharmony_ci RT_SCOPE_LINK); 26538c2ecf20Sopenharmony_ci goto make_route; 26548c2ecf20Sopenharmony_ci } 26558c2ecf20Sopenharmony_ci if (!fl4->saddr) { 26568c2ecf20Sopenharmony_ci if (ipv4_is_multicast(fl4->daddr)) 26578c2ecf20Sopenharmony_ci fl4->saddr = inet_select_addr(dev_out, 0, 26588c2ecf20Sopenharmony_ci fl4->flowi4_scope); 26598c2ecf20Sopenharmony_ci else if (!fl4->daddr) 26608c2ecf20Sopenharmony_ci fl4->saddr = inet_select_addr(dev_out, 0, 26618c2ecf20Sopenharmony_ci RT_SCOPE_HOST); 26628c2ecf20Sopenharmony_ci } 26638c2ecf20Sopenharmony_ci } 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci if (!fl4->daddr) { 26668c2ecf20Sopenharmony_ci fl4->daddr = fl4->saddr; 26678c2ecf20Sopenharmony_ci if (!fl4->daddr) 26688c2ecf20Sopenharmony_ci fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK); 26698c2ecf20Sopenharmony_ci dev_out = net->loopback_dev; 26708c2ecf20Sopenharmony_ci fl4->flowi4_oif = LOOPBACK_IFINDEX; 26718c2ecf20Sopenharmony_ci res->type = RTN_LOCAL; 26728c2ecf20Sopenharmony_ci flags |= RTCF_LOCAL; 26738c2ecf20Sopenharmony_ci goto make_route; 26748c2ecf20Sopenharmony_ci } 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci err = fib_lookup(net, fl4, res, 0); 26778c2ecf20Sopenharmony_ci if (err) { 26788c2ecf20Sopenharmony_ci res->fi = NULL; 26798c2ecf20Sopenharmony_ci res->table = NULL; 26808c2ecf20Sopenharmony_ci if (fl4->flowi4_oif && 26818c2ecf20Sopenharmony_ci (ipv4_is_multicast(fl4->daddr) || 26828c2ecf20Sopenharmony_ci !netif_index_is_l3_master(net, fl4->flowi4_oif))) { 26838c2ecf20Sopenharmony_ci /* Apparently, routing tables are wrong. Assume, 26848c2ecf20Sopenharmony_ci that the destination is on link. 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci WHY? DW. 26878c2ecf20Sopenharmony_ci Because we are allowed to send to iface 26888c2ecf20Sopenharmony_ci even if it has NO routes and NO assigned 26898c2ecf20Sopenharmony_ci addresses. When oif is specified, routing 26908c2ecf20Sopenharmony_ci tables are looked up with only one purpose: 26918c2ecf20Sopenharmony_ci to catch if destination is gatewayed, rather than 26928c2ecf20Sopenharmony_ci direct. Moreover, if MSG_DONTROUTE is set, 26938c2ecf20Sopenharmony_ci we send packet, ignoring both routing tables 26948c2ecf20Sopenharmony_ci and ifaddr state. --ANK 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci We could make it even if oif is unknown, 26988c2ecf20Sopenharmony_ci likely IPv6, but we do not. 26998c2ecf20Sopenharmony_ci */ 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci if (fl4->saddr == 0) 27028c2ecf20Sopenharmony_ci fl4->saddr = inet_select_addr(dev_out, 0, 27038c2ecf20Sopenharmony_ci RT_SCOPE_LINK); 27048c2ecf20Sopenharmony_ci res->type = RTN_UNICAST; 27058c2ecf20Sopenharmony_ci goto make_route; 27068c2ecf20Sopenharmony_ci } 27078c2ecf20Sopenharmony_ci rth = ERR_PTR(err); 27088c2ecf20Sopenharmony_ci goto out; 27098c2ecf20Sopenharmony_ci } 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci if (res->type == RTN_LOCAL) { 27128c2ecf20Sopenharmony_ci if (!fl4->saddr) { 27138c2ecf20Sopenharmony_ci if (res->fi->fib_prefsrc) 27148c2ecf20Sopenharmony_ci fl4->saddr = res->fi->fib_prefsrc; 27158c2ecf20Sopenharmony_ci else 27168c2ecf20Sopenharmony_ci fl4->saddr = fl4->daddr; 27178c2ecf20Sopenharmony_ci } 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci /* L3 master device is the loopback for that domain */ 27208c2ecf20Sopenharmony_ci dev_out = l3mdev_master_dev_rcu(FIB_RES_DEV(*res)) ? : 27218c2ecf20Sopenharmony_ci net->loopback_dev; 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci /* make sure orig_oif points to fib result device even 27248c2ecf20Sopenharmony_ci * though packet rx/tx happens over loopback or l3mdev 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci orig_oif = FIB_RES_OIF(*res); 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci fl4->flowi4_oif = dev_out->ifindex; 27298c2ecf20Sopenharmony_ci flags |= RTCF_LOCAL; 27308c2ecf20Sopenharmony_ci goto make_route; 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci fib_select_path(net, res, fl4, skb); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci dev_out = FIB_RES_DEV(*res); 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_cimake_route: 27388c2ecf20Sopenharmony_ci rth = __mkroute_output(res, fl4, orig_oif, dev_out, flags); 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ciout: 27418c2ecf20Sopenharmony_ci return rth; 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cistatic struct dst_ops ipv4_dst_blackhole_ops = { 27458c2ecf20Sopenharmony_ci .family = AF_INET, 27468c2ecf20Sopenharmony_ci .default_advmss = ipv4_default_advmss, 27478c2ecf20Sopenharmony_ci .neigh_lookup = ipv4_neigh_lookup, 27488c2ecf20Sopenharmony_ci .check = dst_blackhole_check, 27498c2ecf20Sopenharmony_ci .cow_metrics = dst_blackhole_cow_metrics, 27508c2ecf20Sopenharmony_ci .update_pmtu = dst_blackhole_update_pmtu, 27518c2ecf20Sopenharmony_ci .redirect = dst_blackhole_redirect, 27528c2ecf20Sopenharmony_ci .mtu = dst_blackhole_mtu, 27538c2ecf20Sopenharmony_ci}; 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_cistruct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig) 27568c2ecf20Sopenharmony_ci{ 27578c2ecf20Sopenharmony_ci struct rtable *ort = (struct rtable *) dst_orig; 27588c2ecf20Sopenharmony_ci struct rtable *rt; 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci rt = dst_alloc(&ipv4_dst_blackhole_ops, NULL, 1, DST_OBSOLETE_DEAD, 0); 27618c2ecf20Sopenharmony_ci if (rt) { 27628c2ecf20Sopenharmony_ci struct dst_entry *new = &rt->dst; 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci new->__use = 1; 27658c2ecf20Sopenharmony_ci new->input = dst_discard; 27668c2ecf20Sopenharmony_ci new->output = dst_discard_out; 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci new->dev = net->loopback_dev; 27698c2ecf20Sopenharmony_ci if (new->dev) 27708c2ecf20Sopenharmony_ci dev_hold(new->dev); 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci rt->rt_is_input = ort->rt_is_input; 27738c2ecf20Sopenharmony_ci rt->rt_iif = ort->rt_iif; 27748c2ecf20Sopenharmony_ci rt->rt_pmtu = ort->rt_pmtu; 27758c2ecf20Sopenharmony_ci rt->rt_mtu_locked = ort->rt_mtu_locked; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci rt->rt_genid = rt_genid_ipv4(net); 27788c2ecf20Sopenharmony_ci rt->rt_flags = ort->rt_flags; 27798c2ecf20Sopenharmony_ci rt->rt_type = ort->rt_type; 27808c2ecf20Sopenharmony_ci rt->rt_uses_gateway = ort->rt_uses_gateway; 27818c2ecf20Sopenharmony_ci rt->rt_gw_family = ort->rt_gw_family; 27828c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET) 27838c2ecf20Sopenharmony_ci rt->rt_gw4 = ort->rt_gw4; 27848c2ecf20Sopenharmony_ci else if (rt->rt_gw_family == AF_INET6) 27858c2ecf20Sopenharmony_ci rt->rt_gw6 = ort->rt_gw6; 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rt->rt_uncached); 27888c2ecf20Sopenharmony_ci } 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci dst_release(dst_orig); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci return rt ? &rt->dst : ERR_PTR(-ENOMEM); 27938c2ecf20Sopenharmony_ci} 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_cistruct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, 27968c2ecf20Sopenharmony_ci const struct sock *sk) 27978c2ecf20Sopenharmony_ci{ 27988c2ecf20Sopenharmony_ci struct rtable *rt = __ip_route_output_key(net, flp4); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 28018c2ecf20Sopenharmony_ci return rt; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci if (flp4->flowi4_proto) { 28048c2ecf20Sopenharmony_ci flp4->flowi4_oif = rt->dst.dev->ifindex; 28058c2ecf20Sopenharmony_ci rt = (struct rtable *)xfrm_lookup_route(net, &rt->dst, 28068c2ecf20Sopenharmony_ci flowi4_to_flowi(flp4), 28078c2ecf20Sopenharmony_ci sk, 0); 28088c2ecf20Sopenharmony_ci } 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci return rt; 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_route_output_flow); 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_cistruct rtable *ip_route_output_tunnel(struct sk_buff *skb, 28158c2ecf20Sopenharmony_ci struct net_device *dev, 28168c2ecf20Sopenharmony_ci struct net *net, __be32 *saddr, 28178c2ecf20Sopenharmony_ci const struct ip_tunnel_info *info, 28188c2ecf20Sopenharmony_ci u8 protocol, bool use_cache) 28198c2ecf20Sopenharmony_ci{ 28208c2ecf20Sopenharmony_ci#ifdef CONFIG_DST_CACHE 28218c2ecf20Sopenharmony_ci struct dst_cache *dst_cache; 28228c2ecf20Sopenharmony_ci#endif 28238c2ecf20Sopenharmony_ci struct rtable *rt = NULL; 28248c2ecf20Sopenharmony_ci struct flowi4 fl4; 28258c2ecf20Sopenharmony_ci __u8 tos; 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci#ifdef CONFIG_DST_CACHE 28288c2ecf20Sopenharmony_ci dst_cache = (struct dst_cache *)&info->dst_cache; 28298c2ecf20Sopenharmony_ci if (use_cache) { 28308c2ecf20Sopenharmony_ci rt = dst_cache_get_ip4(dst_cache, saddr); 28318c2ecf20Sopenharmony_ci if (rt) 28328c2ecf20Sopenharmony_ci return rt; 28338c2ecf20Sopenharmony_ci } 28348c2ecf20Sopenharmony_ci#endif 28358c2ecf20Sopenharmony_ci memset(&fl4, 0, sizeof(fl4)); 28368c2ecf20Sopenharmony_ci fl4.flowi4_mark = skb->mark; 28378c2ecf20Sopenharmony_ci fl4.flowi4_proto = protocol; 28388c2ecf20Sopenharmony_ci fl4.daddr = info->key.u.ipv4.dst; 28398c2ecf20Sopenharmony_ci fl4.saddr = info->key.u.ipv4.src; 28408c2ecf20Sopenharmony_ci tos = info->key.tos; 28418c2ecf20Sopenharmony_ci fl4.flowi4_tos = RT_TOS(tos); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci rt = ip_route_output_key(net, &fl4); 28448c2ecf20Sopenharmony_ci if (IS_ERR(rt)) { 28458c2ecf20Sopenharmony_ci netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr); 28468c2ecf20Sopenharmony_ci return ERR_PTR(-ENETUNREACH); 28478c2ecf20Sopenharmony_ci } 28488c2ecf20Sopenharmony_ci if (rt->dst.dev == dev) { /* is this necessary? */ 28498c2ecf20Sopenharmony_ci netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr); 28508c2ecf20Sopenharmony_ci ip_rt_put(rt); 28518c2ecf20Sopenharmony_ci return ERR_PTR(-ELOOP); 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci#ifdef CONFIG_DST_CACHE 28548c2ecf20Sopenharmony_ci if (use_cache) 28558c2ecf20Sopenharmony_ci dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); 28568c2ecf20Sopenharmony_ci#endif 28578c2ecf20Sopenharmony_ci *saddr = fl4.saddr; 28588c2ecf20Sopenharmony_ci return rt; 28598c2ecf20Sopenharmony_ci} 28608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ip_route_output_tunnel); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci/* called with rcu_read_lock held */ 28638c2ecf20Sopenharmony_cistatic int rt_fill_info(struct net *net, __be32 dst, __be32 src, 28648c2ecf20Sopenharmony_ci struct rtable *rt, u32 table_id, struct flowi4 *fl4, 28658c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 portid, u32 seq, 28668c2ecf20Sopenharmony_ci unsigned int flags) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci struct rtmsg *r; 28698c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 28708c2ecf20Sopenharmony_ci unsigned long expires = 0; 28718c2ecf20Sopenharmony_ci u32 error; 28728c2ecf20Sopenharmony_ci u32 metrics[RTAX_MAX]; 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*r), flags); 28758c2ecf20Sopenharmony_ci if (!nlh) 28768c2ecf20Sopenharmony_ci return -EMSGSIZE; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 28798c2ecf20Sopenharmony_ci r->rtm_family = AF_INET; 28808c2ecf20Sopenharmony_ci r->rtm_dst_len = 32; 28818c2ecf20Sopenharmony_ci r->rtm_src_len = 0; 28828c2ecf20Sopenharmony_ci r->rtm_tos = fl4 ? fl4->flowi4_tos : 0; 28838c2ecf20Sopenharmony_ci r->rtm_table = table_id < 256 ? table_id : RT_TABLE_COMPAT; 28848c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_TABLE, table_id)) 28858c2ecf20Sopenharmony_ci goto nla_put_failure; 28868c2ecf20Sopenharmony_ci r->rtm_type = rt->rt_type; 28878c2ecf20Sopenharmony_ci r->rtm_scope = RT_SCOPE_UNIVERSE; 28888c2ecf20Sopenharmony_ci r->rtm_protocol = RTPROT_UNSPEC; 28898c2ecf20Sopenharmony_ci r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED; 28908c2ecf20Sopenharmony_ci if (rt->rt_flags & RTCF_NOTIFY) 28918c2ecf20Sopenharmony_ci r->rtm_flags |= RTM_F_NOTIFY; 28928c2ecf20Sopenharmony_ci if (IPCB(skb)->flags & IPSKB_DOREDIRECT) 28938c2ecf20Sopenharmony_ci r->rtm_flags |= RTCF_DOREDIRECT; 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci if (nla_put_in_addr(skb, RTA_DST, dst)) 28968c2ecf20Sopenharmony_ci goto nla_put_failure; 28978c2ecf20Sopenharmony_ci if (src) { 28988c2ecf20Sopenharmony_ci r->rtm_src_len = 32; 28998c2ecf20Sopenharmony_ci if (nla_put_in_addr(skb, RTA_SRC, src)) 29008c2ecf20Sopenharmony_ci goto nla_put_failure; 29018c2ecf20Sopenharmony_ci } 29028c2ecf20Sopenharmony_ci if (rt->dst.dev && 29038c2ecf20Sopenharmony_ci nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) 29048c2ecf20Sopenharmony_ci goto nla_put_failure; 29058c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 29068c2ecf20Sopenharmony_ci if (rt->dst.tclassid && 29078c2ecf20Sopenharmony_ci nla_put_u32(skb, RTA_FLOW, rt->dst.tclassid)) 29088c2ecf20Sopenharmony_ci goto nla_put_failure; 29098c2ecf20Sopenharmony_ci#endif 29108c2ecf20Sopenharmony_ci if (fl4 && !rt_is_input_route(rt) && 29118c2ecf20Sopenharmony_ci fl4->saddr != src) { 29128c2ecf20Sopenharmony_ci if (nla_put_in_addr(skb, RTA_PREFSRC, fl4->saddr)) 29138c2ecf20Sopenharmony_ci goto nla_put_failure; 29148c2ecf20Sopenharmony_ci } 29158c2ecf20Sopenharmony_ci if (rt->rt_uses_gateway) { 29168c2ecf20Sopenharmony_ci if (rt->rt_gw_family == AF_INET && 29178c2ecf20Sopenharmony_ci nla_put_in_addr(skb, RTA_GATEWAY, rt->rt_gw4)) { 29188c2ecf20Sopenharmony_ci goto nla_put_failure; 29198c2ecf20Sopenharmony_ci } else if (rt->rt_gw_family == AF_INET6) { 29208c2ecf20Sopenharmony_ci int alen = sizeof(struct in6_addr); 29218c2ecf20Sopenharmony_ci struct nlattr *nla; 29228c2ecf20Sopenharmony_ci struct rtvia *via; 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci nla = nla_reserve(skb, RTA_VIA, alen + 2); 29258c2ecf20Sopenharmony_ci if (!nla) 29268c2ecf20Sopenharmony_ci goto nla_put_failure; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci via = nla_data(nla); 29298c2ecf20Sopenharmony_ci via->rtvia_family = AF_INET6; 29308c2ecf20Sopenharmony_ci memcpy(via->rtvia_addr, &rt->rt_gw6, alen); 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci } 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_ci expires = rt->dst.expires; 29358c2ecf20Sopenharmony_ci if (expires) { 29368c2ecf20Sopenharmony_ci unsigned long now = jiffies; 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci if (time_before(now, expires)) 29398c2ecf20Sopenharmony_ci expires -= now; 29408c2ecf20Sopenharmony_ci else 29418c2ecf20Sopenharmony_ci expires = 0; 29428c2ecf20Sopenharmony_ci } 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci memcpy(metrics, dst_metrics_ptr(&rt->dst), sizeof(metrics)); 29458c2ecf20Sopenharmony_ci if (rt->rt_pmtu && expires) 29468c2ecf20Sopenharmony_ci metrics[RTAX_MTU - 1] = rt->rt_pmtu; 29478c2ecf20Sopenharmony_ci if (rt->rt_mtu_locked && expires) 29488c2ecf20Sopenharmony_ci metrics[RTAX_LOCK - 1] |= BIT(RTAX_MTU); 29498c2ecf20Sopenharmony_ci if (rtnetlink_put_metrics(skb, metrics) < 0) 29508c2ecf20Sopenharmony_ci goto nla_put_failure; 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci if (fl4) { 29538c2ecf20Sopenharmony_ci if (fl4->flowi4_mark && 29548c2ecf20Sopenharmony_ci nla_put_u32(skb, RTA_MARK, fl4->flowi4_mark)) 29558c2ecf20Sopenharmony_ci goto nla_put_failure; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci if (!uid_eq(fl4->flowi4_uid, INVALID_UID) && 29588c2ecf20Sopenharmony_ci nla_put_u32(skb, RTA_UID, 29598c2ecf20Sopenharmony_ci from_kuid_munged(current_user_ns(), 29608c2ecf20Sopenharmony_ci fl4->flowi4_uid))) 29618c2ecf20Sopenharmony_ci goto nla_put_failure; 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci if (rt_is_input_route(rt)) { 29648c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MROUTE 29658c2ecf20Sopenharmony_ci if (ipv4_is_multicast(dst) && 29668c2ecf20Sopenharmony_ci !ipv4_is_local_multicast(dst) && 29678c2ecf20Sopenharmony_ci IPV4_DEVCONF_ALL(net, MC_FORWARDING)) { 29688c2ecf20Sopenharmony_ci int err = ipmr_get_route(net, skb, 29698c2ecf20Sopenharmony_ci fl4->saddr, fl4->daddr, 29708c2ecf20Sopenharmony_ci r, portid); 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci if (err <= 0) { 29738c2ecf20Sopenharmony_ci if (err == 0) 29748c2ecf20Sopenharmony_ci return 0; 29758c2ecf20Sopenharmony_ci goto nla_put_failure; 29768c2ecf20Sopenharmony_ci } 29778c2ecf20Sopenharmony_ci } else 29788c2ecf20Sopenharmony_ci#endif 29798c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_IIF, fl4->flowi4_iif)) 29808c2ecf20Sopenharmony_ci goto nla_put_failure; 29818c2ecf20Sopenharmony_ci } 29828c2ecf20Sopenharmony_ci } 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci error = rt->dst.error; 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_ci if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, error) < 0) 29878c2ecf20Sopenharmony_ci goto nla_put_failure; 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 29908c2ecf20Sopenharmony_ci return 0; 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_cinla_put_failure: 29938c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 29948c2ecf20Sopenharmony_ci return -EMSGSIZE; 29958c2ecf20Sopenharmony_ci} 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_cistatic int fnhe_dump_bucket(struct net *net, struct sk_buff *skb, 29988c2ecf20Sopenharmony_ci struct netlink_callback *cb, u32 table_id, 29998c2ecf20Sopenharmony_ci struct fnhe_hash_bucket *bucket, int genid, 30008c2ecf20Sopenharmony_ci int *fa_index, int fa_start, unsigned int flags) 30018c2ecf20Sopenharmony_ci{ 30028c2ecf20Sopenharmony_ci int i; 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci for (i = 0; i < FNHE_HASH_SIZE; i++) { 30058c2ecf20Sopenharmony_ci struct fib_nh_exception *fnhe; 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci for (fnhe = rcu_dereference(bucket[i].chain); fnhe; 30088c2ecf20Sopenharmony_ci fnhe = rcu_dereference(fnhe->fnhe_next)) { 30098c2ecf20Sopenharmony_ci struct rtable *rt; 30108c2ecf20Sopenharmony_ci int err; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci if (*fa_index < fa_start) 30138c2ecf20Sopenharmony_ci goto next; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci if (fnhe->fnhe_genid != genid) 30168c2ecf20Sopenharmony_ci goto next; 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci if (fnhe->fnhe_expires && 30198c2ecf20Sopenharmony_ci time_after(jiffies, fnhe->fnhe_expires)) 30208c2ecf20Sopenharmony_ci goto next; 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_input); 30238c2ecf20Sopenharmony_ci if (!rt) 30248c2ecf20Sopenharmony_ci rt = rcu_dereference(fnhe->fnhe_rth_output); 30258c2ecf20Sopenharmony_ci if (!rt) 30268c2ecf20Sopenharmony_ci goto next; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci err = rt_fill_info(net, fnhe->fnhe_daddr, 0, rt, 30298c2ecf20Sopenharmony_ci table_id, NULL, skb, 30308c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 30318c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, flags); 30328c2ecf20Sopenharmony_ci if (err) 30338c2ecf20Sopenharmony_ci return err; 30348c2ecf20Sopenharmony_cinext: 30358c2ecf20Sopenharmony_ci (*fa_index)++; 30368c2ecf20Sopenharmony_ci } 30378c2ecf20Sopenharmony_ci } 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci return 0; 30408c2ecf20Sopenharmony_ci} 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ciint fib_dump_info_fnhe(struct sk_buff *skb, struct netlink_callback *cb, 30438c2ecf20Sopenharmony_ci u32 table_id, struct fib_info *fi, 30448c2ecf20Sopenharmony_ci int *fa_index, int fa_start, unsigned int flags) 30458c2ecf20Sopenharmony_ci{ 30468c2ecf20Sopenharmony_ci struct net *net = sock_net(cb->skb->sk); 30478c2ecf20Sopenharmony_ci int nhsel, genid = fnhe_genid(net); 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ci for (nhsel = 0; nhsel < fib_info_num_path(fi); nhsel++) { 30508c2ecf20Sopenharmony_ci struct fib_nh_common *nhc = fib_info_nhc(fi, nhsel); 30518c2ecf20Sopenharmony_ci struct fnhe_hash_bucket *bucket; 30528c2ecf20Sopenharmony_ci int err; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci if (nhc->nhc_flags & RTNH_F_DEAD) 30558c2ecf20Sopenharmony_ci continue; 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci rcu_read_lock(); 30588c2ecf20Sopenharmony_ci bucket = rcu_dereference(nhc->nhc_exceptions); 30598c2ecf20Sopenharmony_ci err = 0; 30608c2ecf20Sopenharmony_ci if (bucket) 30618c2ecf20Sopenharmony_ci err = fnhe_dump_bucket(net, skb, cb, table_id, bucket, 30628c2ecf20Sopenharmony_ci genid, fa_index, fa_start, 30638c2ecf20Sopenharmony_ci flags); 30648c2ecf20Sopenharmony_ci rcu_read_unlock(); 30658c2ecf20Sopenharmony_ci if (err) 30668c2ecf20Sopenharmony_ci return err; 30678c2ecf20Sopenharmony_ci } 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci return 0; 30708c2ecf20Sopenharmony_ci} 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_cistatic struct sk_buff *inet_rtm_getroute_build_skb(__be32 src, __be32 dst, 30738c2ecf20Sopenharmony_ci u8 ip_proto, __be16 sport, 30748c2ecf20Sopenharmony_ci __be16 dport) 30758c2ecf20Sopenharmony_ci{ 30768c2ecf20Sopenharmony_ci struct sk_buff *skb; 30778c2ecf20Sopenharmony_ci struct iphdr *iph; 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); 30808c2ecf20Sopenharmony_ci if (!skb) 30818c2ecf20Sopenharmony_ci return NULL; 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci /* Reserve room for dummy headers, this skb can pass 30848c2ecf20Sopenharmony_ci * through good chunk of routing engine. 30858c2ecf20Sopenharmony_ci */ 30868c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 30878c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 30888c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 30898c2ecf20Sopenharmony_ci iph = skb_put(skb, sizeof(struct iphdr)); 30908c2ecf20Sopenharmony_ci iph->protocol = ip_proto; 30918c2ecf20Sopenharmony_ci iph->saddr = src; 30928c2ecf20Sopenharmony_ci iph->daddr = dst; 30938c2ecf20Sopenharmony_ci iph->version = 0x4; 30948c2ecf20Sopenharmony_ci iph->frag_off = 0; 30958c2ecf20Sopenharmony_ci iph->ihl = 0x5; 30968c2ecf20Sopenharmony_ci skb_set_transport_header(skb, skb->len); 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci switch (iph->protocol) { 30998c2ecf20Sopenharmony_ci case IPPROTO_UDP: { 31008c2ecf20Sopenharmony_ci struct udphdr *udph; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci udph = skb_put_zero(skb, sizeof(struct udphdr)); 31038c2ecf20Sopenharmony_ci udph->source = sport; 31048c2ecf20Sopenharmony_ci udph->dest = dport; 31058c2ecf20Sopenharmony_ci udph->len = htons(sizeof(struct udphdr)); 31068c2ecf20Sopenharmony_ci udph->check = 0; 31078c2ecf20Sopenharmony_ci break; 31088c2ecf20Sopenharmony_ci } 31098c2ecf20Sopenharmony_ci case IPPROTO_TCP: { 31108c2ecf20Sopenharmony_ci struct tcphdr *tcph; 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci tcph = skb_put_zero(skb, sizeof(struct tcphdr)); 31138c2ecf20Sopenharmony_ci tcph->source = sport; 31148c2ecf20Sopenharmony_ci tcph->dest = dport; 31158c2ecf20Sopenharmony_ci tcph->doff = sizeof(struct tcphdr) / 4; 31168c2ecf20Sopenharmony_ci tcph->rst = 1; 31178c2ecf20Sopenharmony_ci tcph->check = ~tcp_v4_check(sizeof(struct tcphdr), 31188c2ecf20Sopenharmony_ci src, dst, 0); 31198c2ecf20Sopenharmony_ci break; 31208c2ecf20Sopenharmony_ci } 31218c2ecf20Sopenharmony_ci case IPPROTO_ICMP: { 31228c2ecf20Sopenharmony_ci struct icmphdr *icmph; 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci icmph = skb_put_zero(skb, sizeof(struct icmphdr)); 31258c2ecf20Sopenharmony_ci icmph->type = ICMP_ECHO; 31268c2ecf20Sopenharmony_ci icmph->code = 0; 31278c2ecf20Sopenharmony_ci } 31288c2ecf20Sopenharmony_ci } 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci return skb; 31318c2ecf20Sopenharmony_ci} 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_cistatic int inet_rtm_valid_getroute_req(struct sk_buff *skb, 31348c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh, 31358c2ecf20Sopenharmony_ci struct nlattr **tb, 31368c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 31378c2ecf20Sopenharmony_ci{ 31388c2ecf20Sopenharmony_ci struct rtmsg *rtm; 31398c2ecf20Sopenharmony_ci int i, err; 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 31428c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, 31438c2ecf20Sopenharmony_ci "ipv4: Invalid header for route get request"); 31448c2ecf20Sopenharmony_ci return -EINVAL; 31458c2ecf20Sopenharmony_ci } 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci if (!netlink_strict_get_check(skb)) 31488c2ecf20Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 31498c2ecf20Sopenharmony_ci rtm_ipv4_policy, extack); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 31528c2ecf20Sopenharmony_ci if ((rtm->rtm_src_len && rtm->rtm_src_len != 32) || 31538c2ecf20Sopenharmony_ci (rtm->rtm_dst_len && rtm->rtm_dst_len != 32) || 31548c2ecf20Sopenharmony_ci rtm->rtm_table || rtm->rtm_protocol || 31558c2ecf20Sopenharmony_ci rtm->rtm_scope || rtm->rtm_type) { 31568c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for route get request"); 31578c2ecf20Sopenharmony_ci return -EINVAL; 31588c2ecf20Sopenharmony_ci } 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci if (rtm->rtm_flags & ~(RTM_F_NOTIFY | 31618c2ecf20Sopenharmony_ci RTM_F_LOOKUP_TABLE | 31628c2ecf20Sopenharmony_ci RTM_F_FIB_MATCH)) { 31638c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Unsupported rtm_flags for route get request"); 31648c2ecf20Sopenharmony_ci return -EINVAL; 31658c2ecf20Sopenharmony_ci } 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 31688c2ecf20Sopenharmony_ci rtm_ipv4_policy, extack); 31698c2ecf20Sopenharmony_ci if (err) 31708c2ecf20Sopenharmony_ci return err; 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci if ((tb[RTA_SRC] && !rtm->rtm_src_len) || 31738c2ecf20Sopenharmony_ci (tb[RTA_DST] && !rtm->rtm_dst_len)) { 31748c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: rtm_src_len and rtm_dst_len must be 32 for IPv4"); 31758c2ecf20Sopenharmony_ci return -EINVAL; 31768c2ecf20Sopenharmony_ci } 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci for (i = 0; i <= RTA_MAX; i++) { 31798c2ecf20Sopenharmony_ci if (!tb[i]) 31808c2ecf20Sopenharmony_ci continue; 31818c2ecf20Sopenharmony_ci 31828c2ecf20Sopenharmony_ci switch (i) { 31838c2ecf20Sopenharmony_ci case RTA_IIF: 31848c2ecf20Sopenharmony_ci case RTA_OIF: 31858c2ecf20Sopenharmony_ci case RTA_SRC: 31868c2ecf20Sopenharmony_ci case RTA_DST: 31878c2ecf20Sopenharmony_ci case RTA_IP_PROTO: 31888c2ecf20Sopenharmony_ci case RTA_SPORT: 31898c2ecf20Sopenharmony_ci case RTA_DPORT: 31908c2ecf20Sopenharmony_ci case RTA_MARK: 31918c2ecf20Sopenharmony_ci case RTA_UID: 31928c2ecf20Sopenharmony_ci break; 31938c2ecf20Sopenharmony_ci default: 31948c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in route get request"); 31958c2ecf20Sopenharmony_ci return -EINVAL; 31968c2ecf20Sopenharmony_ci } 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci return 0; 32008c2ecf20Sopenharmony_ci} 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_cistatic int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 32038c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 32048c2ecf20Sopenharmony_ci{ 32058c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 32068c2ecf20Sopenharmony_ci struct nlattr *tb[RTA_MAX+1]; 32078c2ecf20Sopenharmony_ci u32 table_id = RT_TABLE_MAIN; 32088c2ecf20Sopenharmony_ci __be16 sport = 0, dport = 0; 32098c2ecf20Sopenharmony_ci struct fib_result res = {}; 32108c2ecf20Sopenharmony_ci u8 ip_proto = IPPROTO_UDP; 32118c2ecf20Sopenharmony_ci struct rtable *rt = NULL; 32128c2ecf20Sopenharmony_ci struct sk_buff *skb; 32138c2ecf20Sopenharmony_ci struct rtmsg *rtm; 32148c2ecf20Sopenharmony_ci struct flowi4 fl4 = {}; 32158c2ecf20Sopenharmony_ci __be32 dst = 0; 32168c2ecf20Sopenharmony_ci __be32 src = 0; 32178c2ecf20Sopenharmony_ci kuid_t uid; 32188c2ecf20Sopenharmony_ci u32 iif; 32198c2ecf20Sopenharmony_ci int err; 32208c2ecf20Sopenharmony_ci int mark; 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci err = inet_rtm_valid_getroute_req(in_skb, nlh, tb, extack); 32238c2ecf20Sopenharmony_ci if (err < 0) 32248c2ecf20Sopenharmony_ci return err; 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 32278c2ecf20Sopenharmony_ci src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; 32288c2ecf20Sopenharmony_ci dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; 32298c2ecf20Sopenharmony_ci iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; 32308c2ecf20Sopenharmony_ci mark = tb[RTA_MARK] ? nla_get_u32(tb[RTA_MARK]) : 0; 32318c2ecf20Sopenharmony_ci if (tb[RTA_UID]) 32328c2ecf20Sopenharmony_ci uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID])); 32338c2ecf20Sopenharmony_ci else 32348c2ecf20Sopenharmony_ci uid = (iif ? INVALID_UID : current_uid()); 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci if (tb[RTA_IP_PROTO]) { 32378c2ecf20Sopenharmony_ci err = rtm_getroute_parse_ip_proto(tb[RTA_IP_PROTO], 32388c2ecf20Sopenharmony_ci &ip_proto, AF_INET, extack); 32398c2ecf20Sopenharmony_ci if (err) 32408c2ecf20Sopenharmony_ci return err; 32418c2ecf20Sopenharmony_ci } 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if (tb[RTA_SPORT]) 32448c2ecf20Sopenharmony_ci sport = nla_get_be16(tb[RTA_SPORT]); 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci if (tb[RTA_DPORT]) 32478c2ecf20Sopenharmony_ci dport = nla_get_be16(tb[RTA_DPORT]); 32488c2ecf20Sopenharmony_ci 32498c2ecf20Sopenharmony_ci skb = inet_rtm_getroute_build_skb(src, dst, ip_proto, sport, dport); 32508c2ecf20Sopenharmony_ci if (!skb) 32518c2ecf20Sopenharmony_ci return -ENOBUFS; 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci fl4.daddr = dst; 32548c2ecf20Sopenharmony_ci fl4.saddr = src; 32558c2ecf20Sopenharmony_ci fl4.flowi4_tos = rtm->rtm_tos & IPTOS_RT_MASK; 32568c2ecf20Sopenharmony_ci fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0; 32578c2ecf20Sopenharmony_ci fl4.flowi4_mark = mark; 32588c2ecf20Sopenharmony_ci fl4.flowi4_uid = uid; 32598c2ecf20Sopenharmony_ci if (sport) 32608c2ecf20Sopenharmony_ci fl4.fl4_sport = sport; 32618c2ecf20Sopenharmony_ci if (dport) 32628c2ecf20Sopenharmony_ci fl4.fl4_dport = dport; 32638c2ecf20Sopenharmony_ci fl4.flowi4_proto = ip_proto; 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_ci rcu_read_lock(); 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci if (iif) { 32688c2ecf20Sopenharmony_ci struct net_device *dev; 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(net, iif); 32718c2ecf20Sopenharmony_ci if (!dev) { 32728c2ecf20Sopenharmony_ci err = -ENODEV; 32738c2ecf20Sopenharmony_ci goto errout_rcu; 32748c2ecf20Sopenharmony_ci } 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci fl4.flowi4_iif = iif; /* for rt_fill_info */ 32778c2ecf20Sopenharmony_ci skb->dev = dev; 32788c2ecf20Sopenharmony_ci skb->mark = mark; 32798c2ecf20Sopenharmony_ci err = ip_route_input_rcu(skb, dst, src, 32808c2ecf20Sopenharmony_ci rtm->rtm_tos & IPTOS_RT_MASK, dev, 32818c2ecf20Sopenharmony_ci &res); 32828c2ecf20Sopenharmony_ci 32838c2ecf20Sopenharmony_ci rt = skb_rtable(skb); 32848c2ecf20Sopenharmony_ci if (err == 0 && rt->dst.error) 32858c2ecf20Sopenharmony_ci err = -rt->dst.error; 32868c2ecf20Sopenharmony_ci } else { 32878c2ecf20Sopenharmony_ci fl4.flowi4_iif = LOOPBACK_IFINDEX; 32888c2ecf20Sopenharmony_ci skb->dev = net->loopback_dev; 32898c2ecf20Sopenharmony_ci rt = ip_route_output_key_hash_rcu(net, &fl4, &res, skb); 32908c2ecf20Sopenharmony_ci err = 0; 32918c2ecf20Sopenharmony_ci if (IS_ERR(rt)) 32928c2ecf20Sopenharmony_ci err = PTR_ERR(rt); 32938c2ecf20Sopenharmony_ci else 32948c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 32958c2ecf20Sopenharmony_ci } 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci if (err) 32988c2ecf20Sopenharmony_ci goto errout_rcu; 32998c2ecf20Sopenharmony_ci 33008c2ecf20Sopenharmony_ci if (rtm->rtm_flags & RTM_F_NOTIFY) 33018c2ecf20Sopenharmony_ci rt->rt_flags |= RTCF_NOTIFY; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE) 33048c2ecf20Sopenharmony_ci table_id = res.table ? res.table->tb_id : 0; 33058c2ecf20Sopenharmony_ci 33068c2ecf20Sopenharmony_ci /* reset skb for netlink reply msg */ 33078c2ecf20Sopenharmony_ci skb_trim(skb, 0); 33088c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 33098c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 33108c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci if (rtm->rtm_flags & RTM_F_FIB_MATCH) { 33138c2ecf20Sopenharmony_ci struct fib_rt_info fri; 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_ci if (!res.fi) { 33168c2ecf20Sopenharmony_ci err = fib_props[res.type].error; 33178c2ecf20Sopenharmony_ci if (!err) 33188c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 33198c2ecf20Sopenharmony_ci goto errout_rcu; 33208c2ecf20Sopenharmony_ci } 33218c2ecf20Sopenharmony_ci fri.fi = res.fi; 33228c2ecf20Sopenharmony_ci fri.tb_id = table_id; 33238c2ecf20Sopenharmony_ci fri.dst = res.prefix; 33248c2ecf20Sopenharmony_ci fri.dst_len = res.prefixlen; 33258c2ecf20Sopenharmony_ci fri.tos = fl4.flowi4_tos; 33268c2ecf20Sopenharmony_ci fri.type = rt->rt_type; 33278c2ecf20Sopenharmony_ci fri.offload = 0; 33288c2ecf20Sopenharmony_ci fri.trap = 0; 33298c2ecf20Sopenharmony_ci if (res.fa_head) { 33308c2ecf20Sopenharmony_ci struct fib_alias *fa; 33318c2ecf20Sopenharmony_ci 33328c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(fa, res.fa_head, fa_list) { 33338c2ecf20Sopenharmony_ci u8 slen = 32 - fri.dst_len; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci if (fa->fa_slen == slen && 33368c2ecf20Sopenharmony_ci fa->tb_id == fri.tb_id && 33378c2ecf20Sopenharmony_ci fa->fa_tos == fri.tos && 33388c2ecf20Sopenharmony_ci fa->fa_info == res.fi && 33398c2ecf20Sopenharmony_ci fa->fa_type == fri.type) { 33408c2ecf20Sopenharmony_ci fri.offload = fa->offload; 33418c2ecf20Sopenharmony_ci fri.trap = fa->trap; 33428c2ecf20Sopenharmony_ci break; 33438c2ecf20Sopenharmony_ci } 33448c2ecf20Sopenharmony_ci } 33458c2ecf20Sopenharmony_ci } 33468c2ecf20Sopenharmony_ci err = fib_dump_info(skb, NETLINK_CB(in_skb).portid, 33478c2ecf20Sopenharmony_ci nlh->nlmsg_seq, RTM_NEWROUTE, &fri, 0); 33488c2ecf20Sopenharmony_ci } else { 33498c2ecf20Sopenharmony_ci err = rt_fill_info(net, dst, src, rt, table_id, &fl4, skb, 33508c2ecf20Sopenharmony_ci NETLINK_CB(in_skb).portid, 33518c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 0); 33528c2ecf20Sopenharmony_ci } 33538c2ecf20Sopenharmony_ci if (err < 0) 33548c2ecf20Sopenharmony_ci goto errout_rcu; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci rcu_read_unlock(); 33578c2ecf20Sopenharmony_ci 33588c2ecf20Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_cierrout_free: 33618c2ecf20Sopenharmony_ci return err; 33628c2ecf20Sopenharmony_cierrout_rcu: 33638c2ecf20Sopenharmony_ci rcu_read_unlock(); 33648c2ecf20Sopenharmony_ci kfree_skb(skb); 33658c2ecf20Sopenharmony_ci goto errout_free; 33668c2ecf20Sopenharmony_ci} 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_civoid ip_rt_multicast_event(struct in_device *in_dev) 33698c2ecf20Sopenharmony_ci{ 33708c2ecf20Sopenharmony_ci rt_cache_flush(dev_net(in_dev->dev)); 33718c2ecf20Sopenharmony_ci} 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 33748c2ecf20Sopenharmony_cistatic int ip_rt_gc_interval __read_mostly = 60 * HZ; 33758c2ecf20Sopenharmony_cistatic int ip_rt_gc_min_interval __read_mostly = HZ / 2; 33768c2ecf20Sopenharmony_cistatic int ip_rt_gc_elasticity __read_mostly = 8; 33778c2ecf20Sopenharmony_cistatic int ip_min_valid_pmtu __read_mostly = IPV4_MIN_MTU; 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_cistatic int ipv4_sysctl_rtcache_flush(struct ctl_table *__ctl, int write, 33808c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 33818c2ecf20Sopenharmony_ci{ 33828c2ecf20Sopenharmony_ci struct net *net = (struct net *)__ctl->extra1; 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci if (write) { 33858c2ecf20Sopenharmony_ci rt_cache_flush(net); 33868c2ecf20Sopenharmony_ci fnhe_genid_bump(net); 33878c2ecf20Sopenharmony_ci return 0; 33888c2ecf20Sopenharmony_ci } 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci return -EINVAL; 33918c2ecf20Sopenharmony_ci} 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_cistatic struct ctl_table ipv4_route_table[] = { 33948c2ecf20Sopenharmony_ci { 33958c2ecf20Sopenharmony_ci .procname = "gc_thresh", 33968c2ecf20Sopenharmony_ci .data = &ipv4_dst_ops.gc_thresh, 33978c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 33988c2ecf20Sopenharmony_ci .mode = 0644, 33998c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34008c2ecf20Sopenharmony_ci }, 34018c2ecf20Sopenharmony_ci { 34028c2ecf20Sopenharmony_ci .procname = "max_size", 34038c2ecf20Sopenharmony_ci .data = &ip_rt_max_size, 34048c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34058c2ecf20Sopenharmony_ci .mode = 0644, 34068c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34078c2ecf20Sopenharmony_ci }, 34088c2ecf20Sopenharmony_ci { 34098c2ecf20Sopenharmony_ci /* Deprecated. Use gc_min_interval_ms */ 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci .procname = "gc_min_interval", 34128c2ecf20Sopenharmony_ci .data = &ip_rt_gc_min_interval, 34138c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34148c2ecf20Sopenharmony_ci .mode = 0644, 34158c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 34168c2ecf20Sopenharmony_ci }, 34178c2ecf20Sopenharmony_ci { 34188c2ecf20Sopenharmony_ci .procname = "gc_min_interval_ms", 34198c2ecf20Sopenharmony_ci .data = &ip_rt_gc_min_interval, 34208c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34218c2ecf20Sopenharmony_ci .mode = 0644, 34228c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 34238c2ecf20Sopenharmony_ci }, 34248c2ecf20Sopenharmony_ci { 34258c2ecf20Sopenharmony_ci .procname = "gc_timeout", 34268c2ecf20Sopenharmony_ci .data = &ip_rt_gc_timeout, 34278c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34288c2ecf20Sopenharmony_ci .mode = 0644, 34298c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 34308c2ecf20Sopenharmony_ci }, 34318c2ecf20Sopenharmony_ci { 34328c2ecf20Sopenharmony_ci .procname = "gc_interval", 34338c2ecf20Sopenharmony_ci .data = &ip_rt_gc_interval, 34348c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34358c2ecf20Sopenharmony_ci .mode = 0644, 34368c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 34378c2ecf20Sopenharmony_ci }, 34388c2ecf20Sopenharmony_ci { 34398c2ecf20Sopenharmony_ci .procname = "redirect_load", 34408c2ecf20Sopenharmony_ci .data = &ip_rt_redirect_load, 34418c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34428c2ecf20Sopenharmony_ci .mode = 0644, 34438c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34448c2ecf20Sopenharmony_ci }, 34458c2ecf20Sopenharmony_ci { 34468c2ecf20Sopenharmony_ci .procname = "redirect_number", 34478c2ecf20Sopenharmony_ci .data = &ip_rt_redirect_number, 34488c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34498c2ecf20Sopenharmony_ci .mode = 0644, 34508c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34518c2ecf20Sopenharmony_ci }, 34528c2ecf20Sopenharmony_ci { 34538c2ecf20Sopenharmony_ci .procname = "redirect_silence", 34548c2ecf20Sopenharmony_ci .data = &ip_rt_redirect_silence, 34558c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34568c2ecf20Sopenharmony_ci .mode = 0644, 34578c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34588c2ecf20Sopenharmony_ci }, 34598c2ecf20Sopenharmony_ci { 34608c2ecf20Sopenharmony_ci .procname = "error_cost", 34618c2ecf20Sopenharmony_ci .data = &ip_rt_error_cost, 34628c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34638c2ecf20Sopenharmony_ci .mode = 0644, 34648c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34658c2ecf20Sopenharmony_ci }, 34668c2ecf20Sopenharmony_ci { 34678c2ecf20Sopenharmony_ci .procname = "error_burst", 34688c2ecf20Sopenharmony_ci .data = &ip_rt_error_burst, 34698c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34708c2ecf20Sopenharmony_ci .mode = 0644, 34718c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34728c2ecf20Sopenharmony_ci }, 34738c2ecf20Sopenharmony_ci { 34748c2ecf20Sopenharmony_ci .procname = "gc_elasticity", 34758c2ecf20Sopenharmony_ci .data = &ip_rt_gc_elasticity, 34768c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34778c2ecf20Sopenharmony_ci .mode = 0644, 34788c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 34798c2ecf20Sopenharmony_ci }, 34808c2ecf20Sopenharmony_ci { 34818c2ecf20Sopenharmony_ci .procname = "mtu_expires", 34828c2ecf20Sopenharmony_ci .data = &ip_rt_mtu_expires, 34838c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34848c2ecf20Sopenharmony_ci .mode = 0644, 34858c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 34868c2ecf20Sopenharmony_ci }, 34878c2ecf20Sopenharmony_ci { 34888c2ecf20Sopenharmony_ci .procname = "min_pmtu", 34898c2ecf20Sopenharmony_ci .data = &ip_rt_min_pmtu, 34908c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34918c2ecf20Sopenharmony_ci .mode = 0644, 34928c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 34938c2ecf20Sopenharmony_ci .extra1 = &ip_min_valid_pmtu, 34948c2ecf20Sopenharmony_ci }, 34958c2ecf20Sopenharmony_ci { 34968c2ecf20Sopenharmony_ci .procname = "min_adv_mss", 34978c2ecf20Sopenharmony_ci .data = &ip_rt_min_advmss, 34988c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 34998c2ecf20Sopenharmony_ci .mode = 0644, 35008c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 35018c2ecf20Sopenharmony_ci }, 35028c2ecf20Sopenharmony_ci { } 35038c2ecf20Sopenharmony_ci}; 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_cistatic const char ipv4_route_flush_procname[] = "flush"; 35068c2ecf20Sopenharmony_ci 35078c2ecf20Sopenharmony_cistatic struct ctl_table ipv4_route_flush_table[] = { 35088c2ecf20Sopenharmony_ci { 35098c2ecf20Sopenharmony_ci .procname = ipv4_route_flush_procname, 35108c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 35118c2ecf20Sopenharmony_ci .mode = 0200, 35128c2ecf20Sopenharmony_ci .proc_handler = ipv4_sysctl_rtcache_flush, 35138c2ecf20Sopenharmony_ci }, 35148c2ecf20Sopenharmony_ci { }, 35158c2ecf20Sopenharmony_ci}; 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_cistatic __net_init int sysctl_route_net_init(struct net *net) 35188c2ecf20Sopenharmony_ci{ 35198c2ecf20Sopenharmony_ci struct ctl_table *tbl; 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_ci tbl = ipv4_route_flush_table; 35228c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) { 35238c2ecf20Sopenharmony_ci tbl = kmemdup(tbl, sizeof(ipv4_route_flush_table), GFP_KERNEL); 35248c2ecf20Sopenharmony_ci if (!tbl) 35258c2ecf20Sopenharmony_ci goto err_dup; 35268c2ecf20Sopenharmony_ci 35278c2ecf20Sopenharmony_ci /* Don't export non-whitelisted sysctls to unprivileged users */ 35288c2ecf20Sopenharmony_ci if (net->user_ns != &init_user_ns) { 35298c2ecf20Sopenharmony_ci if (tbl[0].procname != ipv4_route_flush_procname) 35308c2ecf20Sopenharmony_ci tbl[0].procname = NULL; 35318c2ecf20Sopenharmony_ci } 35328c2ecf20Sopenharmony_ci } 35338c2ecf20Sopenharmony_ci tbl[0].extra1 = net; 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci net->ipv4.route_hdr = register_net_sysctl(net, "net/ipv4/route", tbl); 35368c2ecf20Sopenharmony_ci if (!net->ipv4.route_hdr) 35378c2ecf20Sopenharmony_ci goto err_reg; 35388c2ecf20Sopenharmony_ci return 0; 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_cierr_reg: 35418c2ecf20Sopenharmony_ci if (tbl != ipv4_route_flush_table) 35428c2ecf20Sopenharmony_ci kfree(tbl); 35438c2ecf20Sopenharmony_cierr_dup: 35448c2ecf20Sopenharmony_ci return -ENOMEM; 35458c2ecf20Sopenharmony_ci} 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_cistatic __net_exit void sysctl_route_net_exit(struct net *net) 35488c2ecf20Sopenharmony_ci{ 35498c2ecf20Sopenharmony_ci struct ctl_table *tbl; 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci tbl = net->ipv4.route_hdr->ctl_table_arg; 35528c2ecf20Sopenharmony_ci unregister_net_sysctl_table(net->ipv4.route_hdr); 35538c2ecf20Sopenharmony_ci BUG_ON(tbl == ipv4_route_flush_table); 35548c2ecf20Sopenharmony_ci kfree(tbl); 35558c2ecf20Sopenharmony_ci} 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_cistatic __net_initdata struct pernet_operations sysctl_route_ops = { 35588c2ecf20Sopenharmony_ci .init = sysctl_route_net_init, 35598c2ecf20Sopenharmony_ci .exit = sysctl_route_net_exit, 35608c2ecf20Sopenharmony_ci}; 35618c2ecf20Sopenharmony_ci#endif 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_cistatic __net_init int rt_genid_init(struct net *net) 35648c2ecf20Sopenharmony_ci{ 35658c2ecf20Sopenharmony_ci atomic_set(&net->ipv4.rt_genid, 0); 35668c2ecf20Sopenharmony_ci atomic_set(&net->fnhe_genid, 0); 35678c2ecf20Sopenharmony_ci atomic_set(&net->ipv4.dev_addr_genid, get_random_int()); 35688c2ecf20Sopenharmony_ci return 0; 35698c2ecf20Sopenharmony_ci} 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_cistatic __net_initdata struct pernet_operations rt_genid_ops = { 35728c2ecf20Sopenharmony_ci .init = rt_genid_init, 35738c2ecf20Sopenharmony_ci}; 35748c2ecf20Sopenharmony_ci 35758c2ecf20Sopenharmony_cistatic int __net_init ipv4_inetpeer_init(struct net *net) 35768c2ecf20Sopenharmony_ci{ 35778c2ecf20Sopenharmony_ci struct inet_peer_base *bp = kmalloc(sizeof(*bp), GFP_KERNEL); 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci if (!bp) 35808c2ecf20Sopenharmony_ci return -ENOMEM; 35818c2ecf20Sopenharmony_ci inet_peer_base_init(bp); 35828c2ecf20Sopenharmony_ci net->ipv4.peers = bp; 35838c2ecf20Sopenharmony_ci return 0; 35848c2ecf20Sopenharmony_ci} 35858c2ecf20Sopenharmony_ci 35868c2ecf20Sopenharmony_cistatic void __net_exit ipv4_inetpeer_exit(struct net *net) 35878c2ecf20Sopenharmony_ci{ 35888c2ecf20Sopenharmony_ci struct inet_peer_base *bp = net->ipv4.peers; 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci net->ipv4.peers = NULL; 35918c2ecf20Sopenharmony_ci inetpeer_invalidate_tree(bp); 35928c2ecf20Sopenharmony_ci kfree(bp); 35938c2ecf20Sopenharmony_ci} 35948c2ecf20Sopenharmony_ci 35958c2ecf20Sopenharmony_cistatic __net_initdata struct pernet_operations ipv4_inetpeer_ops = { 35968c2ecf20Sopenharmony_ci .init = ipv4_inetpeer_init, 35978c2ecf20Sopenharmony_ci .exit = ipv4_inetpeer_exit, 35988c2ecf20Sopenharmony_ci}; 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 36018c2ecf20Sopenharmony_cistruct ip_rt_acct __percpu *ip_rt_acct __read_mostly; 36028c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_ROUTE_CLASSID */ 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ciint __init ip_rt_init(void) 36058c2ecf20Sopenharmony_ci{ 36068c2ecf20Sopenharmony_ci void *idents_hash; 36078c2ecf20Sopenharmony_ci int cpu; 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_ci /* For modern hosts, this will use 2 MB of memory */ 36108c2ecf20Sopenharmony_ci idents_hash = alloc_large_system_hash("IP idents", 36118c2ecf20Sopenharmony_ci sizeof(*ip_idents) + sizeof(*ip_tstamps), 36128c2ecf20Sopenharmony_ci 0, 36138c2ecf20Sopenharmony_ci 16, /* one bucket per 64 KB */ 36148c2ecf20Sopenharmony_ci HASH_ZERO, 36158c2ecf20Sopenharmony_ci NULL, 36168c2ecf20Sopenharmony_ci &ip_idents_mask, 36178c2ecf20Sopenharmony_ci 2048, 36188c2ecf20Sopenharmony_ci 256*1024); 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_ci ip_idents = idents_hash; 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_ci prandom_bytes(ip_idents, (ip_idents_mask + 1) * sizeof(*ip_idents)); 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci ip_tstamps = idents_hash + (ip_idents_mask + 1) * sizeof(*ip_idents); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 36278c2ecf20Sopenharmony_ci struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu); 36288c2ecf20Sopenharmony_ci 36298c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ul->head); 36308c2ecf20Sopenharmony_ci spin_lock_init(&ul->lock); 36318c2ecf20Sopenharmony_ci } 36328c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 36338c2ecf20Sopenharmony_ci ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct)); 36348c2ecf20Sopenharmony_ci if (!ip_rt_acct) 36358c2ecf20Sopenharmony_ci panic("IP: failed to allocate ip_rt_acct\n"); 36368c2ecf20Sopenharmony_ci#endif 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci ipv4_dst_ops.kmem_cachep = 36398c2ecf20Sopenharmony_ci kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0, 36408c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); 36418c2ecf20Sopenharmony_ci 36428c2ecf20Sopenharmony_ci ipv4_dst_blackhole_ops.kmem_cachep = ipv4_dst_ops.kmem_cachep; 36438c2ecf20Sopenharmony_ci 36448c2ecf20Sopenharmony_ci if (dst_entries_init(&ipv4_dst_ops) < 0) 36458c2ecf20Sopenharmony_ci panic("IP: failed to allocate ipv4_dst_ops counter\n"); 36468c2ecf20Sopenharmony_ci 36478c2ecf20Sopenharmony_ci if (dst_entries_init(&ipv4_dst_blackhole_ops) < 0) 36488c2ecf20Sopenharmony_ci panic("IP: failed to allocate ipv4_dst_blackhole_ops counter\n"); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci ipv4_dst_ops.gc_thresh = ~0; 36518c2ecf20Sopenharmony_ci ip_rt_max_size = INT_MAX; 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci devinet_init(); 36548c2ecf20Sopenharmony_ci ip_fib_init(); 36558c2ecf20Sopenharmony_ci 36568c2ecf20Sopenharmony_ci if (ip_rt_proc_init()) 36578c2ecf20Sopenharmony_ci pr_err("Unable to create route proc files\n"); 36588c2ecf20Sopenharmony_ci#ifdef CONFIG_XFRM 36598c2ecf20Sopenharmony_ci xfrm_init(); 36608c2ecf20Sopenharmony_ci xfrm4_init(); 36618c2ecf20Sopenharmony_ci#endif 36628c2ecf20Sopenharmony_ci rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, 36638c2ecf20Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 36668c2ecf20Sopenharmony_ci register_pernet_subsys(&sysctl_route_ops); 36678c2ecf20Sopenharmony_ci#endif 36688c2ecf20Sopenharmony_ci register_pernet_subsys(&rt_genid_ops); 36698c2ecf20Sopenharmony_ci register_pernet_subsys(&ipv4_inetpeer_ops); 36708c2ecf20Sopenharmony_ci return 0; 36718c2ecf20Sopenharmony_ci} 36728c2ecf20Sopenharmony_ci 36738c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 36748c2ecf20Sopenharmony_ci/* 36758c2ecf20Sopenharmony_ci * We really need to sanitize the damn ipv4 init order, then all 36768c2ecf20Sopenharmony_ci * this nonsense will go away. 36778c2ecf20Sopenharmony_ci */ 36788c2ecf20Sopenharmony_civoid __init ip_static_sysctl_init(void) 36798c2ecf20Sopenharmony_ci{ 36808c2ecf20Sopenharmony_ci register_net_sysctl(&init_net, "net/ipv4/route", ipv4_route_table); 36818c2ecf20Sopenharmony_ci} 36828c2ecf20Sopenharmony_ci#endif 3683