You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于eBPF/XDP扩展程序检测OpenConnect流量并提取内层IP目的地址

扩展XDP程序实现OpenConnect流量解析与内层IP捕获

1. 识别OpenConnect流量

OpenConnect基于TCP传输,默认使用443端口,可先从端口过滤入手:

  • 在eBPF/XDP程序中添加TCP目标端口为443的过滤规则;
  • 若需更精准识别,可结合TLS Client Hello中的特征(比如OpenConnect客户端会携带特定SNI或cisco-anyconnect ALPN协议标识),新手阶段优先用端口过滤快速验证。

2. 解封装内层IP包(核心步骤)

OpenConnect的流量分两种场景,处理方式不同:

场景A:监听OpenConnect虚拟网卡(推荐)

在客户端/服务器主机上,OpenConnect会创建tun0(或自定义名称)虚拟网卡,内层IP包直接从该设备流出,无需处理加密:

  • 修改eBPF程序的附加逻辑,让它绑定到OpenConnect创建的tun设备(可通过用户态传入设备名参数);
  • 在XDP程序中直接解析IP层,提取目的IP地址(tun设备的数据包无以太网帧头,直接从IP层开始)。

场景B:处理物理网卡上的加密流量

如果必须在物理网卡上捕获加密后的OpenConnect流量,需要先解密:

  • 在eBPF层标记符合端口/特征的TCP流,将TCP payload转发到用户态;
  • 用户态通过TLS会话密钥解密payload(可通过SSLKEYLOGFILE环境变量从OpenConnect客户端导出密钥),再解析内层IP包。

3. 整合到现有Go用户态程序

  • 修改eBPF的事件结构体,新增内层目的IP字段,将提取到的IP地址发送到用户态;
  • 在Go代码中扩展解析逻辑:将uint32类型的IP地址转换为字符串格式;
  • 复用现有gRPC上报逻辑,把内层目的IP作为新增字段加入上报消息。

代码修改示例

eBPF层(xdp_dump.c)

添加IP解析与事件发送逻辑:

#include <linux/if_ether.h>
#include <linux/ip.h>

// 定义事件结构体
struct event {
    __be32 dst_ip;
    // 保留原有字段...
};

// 解析IPv4包,提取目的地址
static __always_inline void parse_inner_ipv4(struct xdp_md *ctx, void *data_end, void *data) {
    struct iphdr *iph = data;
    if ((void *)iph + sizeof(*iph) > data_end)
        return;

    struct event e = {};
    e.dst_ip = iph->daddr;
    // 发送事件到用户态
    xdp_send_event(ctx, &e);
}

SEC("xdp")
int xdp_dump_prog(struct xdp_md *ctx) {
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    struct ethhdr *eth = data;
    if ((void *)eth + sizeof(*eth) > data_end)
        return XDP_PASS;

    // 处理物理网卡的以太网帧,或tun设备的直接IP包
    if (eth->h_proto == htons(ETH_P_IP)) {
        // 物理网卡:跳过以太网帧头解析IP
        parse_inner_ipv4(ctx, data_end, data + sizeof(*eth));
    } else {
        // tun设备:直接解析IP(无以太网帧头)
        parse_inner_ipv4(ctx, data_end, data);
    }

    // 保留原有TCP连接捕获逻辑
    // ...

    return XDP_PASS;
}

Go用户态层

扩展事件解析与上报:

import "net"

type Event struct {
    DstIP uint32 `bpf:"dst_ip"`
    // 原有字段...
}

// 将uint32格式IP转换为字符串
func (e *Event) DstIPString() string {
    return net.IP{
        byte(e.DstIP),
        byte(e.DstIP >> 8),
        byte(e.DstIP >> 16),
        byte(e.DstIP >> 24),
    }.String()
}

// 在gRPC上报函数中添加内层IP字段
func reportEvent(e *Event) {
    req := &pb.ReportRequest{
        InnerDstIp: e.DstIPString(),
        // 原有上报字段...
    }
    // 调用gRPC上报
    // ...
}

注意事项

  • 优先选择场景A(监听虚拟网卡),避免TLS解密的复杂逻辑,更适合新手;
  • 若使用场景B,需确保能获取TLS会话密钥,OpenConnect客户端支持通过环境变量导出密钥;
  • XDP层尽量只做轻量过滤与提取,复杂解析(如解密)放到用户态,避免影响网络性能。

内容的提问来源于stack exchange,提问作者NobinPegasus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:05:28