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