Skip to content

Commit 09fcec0

Browse files
Change API into internal/replication vs external
Inlcudes: - API updates separating internal (underlay) vs external groups with validation and reliance set up accordingly. - Replicate only on encapsulated packets, which simplifies a ton of dataplane work (note: we still decap or not after replication is configured) - Comment cleanup - Additional testing
1 parent e55ddbc commit 09fcec0

File tree

11 files changed

+2612
-1596
lines changed

11 files changed

+2612
-1596
lines changed

dpd-client/tests/integration_tests/mcast.rs

Lines changed: 1605 additions & 814 deletions
Large diffs are not rendered by default.

dpd-client/tests/integration_tests/table_tests.rs

Lines changed: 25 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,9 @@ const IPV4_NAT_TABLE_SIZE: usize = 1024; // nat routing table
4545
const IPV6_NAT_TABLE_SIZE: usize = 1024; // nat routing table
4646
const IPV4_ARP_SIZE: usize = 512; // arp cache
4747
const IPV6_NEIGHBOR_SIZE: usize = 512; // ipv6 neighbor cache
48-
/// Multicast routing tables add two entries for each entry in the
49-
/// replication table, one for each direction (ingress and egress).
50-
///
51-
/// We alternate between IPv4 and IPv6 multicast addresses, so it's
52-
/// 512 entries for each type of address.
53-
const MULTICAST_TABLE_SIZE: usize = 2048;
48+
/// The size of the multicast table related to replication on
49+
/// admin-scoped (internal) multicast groups.
50+
const MULTICAST_TABLE_SIZE: usize = 1024;
5451
const MCAST_TAG: &str = "mcast_table_test"; // multicast group tag
5552

5653
// The result of a table insert or delete API operation.
@@ -76,30 +73,16 @@ fn gen_ipv6_cidr(idx: usize) -> Ipv6Net {
7673
Ipv6Net::new(gen_ipv6_addr(idx), 128).unwrap()
7774
}
7875

79-
/// Generates valid IPv4 multicast addresses that avoid special-purpose ranges
80-
fn gen_ipv4_multicast_addr(idx: usize) -> Ipv4Addr {
81-
// Start with 224.1.0.0 to avoid the 224.0.0.0/24 range
82-
// (which contains link-local multicast)
83-
// 224.0.0.0/24 is reserved for local network control use
84-
let base: u32 = 0xE0010000u32; // hex for 224.1.0.0
85-
86-
// Avoid special-purpose ranges:
87-
// - 232.0.0.0/8 (Source-Specific Multicast)
88-
// - 233.0.0.0/8 (GLOP addressing)
89-
// - 239.0.0.0/8 (Administratively Scoped)
90-
//
91-
// Keep within 224.1.0.0 - 231.255.255.255
92-
let addr: u32 = base + (idx as u32 % 0x00FFFFFF);
93-
94-
// Convert to Ipv4Addr
95-
addr.into()
96-
}
97-
98-
/// Generates valid IPv6 multicast addresses that avoid reserved ranges
76+
// Generates valid IPv6 multicast addresses that are admin-scoped.
9977
fn gen_ipv6_multicast_addr(idx: usize) -> Ipv6Addr {
100-
// Use ff0e::/16 (global scope) to avoid link-local and other reserved scopes
101-
// FF0E is global scope multicast (avoid ff00, ff01, ff02 which are reserved)
102-
Ipv6Addr::new(0xFF0E, 0, 0, 0, 0, 0, 0, (1000 + idx) as u16)
78+
// Use admin-scoped multicast addresses (ff04::/16, ff05::/16, ff08::/16)
79+
// This ensures they will be created as internal groups
80+
let scope = match idx % 3 {
81+
0 => 0xFF04, // admin-scoped
82+
1 => 0xFF05, // admin-scoped
83+
_ => 0xFF08, // admin-scoped
84+
};
85+
Ipv6Addr::new(scope, 0, 0, 0, 0, 0, 0, (1000 + idx) as u16)
10386
}
10487

10588
// For each table we want to test, we define functions to insert, delete, and
@@ -474,8 +457,10 @@ async fn test_routev6_full() -> TestResult {
474457
test_table_capacity::<RouteV6, (), ()>(IPV6_LPM_SIZE).await
475458
}
476459

