mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-16 00:25:48 +08:00
Unify Linux namespace operations
This commit is contained in:
parent
f1b8d30799
commit
cc19bbff6d
@ -165,10 +165,10 @@ syntax.
|
||||
bind internal (\fB-Di\fR) / external (\fB-De\fR) interface to given INTERFACE (e.g. eth0) if \fBSO_BINDTODEVICE\fR is supported by the system. You may need to run as root or have \fBCAP_NET_RAW\fR capability in order to bind to an interface, depending on the system, so this option may require root privileges and can be incompatible with some configuration commands like \fBchroot\fR and \fBsetuid\fR (and \fBdaemon\fR if setcap is used).
|
||||
.br
|
||||
.B -ni\fIPATH\fB, -ne\fIPATH\fR
|
||||
(Linux only) Switch to the network namespace identified by the filesystem path \fIPATH\fR (e.g. \fI/var/run/netns/myns\fR or \fI/proc/PID/ns/net\fR) for the listening socket (\fB-ni\fR) or for outgoing connections (\fB-ne\fR).
|
||||
With \fB-ni\fR the current namespace is saved before opening the listening socket and restored immediately after binding, so that the rest of the process (outgoing connections, child threads) runs in the original namespace unless \fB-ne\fR is also given.
|
||||
With \fB-ne\fR the process switches to the specified namespace after the listening socket is bound (and after restoring from \fB-ni\fR if applicable).
|
||||
Requires \fBCAP_SYS_ADMIN\fR (or \fBCAP_NET_ADMIN\fR on recent kernels) and is incompatible with \fBchroot\fR/\fBsetuid\fR/\fBdaemon\fR if privileges are dropped before the switch takes effect.
|
||||
(Linux only) Use the network namespace identified by the filesystem path \fIPATH\fR (e.g. \fI/var/run/netns/myns\fR or \fI/proc/PID/ns/net\fR) for the sockets which face the clients (\fB-ni\fR: the listening socket, and the socket SOCKS UDP ASSOCIATE talks to the client on) or the destinations (\fB-ne\fR: outgoing connections, SOCKS BIND and the UDP sockets towards the destination, and the connect back sockets of \fB-r\fR and \fB-R\fR).
|
||||
Threads stay in the namespace 3proxy was started in and enter the configured one only to create such a socket. Everything else is done in the original namespace: name resolution, both through \fBnserver\fR and through the system, including the queries \fBdnspr\fR forwards, logging and RADIUS.
|
||||
The paths are opened when the service line is read, so for a service configured after \fBchroot\fR they are looked up inside it; a path which is not a network namespace stops the service from starting.
|
||||
Entering a namespace requires \fBCAP_SYS_ADMIN\fR for as long as the service runs, so these options cannot be combined with \fBsetuid\fR to an unprivileged user.
|
||||
.br
|
||||
.B -Ne
|
||||
(for socks) External NAT address (between 3proxy and destination server) to report to client for CONNECT and BIND. By default external address is reported. It's only useful in the case of IP-IP NAT (will not work for PAT).
|
||||
|
||||
@ -549,6 +549,9 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
nsopen(); /* before a chroot in the configuration takes /proc away */
|
||||
#endif
|
||||
_3proxy_mutex_init(&config_mutex);
|
||||
_3proxy_mutex_init(&runningservices_mutex);
|
||||
_3proxy_mutex_init(&bandlim_mutex);
|
||||
|
||||
37
src/common.c
37
src/common.c
@ -45,10 +45,37 @@ int socks5_setaddr(int family, int atyp, const unsigned char *addr, PROXYSOCKADD
|
||||
#ifdef __linux__
|
||||
#include <sched.h>
|
||||
|
||||
int switch_ns(struct srvparam *srv, int target_fd) {
|
||||
if(target_fd < 0) return 0;
|
||||
if(srv->saved_nsfd >= 0 && setns(srv->saved_nsfd, CLONE_NEWNET)) return -1;
|
||||
return setns(target_fd, CLONE_NEWNET);
|
||||
/* Threads stay in the network namespace the process started in. A socket
|
||||
belongs to the namespace it is made in, whatever thread uses it later,
|
||||
so a service's other namespaces are entered for socket() alone. What a
|
||||
thread does without making a socket of its own, a name looked up through
|
||||
the system, a log line, RADIUS, happens in the main namespace. */
|
||||
int mainnsfd = -1;
|
||||
|
||||
/* Opens the main namespace, once and while /proc can still be reached: a
|
||||
chroot may take it away. */
|
||||
int nsopen(void){
|
||||
if(mainnsfd < 0) mainnsfd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
|
||||
return mainnsfd < 0;
|
||||
}
|
||||
|
||||
SOCKET nssocket(struct srvparam *srv, struct sockfuncs *sf, void *state, int side, int domain, int type, int protocol){
|
||||
int nsfd = (side == NS_OUT)? srv->o_nsfd : srv->i_nsfd;
|
||||
SOCKET s;
|
||||
int err;
|
||||
|
||||
if(nsfd < 0) return sf->_socket(state, domain, type, protocol);
|
||||
if(setns(nsfd, CLONE_NEWNET)) return INVALID_SOCKET;
|
||||
s = sf->_socket(state, domain, type, protocol);
|
||||
err = errno;
|
||||
if(setns(mainnsfd, CLONE_NEWNET)){
|
||||
/* Left in the other namespace: no socket from here, and the
|
||||
connection fails. The thread ends with it. */
|
||||
if(s != INVALID_SOCKET) sf->_closesocket(state, s);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
errno = err;
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -742,7 +769,7 @@ int doconnect(struct clientparam * param){
|
||||
}
|
||||
if(!*SAPORT(¶m->sinsr))*SAPORT(¶m->sinsr) = *SAPORT(¶m->req);
|
||||
for(;;){
|
||||
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->sinsr), SOCK_STREAM,
|
||||
if ((param->remsock=nssocket(param->srv, ¶m->srv->so, param->sostate, NS_OUT, SASOCK(¶m->sinsr), SOCK_STREAM,
|
||||
#ifdef WITH_UN
|
||||
*SAFAMILY(¶m->sinsr) == AF_UNIX? 0 :
|
||||
#endif
|
||||
|
||||
@ -162,7 +162,7 @@ int start_proxy_thread(struct child * chp){
|
||||
#endif
|
||||
|
||||
if((res = mainprepare(chp->argc, (char **)chp->argv, &service))) {
|
||||
fprintf(stderr, "Service not started on line: %d%s\n", linenum, res == 2? ": insufficient memory": res == 3? ": too many filters":"");
|
||||
fprintf(stderr, "Service not started on line: %d%s\n", linenum, res == 2? ": insufficient memory": res == 3? ": too many filters": res == 4? ": network namespace unavailable":"");
|
||||
return(40);
|
||||
}
|
||||
_3proxy_mutex_lock(&runningservices_mutex);
|
||||
|
||||
@ -46,7 +46,7 @@ void * dnsprchild(struct clientparam* param) {
|
||||
_3proxy_sem_unlock(udpinit);
|
||||
semlocked = 0;
|
||||
#if defined(_WIN32) && !defined(SHARE_UDP_SOCKET)
|
||||
if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||
if((param->clisock=nssocket(param->srv, ¶m->srv->so, param->sostate, NS_IN, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) {
|
||||
RETURN(818);
|
||||
}
|
||||
ioctlsocket(param->clisock, FIONBIO, &ul);
|
||||
|
||||
@ -239,8 +239,15 @@ int sockmap(struct clientparam * param, int timeo, int usesplice);
|
||||
int udpsockmap(struct clientparam * param, int timeo);
|
||||
int udpbind(struct clientparam * param);
|
||||
int socks5_setaddr(int family, int atyp, const unsigned char *addr, PROXYSOCKADDRTYPE *sa);
|
||||
/* which of a service's network namespaces a socket is made in */
|
||||
#define NS_IN 0 /* -ni: what faces the clients */
|
||||
#define NS_OUT 1 /* -ne: what faces the destinations */
|
||||
#ifdef __linux__
|
||||
int switch_ns(struct srvparam *srv, int target_fd);
|
||||
extern int mainnsfd;
|
||||
int nsopen(void);
|
||||
SOCKET nssocket(struct srvparam *srv, struct sockfuncs *sf, void *state, int side, int domain, int type, int protocol);
|
||||
#else
|
||||
#define nssocket(srv, sf, state, side, domain, type, protocol) ((sf)->_socket((state), (domain), (type), (protocol)))
|
||||
#endif
|
||||
int socksend(struct clientparam *param, SOCKET sock, unsigned char * buf, int bufsize, int to);
|
||||
int socksendto(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
|
||||
|
||||
@ -748,6 +748,18 @@ static int srvprepare(struct srvstart *s, int argc, char **argv){
|
||||
#endif
|
||||
if(hostname)parsehostname(hostname, defparam, childdef.port);
|
||||
|
||||
#ifdef __linux__
|
||||
/* Opened here, where the configuration is read, before a chroot can hide
|
||||
the paths, and each entered once to be sure it is a network namespace
|
||||
this process may use: a mistake shows now, not on a client's connection. */
|
||||
if(srv->inetns || srv->onetns){
|
||||
if(nsopen()) return 4;
|
||||
if(srv->inetns && (srv->i_nsfd = open(srv->inetns, O_RDONLY | O_CLOEXEC)) < 0) return 4;
|
||||
if(srv->onetns && (srv->o_nsfd = open(srv->onetns, O_RDONLY | O_CLOEXEC)) < 0) return 4;
|
||||
if(srv->i_nsfd >= 0 && (setns(srv->i_nsfd, CLONE_NEWNET) || setns(mainnsfd, CLONE_NEWNET))) return 4;
|
||||
if(srv->o_nsfd >= 0 && (setns(srv->o_nsfd, CLONE_NEWNET) || setns(mainnsfd, CLONE_NEWNET))) return 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef STDMAIN
|
||||
if((error = copyfilter(conf.filters, srv))) return error;
|
||||
@ -772,7 +784,7 @@ static int srvlisten(struct srvstart *s){
|
||||
|
||||
#ifndef NOTCPMAIN
|
||||
if(!s->isudp){
|
||||
sock=srv->so._socket(srv->so.state, SASOCK(&srv->intsa), SOCK_STREAM,
|
||||
sock=nssocket(srv, &srv->so, srv->so.state, NS_IN, SASOCK(&srv->intsa), SOCK_STREAM,
|
||||
#ifdef WITH_UN
|
||||
*SAFAMILY(&srv->intsa) == AF_UNIX? 0 :
|
||||
#endif
|
||||
@ -782,7 +794,7 @@ static int srvlisten(struct srvstart *s){
|
||||
#endif
|
||||
#ifndef NOUDPMAIN
|
||||
if(s->isudp){
|
||||
sock=srv->so._socket(srv->so.state, SASOCK(&srv->intsa), SOCK_DGRAM, IPPROTO_UDP);
|
||||
sock=nssocket(srv, &srv->so, srv->so.state, NS_IN, SASOCK(&srv->intsa), SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
#endif
|
||||
if( sock == INVALID_SOCKET) {
|
||||
@ -845,7 +857,7 @@ static int cblisten(struct srvstart *s){
|
||||
PROXYSOCKADDRTYPE cbsa;
|
||||
|
||||
parsehost(srv->family, s->cbl_string, (struct sockaddr *)&cbsa);
|
||||
if((srv->cbsock=srv->so._socket(srv->so.state, SASOCK(&cbsa), SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) {
|
||||
if((srv->cbsock=nssocket(srv, &srv->so, srv->so.state, NS_OUT, SASOCK(&cbsa), SOCK_STREAM, IPPROTO_TCP))==INVALID_SOCKET) {
|
||||
dolog(defparam, (unsigned char *)"Failed to allocate connect back socket");
|
||||
return -6;
|
||||
}
|
||||
@ -885,7 +897,7 @@ static int tcpnext(struct srvstart *s, SOCKET *client, int *nlog){
|
||||
|
||||
size = sizeof(defparam->sincr);
|
||||
if(s->iscbc){
|
||||
new_sock=so._socket(so.state, SASOCK(&defparam->sincr), SOCK_STREAM, IPPROTO_TCP);
|
||||
new_sock=nssocket(&s->srv, &so, so.state, NS_OUT, SASOCK(&defparam->sincr), SOCK_STREAM, IPPROTO_TCP);
|
||||
if(new_sock == INVALID_SOCKET){
|
||||
usleep(SLEEPTIME);
|
||||
return NEXT_RETRY;
|
||||
@ -1128,10 +1140,6 @@ static int srvrun(struct srvstart *s){
|
||||
#endif
|
||||
/* -r: a TCP service connects out for each client instead of listening */
|
||||
int connectback = !s->isudp && s->iscbc;
|
||||
#ifdef __linux__
|
||||
int saved_nsfd = -1;
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_attr_init(&s->pa);
|
||||
pthread_attr_setstacksize(&s->pa,threadstacksize(srv->stacksize));
|
||||
@ -1145,78 +1153,7 @@ static int srvrun(struct srvstart *s){
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
if(srv->inetns) {
|
||||
saved_nsfd = open("/proc/self/ns/net", O_RDONLY);
|
||||
if(saved_nsfd == -1) {
|
||||
dolog(defparam, (unsigned char *)"failed to open /proc/self/ns/net");
|
||||
res = -13;
|
||||
goto EXIT;
|
||||
}
|
||||
{
|
||||
int nsfd = open(srv->inetns, O_RDONLY);
|
||||
if(nsfd == -1) {
|
||||
dolog(defparam, (unsigned char *)"failed to open inetns");
|
||||
close(saved_nsfd);
|
||||
res = -13;
|
||||
goto EXIT;
|
||||
}
|
||||
if(setns(nsfd, CLONE_NEWNET)) {
|
||||
dolog(defparam, (unsigned char *)"failed to setns inetns");
|
||||
close(nsfd);
|
||||
close(saved_nsfd);
|
||||
res = -13;
|
||||
goto EXIT;
|
||||
}
|
||||
if(srv->service == S_SOCKS) srv->i_nsfd = nsfd;
|
||||
else close(nsfd);
|
||||
}
|
||||
if(srv->service == S_SOCKS) srv->saved_nsfd = saved_nsfd;
|
||||
}
|
||||
#endif
|
||||
if(!connectback && (res = srvlisten(s))) goto EXIT;
|
||||
#ifdef __linux__
|
||||
if(saved_nsfd != -1) {
|
||||
if(setns(saved_nsfd, CLONE_NEWNET)) {
|
||||
dolog(defparam, (unsigned char *)"failed to restore netns");
|
||||
if(srv->service == S_SOCKS) {
|
||||
if(srv->i_nsfd >= 0) { close(srv->i_nsfd); srv->i_nsfd = -1; }
|
||||
srv->saved_nsfd = -1;
|
||||
}
|
||||
close(saved_nsfd);
|
||||
res = -14;
|
||||
goto EXIT;
|
||||
}
|
||||
if(srv->service != S_SOCKS) {
|
||||
close(saved_nsfd);
|
||||
saved_nsfd = -1;
|
||||
}
|
||||
}
|
||||
if(srv->onetns) {
|
||||
int nsfd = open(srv->onetns, O_RDONLY);
|
||||
if(nsfd == -1) {
|
||||
dolog(defparam, (unsigned char *)"failed to open onetns");
|
||||
if(srv->service == S_SOCKS) {
|
||||
if(srv->saved_nsfd >= 0) { close(srv->saved_nsfd); srv->saved_nsfd = -1; }
|
||||
if(srv->i_nsfd >= 0) { close(srv->i_nsfd); srv->i_nsfd = -1; }
|
||||
}
|
||||
res = -14;
|
||||
goto EXIT;
|
||||
}
|
||||
if(setns(nsfd, CLONE_NEWNET)) {
|
||||
dolog(defparam, (unsigned char *)"failed to setns onetns");
|
||||
close(nsfd);
|
||||
if(srv->service == S_SOCKS) {
|
||||
if(srv->saved_nsfd >= 0) { close(srv->saved_nsfd); srv->saved_nsfd = -1; }
|
||||
if(srv->i_nsfd >= 0) { close(srv->i_nsfd); srv->i_nsfd = -1; }
|
||||
}
|
||||
res = -14;
|
||||
goto EXIT;
|
||||
}
|
||||
if(srv->service == S_SOCKS) srv->o_nsfd = nsfd;
|
||||
else close(nsfd);
|
||||
}
|
||||
#endif
|
||||
#ifndef NOTCPMAIN
|
||||
if(!s->isudp && s->iscbl && (res = cblisten(s))) goto EXIT;
|
||||
#endif
|
||||
@ -1419,7 +1356,7 @@ void srvinit(struct srvparam * srv, struct clientparam *param){
|
||||
srv->cbsock = INVALID_SOCKET;
|
||||
srv->needuser = 1;
|
||||
#ifdef __linux__
|
||||
srv->saved_nsfd = srv->i_nsfd = srv->o_nsfd = -1;
|
||||
srv->i_nsfd = srv->o_nsfd = -1;
|
||||
#endif
|
||||
#ifdef WITHSPLICE
|
||||
srv->usesplice = 0;
|
||||
@ -1530,7 +1467,6 @@ void srvfree(struct srvparam * srv){
|
||||
#ifdef __linux__
|
||||
if(srv->inetns) free(srv->inetns);
|
||||
if(srv->onetns) free(srv->onetns);
|
||||
if(srv->saved_nsfd >= 0) { close(srv->saved_nsfd); srv->saved_nsfd = -1; }
|
||||
if(srv->i_nsfd >= 0) { close(srv->i_nsfd); srv->i_nsfd = -1; }
|
||||
if(srv->o_nsfd >= 0) { close(srv->o_nsfd); srv->o_nsfd = -1; }
|
||||
#endif
|
||||
|
||||
@ -179,7 +179,7 @@ void * sockschild(struct clientparam* param) {
|
||||
#endif
|
||||
param->operation = command == 2?BIND:UDPASSOC;
|
||||
if(command == 2){
|
||||
if ((param->remsock=param->srv->so._socket(param->sostate, SASOCK(¶m->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);}
|
||||
if ((param->remsock=nssocket(param->srv, ¶m->srv->so, param->sostate, NS_OUT, SASOCK(¶m->req), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN (11);}
|
||||
setopts(param->remsock, ¶m->srv->lissockopts);
|
||||
if(extbindtodevice(param, param->remsock)) {RETURN (12);}
|
||||
}
|
||||
@ -231,10 +231,7 @@ fflush(stderr);
|
||||
}
|
||||
if(command == 3) {
|
||||
param->ctrlsock = param->clisock;
|
||||
#ifdef __linux__
|
||||
if(switch_ns(param->srv, param->srv->i_nsfd)) {RETURN(11);}
|
||||
#endif
|
||||
param->clisock = param->srv->so._socket(param->sostate, SASOCK(¶m->sincr), SOCK_DGRAM, IPPROTO_UDP);
|
||||
param->clisock = nssocket(param->srv, ¶m->srv->so, param->sostate, NS_IN, SASOCK(¶m->sincr), SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(param->clisock == INVALID_SOCKET) {RETURN(11);}
|
||||
#ifdef _WIN32
|
||||
{ unsigned long ul = 1; ioctlsocket(param->clisock, FIONBIO, &ul); }
|
||||
|
||||
@ -659,7 +659,7 @@ struct srvparam {
|
||||
#ifdef __linux__
|
||||
char * inetns;
|
||||
char * onetns;
|
||||
int saved_nsfd;
|
||||
/* -ni and -ne namespaces, opened with the service, -1 for the main one */
|
||||
int i_nsfd;
|
||||
int o_nsfd;
|
||||
#endif
|
||||
|
||||
@ -104,16 +104,7 @@ int udpbind(struct clientparam *param)
|
||||
SOCKET s;
|
||||
SASIZETYPE sasize;
|
||||
|
||||
#ifdef __linux__
|
||||
if (switch_ns(param->srv, param->srv->o_nsfd)) return 11;
|
||||
#endif
|
||||
s = param->srv->so._socket(param->sostate, SASOCK(¶m->sinsl), SOCK_DGRAM, IPPROTO_UDP);
|
||||
#ifdef __linux__
|
||||
if (switch_ns(param->srv, param->srv->i_nsfd)) {
|
||||
if (s != INVALID_SOCKET) param->srv->so._closesocket(param->sostate, s);
|
||||
return 11;
|
||||
}
|
||||
#endif
|
||||
s = nssocket(param->srv, ¶m->srv->so, param->sostate, NS_OUT, SASOCK(¶m->sinsl), SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s == INVALID_SOCKET) return 11;
|
||||
#ifdef _WIN32
|
||||
{ unsigned long ul = 1; ioctlsocket(s, FIONBIO, &ul); }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user