]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/lxc_user_nic.c
tree-wide: s/getpid()/lxc_raw_getpid()/g
[mirror_lxc.git] / src / lxc / lxc_user_nic.c
CommitLineData
20ab58c7
SH
1/*
2 *
3 * Copyright © 2013 Serge Hallyn <serge.hallyn@ubuntu.com>.
4 * Copyright © 2013 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
811ef482 20#define _GNU_SOURCE
1f109d47
CB
21#include <alloca.h>
22#include <ctype.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <grp.h>
26#include <pwd.h>
27#include <sched.h>
28#include <stdbool.h>
20ab58c7
SH
29#include <stdio.h>
30#include <stdlib.h>
20ab58c7 31#include <string.h>
1f109d47 32#include <unistd.h>
20ab58c7 33#include <arpa/inet.h>
4119204e 34#include <linux/netlink.h>
20ab58c7
SH
35#include <linux/rtnetlink.h>
36#include <linux/sockios.h>
1f109d47
CB
37#include <net/if.h>
38#include <net/if_arp.h>
39#include <netinet/in.h>
40#include <sys/file.h>
41#include <sys/ioctl.h>
42#include <sys/mman.h>
4119204e 43#include <sys/param.h>
1f109d47
CB
44#include <sys/socket.h>
45#include <sys/stat.h>
46#include <sys/types.h>
f2363e38 47
37fc7b9e 48#include "config.h"
0059379f 49#include "namespace.h"
8befa924 50#include "network.h"
1f109d47 51#include "utils.h"
20ab58c7 52
16af2380
CB
53#define usernic_debug_stream(stream, format, ...) \
54 do { \
55 fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__, \
56 __func__, __VA_ARGS__); \
57 } while (false)
58
59#define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__)
60
74a3920a 61static void usage(char *me, bool fail)
20ab58c7 62{
900e5f94
CB
63 fprintf(stderr, "Usage: %s create {lxcpath} {name} {pid} {type} "
64 "{bridge} {nicname}\n", me);
cb5659e1
CB
65 fprintf(stderr, "Usage: %s delete {lxcpath} {name} "
66 "{/proc/<pid>/ns/net} {type} {bridge} {nicname}\n", me);
900e5f94
CB
67 fprintf(stderr, "{nicname} is the name to use inside the container\n");
68
69 if (fail)
70 exit(EXIT_FAILURE);
20ab58c7 71
900e5f94
CB
72 exit(EXIT_SUCCESS);
73}
c43cbc04 74
552a7d05 75static int open_and_lock(char *path)
20ab58c7 76{
af256970 77 int fd, ret;
20ab58c7
SH
78 struct flock lk;
79
af256970 80 fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR);
20ab58c7 81 if (fd < 0) {
af256970
CB
82 usernic_error("Failed to open \"%s\": %s\n", path,
83 strerror(errno));
2b333aee 84 return -1;
20ab58c7
SH
85 }
86
87 lk.l_type = F_WRLCK;
88 lk.l_whence = SEEK_SET;
89 lk.l_start = 0;
90 lk.l_len = 0;
af256970
CB
91
92 ret = fcntl(fd, F_SETLKW, &lk);
93 if (ret < 0) {
94 usernic_error("Failed to lock \"%s\": %s\n", path,
95 strerror(errno));
49aba4d3 96 close(fd);
78f97d4c 97 return -1;
20ab58c7
SH
98 }
99
100 return fd;
101}
102
552a7d05 103static char *get_username(void)
20ab58c7 104{
2b333aee 105 struct passwd *pwd;
20ab58c7 106
2b333aee
CB
107 pwd = getpwuid(getuid());
108 if (!pwd) {
af256970 109 usernic_error("Failed to get username: %s\n", strerror(errno));
20ab58c7
SH
110 return NULL;
111 }
112
37fc7b9e 113 return pwd->pw_name;
20ab58c7
SH
114}
115
21002b39
HK
116static void free_groupnames(char **groupnames)
117{
d791668b 118 int i;
2b333aee 119
d791668b
SH
120 if (!groupnames)
121 return;
2b333aee 122
d791668b
SH
123 for (i = 0; groupnames[i]; i++)
124 free(groupnames[i]);
2b333aee 125
21002b39
HK
126 free(groupnames);
127}
128
3043883c
HK
129static char **get_groupnames(void)
130{
131 int ngroups;
132 gid_t *group_ids;
21002b39 133 int ret, i;
af59ff2e 134 char **groupnames;
3043883c
HK
135 struct group *gr;
136
137 ngroups = getgroups(0, NULL);
2b333aee 138 if (ngroups < 0) {
af256970
CB
139 usernic_error("Failed to get number of groups the user "
140 "belongs to: %s\n", strerror(errno));
af59ff2e
HK
141 return NULL;
142 }
21002b39
HK
143 if (ngroups == 0)
144 return NULL;
1b7eaf07 145
2b333aee
CB
146 group_ids = malloc(sizeof(gid_t) * ngroups);
147 if (!group_ids) {
148 usernic_error("Failed to allocate memory while getting groups "
e771c51d 149 "the user belongs to: %s\n",
2b333aee 150 strerror(errno));
21002b39
HK
151 return NULL;
152 }
153
3043883c 154 ret = getgroups(ngroups, group_ids);
3043883c
HK
155 if (ret < 0) {
156 free(group_ids);
e771c51d 157 usernic_error("Failed to get process groups: %s\n",
2b333aee 158 strerror(errno));
3043883c
HK
159 return NULL;
160 }
161
2b333aee
CB
162 groupnames = malloc(sizeof(char *) * (ngroups + 1));
163 if (!groupnames) {
21002b39 164 free(group_ids);
2b333aee 165 usernic_error("Failed to allocate memory while getting group "
e771c51d 166 "names: %s\n",
2b333aee 167 strerror(errno));
21002b39
HK
168 return NULL;
169 }
170
2b333aee 171 memset(groupnames, 0, sizeof(char *) * (ngroups + 1));
21002b39 172
2b333aee 173 for (i = 0; i < ngroups; i++) {
3043883c 174 gr = getgrgid(group_ids[i]);
2b333aee 175 if (!gr) {
e771c51d 176 usernic_error("Failed to get group name: %s\n",
2b333aee 177 strerror(errno));
3043883c 178 free(group_ids);
21002b39 179 free_groupnames(groupnames);
3043883c
HK
180 return NULL;
181 }
182
af59ff2e 183 groupnames[i] = strdup(gr->gr_name);
2b333aee 184 if (!groupnames[i]) {
e771c51d 185 usernic_error("Failed to copy group name \"%s\"",
2b333aee 186 gr->gr_name);
3043883c 187 free(group_ids);
21002b39 188 free_groupnames(groupnames);
1b7eaf07 189 return NULL;
3043883c
HK
190 }
191 }
192
3043883c
HK
193 free(group_ids);
194
af59ff2e
HK
195 return groupnames;
196}
197
af59ff2e
HK
198static bool name_is_in_groupnames(char *name, char **groupnames)
199{
2b333aee
CB
200 while (groupnames) {
201 if (!strcmp(name, *groupnames))
af59ff2e
HK
202 return true;
203 groupnames++;
204 }
205 return false;
3043883c
HK
206}
207
af59ff2e
HK
208struct alloted_s {
209 char *name;
210 int allowed;
211 struct alloted_s *next;
212};
3043883c 213
af256970
CB
214static struct alloted_s *append_alloted(struct alloted_s **head, char *name,
215 int n)
3043883c
HK
216{
217 struct alloted_s *cur, *al;
218
2b333aee 219 if (!head || !name) {
af256970 220 /* Sanity check. Parameters should not be null. */
e771c51d 221 usernic_error("%s\n", "Unexpected NULL argument");
3043883c
HK
222 return NULL;
223 }
224
2b333aee
CB
225 al = malloc(sizeof(struct alloted_s));
226 if (!al) {
af256970
CB
227 usernic_error("Failed to allocate memory: %s\n",
228 strerror(errno));
3043883c
HK
229 return NULL;
230 }
231
232 al->name = strdup(name);
2b333aee 233 if (!al->name) {
21002b39
HK
234 free(al);
235 return NULL;
236 }
237
3043883c
HK
238 al->allowed = n;
239 al->next = NULL;
240
2b333aee 241 if (!*head) {
3043883c
HK
242 *head = al;
243 return al;
244 }
245
246 cur = *head;
2b333aee 247 while (cur->next)
3043883c 248 cur = cur->next;
3043883c 249 cur->next = al;
2b333aee 250
3043883c
HK
251 return al;
252}
253
254static void free_alloted(struct alloted_s **head)
255{
256 struct alloted_s *cur;
257
2b333aee 258 if (!head)
3043883c 259 return;
3043883c
HK
260
261 cur = *head;
2b333aee 262 while (cur) {
3043883c
HK
263 cur = cur->next;
264 free((*head)->name);
265 free(*head);
266 *head = cur;
267 }
268}
269
20ab58c7
SH
270/* The configuration file consists of lines of the form:
271 *
8c81de19 272 * user type bridge count
af59ff2e
HK
273 * or
274 * @group type bridge count
20ab58c7 275 *
8c81de19
SH
276 * Return the count entry for the calling user if there is one. Else
277 * return -1.
20ab58c7 278 */
af256970
CB
279static int get_alloted(char *me, char *intype, char *link,
280 struct alloted_s **alloted)
20ab58c7 281{
2b333aee 282 int n, ret;
af59ff2e 283 char name[100], type[100], br[100];
3043883c 284 char **groups;
2b333aee 285 FILE *fin;
20ab58c7 286
2b333aee
CB
287 int count = 0;
288 size_t len = 0;
289 char *line = NULL;
290
291 fin = fopen(LXC_USERNIC_CONF, "r");
8c81de19 292 if (!fin) {
af256970
CB
293 usernic_error("Failed to open \"%s\": %s\n", LXC_USERNIC_CONF,
294 strerror(errno));
20ab58c7 295 return -1;
8c81de19 296 }
20ab58c7 297
af59ff2e 298 groups = get_groupnames();
20ab58c7 299 while ((getline(&line, &len, fin)) != -1) {
af256970
CB
300 ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name,
301 type, br, &n);
20ab58c7
SH
302 if (ret != 4)
303 continue;
af59ff2e
HK
304
305 if (strlen(name) == 0)
20ab58c7 306 continue;
af59ff2e 307
2b333aee 308 if (strcmp(name, me)) {
af59ff2e
HK
309 if (name[0] != '@')
310 continue;
2b333aee
CB
311
312 if (!name_is_in_groupnames(name + 1, groups))
af59ff2e
HK
313 continue;
314 }
2b333aee
CB
315
316 if (strcmp(type, intype))
20ab58c7 317 continue;
2b333aee
CB
318
319 if (strcmp(link, br))
20ab58c7 320 continue;
3043883c 321
2b333aee
CB
322 /* Found the user or group with the appropriate settings,
323 * therefore finish the search. What to do if there are more
324 * than one applicable lines? not specified in the docs. Since
325 * getline is implemented with realloc, we don't need to free
326 * line until exiting func.
680836fa 327 *
2b333aee
CB
328 * If append_alloted returns NULL, e.g. due to a malloc error,
329 * we set count to 0 and break the loop, allowing cleanup and
330 * then exiting from main().
af59ff2e 331 */
2b333aee 332 if (!append_alloted(alloted, name, n)) {
680836fa
HK
333 count = 0;
334 break;
335 }
3043883c 336 count += n;
3043883c
HK
337 }
338
af59ff2e 339 free_groupnames(groups);
20ab58c7 340 fclose(fin);
f10fad2f 341 free(line);
21002b39 342
2b333aee 343 /* Now return the total number of nics that this user can create. */
3043883c 344 return count;
20ab58c7
SH
345}
346
8f7b58d6 347static char *get_eol(char *s, char *e)
20ab58c7 348{
2b333aee 349 while ((s < e) && *s && (*s != '\n'))
20ab58c7
SH
350 s++;
351 return s;
352}
353
8f7b58d6 354static char *get_eow(char *s, char *e)
20ab58c7 355{
2b333aee 356 while ((s < e) && *s && !isblank(*s) && (*s != '\n'))
20ab58c7
SH
357 s++;
358 return s;
359}
360
6d8c2779
CB
361static char *find_line(char *buf_start, char *buf_end, char *name,
362 char *net_type, char *net_link, char *net_dev,
363 bool *owner, bool *found, bool *keep)
20ab58c7 364{
6d8c2779 365 char *end_of_line, *end_of_word, *line;
1b7eaf07 366
6d8c2779
CB
367 while (buf_start < buf_end) {
368 size_t len;
369 char netdev_name[IFNAMSIZ];
370
371 *found = false;
372 *keep = true;
373 *owner = false;
374
375 end_of_line = get_eol(buf_start, buf_end);
376 if (end_of_line >= buf_end)
377 return NULL;
378
379 line = buf_start;
380 if (*buf_start == '#')
20ab58c7 381 goto next;
2b333aee 382
6d8c2779
CB
383 while ((buf_start < buf_end) && isblank(*buf_start))
384 buf_start++;
385
386 /* Check whether the line contains the caller's name. */
387 end_of_word = get_eow(buf_start, buf_end);
388 /* corrupt db */
389 if (!end_of_word)
390 return NULL;
391
392 if (strncmp(buf_start, name, strlen(name)))
393 *found = false;
394
395 *owner = true;
2b333aee 396
6d8c2779
CB
397 buf_start = end_of_word + 1;
398 while ((buf_start < buf_end) && isblank(*buf_start))
399 buf_start++;
400
401 /* Check whether line is of the right network type. */
402 end_of_word = get_eow(buf_start, buf_end);
403 /* corrupt db */
404 if (!end_of_word)
405 return NULL;
2b333aee 406
6d8c2779
CB
407 if (strncmp(buf_start, net_type, strlen(net_type)))
408 *found = false;
409
410 buf_start = end_of_word + 1;
411 while ((buf_start < buf_end) && isblank(*buf_start))
412 buf_start++;
413
414 /* Check whether line is contains the right link. */
415 end_of_word = get_eow(buf_start, buf_end);
416 /* corrupt db */
417 if (!end_of_word)
418 return NULL;
419
420 if (strncmp(buf_start, net_link, strlen(net_link)))
421 *found = false;
422
423 buf_start = end_of_word + 1;
424 while ((buf_start < buf_end) && isblank(*buf_start))
425 buf_start++;
426
427 /* Check whether line contains the right network device. */
428 end_of_word = get_eow(buf_start, buf_end);
429 /* corrupt db */
430 if (!end_of_word)
431 return NULL;
432
433 len = end_of_word - buf_start;
434 /* corrupt db */
435 if (len >= IFNAMSIZ)
436 return NULL;
2b333aee 437
6d8c2779
CB
438 memcpy(netdev_name, buf_start, len);
439 netdev_name[len] = '\0';
440 *keep = lxc_nic_exists(netdev_name);
2b333aee 441
6d8c2779
CB
442 if (net_dev && !strcmp(netdev_name, net_dev))
443 *found = true;
2b333aee 444
6d8c2779 445 return line;
2b333aee 446
af256970 447 next:
6d8c2779 448 buf_start = end_of_line + 1;
20ab58c7
SH
449 }
450
451 return NULL;
452}
453
cb5659e1 454static int instantiate_veth(char *veth1, char *veth2)
20ab58c7 455{
cb5659e1 456 int ret;
20ab58c7 457
cb5659e1
CB
458 ret = lxc_veth_create(veth1, veth2);
459 if (ret < 0) {
460 usernic_error("Failed to create %s-%s : %s.\n", veth1, veth2,
461 strerror(-ret));
78f97d4c 462 return -1;
20ab58c7
SH
463 }
464
2b333aee
CB
465 /* Changing the high byte of the mac address to 0xfe, the bridge
466 * interface will always keep the host's mac address and not take the
467 * mac address of a container. */
cb5659e1
CB
468 ret = setup_private_host_hw_addr(veth1);
469 if (ret < 0)
2b333aee 470 usernic_error("Failed to change mac address of host interface "
cb5659e1 471 "%s : %s\n", veth1, strerror(-ret));
20ab58c7 472
cb5659e1 473 return netdev_set_flag(veth1, IFF_UP);
20ab58c7
SH
474}
475
0130df54
SH
476static int get_mtu(char *name)
477{
2b333aee
CB
478 int idx;
479
480 idx = if_nametoindex(name);
8424b4e1
CB
481 if (idx < 0)
482 return -1;
0130df54
SH
483 return netdev_get_mtu(idx);
484}
485
cb5659e1 486static int create_nic(char *nic, char *br, int pid, char **cnic)
20ab58c7 487{
cb5659e1 488 char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
2b333aee
CB
489 int mtu, ret;
490
20ab58c7
SH
491 ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
492 if (ret < 0 || ret >= IFNAMSIZ) {
e771c51d 493 usernic_error("%s", "Could not create nic name\n");
cb5659e1 494 return -1;
20ab58c7
SH
495 }
496
cb5659e1
CB
497 ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf);
498 if (ret < 0 || ret >= IFNAMSIZ) {
499 usernic_error("%s\n", "Could not create nic name");
500 return -1;
501 }
20ab58c7 502 /* create the nics */
cb5659e1
CB
503 ret = instantiate_veth(veth1buf, veth2buf);
504 if (ret < 0) {
e771c51d 505 usernic_error("%s", "Error creating veth tunnel\n");
cb5659e1 506 return -1;
20ab58c7
SH
507 }
508
2b333aee 509 if (strcmp(br, "none")) {
cff7b5eb
FN
510 /* copy the bridge's mtu to both ends */
511 mtu = get_mtu(br);
2b333aee
CB
512 if (mtu > 0) {
513 ret = lxc_netdev_set_mtu(veth1buf, mtu);
514 if (ret < 0) {
af256970
CB
515 usernic_error("Failed to set mtu to %d on %s\n",
516 mtu, veth1buf);
2b333aee
CB
517 goto out_del;
518 }
519
520 ret = lxc_netdev_set_mtu(veth2buf, mtu);
521 if (ret < 0) {
af256970
CB
522 usernic_error("Failed to set mtu to %d on %s\n",
523 mtu, veth2buf);
cff7b5eb
FN
524 goto out_del;
525 }
0130df54 526 }
0130df54 527
cff7b5eb 528 /* attach veth1 to bridge */
581c75e7 529 ret = lxc_bridge_attach(br, veth1buf);
2b333aee 530 if (ret < 0) {
e771c51d 531 usernic_error("Error attaching %s to %s\n", veth1buf, br);
cff7b5eb
FN
532 goto out_del;
533 }
20ab58c7
SH
534 }
535
536 /* pass veth2 to target netns */
8d357196 537 ret = lxc_netdev_move_by_name(veth2buf, pid, NULL);
20ab58c7 538 if (ret < 0) {
af256970
CB
539 usernic_error("Error moving %s to network namespace of %d\n",
540 veth2buf, pid);
20ab58c7
SH
541 goto out_del;
542 }
2b333aee 543
4119204e 544 *cnic = strdup(veth2buf);
2b333aee 545 if (!*cnic) {
e771c51d 546 usernic_error("Failed to copy string \"%s\"\n", veth2buf);
cb5659e1 547 return -1;
2b333aee
CB
548 }
549
cb5659e1 550 return 0;
20ab58c7
SH
551
552out_del:
553 lxc_netdev_delete_by_name(veth1buf);
cb5659e1 554 return -1;
20ab58c7
SH
555}
556
03c2eb15
SH
557struct entry_line {
558 char *start;
559 int len;
560 bool keep;
561};
562
6d8c2779
CB
563static bool cull_entries(int fd, char *name, char *net_type, char *net_link,
564 char *net_dev, bool *found_nicname)
20ab58c7 565{
8b8e00a2 566 int i, ret;
6d8c2779 567 char *buf, *buf_end, *buf_start;
2b333aee 568 struct stat sb;
8b8e00a2 569 int n = 0;
6d8c2779 570 bool found, keep;
03c2eb15 571 struct entry_line *entry_lines = NULL;
20ab58c7 572
8b8e00a2
CB
573 ret = fstat(fd, &sb);
574 if (ret < 0) {
e771c51d 575 usernic_error("Failed to fstat: %s\n", strerror(errno));
1e985428
SG
576 return false;
577 }
2b333aee 578
6d8c2779
CB
579 if (!sb.st_size)
580 return false;
2b333aee 581
8b8e00a2 582 buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
20ab58c7 583 if (buf == MAP_FAILED) {
af256970
CB
584 usernic_error("Failed to establish shared memory mapping: %s\n",
585 strerror(errno));
20ab58c7
SH
586 return false;
587 }
588
6d8c2779
CB
589 buf_start = buf;
590 buf_end = buf + sb.st_size;
591 while ((buf_start = find_line(buf_start, buf_end, name, net_type,
592 net_link, net_dev, &(bool){true}, &found,
593 &keep))) {
2b333aee
CB
594 struct entry_line *newe;
595
596 newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1));
03c2eb15
SH
597 if (!newe) {
598 free(entry_lines);
6d8c2779 599 lxc_strmunmap(buf, sb.st_size);
03c2eb15
SH
600 return false;
601 }
2b333aee 602
6d8c2779
CB
603 if (found)
604 *found_nicname = true;
605
03c2eb15 606 entry_lines = newe;
6d8c2779
CB
607 entry_lines[n].start = buf_start;
608 entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start;
609 entry_lines[n].keep = keep;
03c2eb15 610 n++;
2b333aee 611
6d8c2779
CB
612 buf_start += entry_lines[n - 1].len + 1;
613 if (buf_start >= buf_end)
20ab58c7 614 break;
2b333aee
CB
615 }
616
6d8c2779 617 buf_start = buf;
2b333aee 618 for (i = 0; i < n; i++) {
03c2eb15
SH
619 if (!entry_lines[i].keep)
620 continue;
2b333aee 621
6d8c2779
CB
622 memcpy(buf_start, entry_lines[i].start, entry_lines[i].len);
623 buf_start += entry_lines[i].len;
624 *buf_start = '\n';
625 buf_start++;
03c2eb15
SH
626 }
627 free(entry_lines);
2b333aee 628
6d8c2779 629 ret = ftruncate(fd, buf_start - buf);
3ca10814 630 lxc_strmunmap(buf, sb.st_size);
8b8e00a2 631 if (ret < 0)
af256970
CB
632 usernic_error("Failed to set new file size: %s\n",
633 strerror(errno));
2b333aee 634
20ab58c7
SH
635 return true;
636}
637
6d8c2779 638static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link)
20ab58c7 639{
20ab58c7 640 int count = 0;
6d8c2779 641 bool owner = false;;
cef588fc 642 char *buf_end;
6d8c2779
CB
643
644 buf_end = &buf[len];
645 while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL,
646 &owner, &(bool){true}, &(bool){true}))) {
647 if (owner)
648 count++;
649 buf = get_eol(buf, buf_end) + 1;
650 if (buf >= buf_end)
20ab58c7
SH
651 break;
652 }
653
654 return count;
655}
656
af256970 657/* The dbfile has lines of the format: user type bridge nicname. */
8285dcfb
CB
658static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
659 char *intype, char *br, int allowed, char **cnic)
20ab58c7 660{
2b333aee 661 int ret;
a92028b2 662 size_t slen;
cb5659e1
CB
663 char *newline, *owner;
664 char nicname[IFNAMSIZ];
20ab58c7 665 struct stat sb;
3043883c 666 struct alloted_s *n;
2b333aee
CB
667 int count = 0;
668 char *buf = NULL;
20ab58c7 669
2b333aee 670 for (n = names; n != NULL; n = n->next)
8b8e00a2 671 cull_entries(fd, n->name, intype, br, NULL, NULL);
20ab58c7 672
c073c147 673 if (allowed == 0)
8285dcfb 674 return NULL;
c073c147 675
3043883c
HK
676 owner = names->name;
677
a92028b2
CB
678 ret = fstat(fd, &sb);
679 if (ret < 0) {
e771c51d 680 usernic_error("Failed to fstat: %s\n", strerror(errno));
8285dcfb 681 return NULL;
1e985428 682 }
2b333aee 683
a92028b2
CB
684 if (sb.st_size > 0) {
685 buf = lxc_strmmap(NULL, sb.st_size, PROT_READ | PROT_WRITE,
686 MAP_SHARED, fd, 0);
20ab58c7 687 if (buf == MAP_FAILED) {
a92028b2
CB
688 usernic_error("Failed to establish shared memory "
689 "mapping: %s\n", strerror(errno));
8285dcfb 690 return NULL;
20ab58c7
SH
691 }
692
3043883c 693 owner = NULL;
2b333aee 694 for (n = names; n != NULL; n = n->next) {
a92028b2 695 count = count_entries(buf, sb.st_size, n->name, intype, br);
3043883c
HK
696 if (count >= n->allowed)
697 continue;
698
699 owner = n->name;
700 break;
701 }
a92028b2
CB
702
703 lxc_strmunmap(buf, sb.st_size);
20ab58c7
SH
704 }
705
3043883c 706 if (owner == NULL)
8285dcfb 707 return NULL;
3043883c 708
cb5659e1
CB
709 ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX");
710 if (ret < 0 || (size_t)ret >= sizeof(nicname))
711 return NULL;
712
713 if (!lxc_mkifname(nicname))
714 return NULL;
715
716 ret = create_nic(nicname, br, pid, cnic);
717 if (ret < 0) {
718 usernic_error("%s", "Failed to create new nic\n");
8285dcfb
CB
719 return NULL;
720 }
2b333aee 721
a92028b2
CB
722 /* strlen(owner)
723 * +
724 * " "
725 * +
726 * strlen(intype)
727 * +
728 * " "
729 * +
730 * strlen(br)
731 * +
732 * " "
733 * +
734 * strlen(nicname)
735 * +
736 * \n
737 * +
738 * \0
739 */
740 slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4;
741 newline = malloc(slen + 1);
2b333aee 742 if (!newline) {
a92028b2 743 free(newline);
e771c51d 744 usernic_error("Failed allocate memory: %s\n", strerror(errno));
8285dcfb 745 return NULL;
2b333aee
CB
746 }
747
a92028b2
CB
748 ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname);
749 if (ret < 0 || (size_t)ret >= (slen + 1)) {
8285dcfb
CB
750 if (lxc_netdev_delete_by_name(nicname) != 0)
751 usernic_error("Error unlinking %s\n", nicname);
a92028b2 752 free(newline);
8285dcfb 753 return NULL;
20ab58c7 754 }
2b333aee 755
a92028b2
CB
756 /* Note that the file needs to be truncated to the size **without** the
757 * \0 byte! Files are not \0-terminated!
758 */
759 ret = ftruncate(fd, sb.st_size + slen);
760 if (ret < 0)
761 usernic_error("Failed to truncate file: %s\n", strerror(errno));
2b333aee 762
a92028b2
CB
763 buf = lxc_strmmap(NULL, sb.st_size + slen, PROT_READ | PROT_WRITE,
764 MAP_SHARED, fd, 0);
20ab58c7 765 if (buf == MAP_FAILED) {
af256970
CB
766 usernic_error("Failed to establish shared memory mapping: %s\n",
767 strerror(errno));
8285dcfb
CB
768 if (lxc_netdev_delete_by_name(nicname) != 0)
769 usernic_error("Error unlinking %s\n", nicname);
a92028b2 770 free(newline);
8285dcfb 771 return NULL;
20ab58c7 772 }
2b333aee 773
a92028b2
CB
774 /* Note that the memory needs to be moved in the buffer **without** the
775 * \0 byte! Files are not \0-terminated!
776 */
777 memmove(buf + sb.st_size, newline, slen);
778 free(newline);
779 lxc_strmunmap(buf, sb.st_size + slen);
2b333aee 780
cb5659e1 781 return strdup(nicname);
20ab58c7
SH
782}
783
552a7d05 784static bool create_db_dir(char *fnam)
070a4b8e 785{
af256970 786 int ret;
2b333aee 787 char *p;
070a4b8e 788
2b333aee 789 p = alloca(strlen(fnam) + 1);
070a4b8e
SH
790 strcpy(p, fnam);
791 fnam = p;
792 p = p + 1;
2b333aee 793
070a4b8e 794again:
2b333aee
CB
795 while (*p && *p != '/')
796 p++;
070a4b8e
SH
797 if (!*p)
798 return true;
2b333aee 799
070a4b8e 800 *p = '\0';
af256970
CB
801
802 ret = mkdir(fnam, 0755);
803 if (ret < 0 && errno != EEXIST) {
804 usernic_error("Failed to create %s: %s\n", fnam,
805 strerror(errno));
070a4b8e
SH
806 *p = '/';
807 return false;
808 }
809 *(p++) = '/';
2b333aee 810
070a4b8e
SH
811 goto again;
812}
813
0cffb676 814static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
74c6e2b0 815 int *container_veth_ifidx)
4119204e 816{
c92dfebd 817 int ret;
0059379f 818 pid_t pid_self;
16af2380 819 uid_t ruid, suid, euid;
c92dfebd
CB
820 char ifname[IFNAMSIZ];
821 char *string_ret = NULL, *name = NULL;
822 int fd = -1, ifindex = -1, ofd = -1;
4119204e 823
0059379f
CB
824 pid_self = lxc_raw_getpid();
825 ofd = lxc_preserve_ns(pid_self, "net");
5d04811e 826 if (ofd < 0) {
0059379f 827 usernic_error("Failed opening network namespace path for %d", pid_self);
c92dfebd 828 return NULL;
4119204e 829 }
4119204e 830
5d04811e
CB
831 fd = lxc_preserve_ns(pid, "net");
832 if (fd < 0) {
e771c51d 833 usernic_error("Failed opening network namespace path for %d", pid);
16af2380 834 goto do_partial_cleanup;
4119204e 835 }
5d04811e 836
16af2380
CB
837 ret = getresuid(&ruid, &euid, &suid);
838 if (ret < 0) {
839 usernic_error("Failed to retrieve real, effective, and saved "
840 "user IDs: %s\n",
841 strerror(errno));
842 goto do_partial_cleanup;
4119204e 843 }
16af2380
CB
844
845 ret = setns(fd, CLONE_NEWNET);
846 close(fd);
847 fd = -1;
848 if (ret < 0) {
849 usernic_error("Failed to setns() to the network namespace of "
e771c51d 850 "the container with PID %d: %s\n",
16af2380
CB
851 pid, strerror(errno));
852 goto do_partial_cleanup;
853 }
854
855 ret = setresuid(ruid, ruid, 0);
856 if (ret < 0) {
857 usernic_error("Failed to drop privilege by setting effective "
858 "user id and real user id to %d, and saved user "
e771c51d 859 "ID to 0: %s\n",
16af2380 860 ruid, strerror(errno));
1a0e70ac
CB
861 /* It's ok to jump to do_full_cleanup here since setresuid()
862 * will succeed when trying to set real, effective, and saved to
863 * values they currently have.
864 */
16af2380
CB
865 goto do_full_cleanup;
866 }
867
c92dfebd
CB
868 /* Check if old interface exists. */
869 ifindex = if_nametoindex(oldname);
870 if (!ifindex) {
871 usernic_error("Failed to get netdev index: %s\n", strerror(errno));
872 goto do_full_cleanup;
8aecd66b 873 }
16af2380 874
c92dfebd
CB
875 /* When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
876 * netlink will replace the format specifier with an appropriate index.
877 * So we pass "eth%d".
878 */
879 if (newname)
880 name = newname;
881 else
882 name = "eth%d";
883
884 ret = lxc_netdev_rename_by_name(oldname, name);
a055595c 885 name = NULL;
16af2380 886 if (ret < 0) {
c92dfebd 887 usernic_error("Error %d renaming netdev %s to %s in container\n",
a055595c 888 ret, oldname, newname ? newname : "eth%d");
16af2380 889 goto do_full_cleanup;
4119204e 890 }
16af2380 891
c92dfebd
CB
892 /* Retrieve new name for interface. */
893 if (!if_indextoname(ifindex, ifname)) {
894 usernic_error("Failed to get new netdev name: %s\n", strerror(errno));
895 goto do_full_cleanup;
8aecd66b 896 }
16af2380 897
c92dfebd
CB
898 /* Allocation failure for strdup() is checked below. */
899 name = strdup(ifname);
900 string_ret = name;
74c6e2b0 901 *container_veth_ifidx = ifindex;
16af2380
CB
902
903do_full_cleanup:
904 ret = setresuid(ruid, euid, suid);
905 if (ret < 0) {
c92dfebd
CB
906 usernic_error("Failed to restore privilege by setting "
907 "effective user id to %d, real user id to %d, "
908 "and saved user ID to %d: %s\n", ruid, euid, suid,
909 strerror(errno));
910
911 string_ret = NULL;
4119204e 912 }
4119204e 913
16af2380
CB
914 ret = setns(ofd, CLONE_NEWNET);
915 if (ret < 0) {
916 usernic_error("Failed to setns() to original network namespace "
c92dfebd
CB
917 "of PID %d: %s\n", ofd, strerror(errno));
918
919 string_ret = NULL;
16af2380 920 }
4119204e 921
16af2380 922do_partial_cleanup:
4119204e
SH
923 if (fd >= 0)
924 close(fd);
c92dfebd
CB
925
926 if (!string_ret && name)
927 free(name);
928
16af2380
CB
929 close(ofd);
930
c92dfebd 931 return string_ret;
4119204e
SH
932}
933
1a0e70ac
CB
934/* If the caller (real uid, not effective uid) may read the /proc/[pid]/ns/net,
935 * then it is either the caller's netns or one which it created.
8d9f636d
SH
936 */
937static bool may_access_netns(int pid)
938{
939 int ret;
940 char s[200];
941 uid_t ruid, suid, euid;
942 bool may_access = false;
943
944 ret = getresuid(&ruid, &euid, &suid);
2b333aee
CB
945 if (ret < 0) {
946 usernic_error("Failed to retrieve real, effective, and saved "
947 "user IDs: %s\n",
948 strerror(errno));
8d9f636d
SH
949 return false;
950 }
2b333aee 951
8d9f636d 952 ret = setresuid(ruid, ruid, euid);
2b333aee
CB
953 if (ret < 0) {
954 usernic_error("Failed to drop privilege by setting effective "
955 "user id and real user id to %d, and saved user "
e771c51d 956 "ID to %d: %s\n",
2b333aee 957 ruid, euid, strerror(errno));
8d9f636d
SH
958 return false;
959 }
2b333aee 960
8d9f636d 961 ret = snprintf(s, 200, "/proc/%d/ns/net", pid);
2b333aee 962 if (ret < 0 || ret >= 200)
8d9f636d 963 return false;
2b333aee 964
8d9f636d 965 ret = access(s, R_OK);
2b333aee
CB
966 may_access = true;
967 if (ret < 0) {
968 may_access = false;
969 usernic_error("Uid %d may not access %s: %s\n", (int)ruid, s, strerror(errno));
8d9f636d 970 }
2b333aee 971
8d9f636d 972 ret = setresuid(ruid, euid, suid);
2b333aee
CB
973 if (ret < 0) {
974 usernic_error("Failed to restore user id to %d, real user id "
e771c51d 975 "to %d, and saved user ID to %d: %s\n",
2b333aee 976 ruid, euid, suid, strerror(errno));
8d9f636d
SH
977 may_access = false;
978 }
2b333aee 979
8d9f636d
SH
980 return may_access;
981}
982
900e5f94
CB
983struct user_nic_args {
984 char *cmd;
985 char *lxc_path;
986 char *lxc_name;
987 char *pid;
988 char *type;
989 char *link;
990 char *veth_name;
991};
992
8b8e00a2
CB
993#define LXC_USERNIC_CREATE 0
994#define LXC_USERNIC_DELETE 1
995
1bd8d726
CB
996static bool is_privileged_over_netns(int netns_fd)
997{
998 int ret;
0059379f 999 pid_t pid_self;
1bd8d726
CB
1000 uid_t euid, ruid, suid;
1001 bool bret = false;
1002 int ofd = -1;
1003
0059379f
CB
1004 pid_self = lxc_raw_getpid();
1005 ofd = lxc_preserve_ns(pid_self, "net");
1bd8d726 1006 if (ofd < 0) {
0059379f 1007 usernic_error("Failed opening network namespace path for %d", pid_self);
1bd8d726
CB
1008 return false;
1009 }
1010
1011 ret = getresuid(&ruid, &euid, &suid);
1012 if (ret < 0) {
1013 usernic_error("Failed to retrieve real, effective, and saved "
1014 "user IDs: %s\n",
1015 strerror(errno));
1016 goto do_partial_cleanup;
1017 }
1018
1019 ret = setns(netns_fd, CLONE_NEWNET);
1020 if (ret < 0) {
1021 usernic_error("Failed to setns() to network namespace %s\n",
1022 strerror(errno));
1023 goto do_partial_cleanup;
1024 }
1025
1026 ret = setresuid(ruid, ruid, 0);
1027 if (ret < 0) {
1028 usernic_error("Failed to drop privilege by setting effective "
1029 "user id and real user id to %d, and saved user "
1030 "ID to 0: %s\n",
1031 ruid, strerror(errno));
1032 /* It's ok to jump to do_full_cleanup here since setresuid()
1033 * will succeed when trying to set real, effective, and saved to
1034 * values they currently have.
1035 */
1036 goto do_full_cleanup;
1037 }
1038
1039 /* Test whether we are privileged over the network namespace. To do this
1040 * we try to delete the loopback interface which is not possible. If we
1041 * are privileged over the network namespace we will get ENOTSUP. If we
1042 * are not privileged over the network namespace we will get EPERM.
1043 */
1044 ret = lxc_netdev_delete_by_name("lo");
1045 if (ret == -ENOTSUP)
1046 bret = true;
1047
1048do_full_cleanup:
1049 ret = setresuid(ruid, euid, suid);
1050 if (ret < 0) {
1051 usernic_error("Failed to restore privilege by setting "
1052 "effective user id to %d, real user id to %d, "
1053 "and saved user ID to %d: %s\n", ruid, euid, suid,
1054 strerror(errno));
1055
1056 bret = false;
1057 }
1058
1059 ret = setns(ofd, CLONE_NEWNET);
1060 if (ret < 0) {
1061 usernic_error("Failed to setns() to original network namespace "
1062 "of PID %d: %s\n", ofd, strerror(errno));
1063
1064 bret = false;
1065 }
1066
1067do_partial_cleanup:
1068
1069 close(ofd);
1070 return bret;
1071}
1072
20ab58c7
SH
1073int main(int argc, char *argv[])
1074{
99573f4a 1075 int fd, n, pid, request, ret;
c92dfebd 1076 char *me, *newname;
1bd8d726 1077 struct user_nic_args args;
99573f4a 1078 int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1;
25aead3f 1079 char *cnic = NULL, *nicname = NULL;
3043883c 1080 struct alloted_s *alloted = NULL;
900e5f94
CB
1081
1082 if (argc < 7 || argc > 8) {
1083 usage(argv[0], true);
1084 exit(EXIT_FAILURE);
1085 }
1086
1087 memset(&args, 0, sizeof(struct user_nic_args));
1088 args.cmd = argv[1];
1089 args.lxc_path = argv[2];
1090 args.lxc_name = argv[3];
1091 args.pid = argv[4];
1092 args.type = argv[5];
1093 args.link = argv[6];
1094 if (argc >= 8)
1095 args.veth_name = argv[7];
20ab58c7 1096
8b8e00a2
CB
1097 if (!strcmp(args.cmd, "create")) {
1098 request = LXC_USERNIC_CREATE;
1099 } else if (!strcmp(args.cmd, "delete")) {
1100 request = LXC_USERNIC_DELETE;
1101 } else {
1102 usage(argv[0], true);
1103 exit(EXIT_FAILURE);
1104 }
1105
d04e77c3
CB
1106 /* Set a sane env, because we are setuid-root. */
1107 ret = clearenv();
1108 if (ret) {
e771c51d 1109 usernic_error("%s", "Failed to clear environment\n");
2b333aee 1110 exit(EXIT_FAILURE);
361b4fe7 1111 }
d04e77c3
CB
1112
1113 ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
1114 if (ret < 0) {
e771c51d 1115 usernic_error("%s", "Failed to set PATH, exiting\n");
2b333aee 1116 exit(EXIT_FAILURE);
6ad22d06 1117 }
d04e77c3
CB
1118
1119 me = get_username();
1120 if (!me) {
e771c51d 1121 usernic_error("%s", "Failed to get username\n");
2b333aee 1122 exit(EXIT_FAILURE);
20ab58c7
SH
1123 }
1124
1bd8d726
CB
1125 if (request == LXC_USERNIC_CREATE) {
1126 ret = lxc_safe_int(args.pid, &pid);
1127 if (ret < 0) {
1128 usernic_error("Could not read pid: %s\n", args.pid);
1129 exit(EXIT_FAILURE);
1130 }
1131 } else if (request == LXC_USERNIC_DELETE) {
1132 netns_fd = open(args.pid, O_RDONLY);
1133 if (netns_fd < 0) {
1134 usernic_error("Could not open \"%s\": %s\n", args.pid,
1135 strerror(errno));
1136 exit(EXIT_FAILURE);
1137 }
8d9f636d
SH
1138 }
1139
8befa924 1140 if (!create_db_dir(LXC_USERNIC_DB)) {
e771c51d 1141 usernic_error("%s", "Failed to create directory for db file\n");
1bd8d726
CB
1142 if (netns_fd >= 0)
1143 close(netns_fd);
2b333aee 1144 exit(EXIT_FAILURE);
070a4b8e
SH
1145 }
1146
d04e77c3
CB
1147 fd = open_and_lock(LXC_USERNIC_DB);
1148 if (fd < 0) {
e771c51d 1149 usernic_error("Failed to lock %s\n", LXC_USERNIC_DB);
1bd8d726
CB
1150 if (netns_fd >= 0)
1151 close(netns_fd);
2b333aee 1152 exit(EXIT_FAILURE);
20ab58c7
SH
1153 }
1154
1bd8d726
CB
1155 if (request == LXC_USERNIC_CREATE) {
1156 if (!may_access_netns(pid)) {
1157 usernic_error("User %s may not modify netns for pid %d\n", me, pid);
1158 exit(EXIT_FAILURE);
1159 }
1160 } else if (request == LXC_USERNIC_DELETE) {
1161 bool has_priv;
1162 has_priv = is_privileged_over_netns(netns_fd);
1163 close(netns_fd);
1164 if (!has_priv) {
1165 usernic_error("%s", "Process is not privileged over "
1166 "network namespace\n");
1167 exit(EXIT_FAILURE);
1168 }
8d9f636d
SH
1169 }
1170
8b8e00a2 1171 n = get_alloted(me, args.type, args.link, &alloted);
a055595c 1172
8b8e00a2
CB
1173 if (request == LXC_USERNIC_DELETE) {
1174 int ret;
1175 struct alloted_s *it;
1176 bool found_nicname = false;
1177
1178 if (!is_ovs_bridge(args.link)) {
a055595c 1179 usernic_error("%s", "Deletion of non ovs type network "
8b8e00a2
CB
1180 "devices not implemented\n");
1181 close(fd);
1182 free_alloted(&alloted);
1183 exit(EXIT_FAILURE);
1184 }
1185
1186 /* Check whether the network device we are supposed to delete
1187 * exists in the db. If it doesn't we will not delete it as we
1188 * need to assume the network device is not under our control.
1189 * As a side effect we also clear any invalid entries from the
1190 * database.
1191 */
1192 for (it = alloted; it; it = it->next)
1193 cull_entries(fd, it->name, args.type, args.link,
1194 args.veth_name, &found_nicname);
1195 close(fd);
1196 free_alloted(&alloted);
1197
1198 if (!found_nicname) {
32311345
CB
1199 usernic_error("Caller is not allowed to delete network "
1200 "device \"%s\"\n", args.veth_name);
a055595c
CB
1201 exit(EXIT_FAILURE);
1202 }
1203
1204 ret = lxc_ovs_delete_port(args.link, args.veth_name);
1205 if (ret < 0) {
1206 usernic_error("Failed to remove port \"%s\" from "
1207 "openvswitch bridge \"%s\"",
1208 args.veth_name, args.link);
1209 exit(EXIT_FAILURE);
1210 }
8b8e00a2 1211
a055595c
CB
1212 exit(EXIT_SUCCESS);
1213 }
20ab58c7 1214 if (n > 0)
900e5f94
CB
1215 nicname = get_nic_if_avail(fd, alloted, pid, args.type,
1216 args.link, n, &cnic);
3043883c 1217
20ab58c7 1218 close(fd);
3043883c 1219 free_alloted(&alloted);
8285dcfb 1220 if (!nicname) {
e771c51d 1221 usernic_error("%s", "Quota reached\n");
2b333aee 1222 exit(EXIT_FAILURE);
20ab58c7 1223 }
20ab58c7 1224
2b333aee 1225 /* Now rename the link. */
74c6e2b0
CB
1226 newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name,
1227 &container_veth_ifidx);
c92dfebd 1228 if (!newname) {
e771c51d 1229 usernic_error("%s", "Failed to rename the link\n");
d04e77c3
CB
1230 ret = lxc_netdev_delete_by_name(cnic);
1231 if (ret < 0)
1232 usernic_error("Failed to delete \"%s\"\n", cnic);
8285dcfb 1233 free(nicname);
2b333aee 1234 exit(EXIT_FAILURE);
4119204e 1235 }
cb5659e1 1236
74c6e2b0 1237 host_veth_ifidx = if_nametoindex(nicname);
8424b4e1
CB
1238 if (!host_veth_ifidx) {
1239 free(newname);
1240 free(nicname);
1241 usernic_error("Failed to get netdev index: %s\n", strerror(errno));
1242 exit(EXIT_FAILURE);
1243 }
20ab58c7 1244
74c6e2b0
CB
1245 /* Write names of veth pairs and their ifindeces to stout:
1246 * (e.g. eth0:731:veth9MT2L4:730)
1247 */
1248 fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname,
1249 host_veth_ifidx);
c92dfebd 1250 free(newname);
8285dcfb 1251 free(nicname);
2b333aee 1252 exit(EXIT_SUCCESS);
20ab58c7 1253}