Linux 路由 学习笔记 之九 路由缓存相关的数据结构及初始化

这么久没有更新了,今天开始继续更新,争取把路由缓存分析完。

1. 相关的数据结构

本节主要是分析路由缓存相关的数据结构。对于路由缓存来说有两个主要的数据结构:

struct rtablestruct dst_entry。下面分析一下这两个结构

 

1.1 struct rtable

 

ipv4路由缓存相关结构体,保护了该路由缓存查找的匹配条件,即struct  flowi类型的变量、目的ip、源ip、下一跳网关地址、路由类型等。当然了,还有最重要的,保护了一个协议无关的dst_entry变量,通过该union能够很好的实现dst_entryrtable的转换,而dst_entry中又包含邻居项相关的信息,实现了路由缓存与邻居子系统的关联。

struct rtable

{

union

{

struct dst_entry dst;

} u;

 

/* Cache lookup keys */

/*路由缓存查找相关的匹配条件,该结构体中存放了路由缓存匹配的所有参数*/

struct flowi fl;

/*指向出口设备的ip配置块*/

struct in_device *idev;

/*相应的取值有RTCF_NOTIFYRTCF_LOCALRTCF_BROADCASTRTCF_MULTICASTRTCF_REDIRECTED*/

unsigned rt_flags;

/*路由项的类型,相应的取值有RTN_UNSPECRTN_UNICASTRTN_LOCALRTN_BROADCASTRTN_MULTICAST*/

__u16 rt_type;

/*多路径缓存类型*/

__u16 rt_multipath_alg;

/*目的ip与源ip*/

__be32 rt_dst; /* Path destination */

__be32 rt_src; /* Path source */

/*入口设备index*/

int rt_iif;

 

/* Info on neighbour */

/*目的地址或者下一跳网关地址*/

__be32 rt_gateway;

 

/* Miscellaneous cached information */

__be32 rt_spec_dst; /* RFC1122 specific destination */

/*存储ip peer相关的信息*/

struct inet_peer *peer; /* long-living peer info */

};

 

 

1.2 struct dst_entry

 

协议无关的目的缓存相关的数据结构,保护了路由缓存链接在一起的数据结构成员变量、垃圾回收相关的成员变量、邻居项相关的成员、二层缓存头相关的成员、输入/输出函数指针以用于命中路由缓存的数据包进行后续的数据处理等。

struct dst_entry

{

/*将路由缓存项链接在一起*/

struct rcu_head rcu_head;

/*dst_entry*/

struct dst_entry *child;

/*出口设备*/

struct net_device       *dev;

short error;

/*

该值表示该dst_entry的状态。

1.若为0,表示可以正常使用

2.若为2,则说明该dst_entry即将被

*/

short obsolete;

int flags;

#define DST_HOST 1

#define DST_NOXFRM 2

#define DST_NOPOLICY 4

#define DST_NOHASH 8

#define DST_BALANCED            0x10

/*表示该表项过期的时间戳*/

unsigned long expires;

 

unsigned short header_len; /* more space at head required */

unsigned short nfheader_len; /* more non-fragment space at head required */

unsigned short trailer_len; /* space to reserve at tail */

/*规格向量,主要是被tcp使用*/

u32 metrics[RTAX_MAX];

struct dst_entry *path;

/*icmp redirect等类型的数据包进行限速操作*/

unsigned long rate_last; /* rate limiting for ICMP */

unsigned long rate_tokens;

/*关联的邻居项*/

struct neighbour *neighbour;

 

/*指向二层头部缓存相关的数据结构变量*/

struct hh_cache *hh;

/*xfrm相关的指针*/

struct xfrm_state *xfrm;

/*输入、输出相关的函数指针,当在函数ip_rcv_finish中,完成路由的查找后,即会

根据inputoutput函数,传递数据包,对于本地接收的数据包,则其input函数为

ip_local_deliver,本地接收的数据包,只会使用到函数input;对于要转发的数据包,

input指向函数ip_forward,output则指向函数ip_output,对于转发的数据包则同时需要使用

inputoutput函数;对于本地发送出去的数据包,则只会使用到output函数。*/

int (*input)(struct sk_buff*);

int (*output)(struct sk_buff*);

 

#ifdef CONFIG_NET_CLS_ROUTE

__u32 tclassid;

#endif

/*dst_ops指针,包括路由缓存的slab缓存块,缓存的垃圾回收函数等*/

struct  dst_ops         *ops;

/*最近一次使用该路由缓存的时间戳*/

unsigned long lastuse;

/*引用计数*/

atomic_t __refcnt; /* client references */

/*该缓存已被使用的次数*/

int __use;

union {

struct dst_entry *next;

struct rtable    *rt_next;

struct rt6_info   *rt6_next;

struct dn_route  *dn_next;

};

/*可变长度数组*/

char info[0];

};

 

 

 

