Skip to content

Commit 9c226ed

Browse files
committedJul 25, 2024·
Improve NAT and begin MAC management
1 parent 3472d6d commit 9c226ed

File tree

8 files changed

+251
-37
lines changed

8 files changed

+251
-37
lines changed
 

‎Cargo.lock

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ name = "proton"
1414
version = "0.1.0"
1515
edition = "2021"
1616

17+
[dependencies]
18+
cidr = "0.2.3"
19+
20+
[dependencies.pnet]
21+
version = "0.35.0"
22+
1723
[dependencies.proton_arp]
1824
path = "proton_arp"
1925

‎examples/access_point.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! An example of a simple wireless access point managed by Proton.
2+
3+
use std::net::Ipv4Addr;
4+
5+
use cidr::Ipv4Cidr;
6+
7+
use proton::AccessPoint;
8+
9+
fn main() {
10+
let mut ap = AccessPoint::new(
11+
Ipv4Addr::new(10, 0, 0, 1), // External IPv4 address
12+
Ipv4Cidr::new( // Internal network range
13+
Ipv4Addr::new(10, 0, 0, 0), // Network address
14+
24, // Network length
15+
).unwrap(),
16+
);
17+
18+
19+
}

‎proton_mac/src/mac_addr_policy.rs

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::MacAddrPolicyError;
77
/// Result type for MAC address policy actions.
88
pub type PolicyResult = Result<(), MacAddrPolicyError>;
99

10+
#[derive(Clone)]
1011
/// A MAC address policy.
1112
///
1213
/// This defines the policy by which MAC addresses (hardware addresses
@@ -115,6 +116,12 @@ impl MacAddrPolicy {
115116
}
116117
}
117118

