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