土法炼钢兴趣小组的算法知识备份

【Linux 网络子系统深度拆解】邻居子系统与 ARP:L2 地址解析的内核实现

文章导航

分类入口
linuxnetworking
标签入口
#neighbor#arp#linux-kernel#ndp#nud-state#neigh-table#proxy-arp#gc#l2-resolution#bpftrace

目录

前面我们拆解了 IP 层的路由查找与转发——ip_route_output_flow() 找到了下一跳 IP 地址和出接口。但 IP 层只管 L3 寻址,实际发帧需要 L2 目的 MAC 地址。谁来做这个翻译?

答案是邻居子系统(neighbour subsystem)——一个比 ARP 更通用的框架。ARP 只是 IPv4 在以太网上的一种邻居发现协议;IPv6 用 NDP(Neighbor Discovery Protocol),InfiniBand 用自己的地址解析。邻居子系统把”根据 L3 地址查找 L2 地址”这个动作抽象为统一的接口,不同协议注册不同的解析回调。

在发包路径中,当 ip_finish_output2() 需要发出一个帧时:

ip_finish_output2(net, sk, skb)
    ├── neigh = ip_neigh_for_gw(rt, skb, &is_v6gw)
    │   └── 从路由缓存取或创建邻居对象
    └── neigh_output(neigh, skb, skip_cache)
        ├── NUD_CONNECTED + 缓存有效?
        │   └── neigh_hh_output(hh, skb)  快路径:直接拷贝缓存的 L2 头
        └── 否则 → neigh->output(neigh, skb)
            └── neigh_resolve_output() → 发 ARP 请求,skb 排队等待

本文拆解邻居子系统的三个核心问题:邻居缓存的数据结构与查找、NUD 状态机的完整转换、以及 ARP/NDP 协议的内核实现。

一、struct neighbour:邻居缓存条目

每个已知的”下一跳”在内核中对应一个 struct neighbour 对象(include/net/neighbour.h:137):

struct neighbour {
    struct neighbour __rcu *next;  /* 哈希链表指针 */
    struct neigh_table *tbl;       /* 所属表(arp_tbl / nd_tbl) */
    struct neigh_parms *parms;     /* 协议参数(超时、重试次数等) */

    /* 时间戳 */
    unsigned long confirmed;       /* 最后确认时间(上层反馈可达) */
    unsigned long updated;         /* 最后更新时间 */
    unsigned long used;            /* 最后使用时间 */

    /* NUD 状态机 */
    u8  nud_state;                 /* 当前状态(NUD_REACHABLE 等) */
    atomic_t probes;               /* 已发送的探测次数 */
    struct timer_list timer;       /* 状态超时定时器 */

    /* 未解析队列 */
    struct sk_buff_head arp_queue; /* 等待地址解析的 skb 队列 */
    unsigned int arp_queue_len_bytes;

    /* 硬件地址 */
    seqlock_t ha_lock;
    unsigned char ha[];            /* L2 地址(变长,以太网 6 字节) */

    /* L2 头缓存 */
    struct hh_cache hh;            /* 完整的 L2 头缓存(含 EtherType) */

    /* 发送分发 */
    int (*output)(struct neighbour *, struct sk_buff *);
    const struct neigh_ops *ops;   /* 协议操作集 */

    /* 设备引用 */
    struct net_device *dev;

    /* 查找键(变长) */
    u8  primary_key[];             /* IPv4: 4 字节 IP;IPv6: 16 字节 */
};

关键设计要点:

变长对象ha[]primary_key[] 都是变长的——以太网 ha 是 6 字节,IPv4 primary_key 是 4 字节,IPv6 是 16 字节。neigh_table->entry_size 在表初始化时计算好总大小。

L2 头缓存(hh_cache)。当邻居进入 NUD_CONNECTED 状态后,hh_cache 存储完整的以太网头(14 字节:目的 MAC + 源 MAC + EtherType)。后续发包直接 memcpy 这个缓存,不需要每次构造——这是邻居子系统最重要的性能优化。

output 函数指针。根据 NUD 状态动态切换: - NUD_CONNECTEDoutput = neigh_connected_output(快路径) - 其他状态:output = neigh_resolve_output(需要解析)

二、neigh_table:邻居缓存表

表结构

