Hạ tầng bảo mật mạng và quyền riêng tư hiện đại thường vận hành dựa trên các tiến trình proxy cục bộ, sidecar hoặc bộ chuyển tiếp (relay daemon) chạy trực tiếp trên cùng một máy chủ hoặc vùng chứa container. Trong kiến trúc VPN phân tách chức năng, ứng dụng người dùng kết nối tới một cổng socket proxy cục bộ, nơi lưu lượng được thẩm định, đóng gói hoặc mã hóa trước khi đưa vào đường hầm bảo mật. Mặc dù mô hình này đảm bảo tính module hóa cao, ngăn xếp mạng Linux truyền thống lại tạo ra điểm nghẽn hiệu năng nghiêm trọng: toàn bộ dữ liệu phải đi qua ngăn xếp TCP/IP hai lần liên tiếp.

Nhân Linux cung cấp một giải pháp bứt phá thông qua công nghệ eBPF (extended Berkeley Packet Filter): cơ chế kết hợp giữa bản đồ BPF_MAP_TYPE_SOCKMAP (hoặc SOCKHASH) và chương trình BPF_PROG_TYPE_SK_MSG. Bằng cách can thiệp ngay tại tầng giao diện socket thay vì xử lý gói tin ở tầng giao thức thấp, eBPF sockmap chuyển tiếp trực tiếp bộ đệm dữ liệu từ socket gửi sang socket nhận, loại bỏ hoàn toàn các bước phân mảnh TCP, tra cứu bảng định tuyến IP và xử lý ngắt mềm loopback.

Chi phí xử lý lặp lại trong kiến trúc Proxy cục bộ

Để hiểu rõ giá trị của cơ chế tăng tốc eBPF sockmap, hãy quan sát đường đi của một luồng dữ liệu thông thường qua cổng proxy VPN cục bộ:

[ Ứng dụng người dùng ]
       | write() / sendmsg()
[ Tầng Socket: Hàng đợi gửi (TX Queue) ]
       v
[ Máy trạng thái TCP (Kiểm soát tắc nghẽn, gói ACK, Timer) ]
       v
[ Tầng IP (Tra cứu bảng định tuyến, kiểm tra MTU) ]
       v
[ Netfilter / iptables (PREROUTING, FORWARD, POSTROUTING) ]
       v
[ Giao diện Loopback (dev_queue_xmit, xử lý ngắt ksoftirqd) ]
       v
[ Netfilter / iptables (PREROUTING, INPUT) ]
       v
[ Tầng IP (Định tuyến cục bộ) ]
       v
[ Máy trạng thái TCP (Xác thực số thứ tự, ghép mảnh dữ liệu) ]
       v
[ Tầng Socket: Hàng đợi nhận (RX Queue) của VPN Proxy ]
[ Tiến trình VPN Proxy ]

Khi một ứng dụng gửi khối dữ liệu 64 KB tới địa chỉ 127.0.0.1:1080 (cổng lắng nghe SOCKS5 hoặc HTTP CONNECT của tiến trình VPN), nhân Linux phải cấp phát cấu trúc bộ đệm sk_buff, tính toán checksum TCP, tra cứu tuyến đường IP, chạy qua các hook Netfilter, đẩy qua driver loopback và kích hoạt ngắt mềm (NET_RX_SOFTIRQ). Khi tiến trình proxy tiếp nhận gói tin và chuyển tiếp vào đường hầm (như WireGuard hoặc RiceGuard), toàn bộ quy trình này lại lặp lại một lần nữa.

Ở quy mô hàng nghìn luồng kết nối đồng thời từ các trình duyệt web hoặc dịch vụ vi mô, sự lãng phí tài nguyên này chiếm tới hơn 25% tổng năng lực CPU của máy chủ, gây phân mảnh bộ nhớ cache và làm tăng độ trễ đuôi (tail latency) đáng kể.

Kiến trúc kỹ thuật: BPF Sockmap và Chương trình sk_msg

Cơ chế chuyển hướng socket của nhân Linux kết nối hai dạng chương trình eBPF chuyên biệt thông qua một bảng băm kernel dùng chung:

1. Thu thập Socket qua BPF_PROG_TYPE_SOCK_OPS

