TCP/IP 协议栈实现
Linux 内核实现了完整的 TCP/IP 协议栈,本章深入探讨网络数据包在内核中的处理流程。
网络协议栈架构
┌──────────────────────────────────────────────┐
│ 应用层 (Application Layer) │
│ HTTP, FTP, DNS, SMTP, SSH, ... │
└────────────────┬─────────────────────────────┘
│ Socket API
┌────────────────▼─────────────────────────────┐
│ 传输层 (Transport Layer) │
│ ┌──────────┐ ┌──────────┐ │
│ │ TCP │ │ UDP │ SCTP, DCCP │
│ └──────────┘ └──────────┘ │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ 网络层 (Network Layer) │
│ ┌──────────┐ ┌──────────┐ │
│ │ IPv4 │ │ IPv6 │ ICMP, IGMP │
│ └──────────┘ └──────────┘ │
│ ┌──────────────┐ │
│ │ Routing │ 路由子系统 │
│ └──────────────┘ │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ 链路层 (Link Layer) │
│ Ethernet, WiFi, PPP, ... │
│ ┌──────────────┐ │
│ │ Netfilter │ 防火墙/NAT │
│ └──────────────┘ │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ 设备驱动层 (Device Driver) │
│ 网卡驱动程序 │
└────────────────┬─────────────────────────────┘
│
物理层
sk_buff 数据结构
核心结构
// include/linux/skbuff.h
struct sk_buff {
// 链表指针
struct sk_buff *next;
struct sk_buff *prev;
// 时间戳
ktime_t tstamp;
// 关联的 socket
struct sock *sk;
// 网络设备
struct net_device *dev;
// 数据指针
unsigned char *head; // 缓冲区起始
unsigned char *data; // 数据起始
unsigned char *tail; // 数据结束
unsigned char *end; // 缓冲区结束
// 长度
unsigned int len; // 数据长度
unsigned int data_len; // 分片数据长度
// 协议
__be16 protocol; // 协议类型(ETH_P_IP 等)
// 传输层头
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
// 校验和
__wsum csum;
__u32 priority;
// IP 信息
__u8 ip_summed:2;
__u8 pkt_type:3;
// 引用计数
atomic_t users;
// 析构函数
void (*destructor)(struct sk_buff *skb);
};
// sk_buff 内存布局
head data tail end
│ │ │ │
▼ ▼ ▼ ▼
┌───────────────────┬───────────────────┬───────────────────┐
│ headroom │ 有效数据 │ tailroom │
└───────────────────┴───────────────────┴───────────────────┘
headroom: 用于添加头部(如以太网头、IP 头)
tailroom: 用于添加尾部数据
sk_buff 操作函数
// 分配 sk_buff
struct sk_buff *alloc_skb(unsigned int size, gfp_t priority);
struct sk_buff *dev_alloc_skb(unsigned int length);
// 释放 sk_buff
void kfree_skb(struct sk_buff *skb);
void dev_kfree_skb(struct sk_buff *skb);
// 克隆 sk_buff
struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority);
struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority);
// 操作数据指针
unsigned char *skb_put(struct sk_buff *skb, unsigned int len); // tail 后移
unsigned char *skb_push(struct sk_buff *skb, unsigned int len); // data 前移
unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); // data 后移
// 保留头部空间
void skb_reserve(struct sk_buff *skb, int len);
// 访问协议头
struct iphdr *ip_hdr(const struct sk_buff *skb);
struct tcphdr *tcp_hdr(const struct sk_buff *skb);
struct udphdr *udp_hdr(const struct sk_buff *skb);
struct ethhdr *eth_hdr(const struct sk_buff *skb);
数据包接收流程
接收路径(RX)
// 1. 网卡接收数据包(硬件中断)
// drivers/net/ethernet/.../driver.c
static irqreturn_t eth_interrupt(int irq, void *dev_id)
{
// 禁用中断
// 调度 NAPI poll
napi_schedule(&priv->napi);
return IRQ_HANDLED;
}
// 2. NAPI poll 处理(软中断)
// net/core/dev.c
static int process_backlog(struct napi_struct *napi, int quota)
{
while (1) {
struct sk_buff *skb = dequeue_skb();
if (!skb)
break;
__netif_receive_skb(skb);
if (--quota <= 0)
break;
}
return work;
}
// 3. 协议层处理
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
// 链路层处理
type = skb->protocol;
// 调用协议处理函数
deliver_skb(skb, pt_prev, orig_dev);
// IPv4: ip_rcv()
// IPv6: ipv6_rcv()
}
// 4. IP 层处理
// net/ipv4/ip_input.c
int ip_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
// 校验 IP 头
if (ip_fast_csum((u8 *)iph, iph->ihl))
goto csum_error;
// Netfilter 钩子:NF_INET_PRE_ROUTING
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);
}
// 5. 路由查找
static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
// 路由查找
if (ip_route_input_noref(skb, iph->daddr, iph->saddr,
iph->tos, dev))
goto drop;
// 根据路由结果处理
return dst_input(skb);
}
// 6. 传输层处理
// net/ipv4/tcp_ipv4.c
int tcp_v4_rcv(struct sk_buff *skb)
{
struct tcphdr *th = tcp_hdr(skb);
struct sock *sk;
// 查找 socket
sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th),
th->source, th->dest);
// 传递给 socket
if (tcp_v4_do_rcv(sk, skb))
goto discard_it;
return 0;
}
// 7. 数据到达 socket 缓冲区
// 应用程序通过 recv() 读取
NAPI(New API)
传统方式 vs NAPI:
传统中断方式:
每个数据包 → 硬件中断 → 性能问题
NAPI 方式:
第一个数据包 → 硬件中断 → 禁用中断
后续数据包 → 轮询处理(软中断)
队列空 → 重新启用中断
NAPI 配置:
# 查看网卡的 NAPI 配置
ethtool -c eth0
# 设置 NAPI 参数
ethtool -C eth0 rx-usecs 50 rx-frames 32
# 查看中断统计
cat /proc/interrupts | grep eth0
# 查看软中断统计
cat /proc/softirqs
数据包发送流程
发送路径(TX)
// 1. 应用程序调用 send()
// net/socket.c
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
unsigned int, flags, struct sockaddr __user *, addr,
int, addr_len)
{
// 查找 socket
sock = sockfd_lookup_light(fd, &err, &fput_needed);
// 调用协议发送函数
err = sock_sendmsg(sock, &msg);
}
// 2. TCP/UDP 处理
// net/ipv4/tcp.c
int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{
// 分配 sk_buff
skb = sk_stream_alloc_skb(sk, select_size(sk, sg),
sk->sk_allocation, sk_page_frag(sk));
// 复制用户数据
copy_from_iter(skb_put(skb, copy), copy, &msg->msg_iter);
// 发送
tcp_push(sk, flags, mss_now, TCP_NAGLE_PUSH, size_goal);
}
// 3. IP 层处理
// net/ipv4/ip_output.c
int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl)
{
// 路由查找
rt = (struct rtable *)__sk_dst_check(sk, 0);
// 构建 IP 头
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = 5;
iph->tos = inet->tos;
iph->ttl = ip_select_ttl(inet, &rt->dst);
iph->protocol = sk->sk_protocol;
iph->saddr = fl4->saddr;
iph->daddr = fl4->daddr;
// Netfilter 钩子:NF_INET_LOCAL_OUT
return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
net, sk, skb, NULL, skb_dst(skb)->dev,
ip_output);
}
// 4. 邻居子系统(ARP)
// net/core/neighbour.c
static inline int neigh_output(struct neighbour *n, struct sk_buff *skb)
{
// 查找 MAC 地址
if (!neigh_event_send(n, skb))
return n->output(n, skb);
return -EINVAL;
}
// 5. 设备发送
// net/core/dev.c
int dev_queue_xmit(struct sk_buff *skb)
{
// 选择发送队列
txq = netdev_pick_tx(dev, skb, accel_priv);
// 调用驱动发送
return __dev_queue_xmit(skb, accel_priv);
}
// 6. 网卡驱动发送
// drivers/net/ethernet/.../driver.c
static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
// 映射 DMA
dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
// 配置 TX 描述符
tx_desc->addr = dma_addr;
tx_desc->len = skb->len;
// 通知硬件发送
writel(tx_desc_ptr, hw_reg + TX_DESC_TAIL);
return NETDEV_TX_OK;
}
// 7. DMA 完成(硬件中断)
// 释放 sk_buff
dev_kfree_skb_irq(skb);
Socket 实现
Socket 数据结构
// include/linux/net.h
struct socket {
socket_state state; // socket 状态
short type; // socket 类型(SOCK_STREAM 等)
unsigned long flags;
struct file *file; // 关联的文件
struct sock *sk; // 传输层 socket
const struct proto_ops *ops; // 协议操作
};
// include/net/sock.h
struct sock {
struct sock_common __sk_common;
// 接收队列
struct sk_buff_head sk_receive_queue;
// 发送队列
struct sk_buff_head sk_write_queue;
// 缓冲区大小
int sk_rcvbuf;
int sk_sndbuf;
// 协议操作
struct proto *sk_prot;
// socket 选项
unsigned long sk_flags;
// 等待队列
wait_queue_head_t *sk_sleep;
// 定时器
struct timer_list sk_timer;
};
// include/net/inet_sock.h
struct inet_sock {
struct sock sk;
// 地址信息
__be32 inet_saddr; // 源地址
__be32 inet_daddr; // 目标地址
__be16 inet_sport; // 源端口
__be16 inet_dport; // 目标端口
__u16 inet_id; // IP ID
// TTL
__u8 uc_ttl;
// TOS
__u8 tos;
};
TCP 状态机
// include/net/tcp_states.h
enum {
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING,
};
TCP 连接建立(三次握手):
Client Server
│ │
│──── SYN (seq=x) ──────────────>│ SYN_SENT
│ │ LISTEN → SYN_RECV
│<─── SYN+ACK (seq=y,ack=x+1) ──│
│ │
│──── ACK (seq=x+1,ack=y+1) ────>│ ESTABLISHED
│ │ SYN_RECV → ESTABLISHED
TCP 连接关闭(四次挥手):
Client Server
│ │ ESTABLISHED
│──── FIN (seq=x) ──────────────>│
│ FIN_WAIT1 │ CLOSE_WAIT
│<─── ACK (ack=x+1) ─────────────│
│ FIN_WAIT2 │
│ │
│<─── FIN (seq=y) ───────────────│ LAST_ACK
│ TIME_WAIT │
│──── ACK (ack=y+1) ─────────────>│ CLOSED
│ (等待 2MSL) │
│ CLOSED │
网络调优
Socket 缓冲区
# 查看默认缓冲区大小
sysctl net.core.rmem_default
sysctl net.core.wmem_default
# 查看最大缓冲区大小
sysctl net.core.rmem_max
sysctl net.core.wmem_max
# 调整缓冲区
sysctl -w net.core.rmem_default=262144
sysctl -w net.core.wmem_default=262144
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
# TCP 缓冲区自动调整
sysctl net.ipv4.tcp_rmem
sysctl net.ipv4.tcp_wmem
# 格式:min default max
# 设置 TCP 缓冲区
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
连接队列
# TCP 连接队列长度
sysctl net.core.somaxconn # listen backlog
sysctl net.ipv4.tcp_max_syn_backlog # SYN 队列
# 调整队列大小
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
TCP 参数优化
# TCP Fast Open
sysctl -w net.ipv4.tcp_fastopen=3 # 1: client, 2: server, 3: both
# TCP 窗口缩放
sysctl -w net.ipv4.tcp_window_scaling=1
# TCP 时间戳
sysctl -w net.ipv4.tcp_timestamps=1
# TCP SACK
sysctl -w net.ipv4.tcp_sack=1
# TCP 拥塞控制算法
sysctl net.ipv4.tcp_congestion_control
sysctl -w net.ipv4.tcp_congestion_control=bbr # 使用 BBR
# 可用的拥塞控制算法
sysctl net.ipv4.tcp_available_congestion_control
# TIME_WAIT 复用
sysctl -w net.ipv4.tcp_tw_reuse=1
# FIN_WAIT2 超时
sysctl -w net.ipv4.tcp_fin_timeout=30
网络监控
查看网络统计
# 全面的网络统计
netstat -s
ss -s
# TCP 统计
cat /proc/net/snmp | grep Tcp
# 查看连接状态分布
ss -tan state time-wait | wc -l
ss -tan state established | wc -l
# 查看 socket 内存使用
cat /proc/net/sockstat
实时监控脚本
#!/bin/bash
# network-monitor.sh
while true; do
clear
echo "=== 网络监控 $(date) ==="
echo ""
echo "--- TCP 连接状态 ---"
ss -tan | awk 'NR>1 {state[$1]++} END {for(s in state) printf "%-15s %d\n", s, state[s]}'
echo ""
echo "--- 网络接口流量 ---"
cat /proc/net/dev | awk 'NR>2 {printf "%-10s RX: %10d MB TX: %10d MB\n", $1, $2/1024/1024, $10/1024/1024}'
echo ""
echo "--- TCP 重传 ---"
cat /proc/net/snmp | grep Tcp | awk 'NR==2 {print "RetransSegs: " $13}'
echo ""
sleep 2
done
下一步:学习 Netfilter 和 iptables 章节。