协议栈(路由子系统)

结构体定义

struct net {
   ...
   struct netns_ipv4  ipv4;
   ...
};


struct netns_ipv4 {
   ...
   struct hlist_head  *fib_table_hash; // 长度为FIB_TABLE_HASHSZ
   ...
};


struct fib_table {
   struct hlist_node tb_hlist; // 用来将各个fib_table链成双向链表
   u32       tb_id;
   int       tb_default;
   int       (*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res);
   int       (*tb_insert)(struct fib_table *, struct fib_config *);
   int       (*tb_delete)(struct fib_table *, struct fib_config *);
   int       (*tb_dump)(struct fib_table *table, struct sk_buff *skb,
                 struct netlink_callback *cb);
   int       (*tb_flush)(struct fib_table *table);
   void      (*tb_select_default)(struct fib_table *table,
                    const struct flowi *flp, struct fib_result *res);
 
   unsigned char  tb_data[0]; // fn_hash紧随其后
};


struct fn_hash {
   struct fn_zone *fn_zones[33]; // 对应33个掩码长度(0-32)
   struct fn_zone *fn_zone_list; // 非空fn_zone组成链表表头
};


// 一个fn_zone表示一个由同一掩码长度路由表项组成的集合
struct fn_zone {
   struct fn_zone    *fz_next;  /* Next not empty zone */ // 用来将各个非空fn_zone链成链表
   struct hlist_head  *fz_hash;  /* Hash table pointer  */ // 长度为fz_divisor
   int          fz_nent;   /* Number of entries   */
 
   int          fz_divisor;    /* Hash divisor       */
   u32          fz_hashmask;   /* (fz_divisor - 1)    */
#define FZ_HASHMASK(fz)       ((fz)->fz_hashmask)
 
   int          fz_order;  /* Zone order     */ // 掩码长度
   __be32       fz_mask; // 掩码
#define FZ_MASK(fz)       ((fz)->fz_mask)
};


// 一个fib_node表示一个目的网段
struct fib_node {
   struct hlist_node  fn_hash; // 用来将各个fib_node链成双向链表
   struct list_head   fn_alias; // fib_alias组成链表表头
   __be32       fn_key; // 目的网段
   struct fib_alias        fn_embedded_alias;
};


// 一个fib_alias表示一个路由表项
struct fib_alias {
   struct list_head   fa_list; // 用来将各个fib_alias链成链表
   struct fib_info       *fa_info; // 指向fib_info
   u8       fa_tos;
   u8       fa_type;
   u8       fa_scope;
   u8       fa_state;
#ifdef CONFIG_IP_FIB_TRIE
   struct rcu_head       rcu;
#endif
};


// 一个fib_info表示一个下一跳
struct fib_info {
   struct hlist_node  fib_hash;
   struct hlist_node  fib_lhash;
   struct net    *fib_net;
   int          fib_treeref;
   atomic_t      fib_clntref;
   int          fib_dead;
   unsigned      fib_flags;
   int          fib_protocol;
   __be32       fib_prefsrc;
   u32          fib_priority;
   u32          fib_metrics[RTAX_MAX];
#define fib_mtu fib_metrics[RTAX_MTU-1]
#define fib_window fib_metrics[RTAX_WINDOW-1]
#define fib_rtt fib_metrics[RTAX_RTT-1]
#define fib_advmss fib_metrics[RTAX_ADVMSS-1]
   int          fib_nhs;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
   int          fib_power;
#endif
   struct fib_nh     fib_nh[0];
#define fib_dev       fib_nh[0].nh_dev
};


struct fib_nh {
   struct net_device  *nh_dev;
   struct hlist_node  nh_hash;
   struct fib_info       *nh_parent;
   unsigned      nh_flags;
   unsigned char     nh_scope;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
   int          nh_weight;
   int          nh_power;
#endif
#ifdef CONFIG_NET_CLS_ROUTE
   __u32        nh_tclassid;
#endif
   int          nh_oif;
   __be32       nh_gw;
};

结构体图示

这里写图片描述