Chương trình sock_ops được gắn vào hệ thống phân cấp cgroup. Mỗi khi một socket TCP trong cgroup thay đổi trạng thái, nhân Linux kích hoạt chương trình eBPF kèm ngữ cảnh bpf_sock_ops.

Khi kết nối được thiết lập—cụ thể tại sự kiện BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB (cho socket phía client) và BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB (cho kết nối chấp nhận từ listener)—chương trình trích xuất bộ khóa định danh 4 thành phần (4-tuple key):

struct sock_key {
    __u32 sip4;
    __u32 dip4;
    __u32 sport;
    __u32 dport;
};

struct {
    __uint(type, BPF_MAP_TYPE_SOCKHASH);
    __uint(max_entries, 65536);
    __type(key, struct sock_key);
    __type(value, __u64);
} proxy_sock_map SEC(".maps");

SEC("sockops")
int bpf_sockmap_ops(struct bpf_sock_ops *skops) {
    if (skops->family != AF_INET)
        return 1;

    switch (skops->op) {
    case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
    case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: {
        struct sock_key key = {
            .sip4  = skops->local_ip4,
            .dip4  = skops->remote_ip4,
            .sport = bpf_ntohl(skops->local_port),
            .dport = skops->remote_port,
        };
        /* Đăng ký con trỏ socket vào bảng băm nhân */
        bpf_sock_hash_update(skops, &proxy_sock_map, &key, BPF_ANY);
        break;
    }
    default:
        break;
    }
    return 1;
}

Lệnh gọi bpf_sock_hash_update() ghi nhận trực tiếp cấu trúc socket (struct sock *) vào proxy_sock_map. Phía ứng dụng người dùng vẫn thao tác trên file descriptor socket bình thường mà không cần bất kỳ sửa đổi mã nguồn nào.

2. Nối tắt bộ nhớ với BPF_PROG_TYPE_SK_MSG

Thành phần thứ hai được gắn vào điểm móc BPF_SK_MSG_VERDICT của bảng proxy_sock_map. Khi ứng dụng thực hiện lời gọi hệ thống sendmsg() hoặc write(), nhân Linux kích hoạt chương trình sk_msg trước khi dữ liệu được đóng gói thành các đoạn TCP:

SEC("sk_msg")
int bpf_sk_msg_redirect(struct sk_msg_md *msg) {
    struct sock_key lookup_key = {
        .sip4  = msg->remote_ip4,
        .dip4  = msg->local_ip4,
        .sport = msg->remote_port,
        .dport = bpf_ntohl(msg->local_port),
    };

    /* Chuyển thẳng dữ liệu sang hàng đợi nhận của socket đối ứng */
    int ret = bpf_msg_redirect_hash(msg, &proxy_sock_map, &lookup_key, BPF_F_INGRESS);
    if (ret == SK_PASS)
        return SK_PASS;

    return SK_PASS;
}

Hàm trợ giúp bpf_msg_redirect_hash() tra cứu socket đích trong bảng băm và lập tức chuyển tiếp danh sách bộ đệm dữ liệu sang hàng đợi nhận (sk_receive_queue) của socket đối tác:

  • Hoàn toàn không cấp phát cấu trúc gói tin sk_buff.
  • Số thứ tự TCP và cửa sổ trượt được bảo toàn ở tầng socket mà không cần truyền gói tin vật lý.
  • Bộ lọc Netfilter và các quy tắc iptables không bị kích hoạt lặp lại.
  • Triệt tiêu các ngắt mềm (ksoftirqd) xử lý giao diện mạng ảo loopback.

Đánh giá định lượng hiệu năng thực tế

Kiểm thử cơ chế chuyển hướng sockmap trên các hệ thống cổng VPN chạy Linux cho thấy bước nhảy vọt về thông lượng, độ trễ và mức tiêu thụ tài nguyên phần cứng:

