cast a `&&*const libc::foreignStruct` to a `&libc::foreignStruct`

74 Views Asked by At

I'm using nftnl-rs to build a table of nftables rules, with great success. But now I want to know if there is already a table with the same name in the system. I found two functions in nftnl-rs' nftnl/src/table.rs that seem to be exactly made for this : get_tables_nlmsg() and get_tables_cb(). However I was not able to use them successfully.get_tables_cb()'s docstring says it was made to process the output of the former, but get_tables_nlmsg() returns a Vec<u8>, apparently containing the memory address of the created nlmsghdr, whereas get_tables_cb() takes an &nlmsghdr as an argument.

Specifically, I tried :

use std::collections::HashSet;
use std::ffi::CString;
use nftnl{self, nftnl_sys::libc};

fn dump_tables() -> Result<(), Box<dyn std::error::Error> {
    let mut tables: &mut HashSet<CString> = &mut HashSet::new();
    let mut buffer = nftnl::table::get_tables_nlmsg(0);
    let buffer = buffer.as_ptr() as *const libc::nlmsghdr;

    nftnl::table::get_tables_cb(&&buffer, &mut tables);
    println!("{:?}", tables);
    Ok(())
}

rustc complains that it expected an &nlmsghdr and got a &&*const nlmsghdr, and I was not able to cast the former into the latter. I browsed the source code and the examples without seeing this casting case appearing. Jumping back and forth in the source, I found a part of the Nomicon talking about opaque structs that seems to apply to nlmsghdr, but doesn't solve my problem either. In fact I'm a little confused with the function declaration :

pub fn get_tables_cb(header: &libc::nlmsghdr, tables: &mut HashSet<CString>) -> libc::c_int

If *const libc::nlmsghdr is a foreign Struct, what on earth can &libc::nlmsghdr be ? Could it be that there's a typo somewhere in nftnl-rs' source ?

1

There are 1 best solutions below

0
On

Using Coder-256's suggestion, one can find mnl::cb_run2() from the sister package mnl-rs, that may use nftnl::tables::get_tables_cb() as a callback when receiving a netlink packet. Since netlink packets may come multi-packed if the data is too big to fit in one NlMsg, one would probably use it like this :

use std::collections::HashSet;
use mnl;
use nftnl;

fn dump_tables() -> Result<(), Box<dyn std::error::Error>> {
    let seq = 0;
    let tables = &mut HashSet::new();
    let mut buffer = nftnl::table::get_tables_nlmsg(seq);
    let socket = mnl::Socket::new(mnl::Bus::Netfilter)?;

    socket.send(&buffer)?;
    // Answer may be multi-packed, loop over what we get.
    loop {
        let chars_written = socket.recv(&mut *buffer)?;
        if chars_written == 0 { break; }
        let message = &buffer[..chars_written];
        match mnl::cb_run2(message, seq, socket.portid(), nftnl::table::get_tables_cb, tables)? {
            mnl::CbResult::Stop => break,
            mnl::CbResult::Ok => ()
        }
    }
    println!("{:?}", tables);
    Ok(())
}

I must confess I still don't know what &libc::nlmsghdr means exactly, how and why one would build one ; I will gladly accept any answer that is able to enlighten that point.