一个fib_alias表示一个路由表项,一个fib_node表示一个目的网段,一个fib_info表示一个下一跳

多个fib_alias可能共享一个fib_node,也可能共享一个fib_info

fib_alias插入fib_table过程

根据掩码长度决定fn_zone
根据目的网段决定fib_node
创建fib_alias并链入fib_node->fn_alias

初始化

fs_initcall() -> inet_init() -> ip_init() -> ip_rt_init() -> ip_fib_init()

void __init ip_fib_init(void)
{
   rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
   rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
   rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
 
   register_pernet_subsys(&fib_net_ops);
   register_netdevice_notifier(&fib_netdev_notifier);
   register_inetaddr_notifier(&fib_inetaddr_notifier);
 
   fib_hash_init();
}


static struct pernet_operations fib_net_ops = {
   .init = fib_net_init,
   .exit = fib_net_exit,
};


int register_pernet_subsys(struct pernet_operations *ops)
{
   int error;
   mutex_lock(&net_mutex);
   error =  register_pernet_operations(first_device, ops);
   mutex_unlock(&net_mutex);
   return error;
}


static int register_pernet_operations(struct list_head *list,
                  struct pernet_operations *ops)
{
   struct net *net, *undo_net;
   int error;
 
   list_add_tail(&ops->list, list);
   if (ops->init) {
      for_each_net(net) {
         error = ops->init(net);
         if (error)
            goto out_undo;
      }
   }
   return 0;
 
out_undo:
   /* If I have an error cleanup all namespaces I initialized */
   list_del(&ops->list);
   if (ops->exit) {
      for_each_net(undo_net) {
         if (undo_net == net)
            goto undone;
         ops->exit(undo_net);
      }
   }
undone:
   return error;
}


static int __net_init fib_net_init(struct net *net)
{
   int error;
 
   error = ip_fib_net_init(net);
   if (error < 0)
      goto out;
   error = nl_fib_lookup_init(net);
   if (error < 0)
      goto out_nlfl;
   error = fib_proc_init(net);
   if (error < 0)
      goto out_proc;
out:
   return error;
 
out_proc:
   nl_fib_lookup_exit(net);
out_nlfl:
   ip_fib_net_exit(net);
   goto out;
}


static int __net_init ip_fib_net_init(struct net *net)
{
   int err;
   unsigned int i;
 
   // 分配fib_table_hash
   net->ipv4.fib_table_hash = kzalloc(
         sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL);
   if (net->ipv4.fib_table_hash == NULL)
      return -ENOMEM;
 
   for (i = 0; i < FIB_TABLE_HASHSZ; i++)
      INIT_HLIST_HEAD(&net->ipv4.fib_table_hash[i]);
 
   err = fib4_rules_init(net); // 分配本地/主路由表
   if (err < 0)
      goto fail;
   return 0;
 
fail:
   kfree(net->ipv4.fib_table_hash);
   return err;
}


static int __net_init fib4_rules_init(struct net *net)
{
   struct fib_table *local_table, *main_table;
 
   local_table = fib_hash_table(RT_TABLE_LOCAL); // 分配本地路由表(0)
   if (local_table == NULL)
      return -ENOMEM;
 
   main_table  = fib_hash_table(RT_TABLE_MAIN); // 分配主路由表(1)
   if (main_table == NULL)
      goto fail;
 
   // 将fib_table链入fib_table_hash
   hlist_add_head_rcu(&local_table->tb_hlist,
            &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);
   hlist_add_head_rcu(&main_table->tb_hlist,
            &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);
   return 0;
 
fail:
   kfree(local_table);
   return -ENOMEM;
}


struct fib_table *fib_hash_table(u32 id)
{
   struct fib_table *tb;
 
   // 分配fib_table和fn_hash
   tb = kmalloc(sizeof(struct fib_table) + sizeof(struct fn_hash),
           GFP_KERNEL);
   if (tb == NULL)
      return NULL;
 
