\d):(?P[[:xdigit:]]{0,64}):(?P[[:print:]]{16,256})$")
137 | .unwrap();
138 | }
139 | // capture content using the pre-compiled regular expression
140 | REGEX_HTBODY_AUTH_AV.captures(acc)
141 | }
142 |
--------------------------------------------------------------------------------
/src/api/client/sessions/mod.rs:
--------------------------------------------------------------------------------
1 | //! Client API - sessions support module
2 |
3 | // session authentication
4 | pub mod auth;
5 |
6 | // session token
7 | pub mod token;
8 |
--------------------------------------------------------------------------------
/src/api/client/sessions/token.rs:
--------------------------------------------------------------------------------
1 | //! Client API - session token module
2 |
3 | // std
4 | use std::thread;
5 | use std::time::SystemTime;
6 |
7 | // rand
8 | use rand::Rng;
9 |
10 | // hmac
11 | use hmac::{Hmac, Mac};
12 |
13 | // sha3
14 | use sha3::Sha3_256;
15 |
16 | // time
17 | use std::time;
18 |
19 | // config
20 | use crate::config;
21 |
22 | /// SessionTroken structure
23 | pub struct SessionToken {
24 | user: String,
25 | ts_since: u64,
26 | ts_valid: u64,
27 | nonce: u64,
28 | token: String,
29 | secure: bool,
30 | }
31 |
32 | /// SessionToken implementation
33 | impl SessionToken {
34 | // new() method
35 | pub fn new() -> SessionToken {
36 | SessionToken {
37 | user: "null".to_string(),
38 | ts_since: 0,
39 | ts_valid: 0,
40 | nonce: 0,
41 | token: "null".to_string(),
42 | secure: false,
43 | }
44 | }
45 | // set_user() setter
46 | pub fn set_user(&mut self, user: String) {
47 | self.user = user;
48 | }
49 | // set_tssince() setter
50 | pub fn set_tssince(&mut self, ts: u64) {
51 | self.ts_since = ts;
52 | }
53 | // set_validfor() method
54 | pub fn set_validfor(&mut self, duration: u64) -> u64 {
55 | self.ts_valid = self.ts_since + duration;
56 | self.ts_valid
57 | }
58 | // set_nonce() setter
59 | pub fn set_nonce(&mut self, nonce: u64) {
60 | self.nonce = nonce;
61 | }
62 | // set_token() setter
63 | pub fn set_token(&mut self, token: String) {
64 | self.token = token;
65 | }
66 | // gen_token() method
67 | pub fn gen_token(&mut self, cfg: &config::CConfig) -> std::io::Result<()> {
68 | // get current system time (in seconds since the Unix Epoch)
69 | let now = SystemTime::now();
70 | let time = now
71 | .duration_since(SystemTime::UNIX_EPOCH)
72 | .unwrap()
73 | .as_secs();
74 | // set current time to 'since' timestamp
75 | self.ts_since = time;
76 | // generate a random number of 64 bits
77 | let mut rng = rand::thread_rng();
78 | let nonce: u64 = rng.gen();
79 | // set the nonce
80 | self.nonce = nonce;
81 | // concatenate the user and time with the nonce
82 | let utn = format!("{}{}{}", self.user, self.ts_since, self.nonce);
83 | // hash the above elements
84 | let secret = cfg.api.as_ref().unwrap().secret();
85 | let token = gen_hmac_string(&utn, secret);
86 | // set the hashed token
87 | self.token = token;
88 | // set the token's 'secure' flag if tls is enabled
89 | if cfg.api.as_ref().unwrap().tls() {
90 | self.secure = true;
91 | }
92 | // confirm completion
93 | Ok(())
94 | }
95 | // user() method
96 | pub fn user(&self) -> String {
97 | self.user.clone()
98 | }
99 | // ts_since() method
100 | pub fn ts_since(&self) -> String {
101 | format!("{}", self.ts_since)
102 | }
103 | // nonce() method
104 | pub fn nonce(&self) -> String {
105 | format!("{}", self.nonce)
106 | }
107 | // token() method
108 | pub fn token(&self) -> String {
109 | self.token.clone()
110 | }
111 | // secure() method
112 | pub fn secure(&self) -> bool {
113 | self.secure
114 | }
115 | // validate() method
116 | // check the integrity of the token
117 | pub fn validate(&self, cfg: &config::CConfig) -> Option {
118 | // concatenate the user and time with the nonce
119 | let utn = format!("{}{}{}", self.user, self.ts_since, self.nonce);
120 | // hash the above elements
121 | let secret = cfg.api.as_ref().unwrap().secret();
122 | let token = gen_hmac_string(&utn, secret);
123 | // compare the stored (or passed) hash with the recomputed hash/token above
124 | // make sure the comparison time is randomized or constant to avoid timing attacks
125 | let mut rng = rand::thread_rng();
126 | let rdelay = rng.gen_range(10, 40);
127 | let time = time::Duration::from_millis(rdelay);
128 | thread::sleep(time);
129 | // return the token if they match
130 | if self.token == token {
131 | Some(token)
132 | } else {
133 | None
134 | }
135 | }
136 | }
137 |
138 | // type alias
139 | type HmacSha3_256 = Hmac;
140 |
141 | // gen_hmac_string() function
142 | fn gen_hmac_string(input: &String, secret: String) -> String {
143 | let mut mac = HmacSha3_256::new_varkey(secret.as_bytes()).expect("invalid key length");
144 | mac.input(input.as_bytes());
145 | let res = mac.result();
146 | let out = res.code();
147 |
148 | format!("{:x}", out)
149 | }
150 |
--------------------------------------------------------------------------------
/src/api/mod.rs:
--------------------------------------------------------------------------------
1 | //! Application Programming Interface (API) module
2 |
3 | // client API
4 | pub mod client;
5 |
--------------------------------------------------------------------------------
/src/auth.rs:
--------------------------------------------------------------------------------
1 | //! authentication module
2 | use super::*;
3 |
4 | // hmac
5 | use hmac::digest::{ExtendableOutput, XofReader};
6 | use hmac::{digest::Input, Hmac, Mac};
7 |
8 | // sha2
9 | extern crate sha2;
10 | use sha2::Sha256;
11 |
12 | // sha3
13 | extern crate sha3;
14 | use sha3::Shake256;
15 |
16 | // type aliases
17 | type HmacSha256 = Hmac;
18 |
19 | // gen_auth_data() function
20 | pub fn gen_auth_data(autht: u8, secret: &Option, msg: Option<&[u8]>) -> Vec {
21 | match autht {
22 | // AUTH_TYPE_SIMPLE (RFC2338 Type-1 Plain)
23 | AUTH_TYPE_SIMPLE => match secret {
24 | Some(s) => {
25 | let data = format!("{:\0<8}", s);
26 | return data.into_bytes();
27 | }
28 | None => {
29 | let mut data: Vec = Vec::new();
30 | for _ in 0..8 {
31 | data.push(0);
32 | }
33 | return data;
34 | }
35 | },
36 | // AUTH_TYPE_P0 (PROPRIETARY-TRUNCATED-8B-SHA256)
37 | // This is a proprietary type for a HMAC producing fixed outputs of only 8
38 | // bytes, which should be resistant enough to thwart most protocol attacks.
39 | AUTH_TYPE_P0 => {
40 | let mut data: Vec = Vec::new();
41 | // hmac secret key
42 | let key = match secret {
43 | Some(s) => s,
44 | None => "",
45 | };
46 | // create HMAC-SHA256 instance
47 | let mut mac = HmacSha256::new_varkey(key.as_bytes()).expect("invalid key size");
48 | // input message (if any)
49 | match msg {
50 | Some(m) => mac.input(m),
51 | None => panic!("cannot perform authentication without message"),
52 | }
53 | // get computation result
54 | for b in mac.result().code().iter() {
55 | data.push(*b);
56 | }
57 | // truncat data to 8 bytes
58 | data.truncate(8);
59 | // return data
60 | return data;
61 | }
62 | // AUTH_TYPE_P1 (PROPRIETARY-XOF-8B-SHAKE256)
63 | // This is an internal, proprietary type using the SHAKE256 XOF
64 | AUTH_TYPE_P1 => {
65 | // secret key
66 | let key = match secret {
67 | Some(s) => s.as_bytes(),
68 | None => "".as_bytes(),
69 | };
70 | // create SHAKE256 instance
71 | let mut hasher = Shake256::default();
72 | // feed the hasher
73 | match msg {
74 | Some(m) => {
75 | let km = [key, m].concat();
76 | hasher.input(km);
77 | }
78 | None => panic!("cannot perform authentication without message"),
79 | }
80 | // read result
81 | let mut reader = hasher.xof_result();
82 | let mut data = [0u8; 8];
83 | reader.read(&mut data);
84 | return data.to_vec();
85 | }
86 | // no authentication
87 | _ => {
88 | let mut data: Vec = Vec::new();
89 | for _ in 0..8 {
90 | data.push(0);
91 | }
92 | return data;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/bin/rvrrpd.rs:
--------------------------------------------------------------------------------
1 | //! # rVRRPd
2 | //!
3 | //! `rVRRPd` is aimed to be a fast, secure and multi-platform VRRPv2 implementation.
4 | extern crate rVRRPd;
5 | use rVRRPd::{listen_ip_pkts, Config};
6 |
7 | // getopts
8 | use getopts::Options;
9 |
10 | // std
11 | use std::env;
12 | use std::error::Error;
13 |
14 | // ctrlc (linux signal handling)
15 | extern crate ctrlc;
16 |
17 | /// MyError Type
18 | #[derive(Debug)]
19 | struct MyError(String);
20 |
21 | impl std::fmt::Display for MyError {
22 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23 | write!(f, "{}", self.0)
24 | }
25 | }
26 | impl Error for MyError {}
27 |
28 | // print_usage() function
29 | fn print_usage(program: &str, opts: Options) {
30 | let modes = format!(
31 | "\
32 | Modes:
33 | 0 = VRRPv2 Sniffer
34 | 1 = VRRPv2 Virtual Router (foreground)
35 | 2 = VRRPv2 Virtual Router (daemon)\
36 | "
37 | );
38 | let usage = format!("Usage: {} -m0|1|2 [options]\n\n{}", program, modes);
39 | print!("{}", opts.usage(&usage));
40 | }
41 |
42 | // parse_cl_opts() function
43 | fn parse_cli_opts(args: &[String]) -> Result> {
44 | let program = args[0].clone();
45 | let mut opts = Options::new();
46 |
47 | opts.optflag("h", "help", "display help information");
48 | opts.optopt(
49 | "i",
50 | "iface",
51 | "ethernet interface to listen on (sniffer mode)",
52 | "INTERFACE",
53 | );
54 | opts.optopt(
55 | "m",
56 | "mode",
57 | "operation modes (see Modes):\n 0(sniffer), 1(foreground), 2(daemon)",
58 | "MODE",
59 | );
60 | opts.optopt(
61 | "c",
62 | "conf",
63 | "path to configuration file:\n (default to /etc/rvrrpd/rvrrpd.conf)",
64 | "FILE",
65 | );
66 | opts.optopt(
67 | "d",
68 | "debug",
69 | "debugging level:\n0(none), 1(low), 2(medium), 3(high), 5(extensive)",
70 | "LEVEL",
71 | );
72 | opts.optopt(
73 | "g",
74 | "cfg-format",
75 | "configuration format: toml(default), json",
76 | "FORMAT",
77 | );
78 |
79 | let matches = match opts.parse(&args[1..]) {
80 | Ok(m) => m,
81 | Err(f) => return Result::Err(Box::new(MyError(f.to_string().into()))),
82 | };
83 |
84 | // help command-line option
85 | if matches.opt_present("help") || args[1..].is_empty() {
86 | print_usage(&program, opts);
87 | std::process::exit(1);
88 | }
89 |
90 | // mode command-line option
91 | let mode = matches.opt_str("mode");
92 | let mode = match mode {
93 | Some(x) => x.parse::().unwrap(),
94 | None => {
95 | return Result::Err(Box::new(MyError("No operation mode specified (-m)".into())));
96 | }
97 | };
98 |
99 | // iface command-line option
100 | let iface = matches.opt_str("iface");
101 | let iface = match iface {
102 | Some(x) => Option::Some(x.parse::().unwrap()),
103 | None => {
104 | if mode == 0 {
105 | return Result::Err(Box::new(MyError("No interface specified (-i)".into())));
106 | }
107 | Option::None
108 | }
109 | };
110 |
111 | // config command-line option
112 | let conf = matches.opt_str("conf");
113 | let conf = match conf {
114 | Some(x) => Option::Some(x.parse::().unwrap()),
115 | None => Option::None,
116 | };
117 |
118 | // debug level command-line option
119 | let debug = matches.opt_str("debug");
120 | let debug = match debug {
121 | Some(x) => Option::Some(x.parse::().unwrap()),
122 | None => None,
123 | };
124 |
125 | // configuration file format command-line option
126 | let cfg_format = matches.opt_str("cfg-format");
127 | let cfg_format = match cfg_format {
128 | Some(x) => Option::Some(x.parse::().unwrap()),
129 | None => Option::None,
130 | };
131 |
132 | Ok(Config::new(iface, mode, conf, debug, cfg_format))
133 | }
134 |
135 | // run() function
136 | fn run(cfg: Config) -> Result<(), Box> {
137 | // print information
138 | println!("Starting rVRRPd");
139 |
140 | // Listen to IP packets
141 | match listen_ip_pkts(&cfg) {
142 | Ok(_) => Ok(()),
143 | Err(e) => {
144 | return Result::Err(Box::new(MyError(
145 | format!("A runtime error occured: {}", e).into(),
146 | )));
147 | }
148 | }
149 | }
150 |
151 | // main() function
152 | fn main() {
153 | let args: Vec = env::args().collect();
154 |
155 | match parse_cli_opts(&args) {
156 | // error while parsing cli options
157 | Err(e) => {
158 | eprintln!("{}", e);
159 | std::process::exit(1);
160 | }
161 | // if a configuration is returned from the parser
162 | Ok(c) => match run(c) {
163 | Err(e) => {
164 | eprintln!("{}", e);
165 | std::process::exit(1);
166 | }
167 | Ok(_) => {
168 | std::process::exit(0);
169 | }
170 | },
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/checksums.rs:
--------------------------------------------------------------------------------
1 | //! checksums related functions module
2 | //! This module is dedicated to internet checksums functions.
3 | //!
4 | //! credit to ref. impl. https://github.com/m-labs/smoltcp/blob/master/src/wire/ip.rs
5 | use byteorder::{ByteOrder, NetworkEndian};
6 |
7 | const RFC1071_CHUNK_SIZE: usize = 32;
8 |
9 | // rfc1071() function
10 | /// compute rfc1071 internet checksum
11 | /// returns all-ones if carried checksum is valid
12 | pub fn rfc1071(mut data: &[u8]) -> u16 {
13 | let mut acc = 0;
14 |
15 | // for each 32 bytes chunk
16 | while data.len() >= RFC1071_CHUNK_SIZE {
17 | let mut d = &data[..RFC1071_CHUNK_SIZE];
18 | while d.len() >= 2 {
19 | // sum adjacent pairs converted to 16 bits integer
20 | acc += NetworkEndian::read_u16(d) as u32;
21 | // take the next 2 bytes for the next iteration
22 | d = &d[2..];
23 | }
24 | data = &data[RFC1071_CHUNK_SIZE..];
25 | }
26 |
27 | // if it does not fit a 32 bytes chunk
28 | while data.len() >= 2 {
29 | acc += NetworkEndian::read_u16(data) as u32;
30 | data = &data[2..];
31 | }
32 |
33 | // add odd byte is present
34 | if let Some(&v) = data.first() {
35 | acc += (v as u32) << 8;
36 | }
37 |
38 | propagate_carries(acc)
39 | }
40 |
41 | // propagate final complement?
42 | pub fn propagate_carries(word: u32) -> u16 {
43 | let sum = (word >> 16) + (word & 0xffff);
44 | ((sum >> 16) as u16) + (sum as u16)
45 | }
46 |
47 | // one_complement_sum() function
48 | /// returns all-zeros if checksum is valid
49 | pub fn one_complement_sum(data: &[u8], pos: Option) -> u16 {
50 | let mut sum = 0u32;
51 | let mut idx = 0;
52 |
53 | while idx < data.len() {
54 | match pos {
55 | // if a position is given:
56 | Some(p) => {
57 | if idx == p {
58 | idx = p + 2; // skip 2 bytes
59 | }
60 | // if we reach the end of slice, we are done
61 | if idx == data.len() {
62 | break;
63 | }
64 | }
65 | None => (),
66 | }
67 | let word = (data[idx] as u32) << 8 | data[idx + 1] as u32;
68 | sum = sum + word;
69 | idx = idx + 2;
70 | }
71 |
72 | while sum >> 16 != 0 {
73 | sum = (sum >> 16) + (sum & 0xFFFF);
74 | }
75 |
76 | !sum as u16
77 | }
78 |
--------------------------------------------------------------------------------
/src/config.rs:
--------------------------------------------------------------------------------
1 | //! configuration file handling module
2 | //! This module provides structure and methods related to configuration file handling.
3 | use super::*;
4 |
5 | // std
6 | use std::net::IpAddr;
7 |
8 | // rand
9 | use rand::Rng;
10 |
11 | /// CfgType Enumerator
12 | pub enum CfgType {
13 | Toml, // TOML
14 | Json, // JSON
15 | }
16 |
17 | /// Main Configuration Structure
18 | #[derive(Debug, Deserialize, Serialize, Clone)]
19 | pub struct CConfig {
20 | pub debug: Option,
21 | pub time_zone: Option,
22 | pub time_format: Option,
23 | pub pid: Option,
24 | pub working_dir: Option,
25 | pub main_log: Option,
26 | pub error_log: Option,
27 | pub vrouter: Option>,
28 | pub protocols: Option,
29 | pub client_api: Option,
30 | pub api: Option,
31 | }
32 |
33 | impl CConfig {
34 | // debug() getter
35 | pub fn debug(&self) -> u8 {
36 | match self.debug {
37 | Some(v) => v,
38 | None => DEBUG_LEVEL_INFO,
39 | }
40 | }
41 | // time_zone() getter
42 | pub fn time_zone(&self) -> u8 {
43 | match &self.time_zone {
44 | Some(s) => match &s[..] {
45 | "local" => 0,
46 | "utc" => 1,
47 | _ => 0,
48 | },
49 | None => 0,
50 | }
51 | }
52 | // time_format() getter
53 | pub fn time_format(&self) -> u8 {
54 | match &self.time_format {
55 | Some(s) => match &s[..] {
56 | "disabled" => 0,
57 | "short" => 1,
58 | "rfc2822" => 2,
59 | _ => 0,
60 | },
61 | None => 0,
62 | }
63 | }
64 | // pid() getter
65 | pub fn pid(&self) -> String {
66 | match &self.pid {
67 | Some(v) => v.clone(),
68 | None => RVRRPD_DFLT_PIDFILE.to_string(),
69 | }
70 | }
71 | // working_dir() getter
72 | pub fn working_dir(&self) -> String {
73 | match &self.working_dir {
74 | Some(v) => v.clone(),
75 | None => RVRRPD_DFLT_WORKDIR.to_string(),
76 | }
77 | }
78 | // main_log() getter
79 | pub fn main_log(&self) -> String {
80 | match &self.main_log {
81 | Some(v) => v.clone(),
82 | None => RVRRPD_DFLT_LOGFILE.to_string(),
83 | }
84 | }
85 | // error_log() getter
86 | pub fn error_log(&self) -> String {
87 | match &self.error_log {
88 | Some(v) => v.clone(),
89 | None => RVRRPD_DFLT_ELOGFILE.to_string(),
90 | }
91 | }
92 | // client_api() method
93 | pub fn client_api(&self) -> bool {
94 | match &self.client_api {
95 | Some(s) => match &s[..] {
96 | "http" => true,
97 | _ => true,
98 | },
99 | None => false,
100 | }
101 | }
102 | }
103 |
104 | /// Virtual-Routers Configuration Structure
105 | #[derive(Debug, Deserialize, Serialize, Clone)]
106 | pub struct VRConfig {
107 | group: u8,
108 | interface: String,
109 | vip: Option,
110 | priority: Option,
111 | preemption: Option,
112 | auth_type: Option,
113 | auth_secret: Option,
114 | timers: Option,
115 | rfc3768: Option,
116 | netdrv: Option,
117 | iftype: Option,
118 | vifname: Option,
119 | socket_filter: Option,
120 | }
121 | impl VRConfig {
122 | // group() getter
123 | pub fn group(&self) -> u8 {
124 | if self.group < 1 {
125 | panic!("error(config): Please configure a group id between 1 and 255")
126 | }
127 | self.group
128 | }
129 | // interface() getter
130 | pub fn interface(&self) -> &String {
131 | &self.interface
132 | }
133 | // vip() getter
134 | pub fn vip(&self) -> [u8; 4] {
135 | match &self.vip {
136 | Some(ip) => match ip.parse::().unwrap() {
137 | IpAddr::V4(ip) => ip.octets(),
138 | IpAddr::V6(_ipv6) => panic!("error(config): Only IPv4 addresses are supported"),
139 | },
140 | None => panic!("error(config): No virtual IP specified"),
141 | }
142 | }
143 | // timer_advert() getter
144 | pub fn timer_advert(&self) -> u8 {
145 | match &self.timers {
146 | Some(t) => t.advert,
147 | None => 1,
148 | }
149 | }
150 | // priority() getter
151 | pub fn priority(&self) -> u8 {
152 | match self.priority {
153 | Some(v) => {
154 | if v < 1 || v > 254 {
155 | panic!("error(config): Please configure a priority between 1 and 254");
156 | }
157 | v
158 | }
159 | None => VRRP_V2_DEFAULT_PRIORITY,
160 | }
161 | }
162 | // preemption() getter
163 | pub fn preemption(&self) -> bool {
164 | match self.preemption {
165 | Some(b) => b,
166 | None => false,
167 | }
168 | }
169 | // auth_type() method
170 | pub fn auth_type(&self) -> u8 {
171 | match &self.auth_type {
172 | Some(s) => match &s[..] {
173 | "rfc2338-simple" => AUTH_TYPE_SIMPLE,
174 | "p0-t8-sha256" => AUTH_TYPE_P0,
175 | "p1-b8-shake256" => AUTH_TYPE_P1,
176 | _ => panic!("error(config): authentication type {} is not supported", s),
177 | },
178 | None => 0,
179 | }
180 | }
181 | // auth_secret() method
182 | pub fn auth_secret(&self) -> Option {
183 | match &self.auth_secret {
184 | Some(cs) => match self.auth_type() {
185 | // if type-1, then truncate to 8 bytes
186 | 1 => {
187 | let mut s = cs.clone();
188 | s.truncate(8);
189 | Option::Some(s)
190 | }
191 | _ => {
192 | let s = cs.clone();
193 | Option::Some(s)
194 | }
195 | },
196 | None => Option::None,
197 | }
198 | }
199 | // rfc3768() getter
200 | pub fn rfc3768(&self) -> bool {
201 | // if auth_type is 'p0-t8-sha256', or 'p1-b8-shake256',
202 | // overwrite rfc3768 compatibility flag
203 | match &self.auth_type {
204 | Some(t) => match &t[..] {
205 | "p0-t8-sha256" | "p1-b8-shake256" => {
206 | println!(
207 | "warning(config): authentication type {} is enabled, forcing rfc3768 compatibility.",
208 | t
209 | );
210 | return true;
211 | }
212 | _ => {}
213 | },
214 | None => {}
215 | }
216 | match self.rfc3768 {
217 | Some(b) => b,
218 | None => true,
219 | }
220 | }
221 | // netdrv() method
222 | pub fn netdrv(&self) -> NetDrivers {
223 | // if os is Linux
224 | if cfg!(target_os = "linux") {
225 | // if macvlan is configured, return libnl
226 | match self.iftype() {
227 | IfTypes::macvlan => return NetDrivers::libnl,
228 | _ => {}
229 | }
230 | match &self.netdrv {
231 | Some(s) => match &s[..] {
232 | "ioctl" => NetDrivers::ioctl,
233 | _ => NetDrivers::libnl,
234 | },
235 | None => NetDrivers::libnl,
236 | }
237 | }
238 | // unspecified OSes
239 | else {
240 | NetDrivers::ioctl
241 | }
242 | }
243 | // iftype() method
244 | pub fn iftype(&self) -> IfTypes {
245 | // if os is Linux
246 | if cfg!(target_os = "linux") {
247 | match &self.iftype {
248 | Some(s) => match &s[..] {
249 | "macvlan" => IfTypes::macvlan,
250 | _ => IfTypes::ether,
251 | },
252 | None => IfTypes::ether,
253 | }
254 | } else {
255 | IfTypes::ether
256 | }
257 | }
258 | // vifname() method
259 | pub fn vifname(&self) -> String {
260 | match &self.vifname {
261 | Some(s) => s.clone(),
262 | None => format!("{}{}", RVRRPD_DFLT_MACVLAN_NAME, self.group),
263 | }
264 | }
265 | // socket_filter() method
266 | pub fn socket_filter(&self) -> bool {
267 | match self.socket_filter {
268 | Some(b) => b,
269 | None => true,
270 | }
271 | }
272 | }
273 |
274 | /// Timers Option Type
275 | #[derive(Debug, Deserialize, Serialize, Clone)]
276 | struct Timers {
277 | advert: u8,
278 | }
279 | impl Default for Timers {
280 | fn default() -> Self {
281 | Timers { advert: 1 }
282 | }
283 | }
284 |
285 | /// Protocols Option Type
286 | #[derive(Debug, Deserialize, Serialize, Clone)]
287 | pub struct Protocols {
288 | pub r#static: Option>,
289 | }
290 |
291 | /// Static Option Type
292 | #[derive(Debug, Deserialize, Serialize, Clone)]
293 | pub struct Static {
294 | route: String,
295 | mask: String,
296 | nh: String,
297 | metric: Option,
298 | mtu: Option,
299 | }
300 |
301 | // Static Option Implementation
302 | impl Static {
303 | // route() getter
304 | // convert IPv4 String to array of four 8-bits unsigned integers
305 | pub fn route(&self) -> [u8; 4] {
306 | match self.route.parse::().unwrap() {
307 | IpAddr::V4(ip) => ip.octets(),
308 | IpAddr::V6(_ipv6) => panic!("error(config-static): Only IPv4 routes are supported"),
309 | }
310 | }
311 | // mask() getter
312 | pub fn mask(&self) -> [u8; 4] {
313 | match self.mask.parse::().unwrap() {
314 | IpAddr::V4(ip) => ip.octets(),
315 | IpAddr::V6(_ipv6) => panic!("error(config-static): Only IPv4 masks are supported"),
316 | }
317 | }
318 | // nh() getter
319 | pub fn nh(&self) -> [u8; 4] {
320 | match self.nh.parse::().unwrap() {
321 | IpAddr::V4(ip) => ip.octets(),
322 | IpAddr::V6(_ipv6) => panic!("error(config-static): Only IPv4 next-hops are supported"),
323 | }
324 | }
325 | // metric() getter
326 | pub fn metric(&self) -> i16 {
327 | match self.metric {
328 | Some(v) => v as i16,
329 | None => 0,
330 | }
331 | }
332 | // mtu() getter
333 | pub fn mtu(&self) -> u64 {
334 | match self.mtu {
335 | Some(v) => v as u64,
336 | None => 0,
337 | }
338 | }
339 | }
340 |
341 | // decode_config() function
342 | /// read and decode configuration file
343 | pub fn decode_config(filename: String, cfgtype: CfgType) -> CConfig {
344 | let file: std::string::String = match std::fs::read_to_string(filename) {
345 | Ok(s) => s,
346 | Err(e) => {
347 | eprintln!(
348 | "error(config): Cannot read rVRRPd configuration file: {}",
349 | e
350 | );
351 | std::process::exit(1);
352 | }
353 | };
354 | match cfgtype {
355 | // TOML
356 | CfgType::Toml => {
357 | let config: CConfig = match toml::from_str(&file) {
358 | Ok(c) => c,
359 | Err(e) => {
360 | eprintln!("error(config): Cannot parse TOML configuration file: {}", e);
361 | std::process::exit(1);
362 | }
363 | };
364 | return config;
365 | }
366 | // JSON
367 | CfgType::Json => {
368 | let config: CConfig = match serde_json::from_str(&file) {
369 | Ok(c) => c,
370 | Err(e) => {
371 | eprintln!("error(config): Cannot parse JSON configuration file: {}", e);
372 | std::process::exit(1);
373 | }
374 | };
375 | return config;
376 | }
377 | }
378 | }
379 |
380 | /// API structure
381 | #[derive(Debug, Deserialize, Serialize, Clone)]
382 | pub struct API {
383 | users: Vec,
384 | secret: Option,
385 | host: Option,
386 | tls: Option,
387 | tls_key: Option,
388 | tls_cert: Option,
389 | }
390 |
391 | // API structure implementation
392 | impl API {
393 | // users() method
394 | pub fn users(&self) -> Vec {
395 | self.users.clone()
396 | }
397 | // secret() method
398 | pub fn secret(&self) -> String {
399 | let secret = match &self.secret {
400 | Some(s) => s.clone(),
401 | None => gen_runtime_secret(),
402 | };
403 |
404 | secret
405 | }
406 | // host() method
407 | pub fn host(&self) -> String {
408 | match &self.host {
409 | Some(s) => s.clone(),
410 | None => "0.0.0.0:7080".to_string(),
411 | }
412 | }
413 | // tls() method
414 | pub fn tls(&self) -> bool {
415 | match self.tls {
416 | Some(b) => b,
417 | None => false,
418 | }
419 | }
420 | // tls_key() method
421 | pub fn tls_key(&self) -> String {
422 | match &self.tls_key {
423 | Some(s) => s.clone(),
424 | None => RVRRPD_CFG_DFLT_TLSKEY.to_string(),
425 | }
426 | }
427 | // tls_cert() method
428 | pub fn tls_cert(&self) -> String {
429 | match &self.tls_cert {
430 | Some(s) => s.clone(),
431 | None => RVRRPD_CFG_DFLT_TLSCERT.to_string(),
432 | }
433 | }
434 | }
435 |
436 | // gen_runtime_secret() function
437 | fn gen_runtime_secret() -> String {
438 | // create a static secret key string
439 | lazy_static! {
440 | static ref SECRET: String = {
441 | // generate a unique runtime secret
442 | let mut rng = rand::thread_rng();
443 | let r = rng.gen::();
444 | format!("{:x}", r)
445 | };
446 | }
447 | SECRET.to_string()
448 | }
449 |
--------------------------------------------------------------------------------
/src/constants.rs:
--------------------------------------------------------------------------------
1 | //! Constants module
2 | //! This module regroups all the program's and protocols constants.
3 |
4 | // Program Constants
5 | pub const RVRRPD_DFLT_CFG_FILE: &str = "/etc/rvrrpd/rvrrpd.conf";
6 | pub const RVRRPD_DFLT_PIDFILE: &str = "/var/run/rvrrpd.pid";
7 | pub const RVRRPD_DFLT_WORKDIR: &str = "/tmp";
8 | pub const RVRRPD_DFLT_LOGFILE: &str = "/var/log/rvrrpd.log";
9 | pub const RVRRPD_DFLT_ELOGFILE: &str = "/var/log/rvrrpd-error.log";
10 | pub const RVRRPD_DFLT_DATE_FORMAT: &str = "%b %e %Y %T";
11 | pub const RVRRPD_DFLT_MACVLAN_NAME: &str = "standby";
12 | pub const RVRRPD_DFLT_CLIENT_API: &str = "disabled";
13 | pub const RVRRPD_VERSION_STRING: &str = "0.1.3";
14 |
15 | // Config Constants
16 | pub const RVRRPD_CFG_DFLT_TLSKEY: &str = "/etc/rvrrpd/ssl/key.pem";
17 | pub const RVRRPD_CFG_DFLT_TLSCERT: &str = "/etc/rvrrpd/ssl/cert.pem";
18 |
19 | // Debug Constants
20 | pub const DEBUG_LEVEL_INFO: u8 = 0;
21 | pub const DEBUG_LEVEL_LOW: u8 = 1;
22 | pub const DEBUG_LEVEL_MEDIUM: u8 = 2;
23 | pub const DEBUG_LEVEL_HIGH: u8 = 3;
24 | pub const DEBUG_LEVEL_EXTENSIVE: u8 = 5;
25 | pub const DEBUG_SRC_INFO: &str = "info";
26 | pub const DEBUG_SRC_PROTO: &str = "protocols";
27 | pub const DEBUG_SRC_VR: &str = "vr";
28 | pub const DEBUG_SRC_MAIN: &str = "main";
29 | pub const DEBUG_SRC_MAC: &str = "mac";
30 | pub const DEBUG_SRC_ROUTE: &str = "route";
31 | pub const DEBUG_SRC_PACKET: &str = "packet";
32 | pub const DEBUG_SRC_ARP: &str = "arp";
33 | pub const DEBUG_SRC_THREAD: &str = "thread";
34 | pub const DEBUG_SRC_THREADP: &str = "thread-pool";
35 | pub const DEBUG_SRC_FSM: &str = "fsm";
36 | pub const DEBUG_SRC_WORKER: &str = "worker";
37 | pub const DEBUG_SRC_WORKERG: &str = "worker-reg";
38 | pub const DEBUG_SRC_TIMER: &str = "timer";
39 | pub const DEBUG_SRC_IP: &str = "ip";
40 | pub const DEBUG_SRC_AUTH: &str = "auth";
41 | pub const DEBUG_SRC_MACVLAN: &str = "macvlan";
42 | pub const DEBUG_SRC_BPF: &str = "bpf";
43 |
44 | // Ethernet Constants
45 | pub const ETHER_P_IP: u16 = 0x0800; // IPv4 (/usr/include/linux/if_ether.h)
46 | pub const ETHER_P_ARP: u16 = 0x0806;
47 | pub const ETHER_VRRP_IPADDR_POS: usize = 42; // Position of the IP addresses variable-length field
48 | pub const ETHER_VRRP_V2_SRC_MAC: [u8; 6] = [0x00, 0x00, 0x5e, 0x00, 0x01, 0x00];
49 | pub const ETHER_VRRP_V2_DST_MAC: [u8; 6] = [0x01, 0x00, 0x5e, 0x00, 0x00, 0x12];
50 | pub const ETHER_ARP_DST_MAC: [u8; 6] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
51 | pub const ETHER_FRAME_SIZE: usize = 14;
52 |
53 | // ARP Constants
54 | pub const ARP_HW_TYPE: u16 = 1; // ethernet
55 | pub const ARP_OP_REQUEST: u16 = 1; // request
56 |
57 | // IP Constants
58 | pub const IP_FRAME_OFFSET: usize = 14;
59 | pub const IP_V4_VERSION: u8 = 0x45;
60 | pub const IP_UPPER_PROTO_VRRP: u8 = 112;
61 | pub const IP_TTL_VRRP_MINTTL: u8 = 255;
62 | pub const IP_DSCP_CS6: u8 = 0xc0;
63 |
64 | // VRRP Constants
65 | pub const VRRP_V2_FRAME_OFFSET: usize = 34;
66 | pub const VRRP_V2_CHECKSUM_POS: usize = 6;
67 | pub const VRRP_V2_VER_TYPE_AUTHMSG: u8 = 0x21;
68 | pub const VRRP_V2_IP_MCAST_DST: [u8; 4] = [224, 0, 0, 18];
69 | pub const VRRP_V2_ADVERT_VERSION_TYPE: u8 = 0x21;
70 | pub const VRRP_V2_DEFAULT_PRIORITY: u8 = 100;
71 |
72 | // Authentication Constants
73 | pub const AUTH_TYPE_SIMPLE: u8 = 1;
74 | pub const AUTH_TYPE_P0: u8 = 250;
75 | pub const AUTH_TYPE_P1: u8 = 251;
76 |
--------------------------------------------------------------------------------
/src/debug.rs:
--------------------------------------------------------------------------------
1 | //! debugging module
2 | //! This module provides debugging related functions.
3 | use super::*;
4 |
5 | // chrono
6 | use chrono::{DateTime, Local, Utc};
7 |
8 | // Verbose Structure
9 | #[derive(Clone, Copy)]
10 | pub struct Verbose {
11 | level: u8,
12 | time_zone: u8,
13 | time_format: u8,
14 | }
15 |
16 | // Debug type implementation
17 | impl Verbose {
18 | // new() method
19 | pub fn new(level: u8, time_zone: u8, time_format: u8) -> Verbose {
20 | Verbose {
21 | level,
22 | time_zone,
23 | time_format,
24 | }
25 | }
26 | }
27 |
28 | // print_debug() function
29 | /// This function simply print debugging information according to the specified level
30 | pub fn print_debug(debug: &Verbose, msg_level: u8, src: &str, msg: String) {
31 | // set debug header
32 | let mut hdr = format!("");
33 | if src != DEBUG_SRC_INFO {
34 | hdr = format!("debug({}): ", src);
35 | }
36 |
37 | // print debugging information with date and time
38 | if debug.level >= msg_level {
39 | match debug.time_zone {
40 | 1 => {
41 | // UTC
42 | let now: DateTime = Utc::now();
43 | match debug.time_format {
44 | 1 => println!("[{}] {}{}", now.format(RVRRPD_DFLT_DATE_FORMAT), hdr, msg),
45 | 2 => println!("[{}] {}{}", now.to_rfc2822(), hdr, msg),
46 | _ => println!("{}{}", hdr, msg),
47 | }
48 | } // local time
49 | _ => {
50 | let now: DateTime = Local::now();
51 | match debug.time_format {
52 | 1 => println!("[{}] {}{}", now.format(RVRRPD_DFLT_DATE_FORMAT), hdr, msg),
53 | 2 => println!("[{}] {}{}", now.to_rfc2822(), hdr, msg),
54 | _ => println!("{}{}", hdr, msg),
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/os/drivers.rs:
--------------------------------------------------------------------------------
1 | //! generic drivers module
2 |
3 | // network drivers enumerator
4 | #[allow(non_camel_case_types)]
5 | #[derive(Debug)]
6 | pub enum NetDrivers {
7 | ioctl, // ioctl
8 | libnl, // netlink (libnl-3)
9 | }
10 |
11 | // network interfaces type enumerator
12 | #[allow(non_camel_case_types)]
13 | #[derive(Debug)]
14 | pub enum IfTypes {
15 | ether, // default ethernet
16 | macvlan, // macvlan
17 | }
18 |
19 | // pflag operation Enumerator
20 | #[cfg(target_os = "linux")]
21 | pub enum PflagOp {
22 | Set,
23 | Unset,
24 | }
25 |
26 | // Operation enumerator
27 | #[derive(Debug)]
28 | pub enum Operation {
29 | Add, // Add IP address
30 | Rem, // Remove IP Address
31 | }
32 |
--------------------------------------------------------------------------------
/src/os/freebsd/arp.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD Address Resolution Protocol (ARP) module
2 | //! This module provides ARP related functions.
3 |
4 | /// Address Resolution Protocol (ARP) Structure
5 | #[repr(C)]
6 | pub struct ARPframe {
7 | // Ethernet Header
8 | pub dst_mac: [u8; 6], // destination MAC address
9 | pub src_mac: [u8; 6], // source MAC address
10 | pub ethertype: u16, // ether type
11 |
12 | // ARP
13 | pub hardware_type: u16, // network link type (0x1=ethernet)
14 | pub protocol_type: u16, // upper-layer protocol for resolution
15 | pub hw_addr_len: u8, // length of hardware address (bytes)
16 | pub proto_addr_len: u8, // upper-layer protocol address length
17 | pub opcode: u16, // operation (0x1=request, 0x2=reply)
18 | pub sender_hw_addr: [u8; 6], // sender hardware address
19 | pub sender_proto_addr: [u8; 4], // internetwork address of sender
20 | pub target_hw_addr: [u8; 6], // hardware address of target
21 | pub target_proto_addr: [u8; 4], // internetwork address of target
22 | }
23 |
--------------------------------------------------------------------------------
/src/os/freebsd/bpf.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD Berkeley Packet Filter (BPF) module
2 | use crate::*;
3 |
4 | // std
5 | use std::io;
6 | use std::convert::TryInto;
7 |
8 | // libc
9 | use libc::{IF_NAMESIZE};
10 |
11 | // ffi
12 | use std::ffi::{CString};
13 |
14 | // FreeBSD constants
15 | use crate::os::freebsd::constants::*;
16 |
17 | // Ifreq redifinition (stripped)
18 | #[repr(C)]
19 | struct IfreqS {
20 | ifr_name: [u8; IF_NAMESIZE],
21 | }
22 |
23 | // BPF system structures
24 | // bpf_ts structure
25 | // https://github.com/freebsd/freebsd/blob/master/sys/net/bpf.h:202
26 | #[repr(C)]
27 | pub struct bpf_ts {
28 | pub bt_sec: i64,
29 | pub bt_frac: u64,
30 | }
31 |
32 | // bpf_xhdr structure
33 | #[repr(C)]
34 | pub struct bpf_xhdr {
35 | pub bh_tstamp: bpf_ts, // timestamp
36 | pub bh_caplen: u32, // length of captured pattern
37 | pub bh_datalen: u32, // length of packet
38 | pub bh_hdrlen: u16, // length of this structure + alignment padding
39 | }
40 |
41 | // bpf_open_device() function
42 | //
43 | /// Open a BPF device and return the file descriptor if successful
44 | pub fn bpf_open_device(debug: &Verbose) -> io::Result<(i32)> {
45 | // try /dev/bpf
46 | let bpf_dev = CString::new("/dev/bpf").unwrap();
47 | let bpf_dev_slice = &mut [0i8; 10];
48 | for (i, b) in bpf_dev.as_bytes_with_nul().iter().enumerate() {
49 | bpf_dev_slice[i] = (*b).try_into().unwrap();
50 | }
51 | let mut buf = [0i8; 10];
52 | buf.clone_from_slice(bpf_dev_slice);
53 |
54 | // open /dev/bpf device
55 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF, format!("opening /dev/bpf device"));
56 | let res = unsafe {libc::open(&buf as *const i8, libc::O_RDWR)};
57 | if res >= 0 {
58 | return Ok(res);
59 | }
60 |
61 | // if above failed, try /dev/bpfX devices
62 | for i in 0..99 {
63 | // create bpf device name slice
64 | let bpf_fmtstr = format!("/dev/bpf{}", i);
65 | let bpf_dev = CString::new(bpf_fmtstr).unwrap();
66 | let bpf_dev_slice = &mut [0i8; 11];
67 | for (i, b) in bpf_dev.as_bytes_with_nul().iter().enumerate() {
68 | bpf_dev_slice[i] = (*b).try_into().unwrap();
69 | }
70 | // create bpf device name buffer
71 | let mut buf = [0i8; 11];
72 | buf.clone_from_slice(bpf_dev_slice);
73 |
74 | // open bpf device
75 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF, format!("opening /dev/bpf{} device", i));
76 | let res = unsafe {libc::open(&buf as *const i8, libc::O_RDWR)};
77 |
78 | // check returned value
79 | // if negative, an error occured, continue
80 | // if positive, return the file descriptor
81 | if res >= 0 {
82 | return Ok(res);
83 | }
84 | }
85 |
86 | // if all BPF devices are exhausted
87 | eprintln!("error, cannot find an available BPF device");
88 | return Err(io::Error::last_os_error());
89 | }
90 |
91 | // bpf_bind_device() function
92 | //
93 | /// Bind BPF device to a physical interface
94 | pub fn bpf_bind_device(bpf_fd: i32, interface: &CString, debug: &Verbose) -> io::Result<()> {
95 | let ifname_slice = &mut [0u8; IF_NAMESIZE];
96 | for (i, b) in interface.as_bytes_with_nul().iter().enumerate() {
97 | ifname_slice[i] = *b;
98 | }
99 |
100 | // create Ifreq structure
101 | let ifbound = IfreqS {
102 | ifr_name: {
103 | let mut buf = [0u8; IF_NAMESIZE];
104 | buf.clone_from_slice(ifname_slice);
105 | buf
106 | }
107 | };
108 |
109 | // ioctl
110 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
111 | format!("binding BPF device with fd {} to interface {:?}", bpf_fd, interface)
112 | );
113 | match unsafe { libc::ioctl(bpf_fd, BIOCSETIF, &ifbound) } {
114 | r if r >= 0 => Ok(()),
115 | e => {
116 | eprintln!("error while binding BPF device, fd {}, error no: {}", bpf_fd, e);
117 | return Err(io::Error::last_os_error());
118 | }
119 | }
120 | }
121 |
122 | // bpf_setup_buf() function
123 | //
124 | /// Setup BPF device buffer and features
125 | /// Return size of BPF buffer after setup
126 | pub fn bpf_setup_buf(bpf_fd: i32, pkt_buf: &mut [u8], debug: &Verbose) -> io::Result<(usize)> {
127 | // initialize local buf_len with current buffer size
128 | let buf_len = pkt_buf.len();
129 |
130 | if buf_len == 0 {
131 | // get buffer length (ioctl)
132 | // actually ignoring returned value
133 | match unsafe { libc::ioctl(bpf_fd, BIOCGBLEN, &buf_len)} {
134 | e if e < 0 => {
135 | eprintln!("error while getting buffer length on BPF device, fd {}, error no: {}", bpf_fd, e);
136 | return Err(io::Error::last_os_error());
137 | }
138 | s => {
139 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
140 | format!("required buffer length for BPF device, fd {}, is: {} bytes", bpf_fd, s)
141 | );
142 |
143 | }
144 | };
145 | } else {
146 | // set buffer length (ioctl)
147 | match unsafe { libc::ioctl(bpf_fd, BIOCSBLEN, &buf_len)} {
148 | e if e < 0 => {
149 | eprintln!("error while setting buffer length on BPF device, fd {}, error no: {}", bpf_fd, e);
150 | return Err(io::Error::last_os_error());
151 | }
152 | _ => {
153 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
154 | format!("buffer length for BPF device, fd {} set", bpf_fd)
155 | );
156 | }
157 | };
158 | }
159 |
160 | // activate immediate mode (ioctl)
161 | match unsafe { libc::ioctl(bpf_fd, BIOCIMMEDIATE, &buf_len) } {
162 | e if e < 0 => {
163 | eprintln!("error while setting immediate mode on BPF device, fd {}, error no: {}", bpf_fd, e);
164 | return Err(io::Error::last_os_error());
165 | }
166 | _ => {
167 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
168 | format!("immediate mode set on BPF device, fd {}", bpf_fd)
169 | );
170 | }
171 | };
172 |
173 | // set the header complete flag to one
174 | let flag = 1;
175 | match unsafe { libc::ioctl(bpf_fd, BIOCSHDRCMPLT, &flag) } {
176 | e if e < 0 => {
177 | eprintln!("error while setting ({}) header complete flag on BPF device, fd {}, error no: {}", flag, bpf_fd, e);
178 | return Err(io::Error::last_os_error());
179 | }
180 | _ => {
181 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
182 | format!("header complete flag set to {} on BPF device, fd {}", flag, bpf_fd)
183 | );
184 | }
185 | };
186 |
187 | // return Ok(buf_len) if everything went successful
188 | Ok(buf_len as usize)
189 | }
190 |
191 | // bpf_set_promisc() function
192 | //
193 | /// Set interface bound to the BPF's fd in promiscuous mode
194 | pub fn bpf_set_promisc(bpf_fd: i32, debug: &Verbose) -> io::Result<()> {
195 | // set interface in promiscuous mode
196 | match unsafe { libc::ioctl(bpf_fd, BIOCPROMISC.into(), 0) } {
197 | e if e < 0 => {
198 | eprintln!("error while setting promiscuous mode on BPF device, fd {}, error no: {}", bpf_fd, e);
199 | return Err(io::Error::last_os_error());
200 | }
201 | _ => {
202 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
203 | format!("promiscuous mode set on BPF device, fd {}", bpf_fd)
204 | );
205 | Ok(())
206 | }
207 | }
208 | }
209 |
210 | // bpf_wordalign() function
211 | //
212 | /// Align the BPF buffer to the next frame given capured size
213 | /// Reference: pnet's source/src/bindings/bpf.rs
214 | pub fn bpf_wordalign(s: isize) -> isize {
215 | let bpf_alignement = BPF_ALIGNMENT as isize;
216 | let one = 1;
217 |
218 | (s + (bpf_alignement - one)) & !(bpf_alignement - one)
219 | }
220 |
--------------------------------------------------------------------------------
/src/os/freebsd/constants.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD specific constants
2 |
3 | // libc
4 | use libc::{c_ulong, c_uint, c_int};
5 |
6 | // sockios, ioctls
7 | pub const BIOCGBLEN: c_ulong = 0x40044266;
8 | pub const BIOCSBLEN: c_ulong = 0xc0044266;
9 | pub const BIOCFLUSH: c_uint = 0x20004268;
10 | pub const BIOCPROMISC: c_uint = 0x20004269;
11 | pub const BIOCGDLT: c_ulong = 0x4004426a;
12 | pub const BIOCGETIF: c_ulong = 0x4020426b;
13 | pub const BIOCSETIF: c_ulong = 0x8020426c;
14 | pub const BIOCGSTATS: c_ulong = 0x4008426f;
15 | pub const BIOCIMMEDIATE: c_ulong = 0x80044270;
16 | pub const BIOCVERSION: c_ulong = 0x40044271;
17 | pub const BIOCGRSIG: c_ulong = 0x40044272;
18 | pub const BIOCSRSIG: c_ulong = 0x80044273;
19 | pub const BIOCGHDRCMPLT: c_ulong = 0x40044274;
20 | pub const BIOCSHDRCMPLT: c_ulong = 0x80044275;
21 | pub const BIOCGSEESENT: c_ulong = 0x40044276;
22 | pub const BIOCSSEESENT: c_ulong = 0x80044277;
23 | pub const BIOCSDLT: c_ulong = 0x80044278;
24 | pub const SIOCGIFADDR: c_ulong = 0xc0206921;
25 | pub const SIOCAIFADDR: c_ulong = 0x8040691a;
26 | pub const SIOCDIFADDR: c_ulong = 0x80206919;
27 | pub const SIOCGIFCONF: c_ulong = 0xc0106924;
28 | pub const SIOCGIFFLAGS: c_ulong = 0xc0206911;
29 | pub const SIOCSIFFLAGS: c_ulong = 0x80206910;
30 | pub const SIOCGIFDSTADDR: c_ulong = 0xc0206922;
31 | pub const SIOCSIFDSTADDR: c_ulong = 0x8020690e;
32 | pub const SIOCGIFBRDADDR: c_ulong = 0xc0206923;
33 | pub const SIOCSIFBRDADDR: c_ulong = 0x80206913;
34 | pub const SIOCGIFNETMASK: c_ulong = 0xc0206925;
35 | pub const SIOCSIFNETMASK: c_ulong = 0x80206916;
36 | pub const SIOCGIFMETRIC: c_ulong = 0xc0206917;
37 | pub const SIOCSIFMETRIC: c_ulong = 0x80206918;
38 | pub const SIOCGIFMTU: c_ulong = 0xc0206933;
39 | pub const SIOCSIFMTU: c_ulong = 0x80206934;
40 | pub const SIOCADDMULTI: c_ulong = 0x80206931;
41 | pub const SIOCDELMULTI: c_ulong = 0x80206932;
42 |
43 | // Berkeley Packet Filter
44 | pub const BPF_ALIGNMENT: c_int = 8;
45 |
--------------------------------------------------------------------------------
/src/os/freebsd/libc.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD standard C library support
2 | use crate::*;
3 |
4 | // std, libc
5 | use libc::{read, write, c_void};
6 | use std::io;
7 | use std::mem;
8 |
9 | // read_bpf_buf() function
10 | //
11 | /// Receive IP Packet
12 | pub fn read_bpf_buf(bpf_fd: i32, buf: &mut [u8], buf_size: usize) -> io::Result {
13 | // declare len
14 | let len: isize;
15 |
16 | // read from BPF device (unsafe)
17 | unsafe {
18 | len = match read(bpf_fd, buf.as_ptr() as *mut c_void, buf_size) {
19 | -1 => {
20 | eprintln!("error while reading BPF buffer on fd {}, buffer length {}", bpf_fd, buf.len());
21 | return Err(io::Error::last_os_error());
22 | }
23 | len => len,
24 | }
25 | }
26 |
27 | // return the length of read buffer
28 | Ok(len)
29 | }
30 |
31 | // raw_sendto() function
32 | /// Send RAW frame/packet
33 | pub fn raw_sendto(
34 | fd: i32,
35 | _ifindex: i32,
36 | frame: &mut Vec,
37 | debug: &Verbose
38 | ) -> io::Result<()> {
39 | unsafe {
40 | // unsafe call to write()
41 | match write(fd, &mut frame[..] as *mut _ as *const c_void, mem::size_of_val(&frame[..])) {
42 | -1 => Err(io::Error::last_os_error()),
43 | _ => {
44 | print_debug(debug, DEBUG_LEVEL_MEDIUM, DEBUG_SRC_BPF,
45 | format!("VRRPv2 frame successfully sent on BPF device, fd {}", fd)
46 | );
47 | Ok(())
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/os/freebsd/mod.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD Operating System support
2 |
3 | // FreeBSD constants
4 | #[allow(dead_code)]
5 | pub mod constants;
6 |
7 | // FreeBSD standard C library support
8 | pub mod libc;
9 |
10 | // FreeBSD ARP support
11 | pub mod arp;
12 |
13 | // FreeBSD BPF support
14 | pub mod bpf;
15 |
16 | // FreeBSD network support
17 | pub mod netinet;
18 |
--------------------------------------------------------------------------------
/src/os/freebsd/netinet.rs:
--------------------------------------------------------------------------------
1 | //! FreeBSD network support
2 |
3 | // libc
4 | use libc::{IF_NAMESIZE, c_int, c_short, c_uchar, ioctl, AF_INET};
5 |
6 | // std
7 | use std::io;
8 | use std::ffi::CString;
9 |
10 | // FreeBSD constants
11 | use crate::os::freebsd::constants::*;
12 |
13 | // operating systems drivers
14 | use crate::os::drivers::Operation;
15 |
16 | // IfAliasReq Structure
17 | #[repr(C)]
18 | struct IfAliasReq {
19 | ifr_name: [u8; IF_NAMESIZE], // interface name
20 | ifra_addr: int_sockaddr_in, // IPv4 address
21 | ifra_broadaddr: int_sockaddr_in, // destination address
22 | ifra_mask: int_sockaddr_in, // netmask
23 | ifra_vhid: c_int,
24 | }
25 |
26 | // int_sockaddr alias
27 | #[repr(C)]
28 | struct int_sockaddr_in {
29 | sin_len: c_uchar,
30 | sin_family: c_uchar,
31 | sin_port: c_short,
32 | sin_addr: [c_uchar; 12],
33 | }
34 |
35 | // set_ip_address() function
36 | /// Set IPv4 Address on given interface
37 | pub fn set_ip_address(_fd: i32, ifname: &CString, ip: [u8; 4], netmask: [u8; 4], op: Operation) -> io::Result<()> {
38 | // create a slice of mutable reference to array of 16 u8
39 | let ifname_slice = &mut [0u8; 16];
40 | // for every bytes/character in name of type Cstring, insert it into the above slice.
41 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
42 | ifname_slice[i] = *b;
43 | }
44 | // check interface name size
45 | if ifname_slice.len() > IF_NAMESIZE {
46 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
47 | }
48 |
49 | // create IP address slice
50 | let ip_addr_slice = &mut [0u8; 12];
51 | for (i, b) in ip.iter().enumerate() {
52 | ip_addr_slice[i] = *b;
53 | }
54 |
55 | // create IP netmask slice
56 | let ip_netmask_slice = &mut [0u8; 12];
57 | for (i, b) in netmask.iter().enumerate() {
58 | ip_netmask_slice[i] = *b;
59 | }
60 |
61 | let mut ifaddr = IfAliasReq {
62 | ifr_name: {
63 | let mut buf = [0u8; IF_NAMESIZE];
64 | buf.clone_from_slice(ifname_slice);
65 | buf
66 | },
67 | ifra_addr: int_sockaddr_in {
68 | sin_len: 16,
69 | sin_family: AF_INET as u8,
70 | sin_port: 0,
71 | sin_addr: {
72 | let mut data = [0u8; 12];
73 | data.clone_from_slice(ip_addr_slice);
74 | data
75 | }
76 | },
77 | ifra_broadaddr: int_sockaddr_in {
78 | sin_len: 0,
79 | sin_family: 0,
80 | sin_port: 0,
81 | sin_addr: [0u8; 12],
82 | },
83 | ifra_mask: int_sockaddr_in {
84 | sin_len: 16,
85 | sin_family: AF_INET as u8,
86 | sin_port: 0,
87 | sin_addr: {
88 | let mut data = [0u8; 12];
89 | data.clone_from_slice(ip_netmask_slice);
90 | data
91 | }
92 | },
93 | ifra_vhid: 0,
94 | };
95 |
96 | // open new socket for below ioctl
97 | let fd = match unsafe { libc::socket(libc::PF_INET, libc::SOCK_DGRAM, 0) } {
98 | -1 => return Err(io::Error::last_os_error()),
99 | fd => fd,
100 | };
101 |
102 | // match given operation on IP address
103 | // see man 4 netintro
104 | match op {
105 | Operation::Add => {
106 | // ioctl - set interface's IP address
107 | let res = unsafe { ioctl(fd, SIOCAIFADDR, &mut ifaddr) };
108 | if res < 0 {
109 | return Err(io::Error::last_os_error());
110 | }
111 | },
112 | Operation::Rem => {
113 | // ioctl - remove interface's IP address
114 | let res = unsafe { ioctl(fd, SIOCDIFADDR,&mut ifaddr) };
115 | if res < 0 {
116 | return Err(io::Error::last_os_error());
117 | }
118 | }
119 | }
120 |
121 | Ok(())
122 | }
123 |
--------------------------------------------------------------------------------
/src/os/linux/arp.rs:
--------------------------------------------------------------------------------
1 | //! Linux Address Resolution Protocol (ARP) module
2 | //! This module provides ARP related functions.
3 |
4 | // std
5 | use std::io;
6 |
7 | // libc
8 | use libc::{socket, AF_PACKET, ETH_P_ARP, SOCK_RAW};
9 |
10 | /// Address Resolution Protocol (ARP) Structure
11 | #[repr(C)]
12 | pub struct ARPframe {
13 | // Ethernet Header
14 | pub dst_mac: [u8; 6], // destination MAC address
15 | pub src_mac: [u8; 6], // source MAC address
16 | pub ethertype: u16, // ether type
17 |
18 | // ARP
19 | pub hardware_type: u16, // network link type (0x1=ethernet)
20 | pub protocol_type: u16, // upper-layer protocol for resolution
21 | pub hw_addr_len: u8, // length of hardware address (bytes)
22 | pub proto_addr_len: u8, // upper-layer protocol address length
23 | pub opcode: u16, // operation (0x1=request, 0x2=reply)
24 | pub sender_hw_addr: [u8; 6], // sender hardware address
25 | pub sender_proto_addr: [u8; 4], // internetwork address of sender
26 | pub target_hw_addr: [u8; 6], // hardware address of target
27 | pub target_proto_addr: [u8; 4], // internetwork address of target
28 | }
29 |
30 | // open_raw_socket_arp() function
31 | /// Open raw socket
32 | pub fn open_raw_socket_arp() -> io::Result {
33 | unsafe {
34 | // man 2 socket
35 | // returns a file descriptor or -1 if error.
36 | match socket(AF_PACKET, SOCK_RAW, ETH_P_ARP.to_be() as i32) {
37 | -1 => Err(io::Error::last_os_error()),
38 | fd => Ok(fd),
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/os/linux/filter.rs:
--------------------------------------------------------------------------------
1 | //! Linux Socket Filter module
2 |
3 | // libc
4 | use libc::c_void;
5 |
6 | // SockFilter structure
7 | #[repr(C)]
8 | pub struct SockFilter {
9 | code: u16, // Filter code
10 | jt: u8, // Jump true
11 | jf: u8, // Jump false
12 | k: u32, // Generic multiuse field
13 | }
14 |
15 | // SockFilter implementation
16 | impl SockFilter {
17 | // new_vrrpv2_gid() method
18 | //
19 | // BPF Filter - VRRPv2 Advertisement Packets:
20 | // ldh [12]
21 | // jne #0x800, drop
22 | // ldb [23]
23 | // jneq #0x70, drop
24 | // ldb [34]
25 | // jneq #0x21, drop
26 | // ldb [35]
27 | // jneq #0x1, drop
28 | // ret #-1
29 | // drop: ret #0
30 | //
31 | // BPF Bytecode:
32 | // { 0x28, 0, 0, 0x0000000c },
33 | // { 0x15, 0, 7, 0x00000800 },
34 | // { 0x30, 0, 0, 0x00000017 },
35 | // { 0x15, 0, 5, 0x00000070 },
36 | // { 0x30, 0, 0, 0x00000022 },
37 | // { 0x15, 0, 3, 0x00000021 },
38 | // { 0x30, 0, 0, 0x00000023 },
39 | // { 0x15, 0, 1, 0x00000001 },
40 | // { 0x06, 0, 0, 0xffffffff },
41 | // { 0x06, 0, 0, 0000000000 },
42 | //
43 | pub fn new_vrrpv2_gid(gid: u8) -> [SockFilter; 10] {
44 | let filter: [SockFilter; 10] = [
45 | SockFilter {
46 | // 001
47 | code: 0x28,
48 | jt: 0x0,
49 | jf: 0x0,
50 | k: 0x0000000c,
51 | },
52 | SockFilter {
53 | // 002
54 | code: 0x15,
55 | jt: 0x0,
56 | jf: 0x7,
57 | k: 0x00000800,
58 | },
59 | SockFilter {
60 | // 003
61 | code: 0x30,
62 | jt: 0x0,
63 | jf: 0x0,
64 | k: 0x00000017,
65 | },
66 | SockFilter {
67 | // 004
68 | code: 0x15,
69 | jt: 0x0,
70 | jf: 0x5,
71 | k: 0x00000070,
72 | },
73 | SockFilter {
74 | // 005
75 | code: 0x30,
76 | jt: 0x0,
77 | jf: 0x0,
78 | k: 0x00000022,
79 | },
80 | SockFilter {
81 | // 006
82 | code: 0x15,
83 | jt: 0x0,
84 | jf: 0x0,
85 | k: 0x00000021,
86 | },
87 | SockFilter {
88 | // 007
89 | code: 0x30,
90 | jt: 0x0,
91 | jf: 0x0,
92 | k: 0x00000023,
93 | },
94 | SockFilter {
95 | // 008
96 | code: 0x15,
97 | jt: 0x0,
98 | jf: 0x1,
99 | k: gid as u32, // replace by the group id
100 | },
101 | SockFilter {
102 | // 009
103 | code: 0x06,
104 | jt: 0x0,
105 | jf: 0x0,
106 | k: 0xffffffff,
107 | },
108 | SockFilter {
109 | // 010
110 | code: 0x06,
111 | jt: 0x0,
112 | jf: 0x0,
113 | k: 0000000000,
114 | },
115 | ];
116 | filter
117 | }
118 | }
119 |
120 | // SockFprog structure
121 | #[repr(C)]
122 | pub struct SockFprog {
123 | len: u16, // Number of filter blocks (size of array)
124 | filter: *const c_void, // Pointer to an array of SockFilter
125 | }
126 |
127 | // SockFprog implementation
128 | impl SockFprog {
129 | // build_fprog_vrrpv2_gid() method
130 | pub fn build_fprog_vrrpv2_gid(filter: &[SockFilter; 10]) -> SockFprog {
131 | let fprog = SockFprog {
132 | filter: filter.as_ptr() as *const c_void,
133 | len: filter.len() as u16,
134 | };
135 | fprog
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/os/linux/libc.rs:
--------------------------------------------------------------------------------
1 | //! Linux standard C library compatibility
2 | use crate::*;
3 |
4 | // std, libc, ffi
5 | use libc::{setsockopt, socket, AF_PACKET, SOCK_RAW, SOL_SOCKET, SO_ATTACH_FILTER};
6 | use std::ffi::CString;
7 | use std::io;
8 | use std::mem;
9 |
10 | // Linux socket filter
11 | use crate::os::linux::filter::SockFprog;
12 |
13 | // open_raw_socket_fd() function
14 | /// Open a raw AF_PACKET socket for IPv4
15 | pub fn open_raw_socket_fd() -> io::Result {
16 | unsafe {
17 | // man 2 socket
18 | // returns a file descriptor or -1 if error.
19 | match socket(AF_PACKET, SOCK_RAW, ETHER_P_IP.to_be() as i32) {
20 | -1 => Err(io::Error::last_os_error()),
21 | fd => Ok(fd),
22 | }
23 | }
24 | }
25 |
26 | // set_sock_filter function
27 | /// Set a BPF filter on a socket
28 | pub fn set_sock_filter(sockfd: i32, bpf: &SockFprog) -> io::Result {
29 | unsafe {
30 | // man 2 setsockopt
31 | match setsockopt(
32 | sockfd,
33 | SOL_SOCKET,
34 | SO_ATTACH_FILTER,
35 | bpf as *const _ as *mut c_void,
36 | mem::size_of::() as u32,
37 | ) {
38 | -1 => Err(io::Error::last_os_error()),
39 | ret => Ok(ret),
40 | }
41 | }
42 | }
43 |
44 | // recv_ip_pkts() function
45 | /// Receive IP packets
46 | pub fn recv_ip_pkts(sockfd: i32, sockaddr: &mut sockaddr_ll, buf: &mut [u8]) -> io::Result {
47 | // stack variables
48 | let len: isize;
49 | let mut addr_buf_len: socklen_t = mem::size_of::() as socklen_t;
50 |
51 | unsafe {
52 | // unsafe transmut of sockaddr_ll to a sockaddr type
53 | let addr_ptr: *mut sockaddr = mem::transmute::<*mut sockaddr_ll, *mut sockaddr>(sockaddr);
54 | // unsafe call to libc's recvfrom (man 2 recvfrom)
55 | // returns length of message, -1 if error
56 | len = match recvfrom(
57 | sockfd, // socket file descriptor
58 | buf.as_mut_ptr() as *mut c_void, // pointer to buffer
59 | buf.len(), // buffer length
60 | 0, // flags
61 | addr_ptr as *mut sockaddr, // pointer to source address
62 | &mut addr_buf_len, // address buffer length
63 | ) {
64 | -1 => {
65 | return Err(io::Error::last_os_error());
66 | }
67 | len => len,
68 | }
69 | }
70 |
71 | Ok(len as usize)
72 | }
73 |
74 | // c_ifnametoindex() function
75 | /// see 'man 3 if_nametoindex'
76 | pub fn c_ifnametoindex(ifname: &String) -> io::Result {
77 | unsafe {
78 | let c_ifname = CString::new(ifname.clone()).unwrap();
79 | let r = libc::if_nametoindex(c_ifname.as_ptr());
80 | if r == 0 {
81 | Err(io::Error::last_os_error())
82 | } else {
83 | Ok(r)
84 | }
85 | }
86 | }
87 |
88 | // raw_sendto() function
89 | /// Send RAW frame/packet
90 | pub fn raw_sendto(
91 | sockfd: i32,
92 | ifindex: i32,
93 | frame: &mut Vec,
94 | _debug: &Verbose,
95 | ) -> io::Result<()> {
96 | // sockaddr_ll (man 7 packet)
97 | let mut sa = libc::sockaddr_ll {
98 | sll_family: libc::AF_PACKET as u16,
99 | sll_protocol: ETHER_P_IP.to_be(),
100 | sll_ifindex: ifindex,
101 | sll_hatype: 0,
102 | sll_pkttype: 0,
103 | sll_halen: 0,
104 | sll_addr: [0; 8],
105 | };
106 |
107 | unsafe {
108 | // unsafe call to sendto()
109 | let ptr_sockaddr = mem::transmute::<*mut libc::sockaddr_ll, *mut libc::sockaddr>(&mut sa);
110 | match libc::sendto(
111 | sockfd,
112 | &mut frame[..] as *mut _ as *const c_void,
113 | mem::size_of_val(&frame[..]),
114 | 0,
115 | ptr_sockaddr,
116 | mem::size_of_val(&sa) as u32,
117 | ) {
118 | -1 => Err(io::Error::last_os_error()),
119 | _ => Ok(()),
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/os/linux/mod.rs:
--------------------------------------------------------------------------------
1 | //! Linux Operating System support
2 |
3 | // standard C library compatibility
4 | pub mod libc;
5 | // netdev support
6 | pub mod netdev;
7 | // libnl netlink support
8 | pub mod libnl;
9 | // Linux ARP support
10 | pub mod arp;
11 | // Linux Socket Filter support
12 | pub mod filter;
13 |
--------------------------------------------------------------------------------
/src/os/linux/netdev.rs:
--------------------------------------------------------------------------------
1 | //! Linux specific network device functions module
2 | //! This module interfaces with the linux netdevice kernel API and related networking functions of the standard C library.
3 | use crate::*;
4 |
5 | // std, libc, ffi
6 | use libc::{
7 | c_short, c_uchar, c_ulong, c_ushort, ioctl, AF_INET, ARPHRD_ETHER, ETH_ALEN, IFF_PROMISC,
8 | IFF_RUNNING, IFF_UP, IF_NAMESIZE, RTF_UP,
9 | };
10 | use std::ffi::CString;
11 | use std::io;
12 |
13 | // operating system drivers
14 | use crate::os::drivers::Operation;
15 |
16 | /// ioctl_flags Structure
17 | #[repr(C)]
18 | struct ioctl_flags {
19 | ifr_name: [u8; IF_NAMESIZE],
20 | ifr_flags: c_short,
21 | }
22 |
23 | /// ioctl_4_addr Structure
24 | #[repr(C)]
25 | struct ioctl_v4_addr {
26 | ifr_name: [u8; IF_NAMESIZE],
27 | ifr_addr: int_sockaddr_pad,
28 | }
29 |
30 | /// ioctl_v4_netmask Structure
31 | #[repr(C)]
32 | struct ioctl_v4_netmask {
33 | ifr_name: [u8; IF_NAMESIZE],
34 | ifr_netmask: int_sockaddr_pad,
35 | }
36 |
37 | /// ioctl_v4_route Structure
38 | #[derive(Debug)]
39 | #[repr(C)]
40 | struct ioctl_v4_route {
41 | rt_hash: c_ulong,
42 | rt_dst: int_sockaddr,
43 | rt_gateway: int_sockaddr,
44 | rt_genmask: int_sockaddr,
45 | rt_flags: c_ushort,
46 | rt_pad1: c_short,
47 | rt_pad2: c_ulong,
48 | rt_tos: c_uchar,
49 | rt_class: c_uchar,
50 | rt_pad3: [c_short; 3], // c_short or x3 on 64-bits
51 | rt_metric: c_short,
52 | rt_dev: *const u8,
53 | rt_mtu: c_ulong,
54 | rt_window: c_ulong,
55 | rt_irtt: c_ushort,
56 | }
57 |
58 | /// ioctl_ether_mac Structure
59 | #[repr(C)]
60 | #[derive(Debug)]
61 | struct ioctl_ether_mac {
62 | ifr_name: [u8; IF_NAMESIZE],
63 | ifr_hwaddr: int_sockaddr_ether,
64 | }
65 |
66 | /// internal int_sockaddr_pad Structure
67 | #[repr(C)]
68 | #[derive(Debug)]
69 | struct int_sockaddr_pad {
70 | sa_family: u16,
71 | sa_void: u16, // ensure proper alignement
72 | sa_data: [u8; 14],
73 | }
74 |
75 | /// internal int_sockaddr Structure
76 | #[derive(Debug)]
77 | #[repr(C)]
78 | struct int_sockaddr {
79 | sa_family: u16,
80 | sa_data: [u8; 14],
81 | }
82 |
83 | /// internal int_sockaddr_ether Structure
84 | #[derive(Debug)]
85 | #[repr(C)]
86 | struct int_sockaddr_ether {
87 | sa_family: u16,
88 | sa_data: [u8; ETH_ALEN as usize],
89 | }
90 |
91 | // set_if_promiscuous() function
92 | /// Set (or Unset) interface in promiscuous mode
93 | pub fn set_if_promiscuous(sockfd: i32, ifname: &CString, op: PflagOp) -> io::Result<()> {
94 | // create a slice of mutable reference to array of 16 u8
95 | let ifname_slice = &mut [0u8; 16];
96 |
97 | // for every bytes/character in name of type Cstring, insert it into the above slice.
98 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
99 | ifname_slice[i] = *b;
100 | }
101 |
102 | // check interface name size
103 | if ifname_slice.len() > IF_NAMESIZE {
104 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
105 | }
106 |
107 | // construct ioctl_flags structure
108 | let mut ifopts = ioctl_flags {
109 | ifr_name: {
110 | let mut buf = [0u8; IF_NAMESIZE];
111 | // the src and dst must be of the same size.
112 | buf.clone_from_slice(ifname_slice);
113 | buf
114 | },
115 | ifr_flags: 0,
116 | };
117 |
118 | // operation to perform on promiscuous flag
119 | match op {
120 | PflagOp::Set => {
121 | // set the flags to UP,RUNNING,PROMISC using bitwise OR operation.
122 | ifopts.ifr_flags |= IFF_UP as c_short | IFF_RUNNING as c_short | IFF_PROMISC as c_short;
123 | let res = unsafe { ioctl(sockfd, libc::SIOCSIFFLAGS, &mut ifopts) };
124 | if res < 0 {
125 | return Err(io::Error::last_os_error());
126 | }
127 | }
128 | PflagOp::Unset => {
129 | // unset PROMISC flag
130 | ifopts.ifr_flags |= IFF_UP as c_short | IFF_RUNNING as c_short;
131 | let res = unsafe { ioctl(sockfd, libc::SIOCSIFFLAGS, &mut ifopts) };
132 | if res < 0 {
133 | return Err(io::Error::last_os_error());
134 | }
135 | }
136 | }
137 |
138 | Ok(())
139 | }
140 |
141 | // set_ip_address() function
142 | /// Set an IP address on an interface
143 | pub fn set_ip_address(
144 | sockfd: i32,
145 | ifname: &CString,
146 | ip: [u8; 4],
147 | netmask: [u8; 4],
148 | ) -> io::Result<()> {
149 | // create a slice of mutable reference to array of 16 u8
150 | let ifname_slice = &mut [0u8; 16];
151 | // for every bytes/character in name of type Cstring, insert it into the above slice.
152 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
153 | ifname_slice[i] = *b;
154 | }
155 | // check interface name size
156 | if ifname_slice.len() > IF_NAMESIZE {
157 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
158 | }
159 |
160 | // create IP address slice
161 | let ip_addr_slice = &mut [0u8; 14];
162 | for (i, b) in ip.iter().enumerate() {
163 | ip_addr_slice[i] = *b;
164 | }
165 |
166 | // create IP netmask slice
167 | let ip_netmask_slice = &mut [0u8; 14];
168 | for (i, b) in netmask.iter().enumerate() {
169 | ip_netmask_slice[i] = *b;
170 | }
171 |
172 | // construct ifaddr structure
173 | let mut ifaddr = ioctl_v4_addr {
174 | ifr_name: {
175 | let mut buf = [0u8; IF_NAMESIZE];
176 | // the src and dst must be of the same size.
177 | buf.clone_from_slice(ifname_slice);
178 | buf
179 | },
180 | ifr_addr: {
181 | let mut ip_buf = [0u8; 14];
182 | ip_buf.clone_from_slice(ip_addr_slice);
183 | let addr = int_sockaddr_pad {
184 | sa_family: AF_INET as u16,
185 | sa_void: 0,
186 | sa_data: ip_buf,
187 | };
188 | addr
189 | },
190 | };
191 |
192 | // construct ifnetmask structure
193 | let mut ifnetmask = ioctl_v4_netmask {
194 | ifr_name: {
195 | let mut buf = [0u8; IF_NAMESIZE];
196 | // the src and dst must be of the same size.
197 | buf.clone_from_slice(ifname_slice);
198 | buf
199 | },
200 | ifr_netmask: {
201 | let mut netmask_buf = [0u8; 14];
202 | netmask_buf.clone_from_slice(ip_netmask_slice);
203 | let netmask = int_sockaddr_pad {
204 | sa_family: AF_INET as u16,
205 | sa_void: 0,
206 | sa_data: netmask_buf,
207 | };
208 | netmask
209 | },
210 | };
211 |
212 | // ioctl - set interface's IP address
213 | let res = unsafe { ioctl(sockfd, libc::SIOCSIFADDR, &mut ifaddr) };
214 | if res < 0 {
215 | return Err(io::Error::last_os_error());
216 | }
217 |
218 | // ioctl - set interface's netmask
219 | let res = unsafe { ioctl(sockfd, libc::SIOCSIFNETMASK, &mut ifnetmask) };
220 | if res < 0 {
221 | return Err(io::Error::last_os_error());
222 | }
223 |
224 | Ok(())
225 | }
226 |
227 | // get_mac_addr() function
228 | /// Get the MAC address of an interface
229 | /// this function return the interface's MAC address if read sucessfully
230 | pub fn get_mac_addr(sockfd: i32, ifname: &CString, debug: &Verbose) -> io::Result<[u8; 6]> {
231 | // convert interface name to CString type
232 | let ifname = CString::new(ifname.as_bytes() as &[u8]).unwrap();
233 |
234 | // create a slice of mutable reference to array of 16 u8
235 | let ifname_slice = &mut [0u8; 16];
236 |
237 | // for every bytes/character in name of type Cstring, insert it into the above slice.
238 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
239 | ifname_slice[i] = *b;
240 | }
241 |
242 | // check interface name size
243 | if ifname_slice.len() > IF_NAMESIZE {
244 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
245 | }
246 |
247 | // constuct ifmac structure
248 | let mut ifmac = ioctl_ether_mac {
249 | ifr_name: {
250 | let mut buf = [0u8; IF_NAMESIZE];
251 | // the src and dst must be of the same size.
252 | buf.clone_from_slice(ifname_slice);
253 | buf
254 | },
255 | ifr_hwaddr: {
256 | let mac_buf = [0u8; ETH_ALEN as usize];
257 | let mac = int_sockaddr_ether {
258 | sa_family: 0,
259 | sa_data: mac_buf,
260 | };
261 | mac
262 | },
263 | };
264 |
265 | // ioctl - set/reset MAC address
266 | print_debug(
267 | debug,
268 | DEBUG_LEVEL_HIGH,
269 | DEBUG_SRC_MAC,
270 | format!("getting mac address on interface {:?}", ifname),
271 | );
272 | let result = unsafe { ioctl(sockfd, libc::SIOCGIFHWADDR, &mut ifmac) };
273 | if result < 0 {
274 | return Err(io::Error::last_os_error());
275 | }
276 | print_debug(
277 | debug,
278 | DEBUG_LEVEL_HIGH,
279 | DEBUG_SRC_MAC,
280 | format!("got interface {:?} mac address {:?}", ifname, ifmac),
281 | );
282 |
283 | // return the mac address
284 | Ok(ifmac.ifr_hwaddr.sa_data)
285 | }
286 |
287 | // set_mac_addr() function
288 | /// Set the specified MAC address on interface
289 | pub fn set_mac_addr(
290 | sockfd: i32,
291 | ifname: &CString,
292 | mac: [u8; 6],
293 | debug: &Verbose,
294 | ) -> io::Result<()> {
295 | // convert interface name to CString type
296 | let ifname = CString::new(ifname.as_bytes() as &[u8]).unwrap();
297 |
298 | // create a slice of mutable reference to array of 16 u8
299 | let ifname_slice = &mut [0u8; 16];
300 |
301 | // for every bytes/character in name of type Cstring, insert it into the above slice.
302 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
303 | ifname_slice[i] = *b;
304 | }
305 |
306 | // check interface name size
307 | if ifname_slice.len() > IF_NAMESIZE {
308 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
309 | }
310 |
311 | // create ethernet mac slice
312 | let ether_mac_slice = &mut [0u8; ETH_ALEN as usize];
313 | for (i, b) in mac.iter().enumerate() {
314 | ether_mac_slice[i] = *b;
315 | }
316 |
317 | // constuct ifmac structure
318 | let mut ifmac = ioctl_ether_mac {
319 | ifr_name: {
320 | let mut buf = [0u8; IF_NAMESIZE];
321 | // the src and dst must be of the same size.
322 | buf.clone_from_slice(ifname_slice);
323 | buf
324 | },
325 | ifr_hwaddr: {
326 | let mut mac_buf = [0u8; ETH_ALEN as usize];
327 | mac_buf.clone_from_slice(ether_mac_slice);
328 | let mac = int_sockaddr_ether {
329 | sa_family: ARPHRD_ETHER as u16,
330 | sa_data: mac_buf,
331 | };
332 | mac
333 | },
334 | };
335 |
336 | // ioctl - set/reset MAC address
337 | print_debug(
338 | debug,
339 | DEBUG_LEVEL_HIGH,
340 | DEBUG_SRC_MAC,
341 | format!("setting mac address on {:?}, {:?}", ifname, ifmac),
342 | );
343 | let result = unsafe { ioctl(sockfd, libc::SIOCSIFHWADDR, &mut ifmac) };
344 | if result < 0 {
345 | return Err(io::Error::last_os_error());
346 | }
347 | Ok(())
348 | }
349 |
350 | // set_ip_routes() function
351 | /// Set IP routes into the routing table
352 | /// if boolean 'set_flag' is true then add the route, otherwise remove
353 | pub fn set_ip_route(
354 | sockfd: i32,
355 | ifname: &String,
356 | route: [u8; 4],
357 | rtmask: [u8; 4],
358 | gw: [u8; 4],
359 | metric: i16,
360 | mtu: u64,
361 | op: &Operation,
362 | debug: &Verbose,
363 | ) -> io::Result<()> {
364 | // convert interface name to CString type
365 | let ifname = CString::new(ifname.as_bytes() as &[u8]).unwrap();
366 |
367 | // create a slice of mutable reference to array of 16 u8
368 | let ifname_slice = &mut [0u8; 16];
369 |
370 | // for every bytes/character in name of type Cstring, insert it into the above slice.
371 | for (i, b) in ifname.as_bytes_with_nul().iter().enumerate() {
372 | ifname_slice[i] = *b;
373 | }
374 |
375 | // check interface name size
376 | if ifname_slice.len() > IF_NAMESIZE {
377 | panic!("Interface name is longer than {}", IF_NAMESIZE - 1);
378 | }
379 |
380 | // create route slice
381 | let route_slice = &mut [0u8; 14];
382 | for (i, b) in route.iter().enumerate() {
383 | route_slice[i + 2] = *b;
384 | }
385 |
386 | // create rtmask slice
387 | let rtmask_slice = &mut [0u8; 14];
388 | for (i, b) in rtmask.iter().enumerate() {
389 | rtmask_slice[i + 2] = *b;
390 | }
391 |
392 | // create gateway slice
393 | let gateway_slice = &mut [0u8; 14];
394 | for (i, b) in gw.iter().enumerate() {
395 | gateway_slice[i + 2] = *b;
396 | }
397 |
398 | // construct route
399 | let dst_route = int_sockaddr {
400 | sa_family: AF_INET as u16,
401 | //sa_void: 0,
402 | sa_data: {
403 | let mut route_buf = [0u8; 14];
404 | route_buf.clone_from_slice(route_slice);
405 | route_buf
406 | },
407 | };
408 |
409 | // construct route mask
410 | let route_mask = int_sockaddr {
411 | sa_family: AF_INET as u16,
412 | //sa_void: 0,
413 | sa_data: {
414 | let mut mask_buf = [0u8; 14];
415 | mask_buf.clone_from_slice(rtmask_slice);
416 | mask_buf
417 | },
418 | };
419 |
420 | // construct gateway
421 | let gateway = int_sockaddr {
422 | sa_family: AF_INET as u16,
423 | //sa_void: 0,
424 | sa_data: {
425 | let mut gateway_buf = [0u8; 14];
426 | gateway_buf.clone_from_slice(gateway_slice);
427 | gateway_buf
428 | },
429 | };
430 |
431 | // set device name
432 | let mut dev = [0u8; IF_NAMESIZE];
433 | dev.clone_from_slice(ifname_slice);
434 |
435 | // construct ifroute structure
436 | let mut ifroute = ioctl_v4_route {
437 | rt_hash: 0,
438 | rt_dst: dst_route, // set route
439 | rt_gateway: gateway, // set gateway
440 | rt_genmask: route_mask, // set route's mask
441 | rt_flags: 0, // initialize flags to zero
442 | rt_pad1: 0,
443 | rt_pad2: 0,
444 | rt_tos: 0,
445 | rt_class: 0,
446 | rt_pad3: [0, 0, 0],
447 | rt_metric: metric, // set metric
448 | rt_dev: &dev as *const u8, // set dev
449 | rt_mtu: mtu,
450 | rt_window: 0,
451 | rt_irtt: 0,
452 | };
453 | // set route flags
454 | ifroute.rt_flags |= RTF_UP | libc::RTF_GATEWAY;
455 |
456 | // ioctl - set/delete route
457 | let res: i32;
458 | match op {
459 | Operation::Add => {
460 | print_debug(
461 | debug,
462 | DEBUG_LEVEL_HIGH,
463 | DEBUG_SRC_ROUTE,
464 | format!("adding route {:?}", ifroute),
465 | );
466 | res = unsafe { ioctl(sockfd, libc::SIOCADDRT, &mut ifroute) };
467 | }
468 | Operation::Rem => {
469 | print_debug(
470 | debug,
471 | DEBUG_LEVEL_HIGH,
472 | DEBUG_SRC_ROUTE,
473 | format!("removing route {:?}", ifroute),
474 | );
475 | res = unsafe { ioctl(sockfd, libc::SIOCDELRT, &mut ifroute) };
476 | }
477 | }
478 | // check result of ioctls
479 | if res < 0 {
480 | return Err(io::Error::last_os_error());
481 | }
482 |
483 | Ok(())
484 | }
485 |
--------------------------------------------------------------------------------
/src/os/mod.rs:
--------------------------------------------------------------------------------
1 | //! operating systems support module
2 |
3 | // drivers
4 | pub mod drivers;
5 |
6 | // Linux Operating System support
7 | #[cfg(target_os = "linux")]
8 | pub mod linux;
9 |
10 | // FreeBSD Operating System support
11 | #[cfg(target_os = "freebsd")]
12 | pub mod freebsd;
13 |
14 | // Multi-operating System Support
15 | pub mod multi;
16 |
--------------------------------------------------------------------------------
/src/os/multi/libc.rs:
--------------------------------------------------------------------------------
1 | //! Standard C Library Support (multi-os)
2 |
3 | // std
4 | use std::ffi::CStr;
5 | use std::io;
6 | use std::net::{IpAddr, IpAddr::V4, Ipv4Addr, Ipv6Addr};
7 | use std::ptr;
8 |
9 | // foreign_types
10 | use foreign_types::{ForeignType, ForeignTypeRef};
11 |
12 | // libc-like getifaddrs() function implementation
13 | // Credit sfackler: https://gist.github.com/sfackler/d614e6c130f3462f443e6c0c6255383a
14 | foreign_type! {
15 | #[derive(Debug)]
16 | pub type IfAddrs: Sync + Send {
17 | type CType = libc::ifaddrs;
18 | fn drop = libc::freeifaddrs;
19 | }
20 | }
21 |
22 | impl IfAddrs {
23 | pub fn get() -> io::Result {
24 | unsafe {
25 | let mut ifaddrs = ptr::null_mut();
26 | let r = libc::getifaddrs(&mut ifaddrs);
27 | if r == 0 {
28 | Ok(IfAddrs::from_ptr(ifaddrs))
29 | } else {
30 | Err(io::Error::last_os_error())
31 | }
32 | }
33 | }
34 | }
35 |
36 | impl IfAddrsRef {
37 | // next() method
38 | pub fn next(&self) -> Option<&IfAddrsRef> {
39 | unsafe {
40 | let next = (*self.as_ptr()).ifa_next;
41 | if next.is_null() {
42 | None
43 | } else {
44 | Some(IfAddrsRef::from_ptr(next))
45 | }
46 | }
47 | }
48 |
49 | // name() method
50 | pub fn name(&self) -> &str {
51 | unsafe {
52 | let s = CStr::from_ptr((*self.as_ptr()).ifa_name);
53 | s.to_str().unwrap()
54 | }
55 | }
56 |
57 | // addr() method
58 | pub fn addr(&self) -> Option {
59 | unsafe {
60 | let addr = (*self.as_ptr()).ifa_addr;
61 | if addr.is_null() {
62 | return None;
63 | }
64 |
65 | match (*addr).sa_family as _ {
66 | libc::AF_INET => {
67 | let addr = addr as *mut libc::sockaddr_in;
68 | let addr = Ipv4Addr::from((*addr).sin_addr.s_addr.to_be());
69 | Some(IpAddr::V4(addr))
70 | }
71 | libc::AF_INET6 => {
72 | let addr = addr as *mut libc::sockaddr_in6;
73 | let addr = Ipv6Addr::from((*addr).sin6_addr.s6_addr);
74 | Some(IpAddr::V6(addr))
75 | }
76 | _ => None,
77 | }
78 | }
79 | }
80 |
81 | // netmask() method
82 | pub fn netmask(&self) -> Option {
83 | unsafe {
84 | let netmask = (*self.as_ptr()).ifa_netmask;
85 | if netmask.is_null() {
86 | return None;
87 | }
88 |
89 | match (*netmask).sa_family as _ {
90 | libc::AF_INET => {
91 | let netmask = netmask as *mut libc::sockaddr_in;
92 | let netmask = Ipv4Addr::from((*netmask).sin_addr.s_addr.to_be());
93 | Some(IpAddr::V4(netmask))
94 | }
95 | libc::AF_INET6 => {
96 | let netmask = netmask as *mut libc::sockaddr_in6;
97 | let netmask = Ipv6Addr::from((*netmask).sin6_addr.s6_addr);
98 | Some(IpAddr::V6(netmask))
99 | }
100 | _ => None,
101 | }
102 | }
103 | }
104 |
105 | pub fn iter<'a>(&'a self) -> Iter<'a> {
106 | Iter(Some(self))
107 | }
108 | }
109 |
110 | impl<'a> IntoIterator for &'a IfAddrs {
111 | type Item = &'a IfAddrsRef;
112 | type IntoIter = Iter<'a>;
113 |
114 | fn into_iter(self) -> Iter<'a> {
115 | self.iter()
116 | }
117 | }
118 |
119 | impl<'a> IntoIterator for &'a IfAddrsRef {
120 | type Item = &'a IfAddrsRef;
121 | type IntoIter = Iter<'a>;
122 |
123 | fn into_iter(self) -> Iter<'a> {
124 | self.iter()
125 | }
126 | }
127 |
128 | pub struct Iter<'a>(Option<&'a IfAddrsRef>);
129 |
130 | impl<'a> Iterator for Iter<'a> {
131 | type Item = &'a IfAddrsRef;
132 |
133 | fn next(&mut self) -> Option<&'a IfAddrsRef> {
134 | let cur = match self.0 {
135 | Some(cur) => cur,
136 | None => return None,
137 | };
138 |
139 | self.0 = cur.next();
140 | Some(cur)
141 | }
142 | }
143 |
144 | // get_addrlist() function
145 | /// get list of IP address(es) and store them into vectors
146 | pub fn get_addrlist(
147 | ifname: &String,
148 | v4addrs: &mut Vec<[u8; 4]>,
149 | v4masks: &mut Vec<[u8; 4]>,
150 | ) -> io::Result<()> {
151 | // get list of all ip address per interfaces
152 | let addrlist = IfAddrs::get().unwrap();
153 | // create a vector of tuples (ifname: &str, ipaddr: IpAddr, netmask: IpAddr)
154 | let addrlist = addrlist
155 | .iter()
156 | .map(|a| (a.name(), a.addr(), a.netmask()))
157 | .collect::>();
158 |
159 | // for every tuples in addrlist:
160 | // if the key matches the vr's interface, push the converted IPv4 address
161 | // into the v4addrs vector.
162 | for t in addrlist {
163 | // take the address and netmask of the matching vr's interface
164 | if t.0.to_lowercase() == *ifname {
165 | if let Some(V4(ip)) = t.1 {
166 | v4addrs.push(ip.octets());
167 | if let Some(V4(netmask)) = t.2 {
168 | v4masks.push(netmask.octets())
169 | }
170 | }
171 | }
172 | }
173 |
174 | Ok(())
175 | }
176 |
--------------------------------------------------------------------------------
/src/os/multi/mod.rs:
--------------------------------------------------------------------------------
1 | //! Multi-Operating System Support
2 | // This modules includes codes compatible over multiple operating systems
3 |
4 | // Standard C library
5 | pub mod libc;
6 |
--------------------------------------------------------------------------------
/src/packets.rs:
--------------------------------------------------------------------------------
1 | //! packets handling module
2 | //! This module includes the various packets formats and related functions such as internet checksums calculation function.
3 |
4 | // constants
5 | use crate::constants::*;
6 |
7 | // virtual router
8 | use crate::VirtualRouter;
9 |
10 | /// Raw VRRPv2 Packet Format Structure
11 | /// This is the fixed size portion of a possibly VRRPv2 packet
12 | #[repr(C)]
13 | #[derive(Debug, Clone, Copy)]
14 | pub struct VRRPpkt {
15 | // Ethernet frame headers
16 | dst_mac: [u8; 6], // destination MAC address
17 | src_mac: [u8; 6], // source MAC address
18 | ethertype: u16, // ether type
19 |
20 | // IPv4 packet headers
21 | ipver: u8, // IP version and header length
22 | ipdscp: u8, // DSCP
23 | iplength: u16, // length
24 | ipident: u16, // identifier
25 | ipflags: u16, // flags
26 | ipttl: u8, // TTL
27 | ipproto: u8, // IP Protocol
28 | ipchecksum: u16, // Header Checksum
29 | ipsrc: [u8; 4], // source IP address
30 | ipdst: [u8; 4], // destinatin IP address
31 |
32 | // VRRPv2 packet format (RFC3768)
33 | version: u8, // version/type - 4/4 bits
34 | vrid: u8, // virtual router id - 8 bits
35 | prio: u8, // priority - 8 bits
36 | addrcount: u8, // count ip addr - 8 bits
37 | authtype: u8, // auth type - 8 bits
38 | adverint: u8, // advertisement interval - 8 bits
39 | checksum: u16, // checksum - 16 bits
40 | }
41 |
42 | // VRRPpkt methods
43 | impl VRRPpkt {
44 | // getters
45 | pub fn _dst_mac(&self) -> &[u8; 6] {
46 | &self.dst_mac
47 | }
48 | pub fn _src_mac(&self) -> &[u8; 6] {
49 | &self.src_mac
50 | }
51 | pub fn _ethertype(&self) -> &u16 {
52 | &self.ethertype
53 | }
54 | pub fn ipsrc(&self) -> &[u8; 4] {
55 | &self.ipsrc
56 | }
57 | pub fn ipdst(&self) -> &[u8; 4] {
58 | &self.ipdst
59 | }
60 | pub fn ipttl(&self) -> &u8 {
61 | &self.ipttl
62 | }
63 | pub fn ipproto(&self) -> &u8 {
64 | &self.ipproto
65 | }
66 | pub fn version(&self) -> &u8 {
67 | &self.version
68 | }
69 | pub fn vrid(&self) -> &u8 {
70 | &self.vrid
71 | }
72 | pub fn prio(&self) -> &u8 {
73 | &self.prio
74 | }
75 | pub fn addrcount(&self) -> &u8 {
76 | &self.addrcount
77 | }
78 | // safer getter for addrcount, with checks for valid frame size
79 | pub fn s_addrcount(&self, framesize: usize) -> u8 {
80 | // make sure the address count matches the frame size,
81 | // a valid packet with one address should equal 60 bytes
82 | if framesize != 56 + (self.addrcount as usize * 4) as usize {
83 | return 0u8;
84 | }
85 | self.addrcount
86 | }
87 | pub fn authtype(&self) -> &u8 {
88 | &self.authtype
89 | }
90 | pub fn adverint(&self) -> &u8 {
91 | &self.adverint
92 | }
93 | pub fn checksum(&self) -> &u16 {
94 | &self.checksum
95 | }
96 | // gen_advert() method
97 | // generate a VRRPv2 ADVERTISEMENT packet
98 | pub fn gen_advert(vr: &VirtualRouter) -> VRRPpkt {
99 | // Ethernet frame headers:
100 | // dst multicast MAC address for 224.0.0.18
101 | let dst_mac = ETHER_VRRP_V2_DST_MAC;
102 | // generate source MAC address from VID
103 | let mut src_mac = ETHER_VRRP_V2_SRC_MAC;
104 | src_mac[5] = vr.parameters.vrid();
105 | // ipv4 ethertype
106 | let ethertype = ETHER_P_IP.to_be();
107 |
108 | // IPv4 headers:
109 | let ipver = IP_V4_VERSION;
110 | // dscp (CS6)
111 | let ipdscp = IP_DSCP_CS6;
112 | // lowest total packet length (header+data)
113 | let iplength = 40u16.to_be();
114 | // identification and flags fields to zeros
115 | let ipident = 0x0000;
116 | let ipflags = 0x0000;
117 | // TTL must be set to 255
118 | let ipttl = IP_TTL_VRRP_MINTTL;
119 | // VRRPv2 is IP Proto 112
120 | let ipproto = IP_UPPER_PROTO_VRRP;
121 | // internet checksum (set to all zeros)
122 | let ipchecksum = 0x0000;
123 | // source packet from interface 'primary' ip address
124 | let ipsrc = vr.parameters.primary_ip();
125 | // VRRPv2 multicast group
126 | let ipdst = VRRP_V2_IP_MCAST_DST;
127 |
128 | // VRRPv2 ADVERTISEMENT:
129 | // version = 0x2
130 | // type = 0x1 (ADVERTISEMENT)
131 | let version = VRRP_V2_ADVERT_VERSION_TYPE;
132 | // virtual router id
133 | let vrid = vr.parameters.vrid();
134 | let prio = vr.parameters.prio();
135 | let addrcount = vr.parameters.addrcount();
136 | let authtype = vr.parameters.authtype();
137 | let adverint = vr.parameters.adverint();
138 | // generate checksum on VRRP message
139 | let checksum = 0;
140 |
141 | // return the built VRRP ADVERTISEMENT packet
142 | VRRPpkt {
143 | dst_mac,
144 | src_mac,
145 | ethertype,
146 | ipver,
147 | ipdscp,
148 | iplength,
149 | ipident,
150 | ipflags,
151 | ipttl,
152 | ipproto,
153 | ipchecksum,
154 | ipsrc,
155 | ipdst,
156 | version,
157 | vrid,
158 | prio,
159 | addrcount,
160 | authtype,
161 | adverint,
162 | checksum,
163 | }
164 | }
165 | }
166 |
167 | // as_u8_slice() unsafe function
168 | /// transform type T as slice of u8
169 | pub unsafe fn as_u8_slice(p: &T) -> &[u8] {
170 | ::std::slice::from_raw_parts((p as *const T) as *const u8, ::std::mem::size_of::())
171 | }
172 |
--------------------------------------------------------------------------------
/src/protocols.rs:
--------------------------------------------------------------------------------
1 | //! protocols module
2 | //! This module includes networking protocols data structures and related functions.
3 |
4 | /// Protocols Structure
5 | #[derive(Debug)]
6 | pub struct Protocols {
7 | pub r#static: Option>,
8 | }
9 |
10 | // Protocols Type Imlementation
11 | impl Protocols {
12 | // new() method
13 | pub fn _new(r#static: Option>) -> Protocols {
14 | Protocols { r#static }
15 | }
16 | }
17 |
18 | /// Static Protocol Structure
19 | #[derive(Debug)]
20 | pub struct Static {
21 | route: [u8; 4],
22 | mask: [u8; 4],
23 | nh: [u8; 4],
24 | metric: i16,
25 | mtu: u64,
26 | }
27 |
28 | // Static Protocol Type Implementation
29 | impl Static {
30 | // new() method
31 | pub fn new(route: [u8; 4], mask: [u8; 4], nh: [u8; 4], metric: i16, mtu: u64) -> Static {
32 | Static {
33 | route,
34 | mask,
35 | nh,
36 | metric,
37 | mtu,
38 | }
39 | }
40 | // route() getter
41 | pub fn route(&self) -> [u8; 4] {
42 | self.route
43 | }
44 | // mask() getter
45 | pub fn mask(&self) -> [u8; 4] {
46 | self.mask
47 | }
48 | // nh() getter
49 | pub fn nh(&self) -> [u8; 4] {
50 | self.nh
51 | }
52 | // metric() getter
53 | pub fn metric(&self) -> i16 {
54 | self.metric
55 | }
56 | // mtu() getter
57 | pub fn mtu(&self) -> u64 {
58 | self.mtu
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/threads.rs:
--------------------------------------------------------------------------------
1 | //! threads pool module
2 | //! This module implement the thread pool.
3 | use super::*;
4 |
5 | // concurrency
6 | use std::sync::Arc;
7 | use std::sync::RwLock;
8 | use std::thread;
9 |
10 | // channels
11 | use std::sync::mpsc;
12 |
13 | // debugging
14 | use crate::debug::Verbose;
15 |
16 | // finite state machine
17 | use fsm::{fsm_run, Event};
18 |
19 | /// ThreadPool Structure
20 | pub struct ThreadPool {
21 | workers: Vec,
22 | }
23 |
24 | // ThreadPool Implementation
25 | impl ThreadPool {
26 | // new() method
27 | // Create a new Thread Pool
28 | pub fn new(vrouters: &Vec>>, debug: &Verbose) -> ThreadPool {
29 | // verify the vector is not empty and doesn't exceed 1024 virtual routers
30 | assert!(vrouters.len() > 0 && vrouters.len() < 1024);
31 |
32 | // built a fixed-size vector of workers
33 | let mut workers = Vec::with_capacity(vrouters.len());
34 |
35 | // creating individual workers for every virtual routers
36 | for (id, vr) in vrouters.iter().enumerate() {
37 | // acquire read lock on vr
38 | let vro = vr.read().unwrap();
39 | // create new worker
40 | workers.push(Worker::new(id, Arc::clone(&vr), vro.parameters.fd(), debug));
41 | }
42 |
43 | ThreadPool { workers }
44 | }
45 | // startup() method
46 | // Send startup event to every worker threads
47 | pub fn startup(&self, vrouters: &Vec>>, debug: &Verbose) {
48 | for (id, vr) in vrouters.iter().enumerate() {
49 | // acquire read lock on vr
50 | let vr = vr.read().unwrap();
51 | // print debugging information
52 | print_debug(
53 | debug,
54 | DEBUG_LEVEL_EXTENSIVE,
55 | DEBUG_SRC_THREAD,
56 | format!("sending Startup event to worker threads"),
57 | );
58 | match vr.parameters.notification() {
59 | Some(tx) => tx.lock().unwrap().send(Event::Startup).unwrap(),
60 | None => eprintln!("error(thread): cannot send Startup event for thread {}, channel does not exist", id),
61 | }
62 | }
63 | }
64 | // drop() method
65 | // Custom destructor function for the Thread pool
66 | pub fn drop(&mut self, vrouters: &Vec>>, debug: &Verbose) {
67 | print_debug(
68 | debug,
69 | DEBUG_LEVEL_LOW,
70 | DEBUG_SRC_THREAD,
71 | format!("signaling workers to shut down"),
72 | );
73 | // send Shutdown/Terminate events to all workers
74 | for (id, vr) in vrouters.iter().enumerate() {
75 | // acquire read lock on vr
76 | let vr = vr.read().unwrap();
77 | match vr.parameters.notification() {
78 | Some(tx) => {
79 | // send Shutdown event
80 | tx.lock().unwrap().send(Event::Shutdown).unwrap();
81 | // send Terminate event
82 | tx.lock().unwrap().send(Event::Terminate).unwrap();
83 | }
84 | None => eprintln!(
85 | "error(thread): cannot send Terminate event for {}, channel does not exist",
86 | id
87 | ),
88 | }
89 | }
90 |
91 | // waiting for threads
92 | for worker in &mut self.workers {
93 | // print debugging information
94 | print_debug(
95 | debug,
96 | DEBUG_LEVEL_HIGH,
97 | DEBUG_SRC_THREAD,
98 | format!("waiting for thread {} to exit...", worker.id),
99 | );
100 | // take the thread out of the worker stucture and leave a None
101 | if let Some(thread) = worker.thread.take() {
102 | // Wait for the thread to finish
103 | thread.join().unwrap();
104 | }
105 | }
106 | }
107 | }
108 |
109 | // new Trait FnBox to take ownership of a Self stored in a Box
110 | pub trait FnBox {
111 | fn call_box(self: Box);
112 | }
113 |
114 | // new FnBox trait for any type F having the Trait FnOnce()
115 | impl FnBox for F {
116 | // every FnOnce() closures can now call the .call_box() method
117 | fn call_box(self: Box) {
118 | (*self)()
119 | }
120 | }
121 |
122 | /// Worker Structure
123 | pub struct Worker {
124 | id: usize,
125 | // we wrap thread::JoinHandle in a Option so we can
126 | // consume the thread later when calling .join().
127 | thread: Option>,
128 | }
129 |
130 | // Worker Implementation
131 | impl Worker {
132 | // new() method
133 | fn new(id: usize, vr: Arc>, sockfd: i32, debug: &Verbose) -> Worker {
134 | // creating a pair of sender and receiver channels
135 | let (sender, receiver) = mpsc::channel();
136 | let receiver = Arc::new(Mutex::new(receiver));
137 | let sender = Arc::new(Mutex::new(sender));
138 |
139 | // clone VR for worker thread
140 | let worker_vr = Arc::clone(&vr);
141 |
142 | // clone event channels
143 | let worker_tx = Arc::clone(&sender);
144 | let worker_rx = Arc::clone(&receiver);
145 |
146 | // clone debug
147 | let debug = debug.clone();
148 |
149 | // create worker thread
150 | let worker_thread = thread::spawn(move || {
151 | // print debugging information
152 | print_debug(
153 | &debug,
154 | DEBUG_LEVEL_EXTENSIVE,
155 | DEBUG_SRC_THREADP,
156 | format!("spawning worker thread {}", id),
157 | );
158 | fsm_run(id, &worker_tx, &worker_rx, &worker_vr, sockfd, &debug);
159 | });
160 |
161 | Worker {
162 | id,
163 | thread: Some(worker_thread),
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/timers.rs:
--------------------------------------------------------------------------------
1 | //! Timers related functions module
2 | //! This module implements the various timers threads using tokio.
3 | use super::*;
4 |
5 | // tokio
6 | use tokio::prelude::*;
7 | use tokio::timer::Interval;
8 |
9 | // futures
10 | use futures::Future;
11 |
12 | // channels
13 | use std::sync::mpsc;
14 |
15 | // debugging
16 | use crate::debug::{print_debug, Verbose};
17 |
18 | // fsm
19 | use crate::fsm::Event;
20 |
21 | // std
22 | use std::sync::RwLock;
23 | use std::time::{Duration, Instant};
24 |
25 | // start_timers() function
26 | /// starts the various timers needed for the protocol and internal operations
27 | pub fn start_timers(
28 | tx: Arc>>,
29 | vr: Arc>,
30 | debug: &Verbose,
31 | ) {
32 | // clone vr's Arc
33 | let vr0 = Arc::clone(&vr);
34 | // acquire read lock on virtual router
35 | let vr0 = vr0.read().unwrap();
36 |
37 | // clone debug
38 | let debug = debug.clone();
39 |
40 | // set duration from vr's timer
41 | let master_down = Duration::from_secs(vr0.timers.master_down() as u64);
42 | let advert = Duration::from_secs(vr0.timers.advert() as u64);
43 |
44 | // drop the lock as we don't need read access to vr anymore
45 | drop(vr0);
46 |
47 | // clone tx channel
48 | let tx1 = Arc::clone(&tx);
49 | // clone the vr's Arc
50 | let vr1 = Arc::clone(&vr);
51 | let vr2 = Arc::clone(&vr);
52 |
53 | // new instance of 'master_down' interval
54 | // this is a countdown-type timer which verify that at least one ADVERTISEMEMNT
55 | // packet has been received since the last call of the timer. If the flag equal
56 | // 0x1, no ADVERTISEMENT has been received (since) and the master is signaled
57 | // down to the approriate vr's thread, this timer share the 'tx' channel with.
58 | let master_down_int = Interval::new_interval(master_down)
59 | .take_while(move |_| future::ok(is_master_down_disabled(&vr1, &debug)))
60 | .for_each(move |_| {
61 | print_debug(
62 | &debug,
63 | DEBUG_LEVEL_HIGH,
64 | DEBUG_SRC_TIMER,
65 | format!("master_down interval has expired"),
66 | );
67 |
68 | // first acquire read lock on vr
69 | let vr2 = vr2.read().unwrap();
70 |
71 | // if flag is already set, then signal master down
72 | if vr2.flags.get_down_flag() == 0x1 {
73 | print_debug(
74 | &debug,
75 | DEBUG_LEVEL_EXTENSIVE,
76 | DEBUG_SRC_TIMER,
77 | format!("signaling Master down"),
78 | );
79 | // acquire transmit channel lock
80 | let tx1 = tx1.lock().unwrap();
81 | // send MasterDown Event down the channel
82 | match tx1.send(Event::MasterDown) {
83 | Ok(_) => (),
84 | Err(_) => (), // ignore errors from now
85 | }
86 | // return Ok(())
87 | Ok(())
88 | }
89 | // check if down flag is set
90 | else if vr2.flags.get_down_flag() == 0x0 {
91 | print_debug(
92 | &debug,
93 | DEBUG_LEVEL_EXTENSIVE,
94 | DEBUG_SRC_TIMER,
95 | format!("signaling master_down timer expiry"),
96 | );
97 | // acquire transmit channel lock
98 | let tx1 = tx1.lock().unwrap();
99 | // send MasterDown Event down the channel
100 | tx1.send(Event::MasterDownExpiry).unwrap();
101 | // return Ok(())
102 | Ok(())
103 | } else {
104 | Ok(())
105 | }
106 | })
107 | .map_err(|_| ());
108 |
109 | // clone transmit channel
110 | let tx2 = Arc::clone(&tx);
111 | // clone the vr's Arc
112 | let vr3 = Arc::clone(&vr);
113 |
114 | // new advertisement interval-type timer
115 | // when this future executes, a GenAdvert notification is sent to the worker thread
116 | // which then trigger an ADVERTISEMENT message in some finite state machine states.
117 | let advert_int = Interval::new(Instant::now() + advert, advert)
118 | // must return true to activate the interval timer
119 | .take_while(move |_| future::ok(is_advert_disabled(&vr3, &debug)))
120 | .for_each(move |_| {
121 | // print debugging information
122 | print_debug(
123 | &debug,
124 | DEBUG_LEVEL_HIGH,
125 | DEBUG_SRC_TIMER,
126 | format!("advertisement interval has expired"),
127 | );
128 | // acquire lock on transmit channel
129 | let tx2 = tx2.lock().unwrap();
130 | // print debugging information
131 | print_debug(
132 | &debug,
133 | DEBUG_LEVEL_EXTENSIVE,
134 | DEBUG_SRC_TIMER,
135 | format!("signaling advertisement interval expiry"),
136 | );
137 | // send GenAdvert event to worker thread
138 | tx2.send(Event::GenAdvert).unwrap();
139 | // return Ok(())
140 | Ok(())
141 | })
142 | .map_err(|_| ());
143 |
144 | // start the tokio runtime
145 | tokio::run(future::lazy(|| {
146 | tokio::spawn(master_down_int);
147 | tokio::spawn(advert_int);
148 | Ok(())
149 | }));
150 | }
151 |
152 | // is_master_down_disabled() function
153 | /// return boolean false is the master_down interval is zero or lower
154 | fn is_master_down_disabled(vr: &Arc>, debug: &Verbose) -> bool {
155 | let vr = vr.read().unwrap();
156 | if vr.timers.master_down() > 0.0 {
157 | true
158 | } else {
159 | // print debugging information
160 | print_debug(
161 | debug,
162 | DEBUG_LEVEL_MEDIUM,
163 | DEBUG_SRC_TIMER,
164 | format!("master_down timer is disabled"),
165 | );
166 | false
167 | }
168 | }
169 |
170 | // is_advert_disabled() function
171 | /// return boolean true is the advertisement interval vr's timer
172 | /// is higher than zero
173 | fn is_advert_disabled(vr: &Arc>, debug: &Verbose) -> bool {
174 | let vr = vr.read().unwrap();
175 | if vr.timers.advert() > 0 {
176 | true
177 | } else if vr.timers.advert() == 255 {
178 | // special non-zero value to disable timer
179 | true
180 | } else {
181 | // print debugging information
182 | print_debug(
183 | debug,
184 | DEBUG_LEVEL_MEDIUM,
185 | DEBUG_SRC_TIMER,
186 | format!("the advertisement timer is disabled"),
187 | );
188 | false
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/utils/rvrrpd-pw/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 |
5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
7 | Cargo.lock
8 |
9 | # These are backup files generated by rustfmt
10 | **/*.rs.bk
11 |
12 | # local configuration files
13 | *.local.conf
14 |
--------------------------------------------------------------------------------
/utils/rvrrpd-pw/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rvrrpd-pw"
3 | version = "0.1.0"
4 | authors = ["Nicolas Chabbey "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | clap = "2.33"
9 | rand = "0.7"
10 | sha2 = "0.8"
11 | scrypt = "0.2"
12 |
--------------------------------------------------------------------------------
/utils/rvrrpd-pw/Makefile:
--------------------------------------------------------------------------------
1 | TARGET = target/release
2 | BINARY = rvrrpd-pw
3 | PREFIX = /usr
4 |
5 | main:
6 | @cargo build --release
7 |
8 | test:
9 | @cargo test
10 |
11 | check:
12 | @cargo fmt --all -- --check
13 |
14 | clean:
15 | @cargo clean
16 |
17 | install:
18 | if [ ! -d $(DESTDIR)$(PREFIX)/bin ]; then \
19 | mkdir -p $(DESTDIR)$(PREFIX)/bin; \
20 | fi
21 | cp $(TARGET)/${BINARY} $(DESTDIR)$(PREFIX)/bin/${BINARY}
22 | chmod 755 $(DESTDIR)$(PREFIX)/bin/${BINARY}
23 |
24 | .PHONY: main test check clean install
25 |
--------------------------------------------------------------------------------
/utils/rvrrpd-pw/src/main.rs:
--------------------------------------------------------------------------------
1 | //! rVRRPd-pw - rVRRPd password utility
2 | //! This program provides a fast and easy way to generate hashed passwords for rVRRPd,
3 | //! a fast and highly-secure VRRP daemon.
4 |
5 | // std
6 | use std::error::Error;
7 | use std::fmt;
8 |
9 | // clap
10 | extern crate clap;
11 | use clap::{App, Arg};
12 |
13 | // rand
14 | extern crate rand;
15 | use rand::prelude::Rng;
16 |
17 | // sha256
18 | extern crate sha2;
19 | use sha2::{Digest, Sha256};
20 |
21 | // scrypt
22 | extern crate scrypt;
23 | use scrypt::{scrypt_simple, ScryptParams};
24 |
25 | // MyError type
26 | #[derive(Debug)]
27 | struct MyError {
28 | msg: String,
29 | }
30 |
31 | impl MyError {
32 | fn new(msg: &str) -> MyError {
33 | MyError {
34 | msg: msg.to_string(),
35 | }
36 | }
37 | }
38 |
39 | impl fmt::Display for MyError {
40 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 | write!(f, "Error: {}", self.msg)
42 | }
43 | }
44 |
45 | impl Error for MyError {
46 | fn description(&self) -> &str {
47 | &self.msg
48 | }
49 | }
50 |
51 | // main() function
52 | fn main() {
53 | let matches = App::new("rVRRPd password utility")
54 | .version("v0.1.0")
55 | .author("Nicolas Chabbey ")
56 | .about("Quick and easy password generation for rVRRPd")
57 | .arg(
58 | Arg::with_name("user")
59 | .short("u")
60 | .long("user")
61 | .takes_value(true)
62 | .help("user name")
63 | .required(true)
64 | .index(1),
65 | )
66 | .arg(
67 | Arg::with_name("password")
68 | .short("p")
69 | .long("password")
70 | .takes_value(true)
71 | .help("user's password")
72 | .required(true)
73 | .index(2),
74 | )
75 | .arg(
76 | Arg::with_name("alg")
77 | .short("a")
78 | .long("hash-alg")
79 | .takes_value(true)
80 | .help("hashing algorithm (default: sha256)")
81 | .index(3),
82 | )
83 | .after_help("HASHING ALGS:\n\
84 | sha256\t\tSHA2 (256 bits)\n\
85 | scrypt\t\tscrypt (interactive)\n")
86 | .get_matches();
87 |
88 | let user = matches.value_of("user").unwrap();
89 | let passwd = matches.value_of("password").unwrap();
90 | let alg = matches.value_of("alg").unwrap_or("sha256");
91 |
92 | match gen_hashed_pw(user, passwd, alg) {
93 | Err(e) => {
94 | eprintln!("{}", e);
95 | std::process::exit(1);
96 | }
97 | Ok(_) => std::process::exit(0),
98 | }
99 | }
100 |
101 | /// gen_hashed_pw() function
102 | /// Print new user account information
103 | fn gen_hashed_pw(user: &str, passwd: &str, alg: &str) -> Result<(), MyError> {
104 | match alg {
105 | "sha256" => {
106 | let mut rng = rand::thread_rng();
107 | let r = rng.gen::();
108 | let salt = format!("{:x}", r);
109 | match gen_sha256_hash(&passwd, &salt) {
110 | Some(h) => {
111 | display_userpw_line(alg, user, Some(salt), h);
112 | }
113 | None => {
114 | let err = format!("the {} hashing function failed", alg);
115 | return Err(MyError::new(&err));
116 | }
117 | }
118 | }
119 | "scrypt" => match gen_scrypt_hash(passwd) {
120 | Some(h) => {
121 | display_userpw_line(alg, user, None, h);
122 | }
123 | None => {
124 | let err = format!("the {} hashing function failed", alg);
125 | return Err(MyError::new(&err));
126 | }
127 | },
128 | _ => {
129 | let err = format!("unknown hashing algorithm {}", alg);
130 | return Err(MyError::new(&err));
131 | }
132 | }
133 |
134 | Ok(())
135 | }
136 |
137 | /// gen_sha256_hash() function
138 | /// Generate a SHA256 hashed password with random salt
139 | fn gen_sha256_hash(passwd: &str, salt: &String) -> Option {
140 | // create new sha256 hasher
141 | let mut hasher = Sha256::new();
142 | // feed the hasher with the user supplied password
143 | hasher.input(&passwd);
144 | // feed the hasher with the supplied salt
145 | hasher.input(salt);
146 | // get the result
147 | let h = hasher.result();
148 | // format the result in a hexadecimal string
149 | let hash = format!("{:x}", h);
150 | // return the hash
151 | Some(hash)
152 | }
153 |
154 | /// gen_scrypt_hash() function
155 | /// Generate a scrypt based password
156 | fn gen_scrypt_hash(passwd: &str) -> Option {
157 | // create scrypt parameters with sound values
158 | let params = ScryptParams::new(4, 8, 1).unwrap();
159 | // hash the given password
160 | let hash = scrypt_simple(&passwd, ¶ms).expect("PNRG failure");
161 | // return the hash
162 | Some(hash)
163 | }
164 |
165 | /// display_userpw_line() function
166 | /// Display the user password line for inclusion in rVRRPd configuration
167 | fn display_userpw_line(alg: &str, user: &str, salt: Option, hash: String) {
168 | // not implemented yet
169 | let level = 0;
170 | println!(
171 | "{{{{{}}}}}{}:{}:{}:{}",
172 | alg.to_uppercase(),
173 | user,
174 | level,
175 | salt.unwrap_or("".to_string()),
176 | hash
177 | );
178 | }
179 |
--------------------------------------------------------------------------------