jbaublitz / neli

Rust type safe netlink library
BSD 3-Clause "New" or "Revised" License
180 stars 35 forks source link

Copying `Rtmsg` value #248

Open pseusys opened 2 weeks ago

pseusys commented 2 weeks ago

I would like to implement something close to ip route save command, store all the contents of a routing table and clear it.

For that purpose I have implemented a simple function:

fn save_table(table_idx: i32) -> Result<Vec<Rtmsg>, Box<dyn Error>> {
    let table_const = RtTable::UnrecognizedConst(table_idx as u8);
    let mut receiver_socket = NlSocketHandle::connect(NlFamily::Route, None, &[])?;
    let mut sender_socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();

    let mut table_data = Vec::new();
    let send_payload = Rtmsg {rtm_family: RtAddrFamily::Inet, rtm_dst_len: 0, rtm_src_len: 0, rtm_tos: 0, rtm_table: table_const, rtm_protocol: Rtprot::Unspec, rtm_scope: RtScope::Universe, rtm_type: Rtn::Unspec, rtm_flags: RtmFFlags::empty(), rtattrs: RtBuffer::new()};
    let send_msg = Nlmsghdr::new(None, Rtm::Getroute, NlmFFlags::new(&[NlmF::Request, NlmF::Dump]), None, None, NlPayload::Payload(send_payload));
    receiver_socket.send(send_msg).unwrap();
    for response in receiver_socket.iter(false) {
        let header: Nlmsghdr<Rtm, Rtmsg> = response.unwrap();
        if let NlPayload::Payload(recv_payload) = header.nl_payload {
            if recv_payload.rtm_table == table_const {
                table_data.push(recv_payload);
                let rm_msg = Nlmsghdr::new(None, Rtm::Delroute, NlmFFlags::new(&[NlmF::Request]), None, None, header.nl_payload);  // Here an error appears, since `header.nl_payload` is partially moved.
                sender_socket.send(rm_msg).unwrap();
            }
        }
    }

    Ok(table_data)
}
}

For now I am struggling with copying received Rtmsg instances. Basically I can not store them in an array and also use for removing from the table call at the same time, since they do not implement Copy trait. How could I possibly save the message instance for future use and also remove it from the table? Is there a way to copy it?

pseusys commented 2 weeks ago

Maybe I'm just not proficient with rust enough yet tho :(

jbaublitz commented 2 weeks ago

Hi @pseusys, have you tried .clone()? I'm pretty sure that should be implemented on all data structures.

pseusys commented 2 weeks ago

Yes. I doublechecked that: it is implemented, but only in the latest releases (0.7.0), that are still release candidates. In the latest stable release, there is no Clone trait available.

jbaublitz commented 2 weeks ago

I see your problem after digging into this a little bit deeper. Here's what I would suggest as a workaround. It's not ergonomic, but I think it should work:

        if let NlPayload::Payload(recv_payload) = header.nl_payload {
            if recv_payload.rtm_table == table_const {
                let rm_msg = Nlmsghdr::new(None, Rtm::Delroute, NlmFFlags::new(&[NlmF::Request]), None, None, /* Reconstruct an NlPayload::(Rtmsg::new()) here. All of the fields are able to be copied and are public except for the attributes which can be borrowed. That should be sufficient for sending the message. /*);
                sender_socket.send(rm_msg).unwrap();
                table_data.push(recv_payload); // The above will not consume the data structure, just borrow it, so then you can move ownership to the vector.
            }
        }

Let me know if you need any more help with this.

pseusys commented 1 week ago

I will come back to it again once 0.7.0 is released and try different solutions.