   tb->tb_id = id;
   tb->tb_default = -1;
   tb->tb_lookup = fn_hash_lookup;
   tb->tb_insert = fn_hash_insert;
   tb->tb_delete = fn_hash_delete;
   tb->tb_flush = fn_hash_flush;
   tb->tb_select_default = fn_hash_select_default;
   tb->tb_dump = fn_hash_dump;
   memset(tb->tb_data, 0, sizeof(struct fn_hash));
   return tb;
}

ip_route_input()

int ip_route_input(struct sk_buff *skb, __be32 daddr, __be32 saddr,
         u8 tos, struct net_device *dev)
{
   struct rtable * rth;
   unsigned   hash;
   int iif = dev->ifindex;
   struct net *net;
 
   net = dev_net(dev);
 
   if (!rt_caching(net))
      goto skip_cache;
 
   tos &= IPTOS_RT_MASK;
   hash = rt_hash(daddr, saddr, iif, rt_genid(net));
 
   rcu_read_lock();
   for (rth = rcu_dereference(rt_hash_table[hash].chain); rth;
        rth = rcu_dereference(rth->u.dst.rt_next)) {
      if (((rth->fl.fl4_dst ^ daddr) |
           (rth->fl.fl4_src ^ saddr) |
           (rth->fl.iif ^ iif) |
           rth->fl.oif |
           (rth->fl.fl4_tos ^ tos)) == 0 &&
          rth->fl.mark == skb->mark &&
          net_eq(dev_net(rth->u.dst.dev), net) &&
          !rt_is_expired(rth)) { // 若查找到输入路由缓存
         dst_use(&rth->u.dst, jiffies);
         RT_CACHE_STAT_INC(in_hit);
         rcu_read_unlock();
         skb_dst_set(skb, &rth->u.dst); // 设置skb->_skb_dst
         return 0;
      }
      RT_CACHE_STAT_INC(in_hlist_search);
   }
   rcu_read_unlock();
 
skip_cache:
   /* Multicast recognition logic is moved from route cache to here.
      The problem was that too many Ethernet cards have broken/missing
      hardware multicast filters :-( As result the host on multicasting
      network acquires a lot of useless route cache entries, sort of
      SDR messages from all the world. Now we try to get rid of them.
      Really, provided software IP multicast filter is organized
      reasonably (at least, hashed), it does not result in a slowdown
      comparing with route cache reject entries.
      Note, that multicast routers are not affected, because
      route cache entry is created eventually.
    */
   if (ipv4_is_multicast(daddr)) {
      struct in_device *in_dev;
 
      rcu_read_lock();
      if ((in_dev = __in_dev_get_rcu(dev)) != NULL) {
         int our = ip_check_mc(in_dev, daddr, saddr,
            ip_hdr(skb)->protocol);
         if (our
#ifdef CONFIG_IP_MROUTE
             || (!ipv4_is_local_multicast(daddr) &&
            IN_DEV_MFORWARD(in_dev))
#endif
             ) {
            rcu_read_unlock();
            return ip_route_input_mc(skb, daddr, saddr,
                      tos, dev, our);
         }
      }
      rcu_read_unlock();
      return -EINVAL;
   }
   return ip_route_input_slow(skb, daddr, saddr, tos, dev); // 调用ip_route_input_slow()
}

ip_route_input_slow()

根据fib_lookup()的查找结果设置rth->u.dst.input

