Friday, November 30, 2007

Linux网络代码导读v0.2 - collide的专栏 - CSDNBlog

Linux网络代码导读v0.2

作者:yawl <>
主页:http://www.nsfocus.com

1 前言

许多人在分析linux代码时对网络部分(主要是src/linux/net,src/linux/include/net及
src/linux/include/linux目录下的文件)比较感兴趣,确实,尽管已经从书本上学到了大量的
TCP/IP原理,不读源码的话,头脑中还是建立不起具体的印象。而分析这部分代码的一个问
题便是代码众多而资料很少。这篇文章的目的就是勾勒出一个框架,让读者能够大致能够了
解TCP/IP究竟是怎么工作的。以前见到的许多代码分析都是基于2.0内核的,在新的内核中
许多函数变了名字,这尤其给初学者带来了困难,本文是以2.4.0-test9的代码作例子,这样
对照代码时可能更清晰些。

其实网络部分的代码我只对防火墙部分一行行仔细分析过,其他许多地方也只是一知半解,
如果理解有误,欢迎指正。

建议在看本文的同时,用source insight(www.soucedyn.com)建立一个项目,同时看代码,
这样可能效果更好点。我也用过其他的一些工具,但在分析大量的代码的时候,没有一个工
具比它更方便的了。


2 正文

ISO的七层模型都非常熟悉了,当然,对于internet,用四层模型更为适合。在这两份模型里,
网络协议以层次的形式出现。而LINUX的内核代码中,严格分出清楚的层次却比较困难,因
为除了一些"内核线程(kernel thread外)",整个内核其实是个单一的进程。因此所谓"网络层
",只是一组相关的函数,而各层之间大多通过一般的函数调用的方式完成交互。

而从逻辑上,网络部分的代码更应该这样分层更为合理:
.BSD socket层:这一部分处理BSD socket相关操作,每个socket在内核中以struct socket结
构体现。
这一部分的文件主要有:/net/socket.c /net/protocols.c etc

.INET socket层:BSD socket是个可以用于各种网络协议的接口,而当用于tcp/ip,即建立
了AF_INET形式的socket时,还需要保留些额外的参数,于是就有了struct sock结构。
文件主要有:/net/ipv4/protocol.c /net/ipv4/af_inet.c /net/core/sock.c etc

.TCP/UDP层:处理传输层的操作,传输层用struct inet_protocol和struct proto两个结构表
示。
文件主要有:/net/ipv4/udp.c /net/ipv4/datagram.c /net/ipv4/tcp.c /net/ipv4/tcp_input.c
/net/ipv4//tcp_output.c /net/ipv4/tcp_minisocks.c /net/ipv4/tcp_output.c
/net/ipv4/tcp_timer.c etc

.IP层:处理网络层的操作,网络层用struct packet_type结构表示。
文件主要有:/net/ipv4/ip_forward.c ip_fragment.c ip_input.c ip_output.c etc.

.数据链路层和驱动程序:每个网络设备以struct net_device表示,通用的处理在dev.c中,
驱动程序都在/driver/net目录下。

网络部分还有很多其他文件,如防火墙,路由等,一般根据看到名字便能猜测出相应的处
理,此处不再赘述。

现在我要给出一张表,全文的内容就是为了说明这张表(如果你觉得我在文章中的语言比较
乏味,尽可抛掉他们,结合这张表自己看代码)。在我最初看网络部分代码时,比较喜欢
《linux kernel internals》的第八章的一段,其中有一个进程A通过网络远程向另一进程B发
包的例子,详细介绍了一个数据包如何从网络堆栈中走过的过程。我觉得这样可以更迅速的
帮助读者看清森林的全貌,因此本文参照这种结构来
叙述。

^
| sys_read fs/read_write.c
| sock_read net/socket.c
| sock_recvmsg net/socket.c
| inet_recvmsg net/ipv4/af_inet.c
| udp_recvmsg net/ipv4/udp.c
| skb_recv_datagram net/core/datagram.c
| -------------------------------------------
| sock_queue_rcv_skb include/net/sock.h
| udp_queue_rcv_skb net/ipv4/udp.c
| udp_rcv net/ipv4/udp.c
| ip_local_deliver_finish net/ipv4/ip_input.c
| ip_local_deliver net/ipv4/ip_input.c
| ip_recv net/ipv4/ip_input.c
| net_rx_action net/dev.c
| -------------------------------------------
| netif_rx net/dev.c
| el3_rx driver/net/3c309.c
| el3_interrupt driver/net/3c309.c

==========================

| sys_write fs/read_write.c
| sock_writev net/socket.c
| sock_sendmsg net/socket.c
| inet_sendmsg net/ipv4/af_inet.c
| udp_sendmsg net/ipv4/udp.c
| ip_build_xmit net/ipv4/ip_output.c
| output_maybe_reroute net/ipv4/ip_output.c
| ip_output net/ipv4/ip_output.c
| ip_finish_output net/ipv4/ip_output.c
| dev_queue_xmit net/dev.c
| --------------------------------------------
| el3_start_xmit driver/net/3c309.c
V



我们假设的环境如下:有两台主机通过互联网联在一起,其中一台机子运行这一个进程A,
另外一台运行进程B,进程A将向进程B发出一条信息,比如"Hello",而B接受此信息。
TCP处理本身非常复杂,为了便于叙述,在后面我们将用UDP作为例子。


2.1 建立套接字

在数据发送之前,要建立一个套接字(socket),在两边的程序中都会调用如下语句:

...
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
...

这是个系统调用,因此会通过0x80中断进入系统内核,调用内核中的相应函数.当寻找系统调
用在内核中的对应流程时,一般前面加入"sys_"再找就是了,如对fork来说,就是调用
sys_fork。但是socket相关调用有些特殊,所有的这类调用都是通过一个入口,即
sys_socketcall进入系统内核,然后再通过参数调用具体的sys_socket,socket_bind等函数。

sys_socket会调用sock_create产生一个struct socket结构(见include/linux/net.h),每个套
接字在内核中都有一个这样的结构对应,在初始化了此结构的一些通用成员后(如分配
inode,根据第二个参数为type项赋值等),会根据其一个参数作响应的调度,即这
一句:
...
net_families[family]->create(sock, protocol);
...

我们的程序的第一个参数是AF_INET,所以此函数指针会指向inet_create();
(net_families是个数组,保留了网络协议族(net families)的信息,而这些协议族用
sock_register加载。)

在struct socket结构结构中最重要的信息保留在struct sock结构中,这个结构在网络代码中
经常使用,建议把它和其他常见结构(如struct sk_buff)打印出来放在手边。在inet_create
会为此结构分配内存,并根据套接字类型(其实就是socket函数的第二个参数),作各自不
同的初始化:
...
if (sk->prot->init)
sk->prot->init(sk);
...

如果类型是SOCK_STREAM的话会调用tcp_v4_init_sock,而SOCK_DGRAM类型的
socket没有额外的初始化了,到此socket调用结束。

还有一个值得注意的地方是当inet_create()调用完后,会接着调用sock_map_fd函数,这
个函数中会为套接字分配一个文件描述符并分配一个file文件。在应用层便可象处理文件一样
处理套接字了。

开始的时候可能有些流程难以跟下去,主要便是这些函数指针的实际指向会根据类型变化。


2.2 发送数据

当进程A想发送数据时,程序中会调用如下语句(如果用sendto函数的话会走类似的流程,
略):
...
write(sockfd,"Hello",strlen("Hello"));
...

write在内核中对应的函数就是sys_write,此函数首先根据文件描述符找到struct file结构,
如果此文件存在(file指针非空)且可写(file->f_mode & FMODE_WRITE为true),便调
用此文件结构的写操作:
...
if (file->f_op && (write = file->f_op->write) != NULL)
ret = write(file, buf, count, &file->f_pos);
...

其中f_op是个struct file_operations结构指针,在sock_map_fd中将其指向socket_file_ops,
其定义如下(/net/socket.c):
static struct file_operations socket_file_ops = {
llseek: sock_lseek,
read: sock_read,
write: sock_write,
poll: sock_poll,
ioctl: sock_ioctl,
mmap: sock_mmap,
open: sock_no_open, /* special open code to disallow open via /proc */
release: sock_close,
fasync: sock_fasync,
readv: sock_readv,
writev: sock_writev
};

此时wirte函数指针显然指向了sock_write,我们跟下去看,此函数将一个字符串缓冲整理成
struct msghdr,最后调用了sock_sendmsg.

sock_sendmsg中的scm_send我不了解(scm是Socket level control messages的简写),好
在它也不是很关键,我们注意到这句:
...
sock->ops->sendmsg(sock, msg, size, &scm);
...

又是个函数指针,sock->ops在inet_create()函数中被初始化,由于我们我们是UDP的套
接字,sock->ops指向了inet_dgram_ops(即sock->ops = &inet_dgram_ops;),其定义在
net/ipv4/Af_inet.c中:
struct proto_ops inet_dgram_ops = {
family: PF_INET,

release: inet_release,
bind: inet_bind,
connect: inet_dgram_connect,
socketpair: sock_no_socketpair,
accept: sock_no_accept,
getname: inet_getname,
poll: datagram_poll,
ioctl: inet_ioctl,
listen: sock_no_listen,
shutdown: inet_shutdown,
setsockopt: inet_setsockopt,
getsockopt: inet_getsockopt,
sendmsg: inet_sendmsg,
recvmsg: inet_recvmsg,
mmap: sock_no_mmap,
};

因此我们要看得便是inet_sendmsg()函数了,而马上,这个函数又通过函数指针调用了另
一函数:
...
sk->prot->sendmsg(sk, msg, size);
...

我们不得不再次寻找其具体指向。看到这里,说点题外话,怎么才能找到其具体定义呢?我
一般是这样:对上例而言,sk是个struct sock结构,到其定义(linux/net/sock.h中)出看到
prot是个struct proto结构,此时我们便在源代码树中寻找所有此结构的实例(这些诸如跳到
定义,寻找引用等工作在source insight中实在太方便快速了^_^),很快便会发现诸如
udp_prot,tcp_prot,raw_prot等,猜测是用了udp_prot,便再找一下它在源代码中的引用情
况,果然发现在inet_create中有这么一句:
...
prot=&udp_prot;
...

其实如果前面看inet_create函数时仔细一点会早点发现了,但我总没有这么细心:)。

我们顺着udp_sendmsg往下走:
在这个函数的主要作用是填充UDP头(源端口,目的端口等),接着调用了
ip_route_output,作用是查找出去的路由,而后:
...
ip_build_xmit(sk,
(sk->no_check == UDP_CSUM_NOXMIT ?
udp_getfrag_nosum :
udp_getfrag),
&ufh, ulen, &ipc, rt, msg->msg_flags);
...

ip_build_xmit函数的很大比例是生成sk_buff,并为数据包加入IP头。
后面有这么一句:
...
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
rt->u.dst.dev,output_maybe_reroute);
...

简单的说,在没有防火墙代码干预的情况下,你可以将此处理解为直接调用
output_maybe_reroute,(具体可参看绿盟月刊14期中的《内核防火墙netfilter入门 》)
而output_maybe_reroute中只有一句:
return skb->dst->output(skb);

依旧照上面的方法(不过这个确实不太好找),发现其实这个指针是在ip_route_output中指
定的,(提示:ip_route_output_slow中:rth->u.dst.output=ip_output;),ip_route_output的
作用便是查找路由,并将结果记录到skb->dst中。

于是,我们开始看ip_output函数了,而它马上又走向了ip_finish_output~~。
每个网络设备,如网卡,在内核中由一个net_device表示,在ip_finish_output中找到其用到
的设备(也是在ip_route_output中初始化的),这个参数在会传给netfilter在
NF_IP_POST_ROUTING点登记的函数,结束后调用ip_finish_output2,而这个函数中又会
调用:
...
hh->hh_output(skb);
...

闲话少叙,实际调用了dev_queue_xmit,到此我们完成了TCP/IP层的工作,开始数据链路
层的处理。

在做了一些判断之后,实际的调用是这句:
...
dev->hard_start_xmit(skb, dev);
...

这个函数是在网卡的驱动程序中定义的,每个不同的网卡有不同的处理,我的网卡是比较通
用的3c509(其驱动程序是3c509.c),在网卡处理化的时候(el3_probe),有:
...
dev->hard_start_xmit = &el3_start_xmit;
...

再往下便是IO操作,将数据包真正的发到网络上去,至此发送过程结束。

中间我说的有些草率,完全没顾的上中间的如出错,阻塞,分片等特殊处理,只是将理想的
过程描述出来。
这篇短文的目的也只是帮助大家建立个大致的印象,其实每个地方的都有非常复杂的处理
(尤其是TCP部分)。


2.3 接受数据

当有数据到达网卡的时候,会产生一个硬件中断,然后调用网卡驱动程序中的函数来处理,
对我的3c509网卡来说,其处理函数为:el3_interrupt。(相应的IRQ号是在系统启动,网卡
初始化时通过request_irq函数决定的。)这个中断处理程序首先要做的当然就是进行一些IO
操作将数据读入(读IO用inw函数),当数据帧成功接受后,执行el3_rx(dev)进一步处理。

在el3_rx中,收到的数据报会被封装成struct sk_buff,并脱离驱动程序,转到通用的处理函
数netif_rx(dev.c)中。为了CPU的效率,上层的处理函数的将采用软中断的方式激活,
netif_rx的一个重要工作就是将传入的sk_buff放到等候队列中,并置软中断标志位,然后便
可放心返回,等待下一次网络数据包的到来:
...
__skb_queue_tail(&queue->input_pkt_queue,skb);
__cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);
...

这个地方在2.2内核中一直被称为"底半"处理--bottom half,其内部实现基本类似,目的是快
速的从中断中返回。

