1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: MIT

use crate::{
    packet::{
        nlas::link::Nla,
        LinkMessage,
        NetlinkMessage,
        RtnlMessage,
        IFF_NOARP,
        IFF_PROMISC,
        IFF_UP,
        NLM_F_ACK,
        NLM_F_CREATE,
        NLM_F_EXCL,
        NLM_F_REQUEST,
    },
    try_nl,
    Error,
    Handle,
};
use futures::stream::StreamExt;
use std::os::unix::io::RawFd;

pub struct LinkSetRequest {
    handle: Handle,
    message: LinkMessage,
}

impl LinkSetRequest {
    pub(crate) fn new(handle: Handle, index: u32) -> Self {
        let mut message = LinkMessage::default();
        message.header.index = index;
        LinkSetRequest { handle, message }
    }

    /// Execute the request
    pub async fn execute(self) -> Result<(), Error> {
        let LinkSetRequest {
            mut handle,
            message,
        } = self;
        let mut req = NetlinkMessage::from(RtnlMessage::SetLink(message));
        req.header.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;

        let mut response = handle.request(req)?;
        while let Some(message) = response.next().await {
            try_nl!(message);
        }
        Ok(())
    }

    /// Return a mutable reference to the request
    pub fn message_mut(&mut self) -> &mut LinkMessage {
        &mut self.message
    }

    /// Attach the link to a bridge (its _master_). This is equivalent to `ip link set LINK master
    /// BRIDGE`. To succeed, both the bridge and the link that is being attached must be UP.
    ///
    /// To Remove a link from a bridge, set its master to zero.
    /// This is equvalent to `ip link set LINK nomaster`
    pub fn master(mut self, master_index: u32) -> Self {
        self.message.nlas.push(Nla::Master(master_index));
        self
    }

    /// Detach the link from its _master_. This is equivalent to `ip link set LINK nomaster`.
    ///To succeed, the link that is being detached must be UP.
    pub fn nomaster(mut self) -> Self {
        self.message.nlas.push(Nla::Master(0u32));
        self
    }

    /// Set the link with the given index up (equivalent to `ip link set dev DEV up`)
    pub fn up(mut self) -> Self {
        self.message.header.flags |= IFF_UP;
        self.message.header.change_mask |= IFF_UP;
        self
    }

    /// Set the link with the given index down (equivalent to `ip link set dev DEV down`)
    pub fn down(mut self) -> Self {
        self.message.header.flags &= !IFF_UP;
        self.message.header.change_mask |= IFF_UP;
        self
    }

    /// Enable or disable promiscious mode of the link with the given index (equivalent to `ip link set dev DEV promisc on/off`)
    pub fn promiscuous(mut self, enable: bool) -> Self {
        if enable {
            self.message.header.flags |= IFF_PROMISC;
        } else {
            self.message.header.flags &= !IFF_PROMISC;
        }
        self.message.header.change_mask |= IFF_PROMISC;
        self
    }

    /// Enable or disable the ARP protocol of the link with the given index (equivalent to `ip link set dev DEV arp on/off`)
    pub fn arp(mut self, enable: bool) -> Self {
        if enable {
            self.message.header.flags &= !IFF_NOARP;
        } else {
            self.message.header.flags |= IFF_NOARP;
        }
        self.message.header.change_mask |= IFF_NOARP;
        self
    }

    /// Set the name of the link with the given index (equivalent to `ip link set DEV name NAME`)
    pub fn name(mut self, name: String) -> Self {
        self.message.nlas.push(Nla::IfName(name));
        self
    }

    /// Set the mtu of the link with the given index (equivalent to `ip link set DEV mtu MTU`)
    pub fn mtu(mut self, mtu: u32) -> Self {
        self.message.nlas.push(Nla::Mtu(mtu));
        self
    }

    /// Set the hardware address of the link with the given index (equivalent to `ip link set DEV address ADDRESS`)
    pub fn address(mut self, address: Vec<u8>) -> Self {
        self.message.nlas.push(Nla::Address(address));
        self
    }

    /// Move this network device into the network namespace of the process with the given `pid`.
    pub fn setns_by_pid(mut self, pid: u32) -> Self {
        self.message.nlas.push(Nla::NetNsPid(pid));
        self
    }

    /// Move this network device into the network namespace corresponding to the given file
    /// descriptor.
    pub fn setns_by_fd(mut self, fd: RawFd) -> Self {
        self.message.nlas.push(Nla::NetNsFd(fd));
        self
    }
}