119+
impl Default for MacAddrPolicy {
120+
fn default() -> Self {
121+
Self::public()
122+
}
123+
}
124+
118125
#[cfg(test)]
119126
mod tests {
120127
use super::*;

‎proton_nat/src/nat.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const MINIMUM_NAT_PORT: Layer4Port = 50_000;
1919
/// Maximum Layer 4 port permitted (inclusive).
2020
const MAXIMUM_NAT_PORT: Layer4Port = u16::MAX;
2121

22+
#[derive(Clone)]
2223
/// A Network Address Translation table.
2324
///
2425
/// This table provides a bijective mapping (with the help of the external crate `bimap`) to

‎proton_wap/Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,15 @@ cidr = "0.2.3"
1313
[dependencies.pnet]
1414
version = "0.35.0"
1515

16+
[dependencies.tokio]
17+
version = "1"
18+
features = ["full"]
19+
20+
[dependencies.proton_mac]
21+
path = "../proton_mac"
22+
1623
[dependencies.proton_nat]
1724
path = "../proton_nat"
25+
26+
[dependencies.proton_nif]
27+
path = "../proton_nif"

‎proton_wap/src/ap.rs

+202-36
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{
44
io,
55
net::{
6+
IpAddr,
67
Ipv4Addr,
78
SocketAddrV4,
89
},
@@ -32,8 +33,20 @@ use pnet::{
3233
},
3334
};
3435

36+
use tokio::task;
37+
38+
use proton_mac::MacAddrPolicy;
39+
3540
use proton_nat::NatTable;
3641

42+
use proton_nif::{
43+
ifnames::{
44+
DEFAULT_WIRED_INTERFACE,
45+
DEFAULT_WIRELESS_INTERFACE,
46+
},
47+
NetworkInterface,
48+
};
49+
3750
use crate::AccessPointResult;
3851

3952
/// Transport channel buffer size.
@@ -46,6 +59,9 @@ pub struct AccessPoint {
4659

4760
/// CIDR network range.
4861
range: Ipv4Cidr,
62+
63+
/// MAC address management policy.
64+
mac_policy: MacAddrPolicy,
4965
}
5066

5167
impl AccessPoint {
@@ -56,26 +72,67 @@ impl AccessPoint {
5672
/// to this access point
5773
/// - `range` (`Ipv4Cidr`): the internal network range associated to
5874
/// this access point
75+
/// - `mac_policy` (`MacAddrPolicy`): the MAC address policy of this
76+
/// access point
5977
///
6078
/// # Returns
6179
/// A new `AccessPoint`.
62-
pub fn new(external_ipv4: Ipv4Addr, range: Ipv4Cidr) -> Self {
80+
pub fn new(
81+
external_ipv4: Ipv4Addr,
82+
range: Ipv4Cidr,
83+
mac_policy: MacAddrPolicy,
84+
) -> Self {
6385
Self {
6486
nat: NatTable::new(vec![external_ipv4]),
6587
range,
88+
mac_policy,
6689
}
6790
}
6891

69-
/// Continuously route packets on the Transport Layer (OSI Layer 4).
92+
/// Continuously route packets, monitoring both the Data Link Layer and
93+
/// the Transport Layer to ensure both proper NAT and MAC policy enforcement.
7094
///
7195
/// # Parameters
7296
/// None.
7397
///
7498
/// # Returns
75-
/// An `AccessPointResult` indicating an error, if one occurred.
99+
/// An `AccessPointResult<()>` indicating an error, if one occurred.
100+
///
101+
/// This function does not return during nominal operation.
102+
pub async fn run(&mut self) -> AccessPointResult<()> {
103+
// Construct new NAT
104+
let nat = self.nat.clone();
105+
106+
// Get network range
107+
let range = self.range;
108+
109+
// Get MAC policy
110+
let mac_policy = self.mac_policy.clone();
111+
112+
let layer_2_task = task::spawn(Self::run_layer_2(mac_policy));
113+
114+
let layer_4_task = task::spawn(Self::run_layer_4(nat, range));
115+
116+
match tokio::join!(layer_2_task, layer_4_task) {
117+
(Ok (_), Ok (_)) => Ok (()),
118+
_ => todo!(),
119+
}
120+
}
121+
122+
/// Continuously route packets on the Transport Layer (OSI Layer 4).
123+
///
124+
/// # Parameters
125+
/// - `nat` (`NatTable`): the reference NAT table
126+
/// - `range` (`Ipv4Cidr`): the network range
127+
///
128+
/// # Returns
129+
/// An `AccessPointResult<()>` indicating an error, if one occurred.
76130
///
77-
/// This function does not return if there are no errors.
78-
pub fn run(&mut self) -> AccessPointResult {
131+
/// This function does not return during nominal operation.
132+
async fn run_layer_4(
133+
mut nat: NatTable,
134+
range: Ipv4Cidr,
135+
) -> AccessPointResult<()> {
79136
// Create an IPv4 protocol
80137
let protocol = Layer4 (Ipv4 (IpNextHeaderProtocols::Ipv4));
81138

@@ -85,37 +142,22 @@ impl AccessPoint {
85142
Err (_) => return Err (io::Error::new(io::ErrorKind::Other, "could not open transport channel")),
86143
};
87144

88-
// We treat received packets as if they were IPv4 packets
145+
// We treat received packets as if they are IPv4 packets
89146
let mut iter = ipv4_packet_iter(&mut rx);
90147

91148
// Continuously iterate through the packets on the receiving line
92149
loop {
93150
match iter.next() {
94151
Ok ((packet, addr)) => {
95-
// Allocate enough space for a new packet
96-
let mut vec: Vec<u8> = vec![0; packet.packet().len()];
97-
let mut new_packet = MutableIpv4Packet::new(&mut vec[..]).unwrap();
98-
99-
// Create a clone of the original packet
100-
new_packet.clone_from(&packet);
101-
102-
// Get source IPv4
103-
let source_ipv4: Ipv4Addr = new_packet.get_source();
104-
105-
// Construct immutable packet
106-
let ipv4_packet = new_packet.to_immutable();
107-
108-
// Detect NAT type and translate packet
109-
let translated_packet = if self.range.contains(&source_ipv4) {
110-
// Source NAT
111-
self.translate_outgoing_ipv4_packet(ipv4_packet)
112-
} else {
113-
// Destination NAT
114-
self.translate_incoming_ipv4_packet(ipv4_packet)
115-
}.ok_or(io::Error::new(io::ErrorKind::Other, "could not perform NAT"))?;
152+
let (translated_packet, translated_addr) = Self::translate_packet(
153+
&mut nat,
154+
range,
155+
packet,
156+
addr,
157+
)?;
116158

117159
// Send the translated packet
118-
if tx.send_to(translated_packet, addr).is_err() {
160+
if tx.send_to(translated_packet, translated_addr).is_err() {
119161
println!("Failed to send packet to address {}", addr);
120162
}
121163
}
@@ -128,15 +170,135 @@ impl AccessPoint {
128170
}
129171
}
130172

173+
/// Continuously route frames on the Data Link Layer (OSI Layer 2).
174+
///
175+
/// # Parameters
176+
/// - `mac_policy` (`MacAddrPolicy`): the MAC address management policy
177+
///
178+
/// # Returns
179+
/// An `AccessPointResult<()>` indicating an error, if one occurred.
180+
///
181+
/// This function does not return during nominal operation.
182+
async fn run_layer_2(mac_policy: MacAddrPolicy) -> AccessPointResult<()> {
183+
// Construct the wired network interface
184+
let wired_if = NetworkInterface::new(DEFAULT_WIRED_INTERFACE)
185+
.ok_or(io::Error::new(io::ErrorKind::Other, "could not find wired interface"))?;
186+
187+
// Construct the wireless network interface
188+
let wireless_if = NetworkInterface::new(DEFAULT_WIRELESS_INTERFACE)
189+
.ok_or(io::Error::new(io::ErrorKind::Other, "could not find wireless interface"))?;
190+
191+
// Route outgoing ETH frames
192+
let outgoing_task = task::spawn(Self::route_outgoing_frames(wired_if.clone(), wireless_if.clone(), mac_policy));
193+
194+
// Route incoming ETH frames
195+
let incoming_task = task::spawn(Self::route_incoming_frames(wired_if, wireless_if));
196+
197+
match tokio::join!(outgoing_task, incoming_task) {
198+
(Ok (_), Ok (_)) => Ok (()),
199+
_ => todo!(),
200+
}
201+
}
202+
203+
/// Route incoming Ethernet frames.
204+
///
205+
/// # Parameters
206+
/// - `wired_if` (`NetworkInterface`): the wired network interface
207+
/// - `wireless_if` (`NetworkInterface`): the wireless network interface
208+
///
209+
/// # Returns
210+
/// TODO
211+
async fn route_incoming_frames(
212+
_wired_if: NetworkInterface,
213+
_wireless_if: NetworkInterface,
214+
) {
215+
todo!()
216+
}
217+
218+
/// Route outgoing Ethernet frames.
219+
///
220+
/// # Parameters
221+
/// - `wired_if` (`NetworkInterface`): the wired network interface
222+
/// - `wireless_if` (`NetworkInterface`): the wireless network interface
223+
/// - `mac_policy` (`MacAddrPolicy`): the MAC address management policy
224+
///
225+
/// # Returns
226+
/// TODO
227+
async fn route_outgoing_frames(
228+
_wired_if: NetworkInterface,
229+
_wireless_if: NetworkInterface,
230+
_mac_policy: MacAddrPolicy,
231+
) {
232+
todo!()
233+
}
234+
235+
/// Translate an IPv4 packet.
236+
///
237+
/// # Parameters
238+
/// - `nat` (`&mut NatTable`): the reference NAT table
239+
/// - `range` (`Ipv4Cidr`): the network range
240+
/// - `packet` (`Ipv4Packet`): an IPv4 packet to be translated
241+
/// - `addr` (`IpAddr`): the destination IP address of the packet,
242+
/// which will change in the case of Destination NAT (DNAT).
243+
fn translate_packet<'a>(
244+
nat: &'a mut NatTable,
245+
range: Ipv4Cidr,
246+
packet: Ipv4Packet<'a>,
247+
addr: IpAddr,
248+
) -> AccessPointResult<(Ipv4Packet<'a>, IpAddr)> {
249+
// Allocate enough space for a new packet
250+
let mut vec: Vec<u8> = vec![0; packet.packet().len()];
251+
let mut new_packet = MutableIpv4Packet::new(&mut vec[..]).unwrap();
252+
253+
// Create a clone of the original packet
254+
new_packet.clone_from(&packet);
255+
256+
// Get source IPv4
257+
let source_ipv4: Ipv4Addr = new_packet.get_source();
258+
259+
// Construct immutable packet
260+
let ipv4_packet = new_packet.to_immutable();
261+
262+
// Set translated address
263+
let mut translated_addr = addr;
264+
265+
// Detect NAT type and translate packet
266+
let output_packet = if range.contains(&source_ipv4) {
267+
// Source NAT
268+
Self::translate_outgoing_ipv4_packet(nat, ipv4_packet)
269+
.ok_or(io::Error::new(io::ErrorKind::Other, "could not perform source NAT"))?
270+
} else {
271+
// Destination NAT
272+
let (packet, new_addr) = Self::translate_incoming_ipv4_packet(nat, ipv4_packet)
273+
.ok_or(io::Error::new(io::ErrorKind::Other, "could not perform destination NAT"))?;
274+
275+
// Set new destination address
276+
translated_addr = IpAddr::V4 (*new_addr.ip());
277+
278+
packet
279+
};
280+
281+
// Clone the translated packet
282+
let vec: Vec<u8> = vec![0; packet.packet().len()];
283+
let mut translated_packet = MutableIpv4Packet::owned(vec).unwrap();
284+
translated_packet.clone_from(&output_packet);
285+
286+
Ok ((translated_packet.consume_to_immutable(), translated_addr))
287+
}
288+
131289
/// Translate an outgoing IPv4 packet.
132290
///
133291
/// # Parameters
292+
/// - `nat` (`&mut NatTable`): the reference NAT table
134293
/// - `packet` (`Ipv4Packet`): the IPv4 packet to translate
135294
///
136295
/// # Returns
137296
/// An `Option<IPv4Packet>` with a translated source address and port number, if
138297
/// translation was successful.
139-
fn translate_outgoing_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
298+
fn translate_outgoing_ipv4_packet<'a>(
299+
nat: &'a mut NatTable,
300+
packet: Ipv4Packet<'a>
301+
) -> Option<Ipv4Packet<'a>> {
140302
// Construct a mutable IPv4 packet
141303
let mut ip_packet = MutableIpv4Packet::owned(packet.packet().to_vec())?;
142304

@@ -153,11 +315,11 @@ impl AccessPoint {
153315
let source_socket = SocketAddrV4::new(source_ipv4, source_port);
154316

155317
// Check if the IPv4 address is in the NAT table
156-
let translated_source_socket = if let Some (i) = self.nat.translate_source(source_socket) {
318+
let translated_source_socket = if let Some (i) = nat.translate_source(source_socket) {
157319
i
158320
} else {
159321
// Try to add the address to the NAT table
160-
self.nat.add(source_socket)?
322+
nat.add(source_socket)?
161323
};
162324

163325
// Extract the IPv4 address and port number from the socket
@@ -179,12 +341,16 @@ impl AccessPoint {
179341
/// Translate an incoming IPv4 packet.
180342
///
181343
/// # Parameters
344+
/// - `nat` (`&mut NatTable`): the reference NAT table
182345
/// - `packet` (`Ipv4Packet`): the IPv4 packet to translate
183346
///
184347
/// # Returns
185-
/// An `Option<IPv4Packet>` with a translated destination address and port number, if
186-
/// translation was successful.
187-
fn translate_incoming_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
348+
/// An `Option<(IPv4Packet, SocketAddrV4)>` with an IPv4 packet with translated destination
349+
/// address and port number, and the new destination, if translation was successful.
350+
fn translate_incoming_ipv4_packet<'a>(
351+
nat: &'a mut NatTable,
352+
packet: Ipv4Packet<'a>
353+
) -> Option<(Ipv4Packet<'a>, SocketAddrV4)> {
188354
// Construct a mutable IPv4 packet
189355
let mut ip_packet = MutableIpv4Packet::owned(packet.packet().to_vec())?;
190356

@@ -201,7 +367,7 @@ impl AccessPoint {
201367
let destination_socket = SocketAddrV4::new(destination_ipv4, destination_port);
202368

203369
// Check if the IPv4 address is in the NAT table
204-
let translated_destination_socket = self.nat.translate_destination(destination_socket)?;
370+
let translated_destination_socket = nat.translate_destination(destination_socket)?;
205371

206372
// Extract the IPv4 address and port number from the socket
207373
let new_ipv4 = translated_destination_socket.ip();
@@ -216,6 +382,6 @@ impl AccessPoint {
216382
// Translate the port number
217383
tcp_segment.set_destination(new_port);
218384

219-
Some (ip_packet.consume_to_immutable())
385+
Some ((ip_packet.consume_to_immutable(), translated_destination_socket))
220386
}
221387
}

‎proton_wap/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ use std::io;
1010
pub use ap::AccessPoint;
1111

1212
/// Result type for access point operations.
13-
pub type AccessPointResult = Result<(), io::Error>;
13+
pub type AccessPointResult<T> = Result<T, io::Error>;

0 commit comments

Comments
 (0)
Please sign in to comment.