过了一段时间后,一次CPU调度会由于某些原因会发生(如某进程的时间片用完)。在进程
调度函数即schedule()中,会检查有没有软中断发生,若有则运行相应的处理函数:
...
if (softirq_active(this_cpu) & softirq_mask(this_cpu))
goto handle_softirq;
handle_softirq_back:
...
...
handle_softirq:
do_softirq();
goto handle_softirq_back;
...

在系统初始化的时候,具体说是在net_dev_init中,此软中断的处理函数被定为
net_rx_action:
...
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
...

当下一次进程调度被执行的时候,系统会检查是否发生NET_TX_SOFTIRQ软中断,若有则
调用net_rx_action。

net_tx_action函数既是2.2版本中的net_bh函数,在内核中有两个全局变量用来登记网络层
的,一个是链表ptype_all,另外一个是数组ptype_base[16],他们记载了所有内核能够处理
的第三层(按照OSI7层模型)协议。每个网络层的接收处理由一个
struct packet_type表示,而这个结构将通dev_add_pack函数将他们登记到ptype_all或
ptype_base中。只有packet_type中的type项为ETH_P_ALL时,才会登记到ptype_all链表
中,否则如ip_packet_type,会在数组ptype_base[16]找到相应的位置。两者不同点是如果
是以ETH_P_ALL类型登记,那么处理函数会受到所有类型的包,否则只能处理自己登记的类
型的。

skb->protocol是在el3_rx中赋值的,其实就是以太帧头信息中提取出的上层协议名,对于我
们的例子来说,这个值是ETH_P_IP,所以在net_tx_action中,会选择IP层的接收处理函
数,而从ip_packet_type 不难看出,这个函数便是ip_recv()。
pt_prev->func(实际指向ip_recv)前面有一个atomic_inc(&skb->users)操作(在2.2内核
中这个地方是一句skb_clone,原理类似),目的是增加这个sk_buff的引用数。网络层的接
收函数在处理完或因为某些原因要丢弃此sk_buff时(如防火墙)会调用kfree_skb,而
kfree_skb中首先会检查是否还有其他地方需要此函数,如果没有地方再用,才真正释放此内
存(__kfree_skb),否则只是计数器减一。

现在我们便来看看ip_recv(net/ipv4/ip_input.c)。这个函数的操作是非常清晰的:首先检查
这个包的合法性(版本号,长度,校验和等是否正确),如果合法则进行接下来的处理。在
2.4内核中,为了灵活处理防火墙代码,将原来的一个ip_recv分成了两部分,即将将原来的
的ip_recv的后半段独立出一个ip_rcv_finish函数。在ip_rcv_finish中,一部分是带有IP选项
(如源路由等)的IP包,例外就是通过ip_route_input查找路由,并将结果记录到skb->dst
中。此时接收到的包有两种,发往本地进程(需要传往上层协议)或转发(用作网关时),
此时需要的处理函数也不相同,如果传往本地,则调用ip_local_deliver(/net/ipv4/ip_input.c),
否则调用ip_forward(/net/ipv4/ip_forward.c).skb->dst->input这个函数指针会将数据报领上
正确的道路。

对我们的例子而言,此时应该是调用ip_local_deliver的时候了。
发来的包很有可能是碎片包,这样的话则首先应该把它们组装好再传给上层协议,这当然也
是ip_local_deliver函数所做的第一份工作,如果组装成功(返回的sk_buff不为空),则继续
处理(详细的组装算法可参见绿盟月刊13期中的《IP分片重组的分析和常见碎片攻击》)。
但此时代码又被netfilter一分为二了,象前面一样,我们直接到后半段,即
ip_local_deliver_finish(/net/ipv4/ip_input.c)中去。

传输层(如TCP,UDP,RAW)的处理被登记到了inet_protos中(通过
inet_add_protocol)。ip_local_deliver_finish会根据IP头信息中的上层协议信息(即
iph->protocol),调用相应的处理函数。为了简便,我们采用了udp,此时的ipprot->handler
实际便是udp_rcv了。

前面已经提到,在应用程序中建立的每个socket在内核中有一个struct socket/struct sock对
应。udp_rcv会通过udp_v4_lookup首先找到在内核中的sock,然后将其作参数调用
udp_queue_rcv_skb(/net/ipv4/udp.c)。马上,sock_queue_rcv_skb函数被调用,此函数
将sk_buff放入等待队列,然后通知上层数据到达:
...
kb_set_owner_r(skb, sk);
skb_queue_tail(&sk->receive_queue, skb);
if (!sk->dead)
sk->data_ready(sk,skb->len);
return 0;
...

sk->data_ready的定义在sock结构初始化的时候(sock_init_data):
...
sk->data_ready=sock_def_readable;
...

现在我们便要从上往下看起了:
进程B要接收数据报,在程序里调用:
...
read(sockfd,buff,sizeof(buff));
...

此系统调用在内核中的函数是sys_read(fs/read_write.c)以下的处理类似write的操作,不再
详述.udp_recvmsg函数会调用skb_recv_datagram,如果数据还没有到达,且socket设为阻
塞模式时,进程会挂起(signal_pending(current)),直到data_ready通知进程资源得到满
足后继续处理(wake_up_interruptible(sk->sleep);)。

2.4 skbuff

网络代码中有大量的处理涉及对sk_buff的操作,尽管此文中尽量将其回避了,但在仔细分析
的时候则必须对此作分析,数据包在网络协议层是以sk_buff的形式传送处理的,可以说它是
网络部分最重要的数据结构。具体分析建议参看alan cox的《Network Buffers And Memory
Management》,这篇发表在1996年10月的linux journal上。

这里引用phrack 55-12期中的一幅图,尽管它只描绘了sk_buff的极小的一个侧面,但却非常
有用,尤其是当你像我一样总忘记了skb_put是向前还是向后调指针的时候:)

--- -----------------hand
^ | |
| | | ^ skb_push
| | | |
| -----------------data--- ---
| | | ^ |
true | | | v skb_pull
size | | len
| | | | ^ skb_trim
| | | v |
| -----------------tail--- ---
| | | |
| | | v skb_put
v | |
--- -----------------end

linux网络层效率:在linux的网络层代码中指针被大量应用,其目的就是避免数据拷贝这类耗
费系统资源的操作。一个数据包的数据段部分在读入或发出时只经过两次拷贝,即从网卡中
考到核心态内存,和从核心态内存考到用户态内存。前些天看到,在一些提高sniffer抓包效
率的尝试中,turbo packet(一个内核补丁)采用了核心态和
用户态共享一段内存的办法,又减少了一次数据拷贝,进一步提高了效率。


3 后记:
这次的投稿又是到了最后关头仓促写出来的,看着里面拙劣的文笔,实在觉得有点对不住观
众~~如果有时间我会把这部分好好重写的,其实这也是我一直的愿望:)


4 参考文献:

[1.] phrack 55-12期
[2.] 2nd Edition
[3.] Network Buffers And Memory Management Alan Cox
http://www2.linuxjournal.com/lj-issues/issue30/1312.html
[4.] 浙大源码分析报告《Linux网络设备分析》潘纲
[5.] Linux IP Networking--A Guide to the Implementation and Modification of theLinux
Poptocol Stack
Glenn Herrin May 31,2000 http://www.movement.uklinux.net/linux-net.pdf

IP包的生成和发送接口 - collide的专栏

IP包的生成和发送接口

====================
(1) Linux内核中有3种基本的IP包生成器, 它们分别为ip_build_xmit(), ip_queue_xmit(),
ip_build_and_send_pkt(). ip_build_and_send_pkt()是一简单的IP包头封装接口,
它接照输入包的路由添加一个IP包头后直接输出,不进行分片处理, 用于tcp_v4_send_synack()中.
ip_send_reply()是基于ip_build_xmit()的一个函数,
用于tcp_v4_send_ack()和tcp_v4_send_reset()中.

(2) ip_build_xmit()使用用户定义的回调函数直接读取用户数据片段生成IP包输出.
如果需要分片,ip_build_xmit()按照最后一个片段到第一个片段的顺序来生成IP包,
这是因为第一个IP包片段的数据区可能包含对整个IP包数据区的校验码,
在回调函数中用户可能会计算输出数据的校验码,
采用从后向前的输出顺序可使校验码自然地写到第一个片段中.

(3) ip_queue_xmit()完成面向连接套接字输出包的路由和IP包头封装. 当套接字处于连接状态时,
所有从套接字发出的包都具有确定的路由, 无需为每一个输出包查询它的目的入口,
可将套接字直接绑定到路由入口上, 这由套接字的目的缓冲指针(dst_cache)来完成.
ip_queue_xmit()首先为输入包建立IP包头, 经过本地包过滤器后,
再将IP包分片输出(ip_fragment), 如果需要的话.

(4) IP包生成器的输出经过本地包过滤器后输入包的路由入口, 对于点播地址来说,
输入到IP输出器中(ip_output); 对于广播或同播地址来说, 输入到IP同播输出器(ip_mc_output).
在IP输出器中, 再经过路由后过滤器,
进入路由的"邻居"入口(dst->neighbour->output)或硬件帧头缓冲入口(dst->hh->hh_output).
邻居是指与主机自已在网络接口设备层次上直达的相邻主机.
邻居负责解析输出包的硬件投送地址, 将包投递给相邻的目的主机或网关主机.
当邻居成功解析包的硬件投送地址时, 将在包的目的入口上创建硬件帧头缓冲结构(dst->hh),
使得后继包可以直接使用组装好的帧头, 直接将包传递给包调度器(dev_queue_xmit).
包调度器按照包的优先级进行重排, 最后将包提交给设备驱动程序发送(dev->hard_start_xmit).

IP包生成接口
------------
; net/ipv4/ip_output.c:

int sysctl_ip_default_ttl = IPDEFTTL; 缺省的IP包生存期为64

/*
* Add an ip header to a skbuff and send it out.
*/
int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
对包的数据体添加IP头后直接输出
u32 saddr, u32 daddr, struct ip_options *opt)
{
struct rtable *rt = (struct rtable *)skb->dst;
struct iphdr *iph;

/* Build the IP header. */
if (opt)
iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
else
iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));

iph->version = 4;
iph->ihl = 5;
iph->tos = sk->protinfo.af_inet.tos;
iph->frag_off = 0;
if (ip_dont_fragment(sk, &rt->u.dst)) 如果IP包的目的入口禁止分片
iph->frag_off |= htons(IP_DF);
iph->ttl = sk->protinfo.af_inet.ttl; 取套接字协议选项中的生存期
iph->daddr = rt->rt_dst; 取IP包路由的目的地址
iph->saddr = rt->rt_src; 取IP包路由的源地址
iph->protocol = sk->protocol; 取套接字IP协议代码
iph->tot_len = htons(skb->len); IP包总长度
ip_select_ident(iph, &rt->u.dst); 为IP包分配标识号, 禁止分片的IP包标识为零
skb->nh.iph = iph;

if (opt && opt->optlen) {
iph->ihl += opt->optlen>>2;
ip_options_build(skb, opt, daddr, rt, 0); 设置IP选项区
}
ip_send_check(iph); 设置IP包头的校验和

/* Send it out. */
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
output_maybe_reroute); 过滤输出并且目的路径可能会被改变
}

int ip_build_xmit(struct sock *sk,
int getfrag (const void *,
char *,
unsigned int,
unsigned int), 取数据片段的函数指针
const void *frag, 以上函数的调用参数
unsigned length,
struct ipcm_cookie *ipc, IP包配置信息
struct rtable *rt,
int flags) 从用户数据建立IP包
{
int err;
struct sk_buff *skb;
int df;
struct iphdr *iph;

/*
* Try the simple case first. This leaves fragmented frames, and by
* choice RAW frames within 20 bytes of maximum size(rare) to the long path
*/

if (!sk->protinfo.af_inet.hdrincl) { 如果IP包头不由用户创建
length += sizeof(struct iphdr); 取IP包总长

/*
* Check for slow path.
*/
if (length > rt->u.dst.pmtu || ipc->opt != NULL) 如果包长度大于目的入口的最大片断长
return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);
} else {
if (length > rt->u.dst.dev->mtu) { 如果包长大于目的入口设备的最大片段长
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
return -EMSGSIZE;
}
}
if (flags&MSG_PROBE) 测试操作
goto out;

/*
* Do path mtu discovery if needed.
*/
df = 0;
if (ip_dont_fragment(sk, &rt->u.dst)) 如果禁止分片
df = htons(IP_DF);

/*
* Fast path for unfragmented frames without options.
*/
{
int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;

skb = sock_alloc_send_skb(sk, length+hh_len+15,
0, flags&MSG_DONTWAIT, &err); 为套接字分配发送包
if(skb==NULL)
goto error;
skb_reserve(skb, hh_len); 保留硬件帧头空间
}

skb->priority = sk->priority; 取套接字的优先级
skb->dst = dst_clone(&rt->u.dst); 取路由的目的入口

skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);

if(!sk->protinfo.af_inet.hdrincl) {
iph->version=4;
iph->ihl=5;
iph->tos=sk->protinfo.af_inet.tos;
iph->tot_len = htons(length);
iph->frag_off = df;
iph->ttl=sk->protinfo.af_inet.mc_ttl;
ip_select_ident(iph, &rt->u.dst);
if (rt->rt_type != RTN_MULTICAST)
iph->ttl=sk->protinfo.af_inet.ttl;
iph->protocol=sk->protocol;
iph->saddr=rt->rt_src;
iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
; 读取用户一片数据
}
else 如果IP包头由用户创建, 直接将用户数据读入IP头所在位置
err = getfrag(frag, (void *)iph, 0, length);

if (err)
goto error_fault;

err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
output_maybe_reroute);
if (err > 0)
err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;
if (err)
goto error;
out:
return 0;

