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
a38fa3d1
Unverified
Commit
a38fa3d1
authored
Apr 05, 2019
by
Stéphane Graber
Committed by
GitHub
Apr 05, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2924 from brauner/2019-04-05/lxc_user_nice_update
lxc-user-nic: update
parents
4f34c6f9
ff63fd78
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
115 additions
and
62 deletions
+115
-62
lxc-user-nic.sgml.in
doc/lxc-user-nic.sgml.in
+57
-4
lxc_user_nic.c
src/lxc/cmd/lxc_user_nic.c
+58
-58
No files found.
doc/lxc-user-nic.sgml.in
View file @
a38fa3d1
...
@@ -42,17 +42,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -42,17 +42,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<refname>lxc-user-nic</refname>
<refname>lxc-user-nic</refname>
<refpurpose>
<refpurpose>
Create and attach a nic to another network namespace.
Manage nics in another network namespace
</refpurpose>
</refpurpose>
</refnamediv>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdiv>
<cmdsynopsis>
<cmdsynopsis>
<command>lxc-user-nic</command>
<command>lxc-user-nic</command>
<command>create</command>
<arg choice="req"><replaceable>lxcpath</replaceable></arg>
<arg choice="req"><replaceable>name</replaceable></arg>
<arg choice="req"><replaceable>pid</replaceable></arg>
<arg choice="req"><replaceable>pid</replaceable></arg>
<arg choice="req"><replaceable>type</replaceable></arg>
<arg choice="req"><replaceable>type</replaceable></arg>
<arg choice="req"><replaceable>bridge</replaceable></arg>
<arg choice="req"><replaceable>bridge</replaceable></arg>
<arg choice="opt"><replaceable>nicname</replaceable></arg>
<arg choice="req"><replaceable>container nicname</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>lxc-user-nic</command>
<command>delete</command>
<arg choice="req"><replaceable>lxcpath</replaceable></arg>
<arg choice="req"><replaceable>name</replaceable></arg>
<arg choice="req"><replaceable>path to network namespace</replaceable></arg>
<arg choice="req"><replaceable>type</replaceable></arg>
<arg choice="req"><replaceable>bridge</replaceable></arg>
<arg choice="req"><replaceable>container nicname</replaceable></arg>
</cmdsynopsis>
</cmdsynopsis>
</refsynopsisdiv>
</refsynopsisdiv>
...
@@ -61,7 +75,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -61,7 +75,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<para>
<para>
<command>lxc-user-nic</command> is a setuid-root program with which
<command>lxc-user-nic</command> is a setuid-root program with which
unprivileged users may create network interfaces for use by a lxc container.
unprivileged users may manage network interfaces for use by a
lxc container.
</para>
</para>
<para>
<para>
It will consult the configuration file <filename>@LXC_USERNIC_CONF@</filename>
It will consult the configuration file <filename>@LXC_USERNIC_CONF@</filename>
...
@@ -71,6 +86,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -71,6 +86,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<filename>@LXC_USERNIC_DB@</filename>. It ensures that the calling
<filename>@LXC_USERNIC_DB@</filename>. It ensures that the calling
user is privileged over the network namespace to which the interface
user is privileged over the network namespace to which the interface
will be attached.
will be attached.
<command>lxc-user-nic</command> also allows to delete network devices.
Currently only ovs ports can be deleted.
</para>
</para>
</refsect1>
</refsect1>
...
@@ -80,6 +97,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -80,6 +97,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<title>Options</title>
<title>Options</title>
<variablelist>
<variablelist>
<varlistentry>
<term>
<option><replaceable>lxcpath</replaceable></option>
</term>
<listitem>
<para>
The path of the container. This is currently not used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option><replaceable>name</replaceable></option>
</term>
<listitem>
<para>
The name of the container. This is currently not used.
</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term>
<term>
...
@@ -122,7 +160,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -122,7 +160,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<varlistentry>
<varlistentry>
<term>
<term>
<option><replaceable>nicname</replaceable></option>
<option><replaceable>
container
nicname</replaceable></option>
</term>
</term>
<listitem>
<listitem>
<para>
<para>
...
@@ -132,6 +170,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -132,6 +170,19 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</listitem>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
<term>
<option><replaceable>path to network namespace</replaceable></option>
</term>
<listitem>
<para>
A path to open to get a file descriptor for the target
network namespace.
This is only relevant when an veth device is deleted.
</para>
</listitem>
</varlistentry>
</variablelist>
</variablelist>
</refsect1>
</refsect1>
...
@@ -159,6 +210,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
...
@@ -159,6 +210,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<refsect1>
<refsect1>
<title>Author</title>
<title>Author</title>
<para>Christian Brauner <email>christian@brauner.io</email></para>
<para>Serge Hallyn <email>serge@hallyn.com</email></para>
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
<para>Daniel Lezcano <email>daniel.lezcano@free.fr</email></para>
</refsect1>
</refsect1>
...
...
src/lxc/cmd/lxc_user_nic.c
View file @
a38fa3d1
...
@@ -47,6 +47,7 @@
...
@@ -47,6 +47,7 @@
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
#include "compiler.h"
#include "config.h"
#include "config.h"
#include "log.h"
#include "log.h"
#include "memory_utils.h"
#include "memory_utils.h"
...
@@ -69,13 +70,17 @@
...
@@ -69,13 +70,17 @@
#define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__)
#define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__)
static
void
usage
(
char
*
me
,
bool
fail
)
__noreturn
static
void
usage
(
bool
fail
)
{
{
fprintf
(
stderr
,
"Usage: %s create {lxcpath} {name} {pid} {type} "
fprintf
(
stderr
,
"Description:
\n
"
);
"{bridge} {nicname}
\n
"
,
me
);
fprintf
(
stderr
,
" Manage nics in another network namespace
\n\n
"
);
fprintf
(
stderr
,
"Usage: %s delete {lxcpath} {name} "
"{/proc/<pid>/ns/net} {type} {bridge} {nicname}
\n
"
,
me
);
fprintf
(
stderr
,
"Usage:
\n
"
);
fprintf
(
stderr
,
"{nicname} is the name to use inside the container
\n
"
);
fprintf
(
stderr
,
" lxc-user-nic [command]
\n\n
"
);
fprintf
(
stderr
,
"Available Commands:
\n
"
);
fprintf
(
stderr
,
" create {lxcpath} {name} {pid} {type} {bridge} {container nicname}
\n
"
);
fprintf
(
stderr
,
" delete {lxcpath} {name} {/proc/<pid>/ns/net} {type} {bridge} {container nicname}
\n
"
);
if
(
fail
)
if
(
fail
)
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
...
@@ -83,9 +88,10 @@ static void usage(char *me, bool fail)
...
@@ -83,9 +88,10 @@ static void usage(char *me, bool fail)
_exit
(
EXIT_SUCCESS
);
_exit
(
EXIT_SUCCESS
);
}
}
static
int
open_and_lock
(
char
*
path
)
static
int
open_and_lock
(
c
onst
c
har
*
path
)
{
{
int
fd
,
ret
;
__do_close_prot_errno
int
fd
=
-
EBADF
;
int
ret
;
struct
flock
lk
;
struct
flock
lk
;
fd
=
open
(
path
,
O_RDWR
|
O_CREAT
,
S_IWUSR
|
S_IRUSR
);
fd
=
open
(
path
,
O_RDWR
|
O_CREAT
,
S_IWUSR
|
S_IRUSR
);
...
@@ -102,11 +108,10 @@ static int open_and_lock(char *path)
...
@@ -102,11 +108,10 @@ static int open_and_lock(char *path)
ret
=
fcntl
(
fd
,
F_SETLKW
,
&
lk
);
ret
=
fcntl
(
fd
,
F_SETLKW
,
&
lk
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to lock
\"
%s
\"\n
"
,
path
);
CMD_SYSERROR
(
"Failed to lock
\"
%s
\"\n
"
,
path
);
close
(
fd
);
return
-
1
;
return
-
1
;
}
}
return
fd
;
return
move_fd
(
fd
)
;
}
}
static
char
*
get_username
(
void
)
static
char
*
get_username
(
void
)
...
@@ -881,16 +886,15 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
...
@@ -881,16 +886,15 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
close
(
fd
);
close
(
fd
);
fd
=
-
1
;
fd
=
-
1
;
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to setns() to the network namespace of
"
CMD_SYSERROR
(
"Failed to setns() to the network namespace of
the container with PID %d
\n
"
,
"the container with PID %d
\n
"
,
pid
);
pid
);
goto
do_partial_cleanup
;
goto
do_partial_cleanup
;
}
}
ret
=
setresuid
(
ruid
,
ruid
,
0
);
ret
=
setresuid
(
ruid
,
ruid
,
0
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to drop privilege by setting effective "
CMD_SYSERROR
(
"Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to 0
\n
"
,
"user id and real user id to %d, and saved user "
ruid
);
"ID to 0
\n
"
,
ruid
);
/* It's ok to jump to do_full_cleanup here since setresuid()
/* It's ok to jump to do_full_cleanup here since setresuid()
* will succeed when trying to set real, effective, and saved to
* will succeed when trying to set real, effective, and saved to
* values they currently have.
* values they currently have.
...
@@ -936,18 +940,15 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
...
@@ -936,18 +940,15 @@ static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
do_full_cleanup:
do_full_cleanup:
ret
=
setresuid
(
ruid
,
euid
,
suid
);
ret
=
setresuid
(
ruid
,
euid
,
suid
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to restore privilege by setting "
CMD_SYSERROR
(
"Failed to restore privilege by setting effective user id to %d, real user id to %d, and saved user ID to %d
\n
"
,
"effective user id to %d, real user id to %d, "
ruid
,
euid
,
suid
);
"and saved user ID to %d
\n
"
,
ruid
,
euid
,
suid
);
string_ret
=
NULL
;
string_ret
=
NULL
;
}
}
ret
=
setns
(
ofd
,
CLONE_NEWNET
);
ret
=
setns
(
ofd
,
CLONE_NEWNET
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to setns() to original network namespace "
CMD_SYSERROR
(
"Failed to setns() to original network namespace of PID %d
\n
"
,
ofd
);
"of PID %d
\n
"
,
ofd
);
string_ret
=
NULL
;
string_ret
=
NULL
;
}
}
...
@@ -981,9 +982,8 @@ static bool may_access_netns(int pid)
...
@@ -981,9 +982,8 @@ static bool may_access_netns(int pid)
ret
=
setresuid
(
ruid
,
ruid
,
euid
);
ret
=
setresuid
(
ruid
,
ruid
,
euid
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to drop privilege by setting effective "
CMD_SYSERROR
(
"Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to %d
\n
"
,
"user id and real user id to %d, and saved user "
ruid
,
euid
);
"ID to %d
\n
"
,
ruid
,
euid
);
return
false
;
return
false
;
}
}
...
@@ -1000,8 +1000,8 @@ static bool may_access_netns(int pid)
...
@@ -1000,8 +1000,8 @@ static bool may_access_netns(int pid)
ret
=
setresuid
(
ruid
,
euid
,
suid
);
ret
=
setresuid
(
ruid
,
euid
,
suid
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to restore user id to %d, real user id
"
CMD_SYSERROR
(
"Failed to restore user id to %d, real user id
to %d, and saved user ID to %d
\n
"
,
"to %d, and saved user ID to %d
\n
"
,
ruid
,
euid
,
suid
);
ruid
,
euid
,
suid
);
may_access
=
false
;
may_access
=
false
;
}
}
...
@@ -1018,8 +1018,10 @@ struct user_nic_args {
...
@@ -1018,8 +1018,10 @@ struct user_nic_args {
char
*
veth_name
;
char
*
veth_name
;
};
};
#define LXC_USERNIC_CREATE 0
enum
lxc_user_nic_command
{
#define LXC_USERNIC_DELETE 1
LXC_USERNIC_CREATE
=
0
,
LXC_USERNIC_DELETE
=
1
,
};
static
bool
is_privileged_over_netns
(
int
netns_fd
)
static
bool
is_privileged_over_netns
(
int
netns_fd
)
{
{
...
@@ -1050,9 +1052,8 @@ static bool is_privileged_over_netns(int netns_fd)
...
@@ -1050,9 +1052,8 @@ static bool is_privileged_over_netns(int netns_fd)
ret
=
setresuid
(
ruid
,
ruid
,
0
);
ret
=
setresuid
(
ruid
,
ruid
,
0
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to drop privilege by setting effective "
CMD_SYSERROR
(
"Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to 0
\n
"
,
"user id and real user id to %d, and saved user "
ruid
);
"ID to 0
\n
"
,
ruid
);
/* It's ok to jump to do_full_cleanup here since setresuid()
/* It's ok to jump to do_full_cleanup here since setresuid()
* will succeed when trying to set real, effective, and saved to
* will succeed when trying to set real, effective, and saved to
* values they currently have.
* values they currently have.
...
@@ -1072,16 +1073,15 @@ static bool is_privileged_over_netns(int netns_fd)
...
@@ -1072,16 +1073,15 @@ static bool is_privileged_over_netns(int netns_fd)
do_full_cleanup:
do_full_cleanup:
ret
=
setresuid
(
ruid
,
euid
,
suid
);
ret
=
setresuid
(
ruid
,
euid
,
suid
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to restore privilege by setting "
CMD_SYSERROR
(
"Failed to restore privilege by setting effective user id to %d, real user id to %d, and saved user ID to %d
\n
"
,
"effective user id to %d, real user id to %d, "
ruid
,
euid
,
suid
);
"and saved user ID to %d
\n
"
,
ruid
,
euid
,
suid
);
bret
=
false
;
bret
=
false
;
}
}
ret
=
setns
(
ofd
,
CLONE_NEWNET
);
ret
=
setns
(
ofd
,
CLONE_NEWNET
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
CMD_SYSERROR
(
"Failed to setns() to original network namespace
"
CMD_SYSERROR
(
"Failed to setns() to original network namespace
of PID %d
\n
"
,
"of PID %d
\n
"
,
ofd
);
ofd
);
bret
=
false
;
bret
=
false
;
}
}
...
@@ -1090,6 +1090,18 @@ do_partial_cleanup:
...
@@ -1090,6 +1090,18 @@ do_partial_cleanup:
return
bret
;
return
bret
;
}
}
static
inline
int
validate_args
(
const
struct
user_nic_args
*
args
,
int
argc
)
{
int
request
=
-
EINVAL
;
if
(
!
strcmp
(
args
->
cmd
,
"create"
))
request
=
LXC_USERNIC_CREATE
;
else
if
(
!
strcmp
(
args
->
cmd
,
"delete"
))
request
=
LXC_USERNIC_DELETE
;
return
request
;
}
int
main
(
int
argc
,
char
*
argv
[])
int
main
(
int
argc
,
char
*
argv
[])
{
{
__do_free
char
*
me
=
NULL
,
*
newname
=
NULL
,
*
nicname
=
NULL
;
__do_free
char
*
me
=
NULL
,
*
newname
=
NULL
,
*
nicname
=
NULL
;
...
@@ -1099,10 +1111,8 @@ int main(int argc, char *argv[])
...
@@ -1099,10 +1111,8 @@ int main(int argc, char *argv[])
char
*
cnic
=
NULL
;
char
*
cnic
=
NULL
;
struct
alloted_s
*
alloted
=
NULL
;
struct
alloted_s
*
alloted
=
NULL
;
if
(
argc
<
7
||
argc
>
8
)
{
if
(
argc
<
7
||
argc
>
8
)
usage
(
argv
[
0
],
true
);
usage
(
true
);
_exit
(
EXIT_FAILURE
);
}
memset
(
&
args
,
0
,
sizeof
(
struct
user_nic_args
));
memset
(
&
args
,
0
,
sizeof
(
struct
user_nic_args
));
...
@@ -1112,17 +1122,12 @@ int main(int argc, char *argv[])
...
@@ -1112,17 +1122,12 @@ int main(int argc, char *argv[])
args
.
pid
=
argv
[
4
];
args
.
pid
=
argv
[
4
];
args
.
type
=
argv
[
5
];
args
.
type
=
argv
[
5
];
args
.
link
=
argv
[
6
];
args
.
link
=
argv
[
6
];
if
(
argc
>
=
8
)
if
(
argc
=
=
8
)
args
.
veth_name
=
argv
[
7
];
args
.
veth_name
=
argv
[
7
];
if
(
!
strcmp
(
args
.
cmd
,
"create"
))
{
request
=
validate_args
(
&
args
,
argc
);
request
=
LXC_USERNIC_CREATE
;
if
(
request
<
0
)
}
else
if
(
!
strcmp
(
args
.
cmd
,
"delete"
))
{
usage
(
true
);
request
=
LXC_USERNIC_DELETE
;
}
else
{
usage
(
argv
[
0
],
true
);
_exit
(
EXIT_FAILURE
);
}
/* Set a sane env, because we are setuid-root. */
/* Set a sane env, because we are setuid-root. */
ret
=
clearenv
();
ret
=
clearenv
();
...
@@ -1218,8 +1223,7 @@ int main(int argc, char *argv[])
...
@@ -1218,8 +1223,7 @@ int main(int argc, char *argv[])
has_priv
=
is_privileged_over_netns
(
netns_fd
);
has_priv
=
is_privileged_over_netns
(
netns_fd
);
close
(
netns_fd
);
close
(
netns_fd
);
if
(
!
has_priv
)
{
if
(
!
has_priv
)
{
usernic_error
(
"%s"
,
"Process is not privileged over "
usernic_error
(
"%s"
,
"Process is not privileged over network namespace
\n
"
);
"network namespace
\n
"
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
}
}
...
@@ -1231,8 +1235,7 @@ int main(int argc, char *argv[])
...
@@ -1231,8 +1235,7 @@ int main(int argc, char *argv[])
bool
found_nicname
=
false
;
bool
found_nicname
=
false
;
if
(
!
is_ovs_bridge
(
args
.
link
))
{
if
(
!
is_ovs_bridge
(
args
.
link
))
{
usernic_error
(
"%s"
,
"Deletion of non ovs type network "
usernic_error
(
"%s"
,
"Deletion of non ovs type network devices not implemented
\n
"
);
"devices not implemented
\n
"
);
close
(
fd
);
close
(
fd
);
free_alloted
(
&
alloted
);
free_alloted
(
&
alloted
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
...
@@ -1251,16 +1254,13 @@ int main(int argc, char *argv[])
...
@@ -1251,16 +1254,13 @@ int main(int argc, char *argv[])
free_alloted
(
&
alloted
);
free_alloted
(
&
alloted
);
if
(
!
found_nicname
)
{
if
(
!
found_nicname
)
{
usernic_error
(
"Caller is not allowed to delete network "
usernic_error
(
"Caller is not allowed to delete network device
\"
%s
\"\n
"
,
args
.
veth_name
);
"device
\"
%s
\"\n
"
,
args
.
veth_name
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
ret
=
lxc_ovs_delete_port
(
args
.
link
,
args
.
veth_name
);
ret
=
lxc_ovs_delete_port
(
args
.
link
,
args
.
veth_name
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
usernic_error
(
"Failed to remove port
\"
%s
\"
from "
usernic_error
(
"Failed to remove port
\"
%s
\"
from openvswitch bridge
\"
%s
\"
"
,
args
.
veth_name
,
args
.
link
);
"openvswitch bridge
\"
%s
\"
"
,
args
.
veth_name
,
args
.
link
);
_exit
(
EXIT_FAILURE
);
_exit
(
EXIT_FAILURE
);
}
}
...
...
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