多EIP下的UDP通信异常分析

背景

SRE和程序在测试DDos多EIP防御方案的过程中,发现多EIP模式下, 监听的UDP端口连接会出现客户端访问异常。 表现为客户端发送一次数据后服务端这边主动断开了,或是客户端和服务端同时断开。

该问题会导致业务在多EIP方案下无法达到预期效果,无法进行自动切换EIP对外提供服务。

环境

  • 业务场景:对外的进程默认走udp协议通信,UDP失败后会尝试走TCP协议。
  • 监听方式:0.0.0.0:端口。
  • 防火墙:对外提供的端口全开放。
  • 网络:云(单网卡多EIP)、物理机(多网卡/IP)。

问题定位

抓包发现通信异常的时候,数据包中间会有一个icmp数据包,而我们并没有使用icmp相关协议,并且是icmp unreachable提示。


$tcpdump -nvv -i any |grep 128.1.208.1 02

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 2621 44 bytes

128.1.208.102.44731 > 128.1.208.149.3000: [udp sum ok] UDP, length 2

13.250.175.90.53 > 128.1.208.30.59236: [udp sum ok] 36073 q: A? ******. (148)

128.1.208.102.44731 > 128.1.208.149.3000: [udp sum ok] UDP, length 2

128.1.208.149 > 128.1.208.102: ICMP 128.1.208.149 udp port 3000 unreachable, length 38

128.1.208.102.44731 > 128.1.208.149.3000: [udp sum ok] UDP, length 2

当服务器创建 UDP socket 时,它可以把其中一个主机本地 IP 地址(包括广播地址)指定为 socket 的本 地 IP 地址。那么,只有当 UDP 包的目的 IP 地址与指定的地址相匹配时,该包才能被送到创建该 UPD socket 的业务层。否则,内核将返回一个 ICMP 端口不可达差错,而服务器(业务层)永远看不到该数 据报。

如果存在一个通配的 IP 地址,那么就隐含了一种优先级关系。如果为 UDP socket 指定了特定 IP 地址,那 么在匹配目的地址时,始终优先匹配该 IP 地址。只有在匹配不成功时才使用通配 地址进行匹配。

经常可以看到远端 IP 地址和远端端口号都显示为 .(或 0.0.0.0:*),其意思是该 socket 将接受来自任何 IP 地址和任何端口号的 UDP 数据报。大多数系统允许 UDP socket 对远端地址进行限制,以令其只能接收 来自特定 IP 地址和端口号的 UDP 数据报。

在伯克利派生系统中存在如下副作用:如果在指定远端地址(IP 和 PORT)时没有选择本地地址,那么内 核将自动选择本地地址。其值就成为“选择到达远端 IP 地址路由时”用于做路由判定的 IP 地址。

问题复现

服务端

import socket

ADDR = ('0.0.0.0', 12345) BUFSIZ = 65535

udpSerSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udpSerSock.bind(ADDR)

while True:

print 'waiting for connection...'

data, addr = udpSerSock.recvfrom(BUFSIZ) udpSerSock.sendto(data, addr)

print '...recevied from {0}: {1}'.format(addr, data)

udpSerSock.close()

客户端

 

结论

在多IP且UDP的场景下, 如果使用了bind 0.0.0.0 这种方式, 操作系统会自动选择数据包回复的源地址, 默认 为默认路由对应接口的主IP。

解决方案

方案1:

更新服务端逻辑, 对进来的数据包进行目的地址判断, 使用正确的目的地做为发送数据的源地址, 避免系统自动选择出错(socket中的recvmsg). 

但是该方式改动较大、这个因为我们用的是boost的asio库,asio库不支持获取和修改IP_PKTINFO信息。

方案2

代码层实现分别bind 所有外网ip地址,达到和bind 0.0.0.0一样的效果。

该方式相比原方式改动最小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值