]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxc_user_nic.c
3 * Copyright © 2013 Serge Hallyn <serge.hallyn@ubuntu.com>.
4 * Copyright © 2013 Canonical Ltd.
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.
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.
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.
20 #define _GNU_SOURCE /* See feature_test_macros(7) */
24 #include <sys/types.h>
34 #include <sys/socket.h>
38 #include <sys/ioctl.h>
39 #include <linux/netlink.h>
40 #include <arpa/inet.h>
42 #include <net/if_arp.h>
43 #include <netinet/in.h>
44 #include <linux/if_bridge.h>
45 #include <linux/netlink.h>
46 #include <linux/rtnetlink.h>
47 #include <linux/sockios.h>
48 #include <sys/param.h>
54 static void usage(char *me
, bool fail
)
56 fprintf(stderr
, "Usage: %s pid type bridge nicname\n", me
);
57 fprintf(stderr
, " nicname is the name to use inside the container\n");
61 static int open_and_lock(char *path
)
66 fd
= open(path
, O_RDWR
|O_CREAT
, S_IWUSR
| S_IRUSR
);
68 fprintf(stderr
, "Failed to open %s: %s\n",
69 path
, strerror(errno
));
74 lk
.l_whence
= SEEK_SET
;
77 if (fcntl(fd
, F_SETLKW
, &lk
) < 0) {
78 fprintf(stderr
, "Failed to lock %s: %s\n",
79 path
, strerror(errno
));
88 static char *get_username(void)
90 struct passwd
*pwd
= getpwuid(getuid());
100 static char **get_groupnames(void)
108 ngroups
= getgroups(0, NULL
);
111 fprintf(stderr
, "Failed to get number of groups user belongs to\n");
115 group_ids
= (gid_t
*)malloc(sizeof(gid_t
)*ngroups
);
116 ret
= getgroups(ngroups
, group_ids
);
120 fprintf(stderr
, "Failed to get process groups\n");
124 groupnames
= (char **)malloc(sizeof(char *)*(ngroups
+1));
126 for (i
=0; i
<ngroups
; i
++ ) {
127 gr
= getgrgid(group_ids
[i
]);
130 fprintf(stderr
, "Failed to get group name\n");
132 for (j
=0; j
<i
; j
++) {
139 groupnames
[i
] = strdup(gr
->gr_name
);
141 if (groupnames
[i
] == NULL
) {
142 fprintf(stderr
, "Failed to copy group name: %s", gr
->gr_name
);
144 for (j
=0; j
<i
; j
++) {
152 groupnames
[ngroups
] = NULL
;
159 static void free_groupnames(char **groupnames
)
162 for (group
=groupnames
; group
!= NULL
; group
++)
167 static bool name_is_in_groupnames(char *name
, char **groupnames
)
169 while (groupnames
!= NULL
) {
170 if (strcmp(name
, *groupnames
) == 0)
180 struct alloted_s
*next
;
183 static struct alloted_s
*append_alloted(struct alloted_s
**head
, char *name
, int n
)
185 struct alloted_s
*cur
, *al
;
187 if (head
== NULL
|| name
== NULL
) {
188 // sanity check. parameters should not be null
192 al
= (struct alloted_s
*)malloc(sizeof(struct alloted_s
));
195 // unable to allocate memory to new struct
199 al
->name
= strdup(name
);
209 while (cur
->next
!= NULL
)
216 static void free_alloted(struct alloted_s
**head
)
218 struct alloted_s
*cur
;
226 while (cur
!= NULL
) {
234 /* The configuration file consists of lines of the form:
236 * user type bridge count
238 * @group type bridge count
240 * Return the count entry for the calling user if there is one. Else
243 static int get_alloted(char *me
, char *intype
, char *link
, struct alloted_s
**alloted
)
245 FILE *fin
= fopen(LXC_USERNIC_CONF
, "r");
247 char name
[100], type
[100], br
[100];
249 int n
, ret
, count
= 0;
253 fprintf(stderr
, "Failed to open %s: %s\n", LXC_USERNIC_CONF
,
258 groups
= get_groupnames();
259 while ((getline(&line
, &len
, fin
)) != -1) {
260 ret
= sscanf(line
, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name
, type
, br
, &n
);
265 if (strlen(name
) == 0)
268 if (strcmp(name
, me
) != 0)
272 if (!name_is_in_groupnames(name
+1, groups
))
275 if (strcmp(type
, intype
) != 0)
277 if (strcmp(link
, br
) != 0)
280 /* found the user or group with the appropriate settings, therefore finish the search.
281 * what to do if there are more than one applicable lines? not specified in the docs.
282 * since getline is implemented with realloc, we don't need to free line until exiting func.
284 append_alloted(alloted
, name
, n
);
289 free_groupnames(groups
);
295 static char *get_eol(char *s
, char *e
)
297 while (s
<e
&& *s
&& *s
!= '\n')
302 static char *get_eow(char *s
, char *e
)
304 while (s
<e
&& *s
&& !isblank(*s
) && *s
!= '\n')
309 static char *find_line(char *p
, char *e
, char *u
, char *t
, char *l
)
313 while (p
<e
&& (p1
= get_eol(p
, e
)) < e
) {
317 while (p
<e
&& isblank(*p
)) p
++;
319 if (!p2
|| p2
-p
!= strlen(u
) || strncmp(p
, u
, strlen(u
)) != 0)
322 while (p
<e
&& isblank(*p
)) p
++;
324 if (!p2
|| p2
-p
!= strlen(t
) || strncmp(p
, t
, strlen(t
)) != 0)
327 while (p
<e
&& isblank(*p
)) p
++;
329 if (!p2
|| p2
-p
!= strlen(l
) || strncmp(p
, l
, strlen(l
)) != 0)
339 static bool nic_exists(char *nic
)
341 char path
[MAXPATHLEN
];
345 if (strcmp(nic
, "none") == 0)
347 ret
= snprintf(path
, MAXPATHLEN
, "/sys/class/net/%s", nic
);
348 if (ret
< 0 || ret
>= MAXPATHLEN
) // should never happen!
350 ret
= stat(path
, &sb
);
356 static int instantiate_veth(char *n1
, char **n2
)
360 err
= snprintf(*n2
, IFNAMSIZ
, "%sp", n1
);
361 if (err
< 0 || err
>= IFNAMSIZ
) {
362 fprintf(stderr
, "nic name too long\n");
366 err
= lxc_veth_create(n1
, *n2
);
368 fprintf(stderr
, "failed to create %s-%s : %s\n", n1
, *n2
,
373 /* changing the high byte of the mac address to 0xfe, the bridge interface
374 * will always keep the host's mac address and not take the mac address
376 err
= setup_private_host_hw_addr(n1
);
378 fprintf(stderr
, "failed to change mac address of host interface '%s' : %s\n",
382 return netdev_set_flag(n1
, IFF_UP
);
385 static int get_mtu(char *name
)
387 int idx
= if_nametoindex(name
);
388 return netdev_get_mtu(idx
);
391 static bool create_nic(char *nic
, char *br
, int pid
, char **cnic
)
393 char *veth1buf
, *veth2buf
;
394 veth1buf
= alloca(IFNAMSIZ
);
395 veth2buf
= alloca(IFNAMSIZ
);
398 ret
= snprintf(veth1buf
, IFNAMSIZ
, "%s", nic
);
399 if (ret
< 0 || ret
>= IFNAMSIZ
) {
400 fprintf(stderr
, "host nic name too long\n");
404 /* create the nics */
405 if (instantiate_veth(veth1buf
, &veth2buf
) < 0) {
406 fprintf(stderr
, "Error creating veth tunnel\n");
410 if (strcmp(br
, "none") != 0) {
411 /* copy the bridge's mtu to both ends */
414 if (lxc_netdev_set_mtu(veth1buf
, mtu
) < 0 ||
415 lxc_netdev_set_mtu(veth2buf
, mtu
) < 0) {
416 fprintf(stderr
, "Failed setting mtu\n");
421 /* attach veth1 to bridge */
422 if (lxc_bridge_attach(br
, veth1buf
) < 0) {
423 fprintf(stderr
, "Error attaching %s to %s\n", veth1buf
, br
);
428 /* pass veth2 to target netns */
429 ret
= lxc_netdev_move_by_name(veth2buf
, pid
, NULL
);
431 fprintf(stderr
, "Error moving %s to netns %d\n", veth2buf
, pid
);
434 *cnic
= strdup(veth2buf
);
438 lxc_netdev_delete_by_name(veth1buf
);
444 * *dest will container the name (vethXXXXXX) which is attached
445 * on the host to the lxc bridge
447 static bool get_new_nicname(char **dest
, char *br
, int pid
, char **cnic
)
449 char template[IFNAMSIZ
];
450 snprintf(template, sizeof(template), "vethXXXXXX");
451 *dest
= lxc_mkifname(template);
453 if (!create_nic(*dest
, br
, pid
, cnic
)) {
459 static bool get_nic_from_line(char *p
, char **nic
)
461 char user
[100], type
[100], br
[100];
464 ret
= sscanf(p
, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user
, type
, br
, *nic
);
476 static bool cull_entries(int fd
, char *me
, char *t
, char *br
)
479 char *buf
, *p
, *e
, *nic
;
481 struct entry_line
*entry_lines
= NULL
;
486 if (fstat(fd
, &sb
) < 0) {
487 fprintf(stderr
, "Failed to fstat: %s\n", strerror(errno
));
493 buf
= mmap(NULL
, len
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
494 if (buf
== MAP_FAILED
) {
495 fprintf(stderr
, "Failed to create mapping: %s\n", strerror(errno
));
501 while ((p
= find_line(p
, e
, me
, t
, br
)) != NULL
) {
502 struct entry_line
*newe
= realloc(entry_lines
, sizeof(*entry_lines
)*(n
+1));
508 entry_lines
[n
].start
= p
;
509 entry_lines
[n
].len
= get_eol(p
, e
) - entry_lines
[n
].start
;
510 entry_lines
[n
].keep
= true;
512 if (!get_nic_from_line(p
, &nic
))
514 if (nic
&& !nic_exists(nic
))
515 entry_lines
[n
-1].keep
= false;
516 p
+= entry_lines
[n
-1].len
+ 1;
521 for (i
=0; i
<n
; i
++) {
522 if (!entry_lines
[i
].keep
)
524 memcpy(p
, entry_lines
[i
].start
, entry_lines
[i
].len
);
525 p
+= entry_lines
[i
].len
;
530 munmap(buf
, sb
.st_size
);
531 if (ftruncate(fd
, p
-buf
))
532 fprintf(stderr
, "Failed to set new file size\n");
536 static int count_entries(char *buf
, off_t len
, char *me
, char *t
, char *br
)
540 while ((buf
= find_line(buf
, e
, me
, t
, br
)) != NULL
) {
542 buf
= get_eol(buf
, e
)+1;
551 * The dbfile has lines of the format:
552 * user type bridge nicname
554 static bool get_nic_if_avail(int fd
, struct alloted_s
*names
, int pid
, char *intype
, char *br
, int allowed
, char **nicname
, char **cnic
)
558 char *buf
= NULL
, *newline
;
563 for (n
=names
; n
!=NULL
; n
=n
->next
)
564 cull_entries(fd
, n
->name
, intype
, br
);
571 if (fstat(fd
, &sb
) < 0) {
572 fprintf(stderr
, "Failed to fstat: %s\n", strerror(errno
));
577 buf
= mmap(NULL
, len
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
578 if (buf
== MAP_FAILED
) {
579 fprintf(stderr
, "Failed to create mapping\n");
584 for (n
=names
; n
!=NULL
; n
=n
->next
) {
585 count
= count_entries(buf
, len
, n
->name
, intype
, br
);
587 if (count
>= n
->allowed
)
598 if (!get_new_nicname(nicname
, br
, pid
, cnic
))
600 /* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
601 slen
= strlen(owner
) + strlen(intype
) + strlen(br
) + strlen(*nicname
) + 5;
602 newline
= alloca(slen
);
603 ret
= snprintf(newline
, slen
, "%s %s %s %s\n", owner
, intype
, br
, *nicname
);
604 if (ret
< 0 || ret
>= slen
) {
605 if (lxc_netdev_delete_by_name(*nicname
) != 0)
606 fprintf(stderr
, "Error unlinking %s!\n", *nicname
);
611 if (ftruncate(fd
, len
+ slen
))
612 fprintf(stderr
, "Failed to set new file size\n");
613 buf
= mmap(NULL
, len
+ slen
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
614 if (buf
== MAP_FAILED
) {
615 fprintf(stderr
, "Failed to create mapping after extending: %s\n", strerror(errno
));
616 if (lxc_netdev_delete_by_name(*nicname
) != 0)
617 fprintf(stderr
, "Error unlinking %s!\n", *nicname
);
620 strcpy(buf
+len
, newline
);
621 munmap(buf
, len
+slen
);
625 static bool create_db_dir(char *fnam
)
627 char *p
= alloca(strlen(fnam
)+1);
633 while (*p
&& *p
!= '/') p
++;
637 if (mkdir(fnam
, 0755) && errno
!= EEXIST
) {
638 fprintf(stderr
, "failed to create %s\n", fnam
);
646 #define VETH_DEF_NAME "eth%d"
648 static int rename_in_ns(int pid
, char *oldname
, char **newnamep
)
650 char nspath
[MAXPATHLEN
];
651 int fd
= -1, ofd
= -1, ret
, ifindex
= -1;
652 bool grab_newname
= false;
654 ret
= snprintf(nspath
, MAXPATHLEN
, "/proc/%d/ns/net", getpid());
655 if (ret
< 0 || ret
>= MAXPATHLEN
)
657 if ((ofd
= open(nspath
, O_RDONLY
)) < 0) {
658 fprintf(stderr
, "Opening %s\n", nspath
);
661 ret
= snprintf(nspath
, MAXPATHLEN
, "/proc/%d/ns/net", pid
);
662 if (ret
< 0 || ret
>= MAXPATHLEN
)
665 if ((fd
= open(nspath
, O_RDONLY
)) < 0) {
666 fprintf(stderr
, "Opening %s\n", nspath
);
669 if (setns(fd
, 0) < 0) {
670 fprintf(stderr
, "setns to container network namespace\n");
676 *newnamep
= VETH_DEF_NAME
;
677 if (!(ifindex
= if_nametoindex(oldname
))) {
678 fprintf(stderr
, "failed to get netdev index\n");
682 if ((ret
= lxc_netdev_rename_by_name(oldname
, *newnamep
)) < 0) {
683 fprintf(stderr
, "Error %d renaming netdev %s to %s in container\n", ret
, oldname
, *newnamep
);
687 char ifname
[IFNAMSIZ
], *namep
= ifname
;
688 if (!if_indextoname(ifindex
, namep
)) {
689 fprintf(stderr
, "Failed to get new netdev name\n");
692 *newnamep
= strdup(namep
);
696 if (setns(ofd
, 0) < 0) {
697 fprintf(stderr
, "Error returning to original netns\n");
708 if (setns(ofd
, 0) < 0)
709 fprintf(stderr
, "Error returning to original network namespace\n");
716 * If the caller (real uid, not effective uid) may read the
717 * /proc/[pid]/ns/net, then it is either the caller's netns or one
720 static bool may_access_netns(int pid
)
724 uid_t ruid
, suid
, euid
;
725 bool may_access
= false;
727 ret
= getresuid(&ruid
, &euid
, &suid
);
729 fprintf(stderr
, "Failed to get my uids: %s\n", strerror(errno
));
732 ret
= setresuid(ruid
, ruid
, euid
);
734 fprintf(stderr
, "Failed to set temp uids to (%d,%d,%d): %s\n",
735 (int)ruid
, (int)ruid
, (int)euid
, strerror(errno
));
738 ret
= snprintf(s
, 200, "/proc/%d/ns/net", pid
);
739 if (ret
< 0 || ret
>= 200) // can't happen
741 ret
= access(s
, R_OK
);
743 fprintf(stderr
, "Uid %d may not access %s: %s\n",
744 (int)ruid
, s
, strerror(errno
));
746 may_access
= ret
== 0;
747 ret
= setresuid(ruid
, euid
, suid
);
749 fprintf(stderr
, "Failed to restore uids to (%d,%d,%d): %s\n",
750 (int)ruid
, (int)euid
, (int)suid
, strerror(errno
));
756 int main(int argc
, char *argv
[])
761 char *nicname
= alloca(40);
762 char *cnic
= NULL
; // created nic name in container is returned here.
763 char *vethname
= NULL
;
765 struct alloted_s
*alloted
= NULL
;
767 /* set a sane env, because we are setuid-root */
768 if (clearenv() < 0) {
769 fprintf(stderr
, "Failed to clear environment");
772 if (setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1) < 0) {
773 fprintf(stderr
, "Failed to set PATH, exiting\n");
776 if ((me
= get_username()) == NULL
) {
777 fprintf(stderr
, "Failed to get username\n");
782 usage(argv
[0], true);
787 pid
= (int) strtol(argv
[1], NULL
, 10);
789 fprintf(stderr
, "Could not read pid: %s\n", argv
[1]);
793 if (!create_db_dir(LXC_USERNIC_DB
)) {
794 fprintf(stderr
, "Failed to create directory for db file\n");
798 if ((fd
= open_and_lock(LXC_USERNIC_DB
)) < 0) {
799 fprintf(stderr
, "Failed to lock %s\n", LXC_USERNIC_DB
);
803 if (!may_access_netns(pid
)) {
804 fprintf(stderr
, "User %s may not modify netns for pid %d\n",
809 n
= get_alloted(me
, argv
[2], argv
[3], &alloted
);
811 gotone
= get_nic_if_avail(fd
, alloted
, pid
, argv
[2], argv
[3], n
, &nicname
, &cnic
);
814 free_alloted(&alloted
);
816 fprintf(stderr
, "Quota reached\n");
820 // Now rename the link
821 if (rename_in_ns(pid
, cnic
, &vethname
) < 0) {
822 fprintf(stderr
, "Failed to rename the link\n");
826 // write the name of the interface pair to the stdout - like eth0:veth9MT2L4
827 fprintf(stdout
, "%s:%s\n", vethname
, nicname
);