route.rs 8.4 KB

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