route.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. use managed::ManagedMap;
  2. use crate::time::Instant;
  3. use core::ops::Bound;
  4. use crate::{Error, Result};
  5. use crate::wire::{IpCidr, IpAddress};
  6. #[cfg(feature = "proto-ipv4")]
  7. use crate::wire::{Ipv4Address, Ipv4Cidr};
  8. #[cfg(feature = "proto-ipv6")]
  9. use crate::wire::{Ipv6Address, Ipv6Cidr};
  10. /// A prefix of addresses that should be routed via a router
  11. #[derive(Debug, Clone, Copy)]
  12. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  13. pub struct Route {
  14. pub via_router: IpAddress,
  15. /// `None` means "forever".
  16. pub preferred_until: Option<Instant>,
  17. /// `None` means "forever".
  18. pub expires_at: Option<Instant>,
  19. }
  20. impl Route {
  21. /// Returns a route to 0.0.0.0/0 via the `gateway`, with no expiry.
  22. #[cfg(feature = "proto-ipv4")]
  23. pub fn new_ipv4_gateway(gateway: Ipv4Address) -> Route {
  24. Route {
  25. via_router: gateway.into(),
  26. preferred_until: None,
  27. expires_at: None,
  28. }
  29. }
  30. /// Returns a route to ::/0 via the `gateway`, with no expiry.
  31. #[cfg(feature = "proto-ipv6")]
  32. pub fn new_ipv6_gateway(gateway: Ipv6Address) -> Route {
  33. Route {
  34. via_router: gateway.into(),
  35. preferred_until: None,
  36. expires_at: None,
  37. }
  38. }
  39. }
  40. /// A routing table.
  41. ///
  42. /// # Examples
  43. ///
  44. /// On systems with heap, this table can be created with:
  45. ///
  46. /// ```rust
  47. /// use std::collections::BTreeMap;
  48. /// use smoltcp::iface::Routes;
  49. /// let mut routes = Routes::new(BTreeMap::new());
  50. /// ```
  51. ///
  52. /// On systems without heap, use:
  53. ///
  54. /// ```rust
  55. /// use smoltcp::iface::Routes;
  56. /// let mut routes_storage = [];
  57. /// let mut routes = Routes::new(&mut routes_storage[..]);
  58. /// ```
  59. #[derive(Debug)]
  60. pub struct Routes<'a> {
  61. storage: ManagedMap<'a, IpCidr, Route>,
  62. }
  63. impl<'a> Routes<'a> {
  64. /// Creates a routing tables. The backing storage is **not** cleared
  65. /// upon creation.
  66. pub fn new<T>(storage: T) -> Routes<'a>
  67. where T: Into<ManagedMap<'a, IpCidr, Route>> {
  68. let storage = storage.into();
  69. Routes { storage }
  70. }
  71. /// Update the routes of this node.
  72. pub fn update<F: FnOnce(&mut ManagedMap<'a, IpCidr, Route>)>(&mut self, f: F) {
  73. f(&mut self.storage);
  74. }
  75. /// Add a default ipv4 gateway (ie. "ip route add 0.0.0.0/0 via `gateway`").
  76. ///
  77. /// On success, returns the previous default route, if any.
  78. #[cfg(feature = "proto-ipv4")]
  79. pub fn add_default_ipv4_route(&mut self, gateway: Ipv4Address) -> Result<Option<Route>> {
  80. let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0);
  81. let route = Route::new_ipv4_gateway(gateway);
  82. match self.storage.insert(cidr, route) {
  83. Ok(route) => Ok(route),
  84. Err((_cidr, _route)) => Err(Error::Exhausted)
  85. }
  86. }
  87. /// Add a default ipv6 gateway (ie. "ip -6 route add ::/0 via `gateway`").
  88. ///
  89. /// On success, returns the previous default route, if any.
  90. #[cfg(feature = "proto-ipv6")]
  91. pub fn add_default_ipv6_route(&mut self, gateway: Ipv6Address) -> Result<Option<Route>> {
  92. let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0);
  93. let route = Route::new_ipv6_gateway(gateway);
  94. match self.storage.insert(cidr, route) {
  95. Ok(route) => Ok(route),
  96. Err((_cidr, _route)) => Err(Error::Exhausted)
  97. }
  98. }
  99. pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) ->
  100. Option<IpAddress> {
  101. assert!(addr.is_unicast());
  102. let cidr = match addr {
  103. #[cfg(feature = "proto-ipv4")]
  104. IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)),
  105. #[cfg(feature = "proto-ipv6")]
  106. IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)),
  107. _ => unimplemented!()
  108. };
  109. for (prefix, route) in self.storage.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr))).rev() {
  110. // TODO: do something with route.preferred_until
  111. if let Some(expires_at) = route.expires_at {
  112. if timestamp > expires_at {
  113. continue;
  114. }
  115. }
  116. if prefix.contains_addr(addr) {
  117. return Some(route.via_router);
  118. }
  119. }
  120. None
  121. }
  122. }
  123. #[cfg(test)]
  124. mod test {
  125. use super::*;
  126. #[cfg(feature = "proto-ipv6")]
  127. mod mock {
  128. use super::super::*;
  129. pub const ADDR_1A: Ipv6Address = Ipv6Address(
  130. [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
  131. pub const ADDR_1B: Ipv6Address = Ipv6Address(
  132. [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
  133. pub const ADDR_1C: Ipv6Address = Ipv6Address(
  134. [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
  135. pub fn cidr_1() -> Ipv6Cidr {
  136. Ipv6Cidr::new(Ipv6Address(
  137. [0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
  138. }
  139. pub const ADDR_2A: Ipv6Address = Ipv6Address(
  140. [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
  141. pub const ADDR_2B: Ipv6Address = Ipv6Address(
  142. [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
  143. pub fn cidr_2() -> Ipv6Cidr {
  144. Ipv6Cidr::new(Ipv6Address(
  145. [0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
  146. }
  147. }
  148. #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
  149. mod mock {
  150. use super::super::*;
  151. pub const ADDR_1A: Ipv4Address = Ipv4Address([192, 0, 2, 1]);
  152. pub const ADDR_1B: Ipv4Address = Ipv4Address([192, 0, 2, 13]);
  153. pub const ADDR_1C: Ipv4Address = Ipv4Address([192, 0, 2, 42]);
  154. pub fn cidr_1() -> Ipv4Cidr {
  155. Ipv4Cidr::new(Ipv4Address([192, 0, 2, 0]), 24)
  156. }
  157. pub const ADDR_2A: Ipv4Address = Ipv4Address([198, 51, 100, 1]);
  158. pub const ADDR_2B: Ipv4Address = Ipv4Address([198, 51, 100, 21]);
  159. pub fn cidr_2() -> Ipv4Cidr {
  160. Ipv4Cidr::new(Ipv4Address([198, 51, 100, 0]), 24)
  161. }
  162. }
  163. use self::mock::*;
  164. #[test]
  165. fn test_fill() {
  166. let mut routes_storage = [None, None, None];
  167. let mut routes = Routes::new(&mut routes_storage[..]);
  168. assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None);
  169. assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None);
  170. assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None);
  171. assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
  172. assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
  173. let route = Route {
  174. via_router: ADDR_1A.into(),
  175. preferred_until: None, expires_at: None,
  176. };
  177. routes.update(|storage| {
  178. storage.insert(cidr_1().into(), route).unwrap();
  179. });
  180. assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  181. assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  182. assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  183. assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
  184. assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
  185. let route2 = Route {
  186. via_router: ADDR_2A.into(),
  187. preferred_until: Some(Instant::from_millis(10)),
  188. expires_at: Some(Instant::from_millis(10)),
  189. };
  190. routes.update(|storage| {
  191. storage.insert(cidr_2().into(), route2).unwrap();
  192. });
  193. assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  194. assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  195. assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
  196. assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
  197. assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
  198. assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
  199. assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
  200. assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
  201. assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
  202. assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
  203. }
  204. }