]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/confile_utils.c
utils: add lxc_find_next_power2()
[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++;
0b843d35
CB
95 /* Validate that only whitespace follows. */
96 slide += strspn(slide, " \t\r");
97 /* If there was only one whitespace then we whiped it with our \0 above.
98 * So only ensure that we're not at the end of the string.
99 */
100 if (*slide == '\0')
101 goto on_error;
102
103 /* Mark beginning of hostid. */
104 window = slide;
105 /* Validate that non-whitespace follows. */
106 slide += strcspn(slide, " \t\r");
107 /* There must be non-whitespace. */
108 if (slide == window || *slide == '\0')
109 goto on_error;
110 /* Mark end of nsuid. */
111 *slide = '\0';
112
113 /* Parse hostid. */
114 if (lxc_safe_ulong(window, &tmp_hostid) < 0)
115 goto on_error;
116
117 /* Move beyond \0. */
118 slide++;
0b843d35
CB
119 /* Validate that only whitespace follows. */
120 slide += strspn(slide, " \t\r");
121 /* If there was only one whitespace then we whiped it with our \0 above.
122 * So only ensure that we're not at the end of the string.
123 */
124 if (*slide == '\0')
125 goto on_error;
126
127 /* Mark beginning of range. */
128 window = slide;
129 /* Validate that non-whitespace follows. */
130 slide += strcspn(slide, " \t\r");
131 /* There must be non-whitespace. */
132 if (slide == window)
133 goto on_error;
134
135 /* The range is the last valid entry we expect. So make sure that there
136 * is not trailing garbage and if there is, error out.
137 */
138 if (*(slide + strspn(slide, " \t\r\n")) != '\0')
139 goto on_error;
140 /* Mark end of range. */
141 *slide = '\0';
142
143 /* Parse range. */
144 if (lxc_safe_ulong(window, &tmp_range) < 0)
145 goto on_error;
146
147 *type = tmp_type;
148 *nsid = tmp_nsid;
149 *hostid = tmp_hostid;
150 *range = tmp_range;
151
152 /* Yay, we survived. */
153 ret = 0;
154
155on_error:
156 free(dup);
157
158 return ret;
159}
663e9916
CB
160
161bool lxc_config_value_empty(const char *value)
162{
163 if (value && strlen(value) > 0)
164 return false;
165
166 return true;
167}
ce2f5ae8 168
c302b476 169struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail)
ce2f5ae8
CB
170{
171 struct lxc_list *newlist;
172 struct lxc_netdev *netdev = NULL;
ce2f5ae8
CB
173
174 /* network does not exist */
175 netdev = malloc(sizeof(*netdev));
176 if (!netdev)
177 return NULL;
178
179 memset(netdev, 0, sizeof(*netdev));
180 lxc_list_init(&netdev->ipv4);
181 lxc_list_init(&netdev->ipv6);
182
183 /* give network a unique index */
184 netdev->idx = idx;
185
186 /* prepare new list */
187 newlist = malloc(sizeof(*newlist));
188 if (!newlist) {
189 free(netdev);
190 return NULL;
191 }
192
193 lxc_list_init(newlist);
194 newlist->elem = netdev;
195
c302b476
CB
196 if (tail)
197 lxc_list_add_tail(networks, newlist);
198 else
199 lxc_list_add(networks, newlist);
ce2f5ae8
CB
200 return netdev;
201}
1ed6ba91 202
c302b476
CB
203/* Takes care of finding the correct netdev struct in the networks list or
204 * allocates a new one if it couldn't be found.
205 */
206struct lxc_netdev *lxc_get_netdev_by_idx(struct lxc_conf *conf,
207 unsigned int idx, bool allocate)
208{
209 struct lxc_netdev *netdev = NULL;
210 struct lxc_list *networks = &conf->network;
211 struct lxc_list *insert = networks;
212
213 /* lookup network */
214 if (!lxc_list_empty(networks)) {
215 lxc_list_for_each(insert, networks) {
216 netdev = insert->elem;
217 if (netdev->idx == idx)
218 return netdev;
219 else if (netdev->idx > idx)
220 break;
221 }
222 }
223
224 if (!allocate)
225 return NULL;
226
227 return lxc_network_add(insert, idx, true);
228}
229
1ed6ba91
CB
230void lxc_log_configured_netdevs(const struct lxc_conf *conf)
231{
232 struct lxc_netdev *netdev;
233 struct lxc_list *it = (struct lxc_list *)&conf->network;;
234
235 if ((conf->loglevel != LXC_LOG_LEVEL_TRACE) &&
236 (lxc_log_get_level() != LXC_LOG_LEVEL_TRACE))
237 return;
238
239 if (lxc_list_empty(it)) {
240 TRACE("container has no networks configured");
241 return;
242 }
243
244 lxc_list_for_each(it, &conf->network) {
9b0df30f
CB
245 struct lxc_list *cur, *next;
246 struct lxc_inetdev *inet4dev;
247 struct lxc_inet6dev *inet6dev;
248 char bufinet4[INET_ADDRSTRLEN], bufinet6[INET6_ADDRSTRLEN];
249
1ed6ba91
CB
250 netdev = it->elem;
251
c302b476 252 TRACE("index: %zd", netdev->idx);
7a582518 253 TRACE("ifindex: %d", netdev->ifindex);
1ed6ba91
CB
254 switch (netdev->type) {
255 case LXC_NET_VETH:
256 TRACE("type: veth");
de4855a8 257 if (netdev->priv.veth_attr.pair[0] != '\0')
9b0df30f
CB
258 TRACE("veth pair: %s",
259 netdev->priv.veth_attr.pair);
8ce727fc
CB
260 if (netdev->priv.veth_attr.veth1[0] != '\0')
261 TRACE("veth1 : %s",
262 netdev->priv.veth_attr.veth1);
d952b351
CB
263 if (netdev->priv.veth_attr.ifindex > 0)
264 TRACE("host side ifindex for veth device: %d",
265 netdev->priv.veth_attr.ifindex);
1ed6ba91
CB
266 break;
267 case LXC_NET_MACVLAN:
268 TRACE("type: macvlan");
9b0df30f
CB
269 if (netdev->priv.macvlan_attr.mode > 0) {
270 char *macvlan_mode;
271 macvlan_mode = lxc_macvlan_flag_to_mode(
272 netdev->priv.macvlan_attr.mode);
273 TRACE("macvlan mode: %s",
274 macvlan_mode ? macvlan_mode
275 : "(invalid mode)");
276 }
1ed6ba91
CB
277 break;
278 case LXC_NET_VLAN:
279 TRACE("type: vlan");
9b0df30f 280 TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
1ed6ba91
CB
281 break;
282 case LXC_NET_PHYS:
283 TRACE("type: phys");
b809f232
CB
284 if (netdev->priv.phys_attr.ifindex > 0) {
285 TRACE("host side ifindex for phys device: %d",
286 netdev->priv.phys_attr.ifindex);
287 }
1ed6ba91
CB
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");
de4855a8 303 if (netdev->link[0] != '\0')
9b0df30f 304 TRACE("link: %s", netdev->link);
de4855a8 305 if (netdev->name[0] != '\0')
9b0df30f
CB
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
e5d2fd7c
CB
353 free(netdev->upscript);
354 free(netdev->downscript);
355 free(netdev->hwaddr);
356 free(netdev->mtu);
357
358 free(netdev->ipv4_gateway);
359 lxc_list_for_each_safe(cur, &netdev->ipv4, next) {
360 lxc_list_del(cur);
361 free(cur->elem);
362 free(cur);
363 }
364
365 free(netdev->ipv6_gateway);
366 lxc_list_for_each_safe(cur, &netdev->ipv6, next) {
367 lxc_list_del(cur);
368 free(cur->elem);
369 free(cur);
370 }
371
372 free(netdev);
373}
374
519df1c1
CB
375bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx)
376{
e5d2fd7c 377 struct lxc_list *cur, *next;
519df1c1
CB
378 struct lxc_netdev *netdev;
379 bool found = false;
380
381 lxc_list_for_each_safe(cur, &conf->network, next) {
382 netdev = cur->elem;
383 if (netdev->idx != idx)
384 continue;
385
386 lxc_list_del(cur);
387 found = true;
388 break;
389 }
390
391 if (!found)
392 return false;
393
e5d2fd7c 394 lxc_free_netdev(netdev);
519df1c1
CB
395 free(cur);
396
397 return true;
398}
e5d2fd7c 399
c302b476 400void lxc_free_networks(struct lxc_list *networks)
e5d2fd7c
CB
401{
402 struct lxc_list *cur, *next;
403 struct lxc_netdev *netdev;
404
c302b476 405 lxc_list_for_each_safe(cur, networks, next) {
e5d2fd7c
CB
406 netdev = cur->elem;
407 lxc_free_netdev(netdev);
408 free(cur);
409 }
410
411 /* prevent segfaults */
c302b476 412 lxc_list_init(networks);
e5d2fd7c 413}
9b0df30f
CB
414
415static struct macvlan_mode {
416 char *name;
417 int mode;
418} macvlan_mode[] = {
419 { "private", MACVLAN_MODE_PRIVATE },
420 { "vepa", MACVLAN_MODE_VEPA },
421 { "bridge", MACVLAN_MODE_BRIDGE },
422 { "passthru", MACVLAN_MODE_PASSTHRU },
423};
424
425int lxc_macvlan_mode_to_flag(int *mode, const char *value)
426{
427 size_t i;
428
429 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
430 if (strcmp(macvlan_mode[i].name, value))
431 continue;
432
433 *mode = macvlan_mode[i].mode;
434 return 0;
435 }
436
437 return -1;
438}
439
440char *lxc_macvlan_flag_to_mode(int mode)
441{
442 size_t i;
443
444 for (i = 0; i < sizeof(macvlan_mode) / sizeof(macvlan_mode[0]); i++) {
445 if (macvlan_mode[i].mode == mode)
446 continue;
447
448 return macvlan_mode[i].name;
449 }
450
451 return NULL;
452}
f9373e40
CB
453
454int set_config_string_item(char **conf_item, const char *value)
455{
456 char *new_value;
457
458 if (lxc_config_value_empty(value)) {
459 free(*conf_item);
460 *conf_item = NULL;
461 return 0;
462 }
463
464 new_value = strdup(value);
465 if (!new_value) {
466 SYSERROR("failed to duplicate string \"%s\"", value);
467 return -1;
468 }
469
470 free(*conf_item);
471 *conf_item = new_value;
472 return 0;
473}
474
475int set_config_string_item_max(char **conf_item, const char *value, size_t max)
476{
477 if (strlen(value) >= max) {
478 ERROR("%s is too long (>= %lu)", value, (unsigned long)max);
479 return -1;
480 }
481
482 return set_config_string_item(conf_item, value);
483}
484
485int set_config_path_item(char **conf_item, const char *value)
486{
487 return set_config_string_item_max(conf_item, value, PATH_MAX);
488}
489
490int config_ip_prefix(struct in_addr *addr)
491{
492 if (IN_CLASSA(addr->s_addr))
493 return 32 - IN_CLASSA_NSHIFT;
494 if (IN_CLASSB(addr->s_addr))
495 return 32 - IN_CLASSB_NSHIFT;
496 if (IN_CLASSC(addr->s_addr))
497 return 32 - IN_CLASSC_NSHIFT;
498
499 return 0;
500}
501
de4855a8 502int network_ifname(char *valuep, const char *value)
f9373e40 503{
de4855a8
CB
504 if (strlen(value) >= IFNAMSIZ) {
505 ERROR("Network devie name \"%s\" is too long (>= %zu)", value,
506 (size_t)IFNAMSIZ);
507 }
508
509 strcpy(valuep, value);
510 return 0;
f9373e40
CB
511}
512
513int rand_complete_hwaddr(char *hwaddr)
514{
515 const char hex[] = "0123456789abcdef";
516 char *curs = hwaddr;
517
518#ifndef HAVE_RAND_R
519 randseed(true);
520#else
521 unsigned int seed;
522
523 seed = randseed(false);
524#endif
525 while (*curs != '\0' && *curs != '\n') {
526 if (*curs == 'x' || *curs == 'X') {
527 if (curs - hwaddr == 1) {
528 /* ensure address is unicast */
529#ifdef HAVE_RAND_R
530 *curs = hex[rand_r(&seed) & 0x0E];
531 } else {
532 *curs = hex[rand_r(&seed) & 0x0F];
533#else
534 *curs = hex[rand() & 0x0E];
535 } else {
536 *curs = hex[rand() & 0x0F];
537#endif
538 }
539 }
540 curs++;
541 }
542 return 0;
543}
544
ce4be612 545bool lxc_config_net_hwaddr(const char *line)
546{
547 char *copy, *p;
548
549 if (strncmp(line, "lxc.net", 7) != 0)
550 return false;
551 if (strncmp(line, "lxc.network.hwaddr", 18) == 0)
552 return true;
553
554 /* We have to dup the line, if line is something like
555 * "lxc.net.[i].xxx = xxxxx ", we need to remove
556 * '[i]' and compare its key with 'lxc.net.hwaddr'*/
557 copy = strdup(line);
558 if (!copy) {
559 SYSERROR("failed to allocate memory");
560 return false;
561 }
562 if (*(copy + 8) >= '0' && *(copy + 8) <= '9') {
563 p = strchr(copy + 8, '.');
564 if (!p) {
565 free(copy);
566 return false;
567 }
568 /* strlen("hwaddr") = 6 */
569 strncpy(copy + 8, p + 1, 6);
570 copy[8 + 6] = '\0';
571 }
572 if (strncmp(copy, "lxc.net.hwaddr", 14) == 0) {
573 free(copy);
574 return true;
575 }
576 free(copy);
577
578 /* We have to dup the line second time, if line is something like
579 * "lxc.network.[i].xxx = xxxxx ", we need to remove
580 * '[i]' and compare its key with 'lxc.network.hwaddr'*/
581 copy = strdup(line);
582 if (!copy) {
583 SYSERROR("failed to allocate memory");
584 return false;
585 }
586 if (*(copy + 12) >= '0' && *(copy + 12) <= '9') {
587 p = strchr(copy + 12, '.');
588 if (!p) {
589 free(copy);
590 return false;
591 }
592 /* strlen("hwaddr") = 6 */
593 strncpy(copy + 12, p + 1, 6);
594 copy[12 + 6] = '\0';
595 }
596 if (strncmp(copy, "lxc.network.hwaddr", 18) == 0) {
597 free(copy);
598 return true;
599 }
600
601 free(copy);
602 return false;
603}
604
f9373e40 605/*
ae1dc8b4 606 * If we find a lxc.net.[i].hwaddr or lxc.network.hwaddr in the original config
607 * file, we expand it in the unexpanded_config, so that after a save_config we
608 * store the hwaddr for re-use.
f9373e40
CB
609 * This is only called when reading the config file, not when executing a
610 * lxc.include.
611 * 'x' and 'X' are substituted in-place.
612 */
613void update_hwaddr(const char *line)
614{
615 char *p;
616
617 line += lxc_char_left_gc(line, strlen(line));
618 if (line[0] == '#')
619 return;
620
ae1dc8b4 621 if (!lxc_config_net_hwaddr(line))
f9373e40
CB
622 return;
623
624 /* Let config_net_hwaddr raise the error. */
625 p = strchr(line, '=');
626 if (!p)
627 return;
628 p++;
629
630 while (isblank(*p))
631 p++;
632
633 if (!*p)
634 return;
635
636 rand_complete_hwaddr(p);
637}
638
639bool new_hwaddr(char *hwaddr)
640{
641 int ret;
642
643 (void)randseed(true);
644
645 ret = snprintf(hwaddr, 18, "00:16:3e:%02x:%02x:%02x", rand() % 255,
646 rand() % 255, rand() % 255);
647 if (ret < 0 || ret >= 18) {
648 SYSERROR("Failed to call snprintf().");
649 return false;
650 }
651
652 return true;
653}
953fe44f
CB
654
655int lxc_get_conf_str(char *retv, int inlen, const char *value)
656{
657 if (!value)
658 return 0;
659 if (retv && inlen >= strlen(value) + 1)
660 strncpy(retv, value, strlen(value) + 1);
661
662 return strlen(value);
663}
664
665int lxc_get_conf_int(struct lxc_conf *c, char *retv, int inlen, int v)
666{
667 if (!retv)
668 inlen = 0;
669 else
670 memset(retv, 0, inlen);
671
672 return snprintf(retv, inlen, "%d", v);
673}
240d4b74 674
71460831 675bool parse_limit_value(const char **value, rlim_t *res)
240d4b74 676{
677 char *endptr = NULL;
678
679 if (strncmp(*value, "unlimited", sizeof("unlimited") - 1) == 0) {
680 *res = RLIM_INFINITY;
681 *value += sizeof("unlimited") - 1;
682 return true;
683 }
684
685 errno = 0;
71460831 686 *res = strtoull(*value, &endptr, 10);
240d4b74 687 if (errno || !endptr)
688 return false;
689 *value = endptr;
690
691 return true;
692}