Skip to content

IPv6 TCP/UDP 实现

1. 模块架构

1.1 功能概述

IPv6 下的 TCP 和 UDP 实现与 IPv4 基本相同,主要区别在于地址处理和伪头部校验和计算。

1.2 关键源文件

文件作用
net/ipv6/tcpv6.cIPv6 TCP
net/ipv6/udp.cIPv6 UDP
net/ipv6/inet6_connection.cIPv6 连接处理

2. IPv6 TCP

2.1 tcp_v6_connect()

c
// net/ipv6/tcpv6.c:280
int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
    struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
    struct inet_sock *inet = inet_sk(sk);
    struct inet6_sock *inet6 = inet6_sk(sk);

    // 1. 设置地址
    inet->inet_rcv_saddr = inet->inet_saddr = 0;
    inet6->pinet6->saddr = 0;

    // 2. 设置端口
    inet->inet_dport = usin->sin6_port;
    inet->inet_daddr = usin->sin6_addr;

    // 3. 路由查找
    sk->sk_state = TCP_SYN_SENT;
    err = ip6_route_connect(usk, fl6, addr_len, O_RDWR);

    // 4. 设置本地地址
    if (!sk->sk_rcv_saddr)
        sk->sk_rcv_saddr = fl6->saddr;
}

// net/ipv6/tcpv6.c:200
static struct sock *tcp_v6_syn_recv_sock(...)
{
    struct sock *sk;

    // 创建新的 child socket
    sk = tcp_create_openreq_child(sk, req, skb);
    if (sk) {
        inet6_sk(sk)->pinet6->saddr = fl6->saddr;
        inet6_sk(sk)->rcv_saddr = fl6->daddr;
    }
    return sk;
}

2.2 IPv6 TCP 伪头部

c
// IPv6 TCP 伪头部
struct ipv6_pseudohdr {
    struct in6_addr saddr;
    struct in6_addr daddr;
    __be32         length;
    __u8          next_hdr;
};

tcp_v6_check(struct tcphdr *th, int len, struct in6_addr *saddr,
             struct in6_addr *daddr)
{
    // 计算校验和
    return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, 0);
}

3. IPv6 UDP

3.1 udp_v6_sendmsg()

c
// net/ipv6/udp.c:850
int udp_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
    struct in6_addr *addr = &np->saddr;
    struct flowi6 fl6;

    // 1. 设置源/目的地址
    fl6.flowi6_proto = IPPROTO_UDP;
    fl6.fl6_sport = inet->inet_sport;
    fl6.fl6_dport = inet->inet_dport;

    // 2. 路由查找
    ip6_route_output(sock_net(sk), sk, &fl6);

    // 3. 发送
    return ip6_push_pending_frames(sk);
}

3.2 udp_v6_recvmsg()

c
// net/ipv6/udp.c:600
int udp_v6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
                   int noblock, int flags, int *addr_len)
{
    struct sk_buff *skb;
    unsigned int ulen;

    // 1. 接收数据
    skb = skb_recv_datagram(sk, flags, noblock, &err);

    // 2. 获取地址
    if (use_udp6_rx_csum) {
        // UDPv6 校验和验证
    }

    // 3. 复制数据
    err = skb_copy_datagram_msg(skb, offset, msg, len);

    // 4. 获取源地址
    ipv6_addr_copy(&sin6->sin6_addr, &ipv6_hdr(skb)->saddr);

    return len;
}

3.3 IPv6 UDP 校验和

c
// net/ipv6/udp.c:100
void udp6_set_csum(bool csum, struct sk_buff *skb,
                    struct in6_addr *saddr, struct in6_addr *daddr)
{
    struct udphdr *uh = udp_hdr(skb);

    if (csum) {
        uh->check = ~csum_ipv6_magic(saddr, daddr, skb->len,
                                     IPPROTO_UDP, 0);
    } else {
        uh->check = 0;
    }
}

4. IPv6 特殊处理

4.1 碎片头处理

c
// IPv6 发送时处理分片
// net/ipv6/ip6_output.c:1200
int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{
    struct frag_hdr *fh;
    unsigned int mtu;
    unsigned int hlen;
    unsigned int fragoff;
    unsigned int len;

    // 计算 MTU
    mtu = ip6_skb_dst_mtu(skb);

    // 如果不需要分片,直接发送
    if (skb->len <= mtu)
        return output(skb);

    // 分片
    // ... fragment code
}

4.2 扩展头处理

c
// IPv6 扩展头遍历
// net/ipv6/exthdrs.c
int ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp)
{
    u8 nexthdr = *nexthdrp;

    while (ipv6_ext_hdr(nexthdr)) {
        struct ipv6_opt_hdr *hdr;

        // 跳过扩展头
        hdr = (void *)(skb->data + start);
        start += ipv6_optlen(hdr);
        nexthdr = hdr->nexthdr;
    }

    *nexthdrp = nexthdr;
    return start;
}

5. 连接跟踪

5.1 ipv6_conntrack()

c
// net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
static int ipv6_help(struct sk_buff *skb, unsigned int xtnum)
{
    enum ip_conntrack_info ctinfo;
    struct nf_conn *ct;

    // 查连接跟踪表
    ct = nf_ct_get(skb, &ctinfo);
    if (!ct)
        return NF_ACCEPT;

    // 更新连接状态
    return nf_conntrack_in(skb, PF_INET6, &ctinfo);
}

6. Socket 选项

6.1 IPv6_ONLY

c
// net/ipv6/sockopts.c:150
int ipv6_only_sock(struct sock *sk)
{
    return inet_sk(sk)->inet_ipv6only;
}

6.2 IPV6_V6ONLY

c
// 防止 IPv4 映射地址绑定到 IPv6 socket
// 仅接受真正的 IPv6 连接

基于 VitePress 构建