【问题标题】:Elixir/Erlang :gen_udp pseudo headerElixir/Erlang :gen_udp 伪标头
【发布时间】:2021-06-08 20:32:42
【问题描述】:

我想在 Elixir 中接收 UDP 消息,并且需要获取数据包发送到的目标 IP。

在 iex 中我可以用这个命令打开一个套接字:

{:ok, socket} = :gen_udp.open(19_999, [{:active, true}])

当我收到一条消息时,我会收到以下格式的消息:

{:udp, port, source_ip, source_port, data}

目标 IP 不是消息的一部分。根据 UDP 规范,源 IP 不是 UDP 数据包的一部分,而是 IP 伪标头的一部分。当我使用 tcpdump 时,我可以看到数据包的目标 IP。

有没有什么方法/参数可以获取UDP包的IP或者Pseudo header?

【问题讨论】:

  • 我在某处读到一条评论说伪标头永远不会发送,而是用于计算校验和,这意味着您无法获取伪标头。
  • 我对你的问题有点困惑。如果您的应用程序正在接收一个数据包,那么数据包的目标地址就是您的主机地址。您是否正在寻找目标地址以在收到数据包时回复?
  • 我自己没用过,但是socket 模块似乎支持获取更多关于接收到的数据包的信息:erlang.org/doc/man/socket.html
  • 目标端口是您在:gen_udp.open 中设置的端口(代码中的19_999)。然而,目标 IP 有点棘手,因为:gen_udp.open 默认侦听0.0.0.0,它指的是您所有可用的网络适配器,包括像lo 这样的虚拟适配器。由于不同网络中的设备无法相互通信,也许您可​​以通过查看源IP来确定哪个IP地址正在接收当前数据包。

标签: erlang elixir


【解决方案1】:

目标端口肯定是19_999

要确定哪个 IP 正在接收当前数据包,您需要知道所有网络适配器的配置,包括虚拟适配器。之后,您可以从源 IP 和网络配置中获取目标 IP,因为只有同一网络中的设备才能相互通信。您只需要在所有网络适配器中找到与源 IP 在同一网络中的 IP。可以通过对2个IP地址应用网络掩码来判断2个设备是否在同一个网络中,看看结果是否相同。

顺便说一下,如果你的服务器在 NAT 之后,如果它与服务器不在同一个网络中,你就无法获得客户端的真实源 IP 地址。您只能获得最后一跳的源 IP 地址(通常是您的网络网关的 IP 地址)。

获取网络配置

{:ok, ifaddrs} = :inet.getifaddrs()

你会得到一个看起来像这样的列表

[
  {'lo', [addr: {127, 0, 0, 1}, netmask: {255, 0, 0, 0}, ...]},
  {'eth0', [addr: {192, 168, 0, 10}, netmask: {255, 255, 255, 0}, ...]},
  {'docker_gwbridge', [addr: {172, 18, 0, 1}, netmask: {255, 255, 0, 0}, ...]}
]

由于我们只需要addrnetmask,我们可以在列表中再做一个Enum.map

ifaddrs = Enum.map(ifaddrs, fn{_, opts}-> {opts[:addr], opts[:netmask]} end)

将网络掩码应用于 IP 地址

按位将它们放在一起。

defp apply_netmask({a1, b1, c1, d1} = _ip, {a2, b2, c2, d2} = _netmask) do
  # Don't forget `use Bitwise`
  {
    a1 &&& a2,
    b1 &&& b2,
    c1 &&& c2,
    d1 &&& d2
  }
end

查找目标IP

dest_ip = ifaddrs
          |> Enum.find(fn{addr, netmask}-> 
               apply_netmask(addr, netmask) == apply_netmask(source_ip, netmask) 
          end)
          |> elem(0)

更新

如果可能,我建议您仅在其中一个网络适配器上打开 UDP 套接字。您可以使用{ifaddr, inet:socket_address()} 选项来实现,例如:

{:ok, socket} = :gen_udp.open(19_999, [active: true, ifaddr: {192, 168, 0, 10}])

这样可以100%确定目标IP是192.168.0.10。此外,它还增加了一点安全性。

【讨论】:

  • 感谢您的回答。我最终做了你在更新中建议的事情。似乎无法使用 :gen_udp 获取伪标头。
猜你喜欢
  • 2010-09-09
  • 1970-01-01
  • 2016-05-05
  • 2013-09-16
  • 2023-03-27
  • 2014-03-31
  • 2015-05-15
  • 2017-03-03
  • 2015-02-14
相关资源
最近更新 更多