struct neigh_tableneighbour.h:200)是邻居缓存的全局管理器。IPv4 的 ARP 表是 arp_tblinclude/net/arp.h:11),IPv6 的 NDP 表是 nd_tblinclude/net/ndisc.h:79):

struct neigh_table {
    int family;                /* AF_INET / AF_INET6 */
    unsigned int entry_size;   /* sizeof(neighbour) + key_len + ha_len */
    unsigned int key_len;      /* IPv4: 4, IPv6: 16 */

    /* 协议回调 */
    __u32 (*hash)(...);        /* 哈希函数 */
    bool  (*key_eq)(...);      /* 键比较 */
    int   (*constructor)(...); /* 新建条目初始化——ARP: arp_constructor */

    /* GC 参数 */
    int gc_thresh1;            /* 软下限——低于此不触发 GC */
    int gc_thresh2;            /* 软上限——开始周期性 GC */
    int gc_thresh3;            /* 硬上限——强制立即 GC */

    /* 哈希表 */
    struct neigh_hash_table __rcu *nht;  /* RCU 保护的哈希表 */

    /* 统计 */
    struct neigh_statistics __percpu *stats;

    /* Proxy ARP */
    struct pneigh_entry **phash_buckets;
    struct sk_buff_head   proxy_queue;
    struct timer_list     proxy_timer;
};

哈希查找

neigh_hash_tableneighbour.h:192)使用开链哈希:

struct neigh_hash_table {
    struct neighbour __rcu **hash_buckets;
    unsigned int            hash_shift;
    __u32                   hash_rnd[4];  /* 4 个随机种子 */
};

查找路径(___neigh_lookup_noref()neighbour.h:293):

hash_val = hash(pkey, dev, hash_rnd) >> (32 - hash_shift);
for (n = rcu_dereference(nht->hash_buckets[hash_val]);
     n != NULL;
     n = rcu_dereference(n->next)) {
    if (n->dev == dev && key_eq(n, pkey))
        return n;
}

哈希函数同时考虑 L3 地址网络设备——同一个 IP 在不同接口上可能对应不同的 MAC。

IPv4 的快速查找入口是 __ipv4_neigh_lookup()include/net/arp.h:22),对环回和点对点接口特殊处理(key 强制为 INADDR_ANY)。

三、NUD 状态机:邻居可达性检测

NUD(Neighbor Unreachability Detection)是邻居子系统的核心——它用状态机跟踪每个邻居的可达性。状态定义在 include/uapi/linux/neighbour.h:62

                        ┌──────────────┐
                        │ NUD_PERMANENT│ ← ip neigh add ... nud permanent
                        │ NUD_NOARP    │ ← 无需解析(环回/点对点)
                        └──────────────┘
                              (静态,不参与状态机)

       ┌─────────────────────────────────────────────────────┐
       │                NUD 动态状态机                          │
       │                                                     │
       │  首次发包                                             │
       │  ──────→ NUD_INCOMPLETE                              │
       │           │ 发 ARP Request/NS                        │
       │           │ skb 排入 arp_queue                       │
       │           │                                          │
       │           ├── 收到 Reply → NUD_REACHABLE             │
       │           │                  │ 启动 reachable_time 定时器│
       │           │                  │                       │
       │           │                  └── 超时 → NUD_STALE    │
       │           │                              │            │
       │           │                              ├── 有新发包  │
       │           │                              │   → NUD_DELAY│
       │           │                              │     │      │
       │           │                              │     └── delay_probe_time 超时│
       │           │                              │         → NUD_PROBE│
       │           │                              │           │ │
       │           │                              │   确认可达 ←┘│
       │           │                              │   → NUD_REACHABLE│
       │           │                              │            │
       │           └── 重试耗尽 → NUD_FAILED      ←── 重试耗尽 │
       │                          │ 丢弃 arp_queue 中的包     │
       │                          │ 后续发包到此邻居直接丢弃   │
       └──────────────────────────┴────────────────────────────┘

状态组合

#define NUD_IN_TIMER  (NUD_INCOMPLETE | NUD_REACHABLE | NUD_DELAY | NUD_PROBE)
#define NUD_VALID     (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE |
                       NUD_PROBE | NUD_STALE | NUD_DELAY)
#define NUD_CONNECTED (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)

NUD_CONNECTED 是快路径的门票——只有在这三个状态下,neigh_output() 才会走缓存的 L2 头路径。

状态转换触发

