【C语言】linux内核ip_local_out函数

news2025/6/21 8:20:25

一、讲解

这个函数 __ip_local_out 是 Linux 内核网络子系统中的函数,部分与本地出口的 IPv4 数据包发送相关。下面讲解这段代码的每一部分:
1. 函数声明 int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb):
   - struct net *net:是指向网络命名空间(network namespace)结构的指针,用来划分和隔离不同的网络环境。
   - struct sock *sk:是一个表示网络套接字的结构体指针,可以包含该数据包的源信息(如端口号、套接字选项等)。
   - struct sk_buff *skb:指向“socket buffer”结构体的指针,代表网络层面的数据包,包含要发送的数据和相关控制信息。
2. struct iphdr *iph = ip_hdr(skb);:
   - 定义了一个指向IP头部的指针 iph,通过调用 ip_hdr(skb) 来获得指向 skb 中IP头部的指针。
3. iph->tot_len = htons(skb->len);:
   - 设置IP头部的总长度字段 tot_len。通过 htons 函数将主机字节序转换成网络字节序,并将 skb 缓冲区的长度赋给它。
4. ip_send_check(iph);:
   - 调用 ip_send_check 函数来计算IP头部的校验和。
5-7. 接下来的注释说明,如果出口设备绑定到了一个L3主设备(如VRF - Virtual Routing and Forwarding),则需要将数据包传递给对应的处理程序。
8. skb = l3mdev_ip_out(sk, skb);:
   - 调用 l3mdev_ip_out 函数处理数据包,这个处理可能涉及特定的路由选择。
9. if (unlikely(!skb)) return 0;:
   - 检查 skb 是否已被 l3mdev_ip_out 释放或处理,如果是,那么函数返回0,表示没有更多处理需要完成。
10. skb->protocol = htons(ETH_P_IP);:
    - 在发送之前,设置数据包 skb->protocol 字段为IP协议(`ETH_P_IP`),并使用 htons 进行字节序转换。
11. 最后一行是调用 Netfilter 钩子(hook):   

    return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
                   net, sk, skb, NULL, skb_dst(skb)->dev,
                   dst_output);

    - 这是用于将数据包传递给网络过滤(Netfilter)框架,进行可能的过滤或调整。
    - NFPROTO_IPV4 指明协议是 IPv4。
    - NF_INET_LOCAL_OUT 是钩子点,表示数据包在本地被发送出去之前的处理阶段。
    - net, sk, skb, NULL, skb_dst(skb)->dev 是传递给钩子的各种参数。
    - dst_output 是输出函数,如果数据包通过过滤,它将负责将数据包发送出去。
这个函数最终返回 Netfilter 钩子的返回值,其可能是一个错误码或成功指示。整体来看,这个函数是处理并发送IPV4本地出口数据包的一部分过程。

二、中文注释

这个函数是Linux内核网络栈中的一个函数,它的责任是处理IP层的本地输出。

int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
{
    // 获取指向sk_buff中IP报头的指针
    struct iphdr *iph = ip_hdr(skb);

    // 设置IP报头中的总长度字段为skb的长度,htons用于将主机字节顺序转换为网络字节顺序
    iph->tot_len = htons(skb->len);
    // 计算和填充IP报头的校验和
    ip_send_check(iph);

    /* 如果出口设备属于一个L3主设备,就将skb传递给它的处理函数
     * l3mdev_ip_out负责处理skb,可能进行一些特定于该设备的处理
     */
    skb = l3mdev_ip_out(sk, skb);
    // 如果skb为空,说明处理不成功,返回0
    if (unlikely(!skb))
        return 0;

    // 设置skb的协议字段为IP协议,htons用于将主机字节顺序转换为网络字节顺序
    skb->protocol = htons(ETH_P_IP);

    // 调用netfilter钩子,以便进行进一步的处理(例如,过滤,NAT等)
    // nf_hook会根据配置决定是否处理skb或将其传递给下一个处理阶段
    return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
               net, sk, skb, NULL, skb_dst(skb)->dev,
               dst_output);
}