static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                u8 tos, struct net_device *dev)
{
   struct fib_result res;
   struct in_device *in_dev = in_dev_get(dev);
   struct flowi fl = { .nl_u = { .ip4_u =
                  { .daddr = daddr,
               .saddr = saddr,
               .tos = tos,
               .scope = RT_SCOPE_UNIVERSE,
                  } },
             .mark = skb->mark,
             .iif = dev->ifindex };
   unsigned   flags = 0;
   u32       itag = 0;
   struct rtable * rth;
   unsigned   hash;
   __be32    spec_dst;
   int       err = -EINVAL;
   int       free_res = 0;
   struct net    * net = dev_net(dev);
 
   /* 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.
    */
 
   if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr) ||
       ipv4_is_loopback(saddr))
      goto martian_source;
 
   if (daddr == htonl(0xFFFFFFFF) || (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))
      goto martian_source;
 
   if (ipv4_is_lbcast(daddr) || ipv4_is_zeronet(daddr) ||
       ipv4_is_loopback(daddr))
      goto martian_destination;
 
   /*
    * Now we are ready to route packet.
    */
   if ((err = fib_lookup(net, &fl, &res)) != 0) { // 查找路由表
      if (!IN_DEV_FORWARD(in_dev))
         goto e_hostunreach;
      goto no_route;
   }
   free_res = 1;
 
   RT_CACHE_STAT_INC(in_slow_tot);
 
   if (res.type == RTN_BROADCAST)
      goto brd_input;
 
   if (res.type == RTN_LOCAL) { // 若目的地址为本地地址,res.type由fib_alias的fa_type决定
      int result;
      result = fib_validate_source(saddr, daddr, tos,
                    net->loopback_dev->ifindex,
                    dev, &spec_dst, &itag, skb->mark);
      if (result < 0)
         goto martian_source;
      if (result)
         flags |= RTCF_DIRECTSRC;
      spec_dst = daddr;
      goto local_input; // 跳转到local_input
   }
 
   if (!IN_DEV_FORWARD(in_dev)) // 若禁止转发
      goto e_hostunreach;
   if (res.type != RTN_UNICAST) // 若目的地址不是单播
      goto martian_destination;
 
   err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); // 调用ip_mkroute_input()
done:
   in_dev_put(in_dev);
   if (free_res)
      fib_res_put(&res);
out:   return err;
 
brd_input:
   if (skb->protocol != htons(ETH_P_IP))
      goto e_inval;
 
   if (ipv4_is_zeronet(saddr))
      spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK);
   else {
      err = fib_validate_source(saddr, 0, tos, 0, dev, &spec_dst,
                 &itag, skb->mark);
      if (err < 0)
         goto martian_source;
      if (err)
         flags |= RTCF_DIRECTSRC;
   }
   flags |= RTCF_BROADCAST;
   res.type = RTN_BROADCAST;
   RT_CACHE_STAT_INC(in_brd);
 
local_input:
   rth = dst_alloc(&ipv4_dst_ops);
   if (!rth)
      goto e_nobufs;
 
   rth->u.dst.output= ip_rt_bug;
   rth->rt_genid = rt_genid(net);
 
   atomic_set(&rth->u.dst.__refcnt, 1);
   rth->u.dst.flags= DST_HOST;
   if (IN_DEV_CONF_GET(in_dev, NOPOLICY))
      rth->u.dst.flags |= DST_NOPOLICY;
   rth->fl.fl4_dst    = daddr;
   rth->rt_dst    = daddr;
   rth->fl.fl4_tos    = tos;
   rth->fl.mark    = skb->mark;
   rth->fl.fl4_src    = saddr;
   rth->rt_src    = saddr;
#ifdef CONFIG_NET_CLS_ROUTE
   rth->u.dst.tclassid = itag;
#endif
   rth->rt_iif    =
   rth->fl.iif    = dev->ifindex;
   rth->u.dst.dev = net->loopback_dev;
   dev_hold(rth->u.dst.dev);
   rth->idev  = in_dev_get(rth->u.dst.dev);
   rth->rt_gateway    = daddr;
   rth->rt_spec_dst= spec_dst;
   rth->u.dst.input= ip_local_deliver; // 设置rth->u.dst.input
   rth->rt_flags  = flags|RTCF_LOCAL;
   if (res.type == RTN_UNREACHABLE) {
      rth->u.dst.input= ip_error;
      rth->u.dst.error= -err;
      rth->rt_flags  &= ~RTCF_LOCAL;
   }
   rth->rt_type   = res.type;
   hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net));
   err = rt_intern_hash(hash, rth, NULL, skb); // rt_intern_hash() -> skb_dst_set()
   goto done;
 
