]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/confile_utils.c
lxc-user-nic: initialize vars to silence gcc-7
[mirror_lxc.git] / src / lxc / confile_utils.c
CommitLineData
0b843d35
CB
1/* liblxcapi
2 *
3 * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2017 Canonical Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
ce2f5ae8
CB
20#include "config.h"
21
f9373e40 22#include <ctype.h>
0b843d35 23#include <stdio.h>
ce2f5ae8 24#include <stdlib.h>
0b843d35 25#include <string.h>
9b0df30f 26#include <arpa/inet.h>
0b843d35 27
663e9916 28#include "conf.h"
ce2f5ae8
CB
29#include "confile.h"
30#include "confile_utils.h"
31#include "error.h"
663e9916 32#include "log.h"
ce2f5ae8 33#include "list.h"
811ef482 34#include "network.h"
f9373e40 35#include "parse.h"
0b843d35
CB
36#include "utils.h"
37
ce2f5ae8
CB
38lxc_log_define(lxc_confile_utils, lxc);
39
0b843d35
CB
40int parse_idmaps(const char *idmap, char *type, unsigned long *nsid,
41 unsigned long *hostid, unsigned long *range)
42{
43 int ret = -1;
44 unsigned long tmp_hostid, tmp_nsid, tmp_range;
45 char tmp_type;
46 char *window, *slide;
47 char *dup = NULL;
48
49 /* Duplicate string. */
50 dup = strdup(idmap);
51 if (!dup)
52 goto on_error;
53
54 /* A prototypical idmap entry would be: "u 1000 1000000 65536" */
55
56 /* align */
57 slide = window = dup;
58 /* skip whitespace */
59 slide += strspn(slide, " \t\r");
60 if (slide != window && *slide == '\0')
61 goto on_error;
62
63 /* Validate type. */
64 if (*slide != 'u' && *slide != 'g')
65 goto on_error;
66 /* Assign type. */
67 tmp_type = *slide;
68
69 /* move beyond type */
70 slide++;
71 /* align */
72 window = slide;
73 /* Validate that only whitespace follows. */
74 slide += strspn(slide, " \t\r");
75 /* There must be whitespace. */
76 if (slide == window)
77 goto on_error;
78
79 /* Mark beginning of nsuid. */
80 window = slide;
81 /* Validate that non-whitespace follows. */
82 slide += strcspn(slide, " \t\r");
83 /* There must be non-whitespace. */
84 if (slide == window || *slide == '\0')
85 goto on_error;
86 /* Mark end of nsuid. */
87 *slide = '\0';
88
89 /* Parse nsuid. */
90 if (lxc_safe_ulong(window, &tmp_nsid) < 0)
91 goto on_error;
92
93 /* Move beyond \0. */
94 slide++;
95 /* align */
96 window = slide;
97 /* Validate that only whitespace follows. */
98 slide += strspn(slide, " \t\r");
99 /* If there was only one whitespace then we whiped it with our \0 above.
100 * So only ensure that we're not at the end of the string.
101 */
102 if (*slide == '\0')
103 goto on_error;
104
105 /* Mark beginning of hostid. */
106 window = slide;
107 /* Validate that non-whitespace follows. */
108 slide += strcspn(slide, " \t\r");
109 /* There must be non-whitespace. */
110 if (slide == window || *slide == '\0')
111 goto on_error;
112 /* Mark end of nsuid. */
113 *slide = '\0';
114
115 /* Parse hostid. */
116 if (lxc_safe_ulong(window, &tmp_hostid) < 0)
117 goto on_error;
118
119 /* Move beyond \0. */
120 slide++;
121 /* align */
122 window = slide;
123 /* Validate that only whitespace follows. */
124 slide += strspn(slide, " \t\r");
125 /* If there was only one whitespace then we whiped it with our \0 above.
126 * So only ensure that we're not at the end of the string.
127 */
128 if (*slide == '\0')
129 goto on_error;
130
131 /* Mark beginning of range. */
132 window = slide;
133 /* Validate that non-whitespace follows. */
134 slide += strcspn(slide, " \t\r");
135 /* There must be non-whitespace. */
136 if (slide == window)
137 goto on_error;
138
139 /* The range is the last valid entry we expect. So make sure that there
140 * is not trailing garbage and if there is, error out.
141 */
142 if (*(slide + strspn(slide, " \t\r\n")) != '\0')
143 goto on_error;
144 /* Mark end of range. */
145 *slide = '\0';
146
147 /* Parse range. */
148 if (lxc_safe_ulong(window, &tmp_range) < 0)
149 goto on_error;
150
151 *type = tmp_type;
152 *nsid = tmp_nsid;
153 *hostid = tmp_hostid;
154 *range = tmp_range;
155
156 /* Yay, we survived. */
157 ret = 0;
158
159on_error:
160 free(dup);
161
162 return ret;
163}
663e9916
CB
164
165bool lxc_config_value_empty(const char *value)
166{
167 if (value && strlen(value) > 0)
168 return false;
169
170 return true;
171}
ce2f5ae8 172
c302b476 173struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail)
ce2f5ae8
CB
174{
175 struct lxc_list *newlist;
176 struct lxc_netdev *netdev = NULL;
ce2f5ae8
CB
177
178 /* network does not exist */
179 netdev = malloc(sizeof(*netdev));
180 if (!netdev)
181 return NULL;
182
183 memset(netdev, 0, sizeof(*netdev));
184 lxc_list_init(&netdev->ipv4);
185 lxc_list_init(&netdev->ipv6);
186
187 /* give network a unique index */
188 netdev->idx = idx;
189
190 /* prepare new list */
191 newlist = malloc(sizeof(*newlist));
192 if (!newlist) {
193 free(netdev);
194 return NULL;
195 }
196
197 lxc_list_init(newlist);
198 newlist->elem = netdev;
199
c302b476
CB
200 if (tail)
201 lxc_list_add_tail(networks, newlist);
202 else
203 lxc_list_add(networks, newlist);
ce2f5ae8
CB
204 return netdev;
205}
1ed6ba91 206
c302b476
CB
207/* Takes care of finding the correct netdev struct in the networks list or
208 * allocates a new one if it couldn't be found.
209 */
210struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf,
211 unsigned int idx, bool allocate)
212{
213 struct lxc_netdev *netdev = NULL;
214 struct lxc_list *networks = &conf->network;
215 struct lxc_list *insert = networks;
216
217 /* lookup network */
218 if (!lxc_list_empty(networks)) {
219 lxc_list_for_each(insert, networks) {
220 netdev = insert->elem;
221 if (netdev->idx == idx)
222 return netdev;
223 else if (netdev->idx > idx)
224 break;
225 }
226 }
227
228 if (!allocate)
229 return NULL;
230
231 return lxc_network_add(insert, idx, true);
232}
233
1ed6ba91
CB
234void lxc_log_configured_netdevs(const struct lxc_conf *conf)
235{
236 struct lxc_netdev *netdev;
237 struct lxc_list *it = (struct lxc_list *)&conf->network;;
238
239 if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) &&
240 (lxc_log_get_level() != LXC_LOG_LEVEL_TRACE))
241 return;
242
243 if (lxc_list_empty(it)) {
244 TRACE("container has no networks configured");
245 return;
246 }
247
248 lxc_list_for_each(it, &conf->network) {
9b0df30f
CB
249 struct lxc_list *cur, *next;
250 struct lxc_inetdev *inet4dev;
251 struct lxc_inet6dev *inet6dev;
252 char bufinet4[INET_ADDRSTRLEN], bufinet6[INET6_ADDRSTRLEN];
253
1ed6ba91
CB
254 netdev = it->elem;
255
c302b476 256 TRACE("index: %zd", netdev->idx);
7a582518 257 TRACE("ifindex: %d", netdev->ifindex);
1ed6ba91
CB
258 switch (netdev->type) {
259 case LXC_NET_VETH:
260 TRACE("type: veth");
9b0df30f
CB
261 if (netdev->priv.veth_attr.pair)
262 TRACE("veth pair: %s",
263 netdev->priv.veth_attr.pair);
8ce727fc
CB
264 if (netdev->priv.veth_attr.veth1[0] != '\0')
265 TRACE("veth1 : %s",
266 netdev->priv.veth_attr.veth1);
d952b351
CB
267 if (netdev->priv.veth_attr.ifindex > 0)
268 TRACE("host side ifindex for veth device: %d",
269 netdev->priv.veth_attr.ifindex);
1ed6ba91
CB
270 break;
271 case LXC_NET_MACVLAN:
272 TRACE("type: macvlan");
9b0df30f
CB
273 if (netdev->priv.macvlan_attr.mode > 0) {
274 char *macvlan_mode;
275 macvlan_mode = lxc_macvlan_flag_to_mode(
276 netdev->priv.macvlan_attr.mode);
277 TRACE("macvlan mode: %s",
278 macvlan_mode ? macvlan_mode
279 : "(invalid mode)");
280 }
1ed6ba91
CB
281 break;
282 case LXC_NET_VLAN:
283 TRACE("type: vlan");
9b0df30f 284 TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
1ed6ba91
CB
285 break;
286 case LXC_NET_PHYS:
287 TRACE("type: phys");
288 break;
289 case LXC_NET_EMPTY:
290 TRACE("type: empty");
291 break;
292 case LXC_NET_NONE:
293 TRACE("type: none");
294 break;
295 default:
296 ERROR("invalid network type %d", netdev->type);
297 return;
298 }
299
9b0df30f
CB
300 if (netdev->type != LXC_NET_EMPTY) {
301 TRACE("flags: %s",
302 netdev->flags == IFF_UP ? "up" : "none");
303 if (netdev->link)
304 TRACE("link: %s", netdev->link);
305 if (netdev->name)
306 TRACE("name: %s", netdev->name);
307 if (netdev->hwaddr)
308 TRACE("hwaddr: %s", netdev->hwaddr);
309 if (netdev->mtu)
310 TRACE("mtu: %s", netdev->mtu);
311 if (netdev->upscript)
312 TRACE("upscript: %s", netdev->upscript);
313 if (netdev->downscript)
314 TRACE("downscript: %s", netdev->downscript);
315
316 TRACE("ipv4 gateway auto: %s",
317 netdev->ipv4_gateway_auto ? "true" : "false");
318
319 if (netdev->ipv4_gateway) {
320 inet_ntop(AF_INET, netdev->ipv4_gateway,
321 bufinet4, sizeof(bufinet4));
322 TRACE("ipv4 gateway: %s", bufinet4);
323 }
324
325 lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
326 inet4dev = cur->elem;
327 inet_ntop(AF_INET, &inet4dev->addr, bufinet4,
328 sizeof(bufinet4));
329 TRACE("ipv4 addr: %s", bufinet4);
330 }
331
332 TRACE("ipv6 gateway auto: %s",
333 netdev->ipv6_gateway_auto ? "true" : "false");
334 if (netdev->ipv6_gateway) {
335 inet_ntop(AF_INET6, netdev->ipv6_gateway,
336 bufinet6, sizeof(bufinet6));
337 TRACE("ipv6 gateway: %s", bufinet6);
338 }
339 lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
340 inet6dev = cur->elem;
341 inet_ntop(AF_INET6, &inet6dev->addr, bufinet6,
342 sizeof(bufinet6));
343 TRACE("ipv6 addr: %s", bufinet6);
344 }
345 }
1ed6ba91
CB
346 }
347}
519df1c1 348
e5d2fd7c
CB
349static void lxc_free_netdev(struct lxc_netdev *netdev)
350{
351 struct lxc_list *cur, *next;
352
353 free(netdev->link);
354 free(netdev->name);
355 if (netdev->type == LXC_NET_VETH)
356 free(netdev->priv.veth_attr.pair);
357 free(netdev->upscript);
358 free(netdev->downscript);
359 free(netdev->hwaddr);
360 free(netdev->mtu);
361
362 free(netdev->ipv4_gateway);
363 lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
364 lxc_list_del(cur);
365 free(cur->elem);
366 free(cur);
367 }
368
369 free(netdev->ipv6_gateway);
370 lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
371 lxc_list_del(cur);
372 free(cur->elem);
373 free(cur);
374 }
375
376 free(netdev);
377}
378
519df1c1
CB
379bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx)
380{
e5d2fd7c 381 struct lxc_list *cur, *next;
519df1c1
CB
382 struct lxc_netdev *netdev;
383 bool found = false;
384
385 lxc_list_for_each_safe(cur, &conf->network, next) {
386 netdev = cur->elem;
387 if (netdev->idx != idx)
388 continue;
389
390 lxc_list_del(cur);
391 found = true;
392 break;
393 }
394
395 if (!found)
396 return false;
397
e5d2fd7c 398 lxc_free_netdev(netdev);
519df1c1
CB
399 free(cur);
400
401 return true;
402}
e5d2fd7c 403
c302b476 404void lxc_free_networks(struct lxc_list *networks)
e5d2fd7c
CB
405{
406 struct lxc_list *cur, *next;
407 struct lxc_netdev *netdev;
408
c302b476 409 lxc_list_for_each_safe(cur, networks, next) {
e5d2fd7c
CB
410 netdev = cur->elem;
411 lxc_free_netdev(netdev);
412 free(cur);
413 }
414
415 /* prevent segfaults */
c302b476 416 lxc_list_init(networks);
e5d2fd7c 417}
9b0df30f
CB
418
419static struct macvlan_mode {
420 char *name;
421 int mode;
422} macvlan_mode[] = {
423 { "private", MACVLAN_MODE_PRIVATE },
424 { "vepa", MACVLAN_MODE_VEPA },
425 { "bridge", MACVLAN_MODE_BRIDGE },
426 { "passthru", MACVLAN_MODE_PASSTHRU },
427};
428
429int lxc_macvlan_mode_to_flag(int *mode, const char *value)
430{
431 size_t i;
432
433 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
434 if (strcmp(macvlan_mode[i].name, value))
435 continue;
436
437 *mode = macvlan_mode[i].mode;
438 return 0;
439 }
440
441 return -1;
442}
443
444char *lxc_macvlan_flag_to_mode(int mode)
445{
446 size_t i;
447
448 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
449 if (macvlan_mode[i].mode == mode)
450 continue;
451
452 return macvlan_mode[i].name;
453 }
454
455 return NULL;
456}
f9373e40
CB
457
458int set_config_string_item(char **conf_item, const char *value)
459{
460 char *new_value;
461
462 if (lxc_config_value_empty(value)) {
463 free(*conf_item);
464 *conf_item = NULL;
465 return 0;
466 }
467
468 new_value = strdup(value);
469 if (!new_value) {
470 SYSERROR("failed to duplicate string \"%s\"", value);
471 return -1;
472 }
473
474 free(*conf_item);
475 *conf_item = new_value;
476 return 0;
477}
478
479int set_config_string_item_max(char **conf_item, const char *value, size_t max)
480{
481 if (strlen(value) >= max) {
482 ERROR("%s is too long (>= %lu)", value, (unsigned long)max);
483 return -1;
484 }
485
486 return set_config_string_item(conf_item, value);
487}
488
489int set_config_path_item(char **conf_item, const char *value)
490{
491 return set_config_string_item_max(conf_item, value, PATH_MAX);
492}
493
494int config_ip_prefix(struct in_addr *addr)
495{
496 if (IN_CLASSA(addr->s_addr))
497 return 32 - IN_CLASSA_NSHIFT;
498 if (IN_CLASSB(addr->s_addr))
499 return 32 - IN_CLASSB_NSHIFT;
500 if (IN_CLASSC(addr->s_addr))
501 return 32 - IN_CLASSC_NSHIFT;
502
503 return 0;
504}
505
506int network_ifname(char **valuep, const char *value)
507{
508 return set_config_string_item_max(valuep, value, IFNAMSIZ);
509}
510
511int rand_complete_hwaddr(char *hwaddr)
512{
513 const char hex[] = "0123456789abcdef";
514 char *curs = hwaddr;
515
516#ifndef HAVE_RAND_R
517 randseed(true);
518#else
519 unsigned int seed;
520
521 seed = randseed(false);
522#endif
523 while (*curs != '\0' && *curs != '\n') {
524 if (*curs == 'x' || *curs == 'X') {
525 if (curs - hwaddr == 1) {
526 /* ensure address is unicast */
527#ifdef HAVE_RAND_R
528 *curs = hex[rand_r(&seed) & 0x0E];
529 } else {
530 *curs = hex[rand_r(&seed) & 0x0F];
531#else
532 *curs = hex[rand() & 0x0E];
533 } else {
534 *curs = hex[rand() & 0x0F];
535#endif
536 }
537 }
538 curs++;
539 }
540 return 0;
541}
542
ce4be612 543bool lxc_config_net_hwaddr(const char *line)
544{
545 char *copy, *p;
546
547 if (strncmp(line, "lxc.net", 7) != 0)
548 return false;
549 if (strncmp(line, "lxc.network.hwaddr", 18) == 0)
550 return true;
551
552 /* We have to dup the line, if line is something like
553 * "lxc.net.[i].xxx = xxxxx ", we need to remove
554 * '[i]' and compare its key with 'lxc.net.hwaddr'*/
555 copy = strdup(line);
556 if (!copy) {
557 SYSERROR("failed to allocate memory");
558 return false;
559 }
560 if (*(copy + 8) >= '0' && *(copy + 8) <= '9') {
561 p = strchr(copy + 8, '.');
562 if (!p) {
563 free(copy);
564 return false;
565 }
566 /* strlen("hwaddr") = 6 */
567 strncpy(copy + 8, p + 1, 6);
568 copy[8 + 6] = '\0';
569 }
570 if (strncmp(copy, "lxc.net.hwaddr", 14) == 0) {
571 free(copy);
572 return true;
573 }
574 free(copy);
575
576 /* We have to dup the line second time, if line is something like
577 * "lxc.network.[i].xxx = xxxxx ", we need to remove
578 * '[i]' and compare its key with 'lxc.network.hwaddr'*/
579 copy = strdup(line);
580 if (!copy) {
581 SYSERROR("failed to allocate memory");
582 return false;
583 }
584 if (*(copy + 12) >= '0' && *(copy + 12) <= '9') {
585 p = strchr(copy + 12, '.');
586 if (!p) {
587 free(copy);
588 return false;
589 }
590 /* strlen("hwaddr") = 6 */
591 strncpy(copy + 12, p + 1, 6);
592 copy[12 + 6] = '\0';
593 }
594 if (strncmp(copy, "lxc.network.hwaddr", 18) == 0) {
595 free(copy);
596 return true;
597 }
598
599 free(copy);
600 return false;
601}
602
f9373e40 603/*
ae1dc8b4 604 * If we find a lxc.net.[i].hwaddr or lxc.network.hwaddr in the original config
605 * file, we expand it in the unexpanded_config, so that after a save_config we
606 * store the hwaddr for re-use.
f9373e40
CB
607 * This is only called when reading the config file, not when executing a
608 * lxc.include.
609 * 'x' and 'X' are substituted in-place.
610 */
611void update_hwaddr(const char *line)
612{
613 char *p;
614
615 line += lxc_char_left_gc(line, strlen(line));
616 if (line[0] == '#')
617 return;
618
ae1dc8b4 619 if (!lxc_config_net_hwaddr(line))
f9373e40
CB
620 return;
621
622 /* Let config_net_hwaddr raise the error. */
623 p = strchr(line, '=');
624 if (!p)
625 return;
626 p++;
627
628 while (isblank(*p))
629 p++;
630
631 if (!*p)
632 return;
633
634 rand_complete_hwaddr(p);
635}
636
637bool new_hwaddr(char *hwaddr)
638{
639 int ret;
640
641 (void)randseed(true);
642
643 ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
644 rand() % 255, rand() % 255);
645 if (ret < 0 || ret >= 18) {
646 SYSERROR("Failed to call snprintf().");
647 return false;
648 }
649
650 return true;
651}
953fe44f
CB
652
653int lxc_get_conf_str(char *retv, int inlen, const char *value)
654{
655 if (!value)
656 return 0;
657 if (retv && inlen >= strlen(value) + 1)
658 strncpy(retv, value, strlen(value) + 1);
659
660 return strlen(value);
661}
662
663int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
664{
665 if (!retv)
666 inlen = 0;
667 else
668 memset(retv, 0, inlen);
669
670 return snprintf(retv, inlen, "%d", v);
671}
240d4b74 672
673bool parse_limit_value(const char **value, unsigned long *res)
674{
675 char *endptr = NULL;
676
677 if (strncmp(*value, "unlimited", sizeof("unlimited") - 1) == 0) {
678 *res = RLIM_INFINITY;
679 *value += sizeof("unlimited") - 1;
680 return true;
681 }
682
683 errno = 0;
684 *res = strtoul(*value, &endptr, 10);
685 if (errno || !endptr)
686 return false;
687 *value = endptr;
688
689 return true;
690}