http://tsecer.blog.163.com/blog/static/1501817201178105531970/
一、说明
这个连个模块总体来首关联性不是很明显,但是这次比较琐碎,所以就放在一起了,大家就将就着看看吧。
二、报文转发
对于ip报文的接受流程
ip_route_input_common--->>>ip_route_input_slow
/*
* Now we are ready to route packet.
*/
err = fib_lookup(net, &fl, &res);首先从路由表中查找这个路由项。
if (err != 0) {
if (!IN_DEV_FORWARD(in_dev)) 这里判断设备是否使能了报文转发功能。
goto e_hostunreach;
goto no_route;
}
……
if (res.type == RTN_LOCAL) {这里的RTN_LOCAL标志位对于这个转发还是本地上传具有重要的意义。
err = fib_validate_source(saddr, daddr, tos,
net->loopback_dev->ifindex,
dev, &spec_dst, &itag, skb->mark);
if (err < 0)
goto martian_source_keep_err;
if (err)
flags |= RTCF_DIRECTSRC;
spec_dst = daddr;
goto local_input;
}
在local_input中,对于报文的读取执行的操作为
dev_hold(rth->dst.dev);
rth->idev = in_dev_get(rth->dst.dev);
rth->rt_gateway = daddr;
rth->rt_spec_dst= spec_dst;
rth->dst.input= ip_local_deliver;
这里判断这个地址是否是本机地址,如果是本机地址,着进入到local_input进行处理,否则继续下面的处理:
err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);---》》》__mkroute_input
rth->dst.input = ip_forward;
rth->dst.output = ip_output;
这里设置了转发函数
三、RTN_LOCAL的设置
fib_inetaddr_event---》》》
case NETDEV_UP:
fib_add_ifaddr(ifa);---》》》
fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);这里的addr就是本地网卡设置的网络地址。-------》》》
static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
{
struct net *net = dev_net(ifa->ifa_dev->dev);
struct fib_table *tb;
struct fib_config cfg = {
.fc_protocol = RTPROT_KERNEL,
.fc_type = type,
---》》》fib_table_insert
new_fa->fa_type = cfg->fc_type;
当执行fib_lookup的时候,执行的命令为:fib_table_lookup---》》fib_semantic_match
res->type = fa->fa_type;
从而将刚才设置的RTN_LOCAL传递给了res,进而判断是一个本地地址,执行ip_local_deliver函数进行转发。
转发项的设置为位置位于 /proc/sys/net/ipv4/ip_forward
这个需要使能为1,默认值为零。
四、邻居子系统
1、需要性
对于不同的L2层,都需要一种知道那些和自己相连的科通讯主机,这个可能包括DECnet以及ND(neighbour Discovery)等不同的算法和介质来进行邻居的发现,其中的ARP只是邻居发现的一种协议方法,同时还存在其他的方法。
在路由表中,一个邻居一般式通过dst_entry来实现,这个结构中定义了input和output接口,这些接口的主语都是host,所以是host output to dst或者host input from dst这样的意义。一个dst可以看成一个远端主机在本地的一个成像(IMAGE),也即是这个客观存在在我们主机上的印象或者说倒影。
当dst处于不同的状态的时候,对应的发送操作也可以做不同的出来。也就是一个NUD,这个状态可以决定我们可以如何进行L2报文的生成。
在ip_finish_output2函数中:
if (dst->hh)
return neigh_hh_output(dst->hh, skb);---》》》
static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)
{
unsigned seq;
int hh_len;
do {
int hh_alen;
seq = read_seqbegin(&hh->hh_lock);
hh_len = hh->hh_len;
hh_alen = HH_DATA_ALIGN(hh_len);
memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);这里的memcpy就是对所有的HardWare Header的拷贝动作,也就是所谓的cache命中和使用。循环对所有的报文进行报文头添加操作。
} while (read_seqretry(&hh->hh_lock, seq));
skb_push(skb, hh_len);
return hh->hh_output(skb);
}
在一个neighbour的初始化过程中会判断一个网络是否支持硬件cache,如果支持则初始化其对应的接口
neigh_hh_init
if (dev->header_ops->cache(n, hh)) {
kfree(hh);
return;
}
然后进行hh_output的初始化
if (n->nud_state & NUD_CONNECTED)
hh->hh_output = n->ops->hh_output;
else
hh->hh_output = n->ops->output;