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 章节。