转换 触发条件 内核函数
→ NUD_INCOMPLETE 首次发包,无缓存 neigh_event_send()
INCOMPLETE → REACHABLE 收到 ARP Reply/NA neigh_update()
REACHABLE → STALE reachable_time 超时 neigh_timer_handler()
STALE → DELAY 有新 skb 要发 neigh_event_send()
DELAY → PROBE delay_probe_time 超时 neigh_timer_handler()
PROBE → REACHABLE 收到上层确认(TCP ACK) neigh_confirm()
PROBE → FAILED 重试次数耗尽 neigh_timer_handler()
* → REACHABLE TCP ACK 确认可达 neigh_confirm()

neigh_confirm() 的特殊作用:TCP 收到 ACK 时调用 dst_confirm_neigh(),间接调用 neigh_confirm() 更新 confirmed 时间戳。这意味着活跃的 TCP 连接会持续刷新邻居的可达性——不需要额外发 ARP 探测。

四、ARP 协议实现

ARP 请求发送

当邻居处于 NUD_INCOMPLETENUD_PROBE 状态时,neigh_resolve_output() 调用 ARP 发送逻辑:

neigh_resolve_output(neigh, skb)
    ├── neigh_event_send(neigh, skb)
    │   └── nud_state 不在 NUD_CONNECTED + NUD_DELAY?
    │       └── __neigh_event_send(neigh, skb)
    │           ├── NUD_NONE → NUD_INCOMPLETE
    │           │   └── neigh->ops->solicit(neigh, skb)
    │           │       → arp_solicit(neigh, skb)
    │           │           └── arp_send(ARPOP_REQUEST, ETH_P_ARP,
    │           │                        target_ip, dev, source_ip,
    │           │                        NULL, dev->dev_addr, NULL)
    │           │               → arp_create() 构造 ARP 包
    │           │               → arp_xmit() 发出
    │           └── skb 入 arp_queue 等待解析
    └── 如果已解析 → 填充 L2 头并发出

arp_send()include/net/arp.h:62)构造标准 ARP 请求包:

目的 MAC 使用广播地址 ff:ff:ff:ff:ff:ff

ARP 响应接收

对端回复 ARP Reply 后的处理链:

网卡收包 → netif_receive_skb()
    ↓ ETH_P_ARP
arp_rcv(skb, dev, pt, orig_dev)
    ↓ NF_INET_ARP_IN(Netfilter ARP 钩子)
arp_process(net, sk, skb)
    ├── 解析 ARP 头:ar_op, ar_sha, ar_sip, ar_tha, ar_tip
    │
    ├── ARPOP_REQUEST?
    │   ├── ar_tip 是本机 IP?
    │   │   └── 发 ARPOP_REPLY(源 IP 对应的 MAC)
    │   ├── Proxy ARP 开启?
    │   │   └── pneigh_lookup() → 代理响应
    │   └── 更新发送端的邻居缓存
    │       → neigh_update(n, sha, NUD_STALE, ...)
    │
    └── ARPOP_REPLY?
        └── neigh = neigh_lookup(&arp_tbl, &sip, dev)
            ├── 找到 → neigh_update(n, sha, NUD_REACHABLE, ...)
            │   ├── 更新 ha[](MAC 地址)
            │   ├── 状态 → NUD_REACHABLE
            │   ├── 刷新 hh_cache(L2 头缓存)
            │   └── 发出 arp_queue 中排队的 skb
            └── 未找到 → 丢弃

Gratuitous ARP

Gratuitous ARP(免费 ARP)是源 IP = 目的 IP 的 ARP 请求——用于宣告自己的 MAC 地址变更或检测 IP 冲突。内核在 IP 地址配置时发送:

# 手动触发 Gratuitous ARP
arping -U -I eth0 10.0.0.1
arping -A -I eth0 10.0.0.1  # ARP Reply 形式

五、NDP:IPv6 邻居发现

IPv6 用 ICMPv6 的 Neighbor Solicitation(NS,类型 135)和 Neighbor Advertisement(NA,类型 136)替代 ARP。内核实现在 nd_tblinclude/net/ndisc.h:79)中。

NS/NA 函数

/* include/net/ndisc.h:448-465 */
void ndisc_send_ns(dev, solicit, daddr, saddr, match);  /* 发送 NS */
void ndisc_send_na(dev, daddr, target, router, solicited, override, inc_opt); /* 发送 NA */
enum skb_drop_reason ndisc_rcv(struct sk_buff *skb);     /* 接收处理 */