解释完成后,简单总结此函数的流程:
1. 函数定义有三个参数:net结构体表示网络命名空间,sock结构体表示套接字状态信息,sk_buff结构体指针skb表示要处理的网络数据包。
2. 提取IP报头,并根据skb的长度设置IP报头的总长度字段。
3. 计算IP头部校验和。
4. 如果skb是流向L3主设备的,则调用l3mdev_ip_out函数进行进一步处理。
5. 如果l3mdev_ip_out返回null(即无法处理skb),则函数直接返回0。
6. 设置skb的协议类型为ETH_P_IP,表示这是一个IP数据包。
7. 最后调用`nf_hook`函数,触发相应的Netfilter钩子进行后续处理,如过滤、NAT等,然后将数据包传递到下一步(通常是网络接口层)。这里的`dst_output`是目标输出函数,用于最终将数据包发送出去。
这个函数是网络输出路径的一部分,确保在IP层发送的每个数据包都正确处理,并通过Netfilter框架进行必要的检查和修改。

三、ip_local_out

这段代码是Linux内核网络栈的一部分,是一个用于处理本地发出的IP数据包的函数。`ip_local_out` 函数是在网络层处理完毕,准备发送数据包到链路层之前调用的。下面是详细的中文解释:

int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)

这个函数的定义意味着它用于处理本地发出的IP数据包。它接收三个参数:
- struct net *net:指向当期网络命名空间的指针。
- struct sock *sk:与数据包相关联的socket结构体的指针。
- struct sk_buff *skb:指向"socket buffer"的指针,这是一个封装了待发送数据包内容的结构体。

{
    int err;

定义一个整型变量 err,用于存储操作的结果状态。

    err = __ip_local_out(net, sk, skb);

调用 __ip_local_out 函数尝试处理和发送数据包。这个函数进行一系列的校验和调整,并准备数据包被发送。它返回一个整数值来表示操作的结果。

    if (likely(err == 1))
        err = dst_output(net, sk, skb);

后续的代码检查 __ip_local_out 函数的返回值。使用 likely 宏是一个编译器优化,指示 err == 1 这个条件很可能是真的,这样编译器可以优化代码的分支预测。如果 __ip_local_out 成功,返回值会是1,这时会调用 dst_output 函数来发送数据包。`dst_output` 函数完成将数据包发送到目的地的最后一步工作。

    return err;
}

最后,函数返回 err 变量的值,表示了 ip_local_out 这个函数的整体执行结果。如果一切成功,应该是返回零或者在传输数据包过程中发生的任何错误码。

EXPORT_SYMBOL_GPL(ip_local_out);

最后一行代码 EXPORT_SYMBOL_GPL 是一个宏,用于导出 ip_local_out 函数的符号,使其可以被其他模块调用,同时指定导出的符号遵循GPL许可。这样,只有GPL兼容的代码才能够使用此函数。

四、dst_output

dst_output 函数是一个在网络栈中用来处理传输层发出的数据包的内联函数(inline function),其目的是将数据包传递给下一层(通常是网络层),以便最终发送到网络上。函数原型如下:

static inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb)

参数解释:
- struct net *net: 指向当前网络命名空间的指针,用于在多个网络命名空间环境中确定数据包所属的命名空间。
- struct sock *sk: 指向套接字结构的指针,代表数据包来源的套接字。这个结构包含了一系列关于套接字状态和选项的信息。
- struct sk_buff *skb: 指向“socket buffer”结构的指针,这是内核中用以存储网络数据包的标准容器。
函数的实现只包含一行代码:

return skb_dst(skb)->output(net, sk, skb);

解释:
- skb_dst(skb): 这是一个宏,功能是从 skb(数据包)中提取目的地信息(即 dst_entry 结构)。每个数据包都有一个 dst_entry,包含了关于目的地的信息,如路由决策。
- ->output: 这是 dst_entry 结构中的一个函数指针,指向实际负责输出数据包的函数。
- net, sk, skb: 将 dst_output 函数的参数传递给 output 函数,以便正确处理数据包。
总体来说,`dst_output` 函数的职责是调用正确的输出处理函数,这个函数由路由子系统在数据包通过路由选择的过程中确定,数据包将基于路由信息被发送到正确的网络接口。该函数的返回值通常是一个错误码,指示操作成功还是发生错误。

