]>
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>
33 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <linux/netlink.h>
39 #include <arpa/inet.h>
41 #include <net/if_arp.h>
42 #include <netinet/in.h>
43 #include <linux/if_bridge.h>
44 #include <linux/netlink.h>
45 #include <linux/rtnetlink.h>
46 #include <linux/sockios.h>
47 #include <sys/param.h>
54 #include <../include/getline.h>
58 /* Define setns() if missing from the C library */
60 static inline int setns(int fd
, int nstype
)
63 return syscall(__NR_setns
, fd
, nstype
);
72 #define CONF_FILE "/tmp/lxc-usernet"
73 #define DB_FILE "/tmp/nics"
75 #define CONF_FILE LXC_USERNIC_CONF
76 #define DB_FILE LXC_USERNIC_DB
83 # define IFLA_LINKMODE 17
87 # define IFLA_LINKINFO 18
90 #ifndef IFLA_NET_NS_PID
91 # define IFLA_NET_NS_PID 19
94 #ifndef IFLA_INFO_KIND
95 # define IFLA_INFO_KIND 1
99 # define IFLA_VLAN_ID 1
102 #ifndef IFLA_INFO_DATA
103 # define IFLA_INFO_DATA 2
106 #ifndef VETH_INFO_PEER
107 # define VETH_INFO_PEER 1
110 #ifndef IFLA_MACVLAN_MODE
111 # define IFLA_MACVLAN_MODE 1
114 void usage(char *me
, bool fail
)
116 fprintf(stderr
, "Usage: %s pid type bridge nicname\n", me
);
117 fprintf(stderr
, " nicname is the name to use inside the container\n");
121 int open_and_lock(char *path
)
126 fd
= open(path
, O_RDWR
|O_CREAT
, S_IWUSR
| S_IRUSR
);
133 lk
.l_whence
= SEEK_SET
;
136 if (fcntl(fd
, F_SETLKW
, &lk
) < 0) {
137 perror("fcntl lock");
145 char *get_username(char **buf
)
147 struct passwd
*pwd
= getpwuid(getuid());
157 /* The configuration file consists of lines of the form:
159 * user type bridge nic-name count
161 * We simply count the number of lines in the file, making sure that
162 * every listed nic is still present. Any nics which have disappeared
163 * is removed when we count, in case the container died a harsh death
164 * without being able to clean up after itself.
166 int get_alloted(char *me
, char *intype
, char *link
)
168 FILE *fin
= fopen(CONF_FILE
, "r");
170 char user
[100], type
[100], br
[100];
177 while ((getline(&line
, &len
, fin
)) != -1) {
178 ret
= sscanf(line
, "%99[^ \t] %99[^ \t] %99[^ \t] %d", user
, type
, br
, &n
);
182 if (strcmp(user
, me
) != 0)
184 if (strcmp(type
, intype
) != 0)
186 if (strcmp(link
, br
) != 0)
197 char *get_eol(char *s
)
199 while (*s
&& *s
!= '\n')
204 char *get_eow(char *s
)
206 while (*s
&& !isblank(*s
) && *s
!= '\n')
211 char *find_line(char *p
, char *e
, char *u
, char *t
, char *l
)
215 while (p
< e
&& (p1
= get_eol(p
)) < e
) {
219 while (isblank(*p
)) p
++;
221 if (!p2
|| p2
-p
!= strlen(u
) || strncmp(p
, u
, strlen(u
)) != 0)
224 while (isblank(*p
)) p
++;
226 if (!p2
|| p2
-p
!= strlen(t
) || strncmp(p
, t
, strlen(t
)) != 0)
229 while (isblank(*p
)) p
++;
231 if (!p2
|| p2
-p
!= strlen(l
) || strncmp(p
, l
, strlen(l
)) != 0)
241 bool nic_exists(char *nic
)
248 ret
= snprintf(path
, 200, "/tmp/lxcnettest/%s", nic
);
250 ret
= snprintf(path
, 200, "/sys/class/net/%s", nic
);
252 if (ret
< 0 || ret
>= 200)
254 ret
= stat(path
, &sb
);
262 struct ifinfomsg ifinfomsg
;
267 int lxc_veth_create(const char *name1
, const char *name2
)
269 struct nl_handler nlh
;
270 struct nlmsg
*nlmsg
= NULL
, *answer
= NULL
;
271 struct link_req
*link_req
;
272 struct rtattr
*nest1
, *nest2
, *nest3
;
275 err
= netlink_open(&nlh
, NETLINK_ROUTE
);
281 if (len
== 1 || len
>= IFNAMSIZ
)
285 if (len
== 1 || len
>= IFNAMSIZ
)
289 nlmsg
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
293 answer
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
297 link_req
= (struct link_req
*)nlmsg
;
298 link_req
->ifinfomsg
.ifi_family
= AF_UNSPEC
;
299 nlmsg
->nlmsghdr
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
300 nlmsg
->nlmsghdr
.nlmsg_flags
=
301 NLM_F_REQUEST
|NLM_F_CREATE
|NLM_F_EXCL
|NLM_F_ACK
;
302 nlmsg
->nlmsghdr
.nlmsg_type
= RTM_NEWLINK
;
305 nest1
= nla_begin_nested(nlmsg
, IFLA_LINKINFO
);
309 if (nla_put_string(nlmsg
, IFLA_INFO_KIND
, "veth"))
312 nest2
= nla_begin_nested(nlmsg
, IFLA_INFO_DATA
);
316 nest3
= nla_begin_nested(nlmsg
, VETH_INFO_PEER
);
320 nlmsg
->nlmsghdr
.nlmsg_len
+= sizeof(struct ifinfomsg
);
322 if (nla_put_string(nlmsg
, IFLA_IFNAME
, name2
))
325 nla_end_nested(nlmsg
, nest3
);
327 nla_end_nested(nlmsg
, nest2
);
329 nla_end_nested(nlmsg
, nest1
);
331 if (nla_put_string(nlmsg
, IFLA_IFNAME
, name1
))
334 err
= netlink_transaction(&nlh
, nlmsg
, answer
);
342 int lxc_netdev_move(char *ifname
, pid_t pid
)
344 struct nl_handler nlh
;
345 struct nlmsg
*nlmsg
= NULL
;
346 struct link_req
*link_req
;
349 index
= if_nametoindex(ifname
);
353 err
= netlink_open(&nlh
, NETLINK_ROUTE
);
358 nlmsg
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
362 link_req
= (struct link_req
*)nlmsg
;
363 link_req
->ifinfomsg
.ifi_family
= AF_UNSPEC
;
364 link_req
->ifinfomsg
.ifi_index
= index
;
365 nlmsg
->nlmsghdr
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
366 nlmsg
->nlmsghdr
.nlmsg_flags
= NLM_F_REQUEST
|NLM_F_ACK
;
367 nlmsg
->nlmsghdr
.nlmsg_type
= RTM_NEWLINK
;
369 if (nla_put_u32(nlmsg
, IFLA_NET_NS_PID
, pid
))
372 err
= netlink_transaction(&nlh
, nlmsg
, nlmsg
);
379 static int setup_private_host_hw_addr(char *veth1
)
385 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
389 snprintf((char *)ifr
.ifr_name
, IFNAMSIZ
, "%s", veth1
);
390 err
= ioctl(sockfd
, SIOCGIFHWADDR
, &ifr
);
396 ifr
.ifr_hwaddr
.sa_data
[0] = 0xfe;
397 err
= ioctl(sockfd
, SIOCSIFHWADDR
, &ifr
);
405 static int netdev_set_flag(const char *name
, int flag
)
407 struct nl_handler nlh
;
408 struct nlmsg
*nlmsg
= NULL
, *answer
= NULL
;
409 struct link_req
*link_req
;
412 err
= netlink_open(&nlh
, NETLINK_ROUTE
);
418 if (len
== 1 || len
>= IFNAMSIZ
)
422 nlmsg
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
426 answer
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
431 index
= if_nametoindex(name
);
435 link_req
= (struct link_req
*)nlmsg
;
436 link_req
->ifinfomsg
.ifi_family
= AF_UNSPEC
;
437 link_req
->ifinfomsg
.ifi_index
= index
;
438 link_req
->ifinfomsg
.ifi_change
|= IFF_UP
;
439 link_req
->ifinfomsg
.ifi_flags
|= flag
;
440 nlmsg
->nlmsghdr
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
441 nlmsg
->nlmsghdr
.nlmsg_flags
= NLM_F_REQUEST
|NLM_F_ACK
;
442 nlmsg
->nlmsghdr
.nlmsg_type
= RTM_NEWLINK
;
444 err
= netlink_transaction(&nlh
, nlmsg
, answer
);
452 static int instanciate_veth(char *n1
, char **n2
)
456 err
= snprintf(*n2
, IFNAMSIZ
, "%sp", n1
);
457 if (err
< 0 || err
>= IFNAMSIZ
) {
458 fprintf(stderr
, "nic name too long\n");
462 err
= lxc_veth_create(n1
, *n2
);
464 fprintf(stderr
, "failed to create %s-%s : %s\n", n1
, *n2
,
469 /* changing the high byte of the mac address to 0xfe, the bridge interface
470 * will always keep the host's mac address and not take the mac address
472 err
= setup_private_host_hw_addr(n1
);
474 fprintf(stderr
, "failed to change mac address of host interface '%s' : %s",
478 return netdev_set_flag(n1
, IFF_UP
);
481 int lxc_bridge_attach(const char *bridge
, const char *ifname
)
486 if (strlen(ifname
) >= IFNAMSIZ
)
489 index
= if_nametoindex(ifname
);
493 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
497 strncpy(ifr
.ifr_name
, bridge
, IFNAMSIZ
-1);
498 ifr
.ifr_name
[IFNAMSIZ
-1] = '\0';
499 ifr
.ifr_ifindex
= index
;
500 err
= ioctl(fd
, SIOCBRADDIF
, &ifr
);
508 int lxc_netdev_delete_by_index(int ifindex
)
510 struct nl_handler nlh
;
511 struct nlmsg
*nlmsg
= NULL
, *answer
= NULL
;
512 struct link_req
*link_req
;
515 err
= netlink_open(&nlh
, NETLINK_ROUTE
);
520 nlmsg
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
524 answer
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
528 link_req
= (struct link_req
*)nlmsg
;
529 link_req
->ifinfomsg
.ifi_family
= AF_UNSPEC
;
530 link_req
->ifinfomsg
.ifi_index
= ifindex
;
531 nlmsg
->nlmsghdr
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
532 nlmsg
->nlmsghdr
.nlmsg_flags
= NLM_F_ACK
|NLM_F_REQUEST
;
533 nlmsg
->nlmsghdr
.nlmsg_type
= RTM_DELLINK
;
535 err
= netlink_transaction(&nlh
, nlmsg
, answer
);
543 int lxc_netdev_delete_by_name(const char *name
)
547 index
= if_nametoindex(name
);
551 return lxc_netdev_delete_by_index(index
);
554 int lxc_netdev_delete_by_name(const char *name
)
557 sprintf(path
, "/tmp/lxcnettest/%s", name
);
563 bool create_nic(char *nic
, char *br
, char *pidstr
, char **cnic
)
567 sprintf(path
, "/tmp/lxcnettest/%s", nic
);
568 int fd
= open(path
, O_RDWR
|O_CREAT
, S_IWUSR
| S_IRUSR
);
574 // not yet implemented
575 char *veth1buf
, *veth2buf
;
576 veth1buf
= alloca(IFNAMSIZ
);
577 veth2buf
= alloca(IFNAMSIZ
);
579 int pid
= atoi(pidstr
);
581 ret
= snprintf(veth1buf
, IFNAMSIZ
, "%s", nic
);
582 if (ret
< 0 || ret
>= IFNAMSIZ
) {
583 fprintf(stderr
, "host nic name too long\n");
587 /* create the nics */
588 if (instanciate_veth(veth1buf
, &veth2buf
) < 0) {
589 fprintf(stderr
, "Error creating veth tunnel\n");
593 /* attach veth1 to bridge */
594 if (lxc_bridge_attach(br
, veth1buf
) < 0) {
595 fprintf(stderr
, "Error attaching %s to %s\n", veth1buf
, br
);
599 /* pass veth2 to target netns */
600 ret
= lxc_netdev_move(veth2buf
, pid
);
602 fprintf(stderr
, "Error moving %s to netns %d\n", veth2buf
, pid
);
605 *cnic
= strdup(veth2buf
);
609 lxc_netdev_delete_by_name(veth1buf
);
616 * *dest will container the name (lxcuser-%d) which is attached
617 * on the host to the lxc bridge
619 void get_new_nicname(char **dest
, char *br
, char *pid
, char **cnic
)
622 // TODO - speed this up. For large installations we won't
623 // want n stats for every nth container startup.
625 sprintf(*dest
, "lxcuser-%d", i
);
626 if (!nic_exists(*dest
) && create_nic(*dest
, br
, pid
, cnic
))
632 bool get_nic_from_line(char *p
, char **nic
)
634 char user
[100], type
[100], br
[100];
637 ret
= sscanf(p
, "%99[^ \t\n] %99[^ \t\n] %99[^ \t\n] %99[^ \t\n]", user
, type
, br
, *nic
);
643 bool cull_entries(int fd
, char *me
, char *t
, char *br
)
646 char *buf
, *p
, *e
, *nic
;
655 buf
= mmap(NULL
, len
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
656 if (buf
== MAP_FAILED
) {
657 fprintf(stderr
, "Failed to create mapping: error %d\n", errno
);
663 while ((p
= find_line(p
, e
, me
, t
, br
)) != NULL
) {
664 if (!get_nic_from_line(p
, &nic
))
666 if (nic
&& !nic_exists(nic
)) {
667 // copy from eol(p)+1..e to p
668 char *src
= get_eol(p
) + 1, *dest
= p
;
671 *(dest
++) = *(src
)++;
678 munmap(buf
, sb
.st_size
);
679 if (ftruncate(fd
, e
-buf
))
680 fprintf(stderr
, "Failed to set new file size\n");
684 int count_entries(char *buf
, off_t len
, char *me
, char *t
, char *br
)
688 while ((buf
= find_line(buf
, e
, me
, t
, br
)) != NULL
) {
690 buf
= get_eol(buf
)+1;
699 * The dbfile has lines of the format:
700 * user type bridge nicname
702 bool get_nic_if_avail(int fd
, char *me
, char *pid
, char *intype
, char *br
, int allowed
, char **nicname
, char **cnic
)
706 char *buf
= NULL
, *newline
;
709 cull_entries(fd
, me
, intype
, br
);
714 buf
= mmap(NULL
, len
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
715 if (buf
== MAP_FAILED
) {
716 fprintf(stderr
, "Failed to create mapping\n");
720 count
= count_entries(buf
, len
, me
, intype
, br
);
721 if (count
>= allowed
)
726 get_new_nicname(nicname
, br
, pid
, cnic
);
727 /* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
728 slen
= strlen(me
) + strlen(intype
) + strlen(br
) + strlen(*nicname
) + 5;
729 newline
= alloca(slen
);
730 ret
= snprintf(newline
, slen
, "%s %s %s %s\n", me
, intype
, br
, *nicname
);
731 if (ret
< 0 || ret
>= slen
) {
732 if (lxc_netdev_delete_by_name(*nicname
) != 0)
733 fprintf(stderr
, "Error unlinking %s!\n", *nicname
);
738 if (ftruncate(fd
, len
+ slen
))
739 fprintf(stderr
, "Failed to set new file size\n");
740 buf
= mmap(NULL
, len
+ slen
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
741 if (buf
== MAP_FAILED
) {
742 fprintf(stderr
, "Failed to create mapping after extending: error %d\n", errno
);
743 if (lxc_netdev_delete_by_name(*nicname
) != 0)
744 fprintf(stderr
, "Error unlinking %s!\n", *nicname
);
747 strcpy(buf
+len
, newline
);
748 munmap(buf
, len
+slen
);
752 bool create_db_dir(char *fnam
)
754 char *p
= alloca(strlen(fnam
)+1);
760 while (*p
&& *p
!= '/') p
++;
764 if (mkdir(fnam
, 0755) && errno
!= EEXIST
) {
765 fprintf(stderr
, "failed to create %s\n", fnam
);
773 static int lxc_netdev_rename_by_index(int ifindex
, const char *newname
)
775 struct nl_handler nlh
;
776 struct nlmsg
*nlmsg
= NULL
, *answer
= NULL
;
777 struct link_req
*link_req
;
780 err
= netlink_open(&nlh
, NETLINK_ROUTE
);
784 len
= strlen(newname
);
785 if (len
== 1 || len
>= IFNAMSIZ
)
789 nlmsg
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
793 answer
= nlmsg_alloc(NLMSG_GOOD_SIZE
);
797 link_req
= (struct link_req
*)nlmsg
;
798 link_req
->ifinfomsg
.ifi_family
= AF_UNSPEC
;
799 link_req
->ifinfomsg
.ifi_index
= ifindex
;
800 nlmsg
->nlmsghdr
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
801 nlmsg
->nlmsghdr
.nlmsg_flags
= NLM_F_ACK
|NLM_F_REQUEST
;
802 nlmsg
->nlmsghdr
.nlmsg_type
= RTM_NEWLINK
;
804 if (nla_put_string(nlmsg
, IFLA_IFNAME
, newname
))
807 err
= netlink_transaction(&nlh
, nlmsg
, answer
);
815 static int lxc_netdev_rename_by_name(const char *oldname
, const char *newname
)
819 len
= strlen(oldname
);
820 if (len
== 1 || len
>= IFNAMSIZ
)
823 index
= if_nametoindex(oldname
);
825 fprintf(stderr
, "Error getting ifindex for %s\n", oldname
);
829 return lxc_netdev_rename_by_index(index
, newname
);
832 int rename_in_ns(int pid
, char *oldname
, char *newname
)
834 char nspath
[MAXPATHLEN
];
835 int fd
= -1, ofd
= -1, ret
;
837 ret
= snprintf(nspath
, MAXPATHLEN
, "/proc/%d/ns/net", getpid());
838 if (ret
< 0 || ret
>= MAXPATHLEN
)
840 if ((ofd
= open(nspath
, O_RDONLY
)) < 0) {
841 fprintf(stderr
, "Opening %s\n", nspath
);
844 ret
= snprintf(nspath
, MAXPATHLEN
, "/proc/%d/ns/net", pid
);
845 if (ret
< 0 || ret
>= MAXPATHLEN
)
848 if ((fd
= open(nspath
, O_RDONLY
)) < 0) {
849 fprintf(stderr
, "Opening %s\n", nspath
);
852 if (setns(fd
, 0) < 0) {
853 fprintf(stderr
, "setns to container network namespace\n");
857 if ((ret
= lxc_netdev_rename_by_name(oldname
, newname
)) < 0) {
858 fprintf(stderr
, "Error %d renaming netdev %s to %s in container\n", ret
, oldname
, newname
);
861 if (setns(ofd
, 0) < 0) {
862 fprintf(stderr
, "Error returning to original netns\n");
873 if (setns(ofd
, 0) < 0)
874 fprintf(stderr
, "Error returning to original network namespace\n");
880 int main(int argc
, char *argv
[])
884 char *me
, *buf
= alloca(400);
885 char *nicname
= alloca(40);
886 char *cnic
; // created nic name in container is returned here.
890 if ((me
= get_username(&buf
)) == NULL
) {
891 fprintf(stderr
, "Failed to get username\n");
896 usage(argv
[0], true);
902 if (!create_db_dir(DB_FILE
)) {
903 fprintf(stderr
, "Failed to create directory for db file\n");
907 if ((fd
= open_and_lock(DB_FILE
)) < 0) {
908 fprintf(stderr
, "Failed to lock %s\n", DB_FILE
);
912 n
= get_alloted(me
, argv
[2], argv
[3]);
914 gotone
= get_nic_if_avail(fd
, me
, argv
[1], argv
[2], argv
[3], n
, &nicname
, &cnic
);
917 fprintf(stderr
, "Quota reached\n");
922 // Now rename the link
923 if (rename_in_ns(pid
, cnic
, vethname
) < 0) {
924 fprintf(stderr
, "Failed to rename the link\n");