no_route:
   RT_CACHE_STAT_INC(in_no_route);
   spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
   res.type = RTN_UNREACHABLE;
   if (err == -ESRCH)
      err = -ENETUNREACH;
   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_ratelimit())
      printk(KERN_WARNING "martian destination %pI4 from %pI4, dev %s\n",
         &daddr, &saddr, dev->name);
#endif
 
e_hostunreach:
   err = -EHOSTUNREACH;
   goto done;
 
e_inval:
   err = -EINVAL;
   goto done;
 
e_nobufs:
   err = -ENOBUFS;
   goto done;
 
martian_source:
   ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
   goto e_inval;
}

ip_mkroute_input() -> __mkroute_input()

static int ip_mkroute_input(struct sk_buff *skb,
             struct fib_result *res,
             const struct flowi *fl,
             struct in_device *in_dev,
             __be32 daddr, __be32 saddr, u32 tos)
{
   struct rtable* rth = NULL;
   int err;
   unsigned hash;
 
#ifdef CONFIG_IP_ROUTE_MULTIPATH
   if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0)
      fib_select_multipath(fl, res);
#endif
 
   /* create a routing cache entry */
   err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); // 调用__mkroute_input()
   if (err)
      return err;
 
   /* put it into the cache */
   hash = rt_hash(daddr, saddr, fl->iif,
             rt_genid(dev_net(rth->u.dst.dev)));
   return rt_intern_hash(hash, rth, NULL, skb); // rt_intern_hash() -> skb_dst_set()
}
 
 
static int __mkroute_input(struct sk_buff *skb,
            struct fib_result *res,
            struct in_device *in_dev,
            __be32 daddr, __be32 saddr, u32 tos,
            struct rtable **result)
{
 
   struct rtable *rth;
   int err;
   struct in_device *out_dev;
   unsigned flags = 0;
   __be32 spec_dst;
   u32 itag;
 
   /* get a working reference to the output device */
   out_dev = in_dev_get(FIB_RES_DEV(*res));
   if (out_dev == NULL) {
      if (net_ratelimit())
         printk(KERN_CRIT "Bug in ip_route_input" \
                "_slow(). Please, report\n");
      return -EINVAL;
   }
 
 
   err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(*res),
              in_dev->dev, &spec_dst, &itag, skb->mark);
   if (err < 0) {
      ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
                saddr);
 
      err = -EINVAL;
      goto cleanup;
   }
 
   if (err)
      flags |= RTCF_DIRECTSRC;
 
   if (out_dev == in_dev && err &&
       (IN_DEV_SHARED_MEDIA(out_dev) ||
        inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res))))
      flags |= RTCF_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.
       */
      if (out_dev == in_dev) {
         err = -EINVAL;
         goto cleanup;
      }
   }
 
 
   rth = dst_alloc(&ipv4_dst_ops);
   if (!rth) {
      err = -ENOBUFS;
      goto cleanup;
   }
 
   atomic_set(&rth->u.dst.__refcnt, 1);
   rth->u.dst.flags= DST_HOST;
   if (IN_DEV_CONF_GET(in_dev, NOPOLICY))
      rth->u.dst.flags |= DST_NOPOLICY;
   if (IN_DEV_CONF_GET(out_dev, NOXFRM))
      rth->u.dst.flags |= DST_NOXFRM;
   rth->fl.fl4_dst    = daddr;
   rth->rt_dst    = daddr;
   rth->fl.fl4_tos    = tos;
   rth->fl.mark    = skb->mark;
   rth->fl.fl4_src    = saddr;
   rth->rt_src    = saddr;
   rth->rt_gateway    = daddr;
   rth->rt_iif    =
      rth->fl.iif    = in_dev->dev->ifindex;
   rth->u.dst.dev = (out_dev)->dev;
   dev_hold(rth->u.dst.dev);
   rth->idev  = in_dev_get(rth->u.dst.dev);
   rth->fl.oif    = 0;
   rth->rt_spec_dst= spec_dst;
 
   rth->u.dst.input = ip_forward; // 设置rth->u.dst.input
   rth->u.dst.output = ip_output; // 设置rth->u.dst.output
   rth->rt_genid = rt_genid(dev_net(rth->u.dst.dev));
 
   rt_set_nexthop(rth, res, itag);
 
   rth->rt_flags = flags;
 
   *result = rth;
   err = 0;
 cleanup:
   /* release the working reference to the output device */
   in_dev_put(out_dev);
   return err;
}

