【问题标题】:strace can't parse my netlink message, but it appears validstrace 无法解析我的 netlink 消息,但它似乎有效
【发布时间】:2021-05-11 20:58:06
【问题描述】:

我正在尝试在 linux 中使用 NetLink API。我使用 Rust 是因为它还没有让我大吃一惊,以至于我还不能回到 C 语言。我认为一个好的起点是枚举 netlink 设备,因为已经有一个实用程序可以做到这一点 (ip link)。当我运行 Rust 代码时,它会返回 ip link 返回的 6 个设备中的 3 个设备。因此,我正在尝试检查我发送的请求与 ip link 发送的请求。

# Mine
$ sudo strace -ff  -v -e trace=%network mine 2>&1  |grep sendto
sendto(3, 
  {
    {nlmsg_len=40, nlmsg_type=0x12 /* NLMSG_??? */, nlmsg_flags=NLM_F_REQUEST|0x300, nlmsg_seq=1620766291, nlmsg_pid=0}, 
    "\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x1d\x00\x01\x00\x00\x00"
  },
  40, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 40


$ sudo strace -ff --const-print-style=raw -v -e trace=%network mine 2>&1  |grep sendto                                                                                                            
sendto(3, 
  {
    {nlmsg_len=40, nlmsg_type=0x12, nlmsg_flags=0x301, nlmsg_seq=1620766293, nlmsg_pid=0}, 
    "\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x1d\x00\x01\x00\x00\x00"
  },
  40, 0, {sa_family=0x10, nl_pid=0, nl_groups=00000000}, 12) = 40


# ip link
$ sudo strace -ff  -v -e trace=%network ip link 2>&1  |grep sendto                                            
sendto(3, 
  {
    {nlmsg_len=40, nlmsg_type=RTM_GETLINK, nlmsg_flags=NLM_F_REQUEST|NLM_F_DUMP, nlmsg_seq=1620765818, nlmsg_pid=0}, 
    {ifi_family=AF_PACKET, ifi_type=ARPHRD_NETROM, ifi_index=0, ifi_flags=0, ifi_change=0}, 
    {
      {nla_len=8, nla_type=IFLA_EXT_MASK}, 
      1
    }
  },
  40, 0, NULL, 0) = 40


$ sudo strace -ff --const-print-style=raw -v -e trace=%network ip link 2>&1  |grep sendto
sendto(3, 
  {
    {nlmsg_len=40, nlmsg_type=0x12, nlmsg_flags=0x301, nlmsg_seq=1620765854, nlmsg_pid=0}, 
    {ifi_family=0x11, ifi_type=0, ifi_index=0, ifi_flags=0, ifi_change=0}, 
    {
      {nla_len=8, nla_type=0x1d}, 
      1
    }
  },
  40, 0, NULL, 0) = 40


我可以发现的两个区别是 (A) ip link 绑定了套接字,而 Rust 库为 sendto 提供了一个 dest_addr 参数。我怀疑这是否相关。 (B) strace 可以解析 ip link 发送的结构,但似乎无法完全解析我的 Rust 程序发送的结构。 strace 表示两个程序都同意 struct nlmsghdr 标头。但是,strace 不会解析我的程序的struct ifinfomsg。然而,看看字节,它似乎匹配。

我使用的 rust 库 netlink-packet-route 似乎没有明显等同于 struct rtattr。在 ifinfomsg (在 rust 库中称为 LinkHeader)旁边有一个它称为 Nla 的列表。枚举值与它们的 C 等值对齐,如上所示,常量值也对齐。

man rtnetlink 没有提及任何关于IFLA_EXT_MASK 的内容,因为rtattr 可能是RTM_GETLINK,而且我在文档中没有得到很多其他的点击。

我想下一步是将两者都弹出到gdb,看看这两个调用之间是否有任何其他可观察到的差异。


产生上述消息的超级丑陋的演示质量 Rust 代码:

use std::io::{Result, Error, ErrorKind};
use netlink_sys::{Socket, protocols::NETLINK_ROUTE, SocketAddr};
use netlink_packet_core::{NetlinkMessage, NetlinkHeader, NLM_F_DUMP, NLM_F_REQUEST};
use netlink_packet_route::{RtnlMessage, LinkMessage, LinkHeader, AF_PACKET, ARPHRD_NETROM};
use netlink_packet_core::NetlinkPayload::InnerMessage;
use netlink_packet_route::RtnlMessage::NewLink;
use netlink_packet_route::link::nlas::Nla;
use std::time::{UNIX_EPOCH, SystemTime};

fn main() -> Result<()> {
    println!("Hello, world!");

    let socket = Socket::new(NETLINK_ROUTE)?;
    let kernel_addr = SocketAddr::new(0, 0);

    let msgid = SystemTime::now().duration_since(UNIX_EPOCH).map_err(|_|{Error::from(ErrorKind::Other)})?.as_secs();
    let nlas: Vec<Nla> = vec![Nla::ExtMask(1)];
    let mut packet = NetlinkMessage {
        header: NetlinkHeader {
            sequence_number: msgid as u32,
            flags: NLM_F_DUMP | NLM_F_REQUEST,
            ..Default::default()
        },
        payload: RtnlMessage::GetLink(LinkMessage {header: LinkHeader {
            interface_family: AF_PACKET as u8,
            link_layer_type: ARPHRD_NETROM,
            ..LinkHeader::default()}, nlas, ..LinkMessage::default()}).into(),
    };
    packet.finalize();

    let mut buf = vec![0; packet.header.length as usize];
    packet.serialize(&mut buf[..]);

    let n_sent = socket.send_to(&buf[..], &kernel_addr, 0).unwrap();
    assert_eq!(n_sent, buf.len());
    let mut buf = vec![0; 4096];
    loop {
        let (n_received, sender_addr) = socket.recv_from(&mut buf[..], 0).unwrap();
        assert_eq!(sender_addr, kernel_addr);
        for i in &mut buf[n_received..] { *i = 0 };
        if n_received == 4096 { return Err(Error::from(ErrorKind::OutOfMemory))}
        if buf[4] == 2 && buf[5] == 0 {
            println!("the kernel responded with an error");
            return Err(Error::from(ErrorKind::ConnectionReset));
        }
        if buf[4] == 3 && buf[5] == 0 {
            println!("Done");
            return Ok(());
        }
        let resp = NetlinkMessage::<RtnlMessage>::deserialize(&buf).expect("Failed to deserialize message");
        match resp.payload {
            InnerMessage(i) => match i {
                NewLink(j) => {
                    let name = j.nlas.iter().find(|nla| { matches!(nla, Nla::IfName(_))});

                    println!("index {:?}: {:?}", j.header.index, name);
                },
                _ => println!("Some other message type")
            },
            _ => println!("Some other message type")
        }

    }
}

【问题讨论】:

    标签: linux rust netlink


    【解决方案1】:

    所以事实证明netlink 将在单个响应包中发送多个响应结构。我通过为 netlink 设置监控接口并 tcpdump 来发现这一点。响应ip link 和我的程序返回的字节总数相似,但它们分布在不同数量的数据包上。 recvmsg 调用中使用的缓冲区大小会影响 netlink 打包结果的方式。

    sudo ip link add  nlmon0 type nlmon
    sudo ip link set dev nlmon0 up
    sudo tcpdump -i nlmon0 -w /tmp/dump &
    ip link
    mine
    fg
    CTRL+C
    tshark -r /tmp/dump
    
        1   0.000000              →              Netlink route 56 
        2   0.000073              →              Netlink route 2660 
        3   0.000127              →              Netlink route 2688 
        4   0.000205              →              Netlink route 4060 
        5   0.000258              →              Netlink 36 
        6   3.622386              →              Netlink route 56 
        7   3.622449              →              Netlink route 2660 
        8   3.622512              →              Netlink route 2688 
        9   3.623179              →              Netlink route 2740 
       10   3.623748              →              Netlink route 1336 
       11   3.624273              →              Netlink 36 
    

    我需要弄清楚这个 Rust 库是如何设置来处理这个问题的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 2016-09-18
      相关资源
      最近更新 更多