Skip to content

Commit 82abfd4

Browse files
authored
ipv6 odds and ends (#175)
1 parent c257185 commit 82abfd4

File tree

10 files changed

+9863
-58
lines changed

10 files changed

+9863
-58
lines changed

.helix/languages.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[[language]]
2+
name = "rust"
3+
[language-server.rust-analyzer.config.cargo]
4+
# default to tofino_sde, can change this to any other feature such as softnpu
5+
# as needed during development but in source control this should probably
6+
# remain as tofino_sde
7+
features = [ "tofino_sde" ]

asic/src/softnpu/table.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,8 @@ impl TableOps<Handle> for Table {
381381
}
382382
("rewrite", params)
383383
}
384-
(NAT_INGRESS4, "forward_ipv4_to") => {
384+
(NAT_INGRESS4, "forward_ipv4_to")
385+
| (NAT_INGRESS6, "forward_ipv6_to") => {
385386
let mut target = Vec::new();
386387
let mut vni = Vec::new();
387388
let mut mac = Vec::new();
@@ -587,6 +588,13 @@ fn keyset_data(match_data: Vec<MatchEntryField>, table: &str) -> Vec<u8> {
587588
serialize_value_type(&x, &mut data);
588589
keyset_data.extend_from_slice(&data[..4]);
589590
}
591+
NAT_V6 => {
592+
// "dst_addr" => hdr.ipv6.dst: exact => bit<128>
593+
let mut buf = Vec::new();
594+
serialize_value_type(&x, &mut buf);
595+
buf.reverse();
596+
keyset_data.extend_from_slice(&buf);
597+
}
590598
LOCAL_V6 => {
591599
let mut buf = Vec::new();
592600
serialize_value_type(&x, &mut buf);
@@ -619,7 +627,7 @@ fn keyset_data(match_data: Vec<MatchEntryField>, table: &str) -> Vec<u8> {
619627
// Ranges (i.e. port ranges)
620628
MatchEntryValue::Range(x) => {
621629
match table {
622-
NAT_V4 => {
630+
NAT_V4 | NAT_V6 => {
623631
// "l4_dst_port" => ingress.nat_id: range => bit<16>
624632
let low = &x.low.to_le_bytes();
625633
let high = &x.high.to_le_bytes();

common/src/illumos.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,16 @@ pub async fn address_add(
145145
addr: impl Into<oxnet::IpNet>,
146146
) -> Result<()> {
147147
let addr_obj = format!("{iface}/{tag}");
148-
let addr = addr.into().to_string();
149148

149+
let addr: oxnet::IpNet = addr.into();
150+
if addr.is_ipv6() {
151+
let tag = "ll";
152+
if !address_exists(iface, tag).await? {
153+
linklocal_add(iface, tag).await?;
154+
}
155+
}
156+
157+
let addr = addr.to_string();
150158
ipadm_quiet(&["create-addr", "-t", "-T", "static", "-a", &addr, &addr_obj])
151159
.await
152160
}

dpd-api/src/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ api_versions!([
5656
// | example for the next person.
5757
// v
5858
// (next_int, IDENT),
59+
(2, DUAL_STACK_NAT_WORKFLOW),
5960
(1, INITIAL),
6061
]);
6162

@@ -1331,23 +1332,49 @@ pub trait DpdApi {
13311332
*/
13321333
#[endpoint {
13331334
method = GET,
1334-
path = "/rpw/nat/ipv4/gen"
1335+
path = "/rpw/nat/ipv4/gen",
1336+
versions = ..VERSION_DUAL_STACK_NAT_WORKFLOW
13351337
}]
13361338
async fn ipv4_nat_generation(
13371339
rqctx: RequestContext<Self::Context>,
13381340
) -> Result<HttpResponseOk<i64>, HttpError>;
13391341

1342+
/**
1343+
* Get NAT generation number
1344+
*/
1345+
#[endpoint {
1346+
method = GET,
1347+
path = "/rpw/nat/gen",
1348+
versions = VERSION_DUAL_STACK_NAT_WORKFLOW..
1349+
}]
1350+
async fn nat_generation(
1351+
rqctx: RequestContext<Self::Context>,
1352+
) -> Result<HttpResponseOk<i64>, HttpError>;
1353+
13401354
/**
13411355
* Trigger NATv4 Reconciliation
13421356
*/
13431357
#[endpoint {
13441358
method = POST,
1345-
path = "/rpw/nat/ipv4/trigger"
1359+
path = "/rpw/nat/ipv4/trigger",
1360+
versions = ..VERSION_DUAL_STACK_NAT_WORKFLOW
13461361
}]
13471362
async fn ipv4_nat_trigger_update(
13481363
rqctx: RequestContext<Self::Context>,
13491364
) -> Result<HttpResponseOk<()>, HttpError>;
13501365

1366+
/**
1367+
* Trigger NAT Reconciliation
1368+
*/
1369+
#[endpoint {
1370+
method = POST,
1371+
path = "/rpw/nat/trigger",
1372+
versions = VERSION_DUAL_STACK_NAT_WORKFLOW..
1373+
}]
1374+
async fn nat_trigger_update(
1375+
rqctx: RequestContext<Self::Context>,
1376+
) -> Result<HttpResponseOk<()>, HttpError>;
1377+
13511378
/**
13521379
* Get the list of P4 tables
13531380
*/

dpd/src/api_server.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,18 +1754,30 @@ impl DpdApi for DpdApiImpl {
17541754

17551755
async fn ipv4_nat_generation(
17561756
rqctx: RequestContext<Arc<Switch>>,
1757+
) -> Result<HttpResponseOk<i64>, HttpError> {
1758+
Self::nat_generation(rqctx).await
1759+
}
1760+
1761+
async fn nat_generation(
1762+
rqctx: RequestContext<Arc<Switch>>,
17571763
) -> Result<HttpResponseOk<i64>, HttpError> {
17581764
let switch = rqctx.context();
17591765

1760-
Ok(HttpResponseOk(nat::get_ipv4_nat_generation(switch)))
1766+
Ok(HttpResponseOk(nat::get_nat_generation(switch)))
17611767
}
17621768

17631769
async fn ipv4_nat_trigger_update(
17641770
rqctx: RequestContext<Arc<Switch>>,
1771+
) -> Result<HttpResponseOk<()>, HttpError> {
1772+
Self::nat_trigger_update(rqctx).await
1773+
}
1774+
1775+
async fn nat_trigger_update(
1776+
rqctx: RequestContext<Arc<Switch>>,
17651777
) -> Result<HttpResponseOk<()>, HttpError> {
17661778
let switch = rqctx.context();
17671779

1768-
match switch.workflow_server.trigger(Task::Ipv4Nat) {
1780+
match switch.workflow_server.trigger(Task::Nat) {
17691781
Ok(_) => Ok(HttpResponseOk(())),
17701782
Err(e) => {
17711783
error!(rqctx.log, "unable to trigger rpw"; "error" => ?e);

dpd/src/nat.rs

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use slog::{debug, error, trace};
88
use std::collections::BTreeMap;
99
use std::fmt;
10-
use std::net::{Ipv4Addr, Ipv6Addr};
10+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1111
use std::ops::Bound;
1212

1313
use crate::Switch;
@@ -413,6 +413,19 @@ pub fn get_ipv4_mapping(
413413
Err(DpdError::Missing("no mapping".into()))
414414
}
415415

416+
pub fn set_mapping(
417+
switch: &Switch,
418+
nat_ip: IpAddr,
419+
low: u16,
420+
high: u16,
421+
tgt: NatTarget,
422+
) -> DpdResult<()> {
423+
match nat_ip {
424+
IpAddr::V4(nat_ip) => set_ipv4_mapping(switch, nat_ip, low, high, tgt),
425+
IpAddr::V6(nat_ip) => set_ipv6_mapping(switch, nat_ip, low, high, tgt),
426+
}
427+
}
428+
416429
pub fn set_ipv4_mapping(
417430
switch: &Switch,
418431
nat_ip: Ipv4Addr,
@@ -465,6 +478,18 @@ pub fn set_ipv4_mapping(
465478
}
466479
}
467480

481+
pub fn clear_mapping(
482+
switch: &Switch,
483+
nat_ip: IpAddr,
484+
low: u16,
485+
high: u16,
486+
) -> DpdResult<()> {
487+
match nat_ip {
488+
IpAddr::V4(nat_ip) => clear_ipv4_mapping(switch, nat_ip, low, high),
489+
IpAddr::V6(nat_ip) => clear_ipv6_mapping(switch, nat_ip, low, high),
490+
}
491+
}
492+
468493
/// Find the first `NatTarget` where its `Ipv4NatEntry` matches the provided
469494
/// `Ipv4Addr` and overlaps with the provided port range, then remove it.
470495
pub fn clear_ipv4_mapping(
@@ -502,9 +527,25 @@ pub fn clear_ipv4_mapping(
502527
Ok(())
503528
}
504529

530+
pub fn clear_overlapping_mappings(
531+
switch: &Switch,
532+
nat_ip: IpAddr,
533+
low: u16,
534+
high: u16,
535+
) -> DpdResult<()> {
536+
match nat_ip {
537+
IpAddr::V4(nat_ip) => {
538+
clear_overlapping_mappings_v4(switch, nat_ip, low, high)
539+
}
540+
IpAddr::V6(nat_ip) => {
541+
clear_overlapping_mappings_v6(switch, nat_ip, low, high)
542+
}
543+
}
544+
}
545+
505546
/// Deletes any `Ipv4NatEntry` where each entry matches the provided
506547
/// `Ipv4Addr` and overlaps with the provided port range
507-
pub fn clear_overlapping_ipv4_mappings(
548+
pub fn clear_overlapping_mappings_v4(
508549
switch: &Switch,
509550
nat_ip: Ipv4Addr,
510551
low: u16,
@@ -542,6 +583,44 @@ pub fn clear_overlapping_ipv4_mappings(
542583
Ok(())
543584
}
544585

586+
pub fn clear_overlapping_mappings_v6(
587+
switch: &Switch,
588+
nat_ip: Ipv6Addr,
589+
low: u16,
590+
high: u16,
591+
) -> DpdResult<()> {
592+
let mut nat = switch.nat.lock().unwrap();
593+
trace!(
594+
switch.log,
595+
"clearing all nat entries overlapping with {}/{}-{}", nat_ip, low, high
596+
);
597+
598+
if let Some(mappings) = nat.ipv6_mappings.get_mut(&nat_ip) {
599+
let mut mappings_to_delete = find_mappings(mappings, low, high);
600+
// delete starting with the last index first, or you'll end up shifting the
601+
// collection underneath you
602+
mappings_to_delete.reverse();
603+
for idx in mappings_to_delete {
604+
let ent = mappings.remove(idx);
605+
let full = ipv6_entry(nat_ip, &ent);
606+
match nat::delete_ipv6_entry(switch, nat_ip, ent.low, ent.high) {
607+
Err(e) => {
608+
error!(switch.log, "failed to clear {}: {:?}", full, e);
609+
return Err(e);
610+
}
611+
_ => {
612+
debug!(switch.log, "cleared nat entry {}", full);
613+
}
614+
};
615+
}
616+
if mappings.is_empty() {
617+
nat.ipv6_mappings.remove(&nat_ip);
618+
}
619+
}
620+
621+
Ok(())
622+
}
623+
545624
pub fn reset_ipv6(switch: &Switch) -> DpdResult<()> {
546625
let mut nat = switch.nat.lock().unwrap();
547626

@@ -568,14 +647,14 @@ pub fn reset_ipv4(switch: &Switch) -> DpdResult<()> {
568647
}
569648
}
570649

571-
pub fn set_ipv4_nat_generation(switch: &Switch, generation: i64) {
650+
pub fn set_nat_generation(switch: &Switch, generation: i64) {
572651
let mut nat = switch.nat.lock().unwrap();
573652

574653
debug!(switch.log, "setting nat generation");
575654
nat.ipv4_generation = generation;
576655
}
577656

578-
pub fn get_ipv4_nat_generation(switch: &Switch) -> i64 {
657+
pub fn get_nat_generation(switch: &Switch) -> i64 {
579658
let nat = switch.nat.lock().unwrap();
580659

581660
debug!(switch.log, "fetching nat generation");

0 commit comments

Comments
 (0)