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
use futures::Future;

use rtnetlink::constants::{IFF_UP, NLM_F_ACK, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST};
use rtnetlink::{
    LinkFlags, LinkInfo, LinkInfoData, LinkInfoKind, LinkInfoVlan, LinkMessage, LinkNla,
    NetlinkFlags, NetlinkMessage, RtnlMessage,
};

use connection::ConnectionHandle;
use errors::NetlinkIpError;

use Stream2Ack;

lazy_static! {
    // Flags for `ip link add`
    static ref ADD_FLAGS: NetlinkFlags = NetlinkFlags::from(NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE);
}

/// A request to create a new link. This is equivalent to the `ip link add` commands.
///
/// A few methods for common actions (creating a veth pair, creating a vlan interface, etc.) are
/// provided, but custom requests can be made using the [`message_mut()`](#method.message_mut)
/// accessor.
pub struct LinkAddRequest {
    handle: ConnectionHandle,
    message: LinkMessage,
}

impl LinkAddRequest {
    pub(crate) fn new(handle: ConnectionHandle) -> Self {
        let mut message = LinkMessage::new();
        message.header_mut();
        LinkAddRequest { handle, message }
    }

    /// Execute the request.
    pub fn execute(self) -> impl Future<Item = (), Error = NetlinkIpError> {
        let LinkAddRequest {
            mut handle,
            message,
        } = self;
        let mut req = NetlinkMessage::from(RtnlMessage::NewLink(message));
        req.header_mut().set_flags(*ADD_FLAGS);
        Stream2Ack::new(handle.request(req))
    }

    /// Return a mutable reference to the request message.
    ///
    /// # Example
    ///
    /// Let's say we want to create a vlan interface on a link with id 6. By default, the
    /// [`vlan()`](#method.vlan) method would create a request with the `IFF_UP` link set, so that the
    /// interface is up after creation. If we want to create a interface tha tis down by default we
    /// could do:
    ///
    /// ```rust,no_run
    /// extern crate futures;
    /// extern crate iproute2;
    /// extern crate tokio_core;
    ///
    /// use std::thread::spawn;
    ///
    /// use futures::Future;
    /// use tokio_core::reactor::Core;
    ///
    /// use iproute2::new_connection;
    ///
    /// fn main() {
    ///     let (connection, handle) = new_connection().unwrap();
    ///     spawn(move || Core::new().unwrap().run(connection));
    ///     let vlan_id = 100;
    ///     let link_id = 6;
    ///     let mut request = handle.link().add().vlan("my-vlan-itf".into(), link_id, vlan_id);
    ///     // unset the IFF_UP flag before sending the request
    ///     request.message_mut().header_mut().flags_mut().unset_up();
    ///     request.message_mut().header_mut().change_mask_mut().unset_up();
    ///     // send the request
    ///     request.execute().wait().unwrap();
    /// }
    pub fn message_mut(&mut self) -> &mut LinkMessage {
        &mut self.message
    }

    /// Create a dummy link.
    /// This is equivalent to `ip link add NAME type dummy`.
    pub fn dummy(self, name: String) -> Self {
        self.name(name).link_info(LinkInfoKind::Dummy, None).up()
    }

    /// Create a veth pair.
    /// kThis is equivalent to `ip link add NAME1 type veth peer name NAME2`.
    pub fn veth(self, name: String, peer_name: String) -> Self {
        let mut peer = LinkMessage::new();
        peer.nlas_mut().push(LinkNla::IfName(peer_name));

        self.name(name)
            .link_info(LinkInfoKind::Veth, Some(LinkInfoData::Veth(peer)))
            .up()
    }

    /// Create VLAN on a link.
    /// This is equivalent to `ip link add link LINK name NAME type vlan id VLAN_ID`,
    /// but instead of specifying a link name (`LINK`), we specify a link index.
    pub fn vlan(self, name: String, index: u32, vlan_id: u16) -> Self {
        self.name(name)
            .link_info(
                LinkInfoKind::Vlan,
                Some(LinkInfoData::Vlan(vec![LinkInfoVlan::Id(vlan_id)])),
            )
            .append_nla(LinkNla::Link(index))
            .up()
    }

    fn up(mut self) -> Self {
        self.message_mut()
            .header_mut()
            .set_flags(LinkFlags::from(IFF_UP))
            .set_change_mask(LinkFlags::from(IFF_UP));
        self
    }

    fn link_info(self, kind: LinkInfoKind, data: Option<LinkInfoData>) -> Self {
        let mut link_info_nlas = vec![LinkInfo::Kind(kind)];
        if let Some(data) = data {
            link_info_nlas.push(LinkInfo::Data(data));
        }
        self.append_nla(LinkNla::LinkInfo(link_info_nlas))
    }

    fn name(mut self, name: String) -> Self {
        self.message.append_nla(LinkNla::IfName(name));
        self
    }

    fn append_nla(mut self, nla: LinkNla) -> Self {
        self.message.nlas_mut().push(nla);
        self
    }
}