chiark / gitweb /
a5db0c53530282f1ba291cae6a731298a3d01964
[elogind.git] / src / libsystemd-network / network-internal.c
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>
23 #include <linux/if.h>
24 #include <arpa/inet.h>
25 #include <fnmatch.h>
26
27 #include "strv.h"
28 #include "siphash24.h"
29 #include "libudev-private.h"
30 #include "network-internal.h"
31 #include "dhcp-lease-internal.h"
32 #include "log.h"
33 #include "utf8.h"
34 #include "util.h"
35 #include "conf-parser.h"
36 #include "condition.h"
37
38 const char *net_get_name(struct udev_device *device) {
39         const char *name = NULL, *field = NULL;
40
41         assert(device);
42
43         /* fetch some persistent data unique (on this machine) to this device */
44         FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT",
45                        "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") {
46                 name = udev_device_get_property_value(device, field);
47                 if (name)
48                         break;
49         }
50
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);
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
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,
91                       Condition *match_host,
92                       Condition *match_virt,
93                       Condition *match_kernel,
94                       Condition *match_arch,
95                       const struct ether_addr *dev_mac,
96                       const char *dev_path,
97                       const char *dev_parent_driver,
98                       const char *dev_driver,
99                       const char *dev_type,
100                       const char *dev_name) {
101
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
111         if (match_arch && !condition_test_architecture(match_arch))
112                 return 0;
113
114         if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN)))
115                 return 0;
116
117         if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0)))
118                 return 0;
119
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         }
126
127         if (match_type && !streq_ptr(match_type, dev_type))
128                 return 0;
129
130         if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0)))
131                 return 0;
132
133         return 1;
134 }
135
136 unsigned net_netmask_to_prefixlen(const struct in_addr *addr) {
137         assert(addr);
138
139         return 32 - u32ctz(be32toh(addr->s_addr));
140 }
141
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
183 int config_parse_ifname(const char *unit,
184                         const char *filename,
185                         unsigned line,
186                         const char *section,
187                         unsigned section_line,
188                         const char *lvalue,
189                         int ltype,
190                         const char *rvalue,
191                         void *data,
192                         void *userdata) {
193
194         char **s = data;
195         _cleanup_free_ char *n = NULL;
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);
214         if (*n) {
215                 *s = n;
216                 n = NULL;
217         } else
218                 *s = NULL;
219
220         return 0;
221 }
222
223 int config_parse_ifalias(const char *unit,
224                          const char *filename,
225                          unsigned line,
226                          const char *section,
227                          unsigned section_line,
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
264 int config_parse_hwaddr(const char *unit,
265                         const char *filename,
266                         unsigned line,
267                         const char *section,
268                         unsigned section_line,
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
283         n = new0(struct ether_addr, 1);
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 }
306
307 int net_parse_inaddr(const char *address, unsigned char *family, void *dst) {
308         int r;
309
310         assert(address);
311         assert(family);
312         assert(dst);
313
314         /* IPv4 */
315         r = inet_pton(AF_INET, address, dst);
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)
323                 return -errno;
324         else {
325                 /* not an IPv4 address, so let's try IPv6 */
326                 r = inet_pton(AF_INET6, address, dst);
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)
334                         return -errno;
335                 else
336                         return -EINVAL;
337         }
338
339         return 0;
340 }
341
342 void serialize_in_addrs(FILE *f, const char *key, struct in_addr *addresses, size_t size) {
343         unsigned i;
344
345         assert(f);
346         assert(key);
347         assert(addresses);
348         assert(size);
349
350         fprintf(f, "%s=", key);
351
352         for (i = 0; i < size; i++)
353                 fprintf(f, "%s%s", inet_ntoa(addresses[i]),
354                         (i < (size - 1)) ? " ": "");
355
356         fputs("\n", f);
357 }
358
359 int deserialize_in_addrs(struct in_addr **ret, size_t *ret_size, const char *string) {
360         _cleanup_free_ struct in_addr *addresses = NULL;
361         size_t size = 0;
362         char *word, *state;
363         size_t len;
364
365         assert(ret);
366         assert(ret_size);
367         assert(string);
368
369         FOREACH_WORD(word, len, string, state) {
370                 _cleanup_free_ char *addr_str = NULL;
371                 struct in_addr *new_addresses;
372                 int r;
373
374                 new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr));
375                 if (!new_addresses)
376                         return -ENOMEM;
377                 else
378                         addresses = new_addresses;
379
380                 addr_str = strndup(word, len);
381                 if (!addr_str)
382                         return -ENOMEM;
383
384                 r = inet_pton(AF_INET, addr_str, &(addresses[size]));
385                 if (r <= 0)
386                         continue;
387
388                 size ++;
389         }
390
391         *ret_size = size;
392         *ret = addresses;
393         addresses = NULL;
394
395         return 0;
396 }
397
398 int deserialize_in6_addrs(struct in6_addr **ret, size_t *ret_size, const char *string) {
399         _cleanup_free_ struct in6_addr *addresses = NULL;
400         size_t size = 0;
401         char *word, *state;
402         size_t len;
403
404         assert(ret);
405         assert(ret_size);
406         assert(string);
407
408         FOREACH_WORD(word, len, string, state) {
409                 _cleanup_free_ char *addr_str = NULL;
410                 struct in6_addr *new_addresses;
411                 int r;
412
413                 new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr));
414                 if (!new_addresses)
415                         return -ENOMEM;
416                 else
417                         addresses = new_addresses;
418
419                 addr_str = strndup(word, len);
420                 if (!addr_str)
421                         return -ENOMEM;
422
423                 r = inet_pton(AF_INET6, addr_str, &(addresses[size]));
424                 if (r <= 0)
425                         continue;
426
427                 size++;
428         }
429
430         *ret_size = size;
431         *ret = addresses;
432         addresses = NULL;
433
434         return 0;
435 }
436
437 void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
438         unsigned i;
439
440         assert(f);
441         assert(key);
442         assert(routes);
443         assert(size);
444
445         fprintf(f, "%s=", key);
446
447         for (i = 0; i < size; i++)
448                 fprintf(f, "%s/%" PRIu8 ",%s%s", inet_ntoa(routes[i].dst_addr),
449                         routes[i].dst_prefixlen, inet_ntoa(routes[i].gw_addr),
450                         (i < (size - 1)) ? " ": "");
451
452         fputs("\n", f);
453 }
454
455 int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
456         _cleanup_free_ struct sd_dhcp_route *routes = NULL;
457         size_t size = 0, allocated = 0;
458         char *word, *state;
459         size_t len;
460
461         assert(ret);
462         assert(ret_size);
463         assert(ret_allocated);
464         assert(string);
465
466         FOREACH_WORD(word, len, string, state) {
467                 /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
468                 _cleanup_free_ char* entry;
469                 char *tok, *tok_end;
470                 unsigned n;
471                 int r;
472
473                 if (!GREEDY_REALLOC(routes, allocated, size + 1))
474                         return -ENOMEM;
475
476                 entry = strndup(word, len);
477
478                 tok = entry;
479
480                 /* get the subnet */
481                 tok_end = strchr(tok, '/');
482                 if (!tok_end)
483                         continue;
484                 *tok_end = '\0';
485
486                 r = inet_aton(tok, &routes[size].dst_addr);
487                 if (r == 0)
488                         continue;
489
490                 tok = tok_end + 1;
491
492                 /* get the prefixlen */
493                 tok_end = strchr(tok, ',');
494                 if (!tok_end)
495                         continue;
496
497                 *tok_end = '\0';
498
499                 r = safe_atou(tok, &n);
500                 if (r < 0 || n > 32)
501                         continue;
502
503                 routes[size].dst_prefixlen = (uint8_t) n;
504                 tok = tok_end + 1;
505
506                 /* get the gateway */
507                 r = inet_aton(tok, &routes[size].gw_addr);
508                 if (r == 0)
509                         continue;
510
511                 size++;
512         }
513
514         *ret_size = size;
515         *ret_allocated = allocated;
516         *ret = routes;
517         routes = NULL;
518
519         return 0;
520 }