error_fault:
err = -EFAULT;
kfree_skb(skb);
error:
IP_INC_STATS(IpOutDiscards);
return err;
}
static int ip_build_xmit_slow(struct sock *sk,
int getfrag (const void *,
char *,
unsigned int,
unsigned int),
const void *frag,
unsigned length,
struct ipcm_cookie *ipc,
struct rtable *rt,
int flags) 建立IP选项区或者分片输出
{
unsigned int fraglen, maxfraglen, fragheaderlen;
int err;
int offset, mf;
int mtu;
u16 id = 0;

int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
int nfrags=0;
struct ip_options *opt = ipc->opt;
int df = 0;

mtu = rt->u.dst.pmtu;
if (ip_dont_fragment(sk, &rt->u.dst))
df = htons(IP_DF);

length -= sizeof(struct iphdr);

if (opt) {
fragheaderlen = sizeof(struct iphdr) + opt->optlen;
maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
} else {
fragheaderlen = sizeof(struct iphdr);

/*
* Fragheaderlen is the size of 'overhead' on each buffer. Now work
* out the size of the frames to send.
*/

maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
} 求最大IP包长

if (length + fragheaderlen > 0xFFFF) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
return -EMSGSIZE;
}

/*
* Start at the end of the frame by handling the remainder.
*/

offset = length - (length % (maxfraglen - fragheaderlen));
取最后一个片段的数据偏移量

/*
* Amount of memory to allocate for final fragment.
*/

fraglen = length - offset + fragheaderlen; 求取后一个片段IP包全长

if (length-offset==0) { 如果用户数据恰好是最大单片数据长度的整数倍
fraglen = maxfraglen;
offset -= maxfraglen-fragheaderlen;
}

/*
* The last fragment will not have MF (more fragments) set.
*/

mf = 0;

/*
* Don't fragment packets for path mtu discovery.
*/

if (offset > 0 && sk->protinfo.af_inet.pmtudisc==IP_PMTUDISC_DO) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
return -EMSGSIZE;
}
if (flags&MSG_PROBE)
goto out;

/*
* Begin outputting the bytes.
*/

do {
char *data;
struct sk_buff * skb;

/*
* Get the memory we require with some space left for alignment.
*/

skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
if (skb == NULL)
goto error;

/*
* Fill in the control structures
*/

skb->priority = sk->priority;
skb->dst = dst_clone(&rt->u.dst);
skb_reserve(skb, hh_len);

/*
* Find where to start putting bytes.
*/

data = skb_put(skb, fraglen);
skb->nh.iph = (struct iphdr *)data;

/*
* Only write IP header onto non-raw packets
*/

{
struct iphdr *iph = (struct iphdr *)data;

iph->version = 4;
iph->ihl = 5;
if (opt) {
iph->ihl += opt->optlen>>2;
ip_options_build(skb, opt,
ipc->addr, rt, offset);
}
iph->tos = sk->protinfo.af_inet.tos;
iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
iph->frag_off = htons(offset>>3)|mf|df;
iph->id = id;
if (!mf) {
if (offset || !df) {
/* Select an unpredictable ident only
* for packets without DF or having
* been fragmented.
*/
__ip_select_ident(iph, &rt->u.dst);
id = iph->id;
}

/*
* Any further fragments will have MF set.
*/
mf = htons(IP_MF);
}
if (rt->rt_type == RTN_MULTICAST)
iph->ttl = sk->protinfo.af_inet.mc_ttl;
else
iph->ttl = sk->protinfo.af_inet.ttl;
iph->protocol = sk->protocol;
iph->check = 0;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
data += iph->ihl*4;
}

/*
* User data callback
*/

if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
err = -EFAULT;
kfree_skb(skb);
goto error;
}

offset -= (maxfraglen-fragheaderlen); 片段从后向前进行分割, 是为了方便TCP包的校验
fraglen = maxfraglen;

nfrags++;

err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
skb->dst->dev, output_maybe_reroute);
if (err) {
if (err > 0)
err = sk->protinfo.af_inet.recverr ? net_xmit_errno(err) : 0;
if (err)
goto error;
}
} while (offset >= 0);

if (nfrags>1)
ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
out:
return 0;

error:
IP_INC_STATS(IpOutDiscards);
if (nfrags>1)
ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
return err;
}

/*
* Generic function to send a packet as reply to another packet.
* Used to send TCP resets so far. ICMP should use this function too.
*
* Should run single threaded per socket because it uses the sock
* structure to pass arguments.
*/
void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
unsigned int len)
{
struct {
struct ip_options opt;
char data[40]; 存放IP选项块
} replyopts;
struct ipcm_cookie ipc;
u32 daddr;
struct rtable *rt = (struct rtable*)skb->dst;

if (ip_options_echo(&replyopts.opt, skb)) 将包skb的IP选项刷新到replyopts结构中
return;

daddr = ipc.addr = rt->rt_src;
ipc.opt = NULL;

if (replyopts.opt.optlen) {
ipc.opt = &replyopts.opt;

if (ipc.opt->srr)
daddr = replyopts.opt.faddr;
}

if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
return;

/* And let IP do all the hard work.

This chunk is not reenterable, hence spinlock.
Note that it uses the fact, that this function is called
with locally disabled BH and that sk cannot be already spinlocked.
*/
bh_lock_sock(sk);
sk->protinfo.af_inet.tos = skb->nh.iph->tos;
sk->priority = skb->priority;
sk->protocol = skb->nh.iph->protocol;
ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT);
bh_unlock_sock(sk);

ip_rt_put(rt);
}
struct ip_reply_arg {
struct iovec iov[2];
int n_iov; /* redundant */
u32 csum;
int csumoffset; /* u16 offset of csum in iov[0].iov_base */
/* -1 if not needed */
};
/*
* Fetch data from kernel space and fill in checksum if needed.
*/
static int ip_reply_glue_bits(const void *dptr, char *to, unsigned int offset,
unsigned int fraglen)
{
struct ip_reply_arg *dp = (struct ip_reply_arg*)dptr;
u16 *pktp = (u16 *)to;
struct iovec *iov;
int len;
int hdrflag = 1;

iov = &dp->iov[0];
if (offset >= iov->iov_len) {
offset -= iov->iov_len;
iov++;
hdrflag = 0;
}
len = iov->iov_len - offset;
if (fraglen > len) { /* overlapping. */
dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,
dp->csum);
offset = 0;
fraglen -= len;
to += len;
iov++;
}

dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, fraglen,
dp->csum);

if (hdrflag && dp->csumoffset)
*(pktp + dp->csumoffset) = csum_fold(dp->csum); /* fill in checksum */
return 0;
}

int ip_queue_xmit(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct ip_options *opt = sk->protinfo.af_inet.opt;
struct rtable *rt;
struct iphdr *iph;

/* Make sure we can route this packet. */
rt = (struct rtable *)__sk_dst_check(sk, 0); 取套接字所缓冲的发送包的目的路由入口
if (rt == NULL) { 如果尚未缓冲
u32 daddr;

/* Use correct destination address if we have options. */
daddr = sk->daddr; 取套接字的对端地址作为目的地址
if(opt && opt->srr) 如果具有信源路由选项
daddr = opt->faddr; 取信源路由的转发地址作为目的地址

/* If this fails, retransmit mechanism of transport layer will
* keep trying until route appears or the connection times itself
* out.
*/
if (ip_route_output(&rt, daddr, sk->saddr,
RT_TOS(sk->protinfo.af_inet.tos) | RTO_CONN | sk->localroute,
sk->bound_dev_if)) 查询目的地址的路由目的入口
goto no_route;
__sk_dst_set(sk, &rt->u.dst); 将该路由入口缓冲到套接字上
}
skb->dst = dst_clone(&rt->u.dst); 将路由入口绑定到发送包

if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto no_route; 如果是指定严格信源路由并且其转发地址不等于网关地址,则操作失败

/* OK, we know where to send it, allocate and build IP header. */
iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen :
0));
*((__u16 *)iph) = htons((4 <<>protinfo.af_inet.tos & 0xff));
iph->tot_len = htons(skb->len);
iph->frag_off = 0;
iph->ttl = sk->protinfo.af_inet.ttl;
iph->protocol = sk->protocol;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
skb->nh.iph = iph;
/* Transport layer set skb->h.foo itself. */

if(opt && opt->optlen) { 建立IP选项区
iph->ihl += opt->optlen >> 2;
ip_options_build(skb, opt, sk->daddr, rt, 0);
}

return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
ip_queue_xmit2); 过滤输出

no_route:
IP_INC_STATS(IpOutNoRoutes);
kfree_skb(skb);
return -EHOSTUNREACH;
}

/* Queues a packet to be sent, and starts the transmitter if necessary.
* This routine also needs to put in the total length and compute the
* checksum. We use to do this in two stages, ip_build_header() then
* this, but that scheme created a mess when routes disappeared etc.
* So we do it all here, and the TCP send engine has been changed to
* match. (No more unroutable FIN disasters, etc. wheee...) This will
* most likely make other reliable transport layers above IP easier
* to implement under Linux.
*/
static inline int ip_queue_xmit2(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct rtable *rt = (struct rtable *)skb->dst;
struct net_device *dev;
struct iphdr *iph = skb->nh.iph;

dev = rt->u.dst.dev;

/* This can happen when the transport layer has segments queued
* with a cached route, and by the time we get here things are
* re-routed to a device with a different MTU than the original
* device. Sick, but we must cover it.
*/
if (skb_headroom(skb) <>hard_header_len && dev->hard_header) {
如果包可用的硬件帧头空间不足
struct sk_buff *skb2;

skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);
复制并重新分配原包
kfree_skb(skb);
if (skb2 == NULL)
return -ENOMEM;
if (sk)
skb_set_owner_w(skb2, sk); 设置包的拥有套接字
skb = skb2;
iph = skb->nh.iph;
}

if (skb->len > rt->u.dst.pmtu)
goto fragment;

if (ip_dont_fragment(sk, &rt->u.dst))
iph->frag_off |= __constant_htons(IP_DF);

ip_select_ident(iph, &rt->u.dst);

/* Add an IP checksum. */
ip_send_check(iph);

skb->priority = sk->priority;
return skb->dst->output(skb);

fragment:
if (ip_dont_fragment(sk, &rt->u.dst)) {
/* Reject packet ONLY if TCP might fragment
* it itself, if were careful enough.
*/
iph->frag_off |= __constant_htons(IP_DF);
NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));

icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(rt->u.dst.pmtu));
kfree_skb(skb);
return -EMSGSIZE;
}
ip_select_ident(iph, &rt->u.dst);
return ip_fragment(skb, skb->dst->output);
}

/*
* This IP datagram is too large to be sent in one piece. Break it up into
* smaller pieces (each of size equal to IP header plus
* a block of the data of the original IP data part) that will yet fit in a
* single device frame, and queue such a frame for sending.
*
* Yes this is inefficient, feel free to submit a quicker one.
*/

int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
{
struct iphdr *iph;
unsigned char *raw;
unsigned char *ptr;
struct net_device *dev;
struct sk_buff *skb2;
unsigned int mtu, hlen, left, len;
int offset;
int not_last_frag;
struct rtable *rt = (struct rtable*)skb->dst;
int err = 0;

dev = rt->u.dst.dev;

/*
* Point into the IP datagram header.
*/

raw = skb->nh.raw;
iph = (struct iphdr*)raw;

/*
* Setup starting values.
*/

hlen = iph->ihl * 4;
left = ntohs(iph->tot_len) - hlen; /* Space per frame */
mtu = rt->u.dst.pmtu - hlen; /* Size of data space */
ptr = raw + hlen; /* Where to start from */

/*
* Fragment the datagram.
*/

offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
not_last_frag = iph->frag_off & htons(IP_MF);

/*
* Keep copying data until we run out.
*/

while(left > 0) {
len = left;
/* IF: it doesn't fit, use 'mtu' - the data space left */
if (len > mtu)
len = mtu;
/* IF: we are not sending upto and including the packet end
then align the next start on an eight byte boundary */
if (len < left) {
len &= ~7;
}
/*
* Allocate buffer.
*/

if ((skb2 = alloc_skb(len+hlen+dev->hard_header_len+15,GFP_ATOMIC)) == NULL) {
NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
err = -ENOMEM;
goto fail;
}

/*
* Set up data on packet
*/

skb2->pkt_type = skb->pkt_type;
skb2->priority = skb->priority;
skb_reserve(skb2, (dev->hard_header_len+15)&~15);
skb_put(skb2, len + hlen);
skb2->nh.raw = skb2->data;
skb2->h.raw = skb2->data + hlen;

/*
* Charge the memory for the fragment to any owner
* it might possess
*/

if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
skb2->dst = dst_clone(skb->dst);
skb2->dev = skb->dev;

/*
* Copy the packet header into the new buffer.
*/

memcpy(skb2->nh.raw, raw, hlen);

/*
* Copy a block of the IP datagram.
*/
memcpy(skb2->h.raw, ptr, len);
left -= len;

/*
* Fill in the new header fields.
*/
iph = skb2->nh.iph;
iph->frag_off = htons((offset >> 3));

/* ANK: dirty, but effective trick. Upgrade options only if
* the segment to be fragmented was THE FIRST (otherwise,
* options are already fixed) and make it ONCE
* on the initial skb, so that all the following fragments
* will inherit fixed options.
*/
if (offset == 0)
ip_options_fragment(skb);

/*
* Added AC : If we are fragmenting a fragment that's not the
* last fragment then keep MF on each bit
*/
if (left > 0 || not_last_frag)
iph->frag_off |= htons(IP_MF);
ptr += len;
offset += len;

#ifdef CONFIG_NETFILTER
/* Connection association is same as pre-frag packet */
skb2->nfct = skb->nfct;
nf_conntrack_get(skb2->nfct);
#ifdef CONFIG_NETFILTER_DEBUG
skb2->nf_debug = skb->nf_debug;
#endif
#endif

/*
* Put this fragment into the sending queue.
*/

IP_INC_STATS(IpFragCreates);

iph->tot_len = htons(len + hlen);

ip_send_check(iph);

err = output(skb2);
if (err)
goto fail;
}
kfree_skb(skb);
IP_INC_STATS(IpFragOKs);
return err;

fail:
kfree_skb(skb);
IP_INC_STATS(IpFragFails);
return err;
}

