UDP socket流程(14)——ip_local_out及其调用的函数

简介: 作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.netip_local_out的代码很短int ip_local_out(struct sk_buff *skb) {     int err;     /*     调用netfilter的hook检查该包是否可以发送。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

ip_local_out的代码很短
  1. int ip_local_out(struct sk_buff *skb)
  2. {
  3.     int err;
  4.     /* 
  5.     调用netfilter的hook检查该包是否可以发送。
  6.     */
  7.     err = __ip_local_out(skb);
  8.     /*
  9.     当err为1时,表明netfilter的hook函数告诉调用者允许包通过,并且需要调用者明确的调用hook函数的参数中     的回调函数。在此,需要调用dst_output来真正发送数据包 
  10.     */
  11.     if (likely(err == 1))
  12.         err = dst_output(skb);

  13.     return err;
  14. }
刚看到这里时,看到err很自然的想到err是错误值,尤其是linux中大部分的成功返回都是0。
但是在看完了__ip_local_out的代码后,才知道其返回1时,是一个特定的返回值,要求调用者明确调用hook函数的参数中的回调函数,这里时dst_output。


下面看看__ip_local_out的代码,代码也不长
  1. int __ip_local_out(struct sk_buff *skb)
  2. {
  3.     /* 获得IP报文头的地址 */
  4.     struct iphdr *iph = ip_hdr(skb);
  5.     
  6.     /* 到此,数据包的长度不会改变了,可以对IP报文中的TL字段赋值了,并转为网络序 */
  7.     iph->tot_len = htons(skb->len);
  8.     /* 计算IP报文的校验和 */
  9.     ip_send_check(iph);
  10.     /* 调用netfilter的hook函数 */
  11.     return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
  12.          skb_dst(skb)->dev, dst_output);
  13. }
在编译linux内核时,如果没有指定CONFIG_NETFILTER这个宏,则nf_hook的函数体是直接返回1.
若指定了CONFIG_NETFILTER,则nf_hook函数为
  1. static inline int nf_hook(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
  2.              struct net_device *indev, struct net_device *outdev,
  3.              int (*okfn)(struct sk_buff *))
  4. {
  5.     return nf_hook_thresh(pf, hook, skb, indev, outdev, okfn, INT_MIN);
  6. }
又是一个包装函数,对参数进行一下说明吧。
u_int8_t pf:指定netfilter的协议,值的范围如下:
  1. enum {
  2.     NFPROTO_UNSPEC = 0,
  3.     NFPROTO_IPV4 = 2,
  4.     NFPROTO_ARP = 3,
  5.     NFPROTO_BRIDGE = 7,
  6.     NFPROTO_IPV6 = 10,
  7.     NFPROTO_DECNET = 12,
  8.     NFPROTO_NUMPROTO,
  9. };
unsigned int hook:表示hook的类型,值的范围如下:
  1. enum nf_inet_hooks {
  2.     NF_INET_PRE_ROUTING,
  3.     NF_INET_LOCAL_IN,
  4.     NF_INET_FORWARD,
  5.     NF_INET_LOCAL_OUT,
  6.     NF_INET_POST_ROUTING,
  7.     NF_INET_NUMHOOKS
  8. };
看着几个枚举值,熟悉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. /* 这里对于返回值1进行了说明 */
  2. /**
  3.  *    nf_hook_thresh - call a netfilter hook
  4.  *    
  5.  *    Returns 1 if the hook has allowed the packet to pass. The function
  6.  *    okfn must be invoked by the caller in this case. Any other return
  7.  *    value indicates the packet has been consumed by the hook.
  8.  */
  9. static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
  10.                  struct sk_buff *skb,
  11.                  struct net_device *indev,
  12.                  struct net_device *outdev,
  13.                  int (*okfn)(struct sk_buff *), int thresh)
  14. {
  15. #ifndef CONFIG_NETFILTER_DEBUG
  16.     if (list_empty(&nf_hooks[pf][hook]))
  17.         return 1;
  18. #endif
  19.     return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
  20. }
