Skip to content

Commit 9f28e3e

Browse files
committed
aya: Add netlink functions
`netlink_add_veth_pair`, `netlink_delete_link`, `netlink_set_link_down`, and `netlink_add_ip_addr` are used in setting up and tearing down a veth pair for integration tests.
1 parent f7cad44 commit 9f28e3e

File tree

3 files changed

+191
-11
lines changed

3 files changed

+191
-11
lines changed

aya/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,7 @@ pub use bpf::*;
9292
pub use obj::btf::{Btf, BtfError};
9393
pub use object::Endianness;
9494
#[doc(hidden)]
95-
pub use sys::netlink_set_link_up;
95+
pub use sys::{
96+
netlink_add_ip_addr, netlink_add_veth_pair, netlink_delete_link, netlink_set_link_down,
97+
netlink_set_link_up,
98+
};

aya/src/sys/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ pub(crate) use bpf::*;
1515
#[cfg(test)]
1616
pub(crate) use fake::*;
1717
use libc::{pid_t, SYS_bpf, SYS_perf_event_open};
18-
#[doc(hidden)]
19-
pub use netlink::netlink_set_link_up;
2018
pub(crate) use netlink::*;
19+
#[doc(hidden)]
20+
pub use netlink::{
21+
netlink_add_ip_addr, netlink_add_veth_pair, netlink_delete_link, netlink_set_link_down,
22+
netlink_set_link_up,
23+
};
2124
pub(crate) use perf_event::*;
2225
use thiserror::Error;
2326

aya/src/sys/netlink.rs

+182-8
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,28 @@ use std::{
22
collections::HashMap,
33
ffi::CStr,
44
io, mem,
5+
net::Ipv4Addr,
56
os::fd::{AsRawFd as _, BorrowedFd, FromRawFd as _, OwnedFd},
67
ptr, slice,
78
};
89

910
use libc::{
10-
getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket,
11-
AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFF_UP, IFLA_XDP, NETLINK_EXT_ACK, NETLINK_ROUTE,
11+
getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket, AF_INET,
12+
AF_NETLINK, AF_UNSPEC, ETH_P_ALL, IFA_F_PERMANENT, IFA_LOCAL, IFF_UP, IFLA_IFNAME,
13+
IFLA_INFO_DATA, IFLA_INFO_KIND, IFLA_LINKINFO, IFLA_XDP, NETLINK_EXT_ACK, NETLINK_ROUTE,
1214
NLA_ALIGNTO, NLA_F_NESTED, NLA_TYPE_MASK, NLMSG_DONE, NLMSG_ERROR, NLM_F_ACK, NLM_F_CREATE,
13-
NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELTFILTER, RTM_GETTFILTER,
14-
RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK, SOCK_RAW, SOL_NETLINK,
15+
NLM_F_DUMP, NLM_F_ECHO, NLM_F_EXCL, NLM_F_MULTI, NLM_F_REQUEST, RTM_DELLINK, RTM_DELTFILTER,
16+
RTM_GETTFILTER, RTM_NEWADDR, RTM_NEWLINK, RTM_NEWQDISC, RTM_NEWTFILTER, RTM_SETLINK,
17+
RT_SCOPE_UNIVERSE, SOCK_RAW, SOL_NETLINK,
1518
};
1619
use thiserror::Error;
1720

1821
use crate::{
1922
generated::{
20-
ifinfomsg, tcmsg, IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS, NLMSG_ALIGNTO,
21-
TCA_BPF_FD, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT, TCA_BPF_NAME, TCA_KIND, TCA_OPTIONS,
22-
TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK, TC_H_UNSPEC, XDP_FLAGS_REPLACE,
23+
ifaddrmsg, ifinfomsg, tcmsg, IFLA_XDP_EXPECTED_FD, IFLA_XDP_FD, IFLA_XDP_FLAGS,
24+
NLMSG_ALIGNTO, TCA_BPF_FD, TCA_BPF_FLAGS, TCA_BPF_FLAG_ACT_DIRECT, TCA_BPF_NAME, TCA_KIND,
25+
TCA_OPTIONS, TC_H_CLSACT, TC_H_INGRESS, TC_H_MAJ_MASK, TC_H_UNSPEC, VETH_INFO_PEER,
26+
XDP_FLAGS_REPLACE,
2327
},
2428
programs::TcAttachType,
2529
util::tc_handler_make,
@@ -266,6 +270,63 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
266270
Ok(filter_info)
267271
}
268272

