Skip to content

Commit

Permalink
Add NAT capability to AccessPoint structure
Browse files Browse the repository at this point in the history
  • Loading branch information
josephrhobbs committed Jul 24, 2024
1 parent 2d59442 commit 3472d6d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion proton_wap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ edition = "2021"
name = "proton_wap"
path = "src/lib.rs"

[dependencies]
cidr = "0.2.3"

[dependencies.pnet]
version = "0.35.0"

[dependencies.proton_nat]
path = "../proton_nat"
path = "../proton_nat"
119 changes: 103 additions & 16 deletions proton_wap/src/ap.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,130 @@
//! Wireless access point abstraction.

use std::net::{
Ipv4Addr,
SocketAddrV4,
use std::{
io,
net::{
Ipv4Addr,
SocketAddrV4,
},
};

use pnet::packet::{
ipv4::{
Ipv4Packet,
MutableIpv4Packet,
use cidr::Ipv4Cidr;

use pnet::{
packet::{
ip::IpNextHeaderProtocols,
ipv4::{
Ipv4Packet,
MutableIpv4Packet,
},
tcp::{
TcpPacket,
MutableTcpPacket,
},
Packet,
MutablePacket,
},
tcp::{
TcpPacket,
MutableTcpPacket,
transport::{
TransportChannelType::Layer4,
TransportProtocol::Ipv4,
transport_channel,
ipv4_packet_iter,
},
Packet,
MutablePacket,
};

use proton_nat::NatTable;

use crate::AccessPointResult;

/// Transport channel buffer size.
pub const TRANSPORT_CHANNEL_BUFFER_SIZE: usize = 4_096;

/// A wireless access point.
pub struct AccessPoint {
/// Network Address Translation (NAT) table.
nat: NatTable,

/// CIDR network range.
range: Ipv4Cidr,
}

impl AccessPoint {
/// Constructs a new wireless access point.
///
/// # Parameters
/// - `external_ipv4` (`Ipv4Addr`): the external IPv4 address assigned
/// to this access point.
/// to this access point
/// - `range` (`Ipv4Cidr`): the internal network range associated to
/// this access point
///
/// # Returns
/// A new `AccessPoint`.
pub fn new(external_ipv4: Ipv4Addr) -> Self {
pub fn new(external_ipv4: Ipv4Addr, range: Ipv4Cidr) -> Self {
Self {
nat: NatTable::new(vec![external_ipv4]),
range,
}
}

/// Continuously route packets on the Transport Layer (OSI Layer 4).
///
/// # Parameters
/// None.
///
/// # Returns
/// An `AccessPointResult` indicating an error, if one occurred.
///
/// This function does not return if there are no errors.
pub fn run(&mut self) -> AccessPointResult {
// Create an IPv4 protocol
let protocol = Layer4 (Ipv4 (IpNextHeaderProtocols::Ipv4));

// Create a new transport protocol
let (mut tx, mut rx) = match transport_channel(TRANSPORT_CHANNEL_BUFFER_SIZE, protocol) {
Ok ((tx, rx)) => (tx, rx),
Err (_) => return Err (io::Error::new(io::ErrorKind::Other, "could not open transport channel")),
};

// We treat received packets as if they were IPv4 packets
let mut iter = ipv4_packet_iter(&mut rx);

// Continuously iterate through the packets on the receiving line
loop {
match iter.next() {
Ok ((packet, addr)) => {
// Allocate enough space for a new packet
let mut vec: Vec<u8> = vec![0; packet.packet().len()];
let mut new_packet = MutableIpv4Packet::new(&mut vec[..]).unwrap();

// Create a clone of the original packet
new_packet.clone_from(&packet);

// Get source IPv4
let source_ipv4: Ipv4Addr = new_packet.get_source();

// Construct immutable packet
let ipv4_packet = new_packet.to_immutable();

// Detect NAT type and translate packet
let translated_packet = if self.range.contains(&source_ipv4) {
// Source NAT
self.translate_outgoing_ipv4_packet(ipv4_packet)
} else {
// Destination NAT
self.translate_incoming_ipv4_packet(ipv4_packet)
}.ok_or(io::Error::new(io::ErrorKind::Other, "could not perform NAT"))?;

// Send the translated packet
if tx.send_to(translated_packet, addr).is_err() {
println!("Failed to send packet to address {}", addr);
}
}
Err (e) => {
// If an error occurs, we can handle it here
println!("Failed to route packet: {:#?}", e);
continue;
}
}
}
}

Expand All @@ -49,7 +136,7 @@ impl AccessPoint {
/// # Returns
/// An `Option<IPv4Packet>` with a translated source address and port number, if
/// translation was successful.
pub fn translate_outgoing_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
fn translate_outgoing_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
// Construct a mutable IPv4 packet
let mut ip_packet = MutableIpv4Packet::owned(packet.packet().to_vec())?;

Expand Down Expand Up @@ -97,7 +184,7 @@ impl AccessPoint {
/// # Returns
/// An `Option<IPv4Packet>` with a translated destination address and port number, if
/// translation was successful.
pub fn translate_incoming_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
fn translate_incoming_ipv4_packet(&mut self, packet: Ipv4Packet) -> Option<Ipv4Packet> {
// Construct a mutable IPv4 packet
let mut ip_packet = MutableIpv4Packet::owned(packet.packet().to_vec())?;

Expand Down
7 changes: 6 additions & 1 deletion proton_wap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@

mod ap;

pub use ap::AccessPoint;
use std::io;

pub use ap::AccessPoint;

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

0 comments on commit 3472d6d

Please sign in to comment.