Skip to content

Commit

Permalink
vrrp: changes for mvlanInterface
Browse files Browse the repository at this point in the history
calls for creating the instance vrrp_tx tasks
have now been shifted completely to
MvlanInterfaceNet.

THe changes have also rendered the InterfaceNet
struct useless, which has been deleted.

Signed-off-by: Paul Wekesa <[email protected]>
  • Loading branch information
Paul-weqe committed Oct 7, 2024
1 parent c19842f commit 3e5e28b
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 80 deletions.
2 changes: 1 addition & 1 deletion holo-vrrp/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ impl Instance {
let mut packet = VrrpPacket {
version: 2,
hdr_type: 1,
vrid: u8::default(),
vrid: self.vrid,
priority: self.config.priority,
count_ip: self.config.virtual_addresses.len() as u8,
auth_type: 0,
Expand Down
99 changes: 62 additions & 37 deletions holo-vrrp/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use holo_utils::task::Task;
use holo_utils::{Receiver, Sender, UnboundedSender};
use ipnetwork::{IpNetwork, Ipv4Network};
use tokio::sync::mpsc;
use tracing::{debug, debug_span};
use tracing::{debug, debug_span, error_span};

use crate::error::{Error, IoError};
use crate::instance::{Instance, State};
Expand All @@ -36,8 +36,6 @@ pub struct Interface {
pub name: String,
// Interface system data.
pub system: InterfaceSys,
// Interface raw sockets and Tx/Rx tasks.
pub net: InterfaceNet,
// Interface VRRP instances.
pub instances: BTreeMap<u8, Instance>,
// Tx channels.
Expand All @@ -62,7 +60,7 @@ pub struct MacVlanInterface {
// Interface system data.
pub system: InterfaceSys,
// Interface raw sockets and Tx/Rx tasks.
pub net: Option<InterfaceNet>,
pub net: Option<MvlanInterfaceNet>,
}

#[derive(Debug, Default)]
Expand All @@ -78,7 +76,7 @@ pub struct InterfaceSys {
}

#[derive(Debug)]
pub struct InterfaceNet {
pub struct MvlanInterfaceNet {
// Raw sockets.
pub socket_vrrp: Arc<AsyncFd<Socket>>,
pub socket_arp: Arc<AsyncFd<Socket>>,
Expand Down Expand Up @@ -146,10 +144,26 @@ impl Interface {
debug_span!("change-state").in_scope(|| {
if state == State::Backup {
debug!(%vrid, "state to BACKUP.");
// change admin state of MacVlan to down.
if let Some(ifindex) = instance.mac_vlan.system.ifindex {
for addr in instance.config.virtual_addresses.clone() {
southbound::addr_del(
ifindex,
IpNetwork::V4(addr),
&self.tx.ibus,
);
}
}
} else if state == State::Master {
debug!(%vrid, "state to MASTER.");
// change admin state of MacVlan to up.
if let Some(ifindex) = instance.mac_vlan.system.ifindex {
for addr in instance.config.virtual_addresses.clone() {
southbound::addr_add(
ifindex,
IpNetwork::V4(addr),
&self.tx.ibus,
);
}
}
}
});

Expand Down Expand Up @@ -189,12 +203,18 @@ impl Interface {
}

pub(crate) fn delete_instance_virtual_address(
&self,
&mut self,
vrid: u8,
addr: Ipv4Network,
) {
if let Some(instance) = self.instances.get(&vrid) {
if let Some(instance) = self.instances.get_mut(&vrid) {
if let Some(ifindex) = instance.mac_vlan.system.ifindex {
// remove address from the instance's configs
instance.config.virtual_addresses.remove(&addr);

// netlink system call will be initiated to remove the address.
// when response is received, this will also be modified in our
// system's MacVlan
southbound::addr_del(
ifindex,
IpNetwork::V4(addr),
Expand All @@ -205,17 +225,20 @@ impl Interface {
}

pub(crate) fn send_vrrp_advert(&self, vrid: u8) {
if let Some(instance) = self.instances.get(&vrid) {
// check for the exists instance...
if let Some(instance) = self.instances.get(&vrid)

// ...and confirm if the instance's parent Interface has an IP address
&& let Some(addr) = self.system.addresses.first()
{
let mut buf = BytesMut::new();

// ethernet frame
let eth_frame: &[u8] = &instance.advert_ether_frame().encode();
buf.put(eth_frame);

// ip packet
let ip_pkt: &[u8] = &instance
.adver_ipv4_pkt(self.system.addresses.first().unwrap().ip())
.encode();
let ip_pkt: &[u8] = &instance.adver_ipv4_pkt(addr.ip()).encode();
buf.put(ip_pkt);

// vrrp packet
Expand All @@ -226,7 +249,24 @@ impl Interface {
ifname: instance.mac_vlan.name.clone(),
buf: buf.to_vec(),
};
let _ = self.net.net_tx_packetp.send(msg);
if let Some(net) = &instance.mac_vlan.net {
net.net_tx_packetp.send(msg);
}
} else {
error_span!("send-vrrp").in_scope(|| {
tracing::error!(%vrid, "unable to send vrrp advertisement");
});
}
}

// 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) {
let net = MvlanInterfaceNet::new(self, vrid)
.expect("Failed to intialize VRRP tasks");

if let Some(instance) = self.instances.get_mut(&vrid) {
instance.mac_vlan.net = Some(net);
}
}
}
Expand All @@ -246,12 +286,9 @@ impl ProtocolInstance for Interface {
tx: InstanceChannelsTx<Interface>,
) -> Interface {
// TODO: proper error handling
let net = InterfaceNet::new(&name, &tx)
.expect("Failed to initialize VRRP network tasks");
Interface {
name,
system: Default::default(),
net,
instances: Default::default(),
tx,
shared,
Expand Down Expand Up @@ -320,34 +357,22 @@ impl MacVlanInterface {
net: None,
}
}

pub fn create_net(&mut self, tx_channels: &InstanceChannelsTx<Interface>) {
let net = InterfaceNet::new(&self.name, tx_channels)
.expect("Failed to initialize VRRP tasks");
self.net = Some(net);
}
}

// ===== impl InterfaceNet =====

impl InterfaceNet {
fn new(
ifname: &str,
instance_channels_tx: &InstanceChannelsTx<Interface>,
) -> Result<Self, IoError> {
// create socket_vrrp_rx and socket_vrrp_tx
// Currently being created separately so that we can customize
// our vrrp sockets with some virtual fields in the headers
// when sending out the VRRP advertisements
impl MvlanInterfaceNet {
fn new(parent_iface: &Interface, vrid: u8) -> Result<Self, IoError> {
let instance = parent_iface.instances.get(&vrid).unwrap();
let ifname = &instance.mac_vlan.name;
let instance_channels_tx = &parent_iface.tx;

let socket_vrrp_rx = network::socket_vrrp_rx(ifname)
.map_err(IoError::SocketError)
.and_then(|socket| {
AsyncFd::new(socket).map_err(IoError::SocketError)
})
.map(Arc::new)?;

// tx will change the creation to happen in it's own function.
let socket_vrrp_tx = network::socket_vrrp_tx(ifname)
let socket_vrrp_tx = network::socket_vrrp_tx(parent_iface, vrid)
.map_err(IoError::SocketError)
.and_then(|socket| {
AsyncFd::new(socket).map_err(IoError::SocketError)
Expand Down Expand Up @@ -375,7 +400,7 @@ impl InterfaceNet {
&instance_channels_tx.protocol_input.vrrp_net_packet_tx,
);

Ok(InterfaceNet {
Ok(Self {
socket_vrrp: socket_vrrp_rx,
socket_arp,
_net_tx_task: net_tx_task,
Expand Down
40 changes: 34 additions & 6 deletions holo-vrrp/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,50 @@ use libc::{if_nametoindex, AF_PACKET, ETH_P_ARP, ETH_P_IP};
use nix::sys::socket;
use socket2::{Domain, InterfaceIndexOrAddress, Protocol, Type};
use tokio::sync::mpsc::error::SendError;
use tracing::{debug, debug_span};

use crate::error::IoError;
use crate::interface::Interface;
use crate::packet::{ArpPacket, EthernetFrame, Ipv4Packet, VrrpPacket};
use crate::tasks::messages::input::VrrpNetRxPacketMsg;
use crate::tasks::messages::output::NetTxPacketMsg;

pub fn socket_vrrp_tx(ifname: &str) -> Result<Socket, std::io::Error> {
pub fn socket_vrrp_tx(
interface: &Interface,
vrid: u8,
) -> Result<Socket, std::io::Error> {
#[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(112)))
})?;

capabilities::raise(|| sock.bind_device(Some(ifname.as_bytes())))?;
capabilities::raise(|| sock.set_broadcast(true))?;
capabilities::raise(|| sock.set_nonblocking(true))?;
if let Some(addr) = instance.mac_vlan.system.addresses.first() {
capabilities::raise(|| {
match sock.set_multicast_if_v4(&addr.ip()) {
Ok(_res) => {
debug_span!("socket-vrrp").in_scope(|| {
debug!("successfully joined multicast interface");
});
}
Err(err) => {
debug_span!("socket-vrrp").in_scope(|| {
debug!(%addr, %err, "unable to join multicast interface");
});
}
}
});
}

// bind it to the primary interface's name
capabilities::raise(|| {
sock.bind_device(Some(interface.name.as_str().as_bytes()))
})?;
capabilities::raise(|| {
sock.set_reuse_address(true);
});

Ok(sock)
}
Expand Down Expand Up @@ -93,7 +121,7 @@ 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_family: libc::AF_INET as u16,
sll_protocol: (ETH_P_IP as u16).to_be(),
sll_ifindex: ifindex as i32,
sll_hatype: 0,
Expand Down
52 changes: 37 additions & 15 deletions holo-vrrp/src/southbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ pub(crate) fn process_iface_update(

// update names for all macvlans
for (vrid, instance) in iface.instances.iter_mut() {
let name = format!("mvlan-vrrp-{}", vrid);
instance.mac_vlan.name = name;
instance.mac_vlan.name = format!("mvlan-vrrp-{}", vrid);
}
return;
}

// check if it is one of the macvlans being updated.
for (vrid, instance) in iface.instances.iter_mut() {
let mut target_vrid: Option<u8> = None;

//check if it is one of the macvlans being updated.
'outer: for (vrid, instance) in iface.instances.iter_mut() {
let name = format!("mvlan-vrrp-{}", vrid);
let mvlan_iface = &mut instance.mac_vlan;

Expand All @@ -61,47 +62,68 @@ pub(crate) fn process_iface_update(
});

mvlan_iface.system.addresses = ips;
target_vrid = Some(*vrid);

mvlan_iface.create_net(&iface.tx);
return;
break 'outer;
}
}

if let Some(vrid) = target_vrid {
iface.create_mvlan_net(vrid);
}
}

pub(crate) fn process_addr_add(iface: &mut Interface, msg: AddressMsg) {
if msg.ifname != iface.name {
return;
if let IpNetwork::V4(addr) = msg.addr {
iface.system.addresses.insert(addr);
}
}

if let IpNetwork::V4(addr) = msg.addr {
iface.system.addresses.insert(addr);

// TODO: trigger protocol event?
}
// when this is some, it means that we need to rebind our
// transmission socket multicast address to the newly added address
let mut target_vrid: Option<u8> = None;

// if the interface being updated is one of the macvlans
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 {
target_vrid = Some(*vrid);
}
if mvlan_iface.name == name {
if let IpNetwork::V4(addr) = msg.addr {
instance.config.virtual_addresses.insert(addr);
mvlan_iface.system.addresses.insert(addr);
}
}
}

if let Some(vrid) = target_vrid {
iface.create_mvlan_net(vrid);
}
}

pub(crate) fn process_addr_del(iface: &mut Interface, msg: AddressMsg) {
if msg.ifname != iface.name {
return;
}

// remove the address from the addresses of parent interfaces
if let IpNetwork::V4(addr) = msg.addr {
iface.system.addresses.remove(&addr);
}

// TODO: trigger protocol event?
for (vrid, instance) in iface.instances.iter_mut() {
let name = format!("mvlan-vrrp-{}", vrid);
let mvlan_iface = &mut instance.mac_vlan;

// if it is one of the macvlans being edited, we
// remove the macvlan's
if mvlan_iface.name == name {
if let IpNetwork::V4(addr) = msg.addr {
mvlan_iface.system.addresses.remove(&addr);
}
}
}
}

Expand Down
Loading

0 comments on commit 3e5e28b

Please sign in to comment.