460+
struct MulticastReplicationTableTest {}
461+
477462
impl TableTest<types::MulticastGroupResponse, ()>
478-
for types::MulticastGroupCreateEntry
463+
for MulticastReplicationTableTest
479464
{
480465
async fn insert_entry(
481466
switch: &Switch,
@@ -484,64 +469,36 @@ impl TableTest<types::MulticastGroupResponse, ()>
484469
let (port_id1, link_id1) = switch.link_id(PhysPort(11)).unwrap();
485470
let (port_id2, link_id2) = switch.link_id(PhysPort(12)).unwrap();
486471

487-
// Alternate between IPv4 and IPv6 based on whether idx is even or odd
488-
let group_ip = if idx % 2 == 0 {
489-
IpAddr::V4(gen_ipv4_multicast_addr(idx))
490-
} else {
491-
IpAddr::V6(gen_ipv6_multicast_addr(idx))
492-
};
472+
// Only IPv6 admin-scoped multicast addresses for replication table testing
473+
let group_ip = IpAddr::V6(gen_ipv6_multicast_addr(idx));
493474

494-
// Create a NAT target
495-
let nat_target = types::NatTarget {
496-
internal_ip: Ipv6Addr::new(0xff05, 0, 0, 0, 0, 0, 0, 1),
497-
inner_mac: MacAddr::new(0xe1, 0xd5, 0x5e, 0x67, 0x89, 0xab).into(),
498-
vni: (100 + idx as u32).into(),
499-
};
500-
501-
// Alternate having a vlan_id based on whether idx is even or odd
502-
let vlan_id = if idx % 2 == 0 {
503-
Some(10 + (idx % 4000) as u16)
504-
} else {
505-
None
506-
};
507-
508-
// Create the multicast group
509-
let group_entry = types::MulticastGroupCreateEntry {
475+
// Admin-scoped IPv6 groups are internal with replication info and members
476+
let internal_entry = types::MulticastGroupCreateEntry {
510477
group_ip,
511478
tag: Some(MCAST_TAG.to_string()),
512-
nat_target: Some(nat_target),
513-
vlan_id,
514479
sources: None,
515480
replication_info: types::MulticastReplicationEntry {
516481
level1_excl_id: Some(10),
517482
level2_excl_id: Some(20),
518483
},
519484
members: vec![
520485
types::MulticastGroupMember {
521-
port_id: port_id1,
486+
port_id: port_id1.clone(),
522487
link_id: link_id1,
523488
direction: types::Direction::External,
524489
},
525490
types::MulticastGroupMember {
526-
port_id: port_id2,
491+
port_id: port_id2.clone(),
527492
link_id: link_id2,
528493
direction: types::Direction::External,
529494
},
530495
],
531496
};
532-
533-
switch.client.multicast_group_create(&group_entry).await
497+
switch.client.multicast_group_create(&internal_entry).await
534498
}
535499

536500
async fn delete_entry(switch: &Switch, idx: usize) -> OpResult<()> {
537-
// Find the IP with the matching index
538-
let ip = if idx % 2 == 0 {
539-
IpAddr::V4(gen_ipv4_multicast_addr(idx))
540-
} else {
541-
IpAddr::V6(gen_ipv6_multicast_addr(idx))
542-
};
543-
544-
// Delete the route entry
501+
let ip = IpAddr::V6(gen_ipv6_multicast_addr(idx));
545502
switch.client.multicast_group_delete(&ip).await
546503
}
547504

@@ -559,9 +516,9 @@ impl TableTest<types::MulticastGroupResponse, ()>
559516

560517
#[tokio::test]
561518
#[ignore]
562-
async fn test_multicast_full() -> TestResult {
519+
async fn test_multicast_replication_table_full() -> TestResult {
563520
test_table_capacity::<
564-
types::MulticastGroupCreateEntry,
521+
MulticastReplicationTableTest,
565522
types::MulticastGroupResponse,
566523
(),
567524
>(MULTICAST_TABLE_SIZE)

dpd/p4/constants.p4

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ const bit<2> MULTICAST_TAG_UNDERLAY = 1;
5353
const bit<2> MULTICAST_TAG_UNDERLAY_EXTERNAL = 2;
5454

5555
/* IPv6 Address Mask Constants */
56-
const bit<128> IPV6_SCOPE_MASK = 0xfff00000000000000000000000000000; // Match ff0X::/16
57-
const bit<128> IPV6_ULA_MASK = 0xff00000000000000000000000000000; // Match fd00::/8
56+
const bit<128> IPV6_SCOPE_MASK = 0xffff0000000000000000000000000000; // Match ff00::/16
57+
const bit<128> IPV6_ULA_MASK = 0xff000000000000000000000000000000; // Match fd00::/8
5858

5959
/* IPv6 Address Pattern Constants */
60-
const bit<128> IPV6_ADMIN_LOCAL_PATTERN = 0xff040000000000000000000000000000 & IPV6_SCOPE_MASK; // ff04::/16
61-
const bit<128> IPV6_SITE_LOCAL_PATTERN = 0xff050000000000000000000000000000 & IPV6_SCOPE_MASK; // ff05::/16
62-
const bit<128> IPV6_ORG_SCOPE_PATTERN = 0xff080000000000000000000000000000 & IPV6_SCOPE_MASK; // ff08::/16
63-
const bit<128> IPV6_ULA_PATTERN = 0xFfd00000000000000000000000000000 & IPV6_ULA_MASK; // fd00::/8
60+
const bit<128> IPV6_ADMIN_LOCAL_PATTERN = 0xff040000000000000000000000000000; // ff04::/16
61+
const bit<128> IPV6_SITE_LOCAL_PATTERN = 0xff050000000000000000000000000000; // ff05::/16
62+
const bit<128> IPV6_ORG_SCOPE_PATTERN = 0xff080000000000000000000000000000; // ff08::/16
63+
const bit<128> IPV6_ULA_PATTERN = 0xfd000000000000000000000000000000; // fd00::/8
6464

6565
/* Reasons a packet may be dropped by the p4 pipeline */
6666
const bit<8> DROP_IPV4_SWITCH_ADDR_MISS = 0x01;

dpd/p4/sidecar.p4

Lines changed: 47 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ control Filter(
232232
// that follow the format 33:33:xxxx:xxxx where the last 32 bits
233233
// are taken directly from the last 32 bits of the IPv6 address.
234234
//
235-
// Sadly, the first two conditions cannot e checked properly by
235+
// Sadly, the first two conditions cannot be checked properly by
236236
// the parser, as we reach the total available parser match
237237
// registers on the device.
238238
if (hdr.ethernet.dst_mac[47:40] != 8w0x33 ||
@@ -689,7 +689,7 @@ control NatIngress (
689689
if (hdr.ipv4.isValid() && meta.is_valid) {
690690
if (meta.is_mcast) {
691691
ingress_ipv4_mcast.apply();
692-
} else {
692+
} else {
693693
ingress_ipv4.apply();
694694
}
695695
} else if (hdr.ipv6.isValid() && meta.is_valid) {
@@ -1468,27 +1468,36 @@ control MulticastIngress (
14681468
in ingress_intrinsic_metadata_t ig_intr_md,
14691469
inout ingress_intrinsic_metadata_for_tm_t ig_tm_md)
14701470
{
1471-
DirectCounter<bit<32>>(CounterType_t.PACKETS_AND_BYTES) mcast_ipv4_ctr;
14721471
DirectCounter<bit<32>>(CounterType_t.PACKETS_AND_BYTES) mcast_ipv6_ctr;
14731472
DirectCounter<bit<32>>(CounterType_t.PACKETS_AND_BYTES) mcast_ipv4_ssm_ctr;
14741473
DirectCounter<bit<32>>(CounterType_t.PACKETS_AND_BYTES) mcast_ipv6_ssm_ctr;
14751474

1476-
Hash<bit<13>>(HashAlgorithm_t.CRC16) mcast_hashv4_level1;
1477-
Hash<bit<13>>(HashAlgorithm_t.CRC16) mcast_hashv4_level2;
14781475
Hash<bit<13>>(HashAlgorithm_t.CRC16) mcast_hashv6_level1;
14791476
Hash<bit<13>>(HashAlgorithm_t.CRC16) mcast_hashv6_level2;
14801477

14811478
// Drop action for IPv4 multicast packets with no group.
1479+
//
1480+
// At this point, We should only allow replication for IPv6 packets that
1481+
// are admin-scoped before possible decapping.
14821482
action drop_mcastv4_no_group() {
14831483
ig_dprsr_md.drop_ctl = 1;
14841484
meta.drop_reason = DROP_MULTICAST_NO_GROUP;
1485-
mcast_ipv4_ctr.count();
14861485
}
14871486

14881487
// Drop action for IPv6 multicast packets with no group.
1488+
//
1489+
// At this point, we should only allow replication for IPv6 packets that
1490+
// are admin-scoped before possible decapping.
14891491
action drop_mcastv6_no_group() {
14901492
ig_dprsr_md.drop_ctl = 1;
14911493
meta.drop_reason = DROP_MULTICAST_NO_GROUP;
1494+
}
1495+
1496+
// Drop action for IPv6 multicast packets with no group
1497+
// that is a valid admin-scoped multicast group.
1498+
action drop_mcastv6_admin_scoped_no_group() {
1499+
ig_dprsr_md.drop_ctl = 1;
1500+
meta.drop_reason = DROP_MULTICAST_NO_GROUP;
14921501
mcast_ipv6_ctr.count();
14931502
}
14941503

@@ -1520,35 +1529,9 @@ control MulticastIngress (
15201529
mcast_ipv6_ssm_ctr.count();
15211530
}
15221531

1523-
action configure_mcastv4(
1524-
MulticastGroupId_t mcast_grp_a,
1525-
bit<16> rid,
1526-
bit<16> level1_excl_id,
1527-
bit<9> level2_excl_id
1528-
) {
1529-
ig_tm_md.mcast_grp_a = mcast_grp_a;
1530-
ig_tm_md.rid = rid;
1531-
ig_tm_md.level1_exclusion_id = level1_excl_id;
1532-
ig_tm_md.level2_exclusion_id = level2_excl_id;
1533-
1534-
// Set multicast hash based on IPv4 packet fields
1535-
ig_tm_md.level1_mcast_hash = (bit<13>)mcast_hashv4_level1.get({
1536-
hdr.ipv4.src_addr,
1537-
hdr.ipv4.dst_addr,
1538-
hdr.ipv4.protocol,
1539-
meta.l4_src_port,
1540-
meta.l4_dst_port
1541-
});
1542-
1543-
// Set secondary multicast hash based on IPv4 packet fields
1544-
ig_tm_md.level2_mcast_hash = (bit<13>)mcast_hashv4_level2.get({
1545-
(bit<16>)hdr.ipv4.identification,
1546-
ig_intr_md.ingress_port
1547-
});
1548-
1549-
mcast_ipv4_ctr.count();
1550-
}
1551-
1532+
// Configure IPv6 multicast replication with bifurcated design:
1533+
// mcast_grp_a: external/customer replication group
1534+
// mcast_grp_b: underlay/infrastructure replication group
15521535
action configure_mcastv6(
15531536
MulticastGroupId_t mcast_grp_a,
15541537
MulticastGroupId_t mcast_grp_b,
@@ -1580,21 +1563,10 @@ control MulticastIngress (
15801563
mcast_ipv6_ctr.count();
15811564
}
15821565

1583-
table mcast_replication_ipv4 {
1584-
key = { hdr.ipv4.dst_addr: exact; }
1585-
actions = {
1586-
configure_mcastv4;
1587-
drop_mcastv4_no_group;
1588-
}
1589-
default_action = drop_mcastv4_no_group;
1590-
const size = IPV4_MULTICAST_TABLE_SIZE;
1591-
counters = mcast_ipv4_ctr;
1592-
}
1593-
15941566
table mcast_source_filter_ipv4 {
15951567
key = {
1596-
hdr.ipv4.src_addr: lpm;
1597-
hdr.ipv4.dst_addr: exact;
1568+
hdr.inner_ipv4.src_addr: lpm;
1569+
hdr.inner_ipv4.dst_addr: exact;
15981570
}
15991571
actions = {
16001572
allow_source_mcastv4;
@@ -1609,17 +1581,17 @@ control MulticastIngress (
16091581
key = { hdr.ipv6.dst_addr: exact; }
16101582
actions = {
16111583
configure_mcastv6;
1612-
drop_mcastv6_no_group;
1584+
drop_mcastv6_admin_scoped_no_group;
16131585
}
1614-
default_action = drop_mcastv6_no_group;
1586+
default_action = drop_mcastv6_admin_scoped_no_group;
16151587
const size = IPV6_MULTICAST_TABLE_SIZE;
16161588
counters = mcast_ipv6_ctr;
16171589
}
16181590

16191591
table mcast_source_filter_ipv6 {
16201592
key = {
1621-
hdr.ipv6.src_addr: exact;
1622-
hdr.ipv6.dst_addr: exact;
1593+
hdr.inner_ipv6.src_addr: exact;
1594+
hdr.inner_ipv6.dst_addr: exact;
16231595
}
16241596
actions = {
16251597
allow_source_mcastv6;
@@ -1650,7 +1622,6 @@ control MulticastIngress (
16501622

16511623
table mcast_tag_check {
16521624
key = {
1653-
hdr.ipv6.isValid() : ternary;
16541625
ig_tm_md.mcast_grp_a : ternary;
16551626
ig_tm_md.mcast_grp_b : ternary;
16561627
hdr.geneve.isValid() : ternary;
@@ -1666,50 +1637,46 @@ control MulticastIngress (
16661637
}
16671638

16681639
const entries = {
1669-
( true, _, _, true, true, MULTICAST_TAG_EXTERNAL ) : invalidate_underlay_grp_and_set_decap;
1670-
( true, _, _, true, true, MULTICAST_TAG_UNDERLAY ) : invalidate_external_grp;
1671-
( true, _, _, true, true, MULTICAST_TAG_UNDERLAY_EXTERNAL ) : NoAction;
1672-
( _, 0, _, _, _, _ ) : invalidate_external_grp;
1673-
( _, _, 0, _, _, _ ) : invalidate_underlay_grp;
1674-
( _, 0, 0, _, _, _ ) : invalidate_grps;
1640+
( _, _, true, true, MULTICAST_TAG_EXTERNAL ) : invalidate_underlay_grp_and_set_decap;
1641+
( _, _, true, true, MULTICAST_TAG_UNDERLAY ) : invalidate_external_grp;
1642+
( _, _, true, true, MULTICAST_TAG_UNDERLAY_EXTERNAL ) : NoAction;
1643+
( 0, _, _, _, _ ) : invalidate_external_grp;
1644+
( _, 0, _, _, _ ) : invalidate_underlay_grp;
1645+
( 0, 0, _, _, _ ) : invalidate_grps;
16751646
}
16761647

16771648
const size = 6;
16781649
}
16791650

16801651
// Note: SSM tables currently take one extra stage in the pipeline (17->18).
16811652
apply {
1682-
if (hdr.ipv4.isValid()) {
1683-
// Check if the destination address is an IPv4 SSM multicast
1653+
if (hdr.geneve.isValid() && hdr.inner_ipv4.isValid()) {
1654+
// Check if the inner destination address is an IPv4 SSM multicast
16841655
// address.
1685-
if (hdr.ipv4.dst_addr[31:24] == 8w0xe8) {
1656+
if (hdr.inner_ipv4.dst_addr[31:24] == 8w0xe8) {
16861657
mcast_source_filter_ipv4.apply();
1687-
if (meta.allow_source_mcast) {
1688-
mcast_replication_ipv4.apply();
1689-
}
16901658
} else {
1691-
// Otherwise, apply the multicast replication table for
1692-
// non-SSM multicast addresses.
1693-
mcast_replication_ipv4.apply();
1659+
meta.allow_source_mcast = true;
16941660
}
1695-
} else if (hdr.ipv6.isValid()) {
1696-
// Check if the destination address is an IPv6 SSM multicast
1661+
} else if (hdr.geneve.isValid() && hdr.inner_ipv6.isValid()) {
1662+
// Check if the inner destination address is an IPv6 SSM multicast
16971663
// address.
1698-
if ((hdr.ipv6.dst_addr[127:120] == 8w0xff)
1699-
&& ((hdr.ipv6.dst_addr[119:116] == 4w0x3))) {
1664+
if ((hdr.inner_ipv6.dst_addr[127:120] == 8w0xff)
1665+
&& ((hdr.inner_ipv6.dst_addr[119:116] == 4w0x3))) {
17001666
mcast_source_filter_ipv6.apply();
1701-
if (meta.allow_source_mcast) {
1702-
// Then, apply the multicast replication table.
1703-
mcast_replication_ipv6.apply();
1704-
}
17051667
} else {
1706-
// Otherwise, apply the multicast replication table for
1707-
// non-SSM multicast addresses.
1708-
mcast_replication_ipv6.apply();
1668+
meta.allow_source_mcast = true;
17091669
}
1670+
} else if (hdr.ipv4.isValid()) {
1671+
drop_mcastv4_no_group();
1672+
} else if (hdr.ipv6.isValid()) {
1673+
drop_mcastv6_no_group();
17101674
}
17111675

1712-
mcast_tag_check.apply();
1676+
if (hdr.ipv6.isValid() && meta.allow_source_mcast) {
1677+
mcast_replication_ipv6.apply();
1678+
mcast_tag_check.apply();
1679+
}
17131680
}
17141681
}
17151682

0 commit comments

Comments
 (0)