五、skb_dst

skb_dst 几乎在所有内核版本中都是通过宏定义实现的,因此你不会在代码中找到一个名为`skb_dst`的函数。它是对`sk_buff`数据结构中的一部分内容的一个访问器,通常是这样定义的:

#define skb_dst(skb) ((skb)->dst)

这个宏简单地从`sk_buff`结构体中提取出`dst`成员。`sk_buff`通常是内核网络子系统处理数据包时使用的数据结构,其中包含了数据包的各种控制信息和数据内容。
dst成员是一个`dst_entry`类型的指针,包含了关于目的地路由信息的详细内容,如下一跳地址、路径度量、特定的路由标志等。
实际的`dst_entry`结构体和相关函数的定义可以在内核源代码的路由(routing)相关的文件中找到,例如`include/net/dst.h`和相关的`net/ipv4`或`net/ipv6`目录下的文件。
请注意,内核源代码的具体结构和定义可能会根据正在查看的Linux内核版本有所不同。如果打算对这部分代码进行修改或者深入理解,建议确认正在查看的内核源代码的版本和具体的文件路径。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1503272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AcWing 1262. 鱼塘钓鱼(每日一题)

目录 暴力枚举法: 贪心: 原题链接:1262. 鱼塘钓鱼 - AcWing题库 有 N个鱼塘排成一排,每个鱼塘中有一定数量的鱼,例如:N5 时,如下表: 鱼塘编号12345第1分钟能钓到的鱼的数量&…

k8s-生产级的k8s高可用(2) 25

部署containerd k8s2、k8s3、k8s4在配置前需要重置节点(reset)在上一章已完成 禁用所有节点docker和cri-docker服务 所有节点清除iptables规则 重置后全部节点重启 由于之前部署过docker,因此containerd默认已安装 修改配置 启动containe…

专业140+总分430+西南交通大学924信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

今年报考西南交通大学,考研分数专业课924信号与系统140,总分430,各门分数都还是比较均衡,经过一年的复习,有得有失,总结一下自己的复习经历,希望给大家有点帮助,在复习中做的更好&am…

十七、IO流——综合练习

综合练习 目录 一、制造假数据1.1自己写代码1.2 利用糊涂包生成假数据 二、随机点名器2.1随机点名器12.2 随机点名器22.3 随机点名器32.4 随机点名器42.5 随机点名器5 三、登录注册3.1 登录注册13.2 登录注册23.3 登录注册3 一、制造假数据 需求:制造假数据也是开发…

BadUsb制作

BadUsb制作 一个树莓派pico kali监听 需要的文件 https://pan.baidu.com/s/1_kyzXIqk9JWHGHstTgq7sQ?pwd6666 1.将pico插入电脑 2.将Bad USB固件中的文件复制到pico中,pico会重启 3.将Bad USB目录文件复制进去(打开Bad USB目录文件复制) …

MySQL--explain执行计划详解

什么是执行计划? SQL的执行计划,通俗来说就是SQL的执行情况,一条SQL语句扫描哪些表,那个子查询先执行,是否用到了索引等等,只有当我们知道了这些情况之后才知道,才可以更好的去优化SQL&#xf…

如何将MathType嵌入到word中 word打开MathType显示错误

当我们编辑好mathtype公式以后,有时候需要将这个公式导入到word中,但是有不少用户们不清楚mathtype如何嵌入到word中。今天小编就给大家说明一下mathtype公式导入word的两种不同方法,有需要的用户们赶紧来看一下吧。 一、mathtype如何嵌入到…

(产品之美系列三)小红书投票组建,利用用户好奇心,增大互动

小红书发布笔记或者视频,可以带一个投票功能。此投票功能与其他的有什么不同呢? 发布一个话题:你觉得王维和李白哪个更帅? 如果你自己不投票,就是看不到结果。当你投票之后: 可以知道选择王维的有百分之八十二。 启发:小红书投…