1.3 struct flowi

 

路由查找相关的数据结构

 

struct flowi {

/*出口设备*/

int oif;

/*入口设备*/

int iif;

/*mark*/

__u32 mark;

 

/*三层相关的成员,对于ipv4有目的ip地址、源ip地址、tosscope*/

union {

struct {

__be32 daddr;

__be32 saddr;

__u8 tos;

__u8 scope;

} ip4_u;

struct {

struct in6_addr daddr;

struct in6_addr saddr;

__be32 flowlabel;

} ip6_u;

 

struct {

__le16 daddr;

__le16 saddr;

__u8 scope;

} dn_u;

} nl_u;

#define fld_dst nl_u.dn_u.daddr

#define fld_src nl_u.dn_u.saddr

#define fld_scope nl_u.dn_u.scope

#define fl6_dst nl_u.ip6_u.daddr

#define fl6_src nl_u.ip6_u.saddr

#define fl6_flowlabel nl_u.ip6_u.flowlabel

#define fl4_dst nl_u.ip4_u.daddr

#define fl4_src nl_u.ip4_u.saddr

#define fl4_tos nl_u.ip4_u.tos

#define fl4_scope nl_u.ip4_u.scope

/*四层协议类型与四层协议相关的成员(源、目的端口)*/

__u8 proto;

/*?*/

__u8 flags;

#define FLOWI_FLAG_MULTIPATHOLDROUTE 0x01

union {

struct {

__be16 sport;

__be16 dport;

} ports;

 

struct {

__u8 type;

__u8 code;

} icmpt;

 

struct {

__le16 sport;

__le16 dport;

} dnports;

 

__be32 spi;

 

#ifdef CONFIG_IPV6_MIP6

struct {

__u8 type;

} mht;

#endif

} uli_u;

#define fl_ip_sport uli_u.ports.sport

#define fl_ip_dport uli_u.ports.dport

#define fl_icmp_type uli_u.icmpt.type

#define fl_icmp_code uli_u.icmpt.code

#define fl_ipsec_spi uli_u.spi

#ifdef CONFIG_IPV6_MIP6

#define fl_mh_type uli_u.mht.type

#endif

__u32           secid; /* used by xfrm; see secid.txt */

} __attribute__((__aligned__(BITS_PER_LONG/8)));

 

 

 

路由缓存项是通过hash表链接在一起的,而相应的hash表的定义如下:

static struct rt_hash_bucket  *rt_hash_table;

 

 

下面就是rtabledst_entryneighbourhh_cache之间的关联:

 

Linux 路由 学习笔记 之九 路由缓存相关的数据结构及初始化_第1张图片

Linux 路由 学习笔记 之九 路由缓存相关的数据结构及初始化_第2张图片

 

结合上以上两幅图,再结合着邻居项相关的系列分析中arp_tbl中的hash_buckets与邻居项之间的关联,基本上就将邻居子系统与路由缓存系统联系在一起了。至于他们之间是如何关联的,后续会进行分析。

 

2.路由缓存的初始化

 

由于路由缓存是通过hash表实现路由缓存项之间的关联的,所以在路由缓存初始化时,就需要对hash bucket进行初始化,也需要申请路由缓存项的缓存,以及路由缓存的垃圾回收相关的函数与数值,而这些初始化工作基本上在ip_rt_init中进行的,下面分析下这个函数。

主要实现以下功能:

 

a)为路由缓存创建slab缓存池

b)生成路由缓存hash随机值

c)创建hash bucket rt_hash_table

d)Hash表相应的自旋锁初始化

e)垃圾回收相关的定时器初始化。

int __init ip_rt_init(void)

{

int rc = 0;

/*生成rt_hash_rnd的值,系统会以该值为基础,根据源、目的地址值,为路由缓存

计算相应的hash,从而将路由缓存存放在相应的hash链表中*/

rt_hash_rnd = (int) ((num_physpages ^ (num_physpages>>8)) ^

     (jiffies ^ (jiffies >> 7)));

 

#ifdef CONFIG_NET_CLS_ROUTE

{

int order;

for (order = 0;

     (PAGE_SIZE << order) < 256 * sizeof(struct ip_rt_acct) * NR_CPUS; order++)

/* NOTHING */;

ip_rt_acct = (struct ip_rt_acct *)__get_free_pages(GFP_KERNEL, order);

if (!ip_rt_acct)

panic("IP: failed to allocate ip_rt_acct\n");

memset(ip_rt_acct, 0, PAGE_SIZE << order);

}

#endif

/*dst创建slab缓存池,用于创建struct dst_entry变量*/

ipv4_dst_ops.kmem_cachep =

kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0,

  SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);

/*为路由缓存创建hash链表,并进行初始化。

hash表的每一个成员均为rt_hash_bucket的变量,而rt_hash_bucket里的链表指针则为struct rtable的指针变量*/

rt_hash_table = (struct rt_hash_bucket *)

alloc_large_system_hash("IP route cache",

sizeof(struct rt_hash_bucket),

rhash_entries,

(num_physpages >= 128 * 1024) ?

15 : 17,

0,

&rt_hash_log,

&rt_hash_mask,

0);

memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));

rt_hash_lock_init();

 

ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1);

ip_rt_max_size = (rt_hash_mask + 1) * 16;

 

devinet_init();

ip_fib_init();

 

init_timer(&rt_flush_timer);

rt_flush_timer.function = rt_run_flush;

init_timer(&rt_periodic_timer);

rt_periodic_timer.function = rt_check_expire;

init_timer(&rt_secret_timer);

rt_secret_timer.function = rt_secret_rebuild;

 

/* All the timers, started at system startup tend

   to synchronize. Perturb it a bit.

 */

rt_periodic_timer.expires = jiffies + net_random() % ip_rt_gc_interval +

ip_rt_gc_interval;

add_timer(&rt_periodic_timer);

 

rt_secret_timer.expires = jiffies + net_random() % ip_rt_secret_interval +

ip_rt_secret_interval;

add_timer(&rt_secret_timer);

 

#ifdef CONFIG_PROC_FS

{

struct proc_dir_entry *rtstat_pde = NULL; /* keep gcc happy */

if (!proc_net_fops_create("rt_cache", S_IRUGO, &rt_cache_seq_fops) ||

    !(rtstat_pde = create_proc_entry("rt_cache", S_IRUGO,

     proc_net_stat))) {

return -ENOMEM;

}

rtstat_pde->proc_fops = &rt_cpu_seq_fops;

}

#ifdef CONFIG_NET_CLS_ROUTE

create_proc_read_entry("rt_acct", 0, proc_net, ip_rt_acct_read, NULL);

#endif

#endif

#ifdef CONFIG_XFRM

xfrm_init();

xfrm4_init();

#endif

return rc;

}

至此,完成了路由缓存数据结构与初始化的分析

你可能感兴趣的:(Linux 路由 学习笔记 之九 路由缓存相关的数据结构及初始化)