IP包的输出接口
--------------

/* Don't just hand NF_HOOK skb->dst->output, in case netfilter hook
changes route */
static inline int 发送包的目的入口指针可能被包过滤器所改变,
因此不能向包过滤器直接提供目的入口的输出函数
output_maybe_reroute(struct sk_buff *skb)
{
return skb->dst->output(skb); 通过目的入口输出, 对于点播(UNICAST)地址来说,
指向ip_output()
}

int ip_output(struct sk_buff *skb) 点播地址的IP包输出口
{
#ifdef CONFIG_IP_ROUTE_NAT
struct rtable *rt = (struct rtable*)skb->dst;
#endif

IP_INC_STATS(IpOutRequests);

#ifdef CONFIG_IP_ROUTE_NAT
if (rt->rt_flags&RTCF_NAT)
ip_do_nat(skb);
#endif

return ip_finish_output(skb);
}
__inline__ int ip_finish_output(struct sk_buff *skb)
{
struct net_device *dev = skb->dst->dev;

skb->dev = dev; 设置包的输出设备
skb->protocol = __constant_htons(ETH_P_IP); 设置包的帧类型

return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
ip_finish_output2);
}
static inline int ip_finish_output2(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst; 取IP包绑定的目的入口
struct hh_cache *hh = dst->hh; 取目的入口的硬件帧头缓冲结构

#ifdef CONFIG_NETFILTER_DEBUG
nf_debug_ip_finish_output2(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/

if (hh) { 如果帧头缓冲结构存在
read_lock_bh(&hh->hh_lock);
memcpy(skb->data - 16, hh->hh_data, 16); 直接拷贝硬件帧头
read_unlock_bh(&hh->hh_lock);
skb_push(skb, hh->hh_len);
return hh->hh_output(skb); 通过帧头缓冲输出, 一般指向dev_queue_xmit()
} else if (dst->neighbour) 通过邻居结构(设备硬件地址解析和缓冲结构)输出
return dst->neighbour->output(skb); 指向neigh_resolve_output()
; 关于neighbour结构和arp过程可看自已"Linux硬件地址解析过程"的帖子
printk(KERN_DEBUG "khm\n");
kfree_skb(skb);
return -EINVAL;
}

IP包创建过程中选项区的操作
--------------------------

; net/ipv4/ip_options.c:

struct ip_options { IP选项区索引结构, 当接收到IP包时,
包缓冲的控制块(cb)前部即为此结构
__u32 faddr; /* Saved first hop address */
unsigned char optlen;
unsigned char srr;
unsigned char rr;
unsigned char ts;
unsigned char is_setbyuser:1, /* Set by setsockopt? */
is_data:1, /* Options in __data, rather than skb */
is_strictroute:1, /* Strict source route */
srr_is_hit:1, /* Packet destination addr was our one */
is_changed:1, /* IP checksum more not valid */
rr_needaddr:1, /* Need to record addr of outgoing dev */
ts_needtime:1, /* Need to record timestamp */
ts_needaddr:1; /* Need to record addr of outgoing dev */
unsigned char router_alert;
unsigned char __pad1;
unsigned char __pad2;
unsigned char __data[0];
};
struct inet_skb_parm
{
struct ip_options opt; /* Compiled IP options */
unsigned char flags;

#define IPSKB_MASQUERADED 1
#define IPSKB_TRANSLATED 2
#define IPSKB_FORWARDED 4
};

#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))

/*
* Options "fragmenting", just fill options not
* allowed in fragments with NOOPs.
* Simple and stupid 8), but the most efficient way.
*/

void ip_options_fragment(struct sk_buff * skb)
{
unsigned char * optptr = skb->nh.raw; ??? 不是指向IP头吗,怎么用作IP选项区???
struct ip_options * opt = &(IPCB(skb)->opt); 取包缓冲控制块(cb)
int l = opt->optlen;
int optlen;

while (l > 0) {
switch (*optptr) {
case IPOPT_END:
return;
case IPOPT_NOOP:
l--;
optptr++;
continue;
}
optlen = optptr[1];
if (optlen<2>l)
return;
if (!IPOPT_COPIED(*optptr)) 将所有没有片断复制标志的选项置换成空选项
memset(optptr, IPOPT_NOOP, optlen);
l -= optlen;
optptr += optlen;
}
opt->ts = 0;
opt->rr = 0;
opt->rr_needaddr = 0;
opt->ts_needaddr = 0;
opt->ts_needtime = 0;
return;
}

/*
* Write options to IP header, record destination address to
* source route option, address of outgoing interface
* (we should already know it, so that this function is allowed be
* called only after routing decision) and timestamp,
* if we originate this datagram.
*
* daddr is real destination address, next hop is recorded in IP header.
* saddr is address of outgoing interface.
*/

void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
u32 daddr, struct rtable *rt, int is_frag)
{
unsigned char * iph = skb->nh.raw;

memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
将IP选项参数复制到包缓冲上
memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
将IP选项数据块复制到IP包选项区
opt = &(IPCB(skb)->opt);
opt->is_data = 0;

if (opt->srr) 将地址daddr拷贝到信源路由选项表的最后一项
memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);

if (!is_frag) { 如果不是片段
if (opt->rr_needaddr) 记录输出设备的地址
ip_rt_get_source(iph+opt->rr+iph[opt->rr+2]-5, rt);
if (opt->ts_needaddr) 记录时戳地址
ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, rt);
if (opt->ts_needtime) { 记录时戳
struct timeval tv;
__u32 midtime;
do_gettimeofday(&tv);
midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
}
return;
}
if (opt->rr) { 填充成空选项
memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
opt->rr = 0;
opt->rr_needaddr = 0;
}
if (opt->ts) {
memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
opt->ts = 0;
opt->ts_needaddr = opt->ts_needtime = 0;
}
}

/*
* Provided (sopt, skb) points to received options,
* build in dopt compiled option set appropriate for answering.
* i.e. invert SRR option, copy anothers,
* and grab room in RR/TS options.
*
* NOTE: dopt cannot point to skb.
*/

int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
{
struct ip_options *sopt;
unsigned char *sptr, *dptr;
int soffset, doffset;
int optlen;
u32 daddr;

memset(dopt, 0, sizeof(struct ip_options)); 将输出选项区索引结构清零

dopt->is_data = 1;

sopt = &(IPCB(skb)->opt);

if (sopt->optlen == 0) {
dopt->optlen = 0;
return 0;
}

sptr = skb->nh.raw;
dptr = dopt->__data; 指向输出选项区开始

if (skb->dst)
daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
else
daddr = skb->nh.iph->daddr;

if (sopt->rr) {
optlen = sptr[sopt->rr+1]; 取IP包路由记录表长度
soffset = sptr[sopt->rr+2]; 取当前地址记录索引
dopt->rr = dopt->optlen + sizeof(struct iphdr); 取输出选项区尾部
memcpy(dptr, sptr+sopt->rr, optlen); 复制路由记录表
if (sopt->rr_needaddr && soffset <= optlen) {
if (soffset + 3 > optlen)
return -EINVAL;
dptr[2] = soffset + 4; 更新地址记录索引
dopt->rr_needaddr = 1;
}
dptr += optlen; 更新选项区输出指针
dopt->optlen += optlen; 更新选项区长度变量
}
if (sopt->ts) {
optlen = sptr[sopt->ts+1]; 取时戳表选项长度
soffset = sptr[sopt->ts+2]; 取当前时戳记录索引
dopt->ts = dopt->optlen + sizeof(struct iphdr);
memcpy(dptr, sptr+sopt->ts, optlen); 复制时戳表
if (soffset <= optlen) {
if (sopt->ts_needaddr) {
if (soffset + 3 > optlen)
return -EINVAL;
dopt->ts_needaddr = 1;
soffset += 4;
}
if (sopt->ts_needtime) {
if (soffset + 3 > optlen)
return -EINVAL;
if ((dptr[3]&0xF) != IPOPT_TS_PRESPEC) {
dopt->ts_needtime = 1;
soffset += 4;
} else { 如果为取指定地址时戳
dopt->ts_needtime = 0;

if (soffset + 8 <= optlen) {
__u32 addr;

memcpy(&addr, sptr+soffset-1, 4);
if (inet_addr_type(addr) != RTN_LOCAL) {
dopt->ts_needtime = 1;
soffset += 8;
}
}
}
}
dptr[2] = soffset;
}
dptr += optlen;
dopt->optlen += optlen;
}
if (sopt->srr) {
unsigned char * start = sptr+sopt->srr;
u32 faddr;

optlen = start[1];
soffset = start[2];
doffset = 0;
if (soffset > optlen)
soffset = optlen + 1;
soffset -= 4;
if (soffset > 3) {
memcpy(&faddr, &start[soffset-1], 4);
for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
memcpy(&dptr[doffset-1], &start[soffset-1], 4);
/*
* RFC1812 requires to fix illegal source routes.
*/
if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
doffset -= 4;
}
if (doffset > 3) {
memcpy(&start[doffset-1], &daddr, 4);
dopt->faddr = faddr;
dptr[0] = start[0];
dptr[1] = doffset+3;
dptr[2] = 4;
dptr += doffset+3;
dopt->srr = dopt->optlen + sizeof(struct iphdr);
dopt->optlen += doffset+3;
dopt->is_strictroute = sopt->is_strictroute;
}
}
while (dopt->optlen & 3) {
*dptr++ = IPOPT_END;
dopt->optlen++;
}
return 0;
}

其余一些相关函数
----------------

