1use std::net::IpAddr;
42
43use super::builder::MessageBuilder;
44use super::connection::Connection;
45use super::error::{Error, Result};
46use super::interface_ref::InterfaceRef;
47use super::message::{NLM_F_ACK, NLM_F_DUMP, NLM_F_REQUEST, NlMsgType};
48use super::messages::NeighborMessage;
49use super::protocol::Route;
50use super::types::neigh::{NdMsg, NdaAttr, NeighborState};
51
52const NLM_F_CREATE: u16 = 0x400;
54const NLM_F_EXCL: u16 = 0x200;
56const NLM_F_REPLACE: u16 = 0x100;
58
59const AF_BRIDGE: u8 = 7;
61
62mod ntf {
64 pub const SELF: u8 = 0x02;
66 pub const MASTER: u8 = 0x04;
68 pub const EXT_LEARNED: u8 = 0x10;
70}
71
72mod nud {
74 pub const PERMANENT: u16 = 0x80;
76 pub const REACHABLE: u16 = 0x02;
78}
79
80#[derive(Debug, Clone)]
86pub struct FdbEntry {
87 pub ifindex: u32,
89 pub mac: [u8; 6],
91 pub vlan: Option<u16>,
93 pub dst: Option<IpAddr>,
95 pub vni: Option<u32>,
97 pub state: NeighborState,
99 pub flags: u8,
101 pub master: Option<u32>,
103}
104
105impl FdbEntry {
106 pub fn from_neighbor(msg: &NeighborMessage) -> Option<Self> {
110 let lladdr = msg.lladdr()?;
111 if lladdr.len() != 6 {
112 return None;
113 }
114
115 let mut mac = [0u8; 6];
116 mac.copy_from_slice(lladdr);
117
118 Some(Self {
119 ifindex: msg.ifindex(),
120 mac,
121 vlan: msg.vlan(),
122 dst: msg.destination().cloned(),
123 vni: msg.vni(),
124 state: msg.state(),
125 flags: msg.flags(),
126 master: msg.master(),
127 })
128 }
129
130 pub fn is_permanent(&self) -> bool {
132 self.state == NeighborState::Permanent
133 }
134
135 pub fn is_dynamic(&self) -> bool {
137 !self.is_permanent()
138 }
139
140 pub fn is_self(&self) -> bool {
142 self.flags & ntf::SELF != 0
143 }
144
145 pub fn is_master(&self) -> bool {
147 self.flags & ntf::MASTER != 0
148 }
149
150 pub fn is_extern_learn(&self) -> bool {
152 self.flags & ntf::EXT_LEARNED != 0
153 }
154
155 pub fn mac_str(&self) -> String {
157 format!(
158 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
159 self.mac[0], self.mac[1], self.mac[2], self.mac[3], self.mac[4], self.mac[5]
160 )
161 }
162}
163
164#[derive(Debug, Clone, Default)]
185#[must_use = "builders do nothing unless used"]
186pub struct FdbEntryBuilder {
187 mac: [u8; 6],
188 dev: Option<InterfaceRef>,
189 vlan: Option<u16>,
190 dst: Option<IpAddr>,
191 vni: Option<u32>,
192 master: Option<InterfaceRef>,
193 permanent: bool,
194 self_flag: bool,
195}
196
197impl FdbEntryBuilder {
198 pub fn new(mac: [u8; 6]) -> Self {
202 Self {
203 mac,
204 permanent: true,
205 ..Default::default()
206 }
207 }
208
209 pub fn parse_mac(mac_str: &str) -> Result<[u8; 6]> {
217 crate::util::addr::parse_mac(mac_str)
218 .map_err(|e| Error::InvalidMessage(format!("invalid MAC: {}", e)))
219 }
220
221 pub fn dev(mut self, dev: impl Into<String>) -> Self {
223 self.dev = Some(InterfaceRef::Name(dev.into()));
224 self
225 }
226
227 pub fn ifindex(mut self, ifindex: u32) -> Self {
232 self.dev = Some(InterfaceRef::Index(ifindex));
233 self
234 }
235
236 pub fn device_ref(&self) -> Option<&InterfaceRef> {
238 self.dev.as_ref()
239 }
240
241 pub fn vlan(mut self, vlan: u16) -> Self {
245 self.vlan = Some(vlan);
246 self
247 }
248
249 pub fn dst(mut self, dst: IpAddr) -> Self {
253 self.dst = Some(dst);
254 self
255 }
256
257 pub fn vni(mut self, vni: u32) -> Self {
259 self.vni = Some(vni);
260 self
261 }
262
263 pub fn master(mut self, master: impl Into<String>) -> Self {
265 self.master = Some(InterfaceRef::Name(master.into()));
266 self
267 }
268
269 pub fn master_ifindex(mut self, ifindex: u32) -> Self {
271 self.master = Some(InterfaceRef::Index(ifindex));
272 self
273 }
274
275 pub fn master_ref(&self) -> Option<&InterfaceRef> {
277 self.master.as_ref()
278 }
279
280 pub fn permanent(mut self) -> Self {
282 self.permanent = true;
283 self
284 }
285
286 pub fn dynamic(mut self) -> Self {
288 self.permanent = false;
289 self
290 }
291
292 pub fn self_(mut self) -> Self {
297 self.self_flag = true;
298 self
299 }
300
301 pub(crate) fn write_add(
303 &self,
304 builder: &mut MessageBuilder,
305 ifindex: u32,
306 master_idx: Option<u32>,
307 ) {
308 let state = if self.permanent {
309 nud::PERMANENT
310 } else {
311 nud::REACHABLE
312 };
313
314 let mut ntf_flags: u8 = 0;
315 if self.self_flag {
316 ntf_flags |= ntf::SELF;
317 }
318
319 let ndmsg = NdMsg::new()
320 .with_family(AF_BRIDGE)
321 .with_ifindex(ifindex as i32)
322 .with_state(state)
323 .with_flags(ntf_flags);
324
325 builder.append(&ndmsg);
326
327 builder.append_attr(NdaAttr::Lladdr as u16, &self.mac);
329
330 if let Some(master) = master_idx {
332 builder.append_attr_u32(NdaAttr::Master as u16, master);
333 }
334
335 if let Some(vlan) = self.vlan {
337 builder.append_attr_u16(NdaAttr::Vlan as u16, vlan);
338 }
339
340 if let Some(ref dst) = self.dst {
342 match dst {
343 IpAddr::V4(v4) => {
344 builder.append_attr(NdaAttr::Dst as u16, &v4.octets());
345 }
346 IpAddr::V6(v6) => {
347 builder.append_attr(NdaAttr::Dst as u16, &v6.octets());
348 }
349 }
350 }
351
352 if let Some(vni) = self.vni {
354 builder.append_attr_u32(NdaAttr::Vni as u16, vni);
355 }
356 }
357
358 pub(crate) fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) {
360 let ndmsg = NdMsg::new()
361 .with_family(AF_BRIDGE)
362 .with_ifindex(ifindex as i32);
363
364 builder.append(&ndmsg);
365
366 builder.append_attr(NdaAttr::Lladdr as u16, &self.mac);
368
369 if let Some(vlan) = self.vlan {
371 builder.append_attr_u16(NdaAttr::Vlan as u16, vlan);
372 }
373 }
374}
375
376impl Connection<Route> {
381 pub async fn get_fdb(&self, bridge: impl Into<InterfaceRef>) -> Result<Vec<FdbEntry>> {
396 let bridge_idx = self.resolve_interface(&bridge.into()).await?;
397 self.get_fdb_by_index(bridge_idx).await
398 }
399
400 pub async fn get_fdb_by_index(&self, bridge_idx: u32) -> Result<Vec<FdbEntry>> {
405 let neighbors = self.get_bridge_neighbors().await?;
407
408 Ok(neighbors
409 .iter()
410 .filter(|n| n.master() == Some(bridge_idx) || n.ifindex() == bridge_idx)
411 .filter_map(FdbEntry::from_neighbor)
412 .collect())
413 }
414
415 async fn get_bridge_neighbors(&self) -> Result<Vec<NeighborMessage>> {
417 use super::message::NLMSG_HDRLEN;
418 use super::parse::FromNetlink;
419
420 let ndmsg = NdMsg::new().with_family(AF_BRIDGE);
421 let mut builder = MessageBuilder::new(NlMsgType::RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP);
422 builder.append(&ndmsg);
423
424 let responses = self.send_dump(builder).await?;
425
426 let mut parsed = Vec::new();
427 for response in responses {
428 if response.len() < NLMSG_HDRLEN {
429 continue;
430 }
431 let payload = &response[NLMSG_HDRLEN..];
432 if let Ok(msg) = NeighborMessage::from_bytes(payload) {
433 parsed.push(msg);
434 }
435 }
436 Ok(parsed)
437 }
438
439 pub async fn get_fdb_for_port(
447 &self,
448 bridge: impl Into<InterfaceRef>,
449 port: impl Into<InterfaceRef>,
450 ) -> Result<Vec<FdbEntry>> {
451 let bridge_idx = self.resolve_interface(&bridge.into()).await?;
452 let port_idx = self.resolve_interface(&port.into()).await?;
453
454 let neighbors = self.get_bridge_neighbors().await?;
455
456 Ok(neighbors
457 .iter()
458 .filter(|n| n.ifindex() == port_idx)
459 .filter(|n| n.master() == Some(bridge_idx))
460 .filter_map(FdbEntry::from_neighbor)
461 .collect())
462 }
463
464 async fn resolve_fdb_interfaces(&self, entry: &FdbEntryBuilder) -> Result<(u32, Option<u32>)> {
466 let ifindex = match entry.device_ref() {
467 Some(iface) => self.resolve_interface(iface).await?,
468 None => {
469 return Err(Error::InvalidMessage(
470 "device name or ifindex required".into(),
471 ));
472 }
473 };
474
475 let master_idx = match entry.master_ref() {
476 Some(iface) => Some(self.resolve_interface(iface).await?),
477 None => None,
478 };
479
480 Ok((ifindex, master_idx))
481 }
482
483 pub async fn add_fdb(&self, entry: FdbEntryBuilder) -> Result<()> {
506 let (ifindex, master_idx) = self.resolve_fdb_interfaces(&entry).await?;
507 let mut builder = MessageBuilder::new(
508 NlMsgType::RTM_NEWNEIGH,
509 NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL,
510 );
511 entry.write_add(&mut builder, ifindex, master_idx);
512 self.send_ack(builder)
513 .await
514 .map_err(|e| e.with_context("add_fdb"))
515 }
516
517 pub async fn replace_fdb(&self, entry: FdbEntryBuilder) -> Result<()> {
521 let (ifindex, master_idx) = self.resolve_fdb_interfaces(&entry).await?;
522 let mut builder = MessageBuilder::new(
523 NlMsgType::RTM_NEWNEIGH,
524 NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE,
525 );
526 entry.write_add(&mut builder, ifindex, master_idx);
527 self.send_ack(builder)
528 .await
529 .map_err(|e| e.with_context("replace_fdb"))
530 }
531
532 pub async fn del_fdb(
544 &self,
545 dev: impl Into<InterfaceRef>,
546 mac: [u8; 6],
547 vlan: Option<u16>,
548 ) -> Result<()> {
549 let ifindex = self.resolve_interface(&dev.into()).await?;
550 self.del_fdb_by_index(ifindex, mac, vlan).await
551 }
552
553 pub async fn del_fdb_by_index(
557 &self,
558 ifindex: u32,
559 mac: [u8; 6],
560 vlan: Option<u16>,
561 ) -> Result<()> {
562 let mut entry = FdbEntryBuilder::new(mac).ifindex(ifindex);
563 if let Some(v) = vlan {
564 entry = entry.vlan(v);
565 }
566 let mut builder = MessageBuilder::new(NlMsgType::RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_ACK);
567 entry.write_delete(&mut builder, ifindex);
568 self.send_ack(builder)
569 .await
570 .map_err(|e| e.with_context("del_fdb"))
571 }
572
573 pub async fn flush_fdb(&self, bridge: impl Into<InterfaceRef>) -> Result<()> {
583 let entries = self.get_fdb(bridge).await?;
584
585 for entry in entries {
586 if entry.is_dynamic()
588 && let Err(e) = self
589 .del_fdb_by_index(entry.ifindex, entry.mac, entry.vlan)
590 .await
591 {
592 if !e.is_not_found() {
594 return Err(e);
595 }
596 }
597 }
598
599 Ok(())
600 }
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606
607 #[test]
608 fn test_parse_mac() {
609 let mac = FdbEntryBuilder::parse_mac("aa:bb:cc:dd:ee:ff").unwrap();
610 assert_eq!(mac, [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
611 }
612
613 #[test]
614 fn test_parse_mac_uppercase() {
615 let mac = FdbEntryBuilder::parse_mac("AA:BB:CC:DD:EE:FF").unwrap();
616 assert_eq!(mac, [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
617 }
618
619 #[test]
620 fn test_parse_mac_invalid() {
621 assert!(FdbEntryBuilder::parse_mac("invalid").is_err());
622 assert!(FdbEntryBuilder::parse_mac("aa:bb:cc:dd:ee").is_err());
623 assert!(FdbEntryBuilder::parse_mac("aa:bb:cc:dd:ee:ff:gg").is_err());
624 }
625
626 #[test]
627 fn test_fdb_entry_mac_str() {
628 let entry = FdbEntry {
629 ifindex: 1,
630 mac: [0x00, 0x11, 0x22, 0x33, 0x44, 0x55],
631 vlan: None,
632 dst: None,
633 vni: None,
634 state: NeighborState::Permanent,
635 flags: 0,
636 master: None,
637 };
638 assert_eq!(entry.mac_str(), "00:11:22:33:44:55");
639 }
640
641 #[test]
642 fn test_fdb_entry_flags() {
643 let entry = FdbEntry {
644 ifindex: 1,
645 mac: [0; 6],
646 vlan: None,
647 dst: None,
648 vni: None,
649 state: NeighborState::Permanent,
650 flags: ntf::SELF | ntf::MASTER,
651 master: None,
652 };
653 assert!(entry.is_self());
654 assert!(entry.is_master());
655 assert!(!entry.is_extern_learn());
656 }
657
658 #[test]
659 fn test_fdb_entry_permanent() {
660 let permanent = FdbEntry {
661 ifindex: 1,
662 mac: [0; 6],
663 vlan: None,
664 dst: None,
665 vni: None,
666 state: NeighborState::Permanent,
667 flags: 0,
668 master: None,
669 };
670 assert!(permanent.is_permanent());
671 assert!(!permanent.is_dynamic());
672
673 let dynamic = FdbEntry {
674 ifindex: 1,
675 mac: [0; 6],
676 vlan: None,
677 dst: None,
678 vni: None,
679 state: NeighborState::Reachable,
680 flags: 0,
681 master: None,
682 };
683 assert!(!dynamic.is_permanent());
684 assert!(dynamic.is_dynamic());
685 }
686
687 #[test]
688 fn test_builder_default() {
689 let builder = FdbEntryBuilder::new([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]);
690 assert!(builder.permanent); assert!(!builder.self_flag);
692 }
693
694 #[test]
695 fn test_builder_chain() {
696 let builder = FdbEntryBuilder::new([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
697 .dev("veth0")
698 .master("br0")
699 .vlan(100)
700 .dynamic()
701 .self_();
702
703 assert_eq!(builder.dev, Some(InterfaceRef::Name("veth0".to_string())));
704 assert_eq!(builder.master, Some(InterfaceRef::Name("br0".to_string())));
705 assert_eq!(builder.vlan, Some(100));
706 assert!(!builder.permanent);
707 assert!(builder.self_flag);
708 }
709
710 #[test]
711 fn test_builder_ifindex() {
712 let builder = FdbEntryBuilder::new([0; 6]).ifindex(5).master_ifindex(3);
713
714 assert_eq!(builder.dev, Some(InterfaceRef::Index(5)));
715 assert_eq!(builder.master, Some(InterfaceRef::Index(3)));
716 }
717}