From 2cca7328b111d605f94a4bf2b0e87eaefcd3f241 Mon Sep 17 00:00:00 2001 From: Paul Wekesa Date: Thu, 10 Oct 2024 04:24:05 +0300 Subject: [PATCH] vrrp: packet reformating & fixes on vrrp rx socket Make sure that the transmission of VRRP packets is done with the correct virtual MAC as source and correct multicast destination MAC. Also making sure the vrrp sockets can receive an advertisement. --- holo-vrrp/src/instance.rs | 8 ++--- holo-vrrp/src/interface.rs | 15 +++++---- holo-vrrp/src/network.rs | 62 ++++++++++++++++++++----------------- holo-vrrp/src/packet.rs | 2 -- holo-vrrp/src/southbound.rs | 11 ++++--- holo-vrrp/src/tasks.rs | 6 +--- 6 files changed, 49 insertions(+), 55 deletions(-) diff --git a/holo-vrrp/src/instance.rs b/holo-vrrp/src/instance.rs index 4fb29a8f..5b4fb5f3 100644 --- a/holo-vrrp/src/instance.rs +++ b/holo-vrrp/src/instance.rs @@ -10,7 +10,7 @@ use std::time::Duration; use chrono::{DateTime, Utc}; use holo_utils::task::{IntervalTask, TimeoutTask}; -use crate::interface::MacVlanInterface; +use crate::interface::{MacVlanInterface, VRRP_PROTO_NUMBER}; use crate::northbound::configuration::InstanceCfg; use crate::packet::{ArpPacket, EthernetHdr, Ipv4Packet, VrrpHdr}; use crate::tasks::messages::output::NetTxPacketMsg; @@ -186,7 +186,7 @@ impl Instance { flags: 0x00, offset: 0x00, ttl: 255, - protocol: 112, + protocol: VRRP_PROTO_NUMBER as u8, checksum: 0x00, src_address, dst_address: Ipv4Addr::new(224, 0, 0, 18), @@ -195,10 +195,6 @@ impl Instance { } } - pub(crate) fn advert_ether_frame(&self) -> EthernetHdr { - EthernetHdr::vrrp(self.vrid) - } - pub(crate) fn send_gratuitous_arp(&self) { // send a gratuitous for each of the // virutal IP addresses diff --git a/holo-vrrp/src/interface.rs b/holo-vrrp/src/interface.rs index b1132aac..0954e7a7 100644 --- a/holo-vrrp/src/interface.rs +++ b/holo-vrrp/src/interface.rs @@ -5,10 +5,10 @@ // use std::collections::{BTreeMap, BTreeSet}; +use std::net::Ipv4Addr; use std::sync::Arc; use async_trait::async_trait; -use bytes::{BufMut, BytesMut}; use holo_protocol::{ InstanceChannelsTx, InstanceShared, MessageReceiver, ProtocolInstance, }; @@ -31,6 +31,9 @@ use crate::tasks::messages::output::NetTxPacketMsg; use crate::tasks::messages::{ProtocolInputMsg, ProtocolOutputMsg}; use crate::{events, network, southbound, tasks}; +pub const VRRP_PROTO_NUMBER: i32 = 112; +pub const VRRP_MULTICAST_ADDRESS: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 18); + #[derive(Debug)] pub struct Interface { // Interface name. @@ -136,7 +139,7 @@ impl Interface { self.instances.remove(&vrid); if let Some(ifindex) = mvlan_ifindex { - southbound::delete_iface(ifindex, &self.tx.ibus); + southbound::mvlan_delete(ifindex, &self.tx.ibus); } } @@ -232,13 +235,9 @@ impl Interface { // ...and confirm if the instance's parent Interface has an IP address && let Some(addr) = self.system.addresses.first() { - let mut buf = BytesMut::new(); - - let eth_hdr = instance.advert_ether_frame(); let ip_hdr = instance.adver_ipv4_pkt(addr.ip()); let vrrp_hdr = instance.adver_vrrp_pkt(); let pkt = VrrpPacket { - eth: eth_hdr, ip: ip_hdr, vrrp: vrrp_hdr, }; @@ -259,7 +258,7 @@ impl Interface { // creates the MvlanInterfaceNet for the instance of said // vrid. Must be done here to get some interface specifics. - pub(crate) fn create_mvlan_net(&mut self, vrid: u8) { + pub(crate) fn macvlan_create(&mut self, vrid: u8) { let net = MvlanInterfaceNet::new(self, vrid) .expect("Failed to intialize VRRP tasks"); @@ -363,7 +362,7 @@ impl MvlanInterfaceNet { let ifname = &instance.mac_vlan.name; let instance_channels_tx = &parent_iface.tx; - let socket_vrrp_rx = network::socket_vrrp_rx(ifname) + let socket_vrrp_rx = network::socket_vrrp_rx(parent_iface) .map_err(IoError::SocketError) .and_then(|socket| { AsyncFd::new(socket).map_err(IoError::SocketError) diff --git a/holo-vrrp/src/network.rs b/holo-vrrp/src/network.rs index 108cf077..d6ce2b21 100644 --- a/holo-vrrp/src/network.rs +++ b/holo-vrrp/src/network.rs @@ -5,22 +5,19 @@ // use std::ffi::CString; -use std::net::Ipv4Addr; use std::os::fd::AsRawFd; use std::sync::Arc; use holo_utils::socket::{AsyncFd, Socket}; use holo_utils::{capabilities, Sender, UnboundedReceiver}; -use libc::{if_nametoindex, AF_PACKET, ETH_P_ARP, ETH_P_IP}; +use libc::ETH_P_ARP; use nix::sys::socket; -use socket2::{Domain, InterfaceIndexOrAddress, Protocol, Type}; +use socket2::{Domain, Protocol, Type}; use tokio::sync::mpsc::error::SendError; -use tracing::{debug, debug_span}; use crate::error::IoError; -use crate::interface::Interface; -use crate::packet::VrrpPacket; -use crate::packet::{ArpPacket, EthernetHdr, Ipv4Packet, VrrpHdr}; +use crate::interface::{Interface, VRRP_MULTICAST_ADDRESS, VRRP_PROTO_NUMBER}; +use crate::packet::{ArpPacket, EthernetHdr, Ipv4Packet, VrrpHdr, VrrpPacket}; use crate::tasks::messages::input::VrrpNetRxPacketMsg; use crate::tasks::messages::output::NetTxPacketMsg; @@ -31,20 +28,27 @@ pub fn socket_vrrp_tx( #[cfg(not(feature = "testing"))] { let instance = interface.instances.get(&vrid).unwrap(); - let sock = capabilities::raise(|| { - Socket::new(Domain::PACKET, Type::RAW, Some(Protocol::from(112))) + Socket::new( + Domain::IPV4, + Type::RAW, + Some(Protocol::from(VRRP_PROTO_NUMBER)), + ) })?; - capabilities::raise(|| sock.set_nonblocking(true))?; + if let Some(addr) = instance.mac_vlan.system.addresses.first() { + capabilities::raise(|| sock.set_multicast_if_v4(&addr.ip()))?; + } + + capabilities::raise(|| sock.set_header_included(true))?; // Confirm if we should bind to the primary interface's address... // bind it to the primary interface's name capabilities::raise(|| { - sock.bind_device(Some(interface.name.as_str().as_bytes())) + sock.bind_device(Some(instance.mac_vlan.name.as_bytes())) })?; capabilities::raise(|| { - sock.set_reuse_address(true); + let _ = sock.set_reuse_address(true); }); Ok(sock) @@ -55,17 +59,19 @@ pub fn socket_vrrp_tx( } } -pub fn socket_vrrp_rx(ifname: &str) -> Result { +pub fn socket_vrrp_rx(iface: &Interface) -> Result { #[cfg(not(feature = "testing"))] { let sock = capabilities::raise(|| { - Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::from(112))) + Socket::new( + Domain::IPV4, + Type::RAW, + Some(Protocol::from(VRRP_PROTO_NUMBER)), + ) })?; - - capabilities::raise(|| sock.bind_device(Some(ifname.as_bytes())))?; - capabilities::raise(|| sock.set_broadcast(true))?; + capabilities::raise(|| sock.bind_device(Some(iface.name.as_bytes())))?; capabilities::raise(|| sock.set_nonblocking(true))?; - capabilities::raise(|| join_multicast(&sock, ifname))?; + capabilities::raise(|| join_multicast(&sock, iface))?; Ok(sock) } @@ -107,8 +113,8 @@ pub(crate) async fn send_packet_vrrp( unsafe { let ifindex = libc::if_nametoindex(c_ifname.as_ptr()); let mut sa = libc::sockaddr_ll { - sll_family: libc::AF_PACKET as u16, - sll_protocol: (112 as u16).to_be(), + sll_family: libc::AF_INET as u16, + sll_protocol: (VRRP_PROTO_NUMBER as u16).to_be(), sll_ifindex: ifindex as i32, sll_hatype: 0, sll_pkttype: 0, @@ -192,16 +198,14 @@ pub async fn send_packet_arp( // for joining the VRRP multicast pub fn join_multicast( sock: &Socket, - ifname: &str, + iface: &Interface, ) -> Result<(), std::io::Error> { let sock = socket2::SockRef::from(sock); - let ifname = CString::new(ifname).unwrap(); - let ifindex = unsafe { if_nametoindex(ifname.as_ptr()) }; - - sock.join_multicast_v4_n( - &Ipv4Addr::new(224, 0, 0, 18), - &InterfaceIndexOrAddress::Index(ifindex), - ) + if let Some(addr) = iface.system.addresses.first() { + let ip = addr.ip(); + return sock.join_multicast_v4(&VRRP_MULTICAST_ADDRESS, &ip); + } + Err(std::io::Error::last_os_error()) } #[cfg(not(feature = "testing"))] @@ -240,7 +244,7 @@ pub(crate) async fn vrrp_read_loop( socket_vrrp: Arc>, vrrp_net_packet_rxp: Sender, ) -> Result<(), SendError> { - let mut buf = [0; 128]; + let mut buf = [0u8; 96]; loop { match socket_vrrp .async_io(tokio::io::Interest::READABLE, |sock| { diff --git a/holo-vrrp/src/packet.rs b/holo-vrrp/src/packet.rs index 18ed1b42..c6d4e7c8 100644 --- a/holo-vrrp/src/packet.rs +++ b/holo-vrrp/src/packet.rs @@ -158,7 +158,6 @@ pub struct ArpPacket { #[derive(Clone, Debug, Eq, PartialEq)] #[derive(Deserialize, Serialize)] pub struct VrrpPacket { - pub eth: EthernetHdr, pub ip: Ipv4Packet, pub vrrp: VrrpHdr, } @@ -421,7 +420,6 @@ impl EthernetHdr { impl VrrpPacket { pub fn encode(&self) -> BytesMut { let mut buf = BytesMut::with_capacity(130); - buf.put(self.eth.encode()); buf.put(self.ip.encode()); buf.put(self.vrrp.encode()); buf diff --git a/holo-vrrp/src/southbound.rs b/holo-vrrp/src/southbound.rs index 7ba783b0..bad3c4b6 100644 --- a/holo-vrrp/src/southbound.rs +++ b/holo-vrrp/src/southbound.rs @@ -69,12 +69,12 @@ pub(crate) fn process_iface_update( } if let Some(vrid) = target_vrid { - iface.create_mvlan_net(vrid); + iface.macvlan_create(vrid); } } pub(crate) fn process_addr_add(iface: &mut Interface, msg: AddressMsg) { - if msg.ifname != iface.name { + if msg.ifname == iface.name { if let IpNetwork::V4(addr) = msg.addr { iface.system.addresses.insert(addr); } @@ -88,7 +88,7 @@ pub(crate) fn process_addr_add(iface: &mut Interface, msg: AddressMsg) { for (vrid, instance) in iface.instances.iter_mut() { let name = format!("mvlan-vrrp-{}", vrid); let mvlan_iface = &mut instance.mac_vlan; - if mvlan_iface.system.addresses.len() == 0 { + if mvlan_iface.system.addresses.is_empty() { target_vrid = Some(*vrid); } if mvlan_iface.name == name { @@ -99,7 +99,8 @@ pub(crate) fn process_addr_add(iface: &mut Interface, msg: AddressMsg) { } if let Some(vrid) = target_vrid { - iface.create_mvlan_net(vrid); + iface.macvlan_create(vrid); + iface.reset_timer(vrid); } } @@ -142,7 +143,7 @@ pub(crate) fn create_macvlan_iface( } // deletes and interface e.g eth0 entirely -pub(crate) fn delete_iface(ifindex: u32, ibus_tx: &IbusSender) { +pub(crate) fn mvlan_delete(ifindex: u32, ibus_tx: &IbusSender) { let _ = ibus_tx.send(IbusMsg::InterfaceDeleteRequest(ifindex)); } diff --git a/holo-vrrp/src/tasks.rs b/holo-vrrp/src/tasks.rs index f3387778..71ade490 100644 --- a/holo-vrrp/src/tasks.rs +++ b/holo-vrrp/src/tasks.rs @@ -7,10 +7,9 @@ use std::sync::Arc; use std::time::Duration; -use bytes::{BufMut, BytesMut}; use holo_utils::socket::{AsyncFd, Socket}; use holo_utils::task::{IntervalTask, Task, TimeoutTask}; -use holo_utils::{Sender, UnboundedReceiver, UnboundedSender}; +use holo_utils::{Sender, UnboundedReceiver}; use messages::input::MasterDownTimerMsg; use messages::output::NetTxPacketMsg; use tracing::{debug_span, Instrument}; @@ -199,16 +198,13 @@ pub(crate) fn set_timer( // every ADVERT_INTERVAL seconds until otherwise. crate::instance::State::Master => { // ----------------- - let mut buf = BytesMut::new(); let src_ip = interface.system.addresses.first().unwrap().ip(); - let eth_hdr = instance.advert_ether_frame(); let ip_hdr = instance.adver_ipv4_pkt(src_ip); let vrrp_hdr = instance.adver_vrrp_pkt(); let pkt = VrrpPacket { ip: ip_hdr, - eth: eth_hdr, vrrp: vrrp_hdr, }; let ifname = instance.mac_vlan.name.clone();