Linux 内核源码阅读——ipv4

Linux 内核源码阅读------ipv4

综述

在 Linux 内核中,IPv4 协议的实现主要分布在 net/ipv4/ 目录下。以下是一些关键的源文件及其作用:

1. 协议栈核心

  • net/ipv4/ip_input.c:处理接收到的 IPv4 数据包(输入路径)。
  • net/ipv4/ip_output.c:处理 IPv4 数据包的发送(输出路径)。
  • net/ipv4/ip_forward.c:实现 IP 数据包的转发逻辑。

2. 地址管理

  • net/ipv4/devinet.c:管理 IPv4 地址,包括添加、删除和查询接口地址。
  • net/ipv4/af_inet.c:实现 IPv4 协议族的 socket 操作,如 socket()bind()connect() 等。

3. 路由子系统

  • net/ipv4/route.c:核心的路由查找和管理逻辑。
  • net/ipv4/fib_frontend.cfib_trie.c:实现基于前缀树(Trie)的 FIB(Forwarding Information Base)路由表。

4. 传输层交互

  • net/ipv4/tcp_ipv4.c:IPv4 版本的 TCP 处理。
  • net/ipv4/udp.c:IPv4 版本的 UDP 处理。
  • net/ipv4/raw.c:处理 IPv4 原始套接字(Raw Sockets)。

5. 其他重要模块

  • net/ipv4/ip_fragment.c:处理 IP 数据包的分片和重组。
  • net/ipv4/icmp.c:实现 ICMP(Internet Control Message Protocol)。
  • net/ipv4/igmp.c:实现 IGMP(Internet Group Management Protocol)。
  • net/ipv4/netfilter/ 目录:Linux 内核 Netfilter(防火墙和 NAT)相关代码。

收发包

关键数据结构

iphdr
c 复制代码
struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,      // IP头长度 (单位:4字节)
		version:4;       // IP版本(例如 IPv4)
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,   // IP版本(例如 IPv4)
  		ihl:4;          // IP头长度 (单位:4字节)
#else
#error	"Please fix <asm/byteorder.h>"  // 如果没有定义大小端模式,编译时会报错
#endif
	__u8	tos;          // 服务类型(Type of Service,TOS),用于指定数据包的优先级和路由
	__u16	tot_len;      // 总长度(包括头部和数据部分),单位字节
	__u16	id;           // 标识符,用于标识分片的所有部分
	__u16	frag_off;     // 分片偏移和标志,指示是否是分片,以及分片的位置
	__u8	ttl;          // 生存时间(Time To Live),指定数据包能在网络上生存的最大跳数
	__u8	protocol;     // 上层协议类型(例如 ICMP、TCP、UDP 等)
	__u16	check;        // 校验和,用于错误检查
	__u32	saddr;        // 源 IP 地址
	__u32	daddr;        // 目标 IP 地址
	/*The options start here. */
};

收包主要接口ip_rcv

