1use std::{collections::HashMap, str::FromStr};
5
6use aya::programs::XdpFlags;
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::util::directories::*;
11
12#[derive(Debug, Deserialize, Default, Clone)]
13pub struct Config {
14 pub interfaces: Option<HashMap<String, InterfaceConfig>>,
15 #[serde(default)]
16 pub grpc: Grpc,
17 pub signing: Option<SigningConfig>,
18}
19
20#[derive(Debug, Deserialize, Clone)]
21pub struct SigningConfig {
22 pub allow_unsigned: bool,
23}
24
25impl Default for SigningConfig {
26 fn default() -> Self {
27 Self {
28 allow_unsigned: true,
30 }
31 }
32}
33
34#[derive(Debug, Error)]
35pub enum ConfigError {
36 #[error("Error parsing config file: {0}")]
37 ParseError(#[from] toml::de::Error),
38}
39
40impl FromStr for Config {
41 type Err = ConfigError;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 toml::from_str(s).map_err(ConfigError::ParseError)
45 }
46}
47
48#[derive(Debug, Deserialize, Copy, Clone)]
49pub struct InterfaceConfig {
50 pub xdp_mode: XdpMode,
51}
52
53#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)]
54#[serde(rename_all = "lowercase")]
55pub enum XdpMode {
56 Skb,
57 Drv,
58 Hw,
59}
60
61impl XdpMode {
62 pub fn as_flags(&self) -> XdpFlags {
63 match self {
64 XdpMode::Skb => XdpFlags::SKB_MODE,
65 XdpMode::Drv => XdpFlags::DRV_MODE,
66 XdpMode::Hw => XdpFlags::HW_MODE,
67 }
68 }
69}
70
71impl ToString for XdpMode {
72 fn to_string(&self) -> String {
73 match self {
74 XdpMode::Skb => "skb".to_string(),
75 XdpMode::Drv => "drv".to_string(),
76 XdpMode::Hw => "hw".to_string(),
77 }
78 }
79}
80
81#[derive(Debug, Deserialize, Clone)]
82pub struct Grpc {
83 #[serde(default)]
84 pub endpoints: Vec<Endpoint>,
85}
86
87impl Default for Grpc {
88 fn default() -> Self {
89 Self {
90 endpoints: vec![Endpoint::default()],
91 }
92 }
93}
94
95#[derive(Debug, Deserialize, Clone)]
96#[serde(tag = "type", rename_all = "lowercase")]
97pub enum Endpoint {
98 Unix {
99 #[serde(default = "default_unix")]
100 path: String,
101 #[serde(default = "default_enabled")]
102 enabled: bool,
103 },
104}
105
106impl Default for Endpoint {
107 fn default() -> Self {
108 Endpoint::Unix {
109 path: default_unix(),
110 enabled: default_enabled(),
111 }
112 }
113}
114
115fn default_unix() -> String {
116 RTPATH_BPFD_SOCKET.to_string()
117}
118
119fn default_enabled() -> bool {
120 true
121}
122
123#[cfg(test)]
124mod test {
125 use super::*;
126
127 #[test]
128 fn test_config_from_invalid_string() {
129 assert!(Config::from_str("i am a teapot").is_err());
130 }
131
132 #[test]
133 fn test_config_single_iface() {
134 let input = r#"
135 [interfaces]
136 [interfaces.eth0]
137 xdp_mode = "drv"
138 "#;
139 let config: Config = toml::from_str(input).expect("error parsing toml input");
140 match config.interfaces {
141 Some(i) => {
142 assert!(i.contains_key("eth0"));
143 assert_eq!(i.get("eth0").unwrap().xdp_mode, XdpMode::Drv)
144 }
145 None => panic!("expected interfaces to be present"),
146 }
147 }
148
149 #[test]
150 fn test_config_multiple_iface() {
151 let input = r#"
152 [interfaces]
153 [interfaces.eth0]
154 xdp_mode = "drv"
155 [interfaces.eth1]
156 xdp_mode = "hw"
157 [interfaces.eth2]
158 xdp_mode = "skb"
159 "#;
160 let config: Config = toml::from_str(input).expect("error parsing toml input");
161 match config.interfaces {
162 Some(i) => {
163 assert_eq!(i.len(), 3);
164 assert!(i.contains_key("eth0"));
165 assert_eq!(i.get("eth0").unwrap().xdp_mode, XdpMode::Drv);
166 assert!(i.contains_key("eth1"));
167 assert_eq!(i.get("eth1").unwrap().xdp_mode, XdpMode::Hw);
168 assert!(i.contains_key("eth2"));
169 assert_eq!(i.get("eth2").unwrap().xdp_mode, XdpMode::Skb);
170 }
171 None => panic!("expected interfaces to be present"),
172 }
173 }
174
175 #[test]
176 fn test_config_endpoint_default() {
177 let input = r#"
178 "#;
179
180 let config: Config = toml::from_str(input).expect("error parsing toml input");
181 let endpoints = config.grpc.endpoints;
182 assert_eq!(endpoints.len(), 1);
183
184 match endpoints.get(0).unwrap() {
185 Endpoint::Unix { path, enabled } => {
186 assert_eq!(path, &default_unix());
187 assert_eq!(enabled, &true);
188 }
189 }
190 }
191
192 #[test]
193 fn test_config_endpoint_unix_default() {
194 let input = r#"
195 [[grpc.endpoints]]
196 type = "unix"
197 "#;
198
199 let config: Config = toml::from_str(input).expect("error parsing toml input");
200 let endpoints = config.grpc.endpoints;
201 assert_eq!(endpoints.len(), 1);
202
203 match endpoints.get(0).unwrap() {
204 Endpoint::Unix { path, enabled } => {
205 assert_eq!(path, &default_unix());
206 assert!(enabled);
207 }
208 }
209 }
210
211 #[test]
212 fn test_config_endpoint_unix() {
213 let input = r#"
214 [[grpc.endpoints]]
215 type = "unix"
216 path = "/tmp/socket"
217 "#;
218
219 let config: Config = toml::from_str(input).expect("error parsing toml input");
220 let endpoints = config.grpc.endpoints;
221 assert_eq!(endpoints.len(), 1);
222
223 match endpoints.get(0).unwrap() {
224 Endpoint::Unix { path, enabled } => {
225 assert_eq!(path, "/tmp/socket");
226 assert!(enabled);
227 }
228 }
229 }
230
231 #[test]
232 fn test_config_endpoint() {
233 let input = r#"
234 [[grpc.endpoints]]
235 type = "unix"
236 enabled = true
237 path = "/run/bpfd/bpfd.sock"
238
239 [[grpc.endpoints]]
240 type = "unix"
241 enabled = true
242 path = "/run/bpfd/bpfd2.sock"
243 "#;
244
245 let expected_endpoints: Vec<Endpoint> = vec![
246 Endpoint::Unix {
247 path: String::from("/run/bpfd/bpfd.sock"),
248 enabled: true,
249 },
250 Endpoint::Unix {
251 path: String::from("/run/bpfd/bpfd2.sock"),
252 enabled: true,
253 },
254 ];
255
256 let config: Config = toml::from_str(input).expect("error parsing toml input");
257 let endpoints = config.grpc.endpoints;
258 assert_eq!(endpoints.len(), 2);
259
260 for (i, endpoint) in endpoints.iter().enumerate() {
261 match endpoint {
262 Endpoint::Unix { path, enabled } => {
263 let Endpoint::Unix {
264 path: expected_path,
265 enabled: expected_enabled,
266 } = expected_endpoints.get(i).unwrap();
267 assert_eq!(path, expected_path);
268 assert_eq!(enabled, expected_enabled);
269 }
270 }
271 }
272 }
273}