File: create_vlan.rs

package info (click to toggle)
rust-rtnetlink 0.14.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 828 kB
  • sloc: makefile: 2
file content (136 lines) | stat: -rw-r--r-- 3,977 bytes parent folder | download | duplicates (2)
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>"
    );
}