Tag Archives: tcp

tcpdump 抓包显示 toa 源地址

LVS 是个很流行的高性能负载均衡程序。不过因为工作在 3-4 层,对被代理的后端来说,就只能看到 LVS 的源地址,看不到实际的用户来源 IP 地址了。所以 LVS 提供了一个 toa 内核模块,在后端安装后,可以从 TCP 头的扩展段里获取源 IP 地址。不过这只限于应用层面。系统层面的工具比如 netstat,tcpdump 等仍然只能看到 LVS 的源地址,想通过 tcpdump 抓包过滤或者分析实际来源地址就不行了。不过既然在 TCP 头里有这数据我们就还是可以利用。

tcpdump 输出的内容里,如果有 toa 扩展,会多出一截 Unknown Option。其中后面的 32bit 就是实际的源 IP。如

... ack 1562404315, win 29, options [Unknown Option 20030390a0a0101], length 0

按 TOA 的结构:

struct toa_data {
	__u8 opcode;
	__u8 opsize;
	__u16 port;
	__u32 ip;
};

这里的 0a0a0101 就是 10.10.1.1,3039就是源端口 12345。

因为 TCP 头里多了扩展字段,所以 TCP 头中的 data_offset 就会大于 5(words)。用 ((tcp[12] & 0xf0)>>4) > 5 这个条件过滤可以只处理带 toa 头的包。TCP 扩展字段从 20 字节开始。所以可以直接用比如 tcp[24:4]==0x0a0a0101 过滤指定源 IP 的包。

下面这段脚本就会输出发往 1234 端口的实际源 IP 的地址。

sudo tcpdump -nn -l "tcp dst port 1234 and ((tcp[12] & 0xf0)>>4) > 5" 2>/dev/null | \
    awk 'match($0, "Unknown Option ([0-9a-f]+)",a) {print strtonum("0x" substr(a[1],8,2)) "." strtonum("0x" substr(a[1],10,2)) "." strtonum("0x" substr(a[1],12,2)) "." strtonum("0x" substr(a[1],14,2)); fflush()}'