与 ARP 的关键区别

特性 ARP(IPv4) NDP(IPv6)
协议层 L2(独立 EtherType 0x0806) L3(ICMPv6,EtherType 0x86DD)
地址解析 广播 ARP Request 组播 NS(Solicited-Node 组播组)
无状态地址检测 Gratuitous ARP DAD(Duplicate Address Detection)
路由器发现 无(依赖 DHCP) RS/RA(Router Solicitation/Advertisement)
Redirect ICMP Redirect ICMPv6 Redirect
Proxy Proxy ARP Proxy NDP

NDP 使用 Solicited-Node 组播地址(ff02::1:ffXX:XXXX,基于目标 IP 后 24 位)代替广播,大幅减少了网络上不相关主机的中断。

NDP 定时器

/* include/net/ndisc.h:50-51 */
#define ND_REACHABLE_TIME  (30*HZ)   /* 30 秒 */
#define ND_RETRANS_TIMER   HZ        /* 1 秒 */

六、发送路径的快路径与慢路径

neigh_output:决策入口

neigh_output()neighbour.h:529)是发包路径中邻居子系统的入口:

static inline int neigh_output(struct neighbour *n,
                               struct sk_buff *skb,
                               bool skip_cache)
{
    const struct hh_cache *hh = &n->hh;
    if (!skip_cache && (n->nud_state & NUD_CONNECTED) && hh->hh_len)
        return neigh_hh_output(hh, skb);  /* 快路径 */
    else
        return n->output(n, skb);          /* 慢路径 */
}

快路径:neigh_hh_output

当邻居在 NUD_CONNECTED 状态且 hh_cache 有效时,neigh_hh_output() 直接用 memcpy 把缓存的 L2 头(以太网头 14 字节)拷贝到 skb 前面:

static inline int neigh_hh_output(const struct hh_cache *hh,
                                   struct sk_buff *skb)
{
    unsigned int hh_alen = 0;
    unsigned int seq;
    unsigned int hh_len;

    do {
        seq = read_seqbegin(&hh->hh_lock);
        hh_len = READ_ONCE(hh->hh_len);
        if (likely(hh_len <= HH_DATA_ALIGN(hh_len)))
            memcpy(skb->data - HH_DATA_OFF(hh_len),
                   hh->hh_data, HH_DATA_ALIGN(hh_len));
    } while (read_seqretry(&hh->hh_lock, seq));

    skb_push(skb, hh_len);
    return dev_queue_xmit(skb);
}

这个 seqlock 保护的 memcpy 是整个发包路径中最频繁的操作之一——在正常传输中,绝大多数包走这条路径。无锁竞争,无函数调用,一个 memcpy + dev_queue_xmit() 就完成了 L2 封装。

慢路径:neigh_resolve_output

如果邻居不在 NUD_CONNECTED 状态(新建、超时、探测中),走 neigh_resolve_output()

neigh_resolve_output(neigh, skb)
    ├── __neigh_event_send(neigh, skb)
    │   ├── NUD_INCOMPLETE:skb 入 arp_queue,等 ARP 回复
    │   ├── NUD_STALE → NUD_DELAY:设定延迟探测定时器
    │   └── 已解析:继续
    ├── neigh_hh_init(neigh, dst)
    │   └── 如果 hh_cache 无效 → 初始化
    ├── dev_hard_header(skb, dev, ntohs(skb->protocol),
    │                    neigh->ha, NULL, skb->len)
    │   └── 根据 ha[] 构造 L2 头
    └── dev_queue_xmit(skb)

arp_queue 的长度上限由 neigh_parms->QUEUE_LEN_BYTES 控制(默认 ~64KB)。超过限制时,最老的 skb 被丢弃。

七、Proxy ARP

Proxy ARP 让一台主机代替另一台主机回复 ARP 请求——常用于路由器连接不同子网但不做 NAT 的场景。

内核实现

arp_process() 收到 ARP Request
    ├── ar_tip 不是本机 IP
    ├── dev->flags & IFF_PROMISC 或
    │   pneigh_lookup(&arp_tbl, net, &tip, dev, 0) 找到代理条目
    └── pneigh_enqueue(&arp_tbl, parms, skb)
        └── skb 入 proxy_queue
        └── proxy_timer 触发后
            └── arp_tbl.proxy_redo(skb)
                └── 发 ARP Reply(用本机 MAC 回复)

