华云代理服务器网站,智汇华云 | 通过TProxy实现haproxy 透传用户IP

图四

如果OPTIONS中没有USE_LINUX_TPROXY=1,需要重新编译haproxy打开透传用户IP的代码,haproxy编译步骤如下:

1. wget http://www.haproxy.org/download/1.5/src/haproxy-1.5.14.tar.gz

2. tar zxvf haproxy-1.5.14.tar.gz

3. cd haproxy-1.5.14

4. yum install gcc gcc-c++ autoconf automake -y

5. make TARGET=linux2628 arch=x86_64 USE_LINUX_TPROXY=1

6. make install

3.2 haproxy IP透传如何透传用户IP

这一步其实非常简单。haproxy进程只需要拿到用户IP,然后在创建到后端server的tcp连接时,做三件件事情:

1. 将要启用 IP透传的后端服务器组对应的 backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip",这样会将flags设置为1,使能IP透传,详细配置如下:

1. backend XXXX

2.     mode tcp

3.     balance XXX

4.     source 0.0.0.0 usesrc clientip

5.     . . .

2. 创建和后端server通信的socket,并调用setsockopt函数, 将socket设置为IP_TRANSPARENT或IP_FREEBIND

3. 调用bind函数,将用户IP绑定到该socket,绑定后后端server看到的该tcp连接IP,即为用户源IP。

haproxy相关代码:

1. int tcp_bind_socket(int fd, int flags, struct sockaddr_storage *local, struct sockaddr_storage *remote)

2. {

3. struct sockaddr_storage bind_addr;

4. int foreign_ok = 0;

5. int ret;

6. static int ip_transp_working = 1;

7. static int ip6_transp_working = 1;

8.

9.  switch (local->ss_family) {

10. case AF_INET:

11.      /* backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip"后,flags变量值为1 */

12.         if (flags && ip_transp_working) {

13.             /* 调用setsockopt函数, 将socket设置IP_TRANSPARENT或IP_FREEBIND标识 */

14.             if (0

15. #if defined(IP_TRANSPARENT)

16.                 || (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) == 0)

17. #endif

18. #if defined(IP_FREEBIND)

19.                 || (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, sizeof(one)) == 0)

20. #endif

21. #if defined(IP_BINDANY)

22.                 || (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) == 0)

23. #endif

24. #if defined(SO_BINDANY)

25.                 || (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) == 0)

26. #endif

27.                 )

28.                 foreign_ok = 1;

29.             else

30.                 ip_transp_working = 0;

31.         }

32.         break;

33.     }

34.     if (flags) {

35.         memset(&bind_addr, 0, sizeof(bind_addr));

36.         bind_addr.ss_family = remote->ss_family;

37.         switch (remote->ss_family) {

38.         case AF_INET:

39.             if (flags & 1)

40.             /* 因为flags变量值为1,这儿只会将客户端IP赋值给bind_addr */

41.                 ((struct sockaddr_in *)&bind_addr)->sin_addr = ((struct sockaddr_in *)remote)->sin_addr;

42.             if (flags & 2)

43.                 ((struct sockaddr_in *)&bind_addr)->sin_port = ((struct sockaddr_in *)remote)->sin_port;

44.             break;

45.  default:

46.             /* we don't want to try to bind to an unknown address family */

47.             foreign_ok = 0;

48.         }

49.     }

50.     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

51.     if (foreign_ok) {

52.         if (is_inet_addr(&bind_addr)) {

53.         /* 这儿会将客户端IP绑定到该socket */

54.             ret = bind(fd, (struct sockaddr *)&bind_addr, get_addr_len(&bind_addr));

55.             if (ret 

56.                 return 2;

57.         }

58.     }

59.     else {

60.         if (is_inet_addr(local)) {

61.             ret = bind(fd, (struct sockaddr *)local, get_addr_len(local));

62.             if (ret 

63.                 return 1;

64.         }

65.     }

66.     if (!flags)

67.         return 0;

3.2 后端server如何返回给haproxy服务器

由于后端收到的包源IP为client IP,默认无法返回给haproxy服务器,因此需要配置haproxy服务器的IP设置client IP端的网关IP,后端server才能将回包返回给haproxy服务器

1. route add -net 10.10.0.0/16 gw 10.10.46.198

其中10.10.0.0/16替换成client的网段,10.10.46.198替换成LB的IP地址

3.3 haproxy 服务器将非本地IP的IP包路由到本地环路

配置iptable规则,将发往非haproxy服务器本地(目的IP为客户端源IP)的tcp网络包打上标记(–set-mark 1)

1. iptables -t mangle -F

2. iptables -t mangle -N DIVERT,

3. iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT

4. iptables -t mangle -A DIVERT -j MARK --set-mark 1,

5. iptables -t mangle -A DIVERT -j ACCEPT

haproxy 上配置路由将标记(mark 1)的包返回到本地环路lo

1. ip rule del from all fwmark 0x1 lookup 100

2. ip rule add fwmark 1 lookup 100

3. ip route add local 0.0.0.0/0 dev lo table 100

4 总结

综上所述,通过TProxy实现haproxy IP透传,需要做如下四件事情:

1. 确认linux内核版本大于2.6.28,确认haproxy编译时配置了USE_LINUX_TPROXY=1选项

2. 修改haproxy配置,需要启用IP透传的后端服务器组对应的backend 字段部分增加配置 "source 0.0.0.0 usesrc clientip"

3. 后端服务器配置路由规则,将客户端IP端的网关配置haproxy服务器IP

4. haproxy服务器上添加iptables规则和路由策略,将后端服务器的回包路由给本地环路lo

5 参考文献

http://www.360doc.com/content/13/0821/17/13047933_308812287.shtml

https://blog.csdn.net/frockee/article/details/78641188

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,我们可以暴力枚举所有可能的子矩阵,并计算它们的分值,最后选出分值最小的一个。具体来说,对于每个子矩阵,我们可以先计算出它的每个元素与它所在行和列的中位数之差的绝对值,然后将这些绝对值相加即可得到该子矩阵的分值。时间复杂度为 $O(n^6)$,无法通过本题。 注意到题目中给定了 $r$ 和 $c$,因此我们可以通过预处理每个元素所在行和列的中位数,将每个元素与其所在行和列的中位数之差的绝对值预处理出来,从而将计算子矩阵分值的时间复杂度优化到 $O(n^4)$。具体来说,我们可以使用前缀和的思想,先预处理出每一行和每一列的前缀和,然后对于每个元素,可以通过前缀和求出它所在行和列的中位数,从而计算出它与它所在行和列的中位数之差的绝对值。时间复杂度为 $O(n^2)$。 接下来,我们考虑如何高效地枚举所有可能的子矩阵。我们可以枚举子矩阵的左边界和右边界,然后利用前缀和快速计算出该子矩阵内每一行的和以及每一列的和,从而在 $O(n)$ 的时间内计算出该子矩阵的和。具体来说,我们可以使用前缀和的思想,先预处理出每一行和每一列的前缀和,然后对于每个子矩阵,可以通过前缀和求出该子矩阵内每一行的和以及每一列的和,从而计算出该子矩阵的和。时间复杂度为 $O(n^4)$。 综上所述,总时间复杂度为 $O(n^4)$,可以通过本题。具体实现时,可以先预处理出每个元素与其所在行和列的中位数之差的绝对值,然后再枚举子矩阵的左边界和右边界,利用前缀和计算出该子矩阵的和并更新答案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值