fib_lookup() -> fn_hash_lookup() -> fib_semantic_match()

static inline int fib_lookup(struct net *net, const struct flowi *flp,
              struct fib_result *res)
{
   struct fib_table *table;
 
   table = fib_get_table(net, RT_TABLE_LOCAL);
   if (!table->tb_lookup(table, flp, res)) // 调用fn_hash_lookup()
      return 0;
 
   table = fib_get_table(net, RT_TABLE_MAIN);
   if (!table->tb_lookup(table, flp, res))
      return 0;
   return -ENETUNREACH;
}
 
 
static int
fn_hash_lookup(struct fib_table *tb, const struct flowi *flp, struct fib_result *res)
{
   int err;
   struct fn_zone *fz;
   struct fn_hash *t = (struct fn_hash *)tb->tb_data;
 
   read_lock(&fib_hash_lock);
   for (fz = t->fn_zone_list; fz; fz = fz->fz_next) { // 遍历非空fn_zone组成链表
      struct hlist_head *head;
      struct hlist_node *node;
      struct fib_node *f;
      __be32 k = fz_key(flp->fl4_dst, fz);
 
      head = &fz->fz_hash[fn_hash(k, fz)];
      hlist_for_each_entry(f, node, head, fn_hash) { // 遍历fib_node组成双向链表
         if (f->fn_key != k)
            continue;
 
         err = fib_semantic_match(&f->fn_alias,
                   flp, res,
                   fz->fz_order); // 调用fib_semantic_match()
         if (err <= 0)
            goto out;
      }
   }
   err = 1;
out:
   read_unlock(&fib_hash_lock);
   return err;
}
 
 
int fib_semantic_match(struct list_head *head, const struct flowi *flp,
             struct fib_result *res, int prefixlen)
{
   struct fib_alias *fa;
   int nh_sel = 0;
 
   list_for_each_entry_rcu(fa, head, fa_list) { // 遍历fib_alias组成链表
      int err;
 
      if (fa->fa_tos &&
          fa->fa_tos != flp->fl4_tos)
         continue;
 
      if (fa->fa_scope < flp->fl4_scope)
         continue;
 
      fa->fa_state |= FA_S_ACCESSED;
 
      err = fib_props[fa->fa_type].error;
      if (err == 0) {
         struct fib_info *fi = fa->fa_info;
 
         if (fi->fib_flags & RTNH_F_DEAD)
            continue;
 
         switch (fa->fa_type) {
         case RTN_UNICAST:
         case RTN_LOCAL:
         case RTN_BROADCAST:
         case RTN_ANYCAST:
         case RTN_MULTICAST:
            for_nexthops(fi) {
               if (nh->nh_flags&RTNH_F_DEAD)
                  continue;
               if (!flp->oif || flp->oif == nh->nh_oif)
                  break;
            }
#ifdef CONFIG_IP_ROUTE_MULTIPATH
            if (nhsel < fi->fib_nhs) {
               nh_sel = nhsel;
               goto out_fill_res;
            }
#else
            if (nhsel < 1) {
               goto out_fill_res; // 跳转到out_fill_res
            }
#endif
            endfor_nexthops(fi);
            continue;
 
         default:
            printk(KERN_WARNING "fib_semantic_match bad type %#x\n",
               fa->fa_type);
            return -EINVAL;
         }
      }
      return err;
   }
   return 1;
 
out_fill_res:
   res->prefixlen = prefixlen;
   res->nh_sel = nh_sel;
   res->type = fa->fa_type; // 设置res->type
   res->scope = fa->fa_scope;
   res->fi = fa->fa_info;
   atomic_inc(&res->fi->fib_clntref);
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值