@push.rocks/smartnftables 1.3.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +24 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +4 -4
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.managednftables.d.ts +7 -7
- package/dist_ts/classes.managednftables.js +9 -3
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +1 -1
- package/dist_ts/managed.egress.types.d.ts +91 -0
- package/dist_ts/managed.egress.types.js +2 -0
- package/dist_ts/managed.types.d.ts +25 -19
- package/package.json +5 -5
- package/readme.md +97 -3
- package/rust/src/egress.compile.rs +288 -0
- package/rust/src/egress.host.rs +103 -0
- package/rust/src/egress.router.rs +231 -0
- package/rust/src/egress.rs +682 -0
- package/rust/src/egress_tests.rs +332 -0
- package/rust/src/main.rs +23 -3
- package/rust/src/managed.rs +105 -0
- package/rust/src/namespace.rs +51 -0
- package/rust/src/owner.rs +96 -6
- package/rust/src/owner_coexistence_tests.rs +77 -0
- package/rust/src/owner_egress_identity_tests.rs +155 -0
- package/rust/src/owner_egress_tests.rs +153 -0
- package/rust/src/owner_egress_traffic_tests.rs +470 -0
- package/rust/src/owner_host_traffic_tests.rs +305 -0
- package/rust/src/owner_identity_tests.rs +169 -0
- package/rust/src/owner_link_tests.rs +119 -0
- package/rust/src/owner_namespace_tests.rs +242 -0
- package/rust/src/owner_packet_fixture.rs +200 -0
- package/rust/src/owner_tests.rs +36 -7
- package/rust/src/policy.rs +120 -81
- package/rust/src/tests.rs +22 -0
- package/rust/src/wire.links.rs +268 -0
- package/rust/src/wire.rs +3 -64
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.managednftables.ts +19 -14
- package/ts/index.ts +1 -0
- package/ts/managed.egress.types.ts +85 -0
- package/ts/managed.types.ts +24 -16
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
use super::{empty, in_namespace, ip, isolated, options};
|
|
2
|
+
use crate::owner::{Applied, Owner, Transition};
|
|
3
|
+
use serde_json::{json, Value};
|
|
4
|
+
use std::fs::{self, File};
|
|
5
|
+
use std::io::{BufRead, BufReader, Read, Write};
|
|
6
|
+
use std::os::fd::AsRawFd;
|
|
7
|
+
use std::os::unix::fs::MetadataExt;
|
|
8
|
+
use std::os::unix::process::CommandExt;
|
|
9
|
+
use std::process::{Child, Command, Stdio};
|
|
10
|
+
use std::sync::mpsc::{self, Receiver};
|
|
11
|
+
use std::thread::{self, JoinHandle};
|
|
12
|
+
use std::time::{Duration, Instant};
|
|
13
|
+
|
|
14
|
+
fn command(namespace: &File) -> Command {
|
|
15
|
+
let fd = namespace.as_raw_fd();
|
|
16
|
+
let mut command = Command::new("/bin/managed");
|
|
17
|
+
command
|
|
18
|
+
.args(["--management", "--network-namespace-fd", "3"])
|
|
19
|
+
.stdin(Stdio::piped())
|
|
20
|
+
.stdout(Stdio::piped())
|
|
21
|
+
.stderr(Stdio::inherit());
|
|
22
|
+
unsafe {
|
|
23
|
+
command.pre_exec(move || {
|
|
24
|
+
if libc::dup2(fd, 3) < 0 || libc::fcntl(3, libc::F_SETFD, 0) < 0 {
|
|
25
|
+
return Err(std::io::Error::last_os_error());
|
|
26
|
+
}
|
|
27
|
+
Ok(())
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
command
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
struct NativeOwner {
|
|
34
|
+
child: Child,
|
|
35
|
+
messages: Receiver<Value>,
|
|
36
|
+
reader: Option<JoinHandle<()>>,
|
|
37
|
+
}
|
|
38
|
+
impl NativeOwner {
|
|
39
|
+
fn start(namespace: &File) -> Self {
|
|
40
|
+
let mut child = command(namespace).spawn().unwrap();
|
|
41
|
+
let output = child.stdout.take().unwrap();
|
|
42
|
+
let (send, messages) = mpsc::channel();
|
|
43
|
+
let reader = thread::spawn(move || {
|
|
44
|
+
for line in BufReader::new(output.take(1024 * 1024)).lines() {
|
|
45
|
+
let Ok(line) = line else { break };
|
|
46
|
+
if line.len() > 262_144 {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
let Ok(value) = serde_json::from_str(&line) else {
|
|
50
|
+
break;
|
|
51
|
+
};
|
|
52
|
+
if send.send(value).is_err() {
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
let owner = Self {
|
|
58
|
+
child,
|
|
59
|
+
messages,
|
|
60
|
+
reader: Some(reader),
|
|
61
|
+
};
|
|
62
|
+
assert_eq!(
|
|
63
|
+
owner.messages.recv_timeout(Duration::from_secs(5)).unwrap()["event"],
|
|
64
|
+
"ready"
|
|
65
|
+
);
|
|
66
|
+
owner
|
|
67
|
+
}
|
|
68
|
+
fn response(&mut self, method: &str, params: Value) -> Value {
|
|
69
|
+
writeln!(
|
|
70
|
+
self.child.stdin.as_mut().unwrap(),
|
|
71
|
+
"{}",
|
|
72
|
+
json!({"id":method,"method":method,"params":params})
|
|
73
|
+
)
|
|
74
|
+
.unwrap();
|
|
75
|
+
let response = self.messages.recv_timeout(Duration::from_secs(5)).unwrap();
|
|
76
|
+
assert_eq!(response["id"], method);
|
|
77
|
+
response
|
|
78
|
+
}
|
|
79
|
+
fn call(&mut self, method: &str, params: Value) -> Value {
|
|
80
|
+
let response = self.response(method, params);
|
|
81
|
+
assert_eq!(response["success"], true, "{response}");
|
|
82
|
+
response["result"].clone()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
impl Drop for NativeOwner {
|
|
86
|
+
fn drop(&mut self) {
|
|
87
|
+
let _ = self.child.kill();
|
|
88
|
+
let _ = self.child.wait();
|
|
89
|
+
if let Some(reader) = self.reader.take() {
|
|
90
|
+
let _ = reader.join();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#[test]
|
|
96
|
+
#[ignore = "requires the explicitly isolated QEMU qualification guest"]
|
|
97
|
+
fn inherited_namespace_binds_policy_and_persistent_recovery() {
|
|
98
|
+
isolated();
|
|
99
|
+
ip(&["netns", "add", "fdtarget"]);
|
|
100
|
+
let namespace = File::open("/run/netns/fdtarget").unwrap();
|
|
101
|
+
let metadata = namespace.metadata().unwrap();
|
|
102
|
+
let initial = fs::metadata("/proc/thread-self/ns/net").unwrap();
|
|
103
|
+
assert_ne!(metadata.ino(), initial.ino());
|
|
104
|
+
let mut child = NativeOwner::start(&namespace);
|
|
105
|
+
let pid = child.child.id();
|
|
106
|
+
let status = child.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
107
|
+
assert_eq!(
|
|
108
|
+
status["identity"]["namespaceDevice"],
|
|
109
|
+
metadata.dev().to_string()
|
|
110
|
+
);
|
|
111
|
+
assert_eq!(
|
|
112
|
+
status["identity"]["namespaceInode"],
|
|
113
|
+
metadata.ino().to_string()
|
|
114
|
+
);
|
|
115
|
+
assert_eq!(
|
|
116
|
+
fs::read_dir(format!("/proc/{pid}/task")).unwrap().count(),
|
|
117
|
+
1
|
|
118
|
+
);
|
|
119
|
+
let target_link = fs::read_link(format!("/proc/{pid}/ns/net")).unwrap();
|
|
120
|
+
for fd in fs::read_dir(format!("/proc/{pid}/fd")).unwrap() {
|
|
121
|
+
if let Ok(target) = fs::read_link(fd.unwrap().path()) {
|
|
122
|
+
assert_ne!(
|
|
123
|
+
target, target_link,
|
|
124
|
+
"child namespace descriptor must be closed"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
let first = child.call(
|
|
129
|
+
"reconcilePolicy",
|
|
130
|
+
serde_json::to_value(Transition {
|
|
131
|
+
previous: None,
|
|
132
|
+
target: empty(1),
|
|
133
|
+
})
|
|
134
|
+
.unwrap(),
|
|
135
|
+
);
|
|
136
|
+
assert!(Owner::new(options("nsfd"))
|
|
137
|
+
.unwrap()
|
|
138
|
+
.graph()
|
|
139
|
+
.unwrap()
|
|
140
|
+
.is_none());
|
|
141
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
142
|
+
.unwrap()
|
|
143
|
+
.graph()
|
|
144
|
+
.unwrap()
|
|
145
|
+
.is_some()));
|
|
146
|
+
assert_eq!(
|
|
147
|
+
unsafe { libc::ioctl(namespace.as_raw_fd(), libc::NS_GET_NSTYPE) },
|
|
148
|
+
libc::CLONE_NEWNET
|
|
149
|
+
);
|
|
150
|
+
drop(child); // PERSIST must retain the target graph after native process loss.
|
|
151
|
+
assert!(!std::path::Path::new(&format!("/proc/{pid}")).exists());
|
|
152
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
153
|
+
.unwrap()
|
|
154
|
+
.graph()
|
|
155
|
+
.unwrap()
|
|
156
|
+
.is_some()));
|
|
157
|
+
|
|
158
|
+
let previous: Applied = serde_json::from_value(first).unwrap();
|
|
159
|
+
let transition = serde_json::to_value(Transition {
|
|
160
|
+
previous: Some(previous),
|
|
161
|
+
target: empty(2),
|
|
162
|
+
})
|
|
163
|
+
.unwrap();
|
|
164
|
+
let wrong_namespace = File::open("/proc/thread-self/ns/net").unwrap();
|
|
165
|
+
let mut wrong = NativeOwner::start(&wrong_namespace);
|
|
166
|
+
wrong.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
167
|
+
let denied = wrong.response("reconcilePolicy", transition.clone());
|
|
168
|
+
assert_eq!(denied["success"], false);
|
|
169
|
+
assert_eq!(denied["errorCode"], "CONFLICT");
|
|
170
|
+
assert!(Owner::new(options("nsfd"))
|
|
171
|
+
.unwrap()
|
|
172
|
+
.graph()
|
|
173
|
+
.unwrap()
|
|
174
|
+
.is_none());
|
|
175
|
+
drop(wrong);
|
|
176
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
177
|
+
.unwrap()
|
|
178
|
+
.graph()
|
|
179
|
+
.unwrap()
|
|
180
|
+
.is_some()));
|
|
181
|
+
|
|
182
|
+
let mut recovered = NativeOwner::start(&namespace);
|
|
183
|
+
recovered.call("openOwner", serde_json::to_value(options("nsfd")).unwrap());
|
|
184
|
+
drop(namespace); // The entered child keeps membership; caller capability is not consumed by it.
|
|
185
|
+
let applied = recovered.call("reconcilePolicy", transition);
|
|
186
|
+
assert_eq!(applied["receipt"]["revision"], 2);
|
|
187
|
+
assert_eq!(
|
|
188
|
+
applied["receipt"]["identity"]["namespaceInode"],
|
|
189
|
+
metadata.ino().to_string()
|
|
190
|
+
);
|
|
191
|
+
assert_eq!(recovered.call("inspectPolicy", json!({}))["enforced"], true);
|
|
192
|
+
assert_eq!(recovered.call("closeOwner", json!({}))["released"], true);
|
|
193
|
+
drop(recovered);
|
|
194
|
+
assert!(in_namespace("fdtarget", || Owner::new(options("nsfd"))
|
|
195
|
+
.unwrap()
|
|
196
|
+
.graph()
|
|
197
|
+
.unwrap()
|
|
198
|
+
.is_none()));
|
|
199
|
+
assert!(Owner::new(options("nsfd"))
|
|
200
|
+
.unwrap()
|
|
201
|
+
.graph()
|
|
202
|
+
.unwrap()
|
|
203
|
+
.is_none());
|
|
204
|
+
assert_eq!(
|
|
205
|
+
fs::metadata("/proc/thread-self/ns/net").unwrap().ino(),
|
|
206
|
+
initial.ino()
|
|
207
|
+
);
|
|
208
|
+
ip(&["netns", "delete", "fdtarget"]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#[test]
|
|
212
|
+
#[ignore = "requires the explicitly isolated QEMU qualification guest"]
|
|
213
|
+
fn valid_descriptor_without_entry_permission_never_becomes_ready() {
|
|
214
|
+
isolated();
|
|
215
|
+
let namespace = File::open("/proc/thread-self/ns/net").unwrap();
|
|
216
|
+
let mut child = command(&namespace)
|
|
217
|
+
.uid(65534)
|
|
218
|
+
.gid(65534)
|
|
219
|
+
.stdin(Stdio::null())
|
|
220
|
+
.stderr(Stdio::piped())
|
|
221
|
+
.spawn()
|
|
222
|
+
.unwrap();
|
|
223
|
+
let deadline = Instant::now() + Duration::from_secs(5);
|
|
224
|
+
while child.try_wait().unwrap().is_none() {
|
|
225
|
+
if Instant::now() >= deadline {
|
|
226
|
+
let _ = child.kill();
|
|
227
|
+
let _ = child.wait();
|
|
228
|
+
panic!("denied namespace entry did not exit");
|
|
229
|
+
}
|
|
230
|
+
thread::sleep(Duration::from_millis(10));
|
|
231
|
+
}
|
|
232
|
+
let output = child.wait_with_output().unwrap();
|
|
233
|
+
assert_eq!(output.status.code(), Some(1));
|
|
234
|
+
assert!(output.stdout.is_empty());
|
|
235
|
+
assert!(String::from_utf8(output.stderr)
|
|
236
|
+
.unwrap()
|
|
237
|
+
.contains("network namespace entry failed"));
|
|
238
|
+
assert_eq!(
|
|
239
|
+
unsafe { libc::ioctl(namespace.as_raw_fd(), libc::NS_GET_NSTYPE) },
|
|
240
|
+
libc::CLONE_NEWNET
|
|
241
|
+
);
|
|
242
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
//! Guest-only Ethernet observation/injection for packet-path qualifications.
|
|
2
|
+
use super::*;
|
|
3
|
+
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
|
|
4
|
+
use std::time::{Duration, Instant};
|
|
5
|
+
|
|
6
|
+
pub(super) struct PacketSocket {
|
|
7
|
+
fd: OwnedFd,
|
|
8
|
+
index: i32,
|
|
9
|
+
}
|
|
10
|
+
impl PacketSocket {
|
|
11
|
+
pub(super) fn open(name: &str) -> Self {
|
|
12
|
+
guest_guard();
|
|
13
|
+
let name = std::ffi::CString::new(name).unwrap();
|
|
14
|
+
let index = unsafe { libc::if_nametoindex(name.as_ptr()) } as i32;
|
|
15
|
+
assert!(index > 0);
|
|
16
|
+
let raw = unsafe {
|
|
17
|
+
libc::socket(
|
|
18
|
+
libc::AF_PACKET,
|
|
19
|
+
libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
|
|
20
|
+
i32::from((libc::ETH_P_IP as u16).to_be()),
|
|
21
|
+
)
|
|
22
|
+
};
|
|
23
|
+
assert!(
|
|
24
|
+
raw >= 0,
|
|
25
|
+
"packet socket: {}",
|
|
26
|
+
std::io::Error::last_os_error()
|
|
27
|
+
);
|
|
28
|
+
let fd = unsafe { OwnedFd::from_raw_fd(raw) };
|
|
29
|
+
let mut address: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
|
|
30
|
+
address.sll_family = libc::AF_PACKET as u16;
|
|
31
|
+
address.sll_protocol = (libc::ETH_P_IP as u16).to_be();
|
|
32
|
+
address.sll_ifindex = index;
|
|
33
|
+
assert_eq!(
|
|
34
|
+
unsafe {
|
|
35
|
+
libc::bind(
|
|
36
|
+
raw,
|
|
37
|
+
(&address as *const libc::sockaddr_ll).cast(),
|
|
38
|
+
std::mem::size_of_val(&address) as _,
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
0
|
|
42
|
+
);
|
|
43
|
+
Self { fd, index }
|
|
44
|
+
}
|
|
45
|
+
pub(super) fn send(&self, mac: [u8; 6], packet: &[u8]) {
|
|
46
|
+
let mut address: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
|
|
47
|
+
address.sll_family = libc::AF_PACKET as u16;
|
|
48
|
+
address.sll_protocol = (libc::ETH_P_IP as u16).to_be();
|
|
49
|
+
address.sll_ifindex = self.index;
|
|
50
|
+
address.sll_halen = 6;
|
|
51
|
+
address.sll_addr[..6].copy_from_slice(&mac);
|
|
52
|
+
assert_eq!(
|
|
53
|
+
unsafe {
|
|
54
|
+
libc::sendto(
|
|
55
|
+
self.fd.as_raw_fd(),
|
|
56
|
+
packet.as_ptr().cast(),
|
|
57
|
+
packet.len(),
|
|
58
|
+
0,
|
|
59
|
+
(&address as *const libc::sockaddr_ll).cast(),
|
|
60
|
+
std::mem::size_of_val(&address) as _,
|
|
61
|
+
)
|
|
62
|
+
},
|
|
63
|
+
packet.len() as isize
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
pub(super) fn matching(&self, mut matches: impl FnMut(&[u8]) -> bool) -> Option<Vec<u8>> {
|
|
67
|
+
let deadline = Instant::now() + Duration::from_millis(500);
|
|
68
|
+
loop {
|
|
69
|
+
let remaining = deadline
|
|
70
|
+
.saturating_duration_since(Instant::now())
|
|
71
|
+
.as_millis();
|
|
72
|
+
if remaining == 0 {
|
|
73
|
+
return None;
|
|
74
|
+
}
|
|
75
|
+
let mut poll = libc::pollfd {
|
|
76
|
+
fd: self.fd.as_raw_fd(),
|
|
77
|
+
events: libc::POLLIN,
|
|
78
|
+
revents: 0,
|
|
79
|
+
};
|
|
80
|
+
let ready = unsafe { libc::poll(&mut poll, 1, remaining as i32) };
|
|
81
|
+
assert!(ready >= 0);
|
|
82
|
+
if ready == 0 {
|
|
83
|
+
return None;
|
|
84
|
+
}
|
|
85
|
+
let mut packet = vec![0; 65536];
|
|
86
|
+
let size = unsafe {
|
|
87
|
+
libc::recv(
|
|
88
|
+
self.fd.as_raw_fd(),
|
|
89
|
+
packet.as_mut_ptr().cast(),
|
|
90
|
+
packet.len(),
|
|
91
|
+
libc::MSG_TRUNC,
|
|
92
|
+
)
|
|
93
|
+
};
|
|
94
|
+
assert!(size >= 0 && (size as usize) <= packet.len());
|
|
95
|
+
packet.truncate(size as usize);
|
|
96
|
+
if matches(&packet) {
|
|
97
|
+
return Some(packet);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
pub(super) fn mac(name: &str) -> [u8; 6] {
|
|
103
|
+
let output = std::process::Command::new("/bin/ip")
|
|
104
|
+
.args(["-j", "link", "show", "dev", name])
|
|
105
|
+
.output()
|
|
106
|
+
.unwrap();
|
|
107
|
+
assert!(output.status.success());
|
|
108
|
+
let value = serde_json::from_slice::<serde_json::Value>(&output.stdout).unwrap();
|
|
109
|
+
value[0]["address"]
|
|
110
|
+
.as_str()
|
|
111
|
+
.unwrap()
|
|
112
|
+
.split(':')
|
|
113
|
+
.map(|byte| u8::from_str_radix(byte, 16).unwrap())
|
|
114
|
+
.collect::<Vec<_>>()
|
|
115
|
+
.try_into()
|
|
116
|
+
.unwrap()
|
|
117
|
+
}
|
|
118
|
+
fn checksum(bytes: &[u8]) -> u16 {
|
|
119
|
+
let mut sum: u32 = bytes
|
|
120
|
+
.chunks(2)
|
|
121
|
+
.map(|bytes| u32::from(bytes[0]) * 256 + u32::from(*bytes.get(1).unwrap_or(&0)))
|
|
122
|
+
.sum();
|
|
123
|
+
while sum >> 16 != 0 {
|
|
124
|
+
sum = (sum & 0xffff) + (sum >> 16);
|
|
125
|
+
}
|
|
126
|
+
!(sum as u16)
|
|
127
|
+
}
|
|
128
|
+
pub(super) fn tcp(
|
|
129
|
+
source: [u8; 4],
|
|
130
|
+
destination: [u8; 4],
|
|
131
|
+
source_port: u16,
|
|
132
|
+
destination_port: u16,
|
|
133
|
+
flags: u8,
|
|
134
|
+
) -> Vec<u8> {
|
|
135
|
+
let mut packet = vec![0; 40];
|
|
136
|
+
packet[0] = 0x45;
|
|
137
|
+
packet[2..4].copy_from_slice(&40_u16.to_be_bytes());
|
|
138
|
+
packet[8] = 64;
|
|
139
|
+
packet[9] = 6;
|
|
140
|
+
packet[12..16].copy_from_slice(&source);
|
|
141
|
+
packet[16..20].copy_from_slice(&destination);
|
|
142
|
+
let sum = checksum(&packet[..20]);
|
|
143
|
+
packet[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
144
|
+
packet[20..22].copy_from_slice(&source_port.to_be_bytes());
|
|
145
|
+
packet[22..24].copy_from_slice(&destination_port.to_be_bytes());
|
|
146
|
+
packet[24..28].copy_from_slice(&100_u32.to_be_bytes());
|
|
147
|
+
packet[32] = 0x50;
|
|
148
|
+
packet[33] = flags;
|
|
149
|
+
packet[34..36].copy_from_slice(&65535_u16.to_be_bytes());
|
|
150
|
+
let mut pseudo = Vec::new();
|
|
151
|
+
pseudo.extend(source);
|
|
152
|
+
pseudo.extend(destination);
|
|
153
|
+
pseudo.extend([0, 6, 0, 20]);
|
|
154
|
+
pseudo.extend(&packet[20..]);
|
|
155
|
+
let sum = checksum(&pseudo);
|
|
156
|
+
packet[36..38].copy_from_slice(&sum.to_be_bytes());
|
|
157
|
+
packet
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
pub(super) fn udp(
|
|
161
|
+
source: [u8; 4],
|
|
162
|
+
destination: [u8; 4],
|
|
163
|
+
source_port: u16,
|
|
164
|
+
destination_port: u16,
|
|
165
|
+
) -> Vec<u8> {
|
|
166
|
+
let mut packet = vec![0; 32];
|
|
167
|
+
packet[0] = 0x45;
|
|
168
|
+
packet[2..4].copy_from_slice(&32_u16.to_be_bytes());
|
|
169
|
+
packet[8] = 64;
|
|
170
|
+
packet[9] = 17;
|
|
171
|
+
packet[12..16].copy_from_slice(&source);
|
|
172
|
+
packet[16..20].copy_from_slice(&destination);
|
|
173
|
+
let sum = checksum(&packet[..20]);
|
|
174
|
+
packet[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
175
|
+
packet[20..22].copy_from_slice(&source_port.to_be_bytes());
|
|
176
|
+
packet[22..24].copy_from_slice(&destination_port.to_be_bytes());
|
|
177
|
+
packet[24..26].copy_from_slice(&12_u16.to_be_bytes());
|
|
178
|
+
packet[28..].copy_from_slice(b"frag");
|
|
179
|
+
packet
|
|
180
|
+
}
|
|
181
|
+
pub(super) fn fragments(packet: &[u8], id: u16) -> [Vec<u8>; 2] {
|
|
182
|
+
assert_eq!(packet.len(), 32);
|
|
183
|
+
assert_eq!(packet[0], 0x45);
|
|
184
|
+
let fragment = |body: &[u8], offset: u16| {
|
|
185
|
+
let mut result = packet[..20].to_vec();
|
|
186
|
+
result.extend(body);
|
|
187
|
+
let length = result.len() as u16;
|
|
188
|
+
result[2..4].copy_from_slice(&length.to_be_bytes());
|
|
189
|
+
result[4..6].copy_from_slice(&id.to_be_bytes());
|
|
190
|
+
result[6..8].copy_from_slice(&offset.to_be_bytes());
|
|
191
|
+
result[10..12].fill(0);
|
|
192
|
+
let sum = checksum(&result[..20]);
|
|
193
|
+
result[10..12].copy_from_slice(&sum.to_be_bytes());
|
|
194
|
+
result
|
|
195
|
+
};
|
|
196
|
+
[
|
|
197
|
+
fragment(&packet[20..28], 0x2000),
|
|
198
|
+
fragment(&packet[28..], 1),
|
|
199
|
+
]
|
|
200
|
+
}
|
package/rust/src/owner_tests.rs
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
#[path = "owner_namespace_tests.rs"]
|
|
4
|
+
mod namespace_tests;
|
|
5
|
+
|
|
6
|
+
#[path = "owner_egress_identity_tests.rs"]
|
|
7
|
+
mod egress_identity_tests;
|
|
8
|
+
|
|
9
|
+
#[path = "owner_link_tests.rs"]
|
|
10
|
+
mod link_tests;
|
|
11
|
+
|
|
12
|
+
#[path = "owner_egress_tests.rs"]
|
|
13
|
+
mod egress_tests;
|
|
14
|
+
|
|
15
|
+
#[path = "owner_egress_traffic_tests.rs"]
|
|
16
|
+
mod egress_traffic_tests;
|
|
17
|
+
|
|
18
|
+
#[path = "owner_packet_fixture.rs"]
|
|
19
|
+
mod packet_fixture;
|
|
20
|
+
|
|
21
|
+
#[path = "owner_host_traffic_tests.rs"]
|
|
22
|
+
mod host_traffic_tests;
|
|
23
|
+
|
|
24
|
+
#[path = "owner_coexistence_tests.rs"]
|
|
25
|
+
mod coexistence_tests;
|
|
26
|
+
|
|
27
|
+
fn guest_guard() {
|
|
4
28
|
assert_eq!(
|
|
5
29
|
std::env::var("SMARTNFTABLES_ISOLATED_NATIVE").as_deref(),
|
|
6
30
|
Ok("qemu-6.18.35")
|
|
@@ -9,6 +33,10 @@ fn isolated() {
|
|
|
9
33
|
.unwrap()
|
|
10
34
|
.contains("smartnftables_qualification=1"));
|
|
11
35
|
assert_eq!(unsafe { libc::geteuid() }, 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fn isolated() {
|
|
39
|
+
guest_guard();
|
|
12
40
|
let initial = context().unwrap();
|
|
13
41
|
assert_eq!(
|
|
14
42
|
unsafe { libc::unshare(libc::CLONE_NEWNET | libc::CLONE_NEWNS) },
|
|
@@ -276,7 +304,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
276
304
|
assert_eq!(
|
|
277
305
|
owner.reconcile(Transition {
|
|
278
306
|
previous: None,
|
|
279
|
-
target: invalid_pair.prepare().unwrap()
|
|
307
|
+
target: invalid_pair.prepare().unwrap().into()
|
|
280
308
|
}),
|
|
281
309
|
Err(Error::Conflict)
|
|
282
310
|
);
|
|
@@ -286,7 +314,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
286
314
|
assert_eq!(
|
|
287
315
|
owner.reconcile(Transition {
|
|
288
316
|
previous: None,
|
|
289
|
-
target: policy.clone().prepare().unwrap()
|
|
317
|
+
target: policy.clone().prepare().unwrap().into()
|
|
290
318
|
}),
|
|
291
319
|
Err(Error::Conflict)
|
|
292
320
|
);
|
|
@@ -295,7 +323,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
295
323
|
let current = owner
|
|
296
324
|
.reconcile(Transition {
|
|
297
325
|
previous: None,
|
|
298
|
-
target: policy.clone().prepare().unwrap(),
|
|
326
|
+
target: policy.clone().prepare().unwrap().into(),
|
|
299
327
|
})
|
|
300
328
|
.expect("apply interface policy");
|
|
301
329
|
assert!(owner.inspect().enforced);
|
|
@@ -333,7 +361,7 @@ fn native_interface_ipv4_directed_grants_and_spoof_denial() {
|
|
|
333
361
|
let revoked = owner
|
|
334
362
|
.reconcile(Transition {
|
|
335
363
|
previous: Some(current),
|
|
336
|
-
target: denied.prepare().unwrap(),
|
|
364
|
+
target: denied.prepare().unwrap().into(),
|
|
337
365
|
})
|
|
338
366
|
.expect("atomic complete deny");
|
|
339
367
|
udp(&client, &server, false);
|
|
@@ -361,6 +389,7 @@ fn empty(revision: u64) -> Prepared {
|
|
|
361
389
|
}
|
|
362
390
|
.prepare()
|
|
363
391
|
.unwrap()
|
|
392
|
+
.into()
|
|
364
393
|
}
|
|
365
394
|
|
|
366
395
|
#[test]
|
|
@@ -696,14 +725,14 @@ fn native_tun_forwarding_binds_remote_ingress_and_rejects_tap() {
|
|
|
696
725
|
assert_eq!(
|
|
697
726
|
owner.reconcile(Transition {
|
|
698
727
|
previous: None,
|
|
699
|
-
target: invalid.prepare().unwrap()
|
|
728
|
+
target: invalid.prepare().unwrap().into()
|
|
700
729
|
}),
|
|
701
730
|
Err(Error::Conflict)
|
|
702
731
|
);
|
|
703
732
|
let applied = owner
|
|
704
733
|
.reconcile(Transition {
|
|
705
734
|
previous: None,
|
|
706
|
-
target: policy.prepare().unwrap(),
|
|
735
|
+
target: policy.prepare().unwrap().into(),
|
|
707
736
|
})
|
|
708
737
|
.unwrap();
|
|
709
738
|
send_tunnel(&mut device, true);
|