Routing 在Erlang中查找路由接口

Routing 在Erlang中查找路由接口,routing,erlang,Routing,Erlang,我有一台具有多个网络接口的机器,每个网络接口都连接到不同的网络。我想从一个Erlang应用程序中找到将用于连接到给定主机的接口 例如,我有一台带有接口eth0和eth1的机器。eth0位于10.x.x.x网络上,eth1位于192.168.0.x网络上。我想要一个提供ip地址10.0.1.2的函数,该函数将告诉我eth0,而提供ip地址192.168.0.74的函数将告诉我eth1。而您可以通过inet:getifaddrs/0阅读标准(如果未记录)Erlang中的接口列表及其地址和掩码,您无法

我有一台具有多个网络接口的机器,每个网络接口都连接到不同的网络。我想从一个Erlang应用程序中找到将用于连接到给定主机的接口


例如,我有一台带有接口eth0和eth1的机器。eth0位于10.x.x.x网络上,eth1位于192.168.0.x网络上。我想要一个提供ip地址10.0.1.2的函数,该函数将告诉我eth0,而提供ip地址192.168.0.74的函数将告诉我eth1。

而您可以通过
inet:getifaddrs/0阅读标准(如果未记录)Erlang中的接口列表及其地址和掩码,您无法获取路由列表,该列表提供了获取地址的路由所需的其他信息。在大多数主机上(那些没有复杂路由设置的主机,因为它们通常不转发数据包),您通常只需要默认路由以及接口地址和掩码,就可以确定主机将如何为给定地址路由数据包

首先,您需要一个接口及其路由的列表。
getifaddrs/0
函数提供按接口列出的proplist以及地址和掩码列表。由于接口可以分配多个地址,因此需要进行一点列表解析:

routes() ->
    {ok, IFData} = inet:getifaddrs(),
    lists:append([ routes(IF, IFOpts) || {IF, IFOpts} <- IFData ]).

routes(IF, Opts) ->
    {_,Routes} = lists:foldl(fun parse_opts/2, {undefined, []}, Opts),
    [{IF, Route}
     || Route <- Routes].

parse_opts({addr, Addr}, {undefined, Routes}) ->
    {{addr, Addr}, Routes};
parse_opts({netmask, Mask}, {{addr, Addr}, Routes})
  when tuple_size(Mask) =:= tuple_size(Addr) ->
    {undefined, [{Addr, Mask} | Routes]};
parse_opts(_, Acc) -> Acc.
要匹配路由,您需要屏蔽目标地址,并将其与屏蔽接口地址进行比较。屏蔽地址提供网络地址

match_route(Targ, Addr, Mask)
  when tuple_size(Targ) =:= tuple_size(Addr),
       tuple_size(Targ) =:= tuple_size(Mask) ->
    lists:all(fun (A) -> A end,
              [element(I, Targ) band element(I, Mask)
               =:= element(I, Addr) band element(I, Mask)
               || I <- lists:seq(1, tuple_size(Targ)) ]).
现在把它们放在一起:

route(Targ) ->
    route(Targ, routes()).

route(Targ, Routes) ->
    sort_routes(routes_for(Targ, Routes)).

routes_for(Targ, Routes) ->
    [ RT || RT = {_IF, {Addr, Mask}} <- Routes,
            tuple_size(Targ) =:= tuple_size(Addr),
            match_route(Targ, Addr, Mask)
    ].
因此,当查找到127.0.1.1(127.0.0.0/8网络中的地址)的路由时,我得到:
路由({127,0,0,1})->[{“lo0”、{127,0,0,1}、{255,0,0,0}}}]
。不幸的是,例如,我无法获得
{8,8,8,8}
的路由,因为我只有直接连接网络的信息。如果我将默认路由添加到混合中(通过192.168.1.1),我会得到:

从这里开始,您必须a)想出我必须手动添加的额外路由信息(可能调用os:cmd(“netstat-rn”)和b)对返回网关的路由执行第二级查找(您需要递归调用路由,直到返回具有接口名称的路由——直接连接的网络,而不是网关地址)


以上代码作为要点提供:

我认为上面的答案需要Erlang R14。我们设法解决了我们的父问题,而不需要找到路由接口。我会接受这个答案,因为它确实有效(只是不适用于我们)。你可以用大小来代替它,我认为它应该在大多数版本中都能工作。
sort_routes(Routes) ->
    lists:sort(fun ({_, {_AddrA, MaskA}}, {_, {_AddrB, MaskB}}) ->
                       MaskA > MaskB
               end,
               Routes).
route(Targ) ->
    route(Targ, routes()).

route(Targ, Routes) ->
    sort_routes(routes_for(Targ, Routes)).

routes_for(Targ, Routes) ->
    [ RT || RT = {_IF, {Addr, Mask}} <- Routes,
            tuple_size(Targ) =:= tuple_size(Addr),
            match_route(Targ, Addr, Mask)
    ].
[{"lo0",
  {{0,0,0,0,0,0,0,1},
   {65535,65535,65535,65535,65535,65535,65535,65535}}},
 {"lo0",{{127,0,0,1},{255,0,0,0}}},
 {"lo0",
  {{65152,0,0,0,0,0,0,1},{65535,65535,65535,65535,0,0,0,0}}},
 {"en0",{{192,168,1,7},{255,255,255,0}}},
 {"en0",
  {{65152,0,0,0,1548,52991,65242,57142},
   {65535,65535,65535,65535,0,0,0,0}}},
 {"vmnet1",{{172,16,0,1},{255,255,255,0}}},
 {"vmnet8",{{192,168,148,1},{255,255,255,0}}}]
route({8,8,8,8}, routes() ++ [{{192,168,1,1}, {{0,0,0,0},{0,0,0,0}}}]).
[{{192,168,1,1},{{0,0,0,0},{0,0,0,0}}}]

route({127,0,0,1}, routes() ++ [{{192,168,1,1}, {{0,0,0,0},{0,0,0,0}}}]).
[{"lo0",{{127,0,0,1},{255,0,0,0}}},
 {{192,168,1,1},{{0,0,0,0},{0,0,0,0}}}]