273+
#[doc(hidden)]
274+
pub unsafe fn netlink_add_veth_pair(name1: &CStr, name2: &CStr) -> Result<(), io::Error> {
275+
let sock = NetlinkSocket::open()?;
276+
277+
// Safety: Request is POD so this is safe
278+
let mut req = mem::zeroed::<Request>();
279+
280+
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<ifinfomsg>();
281+
req.header = nlmsghdr {
282+
nlmsg_len: nlmsg_len as u32,
283+
nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE) as u16,
284+
nlmsg_type: RTM_NEWLINK,
285+
nlmsg_pid: 0,
286+
nlmsg_seq: 1,
287+
};
288+
req.if_info.ifi_family = AF_UNSPEC as u8;
289+
req.if_info.ifi_index = 0;
290+
req.if_info.ifi_flags = 0;
291+
req.if_info.ifi_change = 0;
292+
293+
let attrs_buf = request_attributes(&mut req, nlmsg_len);
294+
295+
// add IFLA_IFNAME
296+
let ifname_len = write_attr_bytes(attrs_buf, 0, IFLA_IFNAME, name1.to_bytes_with_nul())?;
297+
298+
// add IFLA_LINKINFO which includes IFLA_INFO_KIND and IFLA_INFO_DATA
299+
let mut linkinfo = NestedAttrs::new(&mut attrs_buf[ifname_len..], IFLA_LINKINFO);
300+
linkinfo.write_attr_bytes(IFLA_INFO_KIND, b"veth")?;
301+
302+
linkinfo.write_nested_attrs(IFLA_INFO_DATA, |info_data| {
303+
info_data.write_nested_attrs(VETH_INFO_PEER as u16, |info_peer| {
304+
// Safety: ifinfomsg is POD so this is safe
305+
let mut peer_if_info = mem::zeroed::<ifinfomsg>();
306+
peer_if_info.ifi_family = AF_UNSPEC as u8;
307+
peer_if_info.ifi_index = 0;
308+
peer_if_info.ifi_flags = 0;
309+
peer_if_info.ifi_change = 0;
310+
311+
info_peer.write_bytes(bytes_of(&peer_if_info))?;
312+
313+
// add IFLA_IFNAME
314+
info_peer.write_attr_bytes(IFLA_IFNAME, name2.to_bytes_with_nul())?;
315+
316+
Ok(())
317+
})?;
318+
Ok(())
319+
})?;
320+
321+
let linkinfo_len = linkinfo.finish()?;
322+
323+
req.header.nlmsg_len += align_to(ifname_len + linkinfo_len, NLA_ALIGNTO as usize) as u32;
324+
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
325+
sock.recv()?;
326+
327+
Ok(())
328+
}
329+
269330
#[doc(hidden)]
270331
pub unsafe fn netlink_set_link_up(if_index: i32) -> Result<(), io::Error> {
271332
let sock = NetlinkSocket::open()?;
@@ -292,11 +353,100 @@ pub unsafe fn netlink_set_link_up(if_index: i32) -> Result<(), io::Error> {
292353
Ok(())
293354
}
294355

356+
#[doc(hidden)]
357+
pub unsafe fn netlink_set_link_down(if_index: i32) -> Result<(), io::Error> {
358+
let sock = NetlinkSocket::open()?;
359+
360+
// Safety: Request is POD so this is safe
361+
let mut req = mem::zeroed::<Request>();
362+
363+
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<ifinfomsg>();
364+
req.header = nlmsghdr {
365+
nlmsg_len: nlmsg_len as u32,
366+
nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK) as u16,
367+
nlmsg_type: RTM_SETLINK,
368+
nlmsg_pid: 0,
369+
nlmsg_seq: 1,
370+
};
371+
req.if_info.ifi_family = AF_UNSPEC as u8;
372+
req.if_info.ifi_index = if_index;
373+
req.if_info.ifi_flags = 0;
374+
req.if_info.ifi_change = IFF_UP as u32;
375+
376+
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
377+
sock.recv()?;
378+
379+
Ok(())
380+
}
381+
382+
#[doc(hidden)]
383+
pub unsafe fn netlink_delete_link(if_index: i32) -> Result<(), io::Error> {
384+
let sock = NetlinkSocket::open()?;
385+
386+
// Safety: Request is POD so this is safe
387+
let mut req = mem::zeroed::<Request>();
388+
389+
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<ifinfomsg>();
390+
req.header = nlmsghdr {
391+
nlmsg_len: nlmsg_len as u32,
392+
nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK) as u16,
393+
nlmsg_type: RTM_DELLINK,
394+
nlmsg_pid: 0,
395+
nlmsg_seq: 1,
396+
};
397+
req.if_info.ifi_family = AF_UNSPEC as u8;
398+
req.if_info.ifi_index = if_index;
399+
req.if_info.ifi_flags = 0;
400+
req.if_info.ifi_change = 0;
401+
402+
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
403+
sock.recv()?;
404+
405+
Ok(())
406+
}
407+
408+
#[doc(hidden)]
409+
pub unsafe fn netlink_add_ip_addr(
410+
if_index: u32,
411+
ipv4_addr: Ipv4Addr,
412+
ipv4_prefix: u8,
413+
) -> Result<(), io::Error> {
414+
let sock = NetlinkSocket::open()?;
415+
416+
// Safety: AddrRequest is POD so this is safe
417+
let mut req = mem::zeroed::<AddrRequest>();
418+
419+
let nlmsg_len = mem::size_of::<nlmsghdr>() + mem::size_of::<ifaddrmsg>();
420+
req.header = nlmsghdr {
421+
nlmsg_len: nlmsg_len as u32,
422+
nlmsg_flags: (NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE) as u16,
423+
nlmsg_type: RTM_NEWADDR,
424+
nlmsg_pid: 0,
425+
nlmsg_seq: 1,
426+
};
427+
req.if_addr.ifa_family = AF_INET as u8;
428+
req.if_addr.ifa_prefixlen = ipv4_prefix;
429+
req.if_addr.ifa_flags = IFA_F_PERMANENT as u8;
430+
req.if_addr.ifa_scope = RT_SCOPE_UNIVERSE;
431+
req.if_addr.ifa_index = if_index;
432+
433+
let attrs_buf = request_attributes(&mut req, nlmsg_len);
434+
435+
// add IFA_LOCAL
436+
let local_len = write_attr_bytes(attrs_buf, 0, IFA_LOCAL, &ipv4_addr.octets())?;
437+
438+
req.header.nlmsg_len += align_to(local_len, NLA_ALIGNTO as usize) as u32;
439+
sock.send(&bytes_of(&req)[..req.header.nlmsg_len as usize])?;
440+
sock.recv()?;
441+
442+
Ok(())
443+
}
444+
295445
#[repr(C)]
296446
struct Request {
297447
header: nlmsghdr,
298448
if_info: ifinfomsg,
299-
attrs: [u8; 64],
449+
attrs: [u8; 128],
300450
}
301451

