Using libc::ioctl to read interface flags

Hi all,

I'm pretty new to rust, I'm trying to open a an interface as promiscuos and in order to do that I'm trying to use ioctl to read and the write the flags of the interface. I'm just "traslating" some simple C code to rust

This is the C code

int setpromisc(int fd, int promisc, char* ifname, int set){

    int res;
    struct ifreq ifreq;

    if(!promisc)
        return 0;

    memset(&ifreq, 0, sizeof(ifreq));
    strncpy(ifreq.ifr_name, ifname, strlen(ifname));

    //read flags of the interface
    res = ioctl(fd, SIOCGIFFLAGS, &ifreq);
    if (res==-1){
        perror("Error on ioctl:SIOCGIFFLAGS");
        return -1;
    }
    ifreq.ifr_flags = set ? ifreq.ifr_flags | IFF_PROMISC : ifreq.ifr_flags ^ IFF_PROMISC;

    //set interface flags
    res = ioctl(fd, SIOCSIFFLAGS, &ifreq);
    if (res==-1){
        perror("Error on ioctl:SIOCGIFFLAGS");
        return -1;
    }
    return res;

}

The rust code is

//man 7 netdevice
#[repr(C)]
pub struct ifmap {
    mem_start:  libc::c_ulong,
    mem_end:    libc::c_ulong,
    base_addr:   libc::c_uchar,
    irq:         libc::c_uchar,
    dma:         libc::c_uchar,
    port:        libc::c_uchar,
}

//  struct ifmap {
//                       unsigned long   mem_start;
//                       unsigned long   mem_end;
//                       unsigned short  base_addr;
//                       unsigned char   irq;
//                       unsigned char   dma;
//                       unsigned char   port;
//                   };


//man 7 netdevice
#[repr(C)]
pub struct ifreq{
    pub ifr_name: [libc::c_uchar; 16],

        ifr_addr:       libc::sockaddr,
        ifr_dstaddr:    libc::sockaddr,
        ifr_broadaddr:  libc::sockaddr,
        ifr_netmask:    libc::sockaddr,
        ifr_hwaddr:     libc::sockaddr,
        ifr_flags:      libc::c_short,
        ifr_ifindex:    libc::c_int,
        ifr_metric:     libc::c_int,
        ifr_mtu:        libc::c_int,
        ifr_map:        ifmap,
        ifr_slave:      [libc::c_uchar; 16],
        ifr_newname:    [libc::c_uchar; 16],
        ifr_data:       *const libc::c_uchar,


}


// struct ifreq {
//                char ifr_name[IFNAMSIZ]; /* Interface name */
//                union {
//                    struct sockaddr ifr_addr;
//                    struct sockaddr ifr_dstaddr;
//                    struct sockaddr ifr_broadaddr;
//                    struct sockaddr ifr_netmask;
//                    struct sockaddr ifr_hwaddr;
//                    short           ifr_flags;
//                    int             ifr_ifindex;
//                    int             ifr_metric;
//                    int             ifr_mtu;
//                    struct ifmap    ifr_map;
//                    char            ifr_slave[IFNAMSIZ];
//                    char            ifr_newname[IFNAMSIZ];
//                    char           *ifr_data;
//                };
//            };

fn set_promisc(fd: &i32, ifname: &String, set: bool)  -> libc::c_int {

    // Enable promiscuous mode
    unsafe {

        let res;
        let mut ifr: ifreq = mem::zeroed();

        let sb = ifname.as_bytes();
        //make a copy to an array of size 16
        let mut arr : [u8;16] = [0;16];
        let mut i = 0;
        for c in sb {
            arr[i] = *c;
            i = i+1;
        }
        arr[i] = '\0' as u8;
        ifr.ifr_name = arr;



        //read flags of the interface
        res = libc::ioctl(*fd, libc::SIOCGIFFLAGS,&ifr);
        if res == -1{
            return -1 as i32;
        }

        println!("Read FLAGS = {}", ifr.ifr_flags);

        if set {
            ifr.ifr_flags = ifr.ifr_flags | (libc::IFF_PROMISC as libc::c_short);
            println!("Should set the flag! NEW FLAGS = {}", ifr.ifr_flags);
        } else {
            ifr.ifr_flags = ifr.ifr_flags ^ (libc::IFF_PROMISC as libc::c_short);
            println!("Should set the remove the flag! NEW FLAGS = {}", ifr.ifr_flags);
        }

        return libc::ioctl(*fd, libc::SIOCSIFFLAGS, &ifr);

    }
}

As you can see I have added the missing structure and I'm passing the pointer of the mutable structure to ioctl, withou getting any result

This is an example of the output

Read FLAGS = 0
Should set the flag! NEW FLAGS = 256

And with strace I can see

....
ioctl(6, SIOCGIFFLAGS, {ifr_name="h2", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
....
ioctl(6, SIOCSIFFLAGS, {ifr_name="h2", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
...

So it is actually reading the modes, then are not stored in the strucure used for setting the flags

Any suggestion?

This should be libc::c_ushort per the C struct definition in if.h.

The members of ifreq must match the C definition from if.h. In this case, everything after ifr_name needs to be inside a union to match the memory layout of struct ifreq.

Right! I misread the type...

You mean I have to declare as a named union? Even if in C it is an anonymous union

Like this:

#[repr(C)]
pub struct ifreq{
    pub ifr_name: [libc::c_uchar; 16],
    f : union test {
        ifr_addr:       libc::sockaddr,
        ifr_dstaddr:    libc::sockaddr,
        ifr_broadaddr:  libc::sockaddr,
        ifr_netmask:    libc::sockaddr,
        ifr_hwaddr:     libc::sockaddr,
        ifr_flags:      libc::c_short,
        ifr_ifindex:    libc::c_int,
        ifr_metric:     libc::c_int,
        ifr_mtu:        libc::c_int,
        ifr_map:        ifmap,
        ifr_slave:      [libc::c_uchar; 16],
        ifr_newname:    [libc::c_uchar; 16],
        ifr_data:       *const libc::c_uchar,
    },

}