Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions p4/sidecar-lite.p4
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,10 @@ control local(
}

action nonlocal() { is_local = false; }
action local() { is_local = true; }
action local(bit<8> rid) {
is_local = true;
ingress.router_id = rid;
}
}

control attached(
Expand Down Expand Up @@ -619,7 +622,10 @@ control router_v4_idx(
Checksum() csum;

table rtr {
key = { dst_addr: lpm; }
key = {
ingress.router_id: exact;
dst_addr: lpm;
}
actions = { drop; index; }
default_action = drop;
}
Expand Down Expand Up @@ -677,7 +683,10 @@ control router_v6_idx(
Checksum() csum;

table rtr {
key = { dst_addr: lpm; }
key = {
ingress.router_id: exact;
dst_addr: lpm;
}
actions = { drop; index; }
default_action = drop;
}
Expand Down
1 change: 1 addition & 0 deletions p4/softnpu.p4
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct ingress_metadata_t {
bit<16> port;
bit<16> nat_id;
bit<16> path_idx;
bit<8> router_id;
bool forward_needed;
bool lldp;
bool allow_source_mcast;
Expand Down
108 changes: 81 additions & 27 deletions scadm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ enum Commands {

/// Next Hop
nexthop: Ipv4Addr,

/// Router id to which it belongs.
#[clap(long, default_value_t = 0)]
rid: u8,
},

/// Remove a route from the routing table.
Expand All @@ -60,6 +64,10 @@ enum Commands {

/// Subnet mask for the destination.
mask: u8,

/// Router id to which it belongs.
#[clap(long, default_value_t = 0)]
rid: u8,
},

/// Add an IPv6 route to the routing table.
Expand All @@ -75,6 +83,10 @@ enum Commands {

/// Next Hop
nexthop: Ipv6Addr,

/// Router id to which it belongs.
#[clap(long, default_value_t = 0)]
rid: u8,
},

/// Remove a route from the routing table.
Expand All @@ -84,12 +96,20 @@ enum Commands {

/// Subnet mask for the destination.
mask: u8,

/// Router id to which it belongs.
#[clap(long, default_value_t = 0)]
rid: u8,
},

/// Add an IPv6 address to the router.
AddAddress6 {
/// Address to add.
address: Ipv6Addr,

/// Router id claiming the address.
#[clap(long, default_value_t = 0)]
rid: u8,
},

/// Remove an IPv6 address from the router.
Expand Down Expand Up @@ -389,30 +409,30 @@ enum RouteType {
}

struct RouteData {
data: BTreeMap<Cidr, u16>,
data: BTreeMap<(u8, Cidr), u16>,
}

impl RouteData {
// Given the full set of tables, extract the data from the route->idx table
fn extract_route_idx(
typ: RouteType,
full: &BTreeMap<String, Vec<TableEntry>>,
) -> BTreeMap<Cidr, u16> {
) -> BTreeMap<(u8, Cidr), u16> {
let mut data = BTreeMap::new();
let table = match typ {
RouteType::V4 => ROUTER_V4_IDX,
RouteType::V6 => ROUTER_V6_IDX,
};
for e in full.get(table).unwrap() {
let subnet = match get_addr_subnet(&e.keyset_data) {
Some((address, mask)) => Cidr { address, mask },
let (rid, subnet) = match get_rid_addr_subnet(&e.keyset_data) {
Some((rid, address, mask)) => (rid, Cidr { address, mask }),
_ => continue,
};
let idx = match get_idx_slots(&e.parameter_data) {
Some((idx, _slots)) => idx,
None => continue,
};
data.insert(subnet, idx);
data.insert((rid, subnet), idx);
}
data
}
Expand Down Expand Up @@ -443,9 +463,9 @@ impl RouteData {
}

// Given a route destination, find the corresponding table index
pub fn lookup(&self, address: IpAddr, mask: u8) -> Option<u16> {
pub fn lookup(&self, rid: u8, address: IpAddr, mask: u8) -> Option<u16> {
let cidr = Cidr { address, mask };
self.data.get(&cidr).copied()
self.data.get(&(rid, cidr)).copied()
}

// Find an open slot in the route table
Expand All @@ -469,6 +489,7 @@ async fn main() {
mask,
port,
nexthop,
rid,
} => {
let table = RouteData::load(RouteType::V4, &cli).await;
let idx = table.find_available();
Expand All @@ -492,8 +513,9 @@ async fn main() {
)
.await;

// Now add cidr->idx to the index table
let mut keyset_data: Vec<u8> = destination.octets().into();
// Now add (rid, cidr)->idx to the index table
let mut keyset_data: Vec<u8> = vec![rid];
keyset_data.extend_from_slice(&destination.octets());
keyset_data.push(mask);

// Hardcoded slot count.
Expand All @@ -511,11 +533,17 @@ async fn main() {
)
.await;
}
Commands::RemoveRoute4 { destination, mask } => {
Commands::RemoveRoute4 {
destination,
mask,
rid,
} => {
let table = RouteData::load(RouteType::V4, &cli).await;
if let Some(idx) = table.lookup(IpAddr::V4(destination), mask) {
// Remove the entry from the subnet->idx table
let mut keyset_data: Vec<u8> = destination.octets().into();
if let Some(idx) = table.lookup(rid, IpAddr::V4(destination), mask)
{
// Remove the entry from the (rid, subnet)->idx table
let mut keyset_data: Vec<u8> = vec![rid];
keyset_data.extend_from_slice(&destination.octets());
keyset_data.push(mask);

send(
Expand Down Expand Up @@ -546,6 +574,7 @@ async fn main() {
mask,
port,
nexthop,
rid,
} => {
let table = RouteData::load(RouteType::V6, &cli).await;
let idx = table.find_available();
Expand All @@ -569,8 +598,9 @@ async fn main() {
)
.await;

// Now add cidr->idx to the index table
let mut keyset_data: Vec<u8> = destination.octets().into();
// Now add (rid, cidr)->idx to the index table
let mut keyset_data: Vec<u8> = vec![rid];
keyset_data.extend_from_slice(&destination.octets());
keyset_data.push(mask);

// Hardcoded slot count.
Expand All @@ -588,11 +618,17 @@ async fn main() {
)
.await;
}
Commands::RemoveRoute6 { destination, mask } => {
Commands::RemoveRoute6 {
destination,
mask,
rid,
} => {
let table = RouteData::load(RouteType::V6, &cli).await;
if let Some(idx) = table.lookup(IpAddr::V6(destination), mask) {
// Remove the entry from the subnet->idx table
let mut keyset_data: Vec<u8> = destination.octets().into();
if let Some(idx) = table.lookup(rid, IpAddr::V6(destination), mask)
{
// Remove the entry from the (rid, subnet)->idx table
let mut keyset_data: Vec<u8> = vec![rid];
keyset_data.extend_from_slice(&destination.octets());
keyset_data.push(mask);

send(
Expand Down Expand Up @@ -733,7 +769,9 @@ async fn main() {
table: LOCAL_V4.into(),
action: "local".into(),
keyset_data,
..Default::default()
// IPv4 addresses are never tunnel endpoints, so they
// always use the default router 0.
parameter_data: vec![0],
}),
&cli,
)
Expand All @@ -752,15 +790,15 @@ async fn main() {
.await;
}

Commands::AddAddress6 { address } => {
Commands::AddAddress6 { address, rid } => {
let mut keyset_data: Vec<u8> = address.octets().into();
keyset_data.reverse();
send(
ManagementRequest::TableAdd(TableAdd {
table: LOCAL_V6.into(),
action: "local".into(),
keyset_data,
..Default::default()
parameter_data: vec![rid],
}),
&cli,
)
Expand Down Expand Up @@ -1410,7 +1448,12 @@ fn dump_tables(table: &BTreeMap<String, Vec<TableEntry>>) {
println!("local v6:");
for e in table.get(LOCAL_V6).unwrap() {
if let Some(a) = get_addr(&e.keyset_data, false) {
println!("{a}")
let rid = e
.parameter_data
.first()
.map(|r| r.to_string())
.unwrap_or_else(|| "?".into());
println!("{a} (rid {rid})")
}
}
println!("local v4:");
Expand All @@ -1422,8 +1465,8 @@ fn dump_tables(table: &BTreeMap<String, Vec<TableEntry>>) {

println!("router v6_idx:");
for e in table.get(ROUTER_V6_IDX).unwrap() {
let tgt = match get_addr_subnet(&e.keyset_data) {
Some((a, m)) => format!("{a}/{m}"),
let tgt = match get_rid_addr_subnet(&e.keyset_data) {
Some((rid, a, m)) => format!("rid {rid}: {a}/{m}"),
None => "?".into(),
};
let idx = match get_idx_slots(&e.parameter_data) {
Expand Down Expand Up @@ -1457,8 +1500,8 @@ fn dump_tables(table: &BTreeMap<String, Vec<TableEntry>>) {

println!("router v4_idx:");
for e in table.get(ROUTER_V4_IDX).unwrap() {
let tgt = match get_addr_subnet(&e.keyset_data) {
Some((a, m)) => format!("{a}/{m}"),
let tgt = match get_rid_addr_subnet(&e.keyset_data) {
Some((rid, a, m)) => format!("rid {rid}: {a}/{m}"),
None => "?".into(),
};
let idx = match get_idx_slots(&e.parameter_data) {
Expand Down Expand Up @@ -1839,6 +1882,17 @@ fn get_addr_subnet(data: &[u8]) -> Option<(IpAddr, u8)> {
}
}

fn get_rid_addr_subnet(data: &[u8]) -> Option<(u8, IpAddr, u8)> {
match data.len() {
6 => Some((data[0], get_addr(&data[1..5], true)?, data[5])),
18 => Some((data[0], get_addr(&data[1..17], true)?, data[17])),
_ => {
println!("expected [rid, address, subnet], found: {data:x?}");
None
}
}
}

fn get_addr_vni_mac(data: &[u8]) -> Option<(IpAddr, u32, [u8; 6])> {
match data.len() {
13 => Some((
Expand Down
Loading