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