后端主机支持获取客户端信息

最近更新时间:2020-10-26 19:26:30

概述

用户在主机(RS)侧加载内核模块 kgwttm.ko 后,对于 TCP 连接:可以通过 socket 函数簇函数获取客户端( client )真实信息,包括客户端 IP( cip )、客户端端口( cport )。

场景介绍

后端主机支持获取客户端信息

Client 在 ksc 之外,通过 lb 访问 RS,RS 可为虚机或物理机。

后端主机支持获取客户端信息

Client 与 RS 同属 ksc,可通过 lb 访问 RS,RS 可为云服务器或裸金属服务器

场景一:6TO4

后端主机支持获取客户端信息

Client 通过其 ipv6 地址(cip6)访问 vip6:vport 服务,后端与 rs 通信为 ipv4 地址。Option 字段携带 cip6:cport 信息。

场景二:4TO4

后端主机支持获取客户端信息

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.*(特定版本环境可提供技术支持)

安装

  1. 内核环境准备
    # rpm -qa | grep kernel-devel-$(uname -r)
    # yum install "kernel-devel-$(uname -r)"
  2. 解压 kgwttm.tgz
    # tar -zxf kgwttm.tgz
  3. 编译
    # cd kgwttm
    # ./build.sh
  4. 安装
    # sudo rpm -ivh kmod-kgwttm-v.*.rpm
  5. 检查是否安装成功
    #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 端信息。 具体样例参考附录

卸载

  1. 检查 rpm 包安装情况
    # rpm –qa|grep kgwttm
  2. 卸载
    # rpm -e `rpm -qa|grep ttm`
  3. 再次检查
    # lsmod|grep kgwttm

附录

用户安装 kgwttm 模块后,可直接运行源码包内 example_ipv6/ 目录下的 c/python server 端 demo 实例。基于业务场景提供 client 端 tcp 访问,在 RS 侧观察效果。

代码示例一:PYTHON

… … # 用户 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
… … # 用户后续逻辑

代码示例二:C

#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));
 }

金山云,开启您的云计算之旅

免费注册