route.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. use heapless::Vec;
  2. use crate::config::IFACE_MAX_ROUTE_COUNT;
  3. use crate::time::Instant;
  4. use crate::wire::{IpAddress, IpCidr};
  5. #[cfg(feature = "proto-ipv4")]
  6. use crate::wire::{Ipv4Address, Ipv4Cidr};
  7. #[cfg(feature = "proto-ipv6")]
  8. use crate::wire::{Ipv6Address, Ipv6Cidr};
  9. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  10. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  11. pub struct RouteTableFull;
  12. /// A prefix of addresses that should be routed via a router
  13. #[derive(Debug, Clone, Copy)]
  14. #[cfg_attr(feature = "defmt", derive(defmt::Format))]
  15. pub struct Route {
  16. pub cidr: IpCidr,
  17. pub via_router: IpAddress,
  18. /// `None` means "forever".
  19. pub preferred_until: Option<Instant>,
  20. /// `None` means "forever".
  21. pub expires_at: Option<Instant>,
  22. }
  23. #[cfg(feature = "proto-ipv4")]
  24. const IPV4_DEFAULT: IpCidr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(0, 0, 0, 0), 0));
  25. #[cfg(feature = "proto-ipv6")]
  26. const IPV6_DEFAULT: IpCidr =
  27. IpCidr::Ipv6(Ipv6Cidr::new(Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0));
  28. impl Route {
  29. /// Returns a route to 0.0.0.0/0 via the `gateway`, with no expiry.
  30. #[cfg(feature = "proto-ipv4")]
  31. pub fn new_ipv4_gateway(gateway: Ipv4Address) -> Route {
  32. Route {
  33. cidr: IPV4_DEFAULT,
  34. via_router: gateway.into(),
  35. preferred_until: None,
  36. expires_at: None,
  37. }
  38. }
  39. /// Returns a route to ::/0 via the `gateway`, with no expiry.
  40. #[cfg(feature = "proto-ipv6")]
  41. pub fn new_ipv6_gateway(gateway: Ipv6Address) -> Route {
  42. Route {
  43. cidr: IPV6_DEFAULT,
  44. via_router: gateway.into(),
  45. preferred_until: None,
  46. expires_at: None,
  47. }
  48. }
  49. }
  50. /// A routing table.
  51. #[derive(Debug)]
  52. pub struct Routes {
  53. storage: Vec<Route, IFACE_MAX_ROUTE_COUNT>,
  54. }
  55. impl Routes {
  56. /// Creates a new empty routing table.
  57. pub fn new() -> Self {
  58. Self {
  59. storage: Vec::new(),
  60. }
  61. }
  62. /// Update the routes of this node.
  63. pub fn update<F: FnOnce(&mut Vec<Route, IFACE_MAX_ROUTE_COUNT>)>(&mut self, f: F) {
  64. f(&mut self.storage);
  65. }
  66. /// Add a default ipv4 gateway (ie. "ip route add 0.0.0.0/0 via `gateway`").
  67. ///
  68. /// On success, returns the previous default route, if any.
  69. #[cfg(feature = "proto-ipv4")]
  70. pub fn add_default_ipv4_route(
  71. &mut self,
  72. gateway: Ipv4Address,
  73. ) -> Result<Option<Route>, RouteTableFull> {
  74. let old = self.remove_default_ipv4_route();
  75. self.storage
  76. .push(Route::new_ipv4_gateway(gateway))
  77. .map_err(|_| RouteTableFull)?;
  78. Ok(old)
  79. }
  80. /// Add a default ipv6 gateway (ie. "ip -6 route add ::/0 via `gateway`").
  81. ///
  82. /// On success, returns the previous default route, if any.
  83. #[cfg(feature = "proto-ipv6")]
  84. pub fn add_default_ipv6_route(
  85. &mut self,
  86. gateway: Ipv6Address,
  87. ) -> Result<Option<Route>, RouteTableFull> {
  88. let old = self.remove_default_ipv6_route();
  89. self.storage
  90. .push(Route::new_ipv6_gateway(gateway))
  91. .map_err(|_| RouteTableFull)?;
  92. Ok(old)
  93. }
  94. /// Remove the default ipv4 gateway
  95. ///
  96. /// On success, returns the previous default route, if any.
  97. #[cfg(feature = "proto-ipv4")]
  98. pub fn remove_default_ipv4_route(&mut self) -> Option<Route> {
  99. if let Some((i, _)) = self
  100. .storage
  101. .iter()
  102. .enumerate()
  103. .find(|(_, r)| r.cidr == IPV4_DEFAULT)
  104. {
  105. Some(self.storage.remove(i))
  106. } else {
  107. None
  108. }
  109. }
  110. /// Remove the default ipv6 gateway
  111. ///
  112. /// On success, returns the previous default route, if any.
  113. #[cfg(feature = "proto-ipv6")]
  114. pub fn remove_default_ipv6_route(&mut self) -> Option<Route> {
  115. if let Some((i, _)) = self
  116. .storage
  117. .iter()
  118. .enumerate()
  119. .find(|(_, r)| r.cidr == IPV6_DEFAULT)
  120. {
  121. Some(self.storage.remove(i))
  122. } else {
  123. None
  124. }
  125. }
  126. pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
  127. assert!(addr.is_unicast());
  128. self.storage
  129. .iter()
  130. // Keep only matching routes
  131. .filter(|route| {
  132. if let Some(expires_at) = route.expires_at {
  133. if timestamp > expires_at {
  134. return false;
  135. }
  136. }
  137. route.cidr.contains_addr(addr)
  138. })
  139. // pick the most specific one (highest prefix_len)
  140. .max_by_key(|route| route.cidr.prefix_len())
  141. .map(|route| route.via_router)
  142. }
  143. }
  144. #[cfg(test)]
  145. mod test {
  146. use super::*;
  147. #[cfg(feature = "proto-ipv6")]
  148. mod mock {
  149. use super::super::*;
  150. pub const ADDR_1A: Ipv6Address =
  151. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
  152. pub const ADDR_1B: Ipv6Address =
  153. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
  154. pub const ADDR_1C: Ipv6Address =
  155. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
  156. pub fn cidr_1() -> Ipv6Cidr {
  157. Ipv6Cidr::new(
  158. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
  159. 64,
  160. )
  161. }
  162. pub const ADDR_2A: Ipv6Address =
  163. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
  164. pub const ADDR_2B: Ipv6Address =
  165. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
  166. pub fn cidr_2() -> Ipv6Cidr {
  167. Ipv6Cidr::new(
  168. Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]),
  169. 64,
  170. )
  171. }
  172. }
  173. #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
  174. mod mock {
  175. use super::super::*;
  176. pub const ADDR_1A: Ipv4Address = Ipv4Address([192, 0, 2, 1]);
  177. pub const ADDR_1B: Ipv4Address = Ipv4Address([192, 0, 2, 13]);
  178. pub const ADDR_1C: Ipv4Address = Ipv4Address([192, 0, 2, 42]);
  179. pub fn cidr_1() -> Ipv4Cidr {
  180. Ipv4Cidr::new(Ipv4Address([192, 0, 2, 0]), 24)
  181. }
  182. pub const ADDR_2A: Ipv4Address = Ipv4Address([198, 51, 100, 1]);
  183. pub const ADDR_2B: Ipv4Address = Ipv4Address([198, 51, 100, 21]);
  184. pub fn cidr_2() -> Ipv4Cidr {
  185. Ipv4Cidr::new(Ipv4Address([198, 51, 100, 0]), 24)
  186. }
  187. }
  188. use self::mock::*;
  189. #[test]
  190. fn test_fill() {
  191. let mut routes = Routes::new();
  192. assert_eq!(
  193. routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
  194. None
  195. );
  196. assert_eq!(
  197. routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
  198. None
  199. );
  200. assert_eq!(
  201. routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
  202. None
  203. );
  204. assert_eq!(
  205. routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
  206. None
  207. );
  208. assert_eq!(
  209. routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
  210. None
  211. );
  212. let route = Route {
  213. cidr: cidr_1().into(),
  214. via_router: ADDR_1A.into(),
  215. preferred_until: None,
  216. expires_at: None,
  217. };
  218. routes.update(|storage| {
  219. storage.push(route).unwrap();
  220. });
  221. assert_eq!(
  222. routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
  223. Some(ADDR_1A.into())
  224. );
  225. assert_eq!(
  226. routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
  227. Some(ADDR_1A.into())
  228. );
  229. assert_eq!(
  230. routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
  231. Some(ADDR_1A.into())
  232. );
  233. assert_eq!(
  234. routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
  235. None
  236. );
  237. assert_eq!(
  238. routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
  239. None
  240. );
  241. let route2 = Route {
  242. cidr: cidr_2().into(),
  243. via_router: ADDR_2A.into(),
  244. preferred_until: Some(Instant::from_millis(10)),
  245. expires_at: Some(Instant::from_millis(10)),
  246. };
  247. routes.update(|storage| {
  248. storage.push(route2).unwrap();
  249. });
  250. assert_eq!(
  251. routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
  252. Some(ADDR_1A.into())
  253. );
  254. assert_eq!(
  255. routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
  256. Some(ADDR_1A.into())
  257. );
  258. assert_eq!(
  259. routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
  260. Some(ADDR_1A.into())
  261. );
  262. assert_eq!(
  263. routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
  264. Some(ADDR_2A.into())
  265. );
  266. assert_eq!(
  267. routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
  268. Some(ADDR_2A.into())
  269. );
  270. assert_eq!(
  271. routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)),
  272. Some(ADDR_1A.into())
  273. );
  274. assert_eq!(
  275. routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)),
  276. Some(ADDR_1A.into())
  277. );
  278. assert_eq!(
  279. routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)),
  280. Some(ADDR_1A.into())
  281. );
  282. assert_eq!(
  283. routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)),
  284. Some(ADDR_2A.into())
  285. );
  286. assert_eq!(
  287. routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)),
  288. Some(ADDR_2A.into())
  289. );
  290. }
  291. }