用户在主机(RS)侧加载内核模块 kgwttm.ko 后,对于 TCP 连接:可以通过 socket 函数簇函数获取客户端( client )真实信息,包括客户端 IP( cip )、客户端端口( cport )。
Client 在 ksc 之外,通过 lb 访问 RS,RS 可为虚机或物理机。
Client 与 RS 同属 ksc,可通过 lb 访问 RS,RS 可为云服务器或裸金属服务器
Client 通过其 ipv6 地址(cip6)访问 vip6:vport 服务,后端与 rs 通信为 ipv4 地址。Option 字段携带 cip6:cport 信息。
Client 通过其 ipv4 地址( cip4 )访问 vip4:vport 服务,后端与 rs 通信为 ipv4 地址。Option 字段携带 cip4:cport 信息。
在 fullnat( fnat )/ttm_cip 模式下,负载均衡器( LB )向后端 RS 转发报文时,会以其 local ip(lip)作为报文源 ip。即,常规模式下在 RS 端得不到 client 端真实 ip/port 信息。
在 tcp 建立连接阶段,ttm 通过 tcp 握手的第三个报文(ack)内利用 tcp option 字段加入 client ip/port 信息,实现在 rs 端的信息获取。Option 字段由 ksc-gw 封装进报文,由加载 kgwttm 的rs 端解封装。Option 字段格式如下所示:
/* 自定义 client 信息结构体 */
struct ttm_peer {
__u16 af;
__be16 port;
union {
__u32 all[4];
__be32 ip;
__be32 ip6[4];
struct in_addr in;
struct in6_addr in6;
};
};
支持内核版本:v2.6* 、v3.* 、v4.*(特定版本环境可提供技术支持)
# rpm -qa | grep kernel-devel-$(uname -r)
# yum install "kernel-devel-$(uname -r)"
# tar -zxf kgwttm.tgz
# cd kgwttm
# ./build.sh
# sudo rpm -ivh kmod-kgwttm-v.*.rpm
#lsmod | grep kgwttm
以 python 为例:
RS 端,用户在 tcp socket server 端 accept()返回成功后,通过调用 socket.getsockopt(level,optname[, buflen]),其中 level 赋值 socket.SOL_IP, optname 赋值 1345,buflen 赋值 20。获取返回信息后,按照 struct ttm_data_v6 解析相应返回值即可。
在 6to4 以及 4to4 模式中,我们提供socket.getsockopt()指定 optname 为 1345 方式获取 client端信息。
4to4 场景,还可通过 socket.accept()返回参数或调用 socket.getpeername()获取 client 端信息。
具体样例参考附录
# rpm –qa|grep kgwttm
# rpm -e `rpm -qa|grep ttm`
# lsmod|grep kgwttm
用户安装 kgwttm 模块后,可直接运行源码包内 example_ipv6/ 目录下的 c/python server 端 demo 实例。基于业务场景提供 client 端 tcp 访问,在 RS 侧观察效果。
… … # 用户 socket 逻辑
ss, addr = s.accept()
# 4to4 场景,可直接获得 cip/cport 信息
# 6to4 场景,获得 xgw 代理 lip/lport 信息
print 'get client info:', addr # 4to4 方法一
print 'getpeername:', ss.getpeername() # 4to4 方法二
# 0 == SOL_IP; 1345 == TTM_SO_GET_PEER; 20 == buff len
str = ss.getsockopt(0, 1345, 20) # 4to4 方法三 / 6to4 方法
# 按照 struct ttm_data_v6 格式解析
obj_p = struct.Struct('!bbH4s12s')
data = obj_p.unpack(str)
# 根据协议簇解析对应信。2 == AF_INET, 10 == AF_INET6
# 4to4 场景推荐使用上述两种方式获取 client 信息
print 'port:', data[2]
if(data[0] == 2):
print 'ipv4:', inet_ntop(data[0], data[3])
else:
print 'ipv6:', inet_ntop(data[0], data[3]+data[4]) # 10 is AF_INET6
… … # 用户后续逻辑
#define TTM_BASE_CTL (64+1024+64+64+64+64) /* base */
#define TTM_SO_GET_PEER (TTM_BASE_CTL+1) /* 1345, 自定义 id */
/* 自定义 client 信息结构体 */
struct ttm_peer {
__u16 af;
__be16 port;
union {
__u32 all[4];
__be32 ip;
__be32 ip6[4];
struct in_addr in;
struct in6_addr in6;
};
};
… … //用户 socket 逻辑
new_fd=accept(sockfd,(struct sockaddr *)&client_addr, &sin_size);
/* 4to4 场景,可直接获得 cip/cport 信息 */
fprintf(stderr,"Server get connection from %s:%u\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
/* 4to4 或 6to4 获取 client 信息 */
if (getsockopt(new_fd, IPPROTO_IP, TTM_SO_GET_PEER, &peer, &len) < 0) {
fprintf(stderr, "getsockopt failed\n");
} else {
inet_ntop(peer.af, &peer.all, &addr_string, sizeof(addr_string));
printf("getsockopt success(addr:%s port:%u)\n", addr_string, ntohs(peer.port));
}
文档内容是否对您有帮助?
评价建议不能为空
非常感谢您的反馈,我们会继续努力做到更好!