]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/net-condition.c
sd-boot+bootctl: invert order of entries w/o sort-key
[thirdparty/systemd.git] / src / shared / net-condition.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/ether.h>
4
5 #include "condition.h"
6 #include "env-util.h"
7 #include "log.h"
8 #include "net-condition.h"
9 #include "netif-util.h"
10 #include "network-util.h"
11 #include "socket-util.h"
12 #include "string-table.h"
13 #include "strv.h"
14 #include "wifi-util.h"
15
16 void net_match_clear(NetMatch *match) {
17 if (!match)
18 return;
19
20 match->hw_addr = set_free(match->hw_addr);
21 match->permanent_hw_addr = set_free(match->permanent_hw_addr);
22 match->path = strv_free(match->path);
23 match->driver = strv_free(match->driver);
24 match->iftype = strv_free(match->iftype);
25 match->kind = strv_free(match->kind);
26 match->ifname = strv_free(match->ifname);
27 match->property = strv_free(match->property);
28 match->wlan_iftype = strv_free(match->wlan_iftype);
29 match->ssid = strv_free(match->ssid);
30 match->bssid = set_free(match->bssid);
31 }
32
33 bool net_match_is_empty(const NetMatch *match) {
34 assert(match);
35
36 return
37 set_isempty(match->hw_addr) &&
38 set_isempty(match->permanent_hw_addr) &&
39 strv_isempty(match->path) &&
40 strv_isempty(match->driver) &&
41 strv_isempty(match->iftype) &&
42 strv_isempty(match->kind) &&
43 strv_isempty(match->ifname) &&
44 strv_isempty(match->property) &&
45 strv_isempty(match->wlan_iftype) &&
46 strv_isempty(match->ssid) &&
47 set_isempty(match->bssid);
48 }
49
50 static bool net_condition_test_strv(char * const *patterns, const char *string) {
51 char * const *p;
52 bool match = false, has_positive_rule = false;
53
54 if (strv_isempty(patterns))
55 return true;
56
57 STRV_FOREACH(p, patterns) {
58 const char *q = *p;
59 bool invert;
60
61 invert = *q == '!';
62 q += invert;
63
64 if (!invert)
65 has_positive_rule = true;
66
67 if (string && fnmatch(q, string, 0) == 0) {
68 if (invert)
69 return false;
70 else
71 match = true;
72 }
73 }
74
75 return has_positive_rule ? match : true;
76 }
77
78 static bool net_condition_test_ifname(char * const *patterns, const char *ifname, char * const *alternative_names) {
79 if (net_condition_test_strv(patterns, ifname))
80 return true;
81
82 char * const *p;
83 STRV_FOREACH(p, alternative_names)
84 if (net_condition_test_strv(patterns, *p))
85 return true;
86
87 return false;
88 }
89
90 static int net_condition_test_property(char * const *match_property, sd_device *device) {
91 char * const *p;
92
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 = rvalue;
241 char ***sv = data;
242 bool invert;
243 int r;
244
245 assert(filename);
246 assert(lvalue);
247 assert(rvalue);
248 assert(data);
249
250 if (isempty(rvalue)) {
251 *sv = strv_free(*sv);
252 return 0;
253 }
254
255 invert = *p == '!';
256 p += invert;
257
258 for (;;) {
259 _cleanup_free_ char *word = NULL, *k = NULL;
260
261 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
262 if (r == 0)
263 return 0;
264 if (r == -ENOMEM)
265 return log_oom();
266 if (r < 0) {
267 log_syntax(unit, LOG_WARNING, filename, line, r,
268 "Invalid syntax, ignoring: %s", rvalue);
269 return 0;
270 }
271
272 if (invert) {
273 k = strjoin("!", word);
274 if (!k)
275 return log_oom();
276 } else
277 k = TAKE_PTR(word);
278
279 r = strv_consume(sv, TAKE_PTR(k));
280 if (r < 0)
281 return log_oom();
282 }
283 }
284
285 int config_parse_match_ifnames(
286 const char *unit,
287 const char *filename,
288 unsigned line,
289 const char *section,
290 unsigned section_line,
291 const char *lvalue,
292 int ltype,
293 const char *rvalue,
294 void *data,
295 void *userdata) {
296
297 const char *p = rvalue;
298 char ***sv = data;
299 bool invert;
300 int r;
301
302 assert(filename);
303 assert(lvalue);
304 assert(rvalue);
305 assert(data);
306
307 if (isempty(rvalue)) {
308 *sv = strv_free(*sv);
309 return 0;
310 }
311
312 invert = *p == '!';
313 p += invert;
314
315 for (;;) {
316 _cleanup_free_ char *word = NULL, *k = NULL;
317
318 r = extract_first_word(&p, &word, NULL, 0);
319 if (r == 0)
320 return 0;
321 if (r == -ENOMEM)
322 return log_oom();
323 if (r < 0) {
324 log_syntax(unit, LOG_WARNING, filename, line, 0,
325 "Failed to parse interface name list, ignoring: %s", rvalue);
326 return 0;
327 }
328
329 if (!ifname_valid_full(word, ltype)) {
330 log_syntax(unit, LOG_WARNING, filename, line, 0,
331 "Interface name is not valid or too long, ignoring assignment: %s", word);
332 continue;
333 }
334
335 if (invert) {
336 k = strjoin("!", word);
337 if (!k)
338 return log_oom();
339 } else
340 k = TAKE_PTR(word);
341
342 r = strv_consume(sv, TAKE_PTR(k));
343 if (r < 0)
344 return log_oom();
345 }
346 }
347
348 int config_parse_match_property(
349 const char *unit,
350 const char *filename,
351 unsigned line,
352 const char *section,
353 unsigned section_line,
354 const char *lvalue,
355 int ltype,
356 const char *rvalue,
357 void *data,
358 void *userdata) {
359
360 const char *p = rvalue;
361 char ***sv = data;
362 bool invert;
363 int r;
364
365 assert(filename);
366 assert(lvalue);
367 assert(rvalue);
368 assert(data);
369
370 if (isempty(rvalue)) {
371 *sv = strv_free(*sv);
372 return 0;
373 }
374
375 invert = *p == '!';
376 p += invert;
377
378 for (;;) {
379 _cleanup_free_ char *word = NULL, *k = NULL;
380
381 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
382 if (r == 0)
383 return 0;
384 if (r == -ENOMEM)
385 return log_oom();
386 if (r < 0) {
387 log_syntax(unit, LOG_WARNING, filename, line, 0,
388 "Invalid syntax, ignoring: %s", rvalue);
389 return 0;
390 }
391
392 if (!env_assignment_is_valid(word)) {
393 log_syntax(unit, LOG_WARNING, filename, line, 0,
394 "Invalid property or value, ignoring assignment: %s", word);
395 continue;
396 }
397
398 if (invert) {
399 k = strjoin("!", word);
400 if (!k)
401 return log_oom();
402 } else
403 k = TAKE_PTR(word);
404
405 r = strv_consume(sv, TAKE_PTR(k));
406 if (r < 0)
407 return log_oom();
408 }
409 }