作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
- int ip_local_out(struct sk_buff *skb)
-
{
-
int err;
- /*
- 调用netfilter的hook检查该包是否可以发送。
- */
-
err = __ip_local_out(skb);
- /*
- 当err为1时,表明netfilter的hook函数告诉调用者允许包通过,并且需要调用者明确的调用hook函数的参数中 的回调函数。在此,需要调用dst_output来真正发送数据包
- */
-
if (likely(err == 1))
-
err = dst_output(skb);
-
-
return err;
- }
刚看到这里时,看到err很自然的想到err是错误值,尤其是linux中大部分的成功返回都是0。
但是在看完了__ip_local_out的代码后,才知道其返回1时,是一个特定的返回值,要求调用者明确调用hook函数的参数中的回调函数,这里时dst_output。
下面看看__ip_local_out的代码,代码也不长
- int __ip_local_out(struct sk_buff *skb)
-
{
- /* 获得IP报文头的地址 */
-
struct iphdr *iph = ip_hdr(skb);
- /* 到此,数据包的长度不会改变了,可以对IP报文中的TL字段赋值了,并转为网络序 */
-
iph->tot_len = htons(skb->len);
- /* 计算IP报文的校验和 */
- ip_send_check(iph);
- /* 调用netfilter的hook函数 */
-
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
-
skb_dst(skb)->dev, dst_output);
- }
在编译linux内核时,如果没有指定CONFIG_NETFILTER这个宏,则nf_hook的函数体是直接返回1.
若指定了CONFIG_NETFILTER,则nf_hook函数为
- static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
-
struct net_device *indev, struct net_device *outdev,
-
int (*okfn)(struct sk_buff *))
-
{
-
return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN);
- }
又是一个包装函数,对参数进行一下说明吧。
u_int8_t pf:指定netfilter的协议,值的范围如下:
- enum {
-
NFPROTO_UNSPEC = 0,
-
NFPROTO_IPV4 = 2,
-
NFPROTO_ARP = 3,
-
NFPROTO_BRIDGE = 7,
-
NFPROTO_IPV6 = 10,
-
NFPROTO_DECNET = 12,
-
NFPROTO_NUMPROTO,
- };
unsigned int hook:表示hook的类型,值的范围如下:
- enum nf_inet_hooks {
-
NF_INET_PRE_ROUTING,
-
NF_INET_LOCAL_IN,
-
NF_INET_FORWARD,
-
NF_INET_LOCAL_OUT,
-
NF_INET_POST_ROUTING,
-
NF_INET_NUMHOOKS
- };
看着几个枚举值,熟悉iptable的朋友应该很眼熟吧——因为iptable就是通过netfilter实现的。
struct sk_buff *skb:要输出的数据包
struct net_device *indev:收到数据包的网卡设备;
struct net_device *outdev:发送数据包的网卡设备;
int (*okfn) (struct sk_buff*):netfiler的hook函数判定成功时,会调用的回调函数。
下面看nf_hook_threash,
- /* 这里对于返回值1进行了说明 */
- /**
-
* nf_hook_thresh - call a netfilter hook
-
*
-
* Returns 1 if the hook has allowed the packet to pass. The function
-
* okfn must be invoked by the caller in this case. Any other return
-
* value indicates the packet has been consumed by the hook.
-
*/
-
static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
-
struct sk_buff *skb,
-
struct net_device *indev,
-
struct net_device *outdev,
-
int (*okfn)(struct sk_buff *), int thresh)
-
{
-
#ifndef CONFIG_NETFILTER_DEBUG
-
if (list_empty(&nf_hooks[pf][hook]))
-
return 1;
-
#endif
-
return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
- }
好,继续追踪nf_hook_slow的代码。
- /* Returns 1 if okfn() needs to be executed by the caller,
-
* -EPERM for NF_DROP, 0 otherwise. */
-
int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
-
struct net_device *indev,
-
struct net_device *outdev,
-
int (*okfn)(struct sk_buff *),
-
int hook_thresh)
-
{
-
struct list_head *elem;
-
unsigned int verdict;
-
int ret = 0;
-
-
/* We may already have this, but read-locks nest anyway */
- /* 获得RCU读锁 */
-
rcu_read_lock();
/* 根据pf的协议类型和hook的类型,得到netfilter动作行为的链表头 */
-
elem = &nf_hooks[pf][hook];
-
next_hook:
- /*
- 遍历这个动作行为链表头并得到最终对该包的判定处理。
- 这里,如果熟悉iptable的朋友很容易理解,因为判定的行为就是iptable中的行为。
- */
-
verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
-
outdev, &elem, okfn, hook_thresh);
-
if (verdict == NF_ACCEPT || verdict == NF_STOP) {
- /*
- 判定是接受或者停止
- NF_STOP是2.6中新加入的行为,与NF_ACCEPT类似。区别就是一旦一个判定为NF_STOP,就立刻返回,不会 进行后面的判定。而NF_ACCEPT则还会继续后面的判定
- */
-
ret = 1;
-
} else if (verdict == NF_DROP) {
- /* 该包需要drop */
-
kfree_skb(skb);
-
ret = -EPERM;
-
} else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
- /* 该包需要queue入列 */
-
if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
-
verdict >> NF_VERDICT_BITS))
-
goto next_hook;
-
}
-
rcu_read_unlock();
-
return ret;
- }
好,今天就先这样了。其实关于netfilter这些函数的处理,看到这儿,已经不妨碍阅读发送IP数据包的代码了。
明天可以还是先把发送数据包的代码看完,然后在把netfilter整理一下。今天进展比较快,因为自己对iptable的了解还可以,所以阅读netfilter的相关代码很容易,因为概念都很清楚。
看来,对应用了解清楚对于阅读内核代码还是有很大的帮助的。