Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
lxc
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
lxc
Commits
36259ede
Commit
36259ede
authored
Sep 03, 2017
by
Serge Hallyn
Committed by
GitHub
Sep 03, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1773 from brauner/2017-08-31/ensure_lxc_user_nic_tests_privilege_over_netns
network: improvements + bugfixes
parents
db3c8336
b9f522c5
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
684 additions
and
535 deletions
+684
-535
conf.c
src/lxc/conf.c
+24
-37
conf.h
src/lxc/conf.h
+0
-2
confile.c
src/lxc/confile.c
+11
-13
confile_legacy.c
src/lxc/confile_legacy.c
+16
-10
confile_utils.c
src/lxc/confile_utils.c
+24
-9
confile_utils.h
src/lxc/confile_utils.h
+1
-1
criu.c
src/lxc/criu.c
+8
-6
lxc_user_nic.c
src/lxc/lxc_user_nic.c
+214
-193
network.c
src/lxc/network.c
+277
-95
network.h
src/lxc/network.h
+20
-12
start.c
src/lxc/start.c
+62
-155
start.h
src/lxc/start.h
+5
-2
utils.c
src/lxc/utils.c
+21
-0
utils.h
src/lxc/utils.h
+1
-0
No files found.
src/lxc/conf.c
View file @
36259ede
...
...
@@ -3067,41 +3067,35 @@ static bool verify_start_hooks(struct lxc_conf *conf)
static
int
lxc_send_ttys_to_parent
(
struct
lxc_handler
*
handler
)
{
int
i
;
int
*
ttyfds
;
struct
lxc_pty_info
*
pty_info
;
struct
lxc_conf
*
conf
=
handler
->
conf
;
const
struct
lxc_tty_info
*
tty_info
=
&
conf
->
tty_info
;
int
sock
=
handler
->
tty
sock
[
0
];
struct
lxc_tty_info
*
tty_info
=
&
conf
->
tty_info
;
int
sock
=
handler
->
data_
sock
[
0
];
int
ret
=
-
1
;
size_t
num_ttyfds
=
(
2
*
conf
->
tty
);
ttyfds
=
malloc
(
num_ttyfds
*
sizeof
(
int
));
if
(
!
ttyfds
)
return
-
1
;
for
(
i
=
0
;
i
<
conf
->
tty
;
i
++
)
{
int
ttyfds
[
2
];
struct
lxc_pty_info
*
pty_info
=
&
tty_info
->
pty_info
[
i
]
;
for
(
i
=
0
;
i
<
num_ttyfds
;
i
++
)
{
pty_info
=
&
tty_info
->
pty_info
[
i
/
2
];
ttyfds
[
i
++
]
=
pty_info
->
slave
;
ttyfds
[
i
]
=
pty_info
->
master
;
TRACE
(
"send pty
\"
%s
\"
with master fd %d and slave fd %d to "
"parent"
,
pty_info
->
name
,
pty_info
->
master
,
pty_info
->
slave
);
ttyfds
[
0
]
=
pty_info
->
master
;
ttyfds
[
1
]
=
pty_info
->
slave
;
ret
=
lxc_abstract_unix_send_fds
(
sock
,
ttyfds
,
2
,
NULL
,
0
);
if
(
ret
<
0
)
break
;
TRACE
(
"Send pty
\"
%s
\"
with master fd %d and slave fd %d to "
"parent"
,
pty_info
->
name
,
pty_info
->
master
,
pty_info
->
slave
);
}
ret
=
lxc_abstract_unix_send_fds
(
sock
,
ttyfds
,
num_ttyfds
,
NULL
,
0
);
if
(
ret
<
0
)
ERROR
(
"
f
ailed to send %d ttys to parent: %s"
,
conf
->
tty
,
ERROR
(
"
F
ailed to send %d ttys to parent: %s"
,
conf
->
tty
,
strerror
(
errno
));
else
TRACE
(
"sent %d ttys to parent"
,
conf
->
tty
);
close
(
handler
->
ttysock
[
0
]);
close
(
handler
->
ttysock
[
1
]);
TRACE
(
"Sent %d ttys to parent"
,
conf
->
tty
);
for
(
i
=
0
;
i
<
num_ttyfds
;
i
++
)
close
(
ttyfds
[
i
]);
free
(
ttyfds
);
close
(
handler
->
data_sock
[
0
]);
close
(
handler
->
data_sock
[
1
]);
lxc_delete_tty
(
tty_info
);
return
ret
;
}
...
...
@@ -3129,6 +3123,11 @@ int lxc_setup(struct lxc_handler *handler)
return
-
1
;
}
if
(
lxc_network_send_name_and_ifindex_to_parent
(
handler
)
<
0
)
{
ERROR
(
"Failed to network device names and ifindices to parent"
);
return
-
1
;
}
if
(
lxc_conf
->
autodev
>
0
)
{
if
(
mount_autodev
(
name
,
&
lxc_conf
->
rootfs
,
lxcpath
))
{
ERROR
(
"failed to mount /dev in the container"
);
...
...
@@ -3460,17 +3459,6 @@ int lxc_clear_hooks(struct lxc_conf *c, const char *key)
return
0
;
}
static
void
lxc_clear_saved_nics
(
struct
lxc_conf
*
conf
)
{
int
i
;
if
(
!
conf
->
saved_nics
)
return
;
for
(
i
=
0
;
i
<
conf
->
num_savednics
;
i
++
)
free
(
conf
->
saved_nics
[
i
].
orig_name
);
free
(
conf
->
saved_nics
);
}
static
inline
void
lxc_clear_aliens
(
struct
lxc_conf
*
conf
)
{
struct
lxc_list
*
it
,
*
next
;
...
...
@@ -3525,7 +3513,6 @@ void lxc_conf_free(struct lxc_conf *conf)
lxc_clear_cgroups
(
conf
,
"lxc.cgroup"
);
lxc_clear_hooks
(
conf
,
"lxc.hook"
);
lxc_clear_mount_entries
(
conf
);
lxc_clear_saved_nics
(
conf
);
lxc_clear_idmaps
(
conf
);
lxc_clear_groups
(
conf
);
lxc_clear_includes
(
conf
);
...
...
src/lxc/conf.h
View file @
36259ede
...
...
@@ -244,8 +244,6 @@ struct lxc_conf {
struct
lxc_list
cgroup
;
struct
lxc_list
id_map
;
struct
lxc_list
network
;
struct
saved_nic
*
saved_nics
;
int
num_savednics
;
int
auto_mounts
;
struct
lxc_list
mount_list
;
struct
lxc_list
caps
;
...
...
src/lxc/confile.c
View file @
36259ede
...
...
@@ -483,7 +483,7 @@ static int set_config_net_link(const char *key, const char *value,
if
(
value
[
strlen
(
value
)
-
1
]
==
'+'
&&
netdev
->
type
==
LXC_NET_PHYS
)
ret
=
create_matched_ifnames
(
value
,
lxc_conf
,
netdev
);
else
ret
=
network_ifname
(
&
netdev
->
link
,
value
);
ret
=
network_ifname
(
netdev
->
link
,
value
);
return
ret
;
}
...
...
@@ -503,7 +503,7 @@ static int set_config_net_name(const char *key, const char *value,
if
(
!
netdev
)
return
-
1
;
return
network_ifname
(
&
netdev
->
name
,
value
);
return
network_ifname
(
netdev
->
name
,
value
);
}
static
int
set_config_net_veth_pair
(
const
char
*
key
,
const
char
*
value
,
...
...
@@ -521,7 +521,7 @@ static int set_config_net_veth_pair(const char *key, const char *value,
if
(
!
netdev
)
return
-
1
;
return
network_ifname
(
&
netdev
->
priv
.
veth_attr
.
pair
,
value
);
return
network_ifname
(
netdev
->
priv
.
veth_attr
.
pair
,
value
);
}
static
int
set_config_net_macvlan_mode
(
const
char
*
key
,
const
char
*
value
,
...
...
@@ -3690,8 +3690,7 @@ static int clr_config_net_name(const char *key, struct lxc_conf *lxc_conf,
if
(
!
netdev
)
return
-
1
;
free
(
netdev
->
name
);
netdev
->
name
=
NULL
;
netdev
->
name
[
0
]
=
'\0'
;
return
0
;
}
...
...
@@ -3725,8 +3724,7 @@ static int clr_config_net_link(const char *key, struct lxc_conf *lxc_conf,
if
(
!
netdev
)
return
-
1
;
free
(
netdev
->
link
);
netdev
->
link
=
NULL
;
netdev
->
link
[
0
]
=
'\0'
;
return
0
;
}
...
...
@@ -3763,8 +3761,7 @@ static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf,
if
(
!
netdev
)
return
-
1
;
free
(
netdev
->
priv
.
veth_attr
.
pair
);
netdev
->
priv
.
veth_attr
.
pair
=
NULL
;
netdev
->
priv
.
veth_attr
.
pair
[
0
]
=
'\0'
;
return
0
;
}
...
...
@@ -4032,7 +4029,7 @@ static int get_config_net_link(const char *key, char *retv, int inlen,
if
(
!
netdev
)
return
-
1
;
if
(
netdev
->
link
)
if
(
netdev
->
link
[
0
]
!=
'\0'
)
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
link
);
return
fulllen
;
...
...
@@ -4056,7 +4053,7 @@ static int get_config_net_name(const char *key, char *retv, int inlen,
if
(
!
netdev
)
return
-
1
;
if
(
netdev
->
name
)
if
(
netdev
->
name
[
0
]
!=
'\0'
)
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
name
);
return
fulllen
;
...
...
@@ -4129,8 +4126,9 @@ static int get_config_net_veth_pair(const char *key, char *retv, int inlen,
return
0
;
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
priv
.
veth_attr
.
pair
?
netdev
->
priv
.
veth_attr
.
pair
:
netdev
->
priv
.
veth_attr
.
veth1
);
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
?
netdev
->
priv
.
veth_attr
.
pair
:
netdev
->
priv
.
veth_attr
.
veth1
);
return
fulllen
;
}
...
...
src/lxc/confile_legacy.c
View file @
36259ede
...
...
@@ -110,10 +110,6 @@ static void lxc_remove_nic(struct lxc_list *it)
lxc_list_del
(
it
);
free
(
netdev
->
link
);
free
(
netdev
->
name
);
if
(
netdev
->
type
==
LXC_NET_VETH
)
free
(
netdev
->
priv
.
veth_attr
.
pair
);
free
(
netdev
->
upscript
);
free
(
netdev
->
downscript
);
free
(
netdev
->
hwaddr
);
...
...
@@ -174,6 +170,16 @@ int set_config_network_legacy_type(const char *key, const char *value,
lxc_list_init
(
&
netdev
->
ipv4
);
lxc_list_init
(
&
netdev
->
ipv6
);
netdev
->
name
[
0
]
=
'\0'
;
netdev
->
link
[
0
]
=
'\0'
;
memset
(
&
netdev
->
priv
,
0
,
sizeof
(
netdev
->
priv
));
/* I'm not completely sure if the memset takes care to zero the arrays
* in the union as well. So let's make extra sure and set the first byte
* to zero so that we don't have any surprises.
*/
netdev
->
priv
.
veth_attr
.
pair
[
0
]
=
'\0'
;
netdev
->
priv
.
veth_attr
.
veth1
[
0
]
=
'\0'
;
list
=
malloc
(
sizeof
(
*
list
));
if
(
!
list
)
{
SYSERROR
(
"failed to allocate memory"
);
...
...
@@ -423,7 +429,7 @@ int set_config_network_legacy_link(const char *key, const char *value,
free
(
it
);
ret
=
create_matched_ifnames
(
value
,
lxc_conf
,
NULL
);
}
else
{
ret
=
network_ifname
(
&
netdev
->
link
,
value
);
ret
=
network_ifname
(
netdev
->
link
,
value
);
}
return
ret
;
...
...
@@ -438,7 +444,7 @@ int set_config_network_legacy_name(const char *key, const char *value,
if
(
!
netdev
)
return
-
1
;
return
network_ifname
(
&
netdev
->
name
,
value
);
return
network_ifname
(
netdev
->
name
,
value
);
}
int
set_config_network_legacy_veth_pair
(
const
char
*
key
,
const
char
*
value
,
...
...
@@ -455,7 +461,7 @@ int set_config_network_legacy_veth_pair(const char *key, const char *value,
return
-
1
;
}
return
network_ifname
(
&
netdev
->
priv
.
veth_attr
.
pair
,
value
);
return
network_ifname
(
netdev
->
priv
.
veth_attr
.
pair
,
value
);
}
int
set_config_network_legacy_macvlan_mode
(
const
char
*
key
,
const
char
*
value
,
...
...
@@ -848,12 +854,12 @@ int get_config_network_legacy_item(const char *key, char *retv, int inlen,
if
(
!
netdev
)
return
-
1
;
if
(
strcmp
(
p1
,
"name"
)
==
0
)
{
if
(
netdev
->
name
)
if
(
netdev
->
name
[
0
]
!=
'\0'
)
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
name
);
}
else
if
(
strcmp
(
p1
,
"type"
)
==
0
)
{
strprint
(
retv
,
inlen
,
"%s"
,
lxc_net_type_to_str
(
netdev
->
type
));
}
else
if
(
strcmp
(
p1
,
"link"
)
==
0
)
{
if
(
netdev
->
link
)
if
(
netdev
->
link
[
0
]
!=
'\0'
)
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
link
);
}
else
if
(
strcmp
(
p1
,
"flags"
)
==
0
)
{
if
(
netdev
->
flags
&
IFF_UP
)
...
...
@@ -895,7 +901,7 @@ int get_config_network_legacy_item(const char *key, char *retv, int inlen,
}
else
if
(
strcmp
(
p1
,
"veth.pair"
)
==
0
)
{
if
(
netdev
->
type
==
LXC_NET_VETH
)
{
strprint
(
retv
,
inlen
,
"%s"
,
netdev
->
priv
.
veth_attr
.
pair
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
?
netdev
->
priv
.
veth_attr
.
pair
:
netdev
->
priv
.
veth_attr
.
veth1
);
}
...
...
src/lxc/confile_utils.c
View file @
36259ede
...
...
@@ -183,6 +183,15 @@ struct lxc_netdev *lxc_network_add(struct lxc_list *networks, int idx, bool tail
memset
(
netdev
,
0
,
sizeof
(
*
netdev
));
lxc_list_init
(
&
netdev
->
ipv4
);
lxc_list_init
(
&
netdev
->
ipv6
);
netdev
->
name
[
0
]
=
'\0'
;
netdev
->
link
[
0
]
=
'\0'
;
memset
(
&
netdev
->
priv
,
0
,
sizeof
(
netdev
->
priv
));
/* I'm not completely sure if the memset takes care to zero the arrays
* in the union as well. So let's make extra sure and set the first byte
* to zero so that we don't have any surprises.
*/
netdev
->
priv
.
veth_attr
.
pair
[
0
]
=
'\0'
;
netdev
->
priv
.
veth_attr
.
veth1
[
0
]
=
'\0'
;
/* give network a unique index */
netdev
->
idx
=
idx
;
...
...
@@ -258,7 +267,7 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
switch
(
netdev
->
type
)
{
case
LXC_NET_VETH
:
TRACE
(
"type: veth"
);
if
(
netdev
->
priv
.
veth_attr
.
pair
)
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
TRACE
(
"veth pair: %s"
,
netdev
->
priv
.
veth_attr
.
pair
);
if
(
netdev
->
priv
.
veth_attr
.
veth1
[
0
]
!=
'\0'
)
...
...
@@ -285,6 +294,10 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
break
;
case
LXC_NET_PHYS
:
TRACE
(
"type: phys"
);
if
(
netdev
->
priv
.
phys_attr
.
ifindex
>
0
)
{
TRACE
(
"host side ifindex for phys device: %d"
,
netdev
->
priv
.
phys_attr
.
ifindex
);
}
break
;
case
LXC_NET_EMPTY
:
TRACE
(
"type: empty"
);
...
...
@@ -300,9 +313,9 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
if
(
netdev
->
type
!=
LXC_NET_EMPTY
)
{
TRACE
(
"flags: %s"
,
netdev
->
flags
==
IFF_UP
?
"up"
:
"none"
);
if
(
netdev
->
link
)
if
(
netdev
->
link
[
0
]
!=
'\0'
)
TRACE
(
"link: %s"
,
netdev
->
link
);
if
(
netdev
->
name
)
if
(
netdev
->
name
[
0
]
!=
'\0'
)
TRACE
(
"name: %s"
,
netdev
->
name
);
if
(
netdev
->
hwaddr
)
TRACE
(
"hwaddr: %s"
,
netdev
->
hwaddr
);
...
...
@@ -350,10 +363,6 @@ static void lxc_free_netdev(struct lxc_netdev *netdev)
{
struct
lxc_list
*
cur
,
*
next
;
free
(
netdev
->
link
);
free
(
netdev
->
name
);
if
(
netdev
->
type
==
LXC_NET_VETH
)
free
(
netdev
->
priv
.
veth_attr
.
pair
);
free
(
netdev
->
upscript
);
free
(
netdev
->
downscript
);
free
(
netdev
->
hwaddr
);
...
...
@@ -503,9 +512,15 @@ int config_ip_prefix(struct in_addr *addr)
return
0
;
}
int
network_ifname
(
char
*
*
valuep
,
const
char
*
value
)
int
network_ifname
(
char
*
valuep
,
const
char
*
value
)
{
return
set_config_string_item_max
(
valuep
,
value
,
IFNAMSIZ
);
if
(
strlen
(
value
)
>=
IFNAMSIZ
)
{
ERROR
(
"Network devie name
\"
%s
\"
is too long (>= %zu)"
,
value
,
(
size_t
)
IFNAMSIZ
);
}
strcpy
(
valuep
,
value
);
return
0
;
}
int
rand_complete_hwaddr
(
char
*
hwaddr
)
...
...
src/lxc/confile_utils.h
View file @
36259ede
...
...
@@ -77,7 +77,7 @@ extern int set_config_string_item_max(char **conf_item, const char *value,
size_t
max
);
extern
int
set_config_path_item
(
char
**
conf_item
,
const
char
*
value
);
extern
int
config_ip_prefix
(
struct
in_addr
*
addr
);
extern
int
network_ifname
(
char
*
*
valuep
,
const
char
*
value
);
extern
int
network_ifname
(
char
*
valuep
,
const
char
*
value
);
extern
int
rand_complete_hwaddr
(
char
*
hwaddr
);
extern
bool
lxc_config_net_hwaddr
(
const
char
*
line
);
extern
void
update_hwaddr
(
const
char
*
line
);
...
...
src/lxc/criu.c
View file @
36259ede
...
...
@@ -524,7 +524,7 @@ static void exec_criu(struct criu_opts *opts)
case
LXC_NET_VETH
:
veth
=
n
->
priv
.
veth_attr
.
pair
;
if
(
n
->
link
)
{
if
(
n
->
link
[
0
]
!=
'\0'
)
{
if
(
external_not_veth
)
fmt
=
"veth[%s]:%s@%s"
;
else
...
...
@@ -543,7 +543,7 @@ static void exec_criu(struct criu_opts *opts)
goto
err
;
break
;
case
LXC_NET_MACVLAN
:
if
(
!
n
->
link
)
{
if
(
n
->
link
[
0
]
==
'\0'
)
{
ERROR
(
"no host interface for macvlan %s"
,
n
->
name
);
goto
err
;
}
...
...
@@ -765,11 +765,13 @@ static bool restore_net_info(struct lxc_container *c)
snprintf
(
template
,
sizeof
(
template
),
"vethXXXXXX"
);
if
(
!
netdev
->
priv
.
veth_attr
.
pair
)
netdev
->
priv
.
veth_attr
.
pair
=
lxc_mkifname
(
template
);
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
==
'\0'
&&
netdev
->
priv
.
veth_attr
.
veth1
[
0
]
==
'\0'
)
{
if
(
!
lxc_mkifname
(
template
))
goto
out_unlock
;
if
(
!
netdev
->
priv
.
veth_attr
.
pair
)
goto
out_unlock
;
strcpy
(
netdev
->
priv
.
veth_attr
.
veth1
,
template
);
}
}
has_error
=
false
;
...
...
src/lxc/lxc_user_nic.c
View file @
36259ede
...
...
@@ -61,8 +61,8 @@ static void usage(char *me, bool fail)
{
fprintf
(
stderr
,
"Usage: %s create {lxcpath} {name} {pid} {type} "
"{bridge} {nicname}
\n
"
,
me
);
fprintf
(
stderr
,
"Usage: %s delete {lxcpath} {name}
{pid} {type}
"
"{bridge} {nicname}
\n
"
,
me
);
fprintf
(
stderr
,
"Usage: %s delete {lxcpath} {name} "
"{
/proc/<pid>/ns/net} {type} {
bridge} {nicname}
\n
"
,
me
);
fprintf
(
stderr
,
"{nicname} is the name to use inside the container
\n
"
);
if
(
fail
)
...
...
@@ -357,95 +357,119 @@ static char *get_eow(char *s, char *e)
return
s
;
}
static
char
*
find_line
(
char
*
p
,
char
*
e
,
char
*
u
,
char
*
t
,
char
*
l
)
static
char
*
find_line
(
char
*
buf_start
,
char
*
buf_end
,
char
*
name
,
char
*
net_type
,
char
*
net_link
,
char
*
net_dev
,
bool
*
owner
,
bool
*
found
,
bool
*
keep
)
{
char
*
p1
,
*
p2
,
*
ret
;
char
*
end_of_line
,
*
end_of_word
,
*
line
;
while
((
p
<
e
)
&&
(
p1
=
get_eol
(
p
,
e
))
<
e
)
{
ret
=
p
;
if
(
*
p
==
'#'
)
goto
next
;
while
(
buf_start
<
buf_end
)
{
size_t
len
;
char
netdev_name
[
IFNAMSIZ
];
while
((
p
<
e
)
&&
isblank
(
*
p
))
p
++
;
*
found
=
false
;
*
keep
=
true
;
*
owner
=
false
;
end_of_line
=
get_eol
(
buf_start
,
buf_end
);
if
(
end_of_line
>=
buf_end
)
return
NULL
;
p2
=
get_eow
(
p
,
e
);
if
(
!
p2
||
((
size_t
)(
p2
-
p
))
!=
strlen
(
u
)
||
strncmp
(
p
,
u
,
strlen
(
u
)))
line
=
buf_start
;
if
(
*
buf_start
==
'#'
)
goto
next
;
p
=
p2
+
1
;
while
((
p
<
e
)
&&
isblank
(
*
p
))
p
++
;
while
((
buf_start
<
buf_end
)
&&
isblank
(
*
buf_start
))
buf_start
++
;
p2
=
get_eow
(
p
,
e
);
if
(
!
p2
||
((
size_t
)(
p2
-
p
))
!=
strlen
(
t
)
||
strncmp
(
p
,
t
,
strlen
(
t
)))
goto
next
;
/* Check whether the line contains the caller's name. */
end_of_word
=
get_eow
(
buf_start
,
buf_end
);
/* corrupt db */
if
(
!
end_of_word
)
return
NULL
;
p
=
p2
+
1
;
while
((
p
<
e
)
&&
isblank
(
*
p
))
p
++
;
if
(
strncmp
(
buf_start
,
name
,
strlen
(
name
)))
*
found
=
false
;
p2
=
get_eow
(
p
,
e
);
if
(
!
p2
||
((
size_t
)(
p2
-
p
))
!=
strlen
(
l
)
||
strncmp
(
p
,
l
,
strlen
(
l
)))
goto
next
;
*
owner
=
true
;
return
ret
;
next:
p
=
p1
+
1
;
}
buf_start
=
end_of_word
+
1
;
while
((
buf_start
<
buf_end
)
&&
isblank
(
*
buf_start
))
buf_start
++
;
return
NULL
;
}
/* Check whether line is of the right network type. */
end_of_word
=
get_eow
(
buf_start
,
buf_end
);
/* corrupt db */
if
(
!
end_of_word
)
return
NULL
;
static
bool
nic_exists
(
char
*
nic
)
{
char
path
[
MAXPATHLEN
];
int
ret
;
struct
stat
sb
;
if
(
strncmp
(
buf_start
,
net_type
,
strlen
(
net_type
)))
*
found
=
false
;
if
(
!
strcmp
(
nic
,
"none"
))
return
true
;
buf_start
=
end_of_word
+
1
;
while
((
buf_start
<
buf_end
)
&&
isblank
(
*
buf_start
))
buf_start
++
;
ret
=
snprintf
(
path
,
MAXPATHLEN
,
"/sys/class/net/%s"
,
nic
);
if
(
ret
<
0
||
ret
>=
MAXPATHLEN
)
return
false
;
/* Check whether line is contains the right link. */
end_of_word
=
get_eow
(
buf_start
,
buf_end
);
/* corrupt db */
if
(
!
end_of_word
)
return
NULL
;
ret
=
stat
(
path
,
&
sb
);
if
(
ret
<
0
)
return
false
;
if
(
strncmp
(
buf_start
,
net_link
,
strlen
(
net_link
)))
*
found
=
false
;
return
true
;
}
buf_start
=
end_of_word
+
1
;
while
((
buf_start
<
buf_end
)
&&
isblank
(
*
buf_start
))
buf_start
++
;
static
int
instantiate_veth
(
char
*
n1
,
char
**
n2
)
{
int
err
;
/* Check whether line contains the right network device. */
end_of_word
=
get_eow
(
buf_start
,
buf_end
);
/* corrupt db */
if
(
!
end_of_word
)
return
NULL
;
err
=
snprintf
(
*
n2
,
IFNAMSIZ
,
"%sp"
,
n1
);
if
(
err
<
0
||
err
>=
IFNAMSIZ
)
{
usernic_error
(
"%s
\n
"
,
"Could not create nic name"
);
return
-
1
;
len
=
end_of_word
-
buf_start
;
/* corrupt db */
if
(
len
>=
IFNAMSIZ
)
return
NULL
;
memcpy
(
netdev_name
,
buf_start
,
len
);
netdev_name
[
len
]
=
'\0'
;
*
keep
=
lxc_nic_exists
(
netdev_name
);
if
(
net_dev
&&
!
strcmp
(
netdev_name
,
net_dev
))
*
found
=
true
;
return
line
;
next:
buf_start
=
end_of_line
+
1
;
}
err
=
lxc_veth_create
(
n1
,
*
n2
);
if
(
err
)
{
usernic_error
(
"Failed to create %s-%s : %s.
\n
"
,
n1
,
*
n2
,
strerror
(
-
err
));
return
NULL
;
}
static
int
instantiate_veth
(
char
*
veth1
,
char
*
veth2
)
{
int
ret
;
ret
=
lxc_veth_create
(
veth1
,
veth2
);
if
(
ret
<
0
)
{
usernic_error
(
"Failed to create %s-%s : %s.
\n
"
,
veth1
,
veth2
,
strerror
(
-
ret
));
return
-
1
;
}
/* Changing the high byte of the mac address to 0xfe, the bridge
* interface will always keep the host's mac address and not take the
* mac address of a container. */
err
=
setup_private_host_hw_addr
(
n
1
);
if
(
err
)
ret
=
setup_private_host_hw_addr
(
veth
1
);
if
(
ret
<
0
)
usernic_error
(
"Failed to change mac address of host interface "
"%s : %s
\n
"
,
n1
,
strerror
(
-
err
));
"%s : %s
\n
"
,
veth1
,
strerror
(
-
ret
));
return
netdev_set_flag
(
n
1
,
IFF_UP
);
return
netdev_set_flag
(
veth
1
,
IFF_UP
);
}
static
int
get_mtu
(
char
*
name
)
...
...
@@ -453,31 +477,32 @@ static int get_mtu(char *name)
int
idx
;
idx
=
if_nametoindex
(
name
);
if
(
idx
<
0
)
return
-
1
;
return
netdev_get_mtu
(
idx
);
}
static
bool
create_nic
(
char
*
nic
,
char
*
br
,
int
pid
,
char
**
cnic
)
static
int
create_nic
(
char
*
nic
,
char
*
br
,
int
pid
,
char
**
cnic
)
{
char
*
veth1buf
,
*
veth2buf
;
char
veth1buf
[
IFNAMSIZ
],
veth2buf
[
IFNAMSIZ
]
;
int
mtu
,
ret
;
veth1buf
=
alloca
(
IFNAMSIZ
);
veth2buf
=
alloca
(
IFNAMSIZ
);
if
(
!
veth1buf
||
!
veth2buf
)
{
usernic_error
(
"Failed allocate memory: %s
\n
"
,
strerror
(
errno
));
return
false
;
}
ret
=
snprintf
(
veth1buf
,
IFNAMSIZ
,
"%s"
,
nic
);
if
(
ret
<
0
||
ret
>=
IFNAMSIZ
)
{
usernic_error
(
"%s"
,
"Could not create nic name
\n
"
);
return
false
;
return
-
1
;
}
ret
=
snprintf
(
veth2buf
,
IFNAMSIZ
,
"%sp"
,
veth1buf
);
if
(
ret
<
0
||
ret
>=
IFNAMSIZ
)
{
usernic_error
(
"%s
\n
"
,
"Could not create nic name"
);
return
-
1
;
}
/* create the nics */
if
(
instantiate_veth
(
veth1buf
,
&
veth2buf
)
<
0
)
{
ret
=
instantiate_veth
(
veth1buf
,
veth2buf
);
if
(
ret
<
0
)
{
usernic_error
(
"%s"
,
"Error creating veth tunnel
\n
"
);
return
false
;
return
-
1
;
}
if
(
strcmp
(
br
,
"none"
))
{
...
...
@@ -518,52 +543,14 @@ static bool create_nic(char *nic, char *br, int pid, char **cnic)
*
cnic
=
strdup
(
veth2buf
);
if
(
!*
cnic
)
{
usernic_error
(
"Failed to copy string
\"
%s
\"\n
"
,
veth2buf
);
return
false
;
return
-
1
;
}
return
true
;
return
0
;
out_del:
lxc_netdev_delete_by_name
(
veth1buf
);
return
false
;
}
/* get_new_nicname() will return the name (vethXXXXXX) which is attached on the
* host to the lxc bridge. The returned string must be freed by caller.
*/
static
char
*
get_new_nicname
(
char
*
br
,
int
pid
,
char
**
cnic
)
{
int
ret
;
char
*
nicname
;
char
template
[
IFNAMSIZ
];
ret
=
snprintf
(
template
,
sizeof
(
template
),
"vethXXXXXX"
);
if
(
ret
<
0
||
(
size_t
)
ret
>=
sizeof
(
template
))
return
NULL
;
nicname
=
lxc_mkifname
(
template
);
if
(
!
nicname
)
return
NULL
;
if
(
!
create_nic
(
nicname
,
br
,
pid
,
cnic
))
{
free
(
nicname
);
return
NULL
;
}
return
nicname
;
}
static
bool
get_nic_from_line
(
char
*
p
,
char
**
nic
)
{
int
ret
;
char
user
[
100
],
type
[
100
],
br
[
100
];
ret
=
sscanf
(
p
,
"%99[^
\t\n
] %99[^
\t\n
] %99[^
\t\n
] %99[^
\t\n
]"
,
user
,
type
,
br
,
*
nic
);
if
(
ret
!=
4
)
return
false
;
return
true
;
return
-
1
;
}
struct
entry_line
{
...
...
@@ -572,29 +559,24 @@ struct entry_line {
bool
keep
;
};
static
bool
cull_entries
(
int
fd
,
char
*
me
,
char
*
t
,
char
*
br
,
char
*
nicname
,
bool
*
found_nicname
)
static
bool
cull_entries
(
int
fd
,
char
*
name
,
char
*
net_type
,
char
*
net_link
,
char
*
net_dev
,
bool
*
found_nicname
)
{
int
i
,
ret
;
off_t
len
;
char
*
buf
,
*
e
,
*
nic
,
*
p
;
char
*
buf
,
*
buf_end
,
*
buf_start
;
struct
stat
sb
;
int
n
=
0
;
bool
found
,
keep
;
struct
entry_line
*
entry_lines
=
NULL
;
nic
=
alloca
(
100
);
if
(
!
nic
)
return
false
;
ret
=
fstat
(
fd
,
&
sb
);
if
(
ret
<
0
)
{
usernic_error
(
"Failed to fstat: %s
\n
"
,
strerror
(
errno
));
return
false
;
}
len
=
sb
.
st_size
;
if
(
len
==
0
)
return
true
;
if
(
!
sb
.
st_size
)
return
false
;
buf
=
lxc_strmmap
(
NULL
,
sb
.
st_size
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
if
(
buf
==
MAP_FAILED
)
{
...
...
@@ -603,51 +585,48 @@ static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
return
false
;
}
p
=
buf
;
e
=
buf
+
len
;
while
((
p
=
find_line
(
p
,
e
,
me
,
t
,
br
)))
{
buf_start
=
buf
;
buf_end
=
buf
+
sb
.
st_size
;
while
((
buf_start
=
find_line
(
buf_start
,
buf_end
,
name
,
net_type
,
net_link
,
net_dev
,
&
(
bool
){
true
},
&
found
,
&
keep
)))
{
struct
entry_line
*
newe
;
newe
=
realloc
(
entry_lines
,
sizeof
(
*
entry_lines
)
*
(
n
+
1
));
if
(
!
newe
)
{
free
(
entry_lines
);
lxc_strmunmap
(
buf
,
sb
.
st_size
);
return
false
;
}
if
(
found
)
*
found_nicname
=
true
;
entry_lines
=
newe
;
entry_lines
[
n
].
start
=
p
;
entry_lines
[
n
].
len
=
get_eol
(
p
,
e
)
-
entry_lines
[
n
].
start
;
entry_lines
[
n
].
keep
=
true
;
entry_lines
[
n
].
start
=
buf_start
;
entry_lines
[
n
].
len
=
get_eol
(
buf_start
,
buf_end
)
-
entry_lines
[
n
].
start
;
entry_lines
[
n
].
keep
=
keep
;
n
++
;
if
(
!
get_nic_from_line
(
p
,
&
nic
))
continue
;
if
(
nic
&&
!
nic_exists
(
nic
))
entry_lines
[
n
-
1
].
keep
=
false
;
if
(
nicname
)
if
(
!
strcmp
(
nic
,
nicname
))
*
found_nicname
=
true
;
p
+=
entry_lines
[
n
-
1
].
len
+
1
;
if
(
p
>=
e
)
buf_start
+=
entry_lines
[
n
-
1
].
len
+
1
;
if
(
buf_start
>=
buf_end
)
break
;
}
p
=
buf
;
buf_start
=
buf
;
for
(
i
=
0
;
i
<
n
;
i
++
)
{
if
(
!
entry_lines
[
i
].
keep
)
continue
;
memcpy
(
p
,
entry_lines
[
i
].
start
,
entry_lines
[
i
].
len
);
p
+=
entry_lines
[
i
].
len
;
*
p
=
'\n'
;
p
++
;
memcpy
(
buf_start
,
entry_lines
[
i
].
start
,
entry_lines
[
i
].
len
);
buf_start
+=
entry_lines
[
i
].
len
;
*
buf_start
=
'\n'
;
buf_start
++
;
}
free
(
entry_lines
);
lxc_strmunmap
(
buf
,
sb
.
st_size
);
ret
=
ftruncate
(
fd
,
p
-
buf
);
ret
=
ftruncate
(
fd
,
buf_start
-
buf
);
if
(
ret
<
0
)
usernic_error
(
"Failed to set new file size: %s
\n
"
,
strerror
(
errno
));
...
...
@@ -655,16 +634,19 @@ static bool cull_entries(int fd, char *me, char *t, char *br, char *nicname,
return
true
;
}
static
int
count_entries
(
char
*
buf
,
off_t
len
,
char
*
me
,
char
*
t
,
char
*
br
)
static
int
count_entries
(
char
*
buf
,
off_t
len
,
char
*
name
,
char
*
net_type
,
char
*
net_link
)
{
char
*
e
;
int
count
=
0
;
e
=
&
buf
[
len
];
while
((
buf
=
find_line
(
buf
,
e
,
me
,
t
,
br
)))
{
count
++
;
buf
=
get_eol
(
buf
,
e
)
+
1
;
if
(
buf
>=
e
)
bool
owner
=
false
;;
char
*
buf_end
=
&
buf
[
len
];
buf_end
=
&
buf
[
len
];
while
((
buf
=
find_line
(
buf
,
buf_end
,
name
,
net_type
,
net_link
,
NULL
,
&
owner
,
&
(
bool
){
true
},
&
(
bool
){
true
})))
{
if
(
owner
)
count
++
;
buf
=
get_eol
(
buf
,
buf_end
)
+
1
;
if
(
buf
>=
buf_end
)
break
;
}
...
...
@@ -676,8 +658,9 @@ static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
char
*
intype
,
char
*
br
,
int
allowed
,
char
**
cnic
)
{
int
ret
;
off_t
len
,
slen
;
char
*
newline
,
*
nicname
,
*
owner
;
size_t
slen
;
char
*
newline
,
*
owner
;
char
nicname
[
IFNAMSIZ
];
struct
stat
sb
;
struct
alloted_s
*
n
;
int
count
=
0
;
...
...
@@ -691,79 +674,110 @@ static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
owner
=
names
->
name
;
if
(
fstat
(
fd
,
&
sb
)
<
0
)
{
ret
=
fstat
(
fd
,
&
sb
);
if
(
ret
<
0
)
{
usernic_error
(
"Failed to fstat: %s
\n
"
,
strerror
(
errno
));
return
NULL
;
}
len
=
sb
.
st_size
;
if
(
len
>
0
)
{
buf
=
mmap
(
NULL
,
len
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
if
(
sb
.
st_size
>
0
)
{
buf
=
lxc_strmmap
(
NULL
,
sb
.
st_size
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
if
(
buf
==
MAP_FAILED
)
{
usernic_error
(
"Failed to establish shared memory
mapping: %s
\n
"
,
strerror
(
errno
));
usernic_error
(
"Failed to establish shared memory
"
"mapping: %s
\n
"
,
strerror
(
errno
));
return
NULL
;
}
owner
=
NULL
;
for
(
n
=
names
;
n
!=
NULL
;
n
=
n
->
next
)
{
count
=
count_entries
(
buf
,
len
,
n
->
name
,
intype
,
br
);
count
=
count_entries
(
buf
,
sb
.
st_size
,
n
->
name
,
intype
,
br
);
if
(
count
>=
n
->
allowed
)
continue
;
owner
=
n
->
name
;
break
;
}
lxc_strmunmap
(
buf
,
sb
.
st_size
);
}
if
(
owner
==
NULL
)
return
NULL
;
nicname
=
get_new_nicname
(
br
,
pid
,
cnic
);
if
(
!
nicname
)
{
usernic_error
(
"%s"
,
"Failed to get new nic name
\n
"
);
ret
=
snprintf
(
nicname
,
sizeof
(
nicname
),
"vethXXXXXX"
);
if
(
ret
<
0
||
(
size_t
)
ret
>=
sizeof
(
nicname
))
return
NULL
;
if
(
!
lxc_mkifname
(
nicname
))
return
NULL
;
ret
=
create_nic
(
nicname
,
br
,
pid
,
cnic
);
if
(
ret
<
0
)
{
usernic_error
(
"%s"
,
"Failed to create new nic
\n
"
);
return
NULL
;
}
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
slen
=
strlen
(
owner
)
+
strlen
(
intype
)
+
strlen
(
br
)
+
strlen
(
nicname
)
+
5
;
newline
=
alloca
(
slen
);
/* strlen(owner)
* +
* " "
* +
* strlen(intype)
* +
* " "
* +
* strlen(br)
* +
* " "
* +
* strlen(nicname)
* +
* \n
* +
* \0
*/
slen
=
strlen
(
owner
)
+
strlen
(
intype
)
+
strlen
(
br
)
+
strlen
(
nicname
)
+
4
;
newline
=
malloc
(
slen
+
1
);
if
(
!
newline
)
{
free
(
n
icnam
e
);
free
(
n
ewlin
e
);
usernic_error
(
"Failed allocate memory: %s
\n
"
,
strerror
(
errno
));
return
NULL
;
}
ret
=
snprintf
(
newline
,
slen
,
"%s %s %s %s
\n
"
,
owner
,
intype
,
br
,
nicname
);
if
(
ret
<
0
||
ret
>=
slen
)
{
ret
=
snprintf
(
newline
,
slen
+
1
,
"%s %s %s %s
\n
"
,
owner
,
intype
,
br
,
nicname
);
if
(
ret
<
0
||
(
size_t
)
ret
>=
(
slen
+
1
)
)
{
if
(
lxc_netdev_delete_by_name
(
nicname
)
!=
0
)
usernic_error
(
"Error unlinking %s
\n
"
,
nicname
);
free
(
n
icnam
e
);
free
(
n
ewlin
e
);
return
NULL
;
}
if
(
len
)
munmap
(
buf
,
len
);
if
(
ftruncate
(
fd
,
len
+
slen
))
usernic_error
(
"Failed to set new file size: %s
\n
"
,
strerror
(
errno
));
/* Note that the file needs to be truncated to the size **without** the
* \0 byte! Files are not \0-terminated!
*/
ret
=
ftruncate
(
fd
,
sb
.
st_size
+
slen
);
if
(
ret
<
0
)
usernic_error
(
"Failed to truncate file: %s
\n
"
,
strerror
(
errno
));
buf
=
mmap
(
NULL
,
len
+
slen
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
buf
=
lxc_strmmap
(
NULL
,
sb
.
st_size
+
slen
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
if
(
buf
==
MAP_FAILED
)
{
usernic_error
(
"Failed to establish shared memory mapping: %s
\n
"
,
strerror
(
errno
));
if
(
lxc_netdev_delete_by_name
(
nicname
)
!=
0
)
usernic_error
(
"Error unlinking %s
\n
"
,
nicname
);
free
(
n
icnam
e
);
free
(
n
ewlin
e
);
return
NULL
;
}
strcpy
(
buf
+
len
,
newline
);
munmap
(
buf
,
len
+
slen
);
/* Note that the memory needs to be moved in the buffer **without** the
* \0 byte! Files are not \0-terminated!
*/
memmove
(
buf
+
sb
.
st_size
,
newline
,
slen
);
free
(
newline
);
lxc_strmunmap
(
buf
,
sb
.
st_size
+
slen
);
return
nicname
;
return
strdup
(
nicname
)
;
}
static
bool
create_db_dir
(
char
*
fnam
)
...
...
@@ -1053,10 +1067,10 @@ do_partial_cleanup:
int
main
(
int
argc
,
char
*
argv
[])
{
int
container_veth_ifidx
,
fd
,
host_veth_ifidx
,
n
,
pid
,
request
,
ret
;
int
fd
,
n
,
pid
,
request
,
ret
;
char
*
me
,
*
newname
;
struct
user_nic_args
args
;
int
netns_fd
=
-
1
;
int
container_veth_ifidx
=
-
1
,
host_veth_ifidx
=
-
1
,
netns_fd
=
-
1
;
char
*
cnic
=
NULL
,
*
nicname
=
NULL
;
struct
alloted_s
*
alloted
=
NULL
;
...
...
@@ -1177,8 +1191,8 @@ int main(int argc, char *argv[])
free_alloted
(
&
alloted
);
if
(
!
found_nicname
)
{
usernic_error
(
"
%s"
,
"Caller is not allowed to delete
"
"
network device
\n
"
);
usernic_error
(
"
Caller is not allowed to delete network
"
"
device
\"
%s
\"\n
"
,
args
.
veth_name
);
exit
(
EXIT_FAILURE
);
}
...
...
@@ -1214,7 +1228,14 @@ int main(int argc, char *argv[])
free
(
nicname
);
exit
(
EXIT_FAILURE
);
}
host_veth_ifidx
=
if_nametoindex
(
nicname
);
if
(
!
host_veth_ifidx
)
{
free
(
newname
);
free
(
nicname
);
usernic_error
(
"Failed to get netdev index: %s
\n
"
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
}
/* Write names of veth pairs and their ifindeces to stout:
* (e.g. eth0:731:veth9MT2L4:730)
...
...
src/lxc/network.c
View file @
36259ede
...
...
@@ -45,6 +45,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "af_unix.h"
#include "conf.h"
#include "config.h"
#include "log.h"
...
...
@@ -101,7 +102,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
char
veth1buf
[
IFNAMSIZ
],
veth2buf
[
IFNAMSIZ
];
unsigned
int
mtu
=
0
;
if
(
netdev
->
priv
.
veth_attr
.
pair
)
{
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
{
veth1
=
netdev
->
priv
.
veth_attr
.
pair
;
if
(
handler
->
conf
->
reboot
)
lxc_netdev_delete_by_name
(
veth1
);
...
...
@@ -140,6 +141,18 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
goto
out_delete
;
}
/* Retrieve ifindex of the host's veth device. */
netdev
->
priv
.
veth_attr
.
ifindex
=
if_nametoindex
(
veth1
);
if
(
!
netdev
->
priv
.
veth_attr
.
ifindex
)
{
ERROR
(
"Failed to retrieve ifindex for
\"
%s
\"
"
,
veth1
);
goto
out_delete
;
}
/* Note that we're retrieving the container's ifindex in the host's
* network namespace because we need it to move the device from the
* host's network namespace to the container's network namespace later
* on.
*/
netdev
->
ifindex
=
if_nametoindex
(
veth2
);
if
(
!
netdev
->
ifindex
)
{
ERROR
(
"Failed to retrieve ifindex for
\"
%s
\"
"
,
veth2
);
...
...
@@ -151,7 +164,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
WARN
(
"Failed to parse mtu"
);
else
INFO
(
"Retrieved mtu %d"
,
mtu
);
}
else
if
(
netdev
->
link
)
{
}
else
if
(
netdev
->
link
[
0
]
!=
'\0'
)
{
bridge_index
=
if_nametoindex
(
netdev
->
link
);
if
(
bridge_index
)
{
mtu
=
netdev_get_mtu
(
bridge_index
);
...
...
@@ -174,7 +187,7 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
}
}
if
(
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
!=
'\0'
)
{
err
=
lxc_bridge_attach
(
netdev
->
link
,
veth1
);
if
(
err
)
{
ERROR
(
"Failed to attach
\"
%s
\"
to bridge
\"
%s
\"
: %s"
,
...
...
@@ -205,9 +218,6 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
out_delete:
if
(
netdev
->
ifindex
!=
0
)
lxc_netdev_delete_by_name
(
veth1
);
if
(
!
netdev
->
priv
.
veth_attr
.
pair
)
free
(
veth1
);
free
(
veth2
);
return
-
1
;
}
...
...
@@ -216,7 +226,7 @@ static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
char
peerbuf
[
IFNAMSIZ
],
*
peer
;
int
err
;
if
(
!
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
==
'\0'
)
{
ERROR
(
"No link for macvlan network device specified"
);
return
-
1
;
}
...
...
@@ -234,29 +244,29 @@ static int instantiate_macvlan(struct lxc_handler *handler, struct lxc_netdev *n
if
(
err
)
{
ERROR
(
"Failed to create macvlan interface
\"
%s
\"
on
\"
%s
\"
: %s"
,
peer
,
netdev
->
link
,
strerror
(
-
err
));
goto
o
ut
;
goto
o
n_error
;
}
netdev
->
ifindex
=
if_nametoindex
(
peer
);
if
(
!
netdev
->
ifindex
)
{
ERROR
(
"Failed to retrieve ifindex for
\"
%s
\"
"
,
peer
);
goto
o
ut
;
goto
o
n_error
;
}
if
(
netdev
->
upscript
)
{
err
=
run_script
(
handler
->
name
,
"net"
,
netdev
->
upscript
,
"up"
,
"macvlan"
,
netdev
->
link
,
(
char
*
)
NULL
);
if
(
err
)
goto
o
ut
;
goto
o
n_error
;
}
DEBUG
(
"Instantiated macvlan
\"
%s
\"
with ifindex is %d and mode %d"
,
peer
,
netdev
->
ifindex
,
netdev
->
priv
.
macvlan_attr
.
mode
);
return
0
;
out:
on_error:
lxc_netdev_delete_by_name
(
peer
);
free
(
peer
);
return
-
1
;
}
...
...
@@ -267,7 +277,7 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
static
uint16_t
vlan_cntr
=
0
;
unsigned
int
mtu
=
0
;
if
(
!
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
==
'\0'
)
{
ERROR
(
"No link for vlan network device specified"
);
return
-
1
;
}
...
...
@@ -296,7 +306,7 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
if
(
lxc_safe_uint
(
netdev
->
mtu
,
&
mtu
)
<
0
)
{
ERROR
(
"Failed to retrieve mtu from
\"
%d
\"
/
\"
%s
\"
."
,
netdev
->
ifindex
,
netdev
->
name
?
netdev
->
name
:
"(null)"
);
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
);
return
-
1
;
}
err
=
lxc_netdev_set_mtu
(
peer
,
mtu
);
...
...
@@ -313,17 +323,29 @@ static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netd
static
int
instantiate_phys
(
struct
lxc_handler
*
handler
,
struct
lxc_netdev
*
netdev
)
{
if
(
!
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
==
'\0'
)
{
ERROR
(
"No link for physical interface specified"
);
return
-
1
;
}
/* Note that we're retrieving the container's ifindex in the host's
* network namespace because we need it to move the device from the
* host's network namespace to the container's network namespace later
* on.
* Note that netdev->link will contain the name of the physical network
* device in the host's namespace.
*/
netdev
->
ifindex
=
if_nametoindex
(
netdev
->
link
);
if
(
!
netdev
->
ifindex
)
{
ERROR
(
"Failed to retrieve ifindex for
\"
%s
\"
"
,
netdev
->
link
);
return
-
1
;
}
/* Store the ifindex of the host's network device in the host's
* namespace.
*/
netdev
->
priv
.
phys_attr
.
ifindex
=
netdev
->
ifindex
;
if
(
netdev
->
upscript
)
{
int
err
;
err
=
run_script
(
handler
->
name
,
"net"
,
netdev
->
upscript
,
...
...
@@ -368,7 +390,7 @@ static int shutdown_veth(struct lxc_handler *handler, struct lxc_netdev *netdev)
char
*
veth1
;
int
err
;
if
(
netdev
->
priv
.
veth_attr
.
pair
)
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
veth1
=
netdev
->
priv
.
veth_attr
.
pair
;
else
veth1
=
netdev
->
priv
.
veth_attr
.
veth1
;
...
...
@@ -1893,14 +1915,17 @@ const char *lxc_net_type_to_str(int type)
static
const
char
padchar
[]
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
char
*
lxc_mkifname
(
c
onst
c
har
*
template
)
char
*
lxc_mkifname
(
char
*
template
)
{
int
ifexists
=
0
;
size_t
i
=
0
;
char
*
name
=
NULL
;
unsigned
int
seed
;
FILE
*
urandom
;
struct
ifaddrs
*
ifa
,
*
ifaddr
;
char
name
[
IFNAMSIZ
];
bool
exists
=
false
;
size_t
i
=
0
;
if
(
strlen
(
template
)
>=
IFNAMSIZ
)
return
NULL
;
/* Get all the network interfaces. */
getifaddrs
(
&
ifaddr
);
...
...
@@ -1921,17 +1946,14 @@ char *lxc_mkifname(const char *template)
/* Generate random names until we find one that doesn't exist. */
while
(
true
)
{
ifexists
=
0
;
name
=
strdup
(
template
);
if
(
name
==
NULL
)
return
NULL
;
name
[
0
]
=
'\0'
;
strcpy
(
name
,
template
);
exists
=
false
;
for
(
i
=
0
;
i
<
strlen
(
name
);
i
++
)
{
if
(
name
[
i
]
==
'X'
)
{
#ifdef HAVE_RAND_R
name
[
i
]
=
padchar
[
rand_r
(
&
seed
)
%
(
strlen
(
padchar
)
-
1
)];
name
[
i
]
=
padchar
[
rand_r
(
&
seed
)
%
(
strlen
(
padchar
)
-
1
)];
#else
name
[
i
]
=
padchar
[
rand
()
%
(
strlen
(
padchar
)
-
1
)];
#endif
...
...
@@ -1939,20 +1961,18 @@ char *lxc_mkifname(const char *template)
}
for
(
ifa
=
ifaddr
;
ifa
!=
NULL
;
ifa
=
ifa
->
ifa_next
)
{
if
(
strcmp
(
ifa
->
ifa_name
,
name
)
==
0
)
{
ifexists
=
1
;
if
(
!
strcmp
(
ifa
->
ifa_name
,
name
)
)
{
exists
=
true
;
break
;
}
}
if
(
ifexists
==
0
)
if
(
!
exists
)
break
;
free
(
name
);
}
freeifaddrs
(
ifaddr
);
return
name
;
return
strcpy
(
template
,
name
)
;
}
int
setup_private_host_hw_addr
(
char
*
veth1
)
...
...
@@ -2002,7 +2022,7 @@ int lxc_find_gateway_addresses(struct lxc_handler *handler)
return
-
1
;
}
if
(
!
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
==
'\0'
)
{
ERROR
(
"Automatic gateway detection needs a link interface"
);
return
-
1
;
}
...
...
@@ -2076,7 +2096,7 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
exit
(
EXIT_FAILURE
);
}
if
(
netdev
->
link
)
if
(
netdev
->
link
[
0
]
!=
'\0'
)
strncpy
(
netdev_link
,
netdev
->
link
,
IFNAMSIZ
);
else
strncpy
(
netdev_link
,
"none"
,
IFNAMSIZ
);
...
...
@@ -2088,8 +2108,8 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
INFO
(
"Execing lxc-user-nic create %s %s %s veth %s %s"
,
lxcpath
,
lxcname
,
pidstr
,
netdev_link
,
netdev
->
name
?
netdev
->
name
:
"(null)"
);
if
(
netdev
->
name
)
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
);
if
(
netdev
->
name
[
0
]
!=
'\0'
)
execlp
(
LXC_USERNIC_PATH
,
LXC_USERNIC_PATH
,
"create"
,
lxcpath
,
lxcname
,
pidstr
,
"veth"
,
netdev_link
,
netdev
->
name
,
(
char
*
)
NULL
);
...
...
@@ -2128,11 +2148,6 @@ static int lxc_create_network_unpriv_exec(const char *lxcpath, char *lxcname,
return
-
1
;
}
netdev
->
name
=
malloc
(
IFNAMSIZ
+
1
);
if
(
!
netdev
->
name
)
{
SYSERROR
(
"Failed to allocate memory"
);
return
-
1
;
}
memset
(
netdev
->
name
,
0
,
IFNAMSIZ
+
1
);
strncpy
(
netdev
->
name
,
token
,
IFNAMSIZ
);
...
...
@@ -2210,6 +2225,7 @@ static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname,
}
if
(
child
==
0
)
{
char
*
hostveth
;
int
ret
;
close
(
pipefd
[
0
]);
...
...
@@ -2223,22 +2239,26 @@ static int lxc_delete_network_unpriv_exec(const char *lxcpath, char *lxcname,
exit
(
EXIT_FAILURE
);
}
if
(
netdev
->
priv
.
veth_attr
.
veth1
[
0
]
==
'\0'
)
{
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
hostveth
=
netdev
->
priv
.
veth_attr
.
pair
;
else
hostveth
=
netdev
->
priv
.
veth_attr
.
veth1
;
if
(
hostveth
[
0
]
==
'\0'
)
{
SYSERROR
(
"Host side veth device name is missing"
);
exit
(
EXIT_FAILURE
);
}
if
(
!
netdev
->
link
)
{
if
(
netdev
->
link
[
0
]
==
'\0'
)
{
SYSERROR
(
"Network link for network device
\"
%s
\"
is "
"missing"
,
netdev
->
priv
.
veth_attr
.
veth1
);
exit
(
EXIT_FAILURE
);
}
INFO
(
"Execing lxc-user-nic delete %s %s %s veth %s %s"
,
lxcpath
,
lxcname
,
netns_path
,
netdev
->
link
,
netdev
->
priv
.
veth_attr
.
veth1
);
lxcname
,
netns_path
,
netdev
->
link
,
hostveth
);
execlp
(
LXC_USERNIC_PATH
,
LXC_USERNIC_PATH
,
"delete"
,
lxcpath
,
lxcname
,
netns_path
,
"veth"
,
netdev
->
link
,
netdev
->
priv
.
veth_attr
.
veth1
,
(
char
*
)
NULL
);
lxcname
,
netns_path
,
"veth"
,
netdev
->
link
,
hostveth
,
(
char
*
)
NULL
);
SYSERROR
(
"Failed to exec lxc-user-nic."
);
exit
(
EXIT_FAILURE
);
}
...
...
@@ -2283,7 +2303,7 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
char
netns_path
[
6
+
LXC_NUMSTRLEN64
+
4
+
LXC_NUMSTRLEN64
+
1
];
bool
deleted_all
=
true
;
if
(
!
am_unpriv
()
)
if
(
handler
->
am_root
)
return
true
;
*
netns_path
=
'\0'
;
...
...
@@ -2333,14 +2353,20 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
if
(
!
is_ovs_bridge
(
netdev
->
link
))
continue
;
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
hostveth
=
netdev
->
priv
.
veth_attr
.
pair
;
else
hostveth
=
netdev
->
priv
.
veth_attr
.
veth1
;
if
(
hostveth
[
0
]
==
'\0'
)
continue
;
ret
=
lxc_delete_network_unpriv_exec
(
handler
->
lxcpath
,
handler
->
name
,
netdev
,
netns_path
);
if
(
ret
<
0
)
{
deleted_all
=
false
;
WARN
(
"Failed to remove port
\"
%s
\"
from openvswitch "
"bridge
\"
%s
\"
"
,
netdev
->
priv
.
veth_attr
.
veth1
,
netdev
->
link
);
"bridge
\"
%s
\"
"
,
hostveth
,
netdev
->
link
);
continue
;
}
INFO
(
"Removed interface
\"
%s
\"
from
\"
%s
\"
"
,
hostveth
,
...
...
@@ -2352,13 +2378,10 @@ bool lxc_delete_network_unpriv(struct lxc_handler *handler)
int
lxc_create_network_priv
(
struct
lxc_handler
*
handler
)
{
bool
am_root
;
struct
lxc_list
*
iterator
;
struct
lxc_list
*
network
=
&
handler
->
conf
->
network
;
/* We need to be root. */
am_root
=
(
getuid
()
==
0
);
if
(
!
am_root
)
if
(
!
handler
->
am_root
)
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
...
...
@@ -2382,7 +2405,7 @@ int lxc_create_network_priv(struct lxc_handler *handler)
int
lxc_network_move_created_netdev_priv
(
const
char
*
lxcpath
,
char
*
lxcname
,
struct
lxc_list
*
network
,
pid_t
pid
)
{
int
err
;
int
ret
;
char
ifname
[
IFNAMSIZ
];
struct
lxc_list
*
iterator
;
...
...
@@ -2402,16 +2425,17 @@ int lxc_network_move_created_netdev_priv(const char *lxcpath, char *lxcname,
return
-
1
;
}
err
=
lxc_netdev_move_by_name
(
ifname
,
pid
,
NULL
);
if
(
err
)
{
ret
=
lxc_netdev_move_by_name
(
ifname
,
pid
,
NULL
);
if
(
ret
)
{
ERROR
(
"Failed to move network device
\"
%s
\"
to "
"network namespace %d: %s"
,
ifname
,
pid
,
strerror
(
-
err
));
strerror
(
-
ret
));
return
-
1
;
}
DEBUG
(
"Moved network device
\"
%s
\"
/
\"
%s
\"
to network namespace "
"of %d:"
,
ifname
,
netdev
->
name
?
netdev
->
name
:
"(null)"
,
"of %d"
,
ifname
,
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
,
pid
);
}
...
...
@@ -2459,7 +2483,7 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
struct
lxc_list
*
network
=
&
handler
->
conf
->
network
;
bool
deleted_all
=
true
;
if
(
am_unpriv
()
)
if
(
!
handler
->
am_root
)
return
true
;
lxc_list_for_each
(
iterator
,
network
)
{
...
...
@@ -2476,12 +2500,13 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
ret
=
lxc_netdev_rename_by_index
(
netdev
->
ifindex
,
netdev
->
link
);
if
(
ret
<
0
)
WARN
(
"Failed to rename interface with index %d "
"to its initial name
\"
%s
\"
"
,
netdev
->
ifindex
,
netdev
->
link
);
"
from
\"
%s
\"
to its initial name
\"
%s
\"
"
,
netdev
->
ifindex
,
netdev
->
name
,
netdev
->
link
);
else
TRACE
(
"Renamed interface with index %d to its "
"initial name
\"
%s
\"
"
,
netdev
->
ifindex
,
netdev
->
link
);
TRACE
(
"Renamed interface with index %d from "
"
\"
%s
\"
to its initial name
\"
%s
\"
"
,
netdev
->
ifindex
,
netdev
->
name
,
netdev
->
link
);
continue
;
}
...
...
@@ -2498,18 +2523,18 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
INFO
(
"Interface
\"
%s
\"
with index %d already "
"deleted or existing in different network "
"namespace"
,
netdev
->
name
?
netdev
->
name
:
"(null)"
,
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
,
netdev
->
ifindex
);
}
else
if
(
ret
<
0
)
{
deleted_all
=
false
;
WARN
(
"Failed to remove interface
\"
%s
\"
with "
"index %d: %s"
,
netdev
->
name
?
netdev
->
name
:
"(null)"
,
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
,
netdev
->
ifindex
,
strerror
(
-
ret
));
continue
;
}
INFO
(
"Removed interface
\"
%s
\"
with index %d"
,
netdev
->
name
?
netdev
->
name
:
"(null)"
,
netdev
->
name
[
0
]
!=
'\0'
?
netdev
->
name
:
"(null)"
,
netdev
->
ifindex
);
if
(
netdev
->
type
!=
LXC_NET_VETH
)
...
...
@@ -2518,11 +2543,11 @@ bool lxc_delete_network_priv(struct lxc_handler *handler)
/* Explicitly delete host veth device to prevent lingering
* devices. We had issues in LXD around this.
*/
if
(
netdev
->
priv
.
veth_attr
.
pair
)
if
(
netdev
->
priv
.
veth_attr
.
pair
[
0
]
!=
'\0'
)
hostveth
=
netdev
->
priv
.
veth_attr
.
pair
;
else
hostveth
=
netdev
->
priv
.
veth_attr
.
veth1
;
if
(
*
hostveth
==
'\0'
)
if
(
hostveth
[
0
]
==
'\0'
)
continue
;
ret
=
lxc_netdev_delete_by_name
(
hostveth
);
...
...
@@ -2577,51 +2602,69 @@ int lxc_requests_empty_network(struct lxc_handler *handler)
}
/* try to move physical nics to the init netns */
void
lxc_restore_phys_nics_to_netns
(
int
netnsfd
,
struct
lxc_conf
*
conf
)
int
lxc_restore_phys_nics_to_netns
(
struct
lxc_handler
*
handler
)
{
int
ret
;
int
i
,
oldfd
;
int
oldfd
;
char
ifname
[
IFNAMSIZ
];
struct
lxc_list
*
iterator
;
int
netnsfd
=
handler
->
netnsfd
;
struct
lxc_conf
*
conf
=
handler
->
conf
;
if
(
netnsfd
<
0
||
conf
->
num_savednics
==
0
)
return
;
/* We need CAP_NET_ADMIN in the parent namespace in order to setns() to
* the parent network namespace. We won't have this capability if we are
* unprivileged.
*/
if
(
!
handler
->
am_root
)
return
0
;
INFO
(
"Trying to restore network device names in original namespace for "
"%d network devices"
,
conf
->
num_savednics
);
TRACE
(
"Moving physical network devices back to parent network namespace"
);
oldfd
=
lxc_preserve_ns
(
getpid
(),
"net"
);
if
(
oldfd
<
0
)
{
SYSERROR
(
"Failed to preserve network namespace"
);
return
;
return
-
1
;
}
ret
=
setns
(
netnsfd
,
0
);
ret
=
setns
(
netnsfd
,
CLONE_NEWNET
);
if
(
ret
<
0
)
{
SYSERROR
(
"Failed to enter network namespace"
);
close
(
oldfd
);
return
;
return
-
1
;
}
for
(
i
=
0
;
i
<
conf
->
num_savednics
;
i
++
)
{
struct
saved_nic
*
s
=
&
conf
->
saved_nics
[
i
]
;
lxc_list_for_each
(
iterator
,
&
conf
->
network
)
{
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
/* retrieve the name of the interface */
if
(
!
if_indextoname
(
s
->
ifindex
,
ifname
))
{
if
(
netdev
->
type
!=
LXC_NET_PHYS
)
continue
;
/* Retrieve the name of the interface in the container's network
* namespace.
*/
if
(
!
if_indextoname
(
netdev
->
ifindex
,
ifname
))
{
WARN
(
"No interface corresponding to ifindex %d"
,
s
->
ifindex
);
netdev
->
ifindex
);
continue
;
}
if
(
lxc_netdev_move_by_name
(
ifname
,
1
,
s
->
orig_name
))
ret
=
lxc_netdev_move_by_name
(
ifname
,
1
,
netdev
->
link
);
if
(
ret
<
0
)
WARN
(
"Error moving network device
\"
%s
\"
back to "
"network namespace"
,
ifname
);
free
(
s
->
orig_name
);
else
TRACE
(
"Moved network device
\"
%s
\"
back to network "
"namespace"
,
ifname
);
}
conf
->
num_savednics
=
0
;
ret
=
setns
(
oldfd
,
0
);
if
(
ret
<
0
)
SYSERROR
(
"Failed to enter network namespace"
);
ret
=
setns
(
oldfd
,
CLONE_NEWNET
);
close
(
oldfd
);
if
(
ret
<
0
)
{
SYSERROR
(
"Failed to enter network namespace"
);
return
-
1
;
}
return
0
;
}
static
int
setup_hw_addr
(
char
*
hwaddr
,
const
char
*
ifname
)
...
...
@@ -2726,8 +2769,7 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
if
(
netdev
->
type
!=
LXC_NET_VETH
)
{
net_type_name
=
lxc_net_type_to_str
(
netdev
->
type
);
ERROR
(
"%s networks are not supported for containers "
"not setup up by privileged users"
,
net_type_name
);
"not setup up by privileged users"
,
net_type_name
);
return
-
1
;
}
...
...
@@ -2755,9 +2797,12 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
* When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
* netlink will replace the format specifier with an appropriate index.
*/
if
(
!
netdev
->
name
)
netdev
->
name
=
netdev
->
type
==
LXC_NET_PHYS
?
netdev
->
link
:
"eth%d"
;
if
(
netdev
->
name
[
0
]
==
'\0'
)
{
if
(
netdev
->
type
==
LXC_NET_PHYS
)
strcpy
(
netdev
->
name
,
netdev
->
link
);
else
strcpy
(
netdev
->
name
,
"eth%d"
);
}
/* rename the interface name */
if
(
strcmp
(
ifname
,
netdev
->
name
)
!=
0
)
{
...
...
@@ -2778,6 +2823,12 @@ static int lxc_setup_netdev_in_child_namespaces(struct lxc_netdev *netdev)
return
-
1
;
}
/* Now update the recorded name of the network device to reflect the
* name of the network device in the child's network namespace. We will
* later on send this information back to the parent.
*/
strcpy
(
netdev
->
name
,
current_ifname
);
/* set a mac address */
if
(
netdev
->
hwaddr
)
{
if
(
setup_hw_addr
(
netdev
->
hwaddr
,
current_ifname
))
{
...
...
@@ -2934,3 +2985,134 @@ int lxc_setup_network_in_child_namespaces(const struct lxc_conf *conf,
return
0
;
}
int
lxc_network_send_veth_names_to_child
(
struct
lxc_handler
*
handler
)
{
struct
lxc_list
*
iterator
;
struct
lxc_list
*
network
=
&
handler
->
conf
->
network
;
int
data_sock
=
handler
->
data_sock
[
0
];
if
(
handler
->
am_root
)
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
int
ret
;
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_VETH
)
continue
;
ret
=
send
(
data_sock
,
netdev
->
name
,
IFNAMSIZ
,
0
);
if
(
ret
<
0
)
{
close
(
handler
->
data_sock
[
0
]);
close
(
handler
->
data_sock
[
1
]);
return
-
1
;
}
else
{
TRACE
(
"Sent network device name
\"
%s
\"
to child"
,
netdev
->
name
);
}
}
return
0
;
}
int
lxc_network_recv_veth_names_from_parent
(
struct
lxc_handler
*
handler
)
{
struct
lxc_list
*
iterator
;
struct
lxc_list
*
network
=
&
handler
->
conf
->
network
;
int
data_sock
=
handler
->
data_sock
[
1
];
if
(
handler
->
am_root
)
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
int
ret
;
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_VETH
)
continue
;
ret
=
recv
(
data_sock
,
netdev
->
name
,
IFNAMSIZ
,
0
);
if
(
ret
<
0
)
{
close
(
handler
->
data_sock
[
0
]);
close
(
handler
->
data_sock
[
1
]);
return
-
1
;
}
else
{
TRACE
(
"Received network device name
\"
%s
\"
from parent"
,
netdev
->
name
);
}
}
return
0
;
}
int
lxc_network_send_name_and_ifindex_to_parent
(
struct
lxc_handler
*
handler
)
{
struct
lxc_list
*
iterator
,
*
network
;
int
data_sock
=
handler
->
data_sock
[
0
];
if
(
!
handler
->
am_root
)
return
0
;
network
=
&
handler
->
conf
->
network
;
lxc_list_for_each
(
iterator
,
network
)
{
int
ret
;
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
/* Send network device name in the child's namespace to parent. */
ret
=
send
(
data_sock
,
netdev
->
name
,
IFNAMSIZ
,
0
);
if
(
ret
<
0
)
goto
on_error
;
/* Send network device ifindex in the child's namespace to
* parent.
*/
ret
=
send
(
data_sock
,
&
netdev
->
ifindex
,
sizeof
(
netdev
->
ifindex
),
0
);
if
(
ret
<
0
)
goto
on_error
;
}
TRACE
(
"Sent network device names and ifindeces to parent"
);
return
0
;
on_error:
close
(
handler
->
data_sock
[
0
]);
close
(
handler
->
data_sock
[
1
]);
return
-
1
;
}
int
lxc_network_recv_name_and_ifindex_from_child
(
struct
lxc_handler
*
handler
)
{
struct
lxc_list
*
iterator
,
*
network
;
int
data_sock
=
handler
->
data_sock
[
1
];
if
(
!
handler
->
am_root
)
return
0
;
network
=
&
handler
->
conf
->
network
;
lxc_list_for_each
(
iterator
,
network
)
{
int
ret
;
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
/* Receive network device name in the child's namespace to
* parent.
*/
ret
=
recv
(
data_sock
,
netdev
->
name
,
IFNAMSIZ
,
0
);
if
(
ret
<
0
)
goto
on_error
;
/* Receive network device ifindex in the child's namespace to
* parent.
*/
ret
=
recv
(
data_sock
,
&
netdev
->
ifindex
,
sizeof
(
netdev
->
ifindex
),
0
);
if
(
ret
<
0
)
goto
on_error
;
}
return
0
;
on_error:
close
(
handler
->
data_sock
[
0
]);
close
(
handler
->
data_sock
[
1
]);
return
-
1
;
}
src/lxc/network.h
View file @
36259ede
...
...
@@ -91,7 +91,7 @@ struct lxc_route6 {
* @ifindex : Ifindex of the network device.
*/
struct
ifla_veth
{
char
*
pair
;
char
pair
[
IFNAMSIZ
]
;
char
veth1
[
IFNAMSIZ
];
int
ifindex
;
};
...
...
@@ -107,10 +107,19 @@ struct ifla_macvlan {
int
mode
;
/* private, vepa, bridge, passthru */
};
/* Contains information about the physical network device as seen from the host.
* @ifindex : The ifindex of the physical network device in the host's network
* namespace.
*/
struct
ifla_phys
{
int
ifindex
;
};
union
netdev_p
{
struct
ifla_macvlan
macvlan_attr
;
struct
ifla_phys
phys_attr
;
struct
ifla_veth
veth_attr
;
struct
ifla_vlan
vlan_attr
;
struct
ifla_macvlan
macvlan_attr
;
};
/*
...
...
@@ -151,8 +160,8 @@ struct lxc_netdev {
int
ifindex
;
int
type
;
int
flags
;
char
*
link
;
char
*
name
;
char
link
[
IFNAMSIZ
]
;
char
name
[
IFNAMSIZ
]
;
char
*
hwaddr
;
char
*
mtu
;
union
netdev_p
priv
;
...
...
@@ -166,16 +175,11 @@ struct lxc_netdev {
char
*
downscript
;
};
struct
saved_nic
{
int
ifindex
;
char
*
orig_name
;
};
/* Convert a string mac address to a socket structure. */
extern
int
lxc_convert_mac
(
char
*
macaddr
,
struct
sockaddr
*
sockaddr
);
/* Move a device between namespaces. */
extern
int
lxc_netdev_move_by_index
(
int
ifindex
,
pid_t
pid
,
const
char
*
ifname
);
extern
int
lxc_netdev_move_by_index
(
int
ifindex
,
pid_t
pid
,
const
char
*
ifname
);
extern
int
lxc_netdev_move_by_name
(
const
char
*
ifname
,
pid_t
pid
,
const
char
*
newname
);
...
...
@@ -252,7 +256,7 @@ extern int lxc_neigh_proxy_off(const char *name, int family);
/* Generate a new unique network interface name.
* Allocated memory must be freed by caller.
*/
extern
char
*
lxc_mkifname
(
c
onst
c
har
*
template
);
extern
char
*
lxc_mkifname
(
char
*
template
);
extern
const
char
*
lxc_net_type_to_str
(
int
type
);
extern
int
setup_private_host_hw_addr
(
char
*
veth1
);
...
...
@@ -268,8 +272,12 @@ extern int lxc_find_gateway_addresses(struct lxc_handler *handler);
extern
int
lxc_create_network_unpriv
(
const
char
*
lxcpath
,
char
*
lxcname
,
struct
lxc_list
*
network
,
pid_t
pid
);
extern
int
lxc_requests_empty_network
(
struct
lxc_handler
*
handler
);
extern
void
lxc_restore_phys_nics_to_netns
(
int
netnsfd
,
struct
lxc_conf
*
conf
);
extern
int
lxc_restore_phys_nics_to_netns
(
struct
lxc_handler
*
handler
);
extern
int
lxc_setup_network_in_child_namespaces
(
const
struct
lxc_conf
*
conf
,
struct
lxc_list
*
network
);
extern
int
lxc_network_send_veth_names_to_child
(
struct
lxc_handler
*
handler
);
extern
int
lxc_network_recv_veth_names_from_parent
(
struct
lxc_handler
*
handler
);
extern
int
lxc_network_send_name_and_ifindex_to_parent
(
struct
lxc_handler
*
handler
);
extern
int
lxc_network_recv_name_and_ifindex_from_child
(
struct
lxc_handler
*
handler
);
#endif
/* __LXC_NETWORK_H */
src/lxc/start.c
View file @
36259ede
...
...
@@ -89,7 +89,7 @@
lxc_log_define
(
lxc_start
,
lxc
);
extern
void
mod_all_rdeps
(
struct
lxc_container
*
c
,
bool
inc
);
static
bool
do_destroy_container
(
struct
lxc_
conf
*
conf
);
static
bool
do_destroy_container
(
struct
lxc_
handler
*
handler
);
static
int
lxc_rmdir_onedev_wrapper
(
void
*
data
);
static
void
lxc_destroy_container_on_signal
(
struct
lxc_handler
*
handler
,
const
char
*
name
);
...
...
@@ -535,7 +535,12 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
memset
(
handler
,
0
,
sizeof
(
*
handler
));
handler
->
ttysock
[
0
]
=
handler
->
ttysock
[
1
]
=
-
1
;
/* Note that am_unpriv() checks the effective uid. We probably don't
* care if we are real root only if we are running as root so this
* should be fine.
*/
handler
->
am_root
=
!
am_unpriv
();
handler
->
data_sock
[
0
]
=
handler
->
data_sock
[
1
]
=
-
1
;
handler
->
conf
=
conf
;
handler
->
lxcpath
=
lxcpath
;
handler
->
pinfd
=
-
1
;
...
...
@@ -759,9 +764,9 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
free
(
cur
);
}
if
(
handler
->
tty
sock
[
0
]
!=
-
1
)
{
close
(
handler
->
tty
sock
[
0
]);
close
(
handler
->
tty
sock
[
1
]);
if
(
handler
->
data_
sock
[
0
]
!=
-
1
)
{
close
(
handler
->
data_
sock
[
0
]);
close
(
handler
->
data_
sock
[
1
]);
}
if
(
handler
->
conf
->
ephemeral
==
1
&&
handler
->
conf
->
reboot
!=
1
)
...
...
@@ -783,55 +788,6 @@ void lxc_abort(const char *name, struct lxc_handler *handler)
}
}
/* netpipe is used in the unprivileged case to transfer the ifindexes from
* parent to child
*/
static
int
netpipe
=
-
1
;
static
inline
int
count_veths
(
struct
lxc_list
*
network
)
{
struct
lxc_list
*
iterator
;
struct
lxc_netdev
*
netdev
;
int
count
=
0
;
lxc_list_for_each
(
iterator
,
network
)
{
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_VETH
)
continue
;
count
++
;
}
return
count
;
}
static
int
read_unpriv_netifindex
(
struct
lxc_list
*
network
)
{
struct
lxc_list
*
iterator
;
struct
lxc_netdev
*
netdev
;
if
(
netpipe
==
-
1
)
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_VETH
)
continue
;
netdev
->
name
=
malloc
(
IFNAMSIZ
);
if
(
!
netdev
->
name
)
{
ERROR
(
"Out of memory."
);
close
(
netpipe
);
return
-
1
;
}
if
(
read
(
netpipe
,
netdev
->
name
,
IFNAMSIZ
)
!=
IFNAMSIZ
)
{
close
(
netpipe
);
return
-
1
;
}
}
close
(
netpipe
);
return
0
;
}
static
int
do_start
(
void
*
data
)
{
struct
lxc_list
*
iterator
;
...
...
@@ -884,8 +840,10 @@ static int do_start(void *data)
if
(
lxc_sync_barrier_parent
(
handler
,
LXC_SYNC_CONFIGURE
))
return
-
1
;
if
(
read_unpriv_netifindex
(
&
handler
->
conf
->
network
)
<
0
)
if
(
lxc_network_recv_veth_names_from_parent
(
handler
)
<
0
)
{
ERROR
(
"Failed to receive veth names from parent"
);
goto
out_warn_father
;
}
/* If we are in a new user namespace, become root there to have
* privilege over our namespace.
...
...
@@ -1099,46 +1057,14 @@ out_error:
return
-
1
;
}
static
int
save_phys_nics
(
struct
lxc_conf
*
conf
)
{
struct
lxc_list
*
iterator
;
int
am_root
=
(
getuid
()
==
0
);
if
(
!
am_root
)
return
0
;
lxc_list_for_each
(
iterator
,
&
conf
->
network
)
{
struct
lxc_netdev
*
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_PHYS
)
continue
;
conf
->
saved_nics
=
realloc
(
conf
->
saved_nics
,
(
conf
->
num_savednics
+
1
)
*
sizeof
(
struct
saved_nic
));
if
(
!
conf
->
saved_nics
)
return
-
1
;
conf
->
saved_nics
[
conf
->
num_savednics
].
ifindex
=
netdev
->
ifindex
;
conf
->
saved_nics
[
conf
->
num_savednics
].
orig_name
=
strdup
(
netdev
->
link
);
if
(
!
conf
->
saved_nics
[
conf
->
num_savednics
].
orig_name
)
return
-
1
;
INFO
(
"Stored saved_nic #%d idx %d name %s."
,
conf
->
num_savednics
,
conf
->
saved_nics
[
conf
->
num_savednics
].
ifindex
,
conf
->
saved_nics
[
conf
->
num_savednics
].
orig_name
);
conf
->
num_savednics
++
;
}
return
0
;
}
static
int
lxc_recv_ttys_from_child
(
struct
lxc_handler
*
handler
)
{
int
i
;
int
*
ttyfds
;
struct
lxc_pty_info
*
pty_info
;
int
ret
=
-
1
;
int
sock
=
handler
->
tty
sock
[
1
];
int
sock
=
handler
->
data_
sock
[
1
];
struct
lxc_conf
*
conf
=
handler
->
conf
;
struct
lxc_tty_info
*
tty_info
=
&
conf
->
tty_info
;
size_t
num_ttyfds
=
(
2
*
conf
->
tty
);
if
(
!
conf
->
tty
)
return
0
;
...
...
@@ -1147,29 +1073,27 @@ static int lxc_recv_ttys_from_child(struct lxc_handler *handler)
if
(
!
tty_info
->
pty_info
)
return
-
1
;
ttyfds
=
malloc
(
num_ttyfds
*
sizeof
(
int
));
if
(
!
ttyfds
)
return
-
1
;
for
(
i
=
0
;
i
<
conf
->
tty
;
i
++
)
{
int
ttyfds
[
2
];
ret
=
lxc_abstract_unix_recv_fds
(
sock
,
ttyfds
,
num_ttyfds
,
NULL
,
0
);
for
(
i
=
0
;
(
ret
>=
0
&&
*
ttyfds
!=
-
1
)
&&
(
i
<
num_ttyfds
);
i
++
)
{
pty_info
=
&
tty_info
->
pty_info
[
i
/
2
];
ret
=
lxc_abstract_unix_recv_fds
(
sock
,
ttyfds
,
2
,
NULL
,
0
);
if
(
ret
<
0
)
break
;
pty_info
=
&
tty_info
->
pty_info
[
i
];
pty_info
->
busy
=
0
;
pty_info
->
slave
=
ttyfds
[
i
++
];
pty_info
->
master
=
ttyfds
[
i
];
TRACE
(
"
r
eceived pty with master fd %d and slave fd %d from "
pty_info
->
master
=
ttyfds
[
0
];
pty_info
->
slave
=
ttyfds
[
1
];
TRACE
(
"
R
eceived pty with master fd %d and slave fd %d from "
"parent"
,
pty_info
->
master
,
pty_info
->
slave
);
}
tty_info
->
nbtty
=
conf
->
tty
;
free
(
ttyfds
);
if
(
ret
<
0
)
ERROR
(
"
f
ailed to receive %d ttys from child: %s"
,
conf
->
tty
,
ERROR
(
"
F
ailed to receive %d ttys from child: %s"
,
conf
->
tty
,
strerror
(
errno
));
else
TRACE
(
"received %d ttys from child"
,
conf
->
tty
);
TRACE
(
"Received %d ttys from child"
,
conf
->
tty
);
tty_info
->
nbtty
=
conf
->
tty
;
return
ret
;
}
...
...
@@ -1208,16 +1132,14 @@ void resolve_clone_flags(struct lxc_handler *handler)
*/
static
int
lxc_spawn
(
struct
lxc_handler
*
handler
)
{
int
failed_before_rename
=
0
;
int
i
,
flags
,
ret
;
const
char
*
name
=
handler
->
name
;
bool
cgroups_connected
=
false
;
int
saved_ns_fd
[
LXC_NS_MAX
];
int
preserve_mask
=
0
,
i
,
flags
;
int
netpipepair
[
2
],
nveths
;
bool
wants_to_map_ids
;
int
saved_ns_fd
[
LXC_NS_MAX
];
struct
lxc_list
*
id_map
;
int
failed_before_rename
=
0
,
preserve_mask
=
0
;
bool
cgroups_connected
=
false
;
netpipe
=
-
1
;
id_map
=
&
handler
->
conf
->
id_map
;
wants_to_map_ids
=
!
lxc_list_empty
(
id_map
);
...
...
@@ -1228,7 +1150,9 @@ static int lxc_spawn(struct lxc_handler *handler)
if
(
lxc_sync_init
(
handler
))
return
-
1
;
if
(
socketpair
(
AF_UNIX
,
SOCK_DGRAM
,
0
,
handler
->
ttysock
)
<
0
)
{
ret
=
socketpair
(
AF_UNIX
,
SOCK_STREAM
|
SOCK_CLOEXEC
,
0
,
handler
->
data_sock
);
if
(
ret
<
0
)
{
lxc_sync_fini
(
handler
);
return
-
1
;
}
...
...
@@ -1258,11 +1182,6 @@ static int lxc_spawn(struct lxc_handler *handler)
return
-
1
;
}
}
if
(
save_phys_nics
(
handler
->
conf
))
{
ERROR
(
"Failed to save physical nic info."
);
goto
out_abort
;
}
}
if
(
!
cgroup_init
(
handler
))
{
...
...
@@ -1293,15 +1212,6 @@ static int lxc_spawn(struct lxc_handler *handler)
if
(
attach_ns
(
handler
->
conf
->
inherit_ns_fd
)
<
0
)
goto
out_delete_net
;
if
(
am_unpriv
()
&&
(
nveths
=
count_veths
(
&
handler
->
conf
->
network
)))
{
if
(
pipe
(
netpipepair
)
<
0
)
{
SYSERROR
(
"Failed to create pipe."
);
goto
out_delete_net
;
}
/* Store netpipe in the global var for do_start's use. */
netpipe
=
netpipepair
[
0
];
}
/* Create a process in a new set of namespaces. */
flags
=
handler
->
clone_flags
;
if
(
handler
->
clone_flags
&
CLONE_NEWUSER
)
{
...
...
@@ -1391,29 +1301,11 @@ static int lxc_spawn(struct lxc_handler *handler)
ERROR
(
"Failed to create the configured network."
);
goto
out_delete_net
;
}
/* Now all networks are created and moved into place. The
* corresponding structs have now all been filled. So log them
* for debugging purposes.
*/
lxc_log_configured_netdevs
(
handler
->
conf
);
}
if
(
netpipe
!=
-
1
)
{
struct
lxc_list
*
iterator
;
struct
lxc_netdev
*
netdev
;
close
(
netpipe
);
lxc_list_for_each
(
iterator
,
&
handler
->
conf
->
network
)
{
netdev
=
iterator
->
elem
;
if
(
netdev
->
type
!=
LXC_NET_VETH
)
continue
;
if
(
write
(
netpipepair
[
1
],
netdev
->
name
,
IFNAMSIZ
)
!=
IFNAMSIZ
)
{
ERROR
(
"Error writing veth name to container."
);
goto
out_delete_net
;
}
}
close
(
netpipepair
[
1
]);
if
(
lxc_network_send_veth_names_to_child
(
handler
)
<
0
)
{
ERROR
(
"Failed to send veth names to child"
);
goto
out_delete_net
;
}
/* Tell the child to continue its initialization. We'll get
...
...
@@ -1448,6 +1340,19 @@ static int lxc_spawn(struct lxc_handler *handler)
if
(
lxc_sync_barrier_child
(
handler
,
LXC_SYNC_POST_CGROUP
))
return
-
1
;
if
(
lxc_network_recv_name_and_ifindex_from_child
(
handler
)
<
0
)
{
ERROR
(
"Failed to receive names and ifindices for network "
"devices from child"
);
goto
out_delete_net
;
}
/* Now all networks are created, network devices are moved into place,
* and the correct names and ifindeces in the respective namespaces have
* been recorded. The corresponding structs have now all been filled. So
* log them for debugging purposes.
*/
lxc_log_configured_netdevs
(
handler
->
conf
);
/* Read tty fds allocated by child. */
if
(
lxc_recv_ttys_from_child
(
handler
)
<
0
)
{
ERROR
(
"Failed to receive tty info from child process."
);
...
...
@@ -1575,8 +1480,10 @@ int __lxc_start(const char *name, struct lxc_handler *handler,
}
}
DEBUG
(
"Pushing physical nics back to host namespace"
);
lxc_restore_phys_nics_to_netns
(
handler
->
netnsfd
,
handler
->
conf
);
err
=
lxc_restore_phys_nics_to_netns
(
handler
);
if
(
err
<
0
)
ERROR
(
"Failed to move physical network devices back to parent "
"network namespace"
);
if
(
handler
->
pinfd
>=
0
)
{
close
(
handler
->
pinfd
);
...
...
@@ -1657,7 +1564,7 @@ static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
int
ret
=
0
;
struct
lxc_container
*
c
;
if
(
handler
->
conf
->
rootfs
.
path
&&
handler
->
conf
->
rootfs
.
mount
)
{
bret
=
do_destroy_container
(
handler
->
conf
);
bret
=
do_destroy_container
(
handler
);
if
(
!
bret
)
{
ERROR
(
"Error destroying rootfs for container
\"
%s
\"
."
,
name
);
return
;
...
...
@@ -1683,7 +1590,7 @@ static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
}
}
if
(
am_unpriv
()
)
if
(
!
handler
->
am_root
)
ret
=
userns_exec_1
(
handler
->
conf
,
lxc_rmdir_onedev_wrapper
,
destroy
,
"lxc_rmdir_onedev_wrapper"
);
else
...
...
@@ -1702,14 +1609,14 @@ static int lxc_rmdir_onedev_wrapper(void *data)
return
lxc_rmdir_onedev
(
arg
,
NULL
);
}
static
bool
do_destroy_container
(
struct
lxc_
conf
*
conf
)
{
if
(
am_unpriv
()
)
{
if
(
userns_exec_1
(
conf
,
storage_destroy_wrapper
,
conf
,
"storage_destroy_wrapper"
)
<
0
)
static
bool
do_destroy_container
(
struct
lxc_
handler
*
handler
)
{
if
(
!
handler
->
am_root
)
{
if
(
userns_exec_1
(
handler
->
conf
,
storage_destroy_wrapper
,
handler
->
conf
,
"storage_destroy_wrapper"
)
<
0
)
return
false
;
return
true
;
}
return
storage_destroy
(
conf
);
return
storage_destroy
(
handler
->
conf
);
}
src/lxc/start.h
View file @
36259ede
...
...
@@ -35,6 +35,7 @@
#include "namespace.h"
struct
lxc_handler
{
bool
am_root
;
pid_t
pid
;
char
*
name
;
lxc_state_t
state
;
...
...
@@ -49,8 +50,10 @@ struct lxc_handler {
const
char
*
lxcpath
;
void
*
cgroup_data
;
/* socketpair for child->parent tty fd passing */
int
ttysock
[
2
];
/* Abstract unix domain SOCK_DGRAM socketpair to pass arbitrary data
* between child and parent.
*/
int
data_sock
[
2
];
/* indicates whether should we close std{in,out,err} on start */
bool
backgrounded
;
...
...
src/lxc/utils.c
View file @
36259ede
...
...
@@ -2409,3 +2409,24 @@ bool has_fs_type(const char *path, fs_type_magic magic_val)
return
has_type
;
}
bool
lxc_nic_exists
(
char
*
nic
)
{
#define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1
char
path
[
__LXC_SYS_CLASS_NET_LEN
];
int
ret
;
struct
stat
sb
;
if
(
!
strcmp
(
nic
,
"none"
))
return
true
;
ret
=
snprintf
(
path
,
__LXC_SYS_CLASS_NET_LEN
,
"/sys/class/net/%s"
,
nic
);
if
(
ret
<
0
||
(
size_t
)
ret
>=
__LXC_SYS_CLASS_NET_LEN
)
return
false
;
ret
=
stat
(
path
,
&
sb
);
if
(
ret
<
0
)
return
false
;
return
true
;
}
src/lxc/utils.h
View file @
36259ede
...
...
@@ -402,5 +402,6 @@ extern void *must_realloc(void *orig, size_t sz);
typedef
__typeof__
(((
struct
statfs
*
)
NULL
)
->
f_type
)
fs_type_magic
;
extern
bool
has_fs_type
(
const
char
*
path
,
fs_type_magic
magic_val
);
extern
bool
is_fs_type
(
const
struct
statfs
*
fs
,
fs_type_magic
magic_val
);
extern
bool
lxc_nic_exists
(
char
*
nic
);
#endif
/* __LXC_UTILS_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment