这么久没有更新了,今天开始继续更新,争取把路由缓存分析完。
本节主要是分析路由缓存相关的数据结构。对于路由缓存来说有两个主要的数据结构:
struct rtable、struct dst_entry。下面分析一下这两个结构
ipv4路由缓存相关结构体,保护了该路由缓存查找的匹配条件,即struct flowi类型的变量、目的ip、源ip、下一跳网关地址、路由类型等。当然了,还有最重要的,保护了一个协议无关的dst_entry变量,通过该union能够很好的实现dst_entry与rtable的转换,而dst_entry中又包含邻居项相关的信息,实现了路由缓存与邻居子系统的关联。
struct rtable
{
union
{
struct dst_entry dst;
} u;
/* Cache lookup keys */
/*路由缓存查找相关的匹配条件,该结构体中存放了路由缓存匹配的所有参数*/
struct flowi fl;
/*指向出口设备的ip配置块*/
struct in_device *idev;
/*相应的取值有RTCF_NOTIFY、RTCF_LOCAL、RTCF_BROADCAST、RTCF_MULTICAST、RTCF_REDIRECTED等*/
unsigned rt_flags;
/*路由项的类型,相应的取值有RTN_UNSPEC、RTN_UNICAST、RTN_LOCAL、RTN_BROADCAST、RTN_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 */
};
协议无关的目的缓存相关的数据结构,保护了路由缓存链接在一起的数据结构成员变量、垃圾回收相关的成员变量、邻居项相关的成员、二层缓存头相关的成员、输入/输出函数指针以用于命中路由缓存的数据包进行后续的数据处理等。
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中,完成路由的查找后,即会
根据input、output函数,传递数据包,对于本地接收的数据包,则其input函数为
ip_local_deliver,本地接收的数据包,只会使用到函数input;对于要转发的数据包,
其input指向函数ip_forward,output则指向函数ip_output,对于转发的数据包则同时需要使用
input与output函数;对于本地发送出去的数据包,则只会使用到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];
};
路由查找相关的数据结构
struct flowi {
/*出口设备*/
int oif;
/*入口设备*/
int iif;
/*mark值*/
__u32 mark;
/*三层相关的成员,对于ipv4有目的ip地址、源ip地址、tos、scope等*/
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;
下面就是rtable、dst_entry、neighbour、hh_cache之间的关联:
结合上以上两幅图,再结合着邻居项相关的系列分析中arp_tbl中的hash_buckets与邻居项之间的关联,基本上就将邻居子系统与路由缓存系统联系在一起了。至于他们之间是如何关联的,后续会进行分析。
由于路由缓存是通过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;
}
至此,完成了路由缓存数据结构与初始化的分析