]>
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" |
be32eb9b | 31 | #include "log.h" |
5fde13d7 | 32 | #include "utf8.h" |
a12fa420 | 33 | #include "util.h" |
5fde13d7 | 34 | #include "conf-parser.h" |
2cc412b5 | 35 | #include "condition.h" |
5fde13d7 | 36 | |
b5db00e5 UTL |
37 | #define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) |
38 | ||
39 | int net_get_unique_predictable_data(struct udev_device *device, uint8_t result[8]) { | |
40 | size_t l, sz = 0; | |
9f2a50a3 | 41 | const char *name = NULL, *field = NULL; |
b5db00e5 UTL |
42 | int r; |
43 | uint8_t *v; | |
44 | ||
45 | /* fetch some persistent data unique (on this machine) to this device */ | |
46 | FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { | |
47 | name = udev_device_get_property_value(device, field); | |
48 | if (name) | |
49 | break; | |
50 | } | |
51 | ||
52 | if (!name) | |
53 | return -ENOENT; | |
54 | ||
55 | l = strlen(name); | |
56 | sz = sizeof(sd_id128_t) + l; | |
57 | v = alloca(sz); | |
58 | ||
59 | /* fetch some persistent data unique to this machine */ | |
60 | r = sd_id128_get_machine((sd_id128_t*) v); | |
61 | if (r < 0) | |
62 | return r; | |
63 | memcpy(v + sizeof(sd_id128_t), name, l); | |
64 | ||
65 | /* Let's hash the machine ID plus the device name. We | |
66 | * use a fixed, but originally randomly created hash | |
67 | * key here. */ | |
68 | siphash24(result, v, sz, HASH_KEY.bytes); | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
be32eb9b TG |
73 | bool net_match_config(const struct ether_addr *match_mac, |
74 | const char *match_path, | |
75 | const char *match_driver, | |
76 | const char *match_type, | |
77 | const char *match_name, | |
2cc412b5 TG |
78 | Condition *match_host, |
79 | Condition *match_virt, | |
80 | Condition *match_kernel, | |
edbb03e9 | 81 | Condition *match_arch, |
505f8da7 | 82 | const struct ether_addr *dev_mac, |
b3e01314 | 83 | const char *dev_path, |
bf175aaf | 84 | const char *dev_parent_driver, |
b3e01314 TG |
85 | const char *dev_driver, |
86 | const char *dev_type, | |
87 | const char *dev_name) { | |
be32eb9b | 88 | |
2cc412b5 TG |
89 | if (match_host && !condition_test_host(match_host)) |
90 | return 0; | |
91 | ||
92 | if (match_virt && !condition_test_virtualization(match_virt)) | |
93 | return 0; | |
94 | ||
95 | if (match_kernel && !condition_test_kernel_command_line(match_kernel)) | |
96 | return 0; | |
97 | ||
edbb03e9 TG |
98 | if (match_arch && !condition_test_architecture(match_arch)) |
99 | return 0; | |
100 | ||
505f8da7 | 101 | if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) |
449f7554 | 102 | return 0; |
be32eb9b | 103 | |
f28964e3 | 104 | if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0))) |
449f7554 | 105 | return 0; |
be32eb9b | 106 | |
bf175aaf TG |
107 | if (match_driver) { |
108 | if (dev_parent_driver && !streq(match_driver, dev_parent_driver)) | |
109 | return 0; | |
110 | else if (!streq_ptr(match_driver, dev_driver)) | |
111 | return 0; | |
112 | } | |
5fde13d7 | 113 | |
d69b12ac | 114 | if (match_type && !streq_ptr(match_type, dev_type)) |
449f7554 | 115 | return 0; |
5fde13d7 | 116 | |
bf175aaf | 117 | if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0))) |
449f7554 | 118 | return 0; |
5fde13d7 | 119 | |
be32eb9b TG |
120 | return 1; |
121 | } | |
5fde13d7 | 122 | |
377a218f | 123 | unsigned net_netmask_to_prefixlen(const struct in_addr *addr) { |
377a218f TG |
124 | assert(addr); |
125 | ||
ba914311 | 126 | return 32 - u32ctz(be32toh(addr->s_addr)); |
377a218f TG |
127 | } |
128 | ||
2cc412b5 TG |
129 | int config_parse_net_condition(const char *unit, |
130 | const char *filename, | |
131 | unsigned line, | |
132 | const char *section, | |
133 | unsigned section_line, | |
134 | const char *lvalue, | |
135 | int ltype, | |
136 | const char *rvalue, | |
137 | void *data, | |
138 | void *userdata) { | |
139 | ||
140 | ConditionType cond = ltype; | |
141 | Condition **ret = data; | |
142 | bool negate; | |
143 | Condition *c; | |
144 | _cleanup_free_ char *s = NULL; | |
145 | ||
146 | assert(filename); | |
147 | assert(lvalue); | |
148 | assert(rvalue); | |
149 | assert(data); | |
150 | ||
151 | negate = rvalue[0] == '!'; | |
152 | if (negate) | |
153 | rvalue++; | |
154 | ||
155 | s = strdup(rvalue); | |
156 | if (!s) | |
157 | return log_oom(); | |
158 | ||
159 | c = condition_new(cond, s, false, negate); | |
160 | if (!c) | |
161 | return log_oom(); | |
162 | ||
163 | if (*ret) | |
164 | condition_free(*ret); | |
165 | ||
166 | *ret = c; | |
167 | return 0; | |
168 | } | |
169 | ||
5fde13d7 TG |
170 | int config_parse_ifname(const char *unit, |
171 | const char *filename, | |
172 | unsigned line, | |
173 | const char *section, | |
71a61510 | 174 | unsigned section_line, |
5fde13d7 TG |
175 | const char *lvalue, |
176 | int ltype, | |
177 | const char *rvalue, | |
178 | void *data, | |
179 | void *userdata) { | |
180 | ||
181 | char **s = data; | |
5a3f1989 | 182 | _cleanup_free_ char *n = NULL; |
5fde13d7 TG |
183 | |
184 | assert(filename); | |
185 | assert(lvalue); | |
186 | assert(rvalue); | |
187 | assert(data); | |
188 | ||
189 | n = strdup(rvalue); | |
190 | if (!n) | |
191 | return log_oom(); | |
192 | ||
193 | if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { | |
194 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
195 | "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); | |
196 | free(n); | |
197 | return 0; | |
198 | } | |
199 | ||
200 | free(*s); | |
5a3f1989 | 201 | if (*n) { |
5fde13d7 | 202 | *s = n; |
5a3f1989 TG |
203 | n = NULL; |
204 | } else | |
5fde13d7 | 205 | *s = NULL; |
5fde13d7 TG |
206 | |
207 | return 0; | |
208 | } | |
209 | ||
d2df0d0e TG |
210 | int config_parse_ifalias(const char *unit, |
211 | const char *filename, | |
212 | unsigned line, | |
213 | const char *section, | |
71a61510 | 214 | unsigned section_line, |
d2df0d0e TG |
215 | const char *lvalue, |
216 | int ltype, | |
217 | const char *rvalue, | |
218 | void *data, | |
219 | void *userdata) { | |
220 | ||
221 | char **s = data; | |
222 | char *n; | |
223 | ||
224 | assert(filename); | |
225 | assert(lvalue); | |
226 | assert(rvalue); | |
227 | assert(data); | |
228 | ||
229 | n = strdup(rvalue); | |
230 | if (!n) | |
231 | return log_oom(); | |
232 | ||
233 | if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) { | |
234 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
235 | "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue); | |
236 | free(n); | |
237 | return 0; | |
238 | } | |
239 | ||
240 | free(*s); | |
241 | if (*n) | |
242 | *s = n; | |
243 | else { | |
244 | free(n); | |
245 | *s = NULL; | |
246 | } | |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
5fde13d7 TG |
251 | int config_parse_hwaddr(const char *unit, |
252 | const char *filename, | |
253 | unsigned line, | |
254 | const char *section, | |
71a61510 | 255 | unsigned section_line, |
5fde13d7 TG |
256 | const char *lvalue, |
257 | int ltype, | |
258 | const char *rvalue, | |
259 | void *data, | |
260 | void *userdata) { | |
261 | struct ether_addr **hwaddr = data; | |
262 | struct ether_addr *n; | |
263 | int r; | |
264 | ||
265 | assert(filename); | |
266 | assert(lvalue); | |
267 | assert(rvalue); | |
268 | assert(data); | |
269 | ||
a12fa420 | 270 | n = new0(struct ether_addr, 1); |
5fde13d7 TG |
271 | if (!n) |
272 | return log_oom(); | |
273 | ||
274 | r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | |
275 | &n->ether_addr_octet[0], | |
276 | &n->ether_addr_octet[1], | |
277 | &n->ether_addr_octet[2], | |
278 | &n->ether_addr_octet[3], | |
279 | &n->ether_addr_octet[4], | |
280 | &n->ether_addr_octet[5]); | |
281 | if (r != 6) { | |
282 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
283 | "Not a valid MAC address, ignoring assignment: %s", rvalue); | |
284 | free(n); | |
285 | return 0; | |
286 | } | |
287 | ||
288 | free(*hwaddr); | |
289 | *hwaddr = n; | |
290 | ||
291 | return 0; | |
292 | } | |
f5284182 TG |
293 | |
294 | int net_parse_inaddr(const char *address, unsigned char *family, void *dst) { | |
295 | int r; | |
296 | ||
297 | assert(address); | |
298 | assert(family); | |
299 | assert(dst); | |
300 | ||
301 | /* IPv4 */ | |
302 | r = inet_pton(AF_INET, address, dst); | |
801bd9e8 TG |
303 | if (r > 0) { |
304 | /* succsefully parsed IPv4 address */ | |
305 | if (*family == AF_UNSPEC) | |
306 | *family = AF_INET; | |
307 | else if (*family != AF_INET) | |
308 | return -EINVAL; | |
309 | } else if (r < 0) | |
f5284182 TG |
310 | return -errno; |
311 | else { | |
312 | /* not an IPv4 address, so let's try IPv6 */ | |
313 | r = inet_pton(AF_INET6, address, dst); | |
801bd9e8 TG |
314 | if (r > 0) { |
315 | /* successfully parsed IPv6 address */ | |
316 | if (*family == AF_UNSPEC) | |
317 | *family = AF_INET6; | |
318 | else if (*family != AF_INET6) | |
319 | return -EINVAL; | |
320 | } else if (r < 0) | |
f5284182 TG |
321 | return -errno; |
322 | else | |
323 | return -EINVAL; | |
324 | } | |
325 | ||
326 | return 0; | |
327 | } | |
7951dea2 | 328 | |
09bee74d TG |
329 | void serialize_in_addrs(FILE *f, const char *key, struct in_addr *addresses, size_t size) { |
330 | unsigned i; | |
331 | ||
332 | assert(f); | |
333 | assert(key); | |
334 | assert(addresses); | |
335 | assert(size); | |
336 | ||
337 | fprintf(f, "%s=", key); | |
338 | ||
339 | for (i = 0; i < size; i++) | |
340 | fprintf(f, "%s%s", inet_ntoa(addresses[i]), | |
341 | (i < (size - 1)) ? " ": ""); | |
342 | ||
343 | fputs("\n", f); | |
344 | } | |
345 | ||
346 | int deserialize_in_addrs(struct in_addr **ret, size_t *ret_size, const char *string) { | |
347 | _cleanup_free_ struct in_addr *addresses = NULL; | |
348 | size_t size = 0; | |
349 | char *word, *state; | |
350 | size_t len; | |
351 | ||
352 | assert(ret); | |
353 | assert(ret_size); | |
354 | assert(string); | |
355 | ||
356 | FOREACH_WORD(word, len, string, state) { | |
357 | _cleanup_free_ char *addr_str = NULL; | |
358 | struct in_addr *new_addresses; | |
359 | int r; | |
360 | ||
361 | new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); | |
362 | if (!new_addresses) | |
363 | return -ENOMEM; | |
364 | else | |
365 | addresses = new_addresses; | |
366 | ||
367 | addr_str = strndup(word, len); | |
368 | if (!addr_str) | |
369 | return -ENOMEM; | |
370 | ||
371 | r = inet_pton(AF_INET, addr_str, &(addresses[size])); | |
372 | if (r <= 0) | |
373 | continue; | |
374 | ||
375 | size ++; | |
376 | } | |
377 | ||
378 | *ret_size = size; | |
379 | *ret = addresses; | |
380 | addresses = NULL; | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | int deserialize_in6_addrs(struct in6_addr **ret, size_t *ret_size, const char *string) { | |
386 | _cleanup_free_ struct in6_addr *addresses = NULL; | |
387 | size_t size = 0; | |
388 | char *word, *state; | |
389 | size_t len; | |
390 | ||
391 | assert(ret); | |
392 | assert(ret_size); | |
393 | assert(string); | |
394 | ||
395 | FOREACH_WORD(word, len, string, state) { | |
396 | _cleanup_free_ char *addr_str = NULL; | |
397 | struct in6_addr *new_addresses; | |
398 | int r; | |
399 | ||
400 | new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); | |
401 | if (!new_addresses) | |
402 | return -ENOMEM; | |
403 | else | |
404 | addresses = new_addresses; | |
405 | ||
406 | addr_str = strndup(word, len); | |
407 | if (!addr_str) | |
408 | return -ENOMEM; | |
409 | ||
410 | r = inet_pton(AF_INET6, addr_str, &(addresses[size])); | |
411 | if (r <= 0) | |
412 | continue; | |
413 | ||
414 | size++; | |
415 | } | |
416 | ||
417 | *ret_size = size; | |
418 | *ret = addresses; | |
419 | addresses = NULL; | |
420 | ||
421 | return 0; | |
422 | } |