|
| 1 | +use anyhow::Context; |
| 2 | +use std::net::IpAddr; |
| 3 | + |
| 4 | +use crate::{ |
| 5 | + constants::{self, RTA_GATEWAY}, |
| 6 | + emit_ip, |
| 7 | + ip_len, |
| 8 | + nlas::{Nla, NlaBuffer}, |
| 9 | + parsers::parse_ip, |
| 10 | + traits::{Emitable, Parseable}, |
| 11 | + DecodeError, |
| 12 | +}; |
| 13 | + |
| 14 | +bitflags! { |
| 15 | + pub struct NextHopFlags: u8 { |
| 16 | + const RTNH_F_EMPTY = 0; |
| 17 | + const RTNH_F_DEAD = constants::RTNH_F_DEAD as u8; |
| 18 | + const RTNH_F_PERVASIVE = constants::RTNH_F_PERVASIVE as u8; |
| 19 | + const RTNH_F_ONLINK = constants::RTNH_F_ONLINK as u8; |
| 20 | + const RTNH_F_OFFLOAD = constants::RTNH_F_OFFLOAD as u8; |
| 21 | + const RTNH_F_LINKDOWN = constants::RTNH_F_LINKDOWN as u8; |
| 22 | + const RTNH_F_UNRESOLVED = constants::RTNH_F_UNRESOLVED as u8; |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +buffer!(NextHopBuffer { |
| 27 | + length: (u16, 0..2), |
| 28 | + flags: (u8, 2), |
| 29 | + hops: (u8, 3), |
| 30 | + interface_id: (u32, 4..8), |
| 31 | + gateway_nla: (slice, GATEWAY_OFFSET..), |
| 32 | +}); |
| 33 | + |
| 34 | +impl<T: AsRef<[u8]>> NextHopBuffer<T> { |
| 35 | + pub fn new_checked(buffer: T) -> Result<Self, DecodeError> { |
| 36 | + let packet = Self::new(buffer); |
| 37 | + packet.check_buffer_length()?; |
| 38 | + Ok(packet) |
| 39 | + } |
| 40 | + |
| 41 | + fn check_buffer_length(&self) -> Result<(), DecodeError> { |
| 42 | + let len = self.buffer.as_ref().len(); |
| 43 | + if len < 8 { |
| 44 | + return Err(format!("invalid NextHopBuffer: length {} < {}", len, 8).into()); |
| 45 | + } |
| 46 | + if len < self.length() as usize { |
| 47 | + return Err(format!( |
| 48 | + "invalid NextHopBuffer: length {} < {}", |
| 49 | + len, |
| 50 | + 8 + self.length() |
| 51 | + ) |
| 52 | + .into()); |
| 53 | + } |
| 54 | + Ok(()) |
| 55 | + } |
| 56 | +} |
| 57 | + |
| 58 | +const GATEWAY_OFFSET: usize = 8; |
| 59 | + |
| 60 | +#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 61 | +pub struct NextHop { |
| 62 | + /// Next-hop flags (see [`NextHopFlags`]) |
| 63 | + pub flags: NextHopFlags, |
| 64 | + /// Next-hop priority |
| 65 | + pub hops: u8, |
| 66 | + /// Interface index for the next-hop |
| 67 | + pub interface_id: u32, |
| 68 | + /// Gateway address (it is actually encoded as an `RTA_GATEWAY` nla) |
| 69 | + pub gateway: Option<IpAddr>, |
| 70 | +} |
| 71 | + |
| 72 | +impl<'a, T: AsRef<[u8]>> Parseable<NextHopBuffer<&'a T>> for NextHop { |
| 73 | + fn parse(buf: &NextHopBuffer<&T>) -> Result<NextHop, DecodeError> { |
| 74 | + let gateway = if buf.length() as usize > GATEWAY_OFFSET { |
| 75 | + let gateway_nla_buf = NlaBuffer::new_checked(buf.gateway_nla()) |
| 76 | + .context("cannot parse RTA_GATEWAY attribute in next-hop")?; |
| 77 | + if gateway_nla_buf.kind() != RTA_GATEWAY { |
| 78 | + return Err(format!("invalid RTA_GATEWAY attribute in next-hop: expected NLA type to be RTA_GATEWAY ({}), but got {} instead", RTA_GATEWAY, gateway_nla_buf.kind()).into()); |
| 79 | + } |
| 80 | + let gateway = parse_ip(gateway_nla_buf.value()).context( |
| 81 | + "invalid RTA_GATEWAY attribute in next-hop: failed to parse NLA value as an IP address", |
| 82 | + )?; |
| 83 | + Some(gateway) |
| 84 | + } else { |
| 85 | + None |
| 86 | + }; |
| 87 | + Ok(NextHop { |
| 88 | + flags: NextHopFlags::from_bits_truncate(buf.flags()), |
| 89 | + hops: buf.hops(), |
| 90 | + interface_id: buf.interface_id(), |
| 91 | + gateway, |
| 92 | + }) |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +struct GatewayNla<'a>(&'a IpAddr); |
| 97 | + |
| 98 | +impl<'a> Nla for GatewayNla<'a> { |
| 99 | + fn value_len(&self) -> usize { |
| 100 | + ip_len(self.0) |
| 101 | + } |
| 102 | + fn kind(&self) -> u16 { |
| 103 | + RTA_GATEWAY |
| 104 | + } |
| 105 | + fn emit_value(&self, buffer: &mut [u8]) { |
| 106 | + emit_ip(buffer, self.0) |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +impl Emitable for NextHop { |
| 111 | + fn buffer_len(&self) -> usize { |
| 112 | + // len, flags, hops and interface id fields |
| 113 | + 8 + self |
| 114 | + .gateway |
| 115 | + .as_ref() |
| 116 | + .map(|ip| { |
| 117 | + // RTA_GATEWAY attribute header (length and type) + value length |
| 118 | + 4 + ip_len(ip) |
| 119 | + }) |
| 120 | + .unwrap_or(0) |
| 121 | + } |
| 122 | + |
| 123 | + fn emit(&self, buffer: &mut [u8]) { |
| 124 | + let mut nh_buffer = NextHopBuffer::new(buffer); |
| 125 | + nh_buffer.set_length(self.buffer_len() as u16); |
| 126 | + nh_buffer.set_flags(self.flags.bits()); |
| 127 | + nh_buffer.set_hops(self.hops); |
| 128 | + nh_buffer.set_interface_id(self.interface_id); |
| 129 | + if let Some(ref gateway) = self.gateway { |
| 130 | + let gateway_nla = GatewayNla(gateway); |
| 131 | + gateway_nla.emit(nh_buffer.gateway_nla_mut()); |
| 132 | + } |
| 133 | + } |
| 134 | +} |
0 commit comments