]>
Commit | Line | Data |
---|---|---|
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 | } |