1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// SPDX-License-Identifier: MIT
#![cfg(feature = "tokio_socket")]
use std::{env, error::Error as StdError, str::FromStr};
use rtnetlink::{new_connection, QosMapping};
fn parse_mapping(parameter: &str) -> Result<QosMapping, Box<dyn StdError>> {
let (from, to) = parameter
.split_once(':')
.ok_or("Failed to parse mapping..")?;
Ok(QosMapping {
from: u32::from_str(from)?,
to: u32::from_str(to)?,
})
}
const ARG_BASE: &str = "--base";
const ARG_NAME: &str = "--name";
const ARG_ID: &str = "--id";
const ARG_INGRESS_QOS: &str = "--ingress-qos-mapping";
const ARG_EGRESS_QOS: &str = "--egress-qos-mapping";
enum ParsingMode {
None,
Base,
Name,
Id,
Ingress,
Egress,
}
#[tokio::main]
async fn main() -> Result<(), String> {
let mut args: Vec<String> = env::args().collect();
let mut base_interface = None;
let mut name = None;
let mut id = None;
let mut ingress = Vec::new();
let mut egress = Vec::new();
let mut mode = ParsingMode::None;
for argument in args.drain(1..) {
fn match_argument(argument: String) -> Result<ParsingMode, String> {
match argument.to_lowercase().as_str() {
ARG_BASE => Ok(ParsingMode::Base),
ARG_NAME => Ok(ParsingMode::Name),
ARG_ID => Ok(ParsingMode::Id),
ARG_INGRESS_QOS => Ok(ParsingMode::Ingress),
ARG_EGRESS_QOS => Ok(ParsingMode::Egress),
other => {
usage();
Err(format!("Unexpected argument: {other}"))
}
}
}
mode = match mode {
ParsingMode::None => match_argument(argument)?,
ParsingMode::Base => {
base_interface = u32::from_str(&argument).ok();
ParsingMode::None
}
ParsingMode::Name => {
name = Some(argument);
ParsingMode::None
}
ParsingMode::Id => {
id = u16::from_str(&argument).ok();
ParsingMode::None
}
mode @ ParsingMode::Ingress => match parse_mapping(&argument) {
Ok(mapping) => {
ingress.push(mapping);
mode
}
Err(_) => match_argument(argument)?,
},
mode @ ParsingMode::Egress => match parse_mapping(&argument) {
Ok(mapping) => {
egress.push(mapping);
mode
}
Err(_) => match_argument(argument)?,
},
}
}
let Some(base) = base_interface else {
usage();
return Err(
"Missing or invalid argument for base interface!".to_owned()
);
};
let Some(name) = name else {
usage();
return Err(
"Missing or invalid argument for new interface name!".to_owned()
);
};
let Some(id) = id else {
usage();
return Err("Missing or invalid argument for vlan id!".to_owned());
};
let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);
handle
.link()
.add()
.vlan_with_qos(name, base, id, ingress, egress)
.execute()
.await
.map_err(|err| format!("Netlink request failed: {err}"))
}
fn usage() {
eprintln!(
"usage:
cargo run --example create_vlan -- --base <base link index> --name <link name> --id <vlan id> [--ingress-qos-mapping <mapping as <integer>:<integer> ..>] [--egress-qos-mapping <mapping as <integer>:<integer> ..>]
Note that you need to run this program as root. Instead of running cargo as root,
build the example normally:
cd netlink-ip ; cargo build --example create_vlan
Then find the binary in the target directory:
cd ../target/debug/example ; sudo ./create_vlan <link_name>"
);
}
|