static inline struct dst_entry *
__sk_dst_check(struct sock *sk, u32 cookie) 校验套接字所缓冲的目的入口
{
struct dst_entry *dst = sk->dst_cache;

if (dst && dst->obsolete && dst->ops->check(dst, cookie) == NULL) {
sk->dst_cache = NULL;
return NULL;
}

return dst;
}
static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
{ ; ipv4协议的目的入口校验函数
dst_release(dst);
return NULL;
}
static inline
int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) 是否允许IP分片
{
return (sk->protinfo.af_inet.pmtudisc == IP_PMTUDISC_DO ||
(sk->protinfo.af_inet.pmtudisc == IP_PMTUDISC_WANT &&
!(dst->mxlock&(1<
}
static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst)
分配IP包标识号
{
if (iph->frag_off&__constant_htons(IP_DF))
iph->id = 0;
else
__ip_select_ident(iph, dst);
}

/* Generate a checksum for an outgoing IP datagram. */
__inline__ void ip_send_check(struct iphdr *iph) 校验IP包头
{
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
}

#define SKB_EXT_ERR(skb) ((struct sock_exterr_skb *) ((skb)->cb))

void ip_local_error(struct sock *sk, int err, u32 daddr, u16 port, u32 info)
生成出错信息包
{
struct sock_exterr_skb *serr;
struct iphdr *iph;
struct sk_buff *skb;

if (!sk->protinfo.af_inet.recverr)
return;

skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
if (!skb)
return;

iph = (struct iphdr*)skb_put(skb, sizeof(struct iphdr));
skb->nh.iph = iph;
iph->daddr = daddr;

serr = SKB_EXT_ERR(skb); 取包缓冲控制块(cb)
serr->ee.ee_errno = err; 错误码
serr->ee.ee_origin = SO_EE_ORIGIN_LOCAL; 错误来源
serr->ee.ee_type = 0;
serr->ee.ee_code = 0;
serr->ee.ee_pad = 0;
serr->ee.ee_info = info; 出错参数
serr->ee.ee_data = 0;
serr->addr_offset = (u8*)&iph->daddr - skb->nh.raw;
serr->port = port;

skb->h.raw = skb->tail;
skb_pull(skb, skb->tail - skb->data);

if (sock_queue_err_skb(sk, skb)) 加入套接字的出错信息包队列
kfree_skb(skb);
}
static inline int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
{
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK
*/
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
return -ENOMEM;
skb_set_owner_r(skb, sk);
skb_queue_tail(&sk->error_queue,skb);
if (!sk->dead)
sk->data_ready(sk,skb->len); 唤醒套接字等待队列
return 0;
}

/*
* Queue a received datagram if it will fit. Stream and sequenced
* protocols can't normally use this as they need to fit buffers in
* and play with them.
*
* Inlined as it's very short and called for pretty much every
* packet ever received.
*/

static inline void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
设置发送包的拥有套接字
{
sock_hold(sk);
skb->sk = sk;
skb->destructor = sock_wfree;
atomic_add(skb->truesize, &sk->wmem_alloc); 刷新发送内存的分配量
}
static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
设置接收包的拥有套接字
{
skb->sk = sk;
skb->destructor = sock_rfree;
atomic_add(skb->truesize, &sk->rmem_alloc); 刷新接收内存的分配量
}
/*
* Write buffer destructor automatically called from kfree_skb.
*/
void sock_wfree(struct sk_buff *skb)
{
struct sock *sk = skb->sk;

/* In case it might be waiting for more memory. */
atomic_sub(skb->truesize, &sk->wmem_alloc);
sk->write_space(sk); 唤醒正在等待写的进程
sock_put(sk);
}

/*
* Read buffer destructor automatically called from kfree_skb.
*/
void sock_rfree(struct sk_buff *skb)
{
struct sock *sk = skb->sk;

atomic_sub(skb->truesize, &sk->rmem_alloc);
}

static inline long sock_sndtimeo(struct sock *sk, int noblock) 取套接字的发送超时
{
return noblock ? 0 : sk->sndtimeo;
}
static inline int sock_error(struct sock *sk) 读取并且复位套接字错误号
{
int err=xchg(&sk->err,0);
return -err;
}

/*
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 rtable *rt) 取路由输出设备的地址
{
u32 src;
struct fib_result res;

if (rt->key.iif == 0)
src = rt->rt_src;
else if (fib_lookup(&rt->key, &res) == 0) {
#ifdef CONFIG_IP_ROUTE_NAT
if (res.type == RTN_NAT)
src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
else
#endif
src = FIB_RES_PREFSRC(res);
fib_res_put(&res);
} else
src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
memcpy(addr, &src, 4);
}

struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
unsigned long fallback, int noblock, int *errcode) 分配套接字的发送包
{
int err;
struct sk_buff *skb;
long timeo;

timeo = sock_sndtimeo(sk, noblock); 取套接字的发送超时

while (1) {
unsigned long try_size = size;

err = sock_error(sk); 读取并且复位套接字的错误码
if (err != 0)
goto failure;

/*
* We should send SIGPIPE in these cases according to
* 1003.1g draft 6.4. If we (the user) did a shutdown()
* call however we should not.
*
* Note: This routine isnt just used for datagrams and
* anyway some datagram protocols have a notion of
* close down.
*/

err = -EPIPE;
if (sk->shutdown&SEND_SHUTDOWN)
goto failure;

if (atomic_read(&sk->wmem_alloc) <>sndbuf) {
如果分配给套接字的发送内存小于其发送缓冲区
if (fallback) {
/* The buffer get won't block, or use the atomic queue.
* It does produce annoying no free page messages still.
*/
skb = alloc_skb(size, GFP_BUFFER);
if (skb)
break;
try_size = fallback;
}
skb = alloc_skb(try_size, sk->allocation);
if (skb)
break;
err = -ENOBUFS;
goto failure;
}

/*
* This means we have too many buffers for this socket already.
*/

set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
set_bit(SOCK_NOSPACE, &sk->socket->flags);
err = -EAGAIN;
if (!timeo)
goto failure;
if (signal_pending(current))
goto interrupted;
timeo = sock_wait_for_wmem(sk, timeo);
}

skb_set_owner_w(skb, sk);
return skb;

interrupted:
err = sock_intr_errno(timeo);
failure:
*errcode = err;
return NULL;
}
/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
I think, these locks should be removed for datagram sockets.
*/
static long sock_wait_for_wmem(struct sock * sk, long timeo)
等待套接字可分配的发送内存
{
DECLARE_WAITQUEUE(wait, current);

clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
add_wait_queue(sk->sleep, &wait);
for (;;) {
if (signal_pending(current))
break;
set_bit(SOCK_NOSPACE, &sk->socket->flags);
set_current_state(TASK_INTERRUPTIBLE);
if (atomic_read(&sk->wmem_alloc) <>sndbuf)
break;
if (sk->shutdown & SEND_SHUTDOWN)
break;
if (sk->err)
break;
timeo = schedule_timeout(timeo);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return timeo;
}


Edited by lucian_yao on 07/02/01 02:38
PM.

Linux的硬件地址解析过程 - collide的专栏

Linux的硬件地址解析过程

1) 在网络接口设备的硬件层之间能够直接进行包交换的设备构成了一个局域网,
局域网中的每一设备
具有唯一的硬件地址. 对TCPIP协议来说, 局域网中的每一设备又具有唯一的IP地址.
当IP包要从某一
设备发向局域网中具有另一IP地址的设备时, 信源设备必须获得信宿设备的硬件地址,
这就需要硬件
地址解析.arp协议是根据设备的IP地址获取其硬件地址的方法.
信源设备向局域网广播自已地址解析
请求, 局域网中其余设备都收到该请求, 具有所请求IP地址的设备向信源设备作出应答,
提供它的硬
件地址. 由于arp请求的广播特性, 某一设备可以对不是自已IP地址的请求作出应答,
这就是arp代理.

2) 在Linux内核中, 将能与自已在硬件层直接通信的外部主机的网络接口设备称为"邻居",
用neighbour
结构描述, 它包含设备的硬件地址信息.
系统中所有的IP包都通过路由所绑定的邻居发送到接口设备上.
邻居由邻居表(arp_tbl)来索引, 用邻居的IP地址可查询邻居表中某个设备的邻居.

3) 当邻居接收到要发送的IP包时, 如果邻居的硬件地址还未解析,
则将发送包暂时缓冲在arp_queue队
列中,然后发送地址解析请求, 这时的状态为未完成状态(NUD_INCOMPLETE).
如果1秒内没收到外部设备
应答, 邻居将重发arp请求, 如果重发达到3次, 则解析失败, 邻居为失败状态(NUD_FAILED).
当收到正
确应答, 邻居进入连接状态(NUD_REACHABLE),
这时arp_queue中发送包将被创建帧头后发送到设备上.

4) 邻居的IP地址与硬件地址的关系并不是固定的, 系统在接收来自邻居的IP包时,
会及时地证实(confirm)
邻居的IP地址与硬件地址的映射关系. 同时,
邻居表每60秒周期性地扫描邻居(neigh_periodic_timer),
一方面从表中清除那些解析失败和长时间(60秒)未被使用的邻居,
另一方面识别那些证实时间已超时的
邻居, 将它们从连接状态变为过期状态(NUD_STALE). 当邻居在过期状态发送IP包时,
就进入延迟状态
(NUD_DELAY), 如果在延迟状态5秒后邻居的硬件地址还是未被证实, 邻居则发送arp请求,
这时进入探测
状态(NUD_PROBE). 在探测状态, IP包并不进行缓冲, 仍旧使用过期的邻居地址发送,
如果探测失败,
邻居进入失败状态.

5) 为了缩短IP包到设备的传输路径, 在邻居结构上还引入了帧头缓冲结构(hh_cache).
如果邻居建立了
帧头缓冲, IP包将通过帧头缓冲的输出发送出去. 当邻居处于连接状态时, 帧头缓冲输出直接指向
dev_queue_xmit(), 当处于过期状态时, 帧头缓冲输出切换为邻居的输出口, 对以太网设备来说,
邻居的
输出口指向neigh_resolve_output(),
neigh_connect()和neigh_suspect()两个函数用来进行这种切换.

6) 当系统对外部设备arp的请求应答时, 系统将在arp_tbl中创建该外部设备的邻居,
刷新为过期状态.
当收到对其它设备的地址解析请求时, 系统将源设备的邻居刷新为过期状态. 当收到单发给自已,
但目
的IP地址不是自已主机地址的arp请求时, 如果设备允许转发并且该IP在代理表有定义,
则将它们缓冲到
proxy_queue队列, 等待一段随机的时间作出应答, 防止目标设备拥塞,
向对方提供的是自已的设备地址.

这一部分有点复杂, 还有很多细节尚未搞清, 只能写这么多, 欢迎大家指点.
现在自已的问题是: 在什么情况下必须使用arp代理?

; net/ipv4/ip_output.c:

static inline int ip_finish_output2(struct sk_buff *skb) IP包的发送出口
{
struct dst_entry *dst = skb->dst; 取IP包的路由结构
struct hh_cache *hh = dst->hh; 取路由的帧头缓冲

#ifdef CONFIG_NETFILTER_DEBUG
nf_debug_ip_finish_output2(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/

if (hh) { 如果路由帧头缓冲非空
read_lock_bh(&hh->hh_lock);
memcpy(skb->data - 16, hh->hh_data, 16); 创建IP包的硬件帧头
read_unlock_bh(&hh->hh_lock);
skb_push(skb, hh->hh_len);
return hh->hh_output(skb); 通过帧头缓冲发出
} else if (dst->neighbour)
return dst->neighbour->output(skb); 通过邻居出口发出

printk(KERN_DEBUG "khm\n");
kfree_skb(skb);
return -EINVAL;
}

; net/ipv4/arp.c, core/neighbour.c:

#define NUD_INCOMPLETE 0x01
#define NUD_REACHABLE 0x02
#define NUD_STALE 0x04
#define NUD_DELAY 0x08
#define NUD_PROBE 0x10
#define NUD_FAILED 0x20

/* Dummy states */
#define NUD_NOARP 0x40
#define NUD_PERMANENT 0x80
#define NUD_NONE 0x00

/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change
and make no address resolution or NUD.
NUD_PERMANENT is also cannot be deleted by garbage collectors.
*/
#define NUD_IN_TIMER (NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE)
#define
NUD_VALID (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE|NUD_PROBE|NUD_STALE|NUD_DELAY)
#define NUD_CONNECTED (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)

#define NEIGH_HASHMASK 0x1F
#define PNEIGH_HASHMASK 0xF

struct neigh_table 网络邻居表
{
struct neigh_table *next;
int family;
int entry_size;
int key_len;
__u32 (*hash)(const void *pkey, const struct net_device *);
int (*constructor)(struct neighbour *);
int (*pconstructor)(struct pneigh_entry *);
void (*pdestructor)(struct pneigh_entry *);
void (*proxy_redo)(struct sk_buff *skb);
char *id;
struct neigh_parms parms;
/* HACK. gc_* shoul follow parms without a gap! */
int gc_interval; (60秒)
int gc_thresh1; (128)
int gc_thresh2; (512)
int gc_thresh3; (1024)
unsigned long last_flush;
struct timer_list gc_timer;
struct timer_list proxy_timer;
struct sk_buff_head proxy_queue;
int entries;
rwlock_t lock;
unsigned long last_rand;
struct neigh_parms *parms_list;
kmem_cache_t *kmem_cachep;
struct tasklet_struct gc_task;
struct neigh_statistics stats;
struct neighbour *hash_buckets[NEIGH_HASHMASK+1];
struct pneigh_entry *phash_buckets[PNEIGH_HASHMASK+1];
};
struct neigh_parms 邻居参数
{
struct neigh_parms *next;
int (*neigh_setup)(struct neighbour *);
struct neigh_table *tbl;
int entries;
void *priv;

void *sysctl_table;

int base_reachable_time; (30秒)
int retrans_time; (1秒)
int gc_staletime; (60秒)
int reachable_time; (30秒左右)
int delay_probe_time; (5秒)

int queue_len; (3)
int ucast_probes; (3)
int app_probes; (0)
int mcast_probes; (3)
int anycast_delay; (1秒)
int proxy_delay; (0.8秒)
int proxy_qlen; (64)
int locktime; (1秒)
};
struct neighbour 网络邻居结构
{
struct neighbour *next;
struct neigh_table *tbl;
struct neigh_parms *parms;
struct net_device *dev;
unsigned long used;
unsigned long confirmed;
unsigned long updated;
__u8 flags;
__u8 nud_state;
__u8 type;
__u8 dead;
atomic_t probes;
rwlock_t lock;
unsigned char ha[(MAX_ADDR_LEN+sizeof(unsigned long)-1)&~(sizeof(unsigned long)-1)];
struct hh_cache *hh;
atomic_t refcnt;
int (*output)(struct sk_buff *skb);
struct sk_buff_head arp_queue;
struct timer_list timer;
struct neigh_ops *ops;
u8 primary_key[0];
};
struct hh_cache 帧头缓冲结构
{
struct hh_cache *hh_next; /* Next entry */
atomic_t hh_refcnt; /* number of users */
unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */
int hh_len; /* length of header */
int (*hh_output)(struct sk_buff *skb);
rwlock_t hh_lock;
/* cached hardware header; allow for machine alignment needs. */
unsigned long hh_data[16/sizeof(unsigned long)];
};


struct neigh_table arp_tbl = arp网络邻居表, 用于TCPIP协议中的网络邻居信息索引
{
NULL,
AF_INET,
sizeof(struct neighbour) + 4,
4,
arp_hash,
arp_constructor,
NULL,
NULL,
parp_redo,
"arp_cache",
{ NULL, NULL, &arp_tbl, 0, NULL, NULL,
30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ,
3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ },30*HZ, 128, 512, 1024,
};

void __init arp_init (void) 地址解析模块初始化
{
neigh_table_init(&arp_tbl); 初始化arp协议网络邻居解析表

dev_add_pack(&arp_packet_type); 注册地址解析包接收器

proc_net_create ("arp", 0, arp_get_info);

#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4, NET_IPV4_NEIGH, "ipv4");
#endif
}

static struct neigh_table *neigh_tables; 所有的网络邻居解析表链表

void neigh_table_init(struct neigh_table *tbl)
{
unsigned long now = jiffies;

tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time);
if (tbl->kmem_cachep == NULL)
tbl->kmem_cachep = kmem_cache_create(tbl->id,
(tbl->entry_size+15)&~15,
0, SLAB_HWCACHE_ALIGN,
NULL, NULL); 建立网络邻居信息结构内存分配器

#ifdef CONFIG_SMP
tasklet_init(&tbl->gc_task, SMP_TIMER_NAME(neigh_periodic_timer), (unsigned
long)tbl);
#endif
init_timer(&tbl->gc_timer);
tbl->lock = RW_LOCK_UNLOCKED;
tbl->gc_timer.data = (unsigned long)tbl;
tbl->gc_timer.function = neigh_periodic_timer;
tbl->gc_timer.expires = now + tbl->gc_interval + tbl->parms.reachable_time;
add_timer(&tbl->gc_timer);

init_timer(&tbl->proxy_timer);
tbl->proxy_timer.data = (unsigned long)tbl;
tbl->proxy_timer.function = neigh_proxy_process;
skb_queue_head_init(&tbl->proxy_queue);

tbl->last_flush = now;
tbl->last_rand = now + tbl->parms.reachable_time*20;
write_lock(&neigh_tbl_lock);
tbl->next = neigh_tables;
neigh_tables = tbl;
write_unlock(&neigh_tbl_lock);
}
unsigned long neigh_rand_reach_time(unsigned long base) 取在(base/2)左右的随机数
{
return (net_random() % base) + (base>>1);
}

