Linux strace can';无法分析我的netlink消息,但它似乎有效
我第一次尝试在linux中使用NetLink API。我用铁锈,因为它还没有咬到我的屁股,让我回到C。我认为一个很好的开始是枚举netlink设备,因为已经有一个实用程序可以这样做(Linux strace can';无法分析我的netlink消息,但它似乎有效,linux,rust,netlink,Linux,Rust,Netlink,我第一次尝试在linux中使用NetLink API。我用铁锈,因为它还没有咬到我的屁股,让我回到C。我认为一个很好的开始是枚举netlink设备,因为已经有一个实用程序可以这样做(ip link)。当我运行Rust代码时,它返回ip link返回的6个设备中的3个设备。因此,我试图检查我正在发送的请求与ip-link正在发送的请求 # Mine $ sudo strace -ff -v -e trace=%network mine 2>&1 |grep sendto send
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链接
绑定套接字,而Rust库为sendto提供了一个dest\u addr
参数。我怀疑这是否相关。(B) strace
可以解析ip-link
发送的结构,但似乎无法完全解析我的Rust程序发送的结构strace
表示两个程序在struct-nlmsghdr
标题上一致。但是,strace
不会解析我的程序的struct-ifinfo-msg
。但是,从字节来看,它似乎是匹配的
我使用的rust库netlink packet route
似乎与struct rtattr
没有明显的等价物。在IFINFOMG
(在rust库中称为LinkHeader
)旁边,有一个它称为Nla
的列表。枚举值与其C等价项对齐,如上图所示,常量值也对齐
manrtnetlink
没有提到任何关于IFLA\u EXT\u MASK
的内容,这可能是RTM\u GETLINK
的rtttr
,我在文档中也没有得到很多关于它的点击
我想下一步是将这两个调用都弹出到gdb
,看看这两个调用之间是否有任何其他可观察到的差异
产生上述信息的超级丑陋的演示质量锈代码:
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")
}
}
}
使用std::io::{Result,Error,ErrorKind};
使用netlink_sys:{Socket,protocols::netlink_ROUTE,SocketAddr};
使用netlink_数据包_核心:{NetlinkMessage,NetlinkHeader,NLM_F_DUMP,NLM_F_REQUEST};
使用netlink_包_路由:{RtnlMessage,LinkMessage,LinkHeader,AF_包,ARPHRD_NETROM};
使用netlink_数据包_core::NetlinkPayload::InnerMessage;
使用netlink_数据包_路由::RtnlMessage::NewLink;
使用netlink_数据包_路由::link::nlas::Nla;
使用std::time::{UNIX_EPOCH,SystemTime};
fn main()->结果{
println!(“你好,世界!”);
让套接字=套接字::新建(NETLINK_路由)?;
让kernel_addr=SocketAddr::new(0,0);
设msgid=SystemTime::now().duration_-since(UNIX_EPOCH).map_-err(| |{Error::from(ErrorKind::Other)}).as_-secs();
让nlas:Vec=Vec![Nla::ExtMask(1)];
让mut packet=NetlinkMessage{
标题:NetlinkHeader{
序列号:msgid为u32,
标志:NLM_F_DUMP | NLM_F_REQUEST,
…默认值::默认值()
},
有效负载:RtnlMessage::GetLink(LinkMessage{header:LinkHeader{
接口_系列:AF_数据包为u8,
链路层类型:ARPHRD\u NETROM,
…LinkHeader::default()},nlas,…LinkMessage::default()}).into(),
};
packet.finalize();
让mut buf=vec![0;packet.header.length as usize];
packet.serialize(&mutbuf[…]);
设n_sent=socket.send_to(&buf[…],&kernel_addr,0).unwrap();
assert_eq!(n_sent,buf.len());
设mut buf=vec![0;4096];
环路{
let(n_received,sender_addr)=socket.recv_from(&mut buf[…],0).unwrap();
断言(发送方地址、内核地址);
对于i in&mut buf[n_received..]{*i=0};
如果n_received==4096{返回Err(Error::from(ErrorKind::OutOfMemory))}
如果buf[4]==2&&buf[5]==0{
println!(“内核响应错误”);
返回Err(Error::from(ErrorKind::ConnectionReset));
}
如果buf[4]==3&&buf[5]==0{
println!(“完成”);
返回Ok(());
}
let resp=NetlinkMessage:::反序列化(&buf).expect(“反序列化消息失败”);
匹配相应有效载荷{
InnerMessage(i)=>匹配i{
新链接(j)=>{
让name=j.nlas.iter().find(| nla |{matches!(nla,nla::IfName(|));
println!(“索引{:?}:{:?}”,j.header.index,name);
},
_=>println!(“其他消息类型”)
},
_=>println!(“其他消息类型”)
}
}
}
因此,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