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
net_match_clear(NetMatch * match)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
net_match_is_empty(const NetMatch * match)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
net_condition_test_strv(char * const * patterns,const char * string)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
net_condition_test_ifname(char * const * patterns,const char * ifname,char * const * alternative_names)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
net_condition_test_property(char * const * match_property,sd_device * device)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
net_match_config(const NetMatch * match,sd_device * device,const struct hw_addr_data * hw_addr,const struct hw_addr_data * permanent_hw_addr,const char * driver,unsigned short iftype,const char * kind,const char * ifname,char * const * alternative_names,enum nl80211_iftype wlan_iftype,const char * ssid,const struct ether_addr * bssid)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
config_parse_net_condition(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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
config_parse_match_strv(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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 = rvalue;
237 char ***sv = data;
238 bool invert;
239 int r;
240
241 assert(filename);
242 assert(lvalue);
243 assert(rvalue);
244 assert(data);
245
246 if (isempty(rvalue)) {
247 *sv = strv_free(*sv);
248 return 0;
249 }
250
251 invert = *p == '!';
252 p += invert;
253
254 for (;;) {
255 _cleanup_free_ char *word = NULL, *k = NULL;
256
257 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
258 if (r == 0)
259 return 0;
260 if (r == -ENOMEM)
261 return log_oom();
262 if (r < 0) {
263 log_syntax(unit, LOG_WARNING, filename, line, r,
264 "Invalid syntax, ignoring: %s", rvalue);
265 return 0;
266 }
267
268 if (invert) {
269 k = strjoin("!", word);
270 if (!k)
271 return log_oom();
272 } else
273 k = TAKE_PTR(word);
274
275 r = strv_consume(sv, TAKE_PTR(k));
276 if (r < 0)
277 return log_oom();
278 }
279 }
280
config_parse_match_ifnames(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)281 int config_parse_match_ifnames(
282 const char *unit,
283 const char *filename,
284 unsigned line,
285 const char *section,
286 unsigned section_line,
287 const char *lvalue,
288 int ltype,
289 const char *rvalue,
290 void *data,
291 void *userdata) {
292
293 const char *p = rvalue;
294 char ***sv = data;
295 bool invert;
296 int r;
297
298 assert(filename);
299 assert(lvalue);
300 assert(rvalue);
301 assert(data);
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
config_parse_match_property(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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 = rvalue;
357 char ***sv = data;
358 bool invert;
359 int r;
360
361 assert(filename);
362 assert(lvalue);
363 assert(rvalue);
364 assert(data);
365
366 if (isempty(rvalue)) {
367 *sv = strv_free(*sv);
368 return 0;
369 }
370
371 invert = *p == '!';
372 p += invert;
373
374 for (;;) {
375 _cleanup_free_ char *word = NULL, *k = NULL;
376
377 r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
378 if (r == 0)
379 return 0;
380 if (r == -ENOMEM)
381 return log_oom();
382 if (r < 0) {
383 log_syntax(unit, LOG_WARNING, filename, line, 0,
384 "Invalid syntax, ignoring: %s", rvalue);
385 return 0;
386 }
387
388 if (!env_assignment_is_valid(word)) {
389 log_syntax(unit, LOG_WARNING, filename, line, 0,
390 "Invalid property or value, ignoring assignment: %s", word);
391 continue;
392 }
393
394 if (invert) {
395 k = strjoin("!", word);
396 if (!k)
397 return log_oom();
398 } else
399 k = TAKE_PTR(word);
400
401 r = strv_consume(sv, TAKE_PTR(k));
402 if (r < 0)
403 return log_oom();
404 }
405 }
406