c 复制代码
/**
 * ip_rcv - 处理接收到的 IPv4 数据包
 * @skb: 指向 socket buffer 的指针,包含接收到的数据包
 * @dev: 指向接收到数据包的网络设备的指针
 * @pt: 指向 packet_type 结构体的指针,描述数据包类型
 *
 * 该函数用于接收并处理从网络设备接收到的 IPv4 数据包。它解析 IP 头部,
 * 检查数据包的有效性,并根据协议类型(如 TCP、UDP 或 ICMP)将数据包
 * 传递给相应的协议栈进行处理。如果数据包不可达或格式无效,返回错误。
 * 如果数据包处理成功,返回 0。
 *
 * 返回值:
 *   - 0:表示数据包处理成功,已传递给适当的协议处理函数
 *   - 负值:表示错误,例如数据包格式无效或目标不可达
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt);
plantuml 复制代码
@startuml

start
:网络数据包到达;
:调用 ip_rcv 函数;
:调用 ip_rcv_finish 完成处理;
:调用 ip_route_input 查找路由;
if (路由(缓存)查找成功?) then (是)
  if (是本地包?) then (是)
    :调用 ip_local_deliver 处理本地包;
    :将数据包交给本地协议栈处理;
  else (否)
    if (是多播包?) then (是)
      :调用 ip_route_input_mc 处理多播包;
      :处理多播包;
    else (否)
      :调用 ip_route_input_slow 进行慢路径处理;
      :处理路由查找未命中的情况;
        note right
	  插入缓存表rt_hash_table
      end note
    endif
  endif
else (否)
  :调用 ip_route_input_slow 进行慢路径处理;
  :处理路由查找未命中的情况;
  note right
	插入缓存表rt_hash_table
  end note
endif
stop

@enduml

本机包:ip_local_deliver

其他包,进行转发:ip_forward

发包主要接口ip_output

该函数的主要任务是:

  • 处理 IP 头部(如检查和更新校验和)
  • 执行 IP 选项 处理
  • 可能需要进行 分片
  • 最终调用 底层链路层接口 进行发送
c 复制代码
int ip_output(struct sk_buff *skb)
主要流程
plantuml 复制代码
@startuml
start
:调用 ip_output(skb);
if (需要分片?) then (是)
    :调用 ip_fragment(skb);
    :对每个分片调用 ip_finish_output(skb);
else (否)
    :调用 ip_finish_output(skb);
endif

:进入 ip_finish_output2;
:添加链路层头部(如以太网头部);

if (存在 hh 缓存?) then (否)
    :发送 ARP 请求;
    :等待 ARP 响应;
    :缓存邻居信息;
endif

:调用 hh->hh_output(skb);
stop
@enduml

设备管理

事件通知处理函数------inetdev_event

处理什么事件?

NETDEV_REGISTER ------注册接口

NETDEV_UP ------ 接口UP

NETDEV_DOWN ------ 接口DOWN

NETDEV_CHANGEMTU ------ 改变MTU

NETDEV_UNREGISTER ------注销接口

NETDEV_CHANGENAME ------ 更改接口名

接口UP处理

分为普通接口和环回口处理

  1. MTU 检查
  2. 环回设备的特殊处理
  3. 启动多播(Multicast)功能

RTNetlink 是 Linux 内核用于网络配置和管理的接口,它通过 Netlink sockets 向用户空间传递网络相关的信息。

关键结构

c 复制代码
static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
	 [4] = { .doit	 = inet_rtm_newaddr,  }, /* 处理新的 IP 地址添加消息 */
	 [5] = { .doit	 = inet_rtm_deladdr,  }, /* 处理删除 IP 地址的消息 */
	 [6] = { .dumpit = inet_dump_ifaddr,  }, /* 处理获取 IP 地址的消息 */
	 [8] = { .doit	 = inet_rtm_newroute, }, /* 处理新的路由添加消息 */
	 [9] = { .doit	 = inet_rtm_delroute, }, /* 处理删除路由的消息*/
	[10] = { .doit	 = inet_rtm_getroute, .dumpit = inet_dump_fib, },
#ifdef CONFIG_IP_MULTIPLE_TABLES /* 多个路由表支持 */
	[16] = { .doit	 = inet_rtm_newrule, },
	[17] = { .doit	 = inet_rtm_delrule, },
	[18] = { .dumpit = inet_dump_rules,  },
#endif
};

主要流程

plantuml 复制代码
@startuml

start
:module_init(inet_init);
floating note left:  module_init内核模块的初始化入口点
:inet_init;
note right
初始化网络协议栈中与 IP 协议相关
end note
:ip_init;
note right
初始化 IPv4 协议栈
end note
:ip_rt_init;
:devinet_init;
note right
初始化与设备相关的网络功能
end note
:rtnetlink_links[PF_INET] = inet_rtnetlink_table;
note left
注册 IPv4 相关的 Netlink 消息处理表
end note
stop


@enduml 
c 复制代码
/**
 * module_init() - driver initialization entry point
 * @x: function to be run at kernel boot time or module insertion
 * 
 * module_init() will either be called during do_initcalls (if
 * builtin) or at module insertion time (if a module).  There can only
 * be one per module.
 */
#define module_init(x)	__initcall(x);
相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
网络研究院3 天前
2026年网络安全
网络·安全·法律·法规·趋势·发展