nmstate/nm/query_apply/
lldp.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::fmt::Write;
4
5use super::super::nm_dbus::{
6    NmConnection, NmLldpNeighbor, NmLldpNeighbor8021Vlan,
7};
8
9use crate::{
10    LldpAddressFamily, LldpChassisId, LldpConfig, LldpMacPhy, LldpMaxFrameSize,
11    LldpMgmtAddr, LldpMgmtAddrs, LldpNeighborTlv, LldpPortId, LldpPpvids,
12    LldpSystemCapabilities, LldpSystemDescription, LldpSystemName, LldpVlan,
13    LldpVlans,
14};
15
16pub(crate) fn is_lldp_enabled(nm_conn: &NmConnection) -> bool {
17    nm_conn.connection.as_ref().and_then(|s| s.lldp) == Some(true)
18}
19
20pub(crate) fn get_lldp(nm_infos: Vec<NmLldpNeighbor>) -> LldpConfig {
21    let mut neighbors = Vec::new();
22    for nm_info in nm_infos {
23        let tlvs = nm_neighbor_to_nmstate(&nm_info);
24        if !tlvs.is_empty() {
25            neighbors.push(tlvs);
26        }
27    }
28    LldpConfig {
29        enabled: true,
30        neighbors,
31    }
32}
33
34fn nm_neighbor_to_nmstate(nm_info: &NmLldpNeighbor) -> Vec<LldpNeighborTlv> {
35    let mut ret = Vec::new();
36
37    if let Some(c) = get_sys_name(nm_info) {
38        ret.push(c)
39    }
40    if let Some(c) = get_sys_description(nm_info) {
41        ret.push(c)
42    }
43    if let Some(c) = get_sys_caps(nm_info) {
44        ret.push(c)
45    }
46    if let Some(c) = get_chassis_id(nm_info) {
47        ret.push(c)
48    }
49    if let Some(c) = get_port_id(nm_info) {
50        ret.push(c)
51    }
52    if let Some(c) = get_vlans(nm_info) {
53        ret.push(c)
54    }
55    if let Some(c) = get_mac_phy_conf(nm_info) {
56        ret.push(c)
57    }
58    if let Some(c) = get_ppvids(nm_info) {
59        ret.push(c)
60    }
61    if let Some(c) = get_mgmt_addrs(nm_info) {
62        ret.push(c)
63    }
64    if let Some(c) = get_max_frame_size(nm_info) {
65        ret.push(c)
66    }
67
68    ret
69}
70
71impl From<&[NmLldpNeighbor8021Vlan]> for LldpVlans {
72    fn from(nm_vlans: &[NmLldpNeighbor8021Vlan]) -> Self {
73        let mut vlans = Vec::new();
74        for nm_vlan in nm_vlans {
75            if let (Some(vid), Some(name)) = (nm_vlan.vid, &nm_vlan.name) {
76                vlans.push(LldpVlan {
77                    vid,
78                    name: name.to_string(),
79                });
80            }
81        }
82
83        LldpVlans::new(vlans)
84    }
85}
86
87fn u8_addr_to_mac_string(data: &[u8]) -> String {
88    let mut addr = String::new();
89    for (i, &val) in data.iter().enumerate() {
90        let _ = write!(addr, "{val:02X}");
91        if i != data.len() - 1 {
92            addr.push(':');
93        }
94    }
95    addr
96}
97
98fn get_chassis_id(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
99    if let (Some(id_type), Some(id)) =
100        (nm_info.chassis_id_type, nm_info.chassis_id.as_deref())
101    {
102        if let Ok(id_type) = u8::try_from(id_type) {
103            return Some(LldpNeighborTlv::ChassisId(LldpChassisId::new(
104                id.to_string(),
105                id_type.into(),
106            )));
107        } else {
108            log::warn!(
109                "Got unsupported chassis_id_type {id_type}, expecting u8"
110            );
111        }
112    }
113    None
114}
115
116fn get_port_id(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
117    if let (Some(id_type), Some(id)) =
118        (nm_info.port_id_type, nm_info.port_id.as_deref())
119    {
120        if let Ok(id_type) = u8::try_from(id_type) {
121            return Some(LldpNeighborTlv::PortId(LldpPortId::new(
122                id.to_string(),
123                id_type.into(),
124            )));
125        } else {
126            log::warn!("Got unsupported port_id_type {id_type}, expecting u8");
127        }
128    }
129    None
130}
131
132fn get_sys_caps(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
133    if let Some(s) = nm_info.system_capabilities {
134        if let Ok(caps) = u16::try_from(s) {
135            return Some(LldpNeighborTlv::SystemCapabilities(
136                LldpSystemCapabilities::from(caps),
137            ));
138        }
139    }
140    None
141}
142
143fn get_sys_name(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
144    if let Some(s) = nm_info.system_name.as_deref() {
145        return Some(LldpNeighborTlv::SystemName(LldpSystemName::new(
146            s.to_string(),
147        )));
148    }
149    None
150}
151
152fn get_sys_description(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
153    if let Some(s) = nm_info.system_description.as_deref() {
154        return Some(LldpNeighborTlv::SystemDescription(
155            LldpSystemDescription::new(s.to_string()),
156        ));
157    }
158    None
159}
160
161fn get_vlans(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
162    if let Some(nm_vlans) = nm_info.ieee_802_1_vlans.as_deref() {
163        return Some(LldpNeighborTlv::Ieee8021Vlans(nm_vlans.into()));
164    }
165    None
166}
167
168fn get_mac_phy_conf(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
169    if let Some(nm_conf) = nm_info.ieee_802_3_mac_phy_conf.as_ref() {
170        if let (Some(a), Some(p), Some(o)) = (
171            nm_conf.autoneg,
172            nm_conf.pmd_autoneg_cap,
173            nm_conf.operational_mau_type,
174        ) {
175            if let (Ok(o), Ok(p)) = (u16::try_from(o), u16::try_from(p)) {
176                return Some(LldpNeighborTlv::Ieee8023MacPhyConf(
177                    LldpMacPhy::new(a > 0, o, p),
178                ));
179            }
180        }
181    }
182    None
183}
184
185fn get_ppvids(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
186    if let Some(nm_ppvids) = nm_info.ieee_802_1_ppvids.as_ref() {
187        let mut ppvids = Vec::new();
188        for nm_ppvid in nm_ppvids {
189            if let Some(p) = nm_ppvid.ppvid {
190                ppvids.push(p);
191            }
192        }
193        return Some(LldpNeighborTlv::Ieee8021Ppvids(LldpPpvids::new(ppvids)));
194    }
195    None
196}
197
198fn get_mgmt_addrs(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
199    if let Some(nm_mgmt_addrs) = nm_info.management_addresses.as_deref() {
200        let mut addrs = Vec::new();
201        for nm_mgmt_addr in nm_mgmt_addrs {
202            if let (Some(at), Some(a), Some(it), Some(i)) = (
203                nm_mgmt_addr.address_subtype,
204                nm_mgmt_addr.address.as_ref(),
205                nm_mgmt_addr.interface_number_subtype,
206                nm_mgmt_addr.interface_number,
207            ) {
208                if let Ok(at) = u16::try_from(at) {
209                    let mut mgmt_addr = LldpMgmtAddr::default();
210                    let at = LldpAddressFamily::from(at);
211                    let addr = match at {
212                        LldpAddressFamily::Ipv4 => {
213                            if a.len() != 4 {
214                                continue;
215                            }
216                            std::net::Ipv4Addr::new(a[0], a[1], a[2], a[3])
217                                .to_string()
218                        }
219                        LldpAddressFamily::Ipv6 => {
220                            if a.len() != 16 {
221                                continue;
222                            }
223                            let mut buff = [0u8; 16];
224                            buff.copy_from_slice(&a[..16]);
225                            std::net::Ipv6Addr::from(buff).to_string()
226                        }
227                        LldpAddressFamily::Mac => u8_addr_to_mac_string(a),
228                        _ => {
229                            continue;
230                        }
231                    };
232
233                    mgmt_addr.address_subtype = at;
234                    mgmt_addr.address = addr;
235                    mgmt_addr.interface_number_subtype = it;
236                    mgmt_addr.interface_number = i;
237                    addrs.push(mgmt_addr);
238                }
239            }
240        }
241        return Some(LldpNeighborTlv::ManagementAddresses(LldpMgmtAddrs::new(
242            addrs,
243        )));
244    }
245    None
246}
247
248fn get_max_frame_size(nm_info: &NmLldpNeighbor) -> Option<LldpNeighborTlv> {
249    if let Some(s) = nm_info.ieee_802_3_max_frame_size {
250        return Some(LldpNeighborTlv::Ieee8023MaxFrameSize(
251            LldpMaxFrameSize::new(s),
252        ));
253    }
254    None
255}