generated from ewpratten/rust-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
protomask-clat.rs
157 lines (140 loc) · 5.78 KB
/
protomask-clat.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Entrypoint for the `protomask-clat` binary.
//!
//! This binary is a Customer-side transLATor (CLAT) that translates all native
//! IPv4 traffic to IPv6 traffic for transmission over an IPv6-only ISP network.
use crate::common::packet_handler::{
get_ipv4_src_dst, get_ipv6_src_dst, get_layer_3_proto, handle_translation_error,
PacketHandlingError,
};
use crate::common::profiler::start_puffin_server;
use crate::{args::protomask_clat::Args, common::permissions::ensure_root};
use clap::Parser;
use common::logging::enable_logger;
use easy_tun::Tun;
use interproto::protocols::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4};
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use rfc6052::{embed_ipv4_addr_unchecked, extract_ipv4_addr_unchecked};
use std::io::{Read, Write};
use std::sync::Arc;
mod args;
mod common;
#[tokio::main]
pub async fn main() {
// Parse CLI args
let args = Args::parse();
// Initialize logging
enable_logger(args.verbose);
// Load config data
let config = args.data().unwrap();
// We must be root to continue program execution
ensure_root();
// Start profiling
#[allow(clippy::let_unit_value)]
let _server = start_puffin_server(&args.profiler_args);
// Bring up a TUN interface
let tun = Arc::new(Tun::new(&args.interface, config.num_queues).unwrap());
// Get the interface index
let rt_handle = rtnl::new_handle().unwrap();
let tun_link_idx = rtnl::link::get_link_index(&rt_handle, tun.name())
.await
.unwrap()
.unwrap();
// Bring the interface up
rtnl::link::link_up(&rt_handle, tun_link_idx).await.unwrap();
// Add an IPv4 default route towards the interface
rtnl::route::route_add(IpNet::V4(Ipv4Net::default()), &rt_handle, tun_link_idx)
.await
.unwrap();
// Add an IPv6 route for each customer prefix
for customer_prefix in config.customer_pool {
let embedded_customer_prefix = unsafe {
Ipv6Net::new(
embed_ipv4_addr_unchecked(customer_prefix.addr(), config.embed_prefix),
config.embed_prefix.prefix_len() + customer_prefix.prefix_len(),
)
.unwrap_unchecked()
};
log::debug!(
"Adding route for {} to {}",
embedded_customer_prefix,
tun.name()
);
rtnl::route::route_add(
IpNet::V6(embedded_customer_prefix),
&rt_handle,
tun_link_idx,
)
.await
.unwrap();
}
// If we are configured to serve prometheus metrics, start the server
if let Some(bind_addr) = config.prom_bind_addr {
log::info!("Starting prometheus server on {}", bind_addr);
tokio::spawn(protomask_metrics::http::serve_metrics(bind_addr));
}
// Translate all incoming packets
log::info!("Translating packets on {}", tun.name());
let mut worker_threads = Vec::new();
for queue_id in 0..config.num_queues {
let tun = Arc::clone(&tun);
worker_threads.push(std::thread::spawn(move || {
log::debug!("Starting worker thread for queue {}", queue_id);
let mut buffer = vec![0u8; 1500];
loop {
// Indicate to the profiler that we are starting a new packet
profiling::finish_frame!();
profiling::scope!("packet");
// Read a packet
let len = tun.fd(queue_id).unwrap().read(&mut buffer).unwrap();
// Translate it based on the Layer 3 protocol number
let translation_result: Result<Option<Vec<u8>>, PacketHandlingError> =
match get_layer_3_proto(&buffer[..len]) {
Some(4) => {
let (source, dest) = get_ipv4_src_dst(&buffer[..len]);
translate_ipv4_to_ipv6(
&buffer[..len],
unsafe { embed_ipv4_addr_unchecked(source, config.embed_prefix) },
unsafe { embed_ipv4_addr_unchecked(dest, config.embed_prefix) },
)
.map(Some)
.map_err(PacketHandlingError::from)
}
Some(6) => {
let (source, dest) = get_ipv6_src_dst(&buffer[..len]);
translate_ipv6_to_ipv4(
&buffer[..len],
unsafe {
extract_ipv4_addr_unchecked(
source,
config.embed_prefix.prefix_len(),
)
},
unsafe {
extract_ipv4_addr_unchecked(
dest,
config.embed_prefix.prefix_len(),
)
},
)
.map(Some)
.map_err(PacketHandlingError::from)
}
Some(proto) => {
log::warn!("Unknown Layer 3 protocol: {}", proto);
continue;
}
None => {
continue;
}
};
// Handle any errors and write
if let Some(output) = handle_translation_error(translation_result) {
tun.fd(queue_id).unwrap().write_all(&output).unwrap();
}
}
}));
}
for worker in worker_threads {
worker.join().unwrap();
}
}