]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/net-condition.c
52b9374ff09007ebd106437275dfe4f071be7096
[thirdparty/systemd.git] / src / shared / net-condition.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fnmatch.h>
4
5 #include "sd-device.h"
6
7 #include "alloc-util.h"
8 #include "condition.h"
9 #include "env-util.h"
10 #include "extract-word.h"
11 #include "log.h"
12 #include "net-condition.h"
13 #include "netif-util.h"
14 #include "set.h"
15 #include "socket-util.h"
16 #include "string-util.h"
17 #include "strv.h"
18 #include "wifi-util.h"
19
20 void net_match_clear(NetMatch *match) {
21 if (!match)
22 return;
23
24 match->hw_addr = set_free(match->hw_addr);
25 match->permanent_hw_addr = set_free(match->permanent_hw_addr);
26 match->path = strv_free(match->path);
27 match->driver = strv_free(match->driver);
28 match->iftype = strv_free(match->iftype);
29 match->kind = strv_free(match->kind);
30 match->ifname = strv_free(match->ifname);
31 match->property = strv_free(match->property);
32 match->wlan_iftype = strv_free(match->wlan_iftype);
33 match->ssid = strv_free(match->ssid);
34 match->bssid = set_free(match->bssid);
35 }
36
37 bool net_match_is_empty(const NetMatch *match) {
38 assert(match);
39
40 return
41 set_isempty(match->hw_addr) &&
42 set_isempty(match->permanent_hw_addr) &&
43 strv_isempty(match->path) &&
44 strv_isempty(match->driver) &&
45 strv_isempty(match->iftype) &&
46 strv_isempty(match->kind) &&
47 strv_isempty(match->ifname) &&
48 strv_isempty(match->property) &&
49 strv_isempty(match->wlan_iftype) &&
50 strv_isempty(match->ssid) &&
51 set_isempty(match->bssid);
52 }
53
54 static bool net_condition_test_strv(char * const *patterns, const char *string) {
55 bool match = false, has_positive_rule = false;
56
57 if (strv_isempty(patterns))
58 return true;
59
60 STRV_FOREACH(p, patterns) {
61 const char *q = *p;
62 bool invert;
63
64 invert = *q == '!';
65 q += invert;
66
67 if (!invert)
68 has_positive_rule = true;
69
70 if (string && fnmatch(q, string, 0) == 0) {
71 if (invert)
72 return false;
73 else
74 match = true;
75 }
76 }
77
78 return has_positive_rule ? match : true;
79 }
80
81 static bool net_condition_test_ifname(char * const *patterns, const char *ifname, char * const *alternative_names) {
82 if (net_condition_test_strv(patterns, ifname))
83 return true;
84
85 STRV_FOREACH(p, alternative_names)
86 if (net_condition_test_strv(patterns, *p))
87 return true;
88
89 return false;
90 }
91
92 static int net_condition_test_property(char * const *match_property, sd_device *device) {
93 if (strv_isempty(match_property))
94 return true;
95
96 STRV_FOREACH(p, match_property) {
97 _cleanup_free_ char *key = NULL;
98 const char *val, *dev_val;
99 bool invert, v;
100
101 invert = **p == '!';
102
103 val = strchr(*p + invert, '=');
104 if (!val)
105 return -EINVAL;
106
107 key = strndup(*p + invert, val - *p - invert);
108 if (!key)
109 return -ENOMEM;
110
111 val++;
112
113 v = device &&
114 sd_device_get_property_value(device, key, &dev_val) >= 0 &&
115 fnmatch(val, dev_val, 0) == 0;
116
117 if (invert ? v : !v)
118 return false;
119 }
120
121 return true;
122 }
123
124 int net_match_config(
125 const NetMatch *match,
126 sd_device *device,
127 const struct hw_addr_data *hw_addr,
128 const struct hw_addr_data *permanent_hw_addr,
129 const char *driver,
130 unsigned short iftype,
131 const char *kind,
132 const char *ifname,
133 char * const *alternative_names,
134 enum nl80211_iftype wlan_iftype,
135 const char *ssid,
136 const struct ether_addr *bssid) {
137
138 _cleanup_free_ char *iftype_str = NULL;
139 const char *path = NULL;
140
141 assert(match);
142
143 if (net_get_type_string(device, iftype, &iftype_str) == -ENOMEM)
144 return -ENOMEM;
145
146 if (device)
147 (void) sd_device_get_property_value(device, "ID_PATH", &path);
148
149 if (match->hw_addr && (!hw_addr || !set_contains(match->hw_addr, hw_addr)))
150 return false;
151
152 if (match->permanent_hw_addr &&
153 (!permanent_hw_addr ||
154 !set_contains(match->permanent_hw_addr, permanent_hw_addr)))
155 return false;
156
157 if (!net_condition_test_strv(match->path, path))
158 return false;
159
160 if (!net_condition_test_strv(match->driver, driver))
161 return false;
162
163 if (!net_condition_test_strv(match->iftype, iftype_str))
164 return false;
165
166 if (!net_condition_test_strv(match->kind, kind))
167 return false;
168
169 if (!net_condition_test_ifname(match->ifname, ifname, alternative_names))
170 return false;
171
172 if (!net_condition_test_property(match->property, device))
173 return false;
174
175 if (!net_condition_test_strv(match->wlan_iftype, nl80211_iftype_to_string(wlan_iftype)))
176 return false;
177
178 if (!net_condition_test_strv(match->ssid, ssid))
179 return false;
180
181 if (match->bssid && (!bssid || !set_contains(match->bssid, bssid)))
182 return false;
183
184 return true;
185 }
186
187 int config_parse_net_condition(
188 const char *unit,
189 const char *filename,
190 unsigned line,
191 const char *section,
192 unsigned section_line,
193 const char *lvalue,
194 int ltype,
195 const char *rvalue,
196 void *data,
197 void *userdata) {
198
199 ConditionType cond = ltype;
200 Condition **list = data, *c;
201 bool negate;
202
203 assert(filename);
204 assert(lvalue);
205 assert(rvalue);
206 assert(data);
207
208 if (isempty(rvalue)) {
209 *list = condition_free_list_type(*list, cond);
210 return 0;
211 }
212
213 negate = rvalue[0] == '!';
214 if (negate)
215 rvalue++;
216
217 c = condition_new(cond, rvalue, false, negate);
218 if (!c)
219 return log_oom();
220
221 /* Drop previous assignment. */
222 *list = condition_free_list_type(*list, cond);
223
224 LIST_PREPEND(conditions, *list, c);
225 return 0;
226 }
227
228 int config_parse_match_strv(
229 const char *unit,
230 const char *filename,
231 unsigned line,
232 const char *section,
233 unsigned section_line,
234 const char *lvalue,
235 int ltype,
236 const char *rvalue,
237 void *data,
238 void *userdata) {
239
240 const char *p = ASSERT_PTR(rvalue);
241 char ***sv = ASSERT_PTR(data);
242 bool invert;
243 int r;
244
245 assert(filename);
246 assert(lvalue);
247
248 if (isempty(rvalue)) {
249 *sv = strv_free(*sv);
250 return 0;
251 }
252
253 invert = *p == '!';
254 p += invert;
255
256 for (;;) {
257 _cleanup_free_ char *word = NULL, *k = NULL;
258
259 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
260 if (r == 0)
261 return 0;
262 if (r == -ENOMEM)
263 return log_oom();
264 if (r < 0) {
265 log_syntax(unit, LOG_WARNING, filename, line, r,
266 "Invalid syntax, ignoring: %s", rvalue);
267 return 0;
268 }
269
270 if (invert) {
271 k = strjoin("!", word);
272 if (!k)
273 return log_oom();
274 } else
275 k = TAKE_PTR(word);
276
277 r = strv_consume(sv, TAKE_PTR(k));
278 if (r < 0)
279 return log_oom();
280 }
281 }
282
283 int config_parse_match_ifnames(
284 const char *unit,
285 const char *filename,
286 unsigned line,
287 const char *section,
288 unsigned section_line,
289 const char *lvalue,
290 int ltype,
291 const char *rvalue,
292 void *data,
293 void *userdata) {
294
295 const char *p = ASSERT_PTR(rvalue);
296 char ***sv = ASSERT_PTR(data);
297 bool invert;
298 int r;
299
300 assert(filename);
301 assert(lvalue);
302
303 if (isempty(rvalue)) {
304 *sv = strv_free(*sv);
305 return 0;
306 }
307
308 invert = *p == '!';
309 p += invert;
310
311 for (;;) {
312 _cleanup_free_ char *word = NULL, *k = NULL;
313
314 r = extract_first_word(&p, &word, NULL, 0);
315 if (r == 0)
316 return 0;
317 if (r == -ENOMEM)
318 return log_oom();
319 if (r < 0) {
320 log_syntax(unit, LOG_WARNING, filename, line, 0,
321 "Failed to parse interface name list, ignoring: %s", rvalue);
322 return 0;
323 }
324
325 if (!ifname_valid_full(word, ltype)) {
326 log_syntax(unit, LOG_WARNING, filename, line, 0,
327 "Interface name is not valid or too long, ignoring assignment: %s", word);
328 continue;
329 }
330
331 if (invert) {
332 k = strjoin("!", word);
333 if (!k)
334 return log_oom();
335 } else
336 k = TAKE_PTR(word);
337
338 r = strv_consume(sv, TAKE_PTR(k));
339 if (r < 0)
340 return log_oom();
341 }
342 }
343
344 int config_parse_match_property(
345 const char *unit,
346 const char *filename,
347 unsigned line,
348 const char *section,
349 unsigned section_line,
350 const char *lvalue,
351 int ltype,
352 const char *rvalue,
353 void *data,
354 void *userdata) {
355
356 const char *p = ASSERT_PTR(rvalue);
357 char ***sv = ASSERT_PTR(data);
358 bool invert;
359 int r;
360
361 assert(filename);
362 assert(lvalue);
363
364 if (isempty(rvalue)) {
365 *sv = strv_free(*sv);
366 return 0;
367 }
368
369 invert = *p == '!';
370 p += invert;
371
372 for (;;) {
373 _cleanup_free_ char *word = NULL, *k = NULL;
374
375 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
376 if (r == 0)
377 return 0;
378 if (r == -ENOMEM)
379 return log_oom();
380 if (r < 0) {
381 log_syntax(unit, LOG_WARNING, filename, line, 0,
382 "Invalid syntax, ignoring: %s", rvalue);
383 return 0;
384 }
385
386 if (!env_assignment_is_valid(word)) {
387 log_syntax(unit, LOG_WARNING, filename, line, 0,
388 "Invalid property or value, ignoring assignment: %s", word);
389 continue;
390 }
391
392 if (invert) {
393 k = strjoin("!", word);
394 if (!k)
395 return log_oom();
396 } else
397 k = TAKE_PTR(word);
398
399 r = strv_consume(sv, TAKE_PTR(k));
400 if (r < 0)
401 return log_oom();
402 }
403 }