chiark / gitweb /
3686267b073168bf1e953b99aed73f6af09d1ea0
[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 "log.h"
32 #include "utf8.h"
33 #include "util.h"
34 #include "conf-parser.h"
35 #include "condition.h"
36
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;
41         const char *name = NULL, *field = NULL;
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
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,
78                       Condition *match_host,
79                       Condition *match_virt,
80                       Condition *match_kernel,
81                       Condition *match_arch,
82                       const char *dev_mac,
83                       const char *dev_path,
84                       const char *dev_parent_driver,
85                       const char *dev_driver,
86                       const char *dev_type,
87                       const char *dev_name) {
88
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
98         if (match_arch && !condition_test_architecture(match_arch))
99                 return 0;
100
101         if (match_mac && (!dev_mac || memcmp(match_mac, ether_aton(dev_mac), ETH_ALEN)))
102                 return 0;
103
104         if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0)))
105                 return 0;
106
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         }
113
114         if (match_type && !streq_ptr(match_type, dev_type))
115                 return 0;
116
117         if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0)))
118                 return 0;
119
120         return 1;
121 }
122
123 unsigned net_netmask_to_prefixlen(const struct in_addr *addr) {
124         assert(addr);
125
126         return 32 - u32ctz(be32toh(addr->s_addr));
127 }
128
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
170 int config_parse_ifname(const char *unit,
171                         const char *filename,
172                         unsigned line,
173                         const char *section,
174                         unsigned section_line,
175                         const char *lvalue,
176                         int ltype,
177                         const char *rvalue,
178                         void *data,
179                         void *userdata) {
180
181         char **s = data;
182         char *n;
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);
201         if (*n)
202                 *s = n;
203         else {
204                 free(n);
205                 *s = NULL;
206         }
207
208         return 0;
209 }
210
211 int config_parse_ifalias(const char *unit,
212                          const char *filename,
213                          unsigned line,
214                          const char *section,
215                          unsigned section_line,
216                          const char *lvalue,
217                          int ltype,
218                          const char *rvalue,
219                          void *data,
220                          void *userdata) {
221
222         char **s = data;
223         char *n;
224
225         assert(filename);
226         assert(lvalue);
227         assert(rvalue);
228         assert(data);
229
230         n = strdup(rvalue);
231         if (!n)
232                 return log_oom();
233
234         if (!ascii_is_valid(n) || strlen(n) >= IFALIASZ) {
235                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
236                            "Interface alias is not ASCII clean or is too long, ignoring assignment: %s", rvalue);
237                 free(n);
238                 return 0;
239         }
240
241         free(*s);
242         if (*n)
243                 *s = n;
244         else {
245                 free(n);
246                 *s = NULL;
247         }
248
249         return 0;
250 }
251
252 int config_parse_hwaddr(const char *unit,
253                         const char *filename,
254                         unsigned line,
255                         const char *section,
256                         unsigned section_line,
257                         const char *lvalue,
258                         int ltype,
259                         const char *rvalue,
260                         void *data,
261                         void *userdata) {
262         struct ether_addr **hwaddr = data;
263         struct ether_addr *n;
264         int r;
265
266         assert(filename);
267         assert(lvalue);
268         assert(rvalue);
269         assert(data);
270
271         n = new0(struct ether_addr, 1);
272         if (!n)
273                 return log_oom();
274
275         r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
276                    &n->ether_addr_octet[0],
277                    &n->ether_addr_octet[1],
278                    &n->ether_addr_octet[2],
279                    &n->ether_addr_octet[3],
280                    &n->ether_addr_octet[4],
281                    &n->ether_addr_octet[5]);
282         if (r != 6) {
283                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
284                            "Not a valid MAC address, ignoring assignment: %s", rvalue);
285                 free(n);
286                 return 0;
287         }
288
289         free(*hwaddr);
290         *hwaddr = n;
291
292         return 0;
293 }
294
295 int net_parse_inaddr(const char *address, unsigned char *family, void *dst) {
296         int r;
297
298         assert(address);
299         assert(family);
300         assert(dst);
301
302         /* IPv4 */
303         r = inet_pton(AF_INET, address, dst);
304         if (r > 0) {
305                 /* succsefully parsed IPv4 address */
306                 if (*family == AF_UNSPEC)
307                         *family = AF_INET;
308                 else if (*family != AF_INET)
309                         return -EINVAL;
310         } else  if (r < 0)
311                 return -errno;
312         else {
313                 /* not an IPv4 address, so let's try IPv6 */
314                 r = inet_pton(AF_INET6, address, dst);
315                 if (r > 0) {
316                         /* successfully parsed IPv6 address */
317                         if (*family == AF_UNSPEC)
318                                 *family = AF_INET6;
319                         else if (*family != AF_INET6)
320                                 return -EINVAL;
321                 } else if (r < 0)
322                         return -errno;
323                 else
324                         return -EINVAL;
325         }
326
327         return 0;
328 }