pneigh_enqueue() 有一个 proxy_delay 延迟——避免代理响应先于真正的目标主机到达。

# 启用接口级 Proxy ARP
echo 1 > /proc/sys/net/ipv4/conf/eth0/proxy_arp

# 添加精确代理条目
ip neigh add proxy 10.0.0.100 dev eth0

八、垃圾回收

邻居缓存条目会持续增长——每个访问过的 IP 都留下一条。GC 通过三级阈值控制:

entries < gc_thresh1 (默认 128):不触发 GC
gc_thresh1 ≤ entries < gc_thresh2 (默认 512):周期性 GC(清理过期条目)
gc_thresh2 ≤ entries < gc_thresh3 (默认 1024):激进 GC
entries ≥ gc_thresh3:强制立即清理,新建条目可能失败

neigh_table->gc_work 是周期性 GC 的工作队列。neigh_forced_gc() 在条目数逼近 gc_thresh3 时强制执行。

大规模网络调优

默认阈值对大型数据中心或多租户网络过小——一个 /16 子网有 65534 个可能的邻居:

# 查看当前邻居表大小
ip neigh show | wc -l

# 调大 GC 阈值
sysctl -w net.ipv4.neigh.default.gc_thresh1=4096
sysctl -w net.ipv4.neigh.default.gc_thresh2=8192
sysctl -w net.ipv4.neigh.default.gc_thresh3=16384

# 对特定接口调参
sysctl -w net.ipv4.neigh.eth0.gc_stale_time=120

九、可观测性实战

邻居表统计

# 查看 ARP 表统计——缓存命中率、GC 次数、解析失败
cat /proc/net/stat/arp_cache
# entries  allocs  destroys  hash_grows  lookups  hits  ...
#   res_failed  rcv_probes_mcast  rcv_probes_ucast  ...
#   periodic_gc_runs  forced_gc_runs  unres_discards  table_fulls

# 查看当前邻居表
ip -s neigh show

NUD 状态变化追踪

# 追踪邻居状态变化——neigh_update 是状态转换的核心入口
bpftrace -e '
kprobe:neigh_update {
    $neigh = (struct neighbour *)arg0;
    $new_state = arg2;
    $dev = $neigh->dev;
    printf("%s dev=%s old_state=0x%x new_state=0x%x\n",
           comm,
           $dev->name,
           $neigh->nud_state,
           $new_state);
}'

ARP 解析延迟

# 测量 ARP 解析耗时——从 INCOMPLETE 到 REACHABLE
bpftrace -e '
kprobe:neigh_resolve_output {
    $neigh = (struct neighbour *)arg0;
    if ($neigh->nud_state == 1) {  /* NUD_INCOMPLETE */
        @resolve_start[$neigh] = nsecs;
    }
}
kprobe:neigh_update {
    $neigh = (struct neighbour *)arg0;
    $new_state = arg2;
    if ($new_state == 2 && @resolve_start[$neigh]) {  /* NUD_REACHABLE */
        @resolve_ms = hist((nsecs - @resolve_start[$neigh]) / 1000000);
        delete(@resolve_start[$neigh]);
    }
}'

GC 压力监控

# 追踪强制 GC——频繁触发说明阈值太小
bpftrace -e '
kprobe:neigh_forced_gc {
    printf("forced_gc triggered! time=%lu\n", nsecs);
    @forced_gc = count();
}
interval:s:60 { print(@forced_gc); clear(@forced_gc); }'

邻居表满检测

# 监控 "neighbour table overflow" 内核日志
bpftrace -e '
kprobe:__neigh_create {
    @create_attempts = count();
}
kretprobe:__neigh_create /retval < 0/ {
    printf("neigh_create FAILED err=%d\n", retval);
    @create_failures = count();
}'

十、关键参数速查

