Skip to content

Commit 9d913ea

Browse files
committed
add RouteAddRequest<IpAddr>
We currently have `RouteAddRequest<Ipv4Addr>` and `RouteAddRequest<Ipv6Addr>`. But when working on codebases that mostly deal with `IpAddr`, this is an inconvenient API because we have to match on every address, and are forced to write the same code twice. The new impl makes working with `IpAddr` much more convenient, with the downside that the requests are validated at runtime instead of compile time.
1 parent 9d54594 commit 9d913ea

File tree

1 file changed

+152
-2
lines changed

1 file changed

+152
-2
lines changed

src/route/add.rs

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use futures::stream::StreamExt;
44
use std::{
55
marker::PhantomData,
6-
net::{Ipv4Addr, Ipv6Addr},
6+
net::{IpAddr, Ipv4Addr, Ipv6Addr},
77
};
88

99
use netlink_packet_core::{
@@ -22,7 +22,7 @@ use crate::{try_nl, Error, Handle};
2222

2323
/// A request to create a new route. This is equivalent to the `ip route add`
2424
/// commands.
25-
pub struct RouteAddRequest<T = ()> {
25+
pub struct RouteAddRequest<T = IpAddr> {
2626
handle: Handle,
2727
message: RouteMessage,
2828
replace: bool,
@@ -246,3 +246,153 @@ impl RouteAddRequest<Ipv6Addr> {
246246
self
247247
}
248248
}
249+
250+
#[derive(Debug, thiserror::Error)]
251+
pub enum InvalidRequest {
252+
#[error("invalid address family {:?}", _0)]
253+
AddressFamily(AddressFamily),
254+
255+
#[error("invalid gateway {}", _0)]
256+
Gateway(IpAddr),
257+
258+
#[error("invalid preferred source {}", _0)]
259+
PrefSource(IpAddr),
260+
261+
#[error("invalid source prefix {}/{}", _0, _1)]
262+
SourcePrefix(IpAddr, u8),
263+
264+
#[error("invalid destination prefix {}/{}", _0, _1)]
265+
DestinationPrefix(IpAddr, u8),
266+
}
267+
268+
impl RouteAddRequest<IpAddr> {
269+
/// Sets the source address prefix.
270+
pub fn source_prefix(
271+
mut self,
272+
addr: IpAddr,
273+
prefix_length: u8,
274+
) -> Result<Self, InvalidRequest> {
275+
self.set_address_family_from_ip_addr(addr);
276+
match self.message.header.address_family {
277+
AddressFamily::Inet => {
278+
if addr.is_ipv6() || prefix_length > 32 {
279+
return Err(InvalidRequest::SourcePrefix(
280+
addr,
281+
prefix_length,
282+
));
283+
}
284+
}
285+
AddressFamily::Inet6 => {
286+
if addr.is_ipv4() || prefix_length > 128 {
287+
return Err(InvalidRequest::SourcePrefix(
288+
addr,
289+
prefix_length,
290+
));
291+
}
292+
}
293+
af => return Err(InvalidRequest::AddressFamily(af)),
294+
};
295+
self.message
296+
.attributes
297+
.push(RouteAttribute::Source(addr.into()));
298+
self.message.header.source_prefix_length = prefix_length;
299+
Ok(self)
300+
}
301+
302+
/// Sets the preferred source address.
303+
pub fn pref_source(mut self, addr: IpAddr) -> Result<Self, InvalidRequest> {
304+
self.set_address_family_from_ip_addr(addr);
305+
match self.message.header.address_family {
306+
AddressFamily::Inet => {
307+
if addr.is_ipv6() {
308+
return Err(InvalidRequest::PrefSource(addr));
309+
};
310+
}
311+
AddressFamily::Inet6 => {
312+
if addr.is_ipv4() {
313+
return Err(InvalidRequest::PrefSource(addr));
314+
};
315+
}
316+
af => {
317+
return Err(InvalidRequest::AddressFamily(af));
318+
}
319+
}
320+
self.message
321+
.attributes
322+
.push(RouteAttribute::PrefSource(addr.into()));
323+
Ok(self)
324+
}
325+
326+
/// Sets the destination address prefix.
327+
pub fn destination_prefix(
328+
mut self,
329+
addr: IpAddr,
330+
prefix_length: u8,
331+
) -> Result<Self, InvalidRequest> {
332+
self.set_address_family_from_ip_addr(addr);
333+
match self.message.header.address_family {
334+
AddressFamily::Inet => {
335+
if addr.is_ipv6() || prefix_length > 32 {
336+
return Err(InvalidRequest::DestinationPrefix(
337+
addr,
338+
prefix_length,
339+
));
340+
}
341+
}
342+
AddressFamily::Inet6 => {
343+
if addr.is_ipv4() || prefix_length > 128 {
344+
return Err(InvalidRequest::DestinationPrefix(
345+
addr,
346+
prefix_length,
347+
));
348+
}
349+
}
350+
af => {
351+
return Err(InvalidRequest::AddressFamily(af));
352+
}
353+
};
354+
self.message.header.destination_prefix_length = prefix_length;
355+
self.message
356+
.attributes
357+
.push(RouteAttribute::Destination(addr.into()));
358+
Ok(self)
359+
}
360+
361+
/// Sets the gateway (via) address.
362+
pub fn gateway(mut self, addr: IpAddr) -> Result<Self, InvalidRequest> {
363+
self.set_address_family_from_ip_addr(addr);
364+
match self.message.header.address_family {
365+
AddressFamily::Inet => {
366+
if addr.is_ipv6() {
367+
return Err(InvalidRequest::Gateway(addr));
368+
};
369+
}
370+
AddressFamily::Inet6 => {
371+
if addr.is_ipv4() {
372+
return Err(InvalidRequest::Gateway(addr));
373+
};
374+
}
375+
af => {
376+
return Err(InvalidRequest::AddressFamily(af));
377+
}
378+
}
379+
self.message
380+
.attributes
381+
.push(RouteAttribute::Gateway(addr.into()));
382+
Ok(self)
383+
}
384+
385+
/// If it is not set already, set the address family based on the
386+
/// given IP address. This is a noop is the address family is
387+
/// already set.
388+
fn set_address_family_from_ip_addr(&mut self, addr: IpAddr) {
389+
if self.message.header.address_family != AddressFamily::Unspec {
390+
return;
391+
}
392+
if addr.is_ipv4() {
393+
self.message.header.address_family = AddressFamily::Inet;
394+
} else {
395+
self.message.header.address_family = AddressFamily::Inet6;
396+
}
397+
}
398+
}

0 commit comments

Comments
 (0)