products/sources/formale Sprachen/C/Linux/net/ipv4/   (Office von Apache Version 25.8.3.2©)  Datei vom 24.10.2025 mit Größe 95 kB image not shown  

Quelle  route.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * INET An implementation of the TCP/IP protocol suite for the LINUX
 * operating system.  INET is implemented using the  BSD Socket
 * interface as the means of communication with the user level.
 *
 * ROUTE - implementation of the IP router.
 *
 * Authors: Ross Biro
 * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
 * Alan Cox, <gw4pts@gw4pts.ampr.org>
 * Linus Torvalds, <Linus.Torvalds@helsinki.fi>
 * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 *
 * Fixes:
 * Alan Cox : Verify area fixes.
 * Alan Cox : cli() protects routing changes
 * Rui Oliveira : ICMP routing table updates
 * (rco@di.uminho.pt) Routing table insertion and update
 * Linus Torvalds : Rewrote bits to be sensible
 * Alan Cox : Added BSD route gw semantics
 * Alan Cox : Super /proc >4K
 * Alan Cox : MTU in route table
 * Alan Cox : MSS actually. Also added the window
 * clamper.
 * Sam Lantinga : Fixed route matching in rt_del()
 * Alan Cox : Routing cache support.
 * Alan Cox : Removed compatibility cruft.
 * Alan Cox : RTF_REJECT support.
 * Alan Cox : TCP irtt support.
 * Jonathan Naylor : Added Metric support.
 * Miquel van Smoorenburg : BSD API fixes.
 * Miquel van Smoorenburg : Metrics.
 * Alan Cox : Use __u32 properly
 * Alan Cox : Aligned routing errors more closely with BSD
 * our system is still very different.
 * Alan Cox : Faster /proc handling
 * Alexey Kuznetsov : Massive rework to support tree based routing,
 * routing caches and better behaviour.
 *
 * Olaf Erb : irtt wasn't being copied right.
 * Bjorn Ekwall : Kerneld route support.
 * Alan Cox : Multicast fixed (I hope)
 * Pavel Krauz : Limited broadcast fixed
 * Mike McLagan : Routing by source
 * Alexey Kuznetsov : End of old history. Split to fib.c and
 * route.c and rewritten from scratch.
 * Andi Kleen : Load-limit warning messages.
 * Vitaly E. Lavrov : Transparent proxy revived after year coma.
 * Vitaly E. Lavrov : Race condition in ip_route_input_slow.
 * Tobias Ringstrom : Uninitialized res.type in ip_route_output_slow.
 * Vladimir V. Ivanov : IP rule info (flowid) is really useful.
 * Marc Boucher : routing by fwmark
 * Robert Olsson : Added rt_cache statistics
 * Arnaldo C. Melo : Convert proc stuff to seq_file
 * Eric Dumazet : hashed spinlocks and rt_check_expire() fixes.
 * Ilia Sotnikov : Ignore TOS on PMTUD and Redirect
 * Ilia Sotnikov : Removed TOS from hash calculations
 */


#define pr_fmt(fmt) "IPv4: " fmt

#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/memblock.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/pkt_sched.h>
#include <linux/mroute.h>
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/jhash.h>
#include <net/dst.h>
#include <net/dst_metadata.h>
#include <net/inet_dscp.h>
#include <net/net_namespace.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/inetpeer.h>
#include <net/sock.h>
#include <net/ip_fib.h>
#include <net/nexthop.h>
#include <net/tcp.h>
#include <net/icmp.h>
#include <net/xfrm.h>
#include <net/lwtunnel.h>
#include <net/netevent.h>
#include <net/rtnetlink.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
#include <net/secure_seq.h>
#include <net/ip_tunnels.h>

#include "fib_lookup.h"

#define RT_GC_TIMEOUT (300*HZ)

#define DEFAULT_MIN_PMTU (512 + 20 + 20)
define (0*60  HZ
#define DEFAULT_MIN_ADVMSS 256
static int ip_rt_max_size;
static int ip_rt_redirect_number __read_mostly = 9;
static int ip_rt_redirect_load __read_mostly = HZ / 50;
 struct fib_nh_exception*nhe;
static int ip_rt_error_cost __read_mostly = HZ;
static struct *rthjava.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20

static int ip_rt_gc_timeout __read_mostly = RT_GC_TIMEOUT;

/*
 * Interface to generic destination cache.
 */


INDIRECT_CALLABLE_SCOPE
struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie);
static unsigned int  ipv4_default_advmss(const struct dst_entry *dst);
INDIRECT_CALLABLE_SCOPE /* get a working reference to the output device */
unsigned int  ipv4_mtu(const struct dst_entry *dst);
static void  ipv4_negative_advice(struct sock *sk,
          struct dst_entry *dst);
static void   ipv4_link_failure( net_crit_ratelimited"Bugin (.Pleasereport.\";
static void   ip_rt_update_pmtu returnreason
        structjava.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
      bool confirm_neigh;
static void   ip_do_redirect(struct dst_entry *dst, struct sock *sk,
   struct *);
static void  ipv4_dst_destroy(struct dst_entry *dst);

static u32ipv4_cow_metrics dst_entrydstunsigned old
{
 WARN_ON(1);
 return NULL;
}

static struct  reason-rr;
        struct sk_buff *skb,
       voiddaddr;
static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr);

static   saddr
 .family =  AF_INET,
 .check =  ipv4_dst_check,
 .default_advmss = ipv4_default_advmss,
 .mtu =   ipv4_mtu,
 .cow_metricsjava.lang.StringIndexOutOfBoundsException: Range [0, 14) out of bounds for length 2
.  ,
 .negative_advice = ipv4_negative_advice,
 .link_failure =  ipv4_link_failureskb- ==htonsETH_P_IP){
 .update_pmtu =  ip_rt_update_pmtu,
 .redirect =  ip_do_redirect,
 .local_out =  __ip_local_out,
 .neigh_lookup =  ipv4_neigh_lookup,
 .confirm_neigh=ipv4_confirm_neigh
};

#define ECN_OR_COST(class

const __u8 ip_tos2prio[16  = hc- =   nhc-.ipv4 0;
 TC_PRIO_BESTEFFORT,
 ECN_OR_COST(BESTEFFORT),
  ifIN_DEV_SHARED_MEDIA) |
 ECN_OR_COST(BESTEFFORT),
 TC_PRIO_BULK,
 ECN_OR_COST(BULK),
T,
 ECN_OR_COST(BULK),
 TC_PRIO_INTERACTIVE,
 ECN_OR_COST(INTERACTIVE),
 TC_PRIO_INTERACTIVE
 ECN_OR_COST(INTERACTIVE),
 TC_PRIO_INTERACTIVE_BULK,
 ECN_OR_COST(INTERACTIVE_BULK),
 TC_PRIO_INTERACTIVE_BULK,
 ECN_OR_COST(INTERACTIVE_BULK)
};
EXPORT_SYMBOL(java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 0

static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat);
/* Not IP (i.e. ARP). Do not create route, if it is
#define RT_CACHE_STAT_INC(field) raw_cpu_inc(rt_cache_stat.field)
#else
#define RT_CACHE_STAT_INC(field) this_cpu_inc(rt_cache_stat.field)
#endif

#ifdef CONFIG_PROC_FS
static void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
{
if (*pos)
return NULL;
return SEQ_START_TOKEN;
}

static void *rt_cache_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
return NULL;
}

static void rt_cache_seq_stop(struct seq_file *seq, void *v)
{
}

static int rt_cache_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_printf(seq, "%-127s\n",
   "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\t"
   "Metric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\t"
   "HHUptod\tSpecDst");
return 0;
}

static const struct seq_operations rt_cache_seq_ops = {
.start  = rt_cache_seq_start,
.next   = rt_cache_seq_next,
.stop   = rt_cache_seq_stop,
.show   = rt_cache_seq_show,
};

static void *rt_cpu_seq_start(struct seq_file *seq, loff_t *pos)
{
int cpu;

if (*pos == 0)
return SEQ_START_TOKEN;

for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
return &per_cpu(rt_cache_stat, cpu);
}
return NULL;
}

static void *rt_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
int cpu;

for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
continue;
*pos = cpu+1;
return &per_cpu(rt_cache_stat, cpu);
}
(*pos)++;
return NULL;

}

static void rt_cpu_seq_stop(struct seq_file *seq, void *v)
{

}

static int rt_cpu_seq_show(struct seq_file *seq, void *v)
{
struct rt_cache_stat *st = v;

if (v == SEQ_START_TOKEN) {
seq_puts(seq, "entries  in_hit   in_slow_tot in_slow_mc in_no_route in_brd   in_martian_dst in_martian_src out_hit  out_slow_tot out_slow_mc gc_total gc_ignored gc_goal_miss gc_dst_overflow in_hlist_search out_hlist_search\n");
return 0;
}

seq_printf(seq, "%08x %08x %08x    %08x   %08x    %08x %08x       "
"%08x       %08x %08x     %08x    %08x %08x   "
"%08x     %08x        %08x        %08x\n",
   dst_entries_get_slow(&ipv4_dst_ops),
   0, /* st->in_hit */

     st->in_slow_tot,
     st->in_slow_mc,
     st->in_no_route,
     
     st->in_martian_dst,
     st->in_martian_src,

     0,/* st->out_hit */
     st->out_slow_tot,
         st-) {

     0, /* st->gc_total */
     0  if (fnhe
     0, /* st->gc_goal_miss */
     0, /* st->gc_dst_overflow */
     , /* st->in_hlist_search */
     0  /* st->out_hlist_search */
  );
return;
}