static void SMP_TIMER_NAME(neigh_periodic_timer)(unsigned long arg)
{
struct neigh_table *tbl = (struct neigh_table*)arg;
unsigned long now = jiffies;
int i;


write_lock(&tbl->lock);

/*
* periodicly recompute ReachableTime from random function
*/

if (now - tbl->last_rand > 300*HZ) {
struct neigh_parms *p;
tbl->last_rand = now;
for (p=&tbl->parms; p; p = p->next)
p->reachable_time = neigh_rand_reach_time(p->base_reachable_time);
}

for (i=0; i <= NEIGH_HASHMASK; i++) { 扫描所有的网络邻居
struct neighbour *n, **np;

np = &tbl->hash_buckets
;
while ((n = *np) != NULL) {
unsigned state;

write_lock(&n->lock);

state = n->nud_state;
if (state&(NUD_PERMANENT|NUD_IN_TIMER)) { 对于不可释放或正在解析的邻居
write_unlock(&n->lock);
goto next_elt;
}

if ((long)(n->used - n->confirmed) < 0)
n->used = n->confirmed;

if (atomic_read(&n->refcnt) == 1 &&
(state == NUD_FAILED || now - n->used > n->parms->gc_staletime)) {
*np = n->next; 释放那些解析失败和引用时间超过(60)秒的邻居
n->dead = 1;
write_unlock(&n->lock);
neigh_release(n);
continue;
}

if (n->nud_state&NUD_REACHABLE &&
now - n->confirmed > n->parms->reachable_time) {
n->nud_state = NUD_STALE; 如果解析时间大于可达超时, 则邻居的地址失效
neigh_suspect(n);
}
write_unlock(&n->lock);

next_elt:
np = &n->next;
}
}

mod_timer(&tbl->gc_timer, now + tbl->gc_interval);
write_unlock(&tbl->lock);
}

int arp_bind_neighbour(struct dst_entry *dst) 将邻居绑定给指定路由
{
struct net_device *dev = dst->dev;
struct neighbour *n = dst->neighbour;

if (dev == NULL)
return -EINVAL;
if (n == NULL) {
u32 nexthop = ((struct rtable*)dst)->rt_gateway; 取网关地址
if (dev->flags&(IFF_LOOPBACK|IFF_POINTOPOINT))
nexthop = 0;
n = __neigh_lookup_errno(
#ifdef CONFIG_ATM_CLIP
dev->type == ARPHRD_ATM ? &clip_tbl :
#endif
&arp_tbl, &nexthop, dev); 在arp_tbl中查询目标地址nexthop的设备邻居
if (IS_ERR(n))
return PTR_ERR(n);
dst->neighbour = n;
}
return 0;
}
static inline struct neighbour *
__neigh_lookup_errno(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{
struct neighbour *n = neigh_lookup(tbl, pkey, dev);

if (n)
return n;

return neigh_create(tbl, pkey, dev); 在邻居表中创建新的设备邻居
}
struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{
struct neighbour *n;
u32 hash_val;
int key_len = tbl->key_len;

hash_val = tbl->hash(pkey, dev); 计算邻居的索引键

read_lock_bh(&tbl->lock);
for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
if (dev == n->dev &&
memcmp(n->primary_key, pkey, key_len) == 0) {
neigh_hold(n);
break;
}
}
read_unlock_bh(&tbl->lock);
return n;
}
static u32 arp_hash(const void *pkey, const struct net_device *dev)
{
u32 hash_val;

hash_val = *(u32*)pkey;
hash_val ^= (hash_val>>16);
hash_val ^= hash_val>>8;
hash_val ^= hash_val>>3;
hash_val = (hash_val^dev->ifindex)&NEIGH_HASHMASK;

return hash_val;
}
struct neighbour * neigh_create(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{
struct neighbour *n, *n1;
u32 hash_val;
int key_len = tbl->key_len;
int error;

n = neigh_alloc(tbl); 为邻居表tbl创建新的邻居
if (n == NULL)
return ERR_PTR(-ENOBUFS);

memcpy(n->primary_key, pkey, key_len); 拷贝邻居的键值
n->dev = dev;
dev_hold(dev);

/* Protocol specific setup. */
if (tbl->constructor && (error = tbl->constructor(n)) < 0) {
neigh_release(n);
return ERR_PTR(error);
}

/* Device specific setup. */
if (n->parms && n->parms->neigh_setup &&
(error = n->parms->neigh_setup(n)) < 0) {
neigh_release(n);
return ERR_PTR(error);
}

n->confirmed = jiffies - (n->parms->base_reachable_time<<1);

hash_val = tbl->hash(pkey, dev);

write_lock_bh(&tbl->lock);
for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
if (dev == n1->dev &&
memcmp(n1->primary_key, pkey, key_len) == 0) {
neigh_hold(n1); 如果邻居表中有重键,则释放新建邻居,返回已有的邻居
write_unlock_bh(&tbl->lock);
neigh_release(n);
return n1;
}
}

n->next = tbl->hash_buckets[hash_val];
tbl->hash_buckets[hash_val] = n; 插入索引链
n->dead = 0;
neigh_hold(n);
write_unlock_bh(&tbl->lock);
NEIGH_PRINTK2("neigh %p is created.\n", n);
return n;
}

static struct neighbour *neigh_alloc(struct neigh_table *tbl) 分配邻居结构
{
struct neighbour *n;
unsigned long now = jiffies;

if (tbl->entries > tbl->gc_thresh3 || 如果邻居表的单元数目超过了(1024)
(tbl->entries > tbl->gc_thresh2 && 或者超过了(512)并且
now - tbl->last_flush > 5*HZ)) { 与上次刷新间隔超过5秒
if (neigh_forced_gc(tbl) == 0 && 释放过时的邻居
tbl->entries > tbl->gc_thresh3)
return NULL;
}

n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
if (n == NULL)
return NULL;

memset(n, 0, tbl->entry_size);

skb_queue_head_init(&n->arp_queue); 当邻居的地址尚未解析时,
发向该邻居的包缓冲在该队列中
n->lock = RW_LOCK_UNLOCKED;
n->updated = n->used = now;
n->nud_state = NUD_NONE;
n->output = neigh_blackhole; 邻居的IP包注入口
n->parms = &tbl->parms; 继承邻居表的参数
init_timer(&n->timer);
n->timer.function = neigh_timer_handler; 接收超时临视器
n->timer.data = (unsigned long)n;
tbl->stats.allocs++;
neigh_glbl_allocs++;
tbl->entries++;
n->tbl = tbl;
atomic_set(&n->refcnt, 1);
n->dead = 1;
return n;
}
static int neigh_blackhole(struct sk_buff *skb)
{
kfree_skb(skb);
return -ENETDOWN;
}
static int neigh_forced_gc(struct neigh_table *tbl) 强制释放邻居
{
int shrunk = 0;
int i;

for (i=0; i<=NEIGH_HASHMASK; i++) {
struct neighbour *n, **np;

np = &tbl->hash_buckets
;
write_lock_bh(&tbl->lock);
while ((n = *np) != NULL) {
/* Neighbour record may be discarded if:
- nobody refers to it.
- it is not premanent
- (NEW and probably wrong)
INCOMPLETE entries are kept at least for
n->parms->retrans_time, otherwise we could
flood network with resolution requests.
It is not clear, what is better table overflow
or flooding.
*/
write_lock(&n->lock);
if (atomic_read(&n->refcnt) == 1 &&
!(n->nud_state&NUD_PERMANENT) &&
(n->nud_state != NUD_INCOMPLETE ||
jiffies - n->used > n->parms->retrans_time)) {
*np = n->next;
n->dead = 1;
shrunk = 1;
write_unlock(&n->lock);
neigh_release(n);
continue;
}
write_unlock(&n->lock);
np = &n->next;
}
write_unlock_bh(&tbl->lock);
}

tbl->last_flush = jiffies;
return shrunk;
}
static inline void neigh_release(struct neighbour *neigh)
{
if (atomic_dec_and_test(&neigh->refcnt))
neigh_destroy(neigh);
}
static int neigh_del_timer(struct neighbour *n)
{
if (n->nud_state & NUD_IN_TIMER) {
if (del_timer(&n->timer)) {
neigh_release(n);
return 1;
}
}
return 0;
}
void neigh_destroy(struct neighbour *neigh)
{
struct hh_cache *hh;

if (!neigh->dead) {
printk("Destroying alive neighbour %p from %08lx\n", neigh,
*(((unsigned long*)&neigh)-1));
return;
}

if (neigh_del_timer(neigh))
printk("Impossible event.\n");

while ((hh = neigh->hh) != NULL) {
neigh->hh = hh->hh_next;
hh->hh_next = NULL;
write_lock_bh(&hh->hh_lock);
hh->hh_output = neigh_blackhole;
write_unlock_bh(&hh->hh_lock);
if (atomic_dec_and_test(&hh->hh_refcnt))
kfree(hh);
} 清除网络邻居的帧头缓冲

if (neigh->ops && neigh->ops->destructor)
(neigh->ops->destructor)(neigh);

skb_queue_purge(&neigh->arp_queue); 清除邻居的IP包缓冲队列

dev_put(neigh->dev);

NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);

neigh_glbl_allocs--;
neigh->tbl->entries--;
kmem_cache_free(neigh->tbl->kmem_cachep, neigh);
}

/* As fast as possible without hh cache */

static __inline__ int neigh_max_probes(struct neighbour *n)
{
struct neigh_parms *p = n->parms;
return p->ucast_probes + p->app_probes + p->mcast_probes;
}

static void neigh_timer_handler(unsigned long arg)
{
unsigned long now = jiffies;
struct neighbour *neigh = (struct neighbour*)arg;
unsigned state;
int notify = 0;

write_lock(&neigh->lock);

state = neigh->nud_state;

if (!(state&NUD_IN_TIMER)) {
#ifndef CONFIG_SMP
printk("neigh: timer & !nud_in_timer\n");
#endif
goto out;
}

if ((state&NUD_VALID) &&
now - neigh->confirmed <>parms->reachable_time) {
neigh->nud_state = NUD_REACHABLE;
NEIGH_PRINTK2("neigh %p is still alive.\n", neigh);
neigh_connect(neigh);
goto out;
}
if (state == NUD_DELAY) {
NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
neigh->nud_state = NUD_PROBE;
atomic_set(&neigh->probes, 0);
}

if (atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
struct sk_buff *skb;

neigh->nud_state = NUD_FAILED;
notify = 1;
neigh->tbl->stats.res_failed++;
NEIGH_PRINTK2("neigh %p is failed.\n", neigh);

/* It is very thin place. report_unreachable is very complicated
routine. Particularly, it can hit the same neighbour entry!

So that, we try to be accurate and avoid dead loop. --ANK
*/
while(neigh->nud_state==NUD_FAILED && (skb=__skb_dequeue(&neigh->arp_queue)) !=
NULL) {
write_unlock(&neigh->lock);
neigh->ops->error_report(neigh, skb);
write_lock(&neigh->lock);
}
skb_queue_purge(&neigh->arp_queue);
goto out;
}

neigh->timer.expires = now + neigh->parms->retrans_time;
add_timer(&neigh->timer);
write_unlock(&neigh->lock);

neigh->ops->solicit(neigh, skb_peek(&neigh->arp_queue)); 重发arp请求
atomic_inc(&neigh->probes);
return;

out:
write_unlock(&neigh->lock);
#ifdef CONFIG_ARPD
if (notify && neigh->parms->app_probes)
neigh_app_notify(neigh);
#endif
neigh_release(neigh);
}
static int arp_constructor(struct neighbour *neigh)
{
u32 addr = *(u32*)neigh->primary_key; 主键为邻居的IP地址
struct net_device *dev = neigh->dev; 取邻居所在的主机设备
struct in_device *in_dev = in_dev_get(dev);

if (in_dev == NULL)
return -EINVAL;

neigh->type = inet_addr_type(addr); 取邻居的IP地址类型
if (in_dev->arp_parms)
neigh->parms = in_dev->arp_parms; 继承设备的arp参数

in_dev_put(in_dev);

if (dev->hard_header == NULL) {
neigh->nud_state = NUD_NOARP;
neigh->ops = &arp_direct_ops; 安装直达操作表
neigh->output = neigh->ops->queue_xmit; 向邻居的输出函数为(dev_queue_xmit)
} else {
/* Good devices (checked by reading texts, but only Ethernet is
tested)

ARPHRD_ETHER: (ethernet, apfddi)
ARPHRD_FDDI: (fddi)
ARPHRD_IEEE802: (tr)
ARPHRD_METRICOM: (strip)
ARPHRD_ARCNET:
etc. etc. etc.

ARPHRD_IPDDP will also work, if author repairs it.
I did not it, because this driver does not work even
in old paradigm.
*/

#if 1
/* So... these "amateur" devices are hopeless.
The only thing, that I can say now:
It is very sad that we need to keep ugly obsolete
code to make them happy.

They should be moved to more reasonable state, now
they use rebuild_header INSTEAD OF hard_start_xmit!!!
Besides that, they are sort of out of date
(a lot of redundant clones/copies, useless in 2.1),
I wonder why people believe that they work.
*/
switch (dev->type) {
default:
break;
case ARPHRD_ROSE:
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25:
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM:
#endif
neigh->ops = &arp_broken_ops;
neigh->output = neigh->ops->output;
return 0;
#endif
;}
#endif
if (neigh->type == RTN_MULTICAST) {
neigh->nud_state = NUD_NOARP;
arp_mc_map(addr, neigh->ha, dev, 1);
} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
neigh->nud_state = NUD_NOARP;
memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
} else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) {
neigh->nud_state = NUD_NOARP;
memcpy(neigh->ha, dev->broadcast, dev->addr_len);
}
if (dev->hard_header_cache)
neigh->ops = &arp_hh_ops; 安装邻居的帧头缓冲操作
else
neigh->ops = &arp_generic_ops;
if (neigh->nud_state&NUD_VALID) 地址是否已解析
neigh->output = neigh->ops->connected_output; 直接发送
else
neigh->output = neigh->ops->output; 先解析再发送
}
return 0;
}