好,继续追踪nf_hook_slow的代码。
  1. /* Returns 1 if okfn() needs to be executed by the caller,
  2.  * -EPERM for NF_DROP, 0 otherwise. */
  3. int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
  4.          struct net_device *indev,
  5.          struct net_device *outdev,
  6.          int (*okfn)(struct sk_buff *),
  7.          int hook_thresh)
  8. {
  9.     struct list_head *elem;
  10.     unsigned int verdict;
  11.     int ret = 0;

  12.     /* We may already have this, but read-locks nest anyway */
  13.     /* 获得RCU读锁 */
  14.     rcu_read_lock();
     /* 根据pf的协议类型和hook的类型,得到netfilter动作行为的链表头 */
  1.     elem = &nf_hooks[pf][hook];
  2. next_hook:
  3.     /* 
  4.     遍历这个动作行为链表头并得到最终对该包的判定处理。
  5.     这里,如果熟悉iptable的朋友很容易理解,因为判定的行为就是iptable中的行为。
  6.     */
  7.     verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev,
  8.              outdev, &elem, okfn, hook_thresh);
  9.     if (verdict == NF_ACCEPT || verdict == NF_STOP) {
  10.         /* 
  11.         判定是接受或者停止
  12.         NF_STOP是2.6中新加入的行为,与NF_ACCEPT类似。区别就是一旦一个判定为NF_STOP,就立刻返回,不会         进行后面的判定。而NF_ACCEPT则还会继续后面的判定
  13.         */
  14.         ret = 1;
  15.     } else if (verdict == NF_DROP) {
  16.         /* 该包需要drop */
  17.         kfree_skb(skb);
  18.         ret = -EPERM;
  19.     } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
  20.         /* 该包需要queue入列 */
  21.         if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
  22.              verdict >> NF_VERDICT_BITS))
  23.             goto next_hook;
  24.     }
  25.     rcu_read_unlock();
  26.     return ret;
  27. }
好,今天就先这样了。其实关于netfilter这些函数的处理,看到这儿,已经不妨碍阅读发送IP数据包的代码了。
明天可以还是先把发送数据包的代码看完,然后在把netfilter整理一下。今天进展比较快,因为自己对iptable的了解还可以,所以阅读netfilter的相关代码很容易,因为概念都很清楚。

看来,对应用了解清楚对于阅读内核代码还是有很大的帮助的。


目录
相关文章
|
1月前
|
存储 Python
Python网络编程基础(Socket编程) UDP 发送和接收数据
【4月更文挑战第10天】对于UDP客户端而言,发送数据是一个相对简单的过程。首先,你需要构建一个要发送的数据报,这通常是一个字节串(bytes)。然后,你可以调用socket对象的`sendto`方法,将数据报发送到指定的服务器地址和端口。
|
1月前
|
存储 Python
Python网络编程基础(Socket编程)UDP客户端编程
【4月更文挑战第9天】在UDP通信中,客户端负责发送数据到服务器,并接收来自服务器的响应。与服务器不同,客户端通常不需要绑定到特定的地址和端口,因为它可以临时使用任何可用的端口来发送数据。下面,我们将详细讲解UDP客户端编程的基本步骤。
|
1月前
|
网络协议 Python
Python网络编程基础(Socket编程)创建UDP socket对象
【4月更文挑战第8天】在Python中创建UDP服务器涉及使用`socket`模块创建socket对象,如`udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)`,然后绑定到特定IP地址和端口,如`udp_socket.bind(('localhost', 12345))`。服务器通过`recvfrom`在无限循环中监听和接收数据报。这只是基础,实际应用还需处理接收、解析、响应及错误处理等。接下来可学习如何利用socket对象进行数据交互以构建完整服务器。
|
1月前
|
网络协议 网络性能优化 开发者
Python网络编程基础(Socket编程)UDP Socket编程
【4月更文挑战第8天】Python网络编程中,UDP与TCP协议各有特点。TCP提供可靠连接,确保数据顺序与完整性,适合文件传输等;UDP则无连接,速度快,常用于实时音视频,牺牲了数据可靠性。Python的socket库支持两者,开发者可根据需求选择。
|
4月前
socket字节序转换与地址转换函数记录
【代码】socket字节序转换与地址转换函数记录。
18 0
|
4月前
socket编程之回声服务器函数的陷阱
由connect函数使用不当导致的小错误 话不多说先看代码:
25 0
|
5天前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
12 0
|
19天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
1月前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【2月更文挑战第19天】
TCP 和 UDP 的 Socket 调用