Git分布式管理-头歌实验远程版本库

Git的一大特点就是,能为不同系统下的开发者提供了一个协作开发的平台。而团队如果要基于Git进行协同开发,就必须依赖远程版本库。远程版本库允许,我们将本地版本库保存在远端服务器,而且,不同的开发者也是基于远程版本…

OJ:循环队列

622. 设计循环队列 - 力扣(LeetCode) 思路 思路:首先循环队列的意思是:空间固定,就是提前开辟好,满了就不能插入了,但是删除数据后仍有空间,删除循环队列里面的数据后,保…

Python学习日记之学习turtle库(上 篇)

一、初步认识turtle库 turtle 库是 Python 语言中一个很流行的绘制图像的函数库,想象一个小乌龟,在一个横 轴为 x、纵轴为 y 的坐标系原点,(0,0)位置开始,它根据一组函数指令的控制,在这个平面 坐标系中移动&#xff0…

如果编程语言是一种武器……

对程序员来说,编程语言就是武器,但有的武器好用,有的武器不好用,有的武器甚至会杀了自己 C语言是M1式加兰德步枪,很老但可靠。 C是双截棍,挥舞起来很强悍,很吸引人,但需要你多年的磨…

Ubuntu平铺左、右、上、下、1/2、1/4窗口(脚本)

前言 之前因为一直在用Ubuntu 18或者Ubuntu 20然后发现装了GNOME插件后,电脑在使用过程中,会时不时的卡死(鼠标没问题,键盘输入会有10-20秒的延迟)频率基本是一小时一次,因为这种卡顿会很容易打断思路&…

攻防世界-MISC-EASY_EVM

题目链接:攻防世界 (xctf.org.cn) 下载附件得到info.txt: pragma solidity ^0.5.0; ABI: [ { "inputs": [], "payable": true, "stateMutability": "payable", "type": "constructor" }, {…

基于单片机的RFID门禁系统设计

目 录 摘 要 I Abstract II 引 言 1 1 控制系统设计 3 1.1 主控制器选择 3 1.2 项目总体设计 3 2 项目硬件设计 5 2.1 单片机控制模块 5 2.2 射频识别模块 8 2.3 矩阵键盘模块 9 2.4 液晶显示模块 10 2.5 报警模块 11 2.6 AT24C02存储模块 12 2.7 继电器驱动模块 13 2.8 总电路…

【ubuntu】安装 Anaconda3

目录 一、Anaconda 说明 二、操作记录 2.1 下载安装包 2.1.1 官网下载 2.1.2 镜像下载 2.2 安装 2.2.1 安装必要的依赖包 2.2.2 正式安装 2.2.3 检测是否安装成功 方法一 方法二 方法三 2.3 其他 三、参考资料 3.1 安装资料 3.2 验证是否成功的资料 四、其他 …

数据结构之八大排序

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary_walk ⸝⋆ ━━━┓ - 个性标签 - :来于“云”的“羽球人”。…

数列操作1——栈+前缀和,典型例题,值得一看

题目描述 先给定一个长度为n数列,再给定m个操作,现在需要维护五个操作: 1 x:在光标的前面插入一个数字x。 2:删除光标前的最后一个数字,如果光标前没有数字则忽略。 3:左移一格光标&#xf…

【Python】7. 基础语法(5) -- 文件+库+习题篇

文件 文件是什么 变量是把数据保存到内存中. 如果程序重启/主机重启, 内存中的数据就会丢失. 要想能让数据被持久化存储, 就可以把数据存储到硬盘中. 也就是在 文件 中保存 在 Windows “此电脑” 中, 看到的内容都是 文件. 文件夹(目录)也是一种特殊的文件->目录文件 通过…

关于装载类子系统

装载类子系统 类加载器字节码调节器类加载运行时数据区 类加载器 将class文件加载进jvm的方法去,并在方法去中创建一个java.lang.Class对象作为外界访问这个类的接口。实现这个动作的代码模块称为类加载器。 类加载器分类 启动类加载器(Bootstrap C…