linux - 如何获取重定向 UDP 消息的原始目标端口?

标签 linux sockets udp redirect iptables

使用 this thing我可以获得 socket(PF_INET, SOCK_DGRAM, 0) 套接字的原始目标 IP 地址。

如何获取原始目的端口?

最佳答案

取决于重定向机制。如果您使用的是 REDIRECT(底层是 NAT),那么您需要使用 SO_ORIGINAL_DST 或 libnetfilter_conntrack 来查询应用 NAT 之前连接的原始目的地。但是,由于您可以使用同一个监听器套接字提供多个连接,因此必须为每个数据包执行此查找。

您可以使用 conntrack 命令行工具试验 libnetfilter_conntrack 及其提供的服务。

另一种方法是使用 TPROXY 进行重定向,这是为了在这种情况下使用。在那里,您可以使用 recvmsg() 使用辅助消息获取数据包的原始目的地。要查找的关键是 IP_RECVORIGDST setsockopt。

有关 TPROXY 的更多信息可以在内核文档目录中的一个名为 tproxy.txt 的文件中找到。它使用起来有点困难,但工作起来更可靠,因为它是由堆栈实现的,而不是包过滤子系统。

已编辑:添加如何使用 TProxy 查询 UDP 目标地址。

  1. 打开一个 UDP 套接字,将其绑定(bind)到 0.0.0.0 或更具体的 IP
  2. 您通过 setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, ...) 启用 IP_RECVORIGDST
  3. 您使用 recvmsg() 而不是 recvfrom()/recv() 来接收帧
  4. recvmsg() 将返回数据包和一系列辅助消息,
  5. 迭代辅助消息,并找到级别为 SOL_IP、索引为 IP_ORIGDSTADDR 的 CMSG block
  6. 此 CMSG block 将包含一个包含 IP 和端口信息的 struct sockaddr_in。

已编辑:SO_ORIGINAL_DST 与 udp

SO_ORIGINAL_DST 应该与 udp 套接字一起使用,但是内核不允许您指定连接端点,它将使用您调用 SO_ORIGINAL_DST 的套接字来获取此地址信息。

这意味着只有当 UDP 套接字被正确绑定(bind)(到重定向到的地址/端口)并连接到(到有问题的客户端)时,它才会工作。您的监听器套接字可能绑定(bind)到 0.0.0.0,并且不仅为单个客户端提供服务,而且为多个客户端提供服务。

但是,您不需要使用实际的监听器套接字来查询目标地址。由于 UDP 不在连接建立时传输数据报,因此您可以创建一个新的 UDP 套接字,将其绑定(bind)到重定向地址并将其连接到客户端(自从它向您的监听器发送第一个数据包以来您就知道其地址)。然后你可以使用这个套接字来运行 SO_ORIGINAL_DST,但是有罪魁祸首:

  1. 一旦你打开这样一个套接字,内核会更喜欢客户端在第一个数据包之后发送额外的数据包,而不是你的监听器套接字
  2. 这本质上是活泼的,因为当您的应用程序有机会调用 SO_ORIGINAL_DST 时,conntrack 条目可能已经超时。
  3. 它很慢而且开销很大

基于 TProxy 的方法显然更好。

关于linux - 如何获取重定向 UDP 消息的原始目标端口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5615579/

相关文章:

python libcloud vcloud连接

python - 如何分发将可执行文件添加到路径的 Python 包?

c - 如何使 C 计时器在 Linux 中的特定系统时间到期

perl - IO::Socket 与 Socket 我都是 "use"吗?

c++ - 管理 C 套接字中客户端突然断开连接的好方法

PHP套接字UDP通信

Java可靠的UDP

linux - 如何检查给定页面是否为内核模块中的 ZERO_PAGE?

javascript - 多变量 socket.io 发出的问题

c - 具有多个接口(interface)的 UDP 多播不工作