struct neigh_ops
{
int family;
void (*destructor)(struct neighbour *);
void (*solicit)(struct neighbour *, struct sk_buff*);
void (*error_report)(struct neighbour *, struct sk_buff*);
int (*output)(struct sk_buff*);
int (*connected_output)(struct sk_buff*);
int (*hh_output)(struct sk_buff*);
int (*queue_xmit)(struct sk_buff*);
};
static struct neigh_ops arp_direct_ops = 无解析发送
{
AF_INET,
NULL,
NULL,
NULL,
dev_queue_xmit,
dev_queue_xmit,
dev_queue_xmit,
dev_queue_xmit
};
struct neigh_ops arp_broken_ops = 不完全解析
{
AF_INET,
NULL,
arp_solicit,
arp_error_report,
neigh_compat_output,
neigh_compat_output,
dev_queue_xmit,
dev_queue_xmit,
};
int neigh_compat_output(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;

__skb_pull(skb, skb->nh.raw - skb->data);

if (dev->hard_header &&
dev->hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len) < 0 &&
dev->rebuild_header(skb))
return 0;

return dev_queue_xmit(skb);
}
static struct neigh_ops arp_hh_ops = 通帧头缓冲解析
{
AF_INET,
NULL,
arp_solicit,
arp_error_report,
neigh_resolve_output,
neigh_resolve_output,
dev_queue_xmit,
dev_queue_xmit
};
int neigh_resolve_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct neighbour *neigh;

if (!dst || !(neigh = dst->neighbour))
goto discard;

__skb_pull(skb, skb->nh.raw - skb->data);

if (neigh_event_send(neigh, skb) == 0) {
int err;
struct net_device *dev = neigh->dev;
if (dev->hard_header_cache && dst->hh == NULL) {
write_lock_bh(&neigh->lock);
if (dst->hh == NULL)
neigh_hh_init(neigh, dst, dst->ops->protocol);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
write_unlock_bh(&neigh->lock);
} else {
read_lock_bh(&neigh->lock);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);
}
if (err >= 0)
return neigh->ops->queue_xmit(skb);
kfree_skb(skb);
return -EINVAL;
}
return 0;

discard:
NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour :
NULL);
kfree_skb(skb);
return -EINVAL;
}
static void neigh_hh_init(struct neighbour *n, struct dst_entry *dst, u16 protocol)
{
struct hh_cache *hh = NULL;
struct net_device *dev = dst->dev;

for (hh=n->hh; hh; hh = hh->hh_next)
if (hh->hh_type == protocol)
break;

if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
memset(hh, 0, sizeof(struct hh_cache));
hh->hh_lock = RW_LOCK_UNLOCKED;
hh->hh_type = protocol;
atomic_set(&hh->hh_refcnt, 0);
hh->hh_next = NULL;
if (dev->hard_header_cache(n, hh)) {
kfree(hh);
hh = NULL;
} else {
atomic_inc(&hh->hh_refcnt);
hh->hh_next = n->hh;
n->hh = hh;
if (n->nud_state&NUD_CONNECTED) 如果设备地址有效
hh->hh_output = n->ops->hh_output; 继承邻居的帧头缓冲输出
else
hh->hh_output = n->ops->output; 继承邻居的输出
}
}
if (hh) {
atomic_inc(&hh->hh_refcnt);
dst->hh = hh;
}
}

static struct neigh_ops arp_generic_ops =
{
AF_INET,
NULL,
arp_solicit,
arp_error_report,
neigh_resolve_output,
neigh_connected_output,
dev_queue_xmit,
dev_queue_xmit
};
int neigh_connected_output(struct sk_buff *skb)
{
int err;
struct dst_entry *dst = skb->dst;
struct neighbour *neigh = dst->neighbour;
struct net_device *dev = neigh->dev;

__skb_pull(skb, skb->nh.raw - skb->data);

read_lock_bh(&neigh->lock);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len);
read_unlock_bh(&neigh->lock);
if (err >= 0)
return neigh->ops->queue_xmit(skb);
kfree_skb(skb);
return -EINVAL;
}

static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
neigh->used = jiffies;
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
return __neigh_event_send(neigh, skb);
return 0;
}
int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
write_lock_bh(&neigh->lock);
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) {
if (!(neigh->nud_state&(NUD_STALE|NUD_INCOMPLETE))) {
if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
atomic_set(&neigh->probes, neigh->parms->ucast_probes);
neigh->nud_state = NUD_INCOMPLETE;
neigh_hold(neigh);
neigh->timer.expires = jiffies + neigh->parms->retrans_time; 下一重发时间
add_timer(&neigh->timer);
write_unlock_bh(&neigh->lock);
neigh->ops->solicit(neigh, skb);
atomic_inc(&neigh->probes);
write_lock_bh(&neigh->lock);
} else {
neigh->nud_state = NUD_FAILED;
write_unlock_bh(&neigh->lock);

if (skb)
kfree_skb(skb);
return 1;
}
}
if (neigh->nud_state == NUD_INCOMPLETE) {
if (skb) {
if (skb_queue_len(&neigh->arp_queue) >= neigh->parms->queue_len) {
struct sk_buff *buff;
buff = neigh->arp_queue.prev;
__skb_unlink(buff, &neigh->arp_queue);
kfree_skb(buff);
}
__skb_queue_head(&neigh->arp_queue, skb);
}
write_unlock_bh(&neigh->lock);
return 1;
}
if (neigh->nud_state == NUD_STALE) {
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh);
neigh->nud_state = NUD_DELAY;
neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;
add_timer(&neigh->timer);
}
}
write_unlock_bh(&neigh->lock);
return 0;
}
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) arp请求
{
u32 saddr;
u8 *dst_ha = NULL;
struct net_device *dev = neigh->dev;
u32 target = *(u32*)neigh->primary_key; 取邻居的IP地址
int probes = atomic_read(&neigh->probes);

if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
saddr = skb->nh.iph->saddr;
else
saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);

if ((probes -= neigh->parms->ucast_probes) < 0) {
if (!(neigh->nud_state&NUD_VALID))
printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n");
dst_ha = neigh->ha;
read_lock_bh(&neigh->lock);
} else if ((probes -= neigh->parms->app_probes) < 0) {
#ifdef CONFIG_ARPD
neigh_app_ns(neigh);
#endif
return;
}

arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
dst_ha, dev->dev_addr, NULL);
if (dst_ha)
read_unlock_bh(&neigh->lock);
}
struct arphdr
{
unsigned short ar_hrd; 硬件地址格式
unsigned short ar_pro; 协议地址格式
unsigned char ar_hln; 硬件地址长度
unsigned char ar_pln; 协议地址长度
unsigned short ar_op; 命令代码

#if 0
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
unsigned char ar_sip[4]; /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
unsigned char ar_tip[4]; /* target IP address */
#endif

};
void arp_send(int type, int ptype, u32 dest_ip,
struct net_device *dev, u32 src_ip,
unsigned char *dest_hw, unsigned char *src_hw,
unsigned char *target_hw)
{
struct sk_buff *skb;
struct arphdr *arp;
unsigned char *arp_ptr;

/*
* No arp on this interface.
*/

if (dev->flags&IFF_NOARP)
return;

/*
* Allocate a buffer
*/

skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+ dev->hard_header_len + 15, GFP_ATOMIC); 创建地址解析包缓冲区
if (skb == NULL)
return;

skb_reserve(skb, (dev->hard_header_len+15)&~15); 保留硬件帧头区域,16字节对齐
skb->nh.raw = skb->data; 设置包头指针
arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
; 分配地址解析包的数据区域
skb->dev = dev; 包的发送设备
skb->protocol = __constant_htons (ETH_P_ARP); 设置包的类型
if (src_hw == NULL)
src_hw = dev->dev_addr; 如果信源硬件地址为空, 取设备的硬件地址
if (dest_hw == NULL)
dest_hw = dev->broadcast; 如果信宿硬件地址为空, 取设备的广播地址

/*
* Fill the device header for the ARP frame
*/
if (dev->hard_header &&
dev->hard_header(skb,dev,ptype,dest_hw,src_hw,skb->len) < 0) 装配包的硬件帧头
goto out;

/*
* Fill out the arp protocol part.
*
* The arp hardware type should match the device type, except for FDDI,
* which (according to RFC 1390) should always equal 1 (Ethernet).
*/
/*
* Exceptions everywhere. AX.25 uses the AX.25 PID value not the
* DIX code for the protocol. Make these device structure fields.
*/
switch (dev->type) {
default:
arp->ar_hrd = htons(dev->type); 取设备类型作为地址解析包的地址类型
arp->ar_pro = __constant_htons(ETH_P_IP);
break;

#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25:
arp->ar_hrd = __constant_htons(ARPHRD_AX25);
arp->ar_pro = __constant_htons(AX25_P_IP);
break;

#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM:
arp->ar_hrd = __constant_htons(ARPHRD_NETROM);
arp->ar_pro = __constant_htons(AX25_P_IP);
break;
#endif
#endif

#ifdef CONFIG_FDDI
case ARPHRD_FDDI:
arp->ar_hrd = __constant_htons(ARPHRD_ETHER);
arp->ar_pro = __constant_htons(ETH_P_IP);
break;
#endif
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR:
arp->ar_hrd = __constant_htons(ARPHRD_IEEE802);
arp->ar_pro = __constant_htons(ETH_P_IP);
break;
#endif
}

arp->ar_hln = dev->addr_len; 设置硬件地址长度
arp->ar_pln = 4; 设置协议地址长度
arp->ar_op = htons(type); 设置地址解析的操作码

arp_ptr=(unsigned char *)(arp+1); 指向包的参数区

memcpy(arp_ptr, src_hw, dev->addr_len); 设置源硬件地址
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &src_ip,4); 设置源IP地址
arp_ptr+=4;
if (target_hw != NULL)
memcpy(arp_ptr, target_hw, dev->addr_len);
else
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4); 设置目的IP地址
skb->dev = dev;

dev_queue_xmit(skb);
return;

out:
kfree_skb(skb);
}
static struct packet_type arp_packet_type =
{
__constant_htons(ETH_P_ARP),
NULL, /* All devices */
arp_rcv,
(void*)1,
NULL
};
static inline int skb_shared(struct sk_buff *skb)
{
return (atomic_read(&skb->users) != 1);
}
static inline struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
{
if (skb_shared(skb)) {
struct sk_buff *nskb;
nskb = skb_clone(skb, pri);
kfree_skb(skb);
return nskb;
}
return skb;
}

#define IN_DEV_PROXY_ARP(in_dev) (ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp)
#define IN_DEV_FORWARD(in_dev) ((in_dev)->cnf.forwarding)

int arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
struct arphdr *arp = skb->nh.arph;
unsigned char *arp_ptr= (unsigned char *)(arp+1);
struct rtable *rt;
unsigned char *sha, *tha;
u32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
struct in_device *in_dev = in_dev_get(dev);
struct neighbour *n;

/*
* The hardware length of the packet should match the hardware length
* of the device. Similarly, the hardware types should match. The
* device should be ARP-able. Also, if pln is not 4, then the lookup
* is not from an IP number. We can't currently handle this, so toss
* it.
*/
if (in_dev == NULL || 如果设备还没打开
arp->ar_hln != dev->addr_len || 如果硬件地址长度不匹配
dev->flags & IFF_NOARP || 如果设备无需arp
skb->pkt_type == PACKET_OTHERHOST || 如果是属于外部任备的包
skb->pkt_type == PACKET_LOOPBACK || 如果是回送包
arp->ar_pln != 4) 如果IP地址长度不等于4
goto out;

if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
goto out_of_mem;

switch (dev_type) {
default:
if (arp->ar_pro != __constant_htons(ETH_P_IP))
goto out;
if (htons(dev_type) != arp->ar_hrd)
goto out;
break;
#ifdef CONFIG_NET_ETHERNET
case ARPHRD_ETHER:
/*
* ETHERNET devices will accept ARP hardware types of either
* 1 (Ethernet) or 6 (IEEE 802.2).
*/
if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
goto out;
if (arp->ar_pro != __constant_htons(ETH_P_IP))
goto out;
break;
#endif
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR:
/*
* Token ring devices will accept ARP hardware types of either
* 1 (Ethernet) or 6 (IEEE 802.2).
*/
if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
goto out;
if (arp->ar_pro != __constant_htons(ETH_P_IP))
goto out;
break;
#endif
#ifdef CONFIG_FDDI
case ARPHRD_FDDI:
/*
* According to RFC 1390, FDDI devices should accept ARP hardware types
* of 1 (Ethernet). However, to be more robust, we'll accept hardware
* types of either 1 (Ethernet) or 6 (IEEE 802.2).
*/
if (arp->ar_hrd != __constant_htons(ARPHRD_ETHER) &&
arp->ar_hrd != __constant_htons(ARPHRD_IEEE802))
goto out;
if (arp->ar_pro != __constant_htons(ETH_P_IP))
goto out;
break;
#endif
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
case ARPHRD_AX25:
if (arp->ar_pro != __constant_htons(AX25_P_IP))
goto out;
if (arp->ar_hrd != __constant_htons(ARPHRD_AX25))
goto out;
break;
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
case ARPHRD_NETROM:
if (arp->ar_pro != __constant_htons(AX25_P_IP))
goto out;
if (arp->ar_hrd != __constant_htons(ARPHRD_NETROM))
goto out;
break;
#endif
#endif
}

