1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: MIT

//! Generic netlink controller implementation
//!
//! This module provides the definition of the controller packet.
//! It also serves as an example for creating a generic family.

use self::nlas::*;
use crate::constants::*;
use crate::traits::*;
use crate::GenlHeader;
use anyhow::Context;
use netlink_packet_utils::nla::NlasIterator;
use netlink_packet_utils::traits::*;
use netlink_packet_utils::DecodeError;
use std::convert::{TryFrom, TryInto};

/// Netlink attributes for this family
pub mod nlas;

/// Command code definition of Netlink controller (nlctrl) family
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GenlCtrlCmd {
    /// Notify from event
    NewFamily,
    /// Notify from event
    DelFamily,
    /// Request to get family info
    GetFamily,
    /// Currently unused
    NewOps,
    /// Currently unused
    DelOps,
    /// Currently unused
    GetOps,
    /// Notify from event
    NewMcastGrp,
    /// Notify from event
    DelMcastGrp,
    /// Currently unused
    GetMcastGrp,
    /// Request to get family policy
    GetPolicy,
}

impl From<GenlCtrlCmd> for u8 {
    fn from(cmd: GenlCtrlCmd) -> u8 {
        use GenlCtrlCmd::*;
        match cmd {
            NewFamily => CTRL_CMD_NEWFAMILY,
            DelFamily => CTRL_CMD_DELFAMILY,
            GetFamily => CTRL_CMD_GETFAMILY,
            NewOps => CTRL_CMD_NEWOPS,
            DelOps => CTRL_CMD_DELOPS,
            GetOps => CTRL_CMD_GETOPS,
            NewMcastGrp => CTRL_CMD_NEWMCAST_GRP,
            DelMcastGrp => CTRL_CMD_DELMCAST_GRP,
            GetMcastGrp => CTRL_CMD_GETMCAST_GRP,
            GetPolicy => CTRL_CMD_GETPOLICY,
        }
    }
}

impl TryFrom<u8> for GenlCtrlCmd {
    type Error = DecodeError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        use GenlCtrlCmd::*;
        Ok(match value {
            CTRL_CMD_NEWFAMILY => NewFamily,
            CTRL_CMD_DELFAMILY => DelFamily,
            CTRL_CMD_GETFAMILY => GetFamily,
            CTRL_CMD_NEWOPS => NewOps,
            CTRL_CMD_DELOPS => DelOps,
            CTRL_CMD_GETOPS => GetOps,
            CTRL_CMD_NEWMCAST_GRP => NewMcastGrp,
            CTRL_CMD_DELMCAST_GRP => DelMcastGrp,
            CTRL_CMD_GETMCAST_GRP => GetMcastGrp,
            CTRL_CMD_GETPOLICY => GetPolicy,
            cmd => return Err(DecodeError::from(format!("Unknown control command: {cmd}"))),
        })
    }
}

/// Payload of generic netlink controller
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GenlCtrl {
    /// Command code of this message
    pub cmd: GenlCtrlCmd,
    /// Netlink attributes in this message
    pub nlas: Vec<GenlCtrlAttrs>,
}

impl GenlFamily for GenlCtrl {
    fn family_name() -> &'static str {
        "nlctrl"
    }

    fn family_id(&self) -> u16 {
        GENL_ID_CTRL
    }

    fn command(&self) -> u8 {
        self.cmd.into()
    }

    fn version(&self) -> u8 {
        2
    }
}

impl Emitable for GenlCtrl {
    fn emit(&self, buffer: &mut [u8]) {
        self.nlas.as_slice().emit(buffer)
    }

    fn buffer_len(&self) -> usize {
        self.nlas.as_slice().buffer_len()
    }
}

impl ParseableParametrized<[u8], GenlHeader> for GenlCtrl {
    type Error = DecodeError;
    fn parse_with_param(buf: &[u8], header: GenlHeader) -> Result<Self, DecodeError> {
        Ok(Self { cmd: header.cmd.try_into()?, nlas: parse_ctrlnlas(buf)? })
    }
}

fn parse_ctrlnlas(buf: &[u8]) -> Result<Vec<GenlCtrlAttrs>, DecodeError> {
    let nlas = NlasIterator::new(buf)
        .map(|nla| {
            nla.map_err(|err| DecodeError::Nla(err)).and_then(|nla| GenlCtrlAttrs::parse(&nla))
        })
        .collect::<Result<Vec<_>, _>>()
        .context("failed to parse control message attributes")?;

    Ok(nlas)
}