参数 默认值 内核对应 调优建议
neigh.default.gc_thresh1 128 neigh_table->gc_thresh1 大网络增大到 4096+
neigh.default.gc_thresh2 512 neigh_table->gc_thresh2 大网络增大到 8192+
neigh.default.gc_thresh3 1024 neigh_table->gc_thresh3 大网络增大到 16384+
neigh.default.gc_stale_time 60 STALE 条目存活时间(秒) 稳定网络可增大
neigh.default.base_reachable_time_ms 30000 NUD_REACHABLE 持续时间 稳定网络可增大
neigh.default.delay_first_probe_time 5 NUD_DELAY → NUD_PROBE 延迟(秒) 通常不需调整
neigh.default.retrans_time_ms 1000 探测重传间隔(毫秒) 通常不需调整
neigh.default.ucast_solicit 3 单播探测次数 不稳定网络可增大
neigh.default.mcast_solicit 3 组播探测次数 不稳定网络可增大
neigh.default.unres_qlen_bytes 65536 未解析队列字节上限 突发大包场景可增大
conf.{iface}.proxy_arp 0 Proxy ARP 开关 按需开启
conf.{iface}.arp_announce 0 ARP 源 IP 选择策略 多 IP 主机建议设为 2
conf.{iface}.arp_filter 0 ARP 响应过滤 多网卡建议开启

参考文献

  1. Linux 内核源码,include/net/neighbour.h,6.8(struct neighbour 定义于第 137 行,struct neigh_table 于第 200 行)
  2. Linux 内核源码,include/net/arp.h,6.8(arp_tbl 于第 11 行,arp_send 于第 62 行)
  3. Linux 内核源码,include/net/ndisc.h,6.8(nd_tbl 于第 79 行,ndisc_send_ns 于第 452 行)
  4. Linux 内核源码,include/uapi/linux/neighbour.h,6.8(NUD 状态常量于第 62 行)
  5. RFC 826, “An Ethernet Address Resolution Protocol”, D. Plummer, 1982
  6. RFC 4861, “Neighbor Discovery for IP version 6 (IPv6)”, T. Narten et al., 2007
  7. Linux 内核文档,Documentation/networking/ip-sysctl.rst

上一篇Socket 层内核实现:从 VFS 到协议栈的桥梁

下一篇路由子系统深度拆解:FIB、策略路由与路由缓存

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-20 · linux / networking

【Linux 网络子系统深度拆解】Socket 层内核实现:从 VFS 到协议栈的桥梁

你调用 socket(AF_INET, SOCK_STREAM, 0) 创建一个 TCP 连接,底层发生了什么?内核分配了两个核心对象——VFS 层的 struct socket 和协议层的 struct sock,通过 proto_ops 和 proto 两张分发表,把文件系统语义的 read/write 翻译成协议语义的 tcp_sendmsg/tcp_recvmsg。本文从 Linux 6.6 内核源码拆解 socket 创建、双层分发、SO_REUSEPORT 多核分发、epoll 集成的完整实现。

2026-04-20 · linux / networking

【Linux 网络子系统深度拆解】UDP 内核实现与 socket lookup 优化

UDP 简单?在内核中它一点都不简单。双哈希表 socket 查找、SO_REUSEPORT 多核分发、Early Demux 路由缓存、UDP GRO 聚合、reader_queue 无锁读、forward allocation 内存管理、UDP 封装(ESP/L2TP/VXLAN)——本文从 Linux 6.6 内核源码拆解 UDP 的每一个优化细节。

2026-04-20 · linux / networking

【Linux 网络子系统深度拆解】TCP 内核实现(下):数据传输与拥塞控制

tcp_sendmsg 把用户数据拷到 sk_buff 就完事了?远没有。后面还有 Nagle 合并、TSQ 限流、cwnd/rwnd 双窗口门控、RACK-TLP 丢包检测、拥塞状态机五态跳转、sk_pacing_rate 软件限速。本文从 Linux 6.6 内核源码拆解 TCP 数据传输的完整路径——从 send() 到 ACK 处理——以及拥塞控制框架 tcp_congestion_ops 的可插拔架构。

2026-04-20 · linux / networking

【Linux 网络子系统深度拆解】TCP 内核实现(上):连接管理与状态机

TCP 连接在内核中不只是一个状态机——它是一组精心设计的数据结构和队列。本文从 Linux 6.6 内核源码出发,拆解 TCP 连接建立的 SYN Queue / Accept Queue 二级队列模型、request_sock 半连接对象、tcp_sock 全连接对象、SYN Cookie 无状态防御、TCP Fast Open 零 RTT 机制、inet_timewait_sock 轻量级 TIME_WAIT 实现,以及完整的 TCP 状态机在内核中的真实转换路径。


By .