/* Understand only these message types */

if (arp->ar_op != __constant_htons(ARPOP_REPLY) &&
arp->ar_op != __constant_htons(ARPOP_REQUEST))
goto out; 只处理"应答"和"请求"两种操作码

/*
* Extract fields
*/
sha=arp_ptr; 指向参数区源硬件地址
arp_ptr += dev->addr_len;
memcpy(&sip, arp_ptr, 4); 发送者IP地址
arp_ptr += 4;
tha=arp_ptr; 所(要)解析的硬件地址
arp_ptr += dev->addr_len;
memcpy(&tip, arp_ptr, 4); 取所(要)解析的IP地址
/*
* Check for bad requests for 127.x.x.x and requests for multicast
* addresses. If this is one such, delete it.
*/
if (LOOPBACK(tip) || MULTICAST(tip))
goto out;

/*
* Process entry. The idea here is we want to send a reply if it is a
* request for us or if it is a request for someone else that we hold
* a proxy for. We want to add an entry to our cache if it is a reply
* to us or if it is a request for our address.
* (The assumption for this last is that if someone is requesting our
* address, they are probably intending to talk to us, so it saves time
* if we cache their address. Their address is also probably not in
* our cache, since ours is not in their cache.)
*
* Putting this another way, we only care about replies if they are to
* us, in which case we add them to the cache. For requests, we care
* about those for us and those for our proxies. We reply to both,
* and in the case of requests for us we add the requester to the arp
* cache.
*/

/* Special case: IPv4 duplicate address detection packet (RFC2131) */
if (sip == 0) { 如果发送者IP地址为零
if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
inet_addr_type(tip) == RTN_LOCAL) 如果所请要解析的IP地址是主机的IP地址
arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
goto out;
}

if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
ip_route_input(skb, tip, sip, 0, dev) == 0) { 查询目的地址在输入设备dev上的路由

rt = (struct rtable*)skb->dst;
addr_type = rt->rt_type;

if (addr_type == RTN_LOCAL) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev); 将请求设备的邻居刷新为"过期"状态
if (n) {
arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
neigh_release(n);
}
goto out;
} else if (IN_DEV_FORWARD(in_dev)) {
if ((rt->rt_flags&RTCF_DNAT) ||
(addr_type == RTN_UNICAST && rt->u.dst.dev != dev &&
(IN_DEV_PROXY_ARP(in_dev) || pneigh_lookup(&arp_tbl, &tip, dev, 0)))) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n)
neigh_release(n);

if (skb->stamp.tv_sec == 0 || 如果是proxy_queue的redo包
skb->pkt_type == PACKET_HOST || 如果是发向自已的包
in_dev->arp_parms->proxy_delay == 0) {
arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
} else {
pneigh_enqueue(&arp_tbl, in_dev->arp_parms, skb); 缓冲请求包,作延迟处理
in_dev_put(in_dev);
return 0;
}
goto out;
}
}
}
; 收到应答包或非已的请求包
/* Update our ARP tables */

n = __neigh_lookup(&arp_tbl, &sip, dev, 0); 寻找发送者IP的邻居

#ifdef CONFIG_IP_ACCEPT_UNSOLICITED_ARP
/* Unsolicited ARP is not accepted by default.
It is possible, that this option should be enabled for some
devices (strip is candidate)
*/
if (n == NULL &&
arp->ar_op == __constant_htons(ARPOP_REPLY) &&
inet_addr_type(sip) == RTN_UNICAST)
n = __neigh_lookup(&arp_tbl, &sip, dev, -1);
#endif

if (n) {
int state = NUD_REACHABLE;
int override = 0;

/* If several different ARP replies follows back-to-back,
use the FIRST one. It is possible, if several proxy
agents are active. Taking the first reply prevents
arp trashing and chooses the fastest router.
*/
if (jiffies - n->updated >= n->parms->locktime)
override = 1;

/* Broadcast replies and request packets
do not assert neighbour reachability.
*/
if (arp->ar_op != __constant_htons(ARPOP_REPLY) ||
skb->pkt_type != PACKET_HOST)
state = NUD_STALE;
neigh_update(n, sha, state, override, 1);
neigh_release(n);
}

out:
kfree_skb(skb);
if (in_dev)
in_dev_put(in_dev);
out_of_mem:
return 0;
}
struct neighbour * neigh_event_ns(struct neigh_table *tbl,
u8 *lladdr, void *saddr,
struct net_device *dev) 将邻居刷新为"过期"状态
{
struct neighbour *neigh;

neigh = __neigh_lookup(tbl, saddr, dev, lladdr || !dev->addr_len);
if (neigh)
neigh_update(neigh, lladdr, NUD_STALE, 1, 1);
return neigh;
}
static inline struct neighbour *
__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int
creat)
{
struct neighbour *n = neigh_lookup(tbl, pkey, dev);

if (n || !creat)
return n;

n = neigh_create(tbl, pkey, dev);
return IS_ERR(n) ? NULL : n;
}
int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, int override, int
arp)
{
u8 old;
int err;
int notify = 0;
struct net_device *dev = neigh->dev;

write_lock_bh(&neigh->lock);
old = neigh->nud_state;

err = -EPERM;
if (arp && (old&(NUD_NOARP|NUD_PERMANENT)))
goto out;

if (!(new&NUD_VALID)) {
neigh_del_timer(neigh);
if (old&NUD_CONNECTED)
neigh_suspect(neigh);
neigh->nud_state = new;
err = 0;
notify = old&NUD_VALID;
goto out;
}

/* Compare new lladdr with cached one */
if (dev->addr_len == 0) {
/* First case: device needs no address. */
lladdr = neigh->ha;
} else if (lladdr) {
/* The second case: if something is already cached
and a new address is proposed:
- compare new & old
- if they are different, check override flag
*/
if (old&NUD_VALID) {
if (memcmp(lladdr, neigh->ha, dev->addr_len) == 0)
lladdr = neigh->ha;
else if (!override)
goto out;
}
} else {
/* No address is supplied; if we know something,
use it, otherwise discard the request.
*/
err = -EINVAL;
if (!(old&NUD_VALID))
goto out;
lladdr = neigh->ha;
}

neigh_sync(neigh);
old = neigh->nud_state;
if (new&NUD_CONNECTED)
neigh->confirmed = jiffies;
neigh->updated = jiffies;

/* If entry was valid and address is not changed,
do not change entry state, if new one is STALE.
*/
err = 0;
if (old&NUD_VALID) {
if (lladdr == neigh->ha)
if (new == old || (new == NUD_STALE && (old&NUD_CONNECTED)))
goto out;
}
neigh_del_timer(neigh);
neigh->nud_state = new;
if (lladdr != neigh->ha) {
memcpy(&neigh->ha, lladdr, dev->addr_len);
neigh_update_hhs(neigh);
if (!(new&NUD_CONNECTED))
neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
#ifdef CONFIG_ARPD
notify = 1;
#endif
}
if (new == old)
goto out;
if (new&NUD_CONNECTED)
neigh_connect(neigh);
else
neigh_suspect(neigh);
if (!(old&NUD_VALID)) {
struct sk_buff *skb;

/* Again: avoid dead loop if something went wrong */

while (neigh->nud_state&NUD_VALID &&
(skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
struct neighbour *n1 = neigh;
write_unlock_bh(&neigh->lock);
/* On shaper/eql skb->dst->neighbour != neigh :( */
if (skb->dst && skb->dst->neighbour)
n1 = skb->dst->neighbour;
n1->output(skb);
write_lock_bh(&neigh->lock);
}
skb_queue_purge(&neigh->arp_queue);
}
out:
write_unlock_bh(&neigh->lock);
#ifdef CONFIG_ARPD
if (notify && neigh->parms->app_probes)
neigh_app_notify(neigh);
#endif
return err;
}
static void neigh_sync(struct neighbour *n)
{
unsigned long now = jiffies;
u8 state = n->nud_state;

ASSERT_WL(n);
if (state&(NUD_NOARP|NUD_PERMANENT))
return;
if (state&NUD_REACHABLE) {
if (now - n->confirmed > n->parms->reachable_time) {
n->nud_state = NUD_STALE;
neigh_suspect(n);
}
} else if (state&NUD_VALID) {
if (now - n->confirmed <>parms->reachable_time) {
neigh_del_timer(n);
n->nud_state = NUD_REACHABLE;
neigh_connect(n);
}
}
}
static void neigh_connect(struct neighbour *neigh)
{
struct hh_cache *hh;

NEIGH_PRINTK2("neigh %p is connected.\n", neigh);

ASSERT_WL(neigh);

neigh->output = neigh->ops->connected_output;

for (hh = neigh->hh; hh; hh = hh->hh_next)
hh->hh_output = neigh->ops->hh_output;
}
static void neigh_suspect(struct neighbour *neigh)
{
struct hh_cache *hh;

NEIGH_PRINTK2("neigh %p is suspecteded.\n", neigh);

ASSERT_WL(neigh);

neigh->output = neigh->ops->output;

for (hh = neigh->hh; hh; hh = hh->hh_next)
hh->hh_output = neigh->ops->output;
}
static __inline__ void neigh_update_hhs(struct neighbour *neigh)
{
struct hh_cache *hh;
void (*update)(struct hh_cache*, struct net_device*, unsigned char*) =
neigh->dev->header_cache_update;

if (update) {
for (hh=neigh->hh; hh; hh=hh->hh_next) {
write_lock_bh(&hh->hh_lock);
update(hh, neigh->dev, neigh->ha);
write_unlock_bh(&hh->hh_lock);
}
}
}

struct pneigh_entry
{
struct pneigh_entry *next;
struct net_device *dev;
u8 key[0];
};
struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev, int creat)
{
struct pneigh_entry *n;
u32 hash_val;
int key_len = tbl->key_len;

hash_val = *(u32*)(pkey + key_len - 4);
hash_val ^= (hash_val>>16);
hash_val ^= hash_val>>8;
hash_val ^= hash_val>>4;
hash_val &= PNEIGH_HASHMASK;

read_lock_bh(&tbl->lock);

for (n = tbl->phash_buckets[hash_val]; n; n = n->next) {
if (memcmp(n->key, pkey, key_len) == 0 &&
(n->dev == dev || !n->dev)) {
read_unlock_bh(&tbl->lock);
return n;
}
}
read_unlock_bh(&tbl->lock);
if (!creat)
return NULL;

n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL);
if (n == NULL)
return NULL;

memcpy(n->key, pkey, key_len);
n->dev = dev;

if (tbl->pconstructor && tbl->pconstructor(n)) {
kfree(n);
return NULL;
}

write_lock_bh(&tbl->lock);
n->next = tbl->phash_buckets[hash_val];
tbl->phash_buckets[hash_val] = n;
write_unlock_bh(&tbl->lock);
return n;
}
void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
struct sk_buff *skb) 缓冲外部设备的arp请求包
{
unsigned long now = jiffies;
long sched_next = net_random()%p->proxy_delay; 下一延迟间隔(0.8秒以内)

if (tbl->proxy_queue.qlen > p->proxy_qlen) {
kfree_skb(skb);
return;
}
skb->stamp.tv_sec = 0;
skb->stamp.tv_usec = now + sched_next;

spin_lock(&tbl->proxy_queue.lock);
if (del_timer(&tbl->proxy_timer)) {
long tval = tbl->proxy_timer.expires - now; 取定时器剩余时间
if (tval < sched_next)
sched_next = tval; 取大者
}
dst_release(skb->dst);
skb->dst = NULL;
dev_hold(skb->dev);
__skb_queue_tail(&tbl->proxy_queue, skb);
mod_timer(&tbl->proxy_timer, now + sched_next);
spin_unlock(&tbl->proxy_queue.lock);
}
static void neigh_proxy_process(unsigned long arg)
{
struct neigh_table *tbl = (struct neigh_table *)arg;
long sched_next = 0;
unsigned long now = jiffies;
struct sk_buff *skb;

spin_lock(&tbl->proxy_queue.lock);

skb = tbl->proxy_queue.next;

while (skb != (struct sk_buff*)&tbl->proxy_queue) {
struct sk_buff *back = skb;
long tdif = back->stamp.tv_usec - now;

skb = skb->next;
if (tdif <= 0) {
struct net_device *dev = back->dev;
__skb_unlink(back, &tbl->proxy_queue);
if (tbl->proxy_redo && netif_running(dev))
tbl->proxy_redo(back);
else
kfree_skb(back);

dev_put(dev);
} else if (!sched_next || tdif < sched_next)
sched_next = tdif;
}
del_timer(&tbl->proxy_timer);
if (sched_next)
mod_timer(&tbl->proxy_timer, jiffies + sched_next);
spin_unlock(&tbl->proxy_queue.lock);
}
static void parp_redo(struct sk_buff *skb)
{
arp_rcv(skb, skb->dev, NULL);
}
static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
{
dst_link_failure(skb);
kfree_skb(skb);
}
static inline void dst_link_failure(struct sk_buff *skb)
{
struct dst_entry * dst = skb->dst;
if (dst && dst->ops && dst->ops->link_failure)
dst->ops->link_failure(skb);
}


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=117148

如何发掘出更多退休的钱?

如何发掘出更多退休的钱? http://bbs.wenxuecity.com/bbs/tzlc/1328415.html 按照常规的说法,退休的收入必须得有退休前的80%,或者是4% withdrawal rule,而且每年还得要加2-3%对付通胀,这是一个很大...