Skip to content

Commit 8a19e7e

Browse files
committed
Review feedback
- Better router error message - Better error messages when parsing IpAddr / IpCidr - Better comments throughout, some better type names - DCE - Fix ARP handling to unconditionally drop outbound requests for anything other than the gateway, and all inbound requests.
1 parent 45dffd3 commit 8a19e7e

File tree

12 files changed

+262
-274
lines changed

12 files changed

+262
-274
lines changed

opte-api/src/cmd.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Copyright 2022 Oxide Computer Company
66

77
use super::encap::Vni;
8+
use super::ip::IpCidr;
89
use super::mac::MacAddr;
910
use super::API_VERSION;
1011
use illumos_sys_hdrs::{c_int, size_t};
@@ -146,7 +147,7 @@ pub enum OpteError {
146147
DeserCmdErr(String),
147148
DeserCmdReq(String),
148149
FlowExists(String),
149-
InvalidRouteDest(String),
150+
InvalidRouterEntry { dest: IpCidr, target: String },
150151
LayerNotFound(String),
151152
MacExists { port: String, vni: Vni, mac: MacAddr },
152153
MaxCapacity(u64),
@@ -181,7 +182,7 @@ impl OpteError {
181182
Self::DeserCmdErr(_) => ENOMSG,
182183
Self::DeserCmdReq(_) => ENOMSG,
183184
Self::FlowExists(_) => EEXIST,
184-
Self::InvalidRouteDest(_) => EINVAL,
185+
Self::InvalidRouterEntry { .. } => EINVAL,
185186
Self::LayerNotFound(_) => ENOENT,
186187
Self::MacExists { .. } => EEXIST,
187188
Self::MaxCapacity(_) => ENFILE,

opte-api/src/ip.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,9 @@ impl FromStr for IpAddr {
338338
if let Ok(ipv4) = val.parse::<Ipv4Addr>() {
339339
Ok(ipv4.into())
340340
} else {
341-
val.parse::<Ipv6Addr>().map(IpAddr::Ip6)
341+
val.parse::<Ipv6Addr>()
342+
.map(IpAddr::Ip6)
343+
.map_err(|_| String::from("Invalid IP address"))
342344
}
343345
}
344346
}
@@ -634,7 +636,10 @@ impl FromStr for IpCidr {
634636
fn from_str(val: &str) -> result::Result<Self, Self::Err> {
635637
match val.parse::<Ipv4Cidr>() {
636638
Ok(ip4) => Ok(IpCidr::Ip4(ip4)),
637-
Err(_) => val.parse::<Ipv6Cidr>().map(IpCidr::Ip6),
639+
Err(_) => val
640+
.parse::<Ipv6Cidr>()
641+
.map(IpCidr::Ip6)
642+
.map_err(|_| String::from("Invalid IP CIDR")),
638643
}
639644
}
640645
}
@@ -784,7 +789,9 @@ impl FromStr for Ipv6Cidr {
784789

785790
let ip = match ip_s.parse::<smoltcp::wire::Ipv6Address>() {
786791
Ok(v) => v.into(),
787-
Err(_) => return Err(String::from("Bad IP address component")),
792+
Err(_) => {
793+
return Err(format!("Bad IP address component: '{}'", ip_s))
794+
}
788795
};
789796

790797
let prefix_len = match prefix_s.parse::<u8>() {

opte-ioctl/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
// Copyright 2022 Oxide Computer Company
66

7-
// Copyright 2022 Oxide Computer Company
87
pub use opte::api::OpteError;
98
use opte::api::{
109
CmdOk, NoResp, OpteCmd, OpteCmdIoctl, SetXdeUnderlayReq, API_VERSION,

opte/src/engine/ip6.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ impl Ipv6Hdr {
262262
self.payload_len = len - self.hdr_len() as u16;
263263
}
264264

265-
/// Return the total length of the packet, including extension headers and
266-
/// payload.
265+
/// Return the total length of the packet, including the base header, any
266+
/// extension headers, and the payload itself.
267267
pub fn total_len(&self) -> u16 {
268268
self.payload_len + IPV6_HDR_SZ as u16
269269
}

opte/src/engine/packet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ impl Packet<Initialized> {
881881
match proto {
882882
Protocol::TCP => Self::parse_hg_tcp(rdr, hg, offsets)?,
883883
Protocol::UDP => Self::parse_hg_udp(rdr, hg, offsets)?,
884-
// See comment above about treating ICMPv6 as a header.
884+
// See comment above about treating ICMP as a header.
885885
Protocol::ICMPv6 => (),
886886
_ => return Err(ParseError::UnsupportedProtocol(proto)),
887887
}

opte/src/engine/rule.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub trait MatchExact<M: MatchExactVal + Eq + PartialEq> {
6666
fn match_exact(&self, val: &M) -> bool;
6767
}
6868

69-
/// A marker trait for types that can be match up to a prefix.
69+
/// A marker trait for types that can be match by prefix.
7070
pub trait MatchPrefixVal {}
7171

7272
/// A trait describing how to match data by prefix.

opte/src/engine/snat.rs

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ pub struct NatPoolEntry {
4242
port: u16,
4343
}
4444

45-
// An entry in the internal free list of `NatPool`
45+
// A public IP and port range for NAT. Includes the list of all possible ports
46+
// and those that are free.
4647
#[derive(Debug, Clone)]
47-
struct FreeListEntry {
48+
struct PortList {
4849
// The public IP address to which a private IP is mapped
4950
ip: IpAddr,
5051
// The list of all possible ports available in the NAT pool
@@ -59,7 +60,7 @@ impl ResourceEntry for NatPoolEntry {}
5960
/// NAT-ing connections.
6061
pub struct NatPool {
6162
// Map private IP to public IP + free list of ports
62-
free_list: KMutex<BTreeMap<IpAddr, FreeListEntry>>,
63+
free_list: KMutex<BTreeMap<IpAddr, PortList>>,
6364
}
6465

6566
mod private {
@@ -84,14 +85,14 @@ impl NatPool {
8485
) {
8586
let free_ports = pub_ports.clone().collect();
8687
let entry =
87-
FreeListEntry { ip: pub_ip.into(), ports: pub_ports, free_ports };
88+
PortList { ip: pub_ip.into(), ports: pub_ports, free_ports };
8889
self.free_list.lock().insert(priv_ip.into(), entry);
8990
}
9091

9192
/// Return the number of available ports for a given private IP address.
9293
pub fn num_avail(&self, priv_ip: IpAddr) -> Result<usize, ResourceError> {
9394
match self.free_list.lock().get(&priv_ip) {
94-
Some(FreeListEntry { free_ports, .. }) => Ok(free_ports.len()),
95+
Some(PortList { free_ports, .. }) => Ok(free_ports.len()),
9596
_ => Err(ResourceError::NoMatch(priv_ip.to_string())),
9697
}
9798
}
@@ -104,7 +105,7 @@ impl NatPool {
104105
self.free_list
105106
.lock()
106107
.get(&priv_ip)
107-
.map(|FreeListEntry { ip, ports, .. }| (ip.clone(), ports.clone()))
108+
.map(|PortList { ip, ports, .. }| (ip.clone(), ports.clone()))
108109
}
109110

110111
/// Create a new NAT pool, with no entries.
@@ -121,7 +122,7 @@ impl NatPool {
121122
pub_port: u16,
122123
) -> bool {
123124
match self.free_list.lock().get(&priv_ip.into()) {
124-
Some(FreeListEntry { ip, free_ports, .. }) => {
125+
Some(PortList { ip, free_ports, .. }) => {
125126
if pub_ip.into() != *ip {
126127
return false;
127128
}
@@ -140,7 +141,7 @@ impl FiniteResource for NatPool {
140141

141142
fn obtain(&self, priv_ip: &IpAddr) -> Result<Self::Entry, ResourceError> {
142143
match self.free_list.lock().get_mut(&priv_ip) {
143-
Some(FreeListEntry { ip, free_ports, .. }) => {
144+
Some(PortList { ip, free_ports, .. }) => {
144145
if let Some(port) = free_ports.pop() {
145146
Ok(Self::Entry { ip: *ip, port })
146147
} else {
@@ -154,7 +155,7 @@ impl FiniteResource for NatPool {
154155

155156
fn release(&self, priv_ip: &IpAddr, entry: Self::Entry) {
156157
match self.free_list.lock().get_mut(&priv_ip) {
157-
Some(FreeListEntry { free_ports, .. }) => {
158+
Some(PortList { free_ports, .. }) => {
158159
free_ports.push(entry.port);
159160
}
160161

@@ -211,16 +212,14 @@ impl StatefulAction for SNat {
211212

212213
Ok(AllowOrDeny::Allow(Arc::new(desc)))
213214
}
214-
215-
// XXX This still needs improving.
216215
Err(ResourceError::Exhausted) => {
217216
Err(rule::GenDescError::ResourceExhausted {
218217
name: "SNAT Pool (exhausted)".to_string(),
219218
})
220219
}
221220
Err(ResourceError::NoMatch(ip)) => {
222-
Err(rule::GenDescError::ResourceExhausted {
223-
name: format!("SNAT pool (no match: {})", ip),
221+
Err(rule::GenDescError::Unexpected {
222+
msg: format!("SNAT pool (no match: {})", ip),
224223
})
225224
}
226225
}
@@ -341,16 +340,16 @@ mod test {
341340
let priv_ipv4: Ipv4Addr = "10.0.0.220".parse().unwrap();
342341
let priv_ip = IpAddr::from(priv_ipv4);
343342
let priv_port = "4999".parse().unwrap();
344-
let pub_ipv4: Ipv4Addr = "52.10.128.69".parse().unwrap();
343+
let pub_ip: Ipv4Addr = "52.10.128.69".parse().unwrap();
345344
let pub_port = "8765".parse().unwrap();
346-
let outside_ipv4: Ipv4Addr = "76.76.21.21".parse().unwrap();
345+
let outside_ip: Ipv4Addr = "76.76.21.21".parse().unwrap();
347346
let outside_port = 80;
348347

349348
let pool = Arc::new(NatPool::new());
350-
pool.add(priv_ipv4, pub_ipv4, 8765..=8765);
349+
pool.add(priv_ipv4, pub_ip, 8765..=8765);
351350
let snat = SNat::new(priv_ip, pool.clone());
352351
let mut action_meta = ActionMeta::new();
353-
assert!(pool.verify_available(priv_ipv4, pub_ipv4, pub_port));
352+
assert!(pool.verify_available(priv_ipv4, pub_ip, pub_port));
354353

355354
// ================================================================
356355
// Build the packet metadata
@@ -362,7 +361,7 @@ mod test {
362361
};
363362
let ip = IpMeta::from(Ipv4Meta {
364363
src: priv_ipv4,
365-
dst: outside_ipv4,
364+
dst: outside_ip,
366365
proto: Protocol::TCP,
367366
});
368367
let ulp = UlpMeta::from(TcpMeta {
@@ -391,7 +390,7 @@ mod test {
391390
Ok(AllowOrDeny::Allow(desc)) => desc,
392391
_ => panic!("expected AllowOrDeny::Allow(desc) result"),
393392
};
394-
assert!(!pool.verify_available(priv_ipv4, pub_ipv4, pub_port));
393+
assert!(!pool.verify_available(priv_ipv4, pub_ip, pub_port));
395394

396395
// ================================================================
397396
// Verify outbound header transformation
@@ -408,8 +407,8 @@ mod test {
408407
_ => panic!("expect Ipv4Meta"),
409408
};
410409

411-
assert_eq!(ip4_meta.src, pub_ipv4);
412-
assert_eq!(ip4_meta.dst, outside_ipv4);
410+
assert_eq!(ip4_meta.src, pub_ip);
411+
assert_eq!(ip4_meta.dst, outside_ip);
413412
assert_eq!(ip4_meta.proto, Protocol::TCP);
414413

415414
let tcp_meta = match pmo.inner.ulp.as_ref().unwrap() {
@@ -430,8 +429,8 @@ mod test {
430429
ether_type: ETHER_TYPE_IPV4,
431430
};
432431
let ip = IpMeta::from(Ipv4Meta {
433-
src: outside_ipv4,
434-
dst: pub_ipv4,
432+
src: outside_ip,
433+
dst: pub_ip,
435434
proto: Protocol::TCP,
436435
});
437436
let ulp = UlpMeta::from(TcpMeta {
@@ -464,7 +463,7 @@ mod test {
464463
_ => panic!("expect Ipv4Meta"),
465464
};
466465

467-
assert_eq!(ip4_meta.src, outside_ipv4);
466+
assert_eq!(ip4_meta.src, outside_ip);
468467
assert_eq!(ip4_meta.dst, priv_ipv4);
469468
assert_eq!(ip4_meta.proto, Protocol::TCP);
470469

@@ -482,21 +481,21 @@ mod test {
482481
// handed back to the pool.
483482
// ================================================================
484483
drop(desc);
485-
assert!(pool.verify_available(priv_ipv4, pub_ipv4, pub_port));
484+
assert!(pool.verify_available(priv_ipv4, pub_ip, pub_port));
486485
}
487486

488487
#[test]
489488
fn nat_mappings() {
490489
let pool = NatPool::new();
491-
let priv1_ipv4 = "192.168.2.8".parse::<Ipv4Addr>().unwrap();
492-
let priv1 = IpAddr::Ip4(priv1_ipv4);
493-
let priv2_ipv4 = "192.168.2.33".parse::<Ipv4Addr>().unwrap();
494-
let priv2 = IpAddr::Ip4(priv2_ipv4);
495-
let public_ipv4 = "52.10.128.69".parse().unwrap();
496-
let public = IpAddr::Ip4(public_ipv4);
497-
498-
pool.add(priv1_ipv4, public_ipv4, 1025..=4096);
499-
pool.add(priv2_ipv4, public_ipv4, 4097..=8192);
490+
let priv1_ip = "192.168.2.8".parse::<Ipv4Addr>().unwrap();
491+
let priv1 = IpAddr::Ip4(priv1_ip);
492+
let priv2_ip = "192.168.2.33".parse::<Ipv4Addr>().unwrap();
493+
let priv2 = IpAddr::Ip4(priv2_ip);
494+
let public_ip = "52.10.128.69".parse().unwrap();
495+
let public = IpAddr::Ip4(public_ip);
496+
497+
pool.add(priv1_ip, public_ip, 1025..=4096);
498+
pool.add(priv2_ip, public_ip, 4097..=8192);
500499

501500
assert_eq!(pool.num_avail(priv1).unwrap(), 3072);
502501
let npe1 = match pool.obtain(&priv1) {

0 commit comments

Comments
 (0)