1use {
2 crate::{
3 lpm::Ipv4Lpm,
4 netlink::{
5 GreTunnelInfo, InterfaceInfo, MacAddress, NeighborEntry, RouteEntry,
6 netlink_get_interfaces, netlink_get_neighbors, netlink_get_routes,
7 },
8 },
9 libc::{AF_INET, RT_TABLE_DEFAULT, RT_TABLE_LOCAL, RT_TABLE_MAIN},
10 log::warn,
11 std::{
12 cmp::Ordering,
13 fmt, io,
14 net::{IpAddr, Ipv4Addr},
15 },
16 thiserror::Error,
17};
18
19#[derive(Debug, Error)]
20pub enum RouteError {
21 #[error("no route found to destination {0}")]
22 NoRouteFound(IpAddr),
23
24 #[error("missing output interface in route")]
25 MissingOutputInterface,
26
27 #[error("could not resolve MAC address")]
28 MacResolutionError,
29
30 #[error("unknown interface index {0}")]
31 UnknownInterfaceIndex(u32),
32}
33
34#[derive(Debug, Clone)]
35pub struct GreRouteInfo {
36 pub if_index: u32,
37 pub mtu: u32,
38 pub underlay_mtu: u32,
39 pub tunnel_info: GreTunnelInfo,
40 pub mac_addr: MacAddress,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub struct VlanRouteInfo {
51 pub if_index: u32,
53 pub vid: u16,
54 pub pcp: u8,
57}
58
59#[derive(Debug, Clone)]
60pub struct NextHop {
61 pub mac_addr: Option<MacAddress>,
62 pub ip_addr: IpAddr,
63 pub if_index: u32,
64 pub mtu: u32,
65 pub preferred_src_ip: Option<Ipv4Addr>,
66 pub gre: Option<GreRouteInfo>,
67 pub vlan: Option<VlanRouteInfo>,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum RouteTable {
72 Default,
73 Local,
74 Main,
75 Other(u32),
76}
77
78impl std::fmt::Display for RouteTable {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 match self {
81 Self::Default => f.write_str("default"),
82 Self::Local => f.write_str("local"),
83 Self::Main => f.write_str("main"),
84 Self::Other(table) => write!(f, "{table}"),
85 }
86 }
87}
88
89impl From<RouteTable> for u32 {
90 fn from(table: RouteTable) -> Self {
91 match table {
92 RouteTable::Default => u32::from(RT_TABLE_DEFAULT),
93 RouteTable::Local => u32::from(RT_TABLE_LOCAL),
94 RouteTable::Main => u32::from(RT_TABLE_MAIN),
95 RouteTable::Other(table) => table,
96 }
97 }
98}
99
100impl From<u32> for RouteTable {
101 fn from(table: u32) -> Self {
102 match table {
103 table if table == u32::from(RT_TABLE_DEFAULT) => Self::Default,
104 table if table == u32::from(RT_TABLE_LOCAL) => Self::Local,
105 table if table == u32::from(RT_TABLE_MAIN) => Self::Main,
106 table => Self::Other(table),
107 }
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct Route<T> {
113 pub destination: Option<T>,
114 pub gateway: Option<T>,
115 pub preferred_src: Option<T>,
116 pub out_if_index: Option<u32>,
117 pub priority: Option<u32>,
118 pub type_: u8,
119 pub dst_len: u8,
120}
121
122impl<T: PartialEq> Route<T> {
123 fn same_key(&self, other: &Self) -> bool {
124 self.destination == other.destination
125 && self.dst_len == other.dst_len
126 && self.priority == other.priority
127 && self.type_ == other.type_
128 }
129}
130
131impl TryFrom<RouteEntry> for Route<Ipv4Addr> {
132 type Error = ();
133
134 fn try_from(entry: RouteEntry) -> Result<Self, Self::Error> {
135 if entry.family != AF_INET as u8 {
136 return Err(());
137 }
138
139 let destination = match entry.destination {
140 Some(IpAddr::V4(addr)) => Some(addr),
141 Some(IpAddr::V6(_)) => return Err(()),
142 None => None,
143 };
144 let gateway = match entry.gateway {
145 Some(IpAddr::V4(addr)) => Some(addr),
146 Some(IpAddr::V6(_)) => return Err(()),
147 None => None,
148 };
149 let preferred_src = match entry.pref_src {
150 Some(IpAddr::V4(addr)) => Some(addr),
151 Some(IpAddr::V6(_)) => return Err(()),
152 None => None,
153 };
154 let out_if_index = entry
155 .out_if_index
156 .map(u32::try_from)
157 .transpose()
158 .map_err(|_| ())?;
159
160 Ok(Self {
161 destination,
162 gateway,
163 preferred_src,
164 out_if_index,
165 priority: entry.priority,
166 type_: entry.type_,
167 dst_len: entry.dst_len.min(32),
168 })
169 }
170}
171
172#[derive(Clone, Debug)]
173pub struct Interfaces {
174 interfaces: Vec<InterfaceInfo>,
175}
176
177impl Interfaces {
178 pub fn new(interfaces: Vec<InterfaceInfo>) -> Self {
179 Self { interfaces }
180 }
181
182 pub fn from_netlink() -> Result<Self, io::Error> {
183 Ok(Self::new(netlink_get_interfaces(AF_INET as u8)?))
184 }
185
186 pub fn iter(&self) -> impl ExactSizeIterator<Item = &InterfaceInfo> {
187 self.interfaces.iter()
188 }
189
190 fn upsert(&mut self, new_interface: InterfaceInfo) -> bool {
191 if let Some(existing) = self
192 .interfaces
193 .iter_mut()
194 .find(|old| old.if_index == new_interface.if_index)
195 {
196 if existing != &new_interface {
197 *existing = new_interface;
198 return true;
199 }
200 return false;
201 }
202 self.interfaces.push(new_interface);
203 true
204 }
205
206 fn remove(&mut self, if_index: u32) -> bool {
207 if let Some(i) = self
208 .interfaces
209 .iter()
210 .position(|old| old.if_index == if_index)
211 {
212 self.interfaces.swap_remove(i);
213 return true;
214 }
215 false
216 }
217}
218
219#[derive(Clone)]
220pub struct Neighbors {
221 neighbors: Vec<NeighborEntry>,
222}
223
224impl Neighbors {
225 pub fn new(neighbors: Vec<NeighborEntry>) -> Self {
226 Self { neighbors }
227 }
228
229 pub fn from_netlink() -> Result<Self, io::Error> {
230 Ok(Self::new(netlink_get_neighbors(None, AF_INET as u8)?))
231 }
232
233 pub fn iter(&self) -> impl ExactSizeIterator<Item = &NeighborEntry> {
234 self.neighbors.iter()
235 }
236
237 fn lookup(&self, ip: IpAddr, if_index: u32) -> Option<&MacAddress> {
238 self.neighbors
239 .iter()
240 .find(|n| n.ifindex == if_index as i32 && n.destination == Some(ip))
241 .and_then(|n| n.lladdr.as_ref())
242 }
243
244 fn upsert(&mut self, new_neighbor: NeighborEntry) -> bool {
245 let Some((ifidx, ip)) = new_neighbor.key() else {
246 return false;
247 };
248
249 if let Some(i) = self
250 .neighbors
251 .iter()
252 .position(|old| old.ifindex == ifidx && old.destination == Some(IpAddr::V4(ip)))
253 {
254 if self.neighbors[i] != new_neighbor {
255 self.neighbors[i] = new_neighbor;
256 return true;
257 }
258 false
259 } else {
260 self.neighbors.push(new_neighbor);
261 true
262 }
263 }
264
265 fn remove(&mut self, ip: Ipv4Addr, if_index: u32) -> bool {
266 if let Some(i) = self.neighbors.iter().position(|old| {
267 old.ifindex == if_index as i32 && old.destination == Some(IpAddr::V4(ip))
268 }) {
269 self.neighbors.swap_remove(i);
270 return true;
271 }
272 false
273 }
274}
275
276#[derive(Clone)]
277pub struct Routes {
278 pub(crate) table: RouteTable,
279 routes: Vec<Route<Ipv4Addr>>,
280}
281
282impl Routes {
283 pub fn new(table: RouteTable, mut routes: Vec<Route<Ipv4Addr>>) -> Self {
284 routes.sort_by(Self::compare_routes);
285 Self { table, routes }
286 }
287
288 pub fn from_netlink(table: RouteTable) -> Result<Self, io::Error> {
289 let routes = netlink_get_routes(AF_INET as u8, u32::from(table))?
290 .into_iter()
291 .filter_map(|entry| Route::try_from(entry).ok())
292 .collect();
293 Ok(Self::new(table, routes))
294 }
295
296 fn compare_routes(left: &Route<Ipv4Addr>, right: &Route<Ipv4Addr>) -> Ordering {
297 right
298 .dst_len
299 .cmp(&left.dst_len)
300 .then_with(|| left.priority.unwrap_or(0).cmp(&right.priority.unwrap_or(0)))
301 }
302
303 pub fn iter(&self) -> impl ExactSizeIterator<Item = &Route<Ipv4Addr>> {
304 self.routes.iter()
305 }
306
307 fn get(&self, route_idx: usize) -> Option<&Route<Ipv4Addr>> {
308 self.routes.get(route_idx)
309 }
310
311 fn as_slice(&self) -> &[Route<Ipv4Addr>] {
312 &self.routes
313 }
314
315 fn upsert(&mut self, new_route: RouteEntry) -> bool {
316 if !new_route
317 .table
318 .is_some_and(|table| self.table == RouteTable::from(table))
319 {
320 return false;
321 }
322
323 let Ok(new_route) = Route::try_from(new_route) else {
324 return false;
325 };
326
327 if let Some(existing) = self.routes.iter_mut().find(|old| old.same_key(&new_route)) {
328 if existing != &new_route {
329 *existing = new_route;
330 return true;
331 }
332 false
333 } else {
334 let route = new_route;
335 let insert_at = self.routes.partition_point(|existing| {
336 matches!(
337 Self::compare_routes(existing, &route),
338 Ordering::Less | Ordering::Equal
339 )
340 });
341 self.routes.insert(insert_at, route);
342 true
343 }
344 }
345
346 fn remove(&mut self, new_route: RouteEntry) -> bool {
347 if !new_route
348 .table
349 .is_some_and(|table| self.table == RouteTable::from(table))
350 {
351 return false;
352 }
353
354 let Ok(new_route) = Route::try_from(new_route) else {
355 return false;
356 };
357
358 if let Some(i) = self.routes.iter().position(|old| old.same_key(&new_route)) {
359 self.routes.remove(i);
360 true
361 } else {
362 false
363 }
364 }
365}
366
367#[derive(Clone)]
368pub struct RoutingTables {
369 pub(crate) routes: Routes,
370 neighbors: Neighbors,
371 interfaces: Interfaces,
372}
373
374impl RoutingTables {
375 pub fn new(routes: Routes, neighbors: Neighbors, interfaces: Interfaces) -> Self {
376 Self {
377 routes,
378 neighbors,
379 interfaces,
380 }
381 }
382
383 pub fn from_netlink(table: RouteTable) -> Result<Self, io::Error> {
384 Ok(Self::new(
385 Routes::from_netlink(table)?,
386 Neighbors::from_netlink()?,
387 Interfaces::from_netlink()?,
388 ))
389 }
390
391 pub fn upsert_route(&mut self, new_route: RouteEntry) -> bool {
392 self.routes.upsert(new_route)
393 }
394
395 pub fn remove_route(&mut self, new_route: RouteEntry) -> bool {
396 self.routes.remove(new_route)
397 }
398
399 pub fn upsert_neighbor(&mut self, new_neighbor: NeighborEntry) -> bool {
400 self.neighbors.upsert(new_neighbor)
401 }
402
403 pub fn remove_neighbor(&mut self, ip: Ipv4Addr, if_index: u32) -> bool {
404 self.neighbors.remove(ip, if_index)
405 }
406
407 pub fn upsert_interface(&mut self, new_interface: InterfaceInfo) -> bool {
408 self.interfaces.upsert(new_interface)
409 }
410
411 pub fn remove_interface(&mut self, if_index: u32) -> bool {
412 self.interfaces.remove(if_index)
413 }
414}
415
416#[derive(Clone)]
417pub struct Router {
418 neighbors: Neighbors,
419 routes: Routes,
420 ipv4_lpm: Ipv4Lpm,
421 interfaces: Interfaces,
422 cached_default_route: Option<NextHop>,
424 cached_gre_info: Vec<GreRouteInfo>,
426 cached_vlan_info: Vec<VlanRouteInfo>,
428}
429
430struct RoutingTableDisplay<'a>(&'a [Route<Ipv4Addr>]);
431
432impl fmt::Display for RoutingTableDisplay<'_> {
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 if self.0.is_empty() {
435 return f.write_str("<empty>");
436 }
437
438 for (i, route) in self.0.iter().enumerate() {
439 if i > 0 {
440 f.write_str("\n")?;
441 }
442 format_route(f, route)?;
443 }
444
445 Ok(())
446 }
447}
448
449impl Router {
450 pub fn new() -> Result<Self, io::Error> {
451 Self::from_tables(RoutingTables::from_netlink(RouteTable::Main)?)
452 }
453
454 pub fn from_tables(tables: RoutingTables) -> Result<Self, io::Error> {
455 let ipv4_lpm = Ipv4Lpm::build(tables.routes.as_slice());
456 let mut router = Self {
457 neighbors: tables.neighbors,
458 routes: tables.routes,
459 ipv4_lpm,
460 interfaces: tables.interfaces,
461 cached_default_route: None,
462 cached_gre_info: Vec::new(),
463 cached_vlan_info: Vec::new(),
464 };
465
466 let mut has_gre_interface = false;
467 for interface in router.interfaces.iter() {
468 if interface.gre_tunnel.is_some() {
469 has_gre_interface = true;
470 if let Some(gre) = router.interface_gre_route_info(interface) {
471 router.cached_gre_info.push(gre);
472 }
473 }
474 if let Some(vlan) = interface.vlan_link {
475 router.cached_vlan_info.push(VlanRouteInfo {
476 if_index: interface.if_index,
477 vid: vlan.vid,
478 pcp: 0,
480 });
481 }
482 }
483 if router.cached_gre_info.is_empty() && has_gre_interface {
484 warn!("GRE cache: GRE interface(s) present but none with valid remote resolved");
485 }
486
487 router.cached_default_route = match router.default_route() {
488 Ok(hop) => Some(hop),
489 Err(RouteError::NoRouteFound(_)) => None,
490 Err(e) => return Err(io::Error::other(e)),
491 };
492
493 Ok(router)
494 }
495
496 pub fn routing_table(&self) -> impl fmt::Display + '_ {
497 RoutingTableDisplay(self.routes.as_slice())
498 }
499
500 fn cached_gre_route_info(&self, if_index: u32) -> Option<&GreRouteInfo> {
501 self.cached_gre_info
502 .iter()
503 .find(|gre| gre.if_index == if_index)
504 }
505
506 fn gre_route_info(&self, if_index: u32) -> Option<GreRouteInfo> {
507 if let Some(gre) = self.cached_gre_route_info(if_index) {
508 return Some(gre.clone());
509 }
510
511 let interface = self
512 .interfaces
513 .iter()
514 .find(|interface| interface.if_index == if_index)?;
515 self.interface_gre_route_info(interface)
516 }
517
518 fn cached_vlan_route_info(&self, if_index: u32) -> Option<VlanRouteInfo> {
519 self.cached_vlan_info
520 .iter()
521 .find(|vlan| vlan.if_index == if_index)
522 .copied()
523 }
524
525 fn resolve_next_hop(
526 &self,
527 route_ip: Ipv4Addr,
528 route: &Route<Ipv4Addr>,
529 ) -> Result<NextHop, RouteError> {
530 let if_index = route
531 .out_if_index
532 .ok_or(RouteError::MissingOutputInterface)?;
533 let next_hop_v4 = route.gateway.unwrap_or(route_ip);
534 let next_hop_ip = IpAddr::V4(next_hop_v4);
535 let preferred_src_ip = route.preferred_src;
536
537 if let Some(default_route) = &self.cached_default_route
538 && default_route.ip_addr == next_hop_ip
539 && default_route.if_index == if_index
540 {
541 return Ok(NextHop {
542 ip_addr: next_hop_ip,
543 if_index,
544 mtu: default_route.mtu,
545 mac_addr: default_route.mac_addr,
546 preferred_src_ip,
547 gre: default_route.gre.clone(),
548 vlan: default_route.vlan,
549 });
550 }
551
552 if let Some(gre) = self.gre_route_info(if_index) {
553 return Ok(NextHop {
554 if_index,
555 mtu: gre.underlay_mtu,
556 ip_addr: next_hop_ip,
557 mac_addr: Some(gre.mac_addr),
558 preferred_src_ip,
559 gre: Some(gre),
560 vlan: None,
561 });
562 }
563
564 let mtu = self
565 .interfaces
566 .iter()
567 .find(|interface| interface.if_index == if_index)
568 .map(|interface| interface.mtu)
569 .ok_or(RouteError::UnknownInterfaceIndex(if_index))?;
570 let vlan = self.cached_vlan_route_info(if_index);
571
572 let mac_addr = if next_hop_v4.is_multicast() {
576 Some(ipv4_multicast_mac(next_hop_v4))
577 } else {
578 self.neighbors.lookup(next_hop_ip, if_index).cloned()
579 };
580 Ok(NextHop {
581 ip_addr: next_hop_ip,
582 mac_addr,
583 if_index,
584 mtu,
585 preferred_src_ip,
586 gre: None,
587 vlan,
588 })
589 }
590
591 fn default_route(&self) -> Result<NextHop, RouteError> {
592 let default_route = self
593 .ipv4_lpm
594 .default_route()
595 .and_then(|route_idx| self.routes.get(route_idx as usize))
596 .ok_or(RouteError::NoRouteFound(IpAddr::V4(Ipv4Addr::UNSPECIFIED)))?;
597 self.resolve_next_hop(Ipv4Addr::UNSPECIFIED, default_route)
598 }
599
600 pub fn default(&self) -> Result<NextHop, RouteError> {
601 if let Some(default_route) = &self.cached_default_route {
602 Ok(default_route.clone())
603 } else {
604 self.default_route()
605 }
606 }
607
608 fn lookup_route_v4(&self, dest_ip: Ipv4Addr) -> Option<&Route<Ipv4Addr>> {
609 self.ipv4_lpm
610 .lookup(dest_ip)
611 .and_then(|route_idx| self.routes.get(route_idx as usize))
612 }
613
614 pub fn route_v4(&self, dest_ip: Ipv4Addr) -> Result<NextHop, RouteError> {
615 let route = self
616 .lookup_route_v4(dest_ip)
617 .ok_or(RouteError::NoRouteFound(dest_ip.into()))?;
618 self.resolve_next_hop(dest_ip, route)
619 }
620
621 fn interface_gre_route_info(&self, interface: &InterfaceInfo) -> Option<GreRouteInfo> {
622 let tunnel_info = interface.gre_tunnel.as_ref()?;
623 let remote = match tunnel_info.remote {
624 IpAddr::V4(remote) => remote,
625 IpAddr::V6(_) => return None,
626 };
627 let local = match tunnel_info.local {
628 IpAddr::V4(local) => local,
629 IpAddr::V6(_) => return None,
630 };
631 if remote == Ipv4Addr::UNSPECIFIED || local == Ipv4Addr::UNSPECIFIED {
633 return None;
634 }
635
636 let underlay_route = self.lookup_route_v4(remote)?;
637 let underlay_if_index = underlay_route.out_if_index?;
638 let underlay_interface = self
639 .interfaces
640 .iter()
641 .find(|candidate| candidate.if_index == underlay_if_index)?;
642 if underlay_interface.is_gre() {
643 warn!(
644 "GRE interface {} has remote {} that routes via another GRE interface {}. \
645 gre-over-gre is not supported.",
646 interface.if_index, tunnel_info.remote, underlay_interface.if_index
647 );
648 return None;
649 }
650 let underlay_next_hop_ip = IpAddr::V4(underlay_route.gateway.unwrap_or(remote));
651 let mac_addr = self
652 .neighbors
653 .lookup(underlay_next_hop_ip, underlay_if_index)
654 .copied()?;
655
656 Some(GreRouteInfo {
657 if_index: interface.if_index,
658 mtu: interface.mtu,
659 underlay_mtu: underlay_interface.mtu,
660 tunnel_info: tunnel_info.clone(),
661 mac_addr,
662 })
663 }
664}
665
666#[inline]
669fn ipv4_multicast_mac(ip: Ipv4Addr) -> MacAddress {
670 let [_, b1, b2, b3] = ip.octets();
671 MacAddress([0x01, 0x00, 0x5e, b1 & 0x7f, b2, b3])
672}
673
674fn format_route(f: &mut fmt::Formatter<'_>, route: &Route<Ipv4Addr>) -> fmt::Result {
675 match route.destination {
676 Some(destination) if route.dst_len == 32 => write!(f, "{destination}")?,
677 Some(destination) => write!(f, "{destination}/{}", route.dst_len)?,
678 None => f.write_str("default")?,
679 }
680
681 if let Some(gateway) = route.gateway {
682 write!(f, " via {gateway}")?;
683 }
684 if let Some(if_index) = route.out_if_index {
685 write!(f, " dev if{if_index}")?;
686 }
687 if let Some(preferred_src) = route.preferred_src {
688 write!(f, " src {preferred_src}")?;
689 }
690 if let Some(priority) = route.priority {
691 write!(f, " metric {priority}")?;
692 }
693
694 Ok(())
695}
696
697#[cfg(test)]
698mod tests {
699 use {
700 super::*,
701 crate::netlink::{MacAddress, NeighborEntry, RouteEntry, VlanLinkInfo},
702 libc::{AF_INET, NUD_REACHABLE},
703 std::net::{IpAddr, Ipv4Addr},
704 };
705
706 const DEFAULT_MTU_FOR_TESTS: u32 = 1500;
707
708 fn test_route(destination: Option<Ipv4Addr>, out_if_index: u32) -> Route<Ipv4Addr> {
709 Route {
710 destination,
711 gateway: None,
712 preferred_src: None,
713 out_if_index: Some(out_if_index),
714 priority: None,
715 type_: 0,
716 dst_len: 32,
717 }
718 }
719
720 fn test_route_entry(
721 destination: Option<Ipv4Addr>,
722 gateway: Option<Ipv4Addr>,
723 out_if_index: u32,
724 dst_len: u8,
725 table: u32,
726 ) -> RouteEntry {
727 test_route_entry_with_priority(destination, gateway, out_if_index, dst_len, table, None)
728 }
729
730 fn test_route_entry_with_priority(
731 destination: Option<Ipv4Addr>,
732 gateway: Option<Ipv4Addr>,
733 out_if_index: u32,
734 dst_len: u8,
735 table: u32,
736 priority: Option<u32>,
737 ) -> RouteEntry {
738 RouteEntry {
739 destination: destination.map(IpAddr::V4),
740 gateway: gateway.map(IpAddr::V4),
741 pref_src: None,
742 out_if_index: Some(out_if_index as i32),
743 in_if_index: None,
744 priority,
745 table: Some(table),
746 protocol: 0,
747 scope: 0,
748 type_: 0,
749 family: AF_INET as u8,
750 dst_len,
751 flags: 0,
752 }
753 }
754
755 fn router_from_tables(
756 neighbors: Vec<NeighborEntry>,
757 routes: Vec<Route<Ipv4Addr>>,
758 interfaces: Vec<InterfaceInfo>,
759 ) -> Router {
760 let tables = RoutingTables::new(
761 Routes::new(RouteTable::Main, routes),
762 Neighbors::new(neighbors),
763 Interfaces::new(interfaces),
764 );
765 Router::from_tables(tables).unwrap()
766 }
767
768 fn empty_tables() -> RoutingTables {
769 RoutingTables::new(
770 Routes::new(RouteTable::Main, Vec::new()),
771 Neighbors::new(Vec::new()),
772 Interfaces::new(Vec::new()),
773 )
774 }
775
776 fn dual_default_tables(primary_gateway: Ipv4Addr, backup_gateway: Ipv4Addr) -> RoutingTables {
777 let mut tables = empty_tables();
778 assert!(tables.upsert_interface(InterfaceInfo {
779 if_index: 1,
780 mtu: DEFAULT_MTU_FOR_TESTS,
781 gre_tunnel: None,
782 vlan_link: None,
783 }));
784 assert!(tables.upsert_interface(InterfaceInfo {
785 if_index: 2,
786 mtu: DEFAULT_MTU_FOR_TESTS,
787 gre_tunnel: None,
788 vlan_link: None,
789 }));
790 assert!(tables.upsert_neighbor(NeighborEntry {
791 destination: Some(IpAddr::V4(primary_gateway)),
792 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
793 ifindex: 1,
794 state: NUD_REACHABLE,
795 }));
796 assert!(tables.upsert_neighbor(NeighborEntry {
797 destination: Some(IpAddr::V4(backup_gateway)),
798 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x02])),
799 ifindex: 2,
800 state: NUD_REACHABLE,
801 }));
802 assert!(tables.upsert_route(test_route_entry_with_priority(
803 None,
804 Some(backup_gateway),
805 2,
806 0,
807 u32::from(RouteTable::Main),
808 Some(200),
809 )));
810 assert!(tables.upsert_route(test_route_entry_with_priority(
811 None,
812 Some(primary_gateway),
813 1,
814 0,
815 u32::from(RouteTable::Main),
816 Some(100),
817 )));
818 tables
819 }
820
821 #[test]
822 fn test_router() {
823 let mut tables = empty_tables();
824 let before_routes_len = tables.routes.iter().len();
825
826 assert!(tables.upsert_interface(InterfaceInfo {
827 if_index: 1,
828 mtu: DEFAULT_MTU_FOR_TESTS,
829 gre_tunnel: None,
830 vlan_link: None,
831 }));
832
833 let gateway = Ipv4Addr::new(10, 255, 255, 1);
834 let neighbor = NeighborEntry {
835 destination: Some(IpAddr::V4(gateway)),
836 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
837 ifindex: 1,
838 state: NUD_REACHABLE,
839 };
840 assert!(tables.upsert_neighbor(neighbor));
841
842 let test_dst = Ipv4Addr::new(10, 255, 255, 123);
844 let route = RouteEntry {
845 destination: Some(IpAddr::V4(test_dst)),
846 gateway: Some(IpAddr::V4(gateway)),
847 pref_src: None,
848 out_if_index: Some(1),
849 in_if_index: None,
850 priority: None,
851 table: Some(u32::from(RouteTable::Main)),
852 protocol: 0,
853 scope: 0,
854 type_: 0,
855 family: AF_INET as u8,
856 dst_len: 32,
857 flags: 0,
858 };
859
860 assert!(tables.upsert_route(route.clone()));
862 assert!(tables.routes.iter().any(|r| {
863 r.destination == Some(test_dst)
864 && r.gateway == Some(gateway)
865 && r.out_if_index == Some(1)
866 }));
867 assert!(tables.routes.iter().len() >= before_routes_len);
868
869 let router = Router::from_tables(tables.clone()).unwrap();
870 let next_hop = router.route_v4(test_dst).unwrap();
871 assert_eq!(next_hop.if_index, 1);
872 assert_eq!(next_hop.ip_addr, IpAddr::V4(gateway));
873
874 assert!(tables.remove_route(route.clone()));
876 assert!(
877 tables
878 .routes
879 .iter()
880 .all(|r| r.destination != Some(test_dst))
881 );
882 assert_eq!(tables.routes.iter().len(), before_routes_len);
883 }
884
885 #[test]
886 fn test_routing_table_display() {
887 let router = router_from_tables(
888 vec![],
889 vec![
890 Route {
891 destination: Some(Ipv4Addr::new(10, 0, 0, 0)),
892 gateway: Some(Ipv4Addr::new(192, 168, 1, 1)),
893 preferred_src: Some(Ipv4Addr::new(192, 168, 1, 10)),
894 out_if_index: Some(2),
895 priority: Some(100),
896 type_: 0,
897 dst_len: 24,
898 },
899 Route {
900 destination: None,
901 gateway: Some(Ipv4Addr::new(192, 168, 1, 254)),
902 preferred_src: None,
903 out_if_index: Some(3),
904 priority: None,
905 type_: 0,
906 dst_len: 0,
907 },
908 ],
909 vec![
910 InterfaceInfo {
911 if_index: 2,
912 mtu: DEFAULT_MTU_FOR_TESTS,
913 gre_tunnel: None,
914 vlan_link: None,
915 },
916 InterfaceInfo {
917 if_index: 3,
918 mtu: DEFAULT_MTU_FOR_TESTS,
919 gre_tunnel: None,
920 vlan_link: None,
921 },
922 ],
923 );
924
925 assert_eq!(
926 router.routing_table().to_string(),
927 "10.0.0.0/24 via 192.168.1.1 dev if2 src 192.168.1.10 metric 100\ndefault via \
928 192.168.1.254 dev if3"
929 );
930 }
931
932 #[test]
933 fn test_neighbors_table() {
934 let mut tables = empty_tables();
935 let before_neigh_len = tables.neighbors.iter().len();
936
937 let neigh_ip = Ipv4Addr::new(10, 255, 255, 77);
939 let entry = NeighborEntry {
940 destination: Some(IpAddr::V4(neigh_ip)),
941 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
942 ifindex: 1,
943 state: NUD_REACHABLE,
944 };
945
946 assert!(tables.upsert_neighbor(entry.clone()));
948 assert!(tables.neighbors.iter().any(|n| n == &entry));
949 assert!(tables.neighbors.iter().len() >= before_neigh_len);
950
951 assert!(tables.remove_neighbor(neigh_ip, 1));
953 assert!(tables.neighbors.iter().all(|n| n != &entry));
954 assert_eq!(tables.neighbors.iter().len(), before_neigh_len);
955 }
956
957 #[test]
958 fn test_interface_table() {
959 let mut tables = empty_tables();
960 let before_interface_len = tables.interfaces.iter().len();
961
962 let test_if_index = 99999;
964 let interface = InterfaceInfo {
965 if_index: test_if_index,
966 mtu: DEFAULT_MTU_FOR_TESTS,
967 gre_tunnel: None,
968 vlan_link: None,
969 };
970
971 assert!(tables.upsert_interface(interface.clone()));
973 assert!(tables.interfaces.iter().any(|i| i == &interface));
974 assert!(tables.interfaces.iter().len() >= before_interface_len);
975
976 assert!(!tables.upsert_interface(interface.clone()));
978
979 let mut modified_interface = interface.clone();
981 modified_interface.gre_tunnel = Some(GreTunnelInfo {
982 local: IpAddr::V4(Ipv4Addr::new(10, 255, 255, 2)),
983 remote: IpAddr::V4(Ipv4Addr::new(10, 255, 255, 1)),
984 ttl: 0,
985 tos: 0,
986 pmtudisc: 0,
987 });
988 assert!(tables.upsert_interface(modified_interface.clone()));
989 assert!(tables.interfaces.iter().any(|i| i == &modified_interface));
990 assert!(tables.interfaces.iter().all(|i| i != &interface));
991
992 assert!(tables.remove_interface(test_if_index));
994 assert!(
995 tables
996 .interfaces
997 .iter()
998 .all(|i| i.if_index != test_if_index)
999 );
1000 assert_eq!(tables.interfaces.iter().len(), before_interface_len);
1001 }
1002
1003 #[test]
1004 fn test_routes_ignore_other_table_updates() {
1005 let test_dst = Ipv4Addr::new(10, 0, 0, 1);
1006 let main_gateway = Ipv4Addr::new(10, 0, 0, 2);
1007 let other_gateway = Ipv4Addr::new(10, 0, 0, 3);
1008 let main_table = u32::from(RouteTable::Main);
1009
1010 let mut tables = empty_tables();
1011 assert!(tables.upsert_interface(InterfaceInfo {
1012 if_index: 1,
1013 mtu: DEFAULT_MTU_FOR_TESTS,
1014 gre_tunnel: None,
1015 vlan_link: None,
1016 }));
1017 assert!(tables.upsert_neighbor(NeighborEntry {
1018 destination: Some(IpAddr::V4(main_gateway)),
1019 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
1020 ifindex: 1,
1021 state: NUD_REACHABLE,
1022 }));
1023 assert!(tables.upsert_route(test_route_entry(
1024 Some(test_dst),
1025 Some(main_gateway),
1026 1,
1027 32,
1028 main_table,
1029 )));
1030
1031 assert!(!tables.upsert_route(test_route_entry(
1032 Some(test_dst),
1033 Some(other_gateway),
1034 2,
1035 32,
1036 100,
1037 )));
1038
1039 let router = Router::from_tables(tables).unwrap();
1040 let next_hop = router.route_v4(test_dst).unwrap();
1041 assert_eq!(next_hop.if_index, 1);
1042 assert_eq!(next_hop.ip_addr, IpAddr::V4(main_gateway));
1043 }
1044
1045 #[test]
1046 fn test_routes_ignore_other_table_deletes() {
1047 let test_dst = Ipv4Addr::new(10, 0, 0, 1);
1048 let main_gateway = Ipv4Addr::new(10, 0, 0, 2);
1049 let main_table = u32::from(RouteTable::Main);
1050
1051 let mut tables = empty_tables();
1052 assert!(tables.upsert_interface(InterfaceInfo {
1053 if_index: 1,
1054 mtu: DEFAULT_MTU_FOR_TESTS,
1055 gre_tunnel: None,
1056 vlan_link: None,
1057 }));
1058 assert!(tables.upsert_neighbor(NeighborEntry {
1059 destination: Some(IpAddr::V4(main_gateway)),
1060 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
1061 ifindex: 1,
1062 state: NUD_REACHABLE,
1063 }));
1064 assert!(tables.upsert_route(test_route_entry(
1065 Some(test_dst),
1066 Some(main_gateway),
1067 1,
1068 32,
1069 main_table,
1070 )));
1071
1072 assert!(!tables.remove_route(test_route_entry(
1073 Some(test_dst),
1074 Some(Ipv4Addr::new(10, 0, 0, 3)),
1075 2,
1076 32,
1077 100,
1078 )));
1079
1080 let router = Router::from_tables(tables).unwrap();
1081 let next_hop = router.route_v4(test_dst).unwrap();
1082 assert_eq!(next_hop.if_index, 1);
1083 assert_eq!(next_hop.ip_addr, IpAddr::V4(main_gateway));
1084 }
1085
1086 #[test]
1087 fn test_multiple_gre_interfaces() {
1088 let remote1 = Ipv4Addr::new(10, 0, 0, 1);
1089 let remote2 = Ipv4Addr::new(10, 0, 0, 2);
1090 let gre_dest1 = Ipv4Addr::new(192, 168, 0, 1);
1091 let gre_dest2 = Ipv4Addr::new(192, 168, 0, 2);
1092 let if_index_underlay = 1;
1093 let if_index_gre1 = 100;
1094 let if_index_gre2 = 200;
1095 let mac1 = MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01]);
1096 let mac2 = MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x02]);
1097
1098 let neighbors = vec![
1099 NeighborEntry {
1100 destination: Some(IpAddr::V4(remote1)),
1101 lladdr: Some(mac1),
1102 ifindex: if_index_underlay,
1103 state: NUD_REACHABLE,
1104 },
1105 NeighborEntry {
1106 destination: Some(IpAddr::V4(remote2)),
1107 lladdr: Some(mac2),
1108 ifindex: if_index_underlay,
1109 state: NUD_REACHABLE,
1110 },
1111 ];
1112
1113 let routes = vec![
1114 test_route(Some(remote1), if_index_underlay as u32),
1115 test_route(Some(remote2), if_index_underlay as u32),
1116 test_route(Some(gre_dest1), if_index_gre1 as u32),
1117 test_route(Some(gre_dest2), if_index_gre2 as u32),
1118 ];
1119
1120 let interfaces = vec![
1121 InterfaceInfo {
1122 if_index: if_index_underlay as u32,
1123 mtu: DEFAULT_MTU_FOR_TESTS,
1124 gre_tunnel: None,
1125 vlan_link: None,
1126 },
1127 InterfaceInfo {
1128 if_index: if_index_gre1 as u32,
1129 mtu: DEFAULT_MTU_FOR_TESTS - 100,
1130 gre_tunnel: Some(GreTunnelInfo {
1131 local: IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3)),
1132 remote: IpAddr::V4(remote1),
1133 ttl: 0,
1134 tos: 0,
1135 pmtudisc: 0,
1136 }),
1137 vlan_link: None,
1138 },
1139 InterfaceInfo {
1140 if_index: if_index_gre2 as u32,
1141 mtu: DEFAULT_MTU_FOR_TESTS + 100,
1142 gre_tunnel: Some(GreTunnelInfo {
1143 local: IpAddr::V4(Ipv4Addr::new(10, 0, 0, 4)),
1144 remote: IpAddr::V4(remote2),
1145 ttl: 0,
1146 tos: 0,
1147 pmtudisc: 0,
1148 }),
1149 vlan_link: None,
1150 },
1151 ];
1152
1153 let router = router_from_tables(neighbors, routes, interfaces);
1154 let hop1 = router.route_v4(gre_dest1).unwrap();
1155 assert_eq!(hop1.if_index, if_index_gre1 as u32);
1156 assert_eq!(hop1.mtu, DEFAULT_MTU_FOR_TESTS);
1157 assert_eq!(hop1.mac_addr, Some(mac1));
1158
1159 let hop1_gre = hop1.gre.as_ref().unwrap();
1160 assert_eq!(hop1_gre.if_index, if_index_gre1 as u32);
1161 assert_eq!(hop1_gre.mtu, DEFAULT_MTU_FOR_TESTS - 100);
1162 assert_eq!(hop1_gre.underlay_mtu, DEFAULT_MTU_FOR_TESTS);
1163 assert_eq!(hop1.mtu, hop1_gre.underlay_mtu);
1164
1165 let hop2 = router.route_v4(gre_dest2).unwrap();
1166 assert_eq!(hop2.if_index, if_index_gre2 as u32);
1167 assert_eq!(hop2.mtu, DEFAULT_MTU_FOR_TESTS);
1168 assert_eq!(hop2.mac_addr, Some(mac2));
1169
1170 let hop2_gre = hop2.gre.as_ref().unwrap();
1171 assert_eq!(hop2_gre.if_index, if_index_gre2 as u32);
1172 assert_eq!(hop2_gre.mtu, DEFAULT_MTU_FOR_TESTS + 100);
1173 assert_eq!(hop2_gre.underlay_mtu, DEFAULT_MTU_FOR_TESTS);
1174 assert_eq!(hop2.mtu, hop2_gre.underlay_mtu);
1175 }
1176
1177 #[test]
1178 fn test_default_route_via_gre() {
1179 let remote = Ipv4Addr::new(10, 0, 0, 1);
1180 let dest = Ipv4Addr::new(203, 0, 113, 9);
1181 let if_index_underlay = 1;
1182 let if_index_gre = 100;
1183 let mac = MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01]);
1184
1185 let neighbors = vec![NeighborEntry {
1186 destination: Some(IpAddr::V4(remote)),
1187 lladdr: Some(mac),
1188 ifindex: if_index_underlay,
1189 state: NUD_REACHABLE,
1190 }];
1191
1192 let routes = vec![
1193 test_route(Some(remote), if_index_underlay as u32),
1194 Route {
1195 destination: None,
1196 gateway: None,
1197 preferred_src: None,
1198 out_if_index: Some(if_index_gre as u32),
1199 priority: None,
1200 type_: 0,
1201 dst_len: 0,
1202 },
1203 ];
1204
1205 let interfaces = vec![
1206 InterfaceInfo {
1207 if_index: if_index_underlay as u32,
1208 mtu: DEFAULT_MTU_FOR_TESTS,
1209 gre_tunnel: None,
1210 vlan_link: None,
1211 },
1212 InterfaceInfo {
1213 if_index: if_index_gre as u32,
1214 mtu: DEFAULT_MTU_FOR_TESTS - 100,
1215 gre_tunnel: Some(GreTunnelInfo {
1216 local: IpAddr::V4(Ipv4Addr::new(10, 1, 0, 1)),
1217 remote: IpAddr::V4(remote),
1218 ttl: 0,
1219 tos: 0,
1220 pmtudisc: 0,
1221 }),
1222 vlan_link: None,
1223 },
1224 ];
1225
1226 let router = router_from_tables(neighbors, routes, interfaces);
1227
1228 let hop_default = router.default().unwrap();
1229 assert_eq!(hop_default.if_index, if_index_gre as u32);
1230 assert_eq!(hop_default.mtu, DEFAULT_MTU_FOR_TESTS);
1231 assert_eq!(hop_default.mac_addr, Some(mac));
1232
1233 let hop_default_gre = hop_default.gre.as_ref().unwrap();
1234 assert_eq!(hop_default_gre.if_index, if_index_gre as u32);
1235 assert_eq!(hop_default_gre.mtu, DEFAULT_MTU_FOR_TESTS - 100);
1236 assert_eq!(hop_default_gre.underlay_mtu, DEFAULT_MTU_FOR_TESTS);
1237 assert_eq!(hop_default.mtu, hop_default_gre.underlay_mtu);
1238
1239 let hop_route = router.route_v4(dest).unwrap();
1240 assert_eq!(hop_route.if_index, if_index_gre as u32);
1241 assert_eq!(hop_route.mtu, DEFAULT_MTU_FOR_TESTS);
1242 assert_eq!(hop_route.mac_addr, Some(mac));
1243
1244 let hop_route_gre = hop_route.gre.as_ref().unwrap();
1245 assert_eq!(hop_route_gre.if_index, if_index_gre as u32);
1246 assert_eq!(hop_route_gre.mtu, DEFAULT_MTU_FOR_TESTS - 100);
1247 assert_eq!(hop_route_gre.underlay_mtu, DEFAULT_MTU_FOR_TESTS);
1248 assert_eq!(hop_route.mtu, hop_route_gre.underlay_mtu);
1249 }
1250
1251 #[test]
1252 fn test_missing_output_interface_blocks_fallback_route() {
1253 let blocked_dst = Ipv4Addr::new(10, 0, 0, 2);
1254 let default_gateway = Ipv4Addr::new(10, 0, 0, 1);
1255
1256 let mut tables = empty_tables();
1257 assert!(tables.upsert_interface(InterfaceInfo {
1258 if_index: 1,
1259 mtu: DEFAULT_MTU_FOR_TESTS,
1260 gre_tunnel: None,
1261 vlan_link: None,
1262 }));
1263 assert!(tables.upsert_neighbor(NeighborEntry {
1264 destination: Some(IpAddr::V4(default_gateway)),
1265 lladdr: Some(MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01])),
1266 ifindex: 1,
1267 state: NUD_REACHABLE,
1268 }));
1269
1270 assert!(tables.upsert_route(test_route_entry(
1271 None,
1272 Some(default_gateway),
1273 1,
1274 0,
1275 u32::from(RouteTable::Main),
1276 )));
1277
1278 assert!(tables.upsert_route(RouteEntry {
1279 destination: Some(IpAddr::V4(blocked_dst)),
1280 gateway: None,
1281 pref_src: None,
1282 out_if_index: None, in_if_index: None,
1284 priority: None,
1285 table: Some(u32::from(RouteTable::Main)),
1286 protocol: 0,
1287 scope: 0,
1288 type_: 0,
1289 family: AF_INET as u8,
1290 dst_len: 32,
1291 flags: 0,
1292 }));
1293
1294 let router = Router::from_tables(tables).unwrap();
1295 assert!(matches!(
1296 router.route_v4(blocked_dst),
1297 Err(RouteError::MissingOutputInterface)
1298 ));
1299 }
1300
1301 #[test]
1302 fn test_same_prefix_diff_priority() {
1303 let primary = Ipv4Addr::new(10, 0, 0, 1);
1304 let backup = Ipv4Addr::new(10, 0, 0, 2);
1305 let tables = dual_default_tables(primary, backup);
1306
1307 let router = Router::from_tables(tables.clone()).unwrap();
1308 let next_hop = router.default().unwrap();
1309 assert_eq!(next_hop.if_index, 1);
1310 assert_eq!(next_hop.ip_addr, IpAddr::V4(primary));
1311
1312 {
1313 let mut tables = tables.clone();
1314 assert!(tables.remove_route(test_route_entry_with_priority(
1315 None,
1316 Some(backup),
1317 2,
1318 0,
1319 u32::from(RouteTable::Main),
1320 Some(200),
1321 )));
1322
1323 let router = Router::from_tables(tables).unwrap();
1324 let next_hop = router.default().unwrap();
1325 assert_eq!(next_hop.if_index, 1);
1326 assert_eq!(next_hop.ip_addr, IpAddr::V4(primary));
1327 }
1328
1329 let mut tables = tables;
1330 assert!(tables.remove_route(test_route_entry_with_priority(
1331 None,
1332 Some(primary),
1333 1,
1334 0,
1335 u32::from(RouteTable::Main),
1336 Some(100),
1337 )));
1338
1339 let router = Router::from_tables(tables).unwrap();
1340 let next_hop = router.default().unwrap();
1341 assert_eq!(next_hop.if_index, 2);
1342 assert_eq!(next_hop.ip_addr, IpAddr::V4(backup));
1343 }
1344
1345 #[test]
1346 fn test_vlan_route_resolves_with_vlan_info() {
1347 let peer = Ipv4Addr::new(10, 228, 0, 99);
1350 let vlan_if = 100u32;
1351 let vlan_src = Ipv4Addr::new(10, 228, 0, 5);
1352 let peer_mac = MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x09]);
1353
1354 let neighbors = vec![NeighborEntry {
1355 destination: Some(IpAddr::V4(peer)),
1356 lladdr: Some(peer_mac),
1357 ifindex: vlan_if as i32,
1358 state: NUD_REACHABLE,
1359 }];
1360 let interfaces = vec![InterfaceInfo {
1361 if_index: vlan_if,
1362 mtu: DEFAULT_MTU_FOR_TESTS,
1363 gre_tunnel: None,
1364 vlan_link: Some(VlanLinkInfo { vid: 900 }),
1365 }];
1366 let routes = vec![Route {
1367 destination: Some(Ipv4Addr::new(10, 228, 0, 0)),
1368 gateway: None,
1369 preferred_src: Some(vlan_src),
1370 out_if_index: Some(vlan_if),
1371 priority: None,
1372 type_: 0,
1373 dst_len: 22,
1374 }];
1375
1376 let router = router_from_tables(neighbors, routes, interfaces);
1377 let next_hop = router.route_v4(peer).unwrap();
1378 assert_eq!(next_hop.if_index, vlan_if);
1379 assert_eq!(next_hop.preferred_src_ip, Some(vlan_src));
1380 assert_eq!(next_hop.mac_addr, Some(peer_mac));
1381 assert!(next_hop.gre.is_none());
1382 let vlan = next_hop.vlan.expect("vlan info must be populated");
1383 assert_eq!(vlan.if_index, vlan_if);
1384 assert_eq!(vlan.vid, 900);
1385 assert_eq!(vlan.pcp, 0);
1386 }
1387
1388 #[test]
1389 fn test_multiple_vlan_interfaces_resolve_their_own_vid() {
1390 let peer_a = Ipv4Addr::new(10, 1, 0, 9);
1393 let peer_b = Ipv4Addr::new(10, 2, 0, 9);
1394 let vlan_if_a = 100u32;
1395 let vlan_if_b = 101u32;
1396 let mac_a = MacAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x0a]);
1397 let mac_b = MacAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x0b]);
1398
1399 let neighbors = vec![
1400 NeighborEntry {
1401 destination: Some(IpAddr::V4(peer_a)),
1402 lladdr: Some(mac_a),
1403 ifindex: vlan_if_a as i32,
1404 state: NUD_REACHABLE,
1405 },
1406 NeighborEntry {
1407 destination: Some(IpAddr::V4(peer_b)),
1408 lladdr: Some(mac_b),
1409 ifindex: vlan_if_b as i32,
1410 state: NUD_REACHABLE,
1411 },
1412 ];
1413 let interfaces = vec![
1414 InterfaceInfo {
1415 if_index: vlan_if_a,
1416 mtu: DEFAULT_MTU_FOR_TESTS,
1417 gre_tunnel: None,
1418 vlan_link: Some(VlanLinkInfo { vid: 900 }),
1419 },
1420 InterfaceInfo {
1421 if_index: vlan_if_b,
1422 mtu: DEFAULT_MTU_FOR_TESTS,
1423 gre_tunnel: None,
1424 vlan_link: Some(VlanLinkInfo { vid: 901 }),
1425 },
1426 ];
1427 let routes = vec![
1428 test_route(Some(peer_a), vlan_if_a),
1429 test_route(Some(peer_b), vlan_if_b),
1430 ];
1431
1432 let router = router_from_tables(neighbors, routes, interfaces);
1433 let hop_a = router.route_v4(peer_a).unwrap();
1434 assert_eq!(hop_a.vlan.map(|v| v.vid), Some(900));
1435 assert_eq!(hop_a.mac_addr, Some(mac_a));
1436 let hop_b = router.route_v4(peer_b).unwrap();
1437 assert_eq!(hop_b.vlan.map(|v| v.vid), Some(901));
1438 assert_eq!(hop_b.mac_addr, Some(mac_b));
1439 }
1440
1441 #[test]
1442 fn test_gre_wins_when_both_gre_and_vlan_configured() {
1443 let remote = Ipv4Addr::new(10, 0, 0, 1);
1446 let gre_dest = Ipv4Addr::new(192, 168, 0, 1);
1447 let if_index_underlay = 1u32;
1448 let if_index_iface = 100u32;
1449 let underlay_mac = MacAddress([0x02, 0xaa, 0xbb, 0xcc, 0xdd, 0x01]);
1450
1451 let neighbors = vec![NeighborEntry {
1452 destination: Some(IpAddr::V4(remote)),
1453 lladdr: Some(underlay_mac),
1454 ifindex: if_index_underlay as i32,
1455 state: NUD_REACHABLE,
1456 }];
1457 let routes = vec![
1458 test_route(Some(remote), if_index_underlay),
1459 test_route(Some(gre_dest), if_index_iface),
1460 ];
1461 let interfaces = vec![
1462 InterfaceInfo {
1463 if_index: if_index_underlay,
1464 mtu: DEFAULT_MTU_FOR_TESTS,
1465 gre_tunnel: None,
1466 vlan_link: None,
1467 },
1468 InterfaceInfo {
1469 if_index: if_index_iface,
1470 mtu: DEFAULT_MTU_FOR_TESTS,
1471 gre_tunnel: Some(GreTunnelInfo {
1472 local: IpAddr::V4(Ipv4Addr::new(10, 0, 0, 3)),
1473 remote: IpAddr::V4(remote),
1474 ttl: 0,
1475 tos: 0,
1476 pmtudisc: 0,
1477 }),
1478 vlan_link: Some(VlanLinkInfo { vid: 5 }),
1479 },
1480 ];
1481 let router = router_from_tables(neighbors, routes, interfaces);
1482 let next_hop = router.route_v4(gre_dest).unwrap();
1483 assert!(next_hop.gre.is_some());
1484 assert!(next_hop.vlan.is_none());
1485 }
1486
1487 #[test]
1488 fn test_ipv4_multicast_mac_mapping() {
1489 assert_eq!(
1491 ipv4_multicast_mac(Ipv4Addr::new(224, 0, 0, 1)),
1492 MacAddress([0x01, 0x00, 0x5e, 0x00, 0x00, 0x01]),
1493 );
1494 assert_eq!(
1495 ipv4_multicast_mac(Ipv4Addr::new(239, 0, 0, 3)),
1496 MacAddress([0x01, 0x00, 0x5e, 0x00, 0x00, 0x03]),
1497 );
1498 assert_eq!(
1500 ipv4_multicast_mac(Ipv4Addr::new(239, 128, 0, 1)),
1501 MacAddress([0x01, 0x00, 0x5e, 0x00, 0x00, 0x01]),
1502 );
1503 assert_eq!(
1504 ipv4_multicast_mac(Ipv4Addr::new(239, 255, 255, 250)),
1505 MacAddress([0x01, 0x00, 0x5e, 0x7f, 0xff, 0xfa]),
1506 );
1507 }
1508
1509 #[test]
1510 fn test_multicast_route_resolves_with_computed_mac_without_arp() {
1511 let multicast_dst = Ipv4Addr::new(239, 0, 0, 3);
1514 let if_index = 100u32;
1515 let routes = vec![Route {
1516 destination: Some(Ipv4Addr::new(239, 0, 0, 0)),
1517 gateway: None,
1518 preferred_src: None,
1519 out_if_index: Some(if_index),
1520 priority: None,
1521 type_: 0,
1522 dst_len: 8,
1523 }];
1524 let interfaces = vec![InterfaceInfo {
1525 if_index,
1526 mtu: DEFAULT_MTU_FOR_TESTS,
1527 gre_tunnel: None,
1528 vlan_link: None,
1529 }];
1530 let router = router_from_tables(vec![], routes, interfaces);
1531 let next_hop = router.route_v4(multicast_dst).unwrap();
1532 assert_eq!(
1533 next_hop.mac_addr,
1534 Some(MacAddress([0x01, 0x00, 0x5e, 0x00, 0x00, 0x03]))
1535 );
1536 assert_eq!(next_hop.if_index, if_index);
1537 }
1538
1539 #[test]
1540 fn test_unicast_without_neighbor_returns_no_mac() {
1541 let dst = Ipv4Addr::new(10, 1, 2, 3);
1544 let if_index = 5u32;
1545 let routes = vec![test_route(Some(dst), if_index)];
1546 let interfaces = vec![InterfaceInfo {
1547 if_index,
1548 mtu: DEFAULT_MTU_FOR_TESTS,
1549 gre_tunnel: None,
1550 vlan_link: None,
1551 }];
1552 let router = router_from_tables(vec![], routes, interfaces);
1553 let next_hop = router.route_v4(dst).unwrap();
1554 assert!(next_hop.mac_addr.is_none());
1555 }
1556}