]>
Commit | Line | Data |
---|---|---|
5fde13d7 TG |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright (C) 2013 Tom Gundersen <teg@jklm.no> | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <netinet/ether.h> | |
d2df0d0e | 23 | #include <linux/if.h> |
f5284182 | 24 | #include <arpa/inet.h> |
f28964e3 | 25 | #include <fnmatch.h> |
5fde13d7 | 26 | |
b5db00e5 UTL |
27 | #include "strv.h" |
28 | #include "siphash24.h" | |
29 | #include "libudev-private.h" | |
c6f7c917 | 30 | #include "network-internal.h" |
e1ea665e | 31 | #include "dhcp-lease-internal.h" |
be32eb9b | 32 | #include "log.h" |
5fde13d7 | 33 | #include "utf8.h" |
a12fa420 | 34 | #include "util.h" |
5fde13d7 | 35 | #include "conf-parser.h" |
2cc412b5 | 36 | #include "condition.h" |
5fde13d7 | 37 | |
fc541430 | 38 | const char *net_get_name(struct udev_device *device) { |
9f2a50a3 | 39 | const char *name = NULL, *field = NULL; |
fc541430 TG |
40 | |
41 | assert(device); | |
b5db00e5 UTL |
42 | |
43 | /* fetch some persistent data unique (on this machine) to this device */ | |
fc541430 TG |
44 | FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", |
45 | "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { | |
b5db00e5 UTL |
46 | name = udev_device_get_property_value(device, field); |
47 | if (name) | |
48 | break; | |
49 | } | |
50 | ||
fc541430 TG |
51 | return name; |
52 | } | |
53 | ||
54 | #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) | |
55 | ||
56 | int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) { | |
57 | size_t l, sz = 0; | |
58 | const char *name = NULL; | |
59 | int r; | |
60 | uint8_t *v; | |
61 | ||
62 | assert(device); | |
63 | ||
64 | name = net_get_name(device); | |
b5db00e5 UTL |
65 | if (!name) |
66 | return -ENOENT; | |
67 | ||
68 | l = strlen(name); | |
69 | sz = sizeof(sd_id128_t) + l; | |
70 | v = alloca(sz); | |
71 | ||
72 | /* fetch some persistent data unique to this machine */ | |
73 | r = sd_id128_get_machine((sd_id128_t*) v); | |
74 | if (r < 0) | |
75 | return r; | |
76 | memcpy(v + sizeof(sd_id128_t), name, l); | |
77 | ||
78 | /* Let's hash the machine ID plus the device name. We | |
79 | * use a fixed, but originally randomly created hash | |
80 | * key here. */ | |
81 | siphash24(result, v, sz, HASH_KEY.bytes); | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
be32eb9b TG |
86 | bool net_match_config(const struct ether_addr *match_mac, |
87 | const char *match_path, | |
88 | const char *match_driver, | |
89 | const char *match_type, | |
90 | const char *match_name, | |
2cc412b5 TG |
91 | Condition *match_host, |
92 | Condition *match_virt, | |
93 | Condition *match_kernel, | |
edbb03e9 | 94 | Condition *match_arch, |
505f8da7 | 95 | const struct ether_addr *dev_mac, |
b3e01314 | 96 | const char *dev_path, |
bf175aaf | 97 | const char *dev_parent_driver, |
b3e01314 TG |
98 | const char *dev_driver, |
99 | const char *dev_type, | |
100 | const char *dev_name) { | |
be32eb9b | 101 | |
2cc412b5 TG |
102 | if (match_host && !condition_test_host(match_host)) |
103 | return 0; | |
104 | ||
105 | if (match_virt && !condition_test_virtualization(match_virt)) | |
106 | return 0; | |
107 | ||
108 | if (match_kernel && !condition_test_kernel_command_line(match_kernel)) | |
109 | return 0; | |
110 | ||
edbb03e9 TG |
111 | if (match_arch && !condition_test_architecture(match_arch)) |
112 | return 0; | |
113 | ||
505f8da7 | 114 | if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) |
449f7554 | 115 | return 0; |
be32eb9b | 116 | |
f28964e3 | 117 | if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0))) |
449f7554 | 118 | return 0; |
be32eb9b | 119 | |
bf175aaf TG |
120 | if (match_driver) { |
121 | if (dev_parent_driver && !streq(match_driver, dev_parent_driver)) | |
122 | return 0; | |
123 | else if (!streq_ptr(match_driver, dev_driver)) | |
124 | return 0; | |
125 | } | |
5fde13d7 | 126 | |
d69b12ac | 127 | if (match_type && !streq_ptr(match_type, dev_type)) |
449f7554 | 128 | return 0; |
5fde13d7 | 129 | |
bf175aaf | 130 | if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0))) |
449f7554 | 131 | return 0; |
5fde13d7 | 132 | |
be32eb9b TG |
133 | return 1; |
134 | } | |
5fde13d7 | 135 | |
377a218f | 136 | unsigned net_netmask_to_prefixlen(const struct in_addr *addr) { |
377a218f TG |
137 | assert(addr); |
138 | ||
ba914311 | 139 | return 32 - u32ctz(be32toh(addr->s_addr)); |
377a218f TG |
140 | } |
141 | ||
2cc412b5 TG |
142 | int config_parse_net_condition(const char *unit, |
143 | const char *filename, | |
144 | unsigned line, | |
145 | const char *section, | |
146 | unsigned section_line, | |
147 | const char *lvalue, | |
148 | int ltype, | |
149 | const char *rvalue, | |
150 | void *data, | |
151 | void *userdata) { | |
152 | ||
153 | ConditionType cond = ltype; | |
154 | Condition **ret = data; | |
155 | bool negate; | |
156 | Condition *c; | |
157 | _cleanup_free_ char *s = NULL; | |
158 | ||
159 | assert(filename); | |
160 | assert(lvalue); | |
161 | assert(rvalue); | |
162 | assert(data); | |
163 | ||
164 | negate = rvalue[0] == '!'; | |
165 | if (negate) | |
166 | rvalue++; | |
167 | ||
168 | s = strdup(rvalue); | |
169 | if (!s) | |
170 | return log_oom(); | |
171 | ||
172 | c = condition_new(cond, s, false, negate); | |
173 | if (!c) | |
174 | return log_oom(); | |
175 | ||
176 | if (*ret) | |
177 | condition_free(*ret); | |
178 | ||
179 | *ret = c; | |
180 | return 0; | |
181 | } | |
182 | ||
5fde13d7 TG |
183 | int config_parse_ifname(const char *unit, |
184 | const char *filename, | |
185 | unsigned line, | |
186 | const char *section, | |
71a61510 | 187 | unsigned section_line, |
5fde13d7 TG |
188 | const char *lvalue, |
189 | int ltype, | |
190 | const char *rvalue, | |
191 | void *data, | |
192 | void *userdata) { | |
193 | ||
194 | char **s = data; | |
5a3f1989 | 195 | _cleanup_free_ char *n = NULL; |
5fde13d7 TG |
196 | |
197 | assert(filename); | |
198 | assert(lvalue); | |
199 | assert(rvalue); | |
200 | assert(data); | |
201 | ||
202 | n = strdup(rvalue); | |
203 | if (!n) | |
204 | return log_oom(); | |
205 | ||
206 | if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { | |
207 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
208 | "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); | |
209 | free(n); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | free(*s); | |
5a3f1989 | 214 | if (*n) { |
5fde13d7 | 215 | *s = n; |
5a3f1989 TG |
216 | n = NULL; |
217 | } else | |
5fde13d7 | 218 | *s = NULL; |
5fde13d7 TG |
219 | |
220 | return 0; | |
221 | } | |
222 | ||
d2df0d0e TG |
223 | int config_parse_ifalias(const char *unit, |
224 | const char *filename, | |
225 | unsigned line, | |
226 | const char *section, | |
71a61510 | 227 | unsigned section_line, |
d2df0d0e TG |
228 | const char *lvalue, |
229 | int ltype, | |
230 | const char *rvalue, | |
231 | void *data, | |
232 | void *userdata) { | |
233 | ||
234 | char **s = data; | |
235 | char *n; | |
236 | ||
237 | assert(filename); | |
238 | assert(lvalue); | |
239 | assert(rvalue); | |
240 | assert(data); | |
241 | ||
242 | n = strdup(rvalue); | |
243 | if (!n) | |
244 | return log_oom(); | |
245 | ||
246 | if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { | |
247 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
248 | "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); | |
249 | free(n); | |
250 | return 0; | |
251 | } | |
252 | ||
253 | free(*s); | |
254 | if (*n) | |
255 | *s = n; | |
256 | else { | |
257 | free(n); | |
258 | *s = NULL; | |
259 | } | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
5fde13d7 TG |
264 | int config_parse_hwaddr(const char *unit, |
265 | const char *filename, | |
266 | unsigned line, | |
267 | const char *section, | |
71a61510 | 268 | unsigned section_line, |
5fde13d7 TG |
269 | const char *lvalue, |
270 | int ltype, | |
271 | const char *rvalue, | |
272 | void *data, | |
273 | void *userdata) { | |
274 | struct ether_addr **hwaddr = data; | |
275 | struct ether_addr *n; | |
276 | int r; | |
277 | ||
278 | assert(filename); | |
279 | assert(lvalue); | |
280 | assert(rvalue); | |
281 | assert(data); | |
282 | ||
a12fa420 | 283 | n = new0(struct ether_addr, 1); |
5fde13d7 TG |
284 | if (!n) |
285 | return log_oom(); | |
286 | ||
287 | r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | |
288 | &n->ether_addr_octet[0], | |
289 | &n->ether_addr_octet[1], | |
290 | &n->ether_addr_octet[2], | |
291 | &n->ether_addr_octet[3], | |
292 | &n->ether_addr_octet[4], | |
293 | &n->ether_addr_octet[5]); | |
294 | if (r != 6) { | |
295 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
296 | "Not a valid MAC address, ignoring assignment: %s", rvalue); | |
297 | free(n); | |
298 | return 0; | |
299 | } | |
300 | ||
301 | free(*hwaddr); | |
302 | *hwaddr = n; | |
303 | ||
304 | return 0; | |
305 | } | |
f5284182 | 306 | |
0dd25fb9 | 307 | int net_parse_inaddr(const char *address, int *family, void *dst) { |
f5284182 TG |
308 | int r; |
309 | ||
310 | assert(address); | |
311 | assert(family); | |
312 | assert(dst); | |
313 | ||
314 | /* IPv4 */ | |
315 | r = inet_pton(AF_INET, address, dst); | |
801bd9e8 TG |
316 | if (r > 0) { |
317 | /* succsefully parsed IPv4 address */ | |
318 | if (*family == AF_UNSPEC) | |
319 | *family = AF_INET; | |
320 | else if (*family != AF_INET) | |
321 | return -EINVAL; | |
322 | } else if (r < 0) | |
f5284182 TG |
323 | return -errno; |
324 | else { | |
325 | /* not an IPv4 address, so let's try IPv6 */ | |
326 | r = inet_pton(AF_INET6, address, dst); | |
801bd9e8 TG |
327 | if (r > 0) { |
328 | /* successfully parsed IPv6 address */ | |
329 | if (*family == AF_UNSPEC) | |
330 | *family = AF_INET6; | |
331 | else if (*family != AF_INET6) | |
332 | return -EINVAL; | |
333 | } else if (r < 0) | |
f5284182 TG |
334 | return -errno; |
335 | else | |
336 | return -EINVAL; | |
337 | } | |
338 | ||
339 | return 0; | |
340 | } | |
7951dea2 | 341 | |
b0e39c82 | 342 | void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { |
09bee74d TG |
343 | unsigned i; |
344 | ||
345 | assert(f); | |
09bee74d TG |
346 | assert(addresses); |
347 | assert(size); | |
348 | ||
09bee74d TG |
349 | for (i = 0; i < size; i++) |
350 | fprintf(f, "%s%s", inet_ntoa(addresses[i]), | |
351 | (i < (size - 1)) ? " ": ""); | |
09bee74d TG |
352 | } |
353 | ||
a2ba62c7 | 354 | int deserialize_in_addrs(struct in_addr **ret, const char *string) { |
09bee74d | 355 | _cleanup_free_ struct in_addr *addresses = NULL; |
a2ba62c7 | 356 | int size = 0; |
09bee74d TG |
357 | char *word, *state; |
358 | size_t len; | |
359 | ||
360 | assert(ret); | |
09bee74d TG |
361 | assert(string); |
362 | ||
363 | FOREACH_WORD(word, len, string, state) { | |
364 | _cleanup_free_ char *addr_str = NULL; | |
365 | struct in_addr *new_addresses; | |
366 | int r; | |
367 | ||
368 | new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); | |
369 | if (!new_addresses) | |
370 | return -ENOMEM; | |
371 | else | |
372 | addresses = new_addresses; | |
373 | ||
374 | addr_str = strndup(word, len); | |
375 | if (!addr_str) | |
376 | return -ENOMEM; | |
377 | ||
378 | r = inet_pton(AF_INET, addr_str, &(addresses[size])); | |
379 | if (r <= 0) | |
380 | continue; | |
381 | ||
382 | size ++; | |
383 | } | |
384 | ||
09bee74d TG |
385 | *ret = addresses; |
386 | addresses = NULL; | |
387 | ||
a2ba62c7 | 388 | return size; |
09bee74d TG |
389 | } |
390 | ||
a2ba62c7 | 391 | int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { |
09bee74d | 392 | _cleanup_free_ struct in6_addr *addresses = NULL; |
a2ba62c7 | 393 | int size = 0; |
09bee74d TG |
394 | char *word, *state; |
395 | size_t len; | |
396 | ||
397 | assert(ret); | |
09bee74d TG |
398 | assert(string); |
399 | ||
400 | FOREACH_WORD(word, len, string, state) { | |
401 | _cleanup_free_ char *addr_str = NULL; | |
402 | struct in6_addr *new_addresses; | |
403 | int r; | |
404 | ||
405 | new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); | |
406 | if (!new_addresses) | |
407 | return -ENOMEM; | |
408 | else | |
409 | addresses = new_addresses; | |
410 | ||
411 | addr_str = strndup(word, len); | |
412 | if (!addr_str) | |
413 | return -ENOMEM; | |
414 | ||
415 | r = inet_pton(AF_INET6, addr_str, &(addresses[size])); | |
416 | if (r <= 0) | |
417 | continue; | |
418 | ||
419 | size++; | |
420 | } | |
421 | ||
09bee74d TG |
422 | *ret = addresses; |
423 | addresses = NULL; | |
424 | ||
a2ba62c7 | 425 | return size; |
09bee74d | 426 | } |
e1ea665e EY |
427 | |
428 | void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) { | |
429 | unsigned i; | |
430 | ||
431 | assert(f); | |
432 | assert(key); | |
433 | assert(routes); | |
434 | assert(size); | |
435 | ||
436 | fprintf(f, "%s=", key); | |
437 | ||
438 | for (i = 0; i < size; i++) | |
439 | fprintf(f, "%s/%" PRIu8 ",%s%s", inet_ntoa(routes[i].dst_addr), | |
440 | routes[i].dst_prefixlen, inet_ntoa(routes[i].gw_addr), | |
441 | (i < (size - 1)) ? " ": ""); | |
442 | ||
443 | fputs("\n", f); | |
444 | } | |
445 | ||
446 | int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { | |
447 | _cleanup_free_ struct sd_dhcp_route *routes = NULL; | |
448 | size_t size = 0, allocated = 0; | |
449 | char *word, *state; | |
450 | size_t len; | |
451 | ||
452 | assert(ret); | |
453 | assert(ret_size); | |
454 | assert(ret_allocated); | |
455 | assert(string); | |
456 | ||
457 | FOREACH_WORD(word, len, string, state) { | |
458 | /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ | |
69f08c83 | 459 | _cleanup_free_ char* entry = NULL; |
e1ea665e EY |
460 | char *tok, *tok_end; |
461 | unsigned n; | |
462 | int r; | |
463 | ||
464 | if (!GREEDY_REALLOC(routes, allocated, size + 1)) | |
465 | return -ENOMEM; | |
466 | ||
467 | entry = strndup(word, len); | |
31db0120 SS |
468 | if(!entry) |
469 | return -ENOMEM; | |
e1ea665e EY |
470 | |
471 | tok = entry; | |
472 | ||
473 | /* get the subnet */ | |
474 | tok_end = strchr(tok, '/'); | |
475 | if (!tok_end) | |
476 | continue; | |
477 | *tok_end = '\0'; | |
478 | ||
479 | r = inet_aton(tok, &routes[size].dst_addr); | |
480 | if (r == 0) | |
481 | continue; | |
482 | ||
483 | tok = tok_end + 1; | |
484 | ||
485 | /* get the prefixlen */ | |
486 | tok_end = strchr(tok, ','); | |
487 | if (!tok_end) | |
488 | continue; | |
489 | ||
490 | *tok_end = '\0'; | |
491 | ||
492 | r = safe_atou(tok, &n); | |
493 | if (r < 0 || n > 32) | |
494 | continue; | |
495 | ||
496 | routes[size].dst_prefixlen = (uint8_t) n; | |
497 | tok = tok_end + 1; | |
498 | ||
499 | /* get the gateway */ | |
500 | r = inet_aton(tok, &routes[size].gw_addr); | |
501 | if (r == 0) | |
502 | continue; | |
503 | ||
504 | size++; | |
505 | } | |
506 | ||
507 | *ret_size = size; | |
508 | *ret_allocated = allocated; | |
509 | *ret = routes; | |
510 | routes = NULL; | |
511 | ||
512 | return 0; | |
513 | } |