302452
#[repr(C)]
@@ -306,6 +456,13 @@ struct TcRequest {
306456
attrs: [u8; 64],
307457
}
308458

459+
#[repr(C)]
460+
struct AddrRequest {
461+
header: nlmsghdr,
462+
if_addr: ifaddrmsg,
463+
attrs: [u8; 64],
464+
}
465+
309466
struct NetlinkSocket {
310467
sock: OwnedFd,
311468
_nl_pid: u32,
@@ -505,6 +662,23 @@ impl<'a> NestedAttrs<'a> {
505662
Ok(size)
506663
}
507664

665+
fn write_bytes(&mut self, value: &[u8]) -> Result<usize, io::Error> {
666+
let size = write_bytes(self.buf, self.offset, value)?;
667+
self.offset += size;
668+
Ok(size)
669+
}
670+
671+
fn write_nested_attrs<F>(&mut self, attr_type: u16, f: F) -> Result<usize, io::Error>
672+
where
673+
F: FnOnce(&mut NestedAttrs<'_>) -> Result<(), io::Error>,
674+
{
675+
let mut nested_attrs = NestedAttrs::new(&mut self.buf[self.offset..], attr_type);
676+
f(&mut nested_attrs)?;
677+
let size = nested_attrs.finish()?;
678+
self.offset += size;
679+
Ok(size)
680+
}
681+
508682
fn finish(self) -> Result<usize, io::Error> {
509683
let nla_len = self.offset;
510684
let attr = nlattr {

0 commit comments

Comments
 (0)