1use ipnet::IpNet;
2use netlink_packet_core::{
3 NetlinkMessage, NetlinkPayload, NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_REPLACE,
4 NLM_F_REQUEST,
5};
6use netlink_packet_route::{
7 address::{self, AddressHeader, AddressMessage},
8 link::{self, LinkFlags, LinkHeader, LinkMessage, State},
9 route::{self, RouteHeader, RouteMessage},
10 AddressFamily, RouteNetlinkMessage,
11};
12use netlink_request::netlink_request_rtnl;
13use std::{io, net::IpAddr};
14use wireguard_control::InterfaceName;
15
16fn if_nametoindex(interface: &InterfaceName) -> Result<u32, io::Error> {
17 match unsafe { libc::if_nametoindex(interface.as_ptr()) } {
18 0 => Err(io::Error::new(
19 io::ErrorKind::NotFound,
20 format!("couldn't find interface '{interface}'."),
21 )),
22 index => Ok(index),
23 }
24}
25
26pub fn set_up(interface: &InterfaceName, mtu: u32) -> Result<(), io::Error> {
27 let index = if_nametoindex(interface)?;
28 let header = LinkHeader {
29 index,
30 flags: LinkFlags::Up,
31 ..Default::default()
32 };
33 let mut message = LinkMessage::default();
34 message.header = header;
35 message.attributes = vec![link::LinkAttribute::Mtu(mtu)];
36 netlink_request_rtnl(RouteNetlinkMessage::SetLink(message), None)?;
37 log::debug!("set interface {} up with mtu {}", interface, mtu);
38 Ok(())
39}
40
41pub fn set_addr(interface: &InterfaceName, addr: IpNet) -> Result<(), io::Error> {
42 let index = if_nametoindex(interface)?;
43 let (family, nlas) = match addr {
44 IpNet::V4(network) => {
45 let addr = IpAddr::V4(network.addr());
46 (
47 AddressFamily::Inet,
48 vec![
49 address::AddressAttribute::Local(addr),
50 address::AddressAttribute::Address(addr),
51 ],
52 )
53 },
54 IpNet::V6(network) => (
55 AddressFamily::Inet6,
56 vec![address::AddressAttribute::Address(IpAddr::V6(
57 network.addr(),
58 ))],
59 ),
60 };
61 let header = AddressHeader {
62 index,
63 family,
64 prefix_len: addr.prefix_len(),
65 scope: address::AddressScope::Universe,
66 ..Default::default()
67 };
68
69 let mut message = AddressMessage::default();
70 message.header = header;
71 message.attributes = nlas;
72 netlink_request_rtnl(
73 RouteNetlinkMessage::NewAddress(message),
74 Some(NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE | NLM_F_CREATE),
75 )?;
76 log::debug!("set address {} on interface {}", addr, interface);
77 Ok(())
78}
79
80pub fn add_route(interface: &InterfaceName, cidr: IpNet) -> Result<bool, io::Error> {
81 let if_index = if_nametoindex(interface)?;
82 let (address_family, dst) = match cidr {
83 IpNet::V4(network) => (
84 AddressFamily::Inet,
85 route::RouteAttribute::Destination(route::RouteAddress::Inet(network.network())),
86 ),
87 IpNet::V6(network) => (
88 AddressFamily::Inet6,
89 route::RouteAttribute::Destination(route::RouteAddress::Inet6(network.network())),
90 ),
91 };
92 let header = RouteHeader {
93 table: RouteHeader::RT_TABLE_MAIN,
94 protocol: route::RouteProtocol::Boot,
95 scope: route::RouteScope::Link,
96 kind: route::RouteType::Unicast,
97 destination_prefix_length: cidr.prefix_len(),
98 address_family,
99 ..Default::default()
100 };
101 let mut message = RouteMessage::default();
102 message.header = header;
103 message.attributes = vec![dst, route::RouteAttribute::Oif(if_index)];
104
105 match netlink_request_rtnl(RouteNetlinkMessage::NewRoute(message), None) {
106 Ok(_) => {
107 log::debug!("added route {} to interface {}", cidr, interface);
108 Ok(true)
109 },
110 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
111 log::debug!("route {} already existed.", cidr);
112 Ok(false)
113 },
114 Err(e) => Err(e),
115 }
116}
117
118fn get_links() -> Result<Vec<String>, io::Error> {
119 let link_responses = netlink_request_rtnl(
120 RouteNetlinkMessage::GetLink(LinkMessage::default()),
121 Some(NLM_F_DUMP | NLM_F_REQUEST),
122 )?;
123 let links = link_responses
124 .into_iter()
125 .filter_map(|response| match response {
127 NetlinkMessage {
128 payload: NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewLink(link)),
129 ..
130 } => Some(link),
131 _ => None,
132 })
133 .filter_map(|link| if !link.header.flags.contains(LinkFlags::Loopback) {
135 Some(link.attributes)
136 } else {
137 None
138 })
139 .filter(|nlas| nlas.iter().any(|nla| nla == &link::LinkAttribute::OperState(State::Up)))
141 .filter_map(|nlas| nlas.iter().find_map(|nla| match nla {
142 link::LinkAttribute::IfName(name) => Some(name.clone()),
143 _ => None,
144 }))
145 .collect::<Vec<_>>();
146
147 Ok(links)
148}
149
150pub fn get_local_addrs() -> Result<impl Iterator<Item = IpAddr>, io::Error> {
151 let links = get_links()?;
152 let addr_responses = netlink_request_rtnl(
153 RouteNetlinkMessage::GetAddress(AddressMessage::default()),
154 Some(NLM_F_DUMP | NLM_F_REQUEST),
155 )?;
156 let addrs = addr_responses
157 .into_iter()
158 .filter_map(|response| match response {
160 NetlinkMessage {
161 payload: NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewAddress(addr)),
162 ..
163 } => Some(addr),
164 _ => None,
165 })
166 .filter_map(|link| if link.header.scope == address::AddressScope::Universe {
168 Some(link.attributes)
169 } else {
170 None
171 })
172 .filter(move |nlas| nlas.iter().any(|nla| {
174 matches!(nla, address::AddressAttribute::Label(label) if links.contains(label))
175 || matches!(nla, address::AddressAttribute::Address(IpAddr::V6(_addr)))
176 }))
177 .filter_map(|nlas| nlas.iter().find_map(|nla| match nla {
178 address::AddressAttribute::Address(IpAddr::V4(addr)) => Some(IpAddr::V4(*addr)),
179 address::AddressAttribute::Address(IpAddr::V6(addr)) => Some(IpAddr::V6(*addr)),
180 _ => None,
181 }));
182 Ok(addrs)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_local_addrs() {
191 let addrs = get_local_addrs().unwrap();
192 println!("{:?}", addrs.collect::<Vec<_>>());
193 }
194}