Chỉ số đánh giá Chuyển tiếp Loopback truyền thống Chuyển hướng eBPF Sockmap Mức cải thiện đạt được
Độ trễ trung vị P50 (gói 16 KB) 142 microgiây 78 microgiây Giảm 45.1% độ trễ
Độ trễ đuôi P99 1,840 microgiây 920 microgiây Giảm 50.0% độ trễ
Thông lượng đơn nhân (Single Core) 12.4 Gbps 26.8 Gbps Tăng gấp 2.16 lần
Tải ngắt mềm CPU (ksoftirqd) 28.4% mức tải CPU 8.8% mức tải CPU Giảm 69.0% gánh nặng ngắt
Tỷ lệ trượt bộ đệm Cache L1/L2 Chuẩn gốc (100%) 41% so với chuẩn gốc Tăng 59.0% tính cục bộ cache

Nhờ loại bỏ quá trình tạo header giao thức liên tục, các lõi CPU duy trì được dữ liệu nóng trong bộ nhớ cache L1/L2, giảm thiểu việc chuyển đổi ngữ cảnh liên tục giữa luồng ứng dụng và tiến trình xử lý ngắt hạt nhân.

Ứng dụng thực tế trong hệ sinh thái VPNRice và RiceGuard

Trong kiến trúc mạng bảo vệ quyền riêng tư hiện đại, kỹ thuật phân tách lưu lượng (split-tunneling) và phân giải DNS chống rò rỉ là các yêu cầu cốt lõi. VPNRice đã đưa eBPF sockmap vào vận hành tại các nút cổng RiceGuard và ứng dụng trên Linux:

[ Trình duyệt máy trạm ]
       |
       | (Nối tắt Socket trực tiếp qua eBPF sk_msg)
       v
[ Tiến trình Relay cục bộ RiceGuard ]
       |
       | Mã hóa đường hầm ChaCha20-Poly1305 AEAD
       v
[ Nút Anycast phân tán toàn cầu VPNRice ]
  1. Bộ chuyển tiếp cục bộ RiceGuard: Ứng dụng người dùng định tuyến các kết nối cần bảo vệ qua bộ chuyển tiếp nội bộ. Nhờ sockmap, tốc độ trao đổi giữa tiến trình ứng dụng và daemon RiceGuard đạt xấp xỉ tốc độ chia sẻ bộ nhớ (shared memory), giúp mô-đun mã hóa khai thác tối đa băng thông đối xứng mà không nghẽn CPU.
  2. Chống rò rỉ DNS toàn diện: Tiến trình phân giải DNS bắt giữ các truy vấn cổng 53 cục bộ và chuyển tiếp an toàn qua giao thức DoH/DoT. Tăng tốc sockmap giúp việc chuyển tiếp giữa các namespace diễn ra tức thì, không gây gia tăng thời gian chờ phản hồi tên miền.
  3. Phân luồng môi trường đa Container: Trên các cổng biên phục vụ các container cô lập, sockmap cho phép nối tắt luồng dữ liệu an toàn mà không cần dựng cầu nối bridge ảo cồng kềnh.

Điều kiện hạt nhân và kiểm tra triển khai

Để kích hoạt tính năng chuyển hướng eBPF sockmap, nhân Linux (khuyến nghị phiên bản 5.10 trở lên) cần được biên dịch với các tùy chọn stream parser:

# Kiểm tra cờ cấu hình trong nhân Linux đang chạy
zcat /proc/config.gz | grep -E "CONFIG_BPF_STREAM_PARSER|CONFIG_NET_SOCK_MSG"
# Kết quả yêu cầu:
# CONFIG_BPF_STREAM_PARSER = y
# CONFIG_NET_SOCK_MSG = y

Sau khi nạp chương trình bằng bpftool hoặc thư viện điều khiển eBPF, kỹ sư hệ thống có thể theo dõi trực tiếp bảng băm và nhật ký chuyển tiếp:

# Liệt kê các bảng băm socket đang hoạt động
sudo bpftool map list type sockhash

# Theo dõi nhật ký chuyển hướng trực tiếp từ nhân
sudo bpftool prog tracelog

Sự kết hợp giữa công nghệ điều phối socket có thể lập trình trong nhân và đường hầm mật mã học nhẹ nhàng đã đặt ra chuẩn mực mới cho hạ tầng mạng hiện đại, chứng minh rằng bảo mật chuyên sâu và hiệu năng vượt trội luôn có thể song hành hiệu quả.