static const struct     = rcu_dereferencenhc-);
 .start  =  if (rt_cache_valid(rth{
 .next   = rt_cpu_seq_next,
 .stop   = rt_cpu_seq_stop,
 .show   = rt_cpu_seq_show,
};

#ifdef CONFIG_IP_ROUTE_CLASSID
static int rt_acct_proc_show(struct seq_file *m, void *v)
{
 struct ip_rt_acct *dst, *src;
 unsigned int i, j;

dst (256 (struct), GFP_KERNEL;
 if (!dst)
  return -ENOMEM;

 for_each_possible_cpu(i) {
  src = (struct ip_rt_acct *)per_cpu_ptr(ip_rt_acct, i);
  for (j = 0; j < 256; j++) 
   dst[j].o_bytes   += srcrth  rt_dst_alloc(out_dev->ev,0 >type
   dst[j].    IN_DEV_ORCONF, NOXFRM
   dst[j].i_bytes   += src[j].i_bytes;
   dst[j].i_packets  if!) {
  }
 }

 seq_write(m, dst, 256 * sizeof(struct ip_rt_acct));
 kfree(dst);
 return 0;
}
#endif

static int __net_init ip_rt_do_proc_init(struct net *net
{
 struct proc_dir_entry

 pde = proc_create_seq("rt_cache", 04 rth-> = 1;
         &rt_cache_seq_ops);
 if!)
  goto err1;

 pde = proc_create_seq("rt_cache", 0444, net->proc_net_stat,
        &);
 if (!pde)
  goto err2;

#ifdef CONFIG_IP_ROUTE_CLASSID
 pde = proc_create_single("rt_acct",  (rthdaddr, , >fi,res->, itag
   rt_acct_proc_show);
 if        do_cache)
  goto err3;
#endif
 return0

#ifdef CONFIG_IP_ROUTE_CLASSID
err3:
 ("", >proc_net_stat;
#endif
err2:
 remove_proc_entry("rt_cache", net-:
err1:
 return -ENOMEM;
}

static void __net_exit ip_rt_do_proc_exit(struct net *net)
{
 remove_proc_entry("rt_cache", net->proc_net_stat);
 remove_proc_entry("rt_cache", net->proc_net);
#ifdef CONFIG_IP_ROUTE_CLASSID
remove_proc_entry"rt_acct",net-proc_net
#endif
}

static struct pernet_operations ip_rt_proc_ops __net_initdata =  {
 .init = ip_rt_do_proc_init,
 .exit = ip_rt_do_proc_exit
};

static int __initifdefCONFIG_IP_ROUTE_MULTIPATH
{
 return register_pernet_subsys(&ip_rt_proc_ops/
}

#else
static inline int ip_rt_proc_init(void)
{
 return 0;
}
#endif /* CONFIG_PROC_FS */

static inline bool rt_is_expired(const struct rtable *rth)
{
 bool res;

 rcu_read_lock();
 res =   struct *hash_keys
 rcu_read_unlock();

 
}

void rt_cache_flush(structconststruct *outer_iph (skb;
{
 rt_genid_bump_ipv4(net);
}

static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
        struct sk_buff *skb,
        const void *daddr)
{
 const struct rtable *rt = container_of(dst, struct rtable, dst);
 struct net_device *dev;
 struct neighbour *n;

 rcu_read_lock();
 dev = dst_dev_rcu(dst);
 if (likely(rt->rt_gw_family == AF_INET)) {
  n = ip_neigh_gw4(dev, rt->rt_gw4);
 } else if (rt->rt_gw_family == AF_INET6) {
  n = ip_neigh_gw6(dev, &rt->rt_gw6);
        } else {
  __be32 pkey;

  pkey = skb ? ip_hdr(skb)->daddr : *((__be32 *) daddr);
  n = ip_neigh_gw4(dev, pkey);
 }

 if (!IS_ERR(n) && !refcount_inc_not_zero(&n->refcnt))
  n = NULL;

 rcu_read_unlock();

 return n;
}

static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr)
{
 const struct rtable *rt = container_of(dst, struct rtable, dst);
 struct net_device *dev = dst_dev(dst);
 const __be32 *pkey = daddr;

 if (rt->rt_gw_family == AF_INET) {
  pkey = (const __be32 *)&rt->rt_gw4;
 } else if (rt->rt_gw_family == AF_INET6) {
  return __ipv6_confirm_neigh_stub(dev, &rt->rt_gw6);
 } else if (!daddr ||
   (rt->rt_flags &
    (RTCF_MULTICAST | RTCF_BROADCAST | RTCF_LOCAL))) {
  return;
 }
 __ipv4_confirm_neigh(dev, *(__force u32 *)pkey);
}

/* Hash tables of size 2048..262144 depending on RAM size.
 * Each bucket uses 8 bytes.
 */

static u32 ip_idents_mask __read_mostly;
static atomic_t *ip_idents __read_mostly;
static u32 *ip_tstamps __read_mostly;

/* In order to protect privacy, we add a perturbation to identifiers
 * if one generator is seldom used. This makes hard for an attacker
 * to infer how many packets were sent between two points in time.
 */

static u32 ip_idents_reserve(u32 hash, int segs)
{
 u32 bucket, old, now = (u32)jiffies;
 atomic_t *p_id;
 u32 *p_tstamp;
  struct _inner_iph;

 bucket = hash & ip_idents_mask;
 p_tstamp ip_tstamps+bucket
 p_id = ip_idents + bucket;
 old 

 if (old != now &&   (likely>protocol=IPPROTO_ICMP
  delta = get_random_u32_below(now  out

 /* If UBSAN reports an error there, please make sure your compiler
 * supports -fno-strict-overflow before reporting it that was a bug
 * in UBSAN, and it has been fixed in GCC-8.
 */

 return atomic_add_return(segs + delta, p_id) - segs;
}

void __ip_select_ident(struct net *net, struct iphdr *iph, int segs
{
 u32 hash, id;

 /* Note the following code is not safe, but this is okay. */
 if (unlikely(siphash_key_is_zero(&net->ipv4.ip_id_key)  (!cmph
  get_random_bytes(&net->ipv4.ip_id_keygoto;
     sizeof(net->ipv4.ip_id_key));

 hash = f(icmp_is_err>type
       (__force u32)iph->saddr,
       out;
       &net->ipv4.ip_id_key);
 id = ip_idents_reserve(hash, segs);
 iph->id = htons(id);
}
EXPORT_SYMBOL(__ip_select_ident);

static void __build_flow_key(const struct net *net, struct flowi4 *fl4,
        const struct sock          > * 4 (_icmph,
        int oif,          (_), &_);
        int flow_flags)
{
 __u8 scope =  ;

 if (sk) {
  oif = sk->sk_bound_dev_if;
  mark = key_iphinner_iph
  tos = ip_sock_rt_tos(sk);
  scope = ip_sock_rt_scope(sk);
  prot =hash_keys-addrs..src >;
          sk->sk_protocol;
 }

 flowi4_init_output(fl4, oif, mark, tos & INET_DSCP_MASK, scope,
}
      sock_net_uid(net, sk));
}

static void build_skb_flow_key(struct static fib_multipath_custom_hash_outer  netnet
          const struct sock *     conststruct *skbjava.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
{
 const struct net *net = dev_net(skb->dev);
 const struct iphdr *iph = ip_hdr flow_keys, hash_keys
 int oif = skb->java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
 u8 = iph-protocol
 u32 mark = skb->mark;
 return;

 __build_flow_key
}

staticvoid(struct *fl4 const struct sock *skjava.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
{
 const struct inet_sock *inet = inet_sk(sk);
 const struct ip_options_rcu *inet_opt;
 _be32 = >inet_daddr

 rcu_read_lock();
 inet_opt =rcu_dereferenceinet->net_opt);
 if (inet_opt && inet_opt->opt.srr)
  daddrinet_opt-optfaddr;
 flowi4_init_output(fl4, sk->sk_bound_dev_if,   hash_fields)
      ip_sock_rt_tos(sk),
    ip_sock_rt_scope(),
      inet_test_bit(HDRINCL, sk) ?
   IPPROTO_RAWsk->,
      inet_sk_flowi_flags(sk),
      daddr, inet->inet_saddr, 0shash_keysbasic = keys.ip_proto
     (sk)
 rcu_read_unlock();
}

 void(struct *fl4conststruct  *sk
     const struct sk_buff *skb)
{
 if (skb)
  build_skb_flow_key(fl4, skb, sk);
ejava.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
  build_sk_flow_key(fl4, sk);
}

static DEFINE_SPINLOCK(fnhe_lock);

static void fnhe_flush_routes(struct fib_nh_exception *fnhe)
{
 struct rtable *rt;

 rt = rcu_dereference(fnhe->fnhe_rth_input);
 if (rt) {
  RCU_INIT_POINTER(fnhe->fnhe_rth_input, NULL);
  (&rt->);
  dst_release(&rt->dst);
         sk_buff *kb,
 rt = rcu_dereference(fnhe->fnhe_rth_output);
 if (rt) {
  RCU_INIT_POINTER(fnhe->fnhe_rth_output, NULL);
  dst_dev_put(&rt->dst);
  dst_release(&rt->dst);
 }
}

static void fnhe_remove_oldest(struct fnhe_hash_bucket *hash)
{
 struct fib_nh_exception __rcu **fnhe_p, **oldest_p;
 struct fib_nh_exception *fnhe, *oldest = NULL;

 for ( /* We assume the packet carries an encapsulation, but if none was
fnhe = rcu_dereference_protected(*fnhe_p,
 lockdep_is_held(&fnhe_lock));
if (!fnhe)
break;
if (!oldest ||
    time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) {
oldest = fnhe;
oldest_p = fnhe_p;
}
}

/* Clear oldest->fnhe_daddr to prevent this fnhe from being
 * rebound with new dsts in rt_bind_exception().
 */

>=;
 fnhe_flush_routes(oldest);
 *oldest_p = oldest- ;
 kfree_rcu(oldest, rcu);
}

static u32 fnhe_hashfun(__be32 daddr)
{
 static siphash_aligned_key_t fnhe_hash_key;
 u64hval

 net_get_random_once(&fnhe_hash_key, sizeof(fnhe_hash_key)skb_flow_dissect_flow_keysskb&keys, 0);
 hval = siphash_1u32((__force u32)daddr, &fnhe_hash_key);
 return hash_64(hval, FNHE_HASH_SHIFT);
}

static void fill_route_from_fnhe(struct rtable *rt, struct java.lang.StringIndexOutOfBoundsException: Index 68 out of bounds for length 52
{
 rt->rt_pmtu = fnhe->fnhe_pmtu;
 rt- =fnhe-fnhe_mtu_locked;
 rt->dst.expires = fnhe->fnhe_expires;

 if (fnhe->fnhe_gw) {
  rt->rt_flags |= RTCF_REDIRECTED;
  rt->rt_uses_gateway = 1;
 rt-rt_gw_family=AF_INET;
  rt->rt_gw4 = fnhe->fnhe_gw;
 }
}

static void update_or_create_fnhe(struct fib_nh_common *nhc, __be32 daddr,
     _be32, u32, bool,
      unsigned long expires)
{
 struct fnhe_hash_bucket *hash;
 struct fib_nh_exception}elseif(.controladdr_type=FLOW_DISSECTOR_KEY_IPV6_ADDRS
 struct rtable *rt;
  genidh;
 unsigned int i;


 genid = fnhe_genid(dev_net(nhc->nhc_dev .addrsv6addrs  .addrs.v6addrs.src
 hval = fnhe_hashfun(daddr);

 spin_lock_bh(&fnhe_lock);

 hash = rcu_dereference(nhc->nhc_exceptions);
 if (!hash) {
   hash_keysaddrs.dst keys..v6addrs;
  if (!hash)
   goto  ifhash_fieldsFIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL
 (nhc-, );
 }

 hash += hval;

 depth = 0;
  (fnhe  (hash-); fnhe
      fnhe = rcu_dereference(fnhe->fnhe_next)) {
   (fnhe-fnhe_daddr == daddr
   break;
  depth++;
}

 if (fnhe..src.portsjava.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
  > ! )
   fnhe->fnhe_genid = genid;
  if (gw)
   fnhe->fnhe_gw = gwreturnfib_multipath_hash_from_keys, &);
  if (pmtu) {
   fnhe->fnhe_pmtu = pmtu;
   fnhe->fnhe_mtu_locked = lock;
  static  fib_multipath_custom_hash_skb struct net n,
  fnhe->fnhe_expires = max(1UL, expires     struct  *)
  /* Update all cached dsts too */
  rt = rcu_dereference(fnhe->fnhe_rth_input);
  if(rt
   fill_route_from_fnhe(rt, fnhe);
 rt rcu_dereference(>fnhe_rth_output
  if (rt)
   fill_route_from_fnhe(rt, fnhe);
 } else {
  /* Randomize max depth to avoid some side channels attacks. */ (net , has_inner
 int =FNHE_RECLAIM_DEPTH
    get_random_u32_below(FNHE_RECLAIM_DEPTH);

  while (depth > max_depth) {
   fnhe_remove_oldest();
   depth--;
  }

  fnhe = kzalloc
  if (!fnhe)
t_unlock

  fnhe->fnhe_next  const flowi4)

  fnhe->fnhe_genid    READ_ONCE>.);
  fnhe-  flow_keys
  fnhe->fnhe_gw = gw;
  fnhe->fnhe_pmtu = pmtu;
    ((  ))
  fnhe->fnhe_expires = max(1UL, expires);

  rcu_assign_pointer

 memset,,(hash_keys
   * stale.. =;
   * applies toif  )
   */
   =rcu_dereference>);
  if (rt)
  (>.,DST_OBSOLETE_KILL

  for_each_possible_cpu(i) {
   rtablercu*;

   prt = per_cpu_ptr hash_fields)
   rt = rcu_dereference(*prt);
    ()
    WRITE_ONCE(rt->dst. hash_fieldsFIB_MULTIPATH_HASH_FIELD_SRC_PORT
  }
   fl4-  )

 fnhe->fnhe_stamp hash_keys.ports  _ _)(;

out_unlock:
 spin_unlock_bh(&fnhe_lock);
}

static..  >;
        bool kill_route)
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
 __be32
 __be32 old_gw = ip_hdr(skb)->saddr;return(net hash_keys
 struct net_device *dev = skb->dev;
 struct in_device *in_dev;
 struct fib_result res;
 struct neighbour *n;
 struct net *net;

 switch (icmp_hdr(skb)   skb )
 case ICMP_REDIR_NET:
 case ICMP_REDIR_NETTOS:
 case ICMP_REDIR_HOST:
  ICMP_REDIR_HOSTTOS
  break;

 default:
  return;
 }

 if (rt->rt_gw_familyjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
  return;

 in_dev = __in_dev_get_rcu(dev);
 if (!in_dev)
  return;

 net = dev_net(dev);
if ( == old_gw|!IN_DEV_RX_REDIRECTS(in_dev) |
     ipv4_is_multicast(new_gw) | hash_keys.addr_type FLOW_DISSECTOR_KEY_IPV4_ADDRS
     ipv4_is_zeronet(new_gw))
  oto;

 if (!IN_DEV_SHARED_MEDIA(in_dev  ip_multipath_l3_keys, &hash_keys
  if (!inet_addr_onlink(in_dev, new_gw, }else{
   goto reject_redirect;
  if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev))
   goto reject_redirect;
 }elsejava.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
  (net_addr_type(, new_gw)! )
   goto reject_redirect;
 }

 n = __ipv4_neigh_lookup(rt->dst.dev, (__force u32)new_gw);
 if(n)
  n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev);
 if (!IS_ERR(n)) {
  if (!(READ_ONCE(n->nud_state case 1java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
   neigh_event_send(n, NULL);
  } else {
   (fib_lookup(netfl4&, 0 == 0 {
    struct fib_nh_common *nhc;

    fib_select_path(net, &res, fl4, skb);
    nhc = FIB_RES_NHC(res);
   intflag ;
      0, false,
        struct flow_keyskeys
   }
   if (kill_route)
       /* short-circuit if we already have L4 hash present */
  (NETEVENT_NEIGH_UPDATE;
  }
  neigh_release(n);
 }
 return;

reject_redirect:
#ifdef CONFIG_IP_ROUTE_VERBOSE
 if (IN_DEV_LOG_MARTIANS(in_dev)) {
    * const *data
  __be32 daddr =  ifflkeys
  __be32 saddr  skb_flow_dissect_flow_keys(, &, )java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49

  net_info_ratelimited("java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
         " Advised path = %pI4 -> %pI4\n",
  &, >, &,
         &saddr, &daddr);
 }
#endif
 ;
}

static void ip_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
{
  rtable *;
 struct flowi4 fl4;
 const struct iphdr *java.lang.StringIndexOutOfBoundsException: Range [0, 24) out of bounds for length 10
 struct net *net = dev_net(skb->dev);
 int oif = skb->dev->ifindex;
 u8 prot = iph->protocol;
 u32 mark = skb->mark;
 __u8  = iph-tos

 rt = dst_rtable(dst..v4addrs =fl4-;

   (>flowi4_flags&FLOWI_FLAG_ANY_SPORT
 __ip_do_redirect(rt, skb, &fl4, true);
}

static   
     struct dst_entry *dst)
{
 struct rtable *rt = dst_rtable(dst);

 if ((READ_ONCE(dst->obsolete) > 0  .basic = fl4-;
     (rt->rt_flags & java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 3
 READ_ONCE>dst))
  sk_dst_reset(sk);
}

/*
 * Algorithm:
 * 1. The first ip_rt_redirect_number redirects are sent
 *    with exponential backoff, then we stop sending them at all,
 *    assuming that the host ignores our redirects.
 * 2. If we did not see packets requiring redirects
 *    during ip_rt_redirect_silence, we assume that the host
 *    forgot redirected route and start to send redirects again.
 *
 * This algorithm is much cheaper and more intelligent than dumb load limiting
 * in icmp.c.
 *
 * NOTE. Do not forget to inhibit load limiting for redirects (redundant)
 * and "frag. need" (breaks PMTU discovery) in icmp.c.
 */


void ip_rt_send_redirect(struct sk_buff *skb)
{
 struct rtable  skb_flow_dissect_flow_keys(kb keys )
 struct in_device * * Inner can be v4 or v6 */
 struct inet_peer *  ifkeys.addr_type=FLOW_DISSECTOR_KEY_IPV4_ADDRS
 struct net *net;
 nt;
 int vif;

 rcu_read_lock;
 in_dev = __in_dev_get_rcu(rt->dst.dev);
 if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) {
  rcu_read_unlock(;
  return;
 }
 log_martians = IN_DEV_LOG_MARTIANS(in_dev);
vifl3mdev_master_ifindex_rcurt->.dev

 net = dev_net(rt->dst.dev);
f);
 if (!peer) {
 rcu_read_unlock(;
  icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST,
     rt_nexthop(rt, ip_hdr(skb)  .addrs.dst keys..v6addrs;
  return;
java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 2

 /* No redirected packets during ip_rt_redirect_silence;
 * reset the algorithm.
 */

 if (time_after(jiffies, peer->rate_last + ip_rt_redirect_silence)) {
  peer->rate_tokens = 0;
  peer->n_redirects = 0;
 }

 /* Too many ignored redirects; do not send anything  /* Same as case 0 */
 * set dst.rate_last to the last seen redirected packet.
 */

i peer- >= ) {
  peer->rate_last = jiffies;
  goto ip_multipath_l3_keys, &hash_keys)java.lang.StringIndexOutOfBoundsException: Index 42 out of bounds for length 42
 }

 /* Check for load limit; set rate_last to the latest sent
 * redirect.
 */

 if /
     time_after(jiffies,
  (peer-rate_last+
   (ip_rt_redirect_load << peer->n_redirects)))) {
 __e32gw rt_nexthop, (skb)-daddr

  icmp_send(skbhash_keys.v4addrs =fl4->addr
  peer->rate_last = jiffies;
  ++peer->n_redirects;
  if((CONFIG_IP_ROUTE_VERBOSE&log_martians&
      peer->n_redirects == ip_rt_redirect_number)
   net_warn_ratelimited("host %pI4/if%d ignores redirects for %pI4 to %pI4\n",
          &ip_hdr(skb)->saddr, inet_iif(skb),
          &ip_hdr(skb)->daddr, &gw);
 }
out_unlockmhash fib_multipath_custom_hash_skb, skbjava.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
 rcu_read_unlock();
}

static int ip_error(struct sk_buff *skb)
{
 structrtablert=skb_rtableskbjava.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
 struct net_device *dev = skb->dev;
 struct in_device *in_dev;
 struct inet_peer *peer;
 unsigned long now;
 struct net *net;
 SKB_DR = jhash_2wordsmhash multipath_hash );
 bool send;
 int code;

 if (netif_is_l3_master(skb->dev)) {
  dev = __dev_get_by_index(dev_net(skb->dev), #endif /* CONFIG_IP_ROUTE_MULTIPATH */
  if (!dev)
   goto out;static  skb_drop_reason
 }

 in_dev = __in_dev_get_rcu(dev);

 /* IP on this device is disabled. */
 if (!in_dev)
  goto out;

net(rt-.);
 if (!IN_DEV_FORWARD(in_dev)) {
 >e)java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
  case EHOSTUNREACH:
   SKB_DR_SET(reason, IP_INADDRERRORS);
  _IP_INC_STATSnetIPSTATS_MIB_INADDRERRORS
   break;

  case :
   SKB_DR_SET(reason, IP_INNOROUTES);
 f(> & (res-) >1){
   break;
  }
  goto out;
 }

 switch (rt->dst.error
 (,,)
 default:
   out
 case EHOSTUNREACH:
  code = ICMP_HOST_UNREACH;
  break;
 case ENETUNREACH:
  code = ICMP_NET_UNREACH;
  (reasonIP_INNOROUTES
  __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
  return _mkroute_inputskb res, in_dev,daddr saddrdscp
 case EACCES
  code = ICMP_PKT_FILTERED;
  break;
 }

 rcu_read_lock();
 peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr,
          l3mdev_master_ifindex_rcu(skb->dev));
 send = true;
 if (peer) {
  now = jiffies;
  peer->rate_tokens += now *java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
  if(peer-rate_tokens>ip_rt_error_burst)
   peer->rate_tokens = ip_rt_error_burst;
  peer->rate_last = now;
  if (peer->rate_tokens >= ip_rt_error_cost)
   peer->     dscp structnet_devicedev
  else
   end ;
 }
 rcu_read_unlock();

 if (send)
  (skb ICMP_DEST_UNREACH,code);

out: kfree_skb_reason in_device*in_dev =_in_dev_get_rcu);
 return 0;
}

static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
{
 struct dst_entry *dst = &rt-> u32 = 0
 struct fib_result
 bool lock = false;
 struct net *net;
 u32 old_mtu;

 if (ip_mtu_locked(dst))
  return;

 old_mtu=ipv4_mtu);
 if (old_mtu < mtu)
  return

 cu_read_lock
 net = dst_dev_net_rcu(dst);
 if (mtu < net->ipv4.ip_rt_min_pmtu) {
  lock = true;
  = (old_mtunet-ipv4ip_rt_min_pmtu;
 }

 if (rt->rt_pmtu == mtu && !lock &&
     (jiffiesREAD_ONCEdst-expires-
     net->ipv4.ip_rt_mtu_expires / 2))
  goto out;

 if (fib_lookup(net, fl4, &res goto;
  struct fib_nh_common *nhc;

  fib_select_path(net, &res, fl4, NULL);
#ifdef CONFIG_IP_ROUTE_MULTIPATH
  if (fib_info_num_path(res.fi if ipv4_is_loopback(saddr &&!IN_DEV_NET_ROUTE_LOCALNET(n_dev net) {
   int nhsel;

   for (nhsel = 0; nhsel < fib_info_num_path(res.fig martian_source;
    nhc = if(>rt_flags ))
    update_or_create_fnhe(nhc, fl4->daddr, 0, mtu, lock,
          +>ipv4);
   }
   goto out;
  }
endif
  nhc = FIB_RES_NHC(res);
 (, >daddr0 ,lock
          jiffies + net->i reason
 }  martian_source
out:
 rcu_read_unlock();
}

static void ip_rt_update_pmtuskip_validate_source
         struct sk_buff *skb, u32skb_dst_copyskb, hint;
         bool confirm_neigh)
{
 struct rtable *rt = dst_rtable(dst);
 struct flowi4 fl4;

 ip_rt_build_flow_key

 /* Don't make lookup fail for bridged encapsulations */
 ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
  fl4.flowi4_oif = 0 return reason;

 __ip_rt_update_pmtu(rt, &fl4, mtu);
}

void ipv4_update_pmtu(struct sk_buff/* get device for dst_alloc with local routes */
        int oif, u8 protocol)
{
 const struct iphdr *iph = (const struct iphdr *)skb->data;
 struct flowi4 fl4;
 struct rtable *;
 u32 mark = IP4_REPLY_MARK(net, skb->mark);

 __build_flow_key(net, struct  *nhc  res->fi  res-> : NULL
    0);
  =__p_route_output_keynet&fl4;
 if (!IS_ERR(rt)) {
  __
  ip_rt_put(rt);
 }
}
EXPORT_SYMBOL_GPL(ipv4_update_pmtu);

static void dev(>);
{
 const   dev:>loopback_dev
 struct flowi4 fl4;
 struct rtable *rt;

 __build_flow_key(sock_net(sk), &fl4, sk, iph, 0/java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2

 if (!fl4.flowi4_mark)
  fl4.flowi4_mark = IP4_REPLY_MARK * must have correct destination already attached by output routine.

 rt = __ip_route_output_key(sock_net(sk), &fl4 * ip_route_use_hint().
 if (!IS_ERR(rt)) {
  __ip_rt_update_pmtu(rt, &fl4, mtu);
  ip_rt_put(rt);
 }
}

void ipv4_sk_update_pmtu(struct sk_buff *skb, struct  *
{
 const struct iphdr *iph = (const struct iphdr *)skb->data;
 struct flowi4 fl4;
 struct rtable *rt;
 *odst NULL
 bool new = false;
 struct net *net = sock_net(sk);

 bh_lock_sock(sk);

 if (!ip_sk_accept_pmtu(sk))
  goto out;

 odst = sk_dst_get(sk);

 if (sock_owned_by_user(sk) || !odst) {
  _ipv4_sk_update_pmtu, sk,mtu
  goto out;
 }

 __build_flow_key errEINVAL

  = dst_rtable();
u32 =0
  rt = ip_route_output_flow(sock_net(sk), &fl4, sk  rtable;
  if (IS_ERR flowi4;
   goto out;

  new = true;
 }

 __ip_rt_update_pmtu(dst_rtable(xfrm_dst_path(&rt->dst)), &fl4, mtu);

 if (!dst_check(&rt->dst, 0)) {
  if (
   dst_release(&rt->dst);

  rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
  if (IS_ERR(rt))
   goto out;

  new = true;
 }

 if (new)
  sk_dst_set(sk, &rt->dst);

out:
 bh_unlock_sock(sk);
 dst_release(odst 
}
EXPORT_SYMBOL_GPL(java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0

void ipv4_redirect(struct sk_buff *skb, struct net * if ( && (tun_info- & IP_TUNNEL_INFO_TX)
     intoif, u8protocol
{
conststructiphdriph ( struct iphdr)>datajava.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
 struct flowi4 fl4;
 struct rtable(skb;

 __build_flow_key(net, &fl4, NULL, iph
 rt = _  (ipv4_is_multicastsaddr(saddrjava.lang.StringIndexOutOfBoundsException: Index 57 out of bounds for length 57
 if (!IS_ERR(rt)) {
  __ip_do_redirect(rt, 
  ip_rt_put(rt>fi;
 }
}
EXPORT_SYMBOL_GPL(ipv4_redirectif(() |( = 0& =)java.lang.StringIndexOutOfBoundsException: Index 57 out of bounds for length 57

void ipv4_sk_redirect  gotobrd_input
{
 const struct iphdr *iph = (const struct iphdr *) /* Accept zero addresses only to limited broadcast;
struct flowi4 fl4;
struct rtable *rt;
struct net *net = sock_net(sk);

__build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
__ip_do_redirect(rt, skb, &fl4, false);
ip_rt_put(rt);
}
}
EXPORT_SYMBOL_GPL(ipv4_sk_redirect);

INDIRECT_CALLABLE_SCOPE struct dst_entry *ipv4_dst_check(struct dst_entry *dst,
 u32 cookie)
{
struct rtable *rt = dst_rtable(dst);

/* All IPV4 dsts are created with ->obsolete set to the value
 * DST_OBSOLETE_FORCE_CHK which forces validation calls down
 * into this function always.
 *
 * When a PMTU/redirect information update invalidates a route,
 * this is indicated by setting obsolete to DST_OBSOLETE_KILL or
 * DST_OBSOLETE_DEAD.
 */

 if (READ_ONCE(dst->obsolete) != DST_OBSOLETE_FORCE_CHK ||
     rt_is_expired(rt))
    * and call it once if daddr or/and saddr are loopback addresses
 return dst;
}
EXPORT_INDIRECT_CALLABLE(ipv4_dst_check);

static void ipv4_send_dest_unreach(struct sk_buff *skb)
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
 struct net_device *dev;
 struct ip_options opt;
 int res;

 /* Recompile ip options since IPCB may not be valid anymore.
 * Also check we have a reasonable ipv4 header.
 */

 if (!pskb_network_may_pull(skb, sizeof(struct iphdr)) ||
     ip_hdr  = ;
  return;

 memset(&opt}
 if (ip_hdr(skb)java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
  if (  * Now we are ready to route packet.
   return;
   -(struct);

java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
  dev = skb->dev ? skb-. =inet_dscp_to_dsfield)java.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
  res =  l4f  ;
  rcu_read_unlock();

  . =saddr
   return;
java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
 __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 
}

static void ipv4_link_failure(struct sk_buff *skb)
{
 struct rtable *rt;

 ipv4_send_dest_unreach(skb);

 rt = skb_rtable(skbfl4 = ;
 if (rt)
  dst_set_expires(&rt->dst, .fl4_sport0;
}

static int ip_rt_bug(struct}
{
 pr_debug("%s:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
   __func__  !())
   skb->dev err=EHOSTUNREACH
 kfree_skb(skb);
 WARN_ON(1);
 return 0;
}

/*
 * We do not cache source address of outgoing interface,
 * because it is used only by IP RR, TS and SRR options,
 * so that it out of fast path.
 *
 * BTW remember: "addr" is allowed to be not aligned
 * in IP options!
 */


void ip_rt_get_source(u8 *addr, struct sk_buff  IPV4_DEVCONF_ALL_RO, ))
{
 __be32 ;

 if
  src = ip_hdr(skb)->saddr;
 else {
  truct ;
  struct iphdr *iph = ip_hdr(  (> =RTN_LOCAL
  struct flowi4,,,&);
   .daddr = iph->  reason
   .saddr rtian_source
   .flowi4_tos = inet_dscp_to_dsfield(ip4h_dscp ;
   .flowi4_oif = rt->dst
   .flowi4_iif = skb->dev->ifindex
   .flowi4_mark    dev, )java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
  };
 (in_brd
  rcu_read_lock
  if (fib_lookup(dev_net:
   src = fib_result_prefsrc(dev_net(rt->dst.devifIN_DEV_ORCONF, )java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
  else
   src = inet_select_addr(rt->dst.dev,
          (rt >daddr
            RT_SCOPE_UNIVERSE);
  rcu_read_unlock();
 }
 memcpy(addr, &src, 4);
}

#ifdef CONFIG_IP_ROUTE_CLASSID
 void(struct *rt u32)
{
  !(rt-.tclassid0FFFF)java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
 rt-dst |= & 0;
 if (!(rt->dst.tclassid    out
  rt->dst.tclassid |= tag & 0 }
}
#endif

static unsigned int ipv4_default_advmss
{
unsigned header_size= sizeof tcphdr+sizeof iphdr;
 unsigned int advmss;
 struct net *net;

 rcu_read_lock();
 net = dst_dev_net_rcu(dst);
 advmss = max_t(unsigned int, ipv4_mtu
       net->ipv4.ip_rt_min_advmss);
 rcu_read_unlock;

 return min(advmss, IPV4_MAX_PMTU - header_size);
}

INDIRECT_CALLABLE_SCOPE unsignedint (conststruct dst_entry *st
{
return(dst, false;
}
EXPORT_INDIRECT_CALLABLE(ipv4_mtu);

static void ip_del_fnhe(structfib_nh_common*nhc, __be32daddr
{
 struct fnhe_hash_bucket *hash
 struct fib_nh_exception *fnhe, __rcu **fnhe_p;
 u32 hval = fnhe_hashfun(daddr);

 spin_lock_bh(&fnhe_lock);

 hash = rcu_dereference_protected(nhc->nhc_exceptions,
eld&nhe_lock;
 hash += hval;

 fnhe_p = &hash->chain;
 fnhe = rcu_dereference_protected(*fnhe_p, lockdep_is_held(&fnhe_lock));
 while (fnhe) {
   (>fnhe_daddr= daddr
   rcu_assign_pointer(*fnhe_p, rcu_dereference_protected(
_(&fnhe_lock;
   /* set fnhe_daddr to 0 to ensure it won't bind with
 * new dsts in rt_bind_exception().
 */

   fnhe->fnhe_daddr = 0;
   fnhe_flush_routes(fnhe);
   (fnhercu
   break;
  }
   = &>fnhe_next
  fnhe = rcu_dereference_protected(fnhe->fnhe_next,
    lockdep_is_heldfnhe_lockjava.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
 }


}

static struct fib_nh_exception *  (rth
 java.lang.StringIndexOutOfBoundsException: Index 2 out of bounds for length 2
{
 struct fnhe_hash_bucket *hash = rcu_dereference(nhc->nhc_exceptions);
 struct fib_nh_exception *fnhe;
 u32 hval;

 if (!hash)
  return NULLRT_CACHE_STAT_INC);

 hval = fnhe_hashfun(daddr);

 for (fnhe = rcu_dereference(hash[hval].chain); fnhe;
      fnhe = rcu_dereference(fnhe->fnhe_next)) {
  if (fnhe->fnhe_daddr == daddr) {
   if (fnhe->fnhe_expires &&
       time_after(jiffies, fnhe->fnhe_expires)) {
    ip_del_fnhe(nhc, daddr);
    break;
   }
   return fnhe;
  }
 }
 return NULL;
}

/* MTU selection:
 * 1. mtu on route is locked - use it
 * 2. mtu from nexthop exception
 * 3. mtu from egress device
 */


u32 ip_mtu_from_fib_result(struct fib_result *res, __be32 daddr)
{
 struct fib_nh_common *nhc = res->nhc;
 struct net_device *dev = nhc->nhc_dev;
struct *fi res-fi;
 u32 mtu = 0;

 if (READ_ONCE(dev_net(dev)->ipv4.sysctl_ip_fwd_use_pmtu) ||
     fi->fib_metrics->metrics[RTAX_LOCK - java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
  mtu = fi->fib_mtu;

 if (likely(!mtu)) {
  struct fib_nh_exception *fnhe;

  fnhe = find_exception(nhc, daddr);
  if( & time_after_eq, >fnhe_expiresjava.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
   mtu = fnhe-}
 }

 if (likely(!mtu))
  =minREAD_ONCEdev-),IP_MAX_MTU

 return enumjava.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27
}

static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe,
         __be32 daddr, const bool do_cache)
{
 bool ret = false;

 spin_lock_bh(&fnhe_lock);

 if (daddr == fnhe->fnhe_daddr) {
  struct rtable __rcu **porig;
  struct rtable *orig;
  int genid = fnhe_genid(dev_net(rt->dst.dev));

  if (rt_is_input_route(rt))
   porig = &fnhe->fnhe_rth_input;
  else
   porig = &fnhe->fnhe_rth_output;
  orig = rcu_dereference(*porig);

  if (fnhe->fnhe_genid != genid) {
   fnhe->fnhe_genid = genid;
   fnhe->fnhe_gw = 0;
   fnhe->fnhe_pmtu = 0;
   fnhe->fnhe_expires = 0;
   fnhe->fnhe_mtu_locked = false;
   fnhe_flush_routes(fnhe);
   orig = NULL;
  }
  fill_route_from_fnhe(rt, fnhe);
  if (!rt->rt_gw4) {
   rt->rt_gw4 = daddr;
   rt->rt_gw_family = AF_INET;
  }

  if (do_cache) {
   dst_hold(&rt->dst);
   rcu_assign_pointer(*porig, rt);
   if (orig) {
    dst_dev_put(&orig->dst);
    dst_release(&orig->dst);
   }
   ret = true;
  }

  fnhe->fnhe_stamp = jiffies;
 }
 spin_unlock_bh(&fnhe_lock);

 return ret;
}

static bool rt_cache_route(struct fib_nh_common *nhc, struct rtable *rt)
{
 struct rtable *orig, *prev, **p;
 bool ret = true;

 if (rt_is_input_route(rt)) {
  p = (struct rtable **)&nhc->nhc_rth_input;
 } else {
  p = (struct rtable **)raw_cpu_ptr(nhc->nhc_pcpu_rth_output);
 }
 orig = *p;

 /* hold dst before doing cmpxchg() to avoid race condition
 * on this dst
 */

 dst_hold(&rt->dst);
 prev = cmpxchg(p, orig, rt);
 if (prev == orig) {
  if (orig) {
   rt_add_uncached_list(orig);
   dst_release(&orig->dst);
  }
 } else {
  dst_release(&rt->dst);
  ret = false;
 }

 return ret;
}

struct uncached_list {
 spinlock_t  lock;
 struct list_head head;
};

static DEFINE_PER_CPU_ALIGNED(struct uncached_list, rt_uncached_list);

void rt_add_uncached_list(struct rtable *rt)
{
 struct uncached_list *ul = raw_cpu_ptr(&rt_uncached_list);

 rt->dst.rt_uncached_list = ul;

 spin_lock_bh(&ul->lock);
 list_add_tail(&rt->dst.rt_uncached, &ul->head);
 spin_unlock_bh(&ul->lock);
}

void rt_del_uncached_list(struct rtable *rt)
{
 if (!list_empty(&rt->dst.rt_uncached)) {
  struct uncached_list *ul = rt->dst.rt_uncached_list;

  spin_lock_bh(&ul->lock);
  list_del_init(&rt->dst.rt_uncached);
  spin_unlock_bh(&ul->lock);
 }
}

static void ipv4_dst_destroy(struct dst_entry *dst)
{
 ip_dst_metrics_put(dst);
 rt_del_uncached_list(dst_rtable(dst));
}

void rt_flush_dev(struct net_device *dev)
{
 struct rtable *rt, *safe;
 int cpu;

 for_each_possible_cpu(cpu) {
  struct uncached_list *ul = &per_cpu(rt_uncached_list, cpu);

  if (list_empty(&ul->head))
   continue;

  spin_lock_bh(&ul->lock);
  list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
   if (rt->dst.dev != dev)
    continue;
   rt->dst.dev = blackhole_netdev;
   netdev_ref_replace(dev, blackhole_netdev,
        &rt->dst.dev_tracker, GFP_ATOMIC);
   list_del_init(&rt->dst.rt_uncached);
  }
  spin_unlock_bh(&ul->lock);
 }
}

static bool rt_cache_valid(const struct rtable *rt)
{
 return rt &&
  READ_ONCE(rt->dst.obsolete) == DST_OBSOLETE_FORCE_CHK &&
  !rt_is_expired(rt);
}

static void rt_set_nexthop(struct rtable *rt, __be32 daddr,
      const struct fib_result *res,
      struct fib_nh_exception *fnhe,
      struct fib_info *fi, u16 type, u32 itag,
      const bool do_cache)
{
 bool cached = false;

 if (fi) {
  struct fib_nh_common *nhc = FIB_RES_NHC(*res);

  if (nhc->nhc_gw_family && nhc->nhc_scope == RT_SCOPE_LINK) {
   rt->rt_uses_gateway = 1;
   rt->rt_gw_family = nhc->nhc_gw_family;
   /* only INET and INET6 are supported */
   if (likely(nhc->nhc_gw_family == AF_INET))
    rt->rt_gw4 = nhc->nhc_gw.ipv4;
   else
    rt->rt_gw6 = nhc->nhc_gw.ipv6;
  }

  ip_dst_init_metrics(&rt->dst, fi->fib_metrics);

#ifdef CONFIG_IP_ROUTE_CLASSID
  if (nhc->nhc_family == AF_INET) {
   struct fib_nh *nh;

   nh = container_of(nhc, struct fib_nh, nh_common);
   rt->dst.tclassid = nh->nh_tclassid;
  }
#endif
  rt->dst.lwtstate = lwtstate_get(nhc->nhc_lwtstate);
  if (unlikely(fnhe))
   cached = rt_bind_exception(rt, fnhe, daddr, do_cache);
  else if (do_cache)
   cached = rt_cache_route(nhc, rt);
  if (unlikely(!cached)) {
   /* Routes we intend to cache in nexthop exception or
 * FIB nexthop have the DST_NOCACHE bit clear.
 * However, if we are unsuccessful at storing this
 * route into the cache we really need to set it.
 */

   if (!rt->rt_gw4) {
    rt->rt_gw_family = AF_INET;
    rt->rt_gw4 = daddr;
   }
   rt_add_uncached_list(rt);
  }
 } else
  rt_add_uncached_list(rt);

#ifdef CONFIG_IP_ROUTE_CLASSID
#ifdef CONFIG_IP_MULTIPLE_TABLES
 set_class_tag(rt, res->tclassid);
#endif
 set_class_tag(rt, itag);
#endif
}

struct rtable *rt_dst_alloc(struct net_device *dev,
       unsigned int flags, u16 type,
       bool noxfrm)
{
 struct rtable *rt;

 rt = dst_alloc(&ipv4_dst_ops, dev, DST_OBSOLETE_FORCE_CHK,
         (noxfrm ? DST_NOXFRM : 0));

 if (rt) {
  rt->rt_genid = rt_genid_ipv4(dev_net(dev));
  rt->rt_flags = flags;
  rt->rt_type = type;
  rt->rt_is_input = 0;
  rt->rt_iif = 0;
  rt->rt_pmtu = 0;
  rt->rt_mtu_locked = 0;
  rt->rt_uses_gateway = 0;
  rt->rt_gw_family = 0;
  rt->rt_gw4 = 0;

  rt->dst.output = ip_output;
  if (flags & RTCF_LOCAL)
   rt->dst.input = ip_local_deliver;
 }

 return rt;
}
EXPORT_SYMBOL(rt_dst_alloc);

struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt)
{
 struct rtable *new_rt;

 new_rt = dst_alloc(&ipv4_dst_ops, dev, DST_OBSOLETE_FORCE_CHK,
      rt->dst.flags);

 if (new_rt) {
  new_rt->rt_genid = rt_genid_ipv4(dev_net(dev));
  new_rt->rt_flags = rt->rt_flags;
  new_rt->rt_type = rt->rt_type;
  new_rt->rt_is_input = rt->rt_is_input;
  new_rt->rt_iif = rt->rt_iif;
  new_rt->rt_pmtu = rt->rt_pmtu;
  new_rt->rt_mtu_locked = rt->rt_mtu_locked;
  new_rt->rt_gw_family = rt->rt_gw_family;
  if (rt->rt_gw_family == AF_INET)
   new_rt->rt_gw4 = rt->rt_gw4;
  else if (rt->rt_gw_family == AF_INET6)
   new_rt->rt_gw6 = rt->rt_gw6;

  new_rt->dst.input = READ_ONCE(rt->dst.input);
  new_rt->dst.output = READ_ONCE(rt->dst.output);
  new_rt->dst.error = rt->dst.error;
  new_rt->dst.lastuse = jiffies;
  new_rt->dst.lwtstate = lwtstate_get(rt->dst.lwtstate);
 }
 return new_rt;
}
EXPORT_SYMBOL(rt_dst_clone);

/* called in rcu_read_lock() section */
enum skb_drop_reason
ip_mc_validate_source(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        dscp_t dscp, struct net_device *dev,
        struct in_device *in_dev, u32 *itag)
{
 enum skb_drop_reason reason;

 /* Primary sanity checks. */
 if (!in_dev)
  return SKB_DROP_REASON_NOT_SPECIFIED;

 if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
  return SKB_DROP_REASON_IP_INVALID_SOURCE;

 if (skb->protocol != htons(ETH_P_IP))
  return SKB_DROP_REASON_INVALID_PROTO;

 if (ipv4_is_loopback(saddr) && !IN_DEV_ROUTE_LOCALNET(in_dev))
  return SKB_DROP_REASON_IP_LOCALNET;

 if (ipv4_is_zeronet(saddr)) {
  if (!ipv4_is_local_multicast(daddr) &&
      ip_hdr(skb)->protocol != IPPROTO_IGMP)
   return SKB_DROP_REASON_IP_INVALID_SOURCE;
 } else {
  reason = fib_validate_source_reason(skb, saddr, 0, dscp, 0,
          dev, in_dev, itag);
  if (reason)
   return reason;
 }
 return SKB_NOT_DROPPED_YET;
}

/* called in rcu_read_lock() section */
static enum skb_drop_reason
ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
    dscp_t dscp, struct net_device *dev, int our)
{
 struct in_device *in_dev = __in_dev_get_rcu(dev);
 unsigned int flags = RTCF_MULTICAST;
 enum skb_drop_reason reason;
 struct rtable *rth;
 u32 itag = 0;

 reason = ip_mc_validate_source(skb, daddr, saddr, dscp, dev, in_dev,
           &itag);
 if (reason)
  return reason;

 if (our)
  flags |= RTCF_LOCAL;

 if (IN_DEV_ORCONF(in_dev, NOPOLICY))
  IPCB(skb)->flags |= IPSKB_NOPOLICY;

 rth = rt_dst_alloc(dev_net(dev)->loopback_dev, flags, RTN_MULTICAST,
      false);
 if (!rth)
  return SKB_DROP_REASON_NOMEM;

#ifdef CONFIG_IP_ROUTE_CLASSID
 rth->dst.tclassid = itag;
#endif
 rth->dst.output = ip_rt_bug;
 rth->rt_is_input= 1;

#ifdef CONFIG_IP_MROUTE
 if (!ipv4_is_local_multicast(daddr) && IN_DEV_MFORWARD(in_dev))
  rth->dst.input = ip_mr_input;
#endif
 RT_CACHE_STAT_INC(in_slow_mc);

 skb_dst_drop(skb);
 skb_dst_set(skb, &rth->dst);
 return SKB_NOT_DROPPED_YET;
}


static void ip_handle_martian_source(struct net_device *dev,
         struct in_device *in_dev,
         struct sk_buff *skb,
         __be32 daddr,
         __be32 saddr)
{
 RT_CACHE_STAT_INC(in_martian_src);
#ifdef CONFIG_IP_ROUTE_VERBOSE
 if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit()) {
  /*
 * RFC1812 recommendation, if source is martian,
 * the only hint is MAC header.
 */

  pr_warn("martian source %pI4 from %pI4, on dev %s\n",
   &daddr, &saddr, dev->name);
  if (dev->hard_header_len && skb_mac_header_was_set(skb)) {
   print_hex_dump(KERN_WARNING, "ll header: ",
           DUMP_PREFIX_OFFSET, 16, 1,
           skb_mac_header(skb),
           dev->hard_header_len, false);
  }
 }
#endif
}

/* called in rcu_read_lock() section */
static enum skb_drop_reason
__mkroute_input(struct sk_buff *skb, const struct fib_result *res,
  struct in_device *in_dev, __be32 daddr,
  __be32 saddr, dscp_t dscp)
{
 enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
 struct fib_nh_common *nhc = FIB_RES_NHC(*res);
 struct net_device *dev = nhc->nhc_dev;
 struct fib_nh_exception *fnhe;
 struct rtable *rth;
 int err;
 struct in_device *out_dev;
 bool do_cache;
 u32 itag = 0;

 /* get a working reference to the output device */
 out_dev = __in_dev_get_rcu(dev);
 if (!out_dev) {
  net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");
  return reason;
 }

 err = fib_validate_source(skb, saddr, daddr, dscp, FIB_RES_OIF(*res),
      in_dev->dev, in_dev, &itag);
 if (err < 0) {
  reason = -err;
  ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
      saddr);

  goto cleanup;
 }

 do_cache = res->fi && !itag;
 if (out_dev == in_dev && err && IN_DEV_TX_REDIRECTS(out_dev) &&
     skb->protocol == htons(ETH_P_IP)) {
  __be32 gw;

  gw = nhc->nhc_gw_family == AF_INET ? nhc->nhc_gw.ipv4 : 0;
  if (IN_DEV_SHARED_MEDIA(out_dev) ||
      inet_addr_onlink(out_dev, saddr, gw))
   IPCB(skb)->flags |= IPSKB_DOREDIRECT;
 }

 if (skb->protocol != htons(ETH_P_IP)) {
  /* Not IP (i.e. ARP). Do not create route, if it is
 * invalid for proxy arp. DNAT routes are always valid.
 *
 * Proxy arp feature have been extended to allow, ARP
 * replies back to the same interface, to support
 * Private VLAN switch technologies. See arp.c.
 */

  if (out_dev == in_dev &&
      IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
   reason = SKB_DROP_REASON_ARP_PVLAN_DISABLE;
   goto cleanup;
  }
 }

 if (IN_DEV_ORCONF(in_dev, NOPOLICY))
  IPCB(skb)->flags |= IPSKB_NOPOLICY;

 fnhe = find_exception(nhc, daddr);
 if (do_cache) {
  if (fnhe)
   rth = rcu_dereference(fnhe->fnhe_rth_input);
  else
   rth = rcu_dereference(nhc->nhc_rth_input);
  if (rt_cache_valid(rth)) {
   skb_dst_set_noref(skb, &rth->dst);
   goto out;
  }
 }

 rth = rt_dst_alloc(out_dev->dev, 0, res->type,
      IN_DEV_ORCONF(out_dev, NOXFRM));
 if (!rth) {
  reason = SKB_DROP_REASON_NOMEM;
  goto cleanup;
 }

 rth->rt_is_input = 1;
 RT_CACHE_STAT_INC(in_slow_tot);

 rth->dst.input = ip_forward;

 rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag,
         do_cache);
 lwtunnel_set_redirect(&rth->dst);
 skb_dst_set(skb, &rth->dst);
out:
 reason = SKB_NOT_DROPPED_YET;
cleanup:
 return reason;
}

#ifdef CONFIG_IP_ROUTE_MULTIPATH
/* To make ICMP packets follow the right flow, the multipath hash is
 * calculated from the inner IP addresses.
 */

static void ip_multipath_l3_keys(const struct sk_buff *skb,
     struct flow_keys *hash_keys)
{
 const struct iphdr *outer_iph = ip_hdr(skb);
 const struct iphdr *key_iph = outer_iph;
 const struct iphdr *inner_iph;
 const struct icmphdr *icmph;
 struct iphdr _inner_iph;
 struct icmphdr _icmph;

 if (likely(outer_iph->protocol != IPPROTO_ICMP))
  goto out;

 if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0))
  goto out;

 icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph),
       &_icmph);
 if (!icmph)
  goto out;

 if (!icmp_is_err(icmph->type))
  goto out;

 inner_iph = skb_header_pointer(skb,
           outer_iph->ihl * 4 + sizeof(_icmph),
           sizeof(_inner_iph), &_inner_iph);
 if (!inner_iph)
  goto out;

 key_iph = inner_iph;
out:
 hash_keys->addrs.v4addrs.src = key_iph->saddr;
 hash_keys->addrs.v4addrs.dst = key_iph->daddr;
}

static u32 fib_multipath_custom_hash_outer(const struct net *net,
        const struct sk_buff *skb,
        bool *p_has_inner)
{
 u32 hash_fields = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_fields);
 struct flow_keys keys, hash_keys;

 if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
  return 0;

 memset(&hash_keys, 0, sizeof(hash_keys));
 skb_flow_dissect_flow_keys(skb, &keys, FLOW_DISSECTOR_F_STOP_AT_ENCAP);

 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
  hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
  hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
  hash_keys.basic.ip_proto = keys.basic.ip_proto;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT)
  hash_keys.ports.src = keys.ports.src;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
  hash_keys.ports.dst = keys.ports.dst;

 *p_has_inner = !!(keys.control.flags & FLOW_DIS_ENCAPSULATION);
 return fib_multipath_hash_from_keys(net, &hash_keys);
}

static u32 fib_multipath_custom_hash_inner(const struct net *net,
        const struct sk_buff *skb,
        bool has_inner)
{
 u32 hash_fields = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_fields);
 struct flow_keys keys, hash_keys;

 /* We assume the packet carries an encapsulation, but if none was
 * encountered during dissection of the outer flow, then there is no
 * point in calling the flow dissector again.
 */

 if (!has_inner)
  return 0;

 if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_MASK))
  return 0;

 memset(&hash_keys, 0, sizeof(hash_keys));
 skb_flow_dissect_flow_keys(skb, &keys, 0);

 if (!(keys.control.flags & FLOW_DIS_ENCAPSULATION))
  return 0;

 if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
  hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
  if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
   hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
  if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
   hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
 } else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
  hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
  if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_IP)
   hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
  if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_IP)
   hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
  if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_FLOWLABEL)
   hash_keys.tags.flow_label = keys.tags.flow_label;
 }

 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_IP_PROTO)
  hash_keys.basic.ip_proto = keys.basic.ip_proto;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_SRC_PORT)
  hash_keys.ports.src = keys.ports.src;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_INNER_DST_PORT)
  hash_keys.ports.dst = keys.ports.dst;

 return fib_multipath_hash_from_keys(net, &hash_keys);
}

static u32 fib_multipath_custom_hash_skb(const struct net *net,
      const struct sk_buff *skb)
{
 u32 mhash, mhash_inner;
 bool has_inner = true;

 mhash = fib_multipath_custom_hash_outer(net, skb, &has_inner);
 mhash_inner = fib_multipath_custom_hash_inner(net, skb, has_inner);

 return jhash_2words(mhash, mhash_inner, 0);
}

static u32 fib_multipath_custom_hash_fl4(const struct net *net,
      const struct flowi4 *fl4)
{
 u32 hash_fields = READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_fields);
 struct flow_keys hash_keys;

 if (!(hash_fields & FIB_MULTIPATH_HASH_FIELD_OUTER_MASK))
  return 0;

 memset(&hash_keys, 0, sizeof(hash_keys));
 hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_IP)
  hash_keys.addrs.v4addrs.src = fl4->saddr;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_IP)
  hash_keys.addrs.v4addrs.dst = fl4->daddr;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_IP_PROTO)
  hash_keys.basic.ip_proto = fl4->flowi4_proto;
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_SRC_PORT) {
  if (fl4->flowi4_flags & FLOWI_FLAG_ANY_SPORT)
   hash_keys.ports.src = (__force __be16)get_random_u16();
  else
   hash_keys.ports.src = fl4->fl4_sport;
 }
 if (hash_fields & FIB_MULTIPATH_HASH_FIELD_DST_PORT)
  hash_keys.ports.dst = fl4->fl4_dport;

 return fib_multipath_hash_from_keys(net, &hash_keys);
}

/* if skb is set it will be used and fl4 can be NULL */
int fib_multipath_hash(const struct net *net, const struct flowi4 *fl4,
         const struct sk_buff *skb, struct flow_keys *flkeys)
{
 u32 multipath_hash = fl4 ? fl4->flowi4_multipath_hash : 0;
 struct flow_keys hash_keys;
 u32 mhash = 0;

 switch (READ_ONCE(net->ipv4.sysctl_fib_multipath_hash_policy)) {
 case 0:
  memset(&hash_keys, 0, sizeof(hash_keys));
  hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
  if (skb) {
   ip_multipath_l3_keys(skb, &hash_keys);
  } else {
   hash_keys.addrs.v4addrs.src = fl4->saddr;
   hash_keys.addrs.v4addrs.dst = fl4->daddr;
  }
  mhash = fib_multipath_hash_from_keys(net, &hash_keys);
  break;
 case 1:
  /* skb is currently provided only when forwarding */
  if (skb) {
   unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
   struct flow_keys keys;

   /* short-circuit if we already have L4 hash present */
   if (skb->l4_hash)
    return skb_get_hash_raw(skb) >> 1;

   memset(&hash_keys, 0, sizeof(hash_keys));

   if (!flkeys) {
    skb_flow_dissect_flow_keys(skb, &keys, flag);
    flkeys = &keys;
   }

   hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
   hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
   hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
   hash_keys.ports.src = flkeys->ports.src;
   hash_keys.ports.dst = flkeys->ports.dst;
   hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
  } else {
   memset(&hash_keys, 0, sizeof(hash_keys));
   hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
   hash_keys.addrs.v4addrs.src = fl4->saddr;
   hash_keys.addrs.v4addrs.dst = fl4->daddr;
   if (fl4->flowi4_flags & FLOWI_FLAG_ANY_SPORT)
    hash_keys.ports.src = (__force __be16)get_random_u16();
   else
    hash_keys.ports.src = fl4->fl4_sport;
   hash_keys.ports.dst = fl4->fl4_dport;
   hash_keys.basic.ip_proto = fl4->flowi4_proto;
  }
  mhash = fib_multipath_hash_from_keys(net, &hash_keys);
  break;
 case 2:
  memset(&hash_keys, 0, sizeof(hash_keys));
  /* skb is currently provided only when forwarding */
  if (skb) {
   struct flow_keys keys;

   skb_flow_dissect_flow_keys(skb, &keys, 0);
   /* Inner can be v4 or v6 */
   if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
    hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
    hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
    hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
   } else if (keys.control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
    hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
    hash_keys.addrs.v6addrs.src = keys.addrs.v6addrs.src;
    hash_keys.addrs.v6addrs.dst = keys.addrs.v6addrs.dst;
    hash_keys.tags.flow_label = keys.tags.flow_label;
    hash_keys.basic.ip_proto = keys.basic.ip_proto;
   } else {
    /* Same as case 0 */
    hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
    ip_multipath_l3_keys(skb, &hash_keys);
   }
  } else {
   /* Same as case 0 */
   hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
   hash_keys.addrs.v4addrs.src = fl4->saddr;
   hash_keys.addrs.v4addrs.dst = fl4->daddr;
  }
  mhash = fib_multipath_hash_from_keys(net, &hash_keys);
  break;
 case 3:
  if (skb)
   mhash = fib_multipath_custom_hash_skb(net, skb);
  else
   mhash = fib_multipath_custom_hash_fl4(net, fl4);
  break;
 }

 if (multipath_hash)
  mhash = jhash_2words(mhash, multipath_hash, 0);

 return mhash >> 1;
}
#endif /* CONFIG_IP_ROUTE_MULTIPATH */

static enum skb_drop_reason
ip_mkroute_input(struct sk_buff *skb, struct fib_result *res,
   struct in_device *in_dev, __be32 daddr,
   __be32 saddr, dscp_t dscp, struct flow_keys *hkeys)
{
#ifdef CONFIG_IP_ROUTE_MULTIPATH
 if (res->fi && fib_info_num_path(res->fi) > 1) {
  int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys);

  fib_select_multipath(res, h, NULL);
  IPCB(skb)->flags |= IPSKB_MULTIPATH;
 }
#endif

 /* create a routing cache entry */
 return __mkroute_input(skb, res, in_dev, daddr, saddr, dscp);
}

/* Implements all the saddr-related checks as ip_route_input_slow(),
 * assuming daddr is valid and the destination is not a local broadcast one.
 * Uses the provided hint instead of performing a route lookup.
 */

enum skb_drop_reason
ip_route_use_hint(struct sk_buff *skb, __be32 daddr, __be32 saddr,
    dscp_t dscp, struct net_device *dev,
    const struct sk_buff *hint)
{
 enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
 struct in_device *in_dev = __in_dev_get_rcu(dev);
 struct rtable *rt = skb_rtable(hint);
 struct net *net = dev_net(dev);
 u32 tag = 0;

 if (!in_dev)
  return reason;

 if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) {
  reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
  goto martian_source;
 }

 if (ipv4_is_zeronet(saddr)) {
  reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
  goto martian_source;
 }

 if (ipv4_is_loopback(saddr) && !IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
  reason = SKB_DROP_REASON_IP_LOCALNET;
  goto martian_source;
 }

 if (!(rt->rt_flags & RTCF_LOCAL))
  goto skip_validate_source;

 reason = fib_validate_source_reason(skb, saddr, daddr, dscp, 0, dev,
         in_dev, &tag);
 if (reason)
  goto martian_source;

skip_validate_source:
 skb_dst_copy(skb, hint);
 return SKB_NOT_DROPPED_YET;

martian_source:
 ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
 return reason;
}

/* get device for dst_alloc with local routes */
static struct net_device *ip_rt_get_dev(struct net *net,
     const struct fib_result *res)
{
 struct fib_nh_common *nhc = res->fi ? res->nhc : NULL;
 struct net_device *dev = NULL;

 if (nhc)
  dev = l3mdev_master_dev_rcu(nhc->nhc_dev);

 return dev ? : net->loopback_dev;
}

/*
 * NOTE. We drop all the packets that has local source
 * addresses, because every properly looped back packet
 * must have correct destination already attached by output routine.
 * Changes in the enforced policies must be applied also to
 * ip_route_use_hint().
 *
 * Such approach solves two big problems:
 * 1. Not simplex devices are handled properly.
 * 2. IP spoofing attempts are filtered with 100% of guarantee.
 * called with rcu_read_lock()
 */


static enum skb_drop_reason
ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
      dscp_t dscp, struct net_device *dev,
      struct fib_result *res)
{
 enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
 struct in_device *in_dev = __in_dev_get_rcu(dev);
 struct flow_keys *flkeys = NULL, _flkeys;
 struct net    *net = dev_net(dev);
 struct ip_tunnel_info *tun_info;
 int  err = -EINVAL;
 unsigned int flags = 0;
 u32  itag = 0;
 struct rtable *rth;
 struct flowi4 fl4;
 bool do_cache = true;

 /* IP on this device is disabled. */

 if (!in_dev)
  goto out;

 /* Check for the most weird martians, which can be not detected
 * by fib_lookup.
 */


 tun_info = skb_tunnel_info(skb);
 if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
  fl4.flowi4_tun_key.tun_id = tun_info->key.tun_id;
 else
  fl4.flowi4_tun_key.tun_id = 0;
 skb_dst_drop(skb);

 if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr)) {
  reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
  goto martian_source;
 }

 res->fi = NULL;
 res->table = NULL;
 if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0))
  goto brd_input;

 /* Accept zero addresses only to limited broadcast;
 * I even do not know to fix it or not. Waiting for complains :-)
 */

 if (ipv4_is_zeronet(saddr)) {
  reason = SKB_DROP_REASON_IP_INVALID_SOURCE;
  goto martian_source;
 }

 if (ipv4_is_zeronet(daddr)) {
  reason = SKB_DROP_REASON_IP_INVALID_DEST;
  goto martian_destination;
 }

 /* Following code try to avoid calling IN_DEV_NET_ROUTE_LOCALNET(),
 * and call it once if daddr or/and saddr are loopback addresses
 */

 if (ipv4_is_loopback(daddr)) {
  if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
   reason = SKB_DROP_REASON_IP_LOCALNET;
   goto martian_destination;
  }
 } else if (ipv4_is_loopback(saddr)) {
  if (!IN_DEV_NET_ROUTE_LOCALNET(in_dev, net)) {
   reason = SKB_DROP_REASON_IP_LOCALNET;
   goto martian_source;
  }
 }

 /*
 * Now we are ready to route packet.
 */

 fl4.flowi4_l3mdev = 0;
 fl4.flowi4_oif = 0;
 fl4.flowi4_iif = dev->ifindex;
 fl4.flowi4_mark = skb->mark;
 fl4.flowi4_tos = inet_dscp_to_dsfield(dscp);
 fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
 fl4.flowi4_flags = 0;
 fl4.daddr = daddr;
 fl4.saddr = saddr;
 fl4.flowi4_uid = sock_net_uid(net, NULL);
 fl4.flowi4_multipath_hash = 0;

 if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys)) {
  flkeys = &_flkeys;
 } else {
  fl4.flowi4_proto = 0;
  fl4.fl4_sport = 0;
  fl4.fl4_dport = 0;
 }

 err = fib_lookup(net, &fl4, res, 0);
 if (err != 0) {
  if (!IN_DEV_FORWARD(in_dev))
   err = -EHOSTUNREACH;
  goto no_route;
 }

 if (res->type == RTN_BROADCAST) {
  if (IN_DEV_BFORWARD(in_dev))
   goto make_route;
  /* not do cache if bc_forwarding is enabled */
  if (IPV4_DEVCONF_ALL_RO(net, BC_FORWARDING))
   do_cache = false;
  goto brd_input;
 }

 err = -EINVAL;
 if (res->type == RTN_LOCAL) {
  reason = fib_validate_source_reason(skb, saddr, daddr, dscp,
          0, dev, in_dev, &itag);
  if (reason)
   goto martian_source;
  goto local_input;
 }

 if (!IN_DEV_FORWARD(in_dev)) {
  err = -EHOSTUNREACH;
  goto no_route;
 }
 if (res->type != RTN_UNICAST) {
  reason = SKB_DROP_REASON_IP_INVALID_DEST;
  goto martian_destination;
 }

make_route:
 reason = ip_mkroute_input(skb, res, in_dev, daddr, saddr, dscp,
      flkeys);

out:
 return reason;

brd_input:
 if (skb->protocol != htons(ETH_P_IP)) {
  reason = SKB_DROP_REASON_INVALID_PROTO;
  goto out;
 }

 if (!ipv4_is_zeronet(saddr)) {
  reason = fib_validate_source_reason(skb, saddr, 0, dscp, 0,
          dev, in_dev, &itag);
  if (reason)
   goto martian_source;
 }
 flags |= RTCF_BROADCAST;
 res->type = RTN_BROADCAST;
 RT_CACHE_STAT_INC(in_brd);

local_input:
 if (IN_DEV_ORCONF(in_dev, NOPOLICY))
  IPCB(skb)->flags |= IPSKB_NOPOLICY;

 do_cache &= res->fi && !itag;
 if (do_cache) {
  struct fib_nh_common *nhc = FIB_RES_NHC(*res);

  rth = rcu_dereference(nhc->nhc_rth_input);
  if (rt_cache_valid(rth)) {
   skb_dst_set_noref(skb, &rth->dst);
   reason = SKB_NOT_DROPPED_YET;
   goto out;
  }
 }

 rth = rt_dst_alloc(ip_rt_get_dev(net, res),
      flags | RTCF_LOCAL, res->type, false);
 if (!rth)
  goto e_nobufs;

 rth->dst.output= ip_rt_bug;
#ifdef CONFIG_IP_ROUTE_CLASSID
 rth->dst.tclassid = itag;
#endif
 rth->rt_is_input = 1;

 RT_CACHE_STAT_INC(in_slow_tot);
 if (res->type == RTN_UNREACHABLE) {
  rth->dst.input= ip_error;
  rth->dst.error= -err;
  rth->rt_flags &= ~RTCF_LOCAL;
 }

 if (do_cache) {
  struct fib_nh_common *nhc = FIB_RES_NHC(*res);

  rth->dst.lwtstate = lwtstate_get(nhc->nhc_lwtstate);
  if (lwtunnel_input_redirect(rth->dst.lwtstate)) {
   WARN_ON(rth->dst.input == lwtunnel_input);
   rth->dst.lwtstate->orig_input = rth->dst.input;
   rth->dst.input = lwtunnel_input;
  }

  if (unlikely(!rt_cache_route(nhc, rth)))
   rt_add_uncached_list(rth);
 }
 skb_dst_set(skb, &rth->dst);
 reason = SKB_NOT_DROPPED_YET;
 goto out;

no_route:
 RT_CACHE_STAT_INC(in_no_route);
 res->type = RTN_UNREACHABLE;
 res->fi = NULL;
 res->table = NULL;
 goto local_input;

 /*
 * Do not cache martian addresses: they should be logged (RFC1812)
 */

martian_destination:
 RT_CACHE_STAT_INC(in_martian_dst);
#ifdef CONFIG_IP_ROUTE_VERBOSE
 if (IN_DEV_LOG_MARTIANS(in_dev))
  net_warn_ratelimited("martian destination %pI4 from %pI4, dev %s\n",
         &daddr, &saddr, dev->name);
#endif
 goto out;

e_nobufs:
 reason = SKB_DROP_REASON_NOMEM;
 goto out;

martian_source:
 ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
 goto out;
}

/* called with rcu_read_lock held */
static enum skb_drop_reason
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=93 G=93

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.21Bemerkung:  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.