@ -1,11 +1,12 @@
use std ::{ ffi ::CStr , io , mem , os ::unix ::io ::RawFd , ptr , slice } ;
use std ::{ collections ::HashMap , ffi ::CStr , io , mem , os ::unix ::io ::RawFd , ptr , slice } ;
use thiserror ::Error ;
use libc ::{
use libc ::{
c_int , close , getsockname , nlattr , nlmsgerr , nlmsghdr , recv , send , setsockopt , sockaddr_nl ,
c_int , close , getsockname , nlattr , nlmsgerr , nlmsghdr , recv , send , setsockopt , sockaddr_nl ,
socket , AF_NETLINK , AF_UNSPEC , ETH_P_ALL , IFLA_XDP , NETLINK_ROUTE , NLA_ALIGNTO , NLA_F_NESTED ,
socket , AF_NETLINK , AF_UNSPEC , ETH_P_ALL , IFLA_XDP , NETLINK_ROUTE , NLA_ALIGNTO , NLA_F_NESTED ,
NL MSG_DONE, NLMSG_ERROR , NLM_F_ACK , NLM_F_CREATE , NLM_F_ ECHO, NLM_F_EXCL , NLM_F_MULTI ,
NL A_TYPE_MASK, NL MSG_DONE, NLMSG_ERROR , NLM_F_ACK , NLM_F_CREATE , NLM_F_ DUMP, NLM_F_ECHO ,
NLM_F_ REQUEST, RTM_DELTFILTER , RTM_NEWQDISC , RTM_NEWTFILTER , RTM_SETLINK , SOCK_RAW ,
NLM_F_ EXCL, NLM_F_MULTI , NLM_F_REQUEST , RTM_DELTFILTER , RTM_GETTFILTER , RTM_NEWQDISC ,
SOL_NETLINK,
RTM_NEWTFILTER, RTM_SETLINK , SOCK_RAW , SOL_NETLINK,
} ;
} ;
use crate ::{
use crate ::{
@ -192,6 +193,54 @@ pub(crate) unsafe fn netlink_qdisc_detach(
Ok ( ( ) )
Ok ( ( ) )
}
}
pub ( crate ) unsafe fn netlink_find_filter_with_name (
if_index : i32 ,
attach_type : TcAttachType ,
name : & CStr ,
) -> Result < Vec < u32 > , io ::Error > {
let mut req = mem ::zeroed ::< TcRequest > ( ) ;
let nlmsg_len = mem ::size_of ::< nlmsghdr > ( ) + mem ::size_of ::< tcmsg > ( ) ;
req . header = nlmsghdr {
nlmsg_len : nlmsg_len as u32 ,
nlmsg_type : RTM_GETTFILTER ,
nlmsg_flags : ( NLM_F_REQUEST | NLM_F_DUMP ) as u16 ,
nlmsg_pid : 0 ,
nlmsg_seq : 1 ,
} ;
req . tc_info . tcm_family = AF_UNSPEC as u8 ;
req . tc_info . tcm_handle = 0 ; // auto-assigned, if not provided
req . tc_info . tcm_ifindex = if_index ;
req . tc_info . tcm_parent = attach_type . parent ( ) ;
let sock = NetlinkSocket ::open ( ) ? ;
sock . send ( & bytes_of ( & req ) [ .. req . header . nlmsg_len as usize ] ) ? ;
let mut prios = Vec ::new ( ) ;
for msg in sock . recv ( ) ? {
if msg . header . nlmsg_type ! = RTM_NEWTFILTER {
continue ;
}
let tc_msg = ptr ::read_unaligned ( msg . data . as_ptr ( ) as * const tcmsg ) ;
let priority = tc_msg . tcm_info > > 16 ;
let attrs = parse_attrs ( & msg . data [ mem ::size_of ::< tcmsg > ( ) .. ] ) ? ;
if let Some ( opts ) = attrs . get ( & ( TCA_OPTIONS as u16 ) ) {
let opts = parse_attrs ( & opts . data ) ? ;
if let Some ( f_name ) = opts . get ( & ( TCA_BPF_NAME as u16 ) ) {
if let Ok ( f_name ) = CStr ::from_bytes_with_nul ( f_name . data ) {
if name = = f_name {
prios . push ( priority ) ;
}
}
}
}
}
Ok ( prios )
}
#[ repr(C) ]
#[ repr(C) ]
struct Request {
struct Request {
header : nlmsghdr ,
header : nlmsghdr ,
@ -447,8 +496,84 @@ fn write_bytes(buf: &mut [u8], offset: usize, value: &[u8]) -> Result<usize, io:
Ok ( align_len )
Ok ( align_len )
}
}
struct NlAttrsIterator < ' a > {
attrs : & ' a [ u8 ] ,
offset : usize ,
}
impl < ' a > NlAttrsIterator < ' a > {
fn new ( attrs : & [ u8 ] ) -> NlAttrsIterator {
NlAttrsIterator { attrs , offset : 0 }
}
}
impl < ' a > Iterator for NlAttrsIterator < ' a > {
type Item = Result < NlAttr < ' a > , NlAttrError > ;
fn next ( & mut self ) -> Option < Self ::Item > {
let buf = & self . attrs [ self . offset .. ] ;
if buf . len ( ) = = 0 {
return None ;
}
if NLA_HDR_LEN > buf . len ( ) {
self . offset = buf . len ( ) ;
return Some ( Err ( NlAttrError ::InvalidBufferLength {
size : buf . len ( ) ,
expected : NLA_HDR_LEN ,
} ) ) ;
}
let attr = unsafe { ptr ::read_unaligned ( buf . as_ptr ( ) as * const nlattr ) } ;
let len = attr . nla_len as usize ;
let align_len = align_to ( len , NLA_ALIGNTO as usize ) ;
if len < NLA_HDR_LEN {
return Some ( Err ( NlAttrError ::InvalidHeaderLength ( len ) ) ) ;
}
if align_len > buf . len ( ) {
return Some ( Err ( NlAttrError ::InvalidBufferLength {
size : buf . len ( ) ,
expected : align_len ,
} ) ) ;
}
let data = & buf [ NLA_HDR_LEN .. len ] ;
self . offset + = align_len ;
Some ( Ok ( NlAttr { header : attr , data } ) )
}
}
fn parse_attrs ( buf : & [ u8 ] ) -> Result < HashMap < u16 , NlAttr > , NlAttrError > {
let mut attrs = HashMap ::new ( ) ;
for attr in NlAttrsIterator ::new ( buf ) {
let attr = attr ? ;
attrs . insert ( attr . header . nla_type & NLA_TYPE_MASK as u16 , attr ) ;
}
Ok ( attrs )
}
#[ derive(Clone) ]
struct NlAttr < ' a > {
header : nlattr ,
data : & ' a [ u8 ] ,
}
}
#[ derive(Debug, Error, PartialEq) ]
enum NlAttrError {
#[ error( " invalid buffer size `{size}`, expected `{expected}` " ) ]
InvalidBufferLength { size : usize , expected : usize } ,
#[ error( " invalid nlattr header length `{0}` " ) ]
InvalidHeaderLength ( usize ) ,
}
impl From < NlAttrError > for io ::Error {
fn from ( e : NlAttrError ) -> io ::Error {
io ::Error ::new ( io ::ErrorKind ::Other , e )
}
}
unsafe fn request_attributes < T > ( req : & mut T , msg_len : usize ) -> & mut [ u8 ] {
unsafe fn request_attributes < T > ( req : & mut T , msg_len : usize ) -> & mut [ u8 ] {
let attrs_addr = align_to (
let attrs_addr = align_to (
@ -463,8 +588,11 @@ fn bytes_of<T>(val: &T) -> &[u8] {
let size = mem ::size_of ::< T > ( ) ;
let size = mem ::size_of ::< T > ( ) ;
unsafe { slice ::from_raw_parts ( slice ::from_ref ( val ) . as_ptr ( ) . cast ( ) , size ) }
unsafe { slice ::from_raw_parts ( slice ::from_ref ( val ) . as_ptr ( ) . cast ( ) , size ) }
}
}
#[ cfg(test) ]
#[ cfg(test) ]
mod tests {
mod tests {
use std ::{ convert ::TryInto , ffi ::CString } ;
use super ::* ;
use super ::* ;
#[ test ]
#[ test ]
@ -510,4 +638,88 @@ mod tests {
} ;
} ;
assert_eq! ( fd , 24 ) ;
assert_eq! ( fd , 24 ) ;
}
}
#[ test ]
fn test_nlattr_iterator_empty ( ) {
let mut iter = NlAttrsIterator ::new ( & [ ] ) ;
assert! ( iter . next ( ) . is_none ( ) ) ;
}
#[ test ]
fn test_nlattr_iterator_one ( ) {
let mut buf = [ 0 ; NLA_HDR_LEN + mem ::size_of ::< u32 > ( ) ] ;
write_attr ( & mut buf , 0 , IFLA_XDP_FD as u16 , 42 u32 ) . unwrap ( ) ;
let mut iter = NlAttrsIterator ::new ( & buf ) ;
let attr = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! ( attr . header . nla_type , IFLA_XDP_FD as u16 ) ;
assert_eq! ( attr . data . len ( ) , mem ::size_of ::< u32 > ( ) ) ;
assert_eq! ( u32 ::from_ne_bytes ( attr . data . try_into ( ) . unwrap ( ) ) , 42 ) ;
assert! ( iter . next ( ) . is_none ( ) ) ;
}
#[ test ]
fn test_nlattr_iterator_many ( ) {
let mut buf = [ 0 ; ( NLA_HDR_LEN + mem ::size_of ::< u32 > ( ) ) * 2 ] ;
write_attr ( & mut buf , 0 , IFLA_XDP_FD as u16 , 42 u32 ) . unwrap ( ) ;
write_attr (
& mut buf ,
NLA_HDR_LEN + mem ::size_of ::< u32 > ( ) ,
IFLA_XDP_EXPECTED_FD as u16 ,
12 u32 ,
)
. unwrap ( ) ;
let mut iter = NlAttrsIterator ::new ( & buf ) ;
let attr = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! ( attr . header . nla_type , IFLA_XDP_FD as u16 ) ;
assert_eq! ( attr . data . len ( ) , mem ::size_of ::< u32 > ( ) ) ;
assert_eq! ( u32 ::from_ne_bytes ( attr . data . try_into ( ) . unwrap ( ) ) , 42 ) ;
let attr = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! ( attr . header . nla_type , IFLA_XDP_EXPECTED_FD as u16 ) ;
assert_eq! ( attr . data . len ( ) , mem ::size_of ::< u32 > ( ) ) ;
assert_eq! ( u32 ::from_ne_bytes ( attr . data . try_into ( ) . unwrap ( ) ) , 12 ) ;
assert! ( iter . next ( ) . is_none ( ) ) ;
}
#[ test ]
fn test_nlattr_iterator_nested ( ) {
let mut buf = [ 0 ; 1024 ] ;
let mut options = NestedAttrs ::new ( & mut buf , TCA_OPTIONS as u16 ) ;
options . write_attr ( TCA_BPF_FD as u16 , 42 ) . unwrap ( ) ;
let name = CString ::new ( "foo" ) . unwrap ( ) ;
options
. write_attr_bytes ( TCA_BPF_NAME as u16 , name . to_bytes_with_nul ( ) )
. unwrap ( ) ;
options . finish ( ) . unwrap ( ) ;
let mut iter = NlAttrsIterator ::new ( & buf ) ;
let outer = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! (
outer . header . nla_type & NLA_TYPE_MASK as u16 ,
TCA_OPTIONS as u16
) ;
let mut iter = NlAttrsIterator ::new ( & outer . data ) ;
let inner = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! (
inner . header . nla_type & NLA_TYPE_MASK as u16 ,
TCA_BPF_FD as u16
) ;
let inner = iter . next ( ) . unwrap ( ) . unwrap ( ) ;
assert_eq! (
inner . header . nla_type & NLA_TYPE_MASK as u16 ,
TCA_BPF_NAME as u16
) ;
let name = CStr ::from_bytes_with_nul ( inner . data ) . unwrap ( ) ;
assert_eq! ( name . to_string_lossy ( ) , "foo" ) ;
}
}
}