listen-netns: fix worker case for DTLS

while using udp, we later open a file descriptor for the worker. With a
listen netns config, I overlooked this case which oblige me to move the
struct containing the file descriptor in the main one. Then I can access
them from each worker to make it possible to open the socket in the
correct netns. I also need to keep the netns fd open during the whole
life of the process.

the issue was not visible on a tcp-only case, but while using udp you
can see logs such as:

main[user]: x.x.x.x:54024 bind UDP to 0.0.0.0:443: Cannot assign requested address
worker[user]: x.x.x.x setting up DTLS-PSK connection
main[user]: x.x.x.x:54024 bind UDP to 0.0.0.0:443: Cannot assign requested address

update tests to reflects that:
- instead of creating our own netns, use the one created in common.sh
- we start server in ns1, but listen in ns2, and test client from ns3
  (we don't want to listen in ns1 to test listen-ns)

Signed-off-by: William Dauchy <w.dauchy@criteo.com>
This commit is contained in:
William Dauchy 2020-09-15 21:06:19 +02:00 committed by Nikos Mavrogiannopoulos
parent 8f6ff20f66
commit 80babceacf
4 changed files with 88 additions and 29 deletions

View File

@ -316,10 +316,10 @@ int _listen_unix_ports(void *pool, struct perm_cfg_st* config,
*/
static int
listen_ports(void *pool, struct perm_cfg_st* config,
struct listen_list_st *list)
struct listen_list_st *list,
struct netns_fds *netns)
{
struct addrinfo hints, *res;
struct netns_fds netns = {-1, -1};
char portname[6];
int ret;
#ifdef HAVE_LIBSYSTEMD
@ -329,11 +329,6 @@ listen_ports(void *pool, struct perm_cfg_st* config,
list_head_init(&list->head);
list->total = 0;
if (config->listen_netns_name && open_namespaces(&netns, config) < 0) {
fprintf(stderr, "cannot init listen namespaces\n");
return -1;
}
#ifdef HAVE_LIBSYSTEMD
/* Support for systemd socket-activatable service */
if ((fds=sd_listen_fds(0)) > 0) {
@ -427,7 +422,7 @@ listen_ports(void *pool, struct perm_cfg_st* config,
return -1;
}
ret = _listen_ports(pool, config, res, list, &netns);
ret = _listen_ports(pool, config, res, list, netns);
freeaddrinfo(res);
if (ret < 0) {
@ -464,7 +459,7 @@ listen_ports(void *pool, struct perm_cfg_st* config,
return -1;
}
ret = _listen_ports(pool, config, res, list, &netns);
ret = _listen_ports(pool, config, res, list, netns);
if (ret < 0) {
return -1;
}
@ -472,11 +467,6 @@ listen_ports(void *pool, struct perm_cfg_st* config,
freeaddrinfo(res);
}
if (config->listen_netns_name && close_namespaces(&netns) < 0) {
fprintf(stderr, "cannot close listen namespaces\n");
return -1;
}
return 0;
}
@ -808,7 +798,7 @@ int sfd = -1;
goto fail;
}
sfd = socket(listener->family, SOCK_DGRAM, listener->protocol);
sfd = socket_netns(&s->netns, listener->family, SOCK_DGRAM, listener->protocol);
if (sfd < 0) {
e = errno;
mslog(s, proc_to_send, LOG_ERR, "new UDP socket failed: %s",
@ -1415,6 +1405,8 @@ int main(int argc, char** argv)
s->stats.start_time = s->stats.last_reset = time(0);
s->top_fd = -1;
s->ctl_fd = -1;
s->netns.default_fd = -1;
s->netns.listen_fd = -1;
if (!hmac_init_key(sizeof(s->hmac_key), (uint8_t*)(s->hmac_key))) {
fprintf(stderr, "unable to generate hmac key\n");
@ -1476,8 +1468,12 @@ int main(int argc, char** argv)
exit(1);
}
if (GETPCONFIG(s)->listen_netns_name && open_namespaces(&s->netns, GETPCONFIG(s)) < 0) {
fprintf(stderr, "cannot init listen namespaces\n");
exit(1);
}
/* Listen to network ports */
ret = listen_ports(s, GETPCONFIG(s), &s->listen_list);
ret = listen_ports(s, GETPCONFIG(s), &s->listen_list, &s->netns);
if (ret < 0) {
fprintf(stderr, "Cannot listen to specified ports\n");
exit(1);
@ -1670,6 +1666,10 @@ int main(int argc, char** argv)
snapshot_terminate(config_snapshot);
if (GETPCONFIG(s)->listen_netns_name && close_namespaces(&s->netns) < 0) {
fprintf(stderr, "cannot close listen namespaces\n");
exit(1);
}
clear_lists(s);
clear_vhosts(s->vconfig);
talloc_free(s->config_pool);

View File

@ -36,6 +36,7 @@
#include <ev.h>
#include <hmac.h>
#include "vhost.h"
#include <namespace.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
# include <limits.h>
@ -295,6 +296,8 @@ typedef struct main_server_st {
/* used as temporary buffer (currently by forward_udp_to_owner) */
uint8_t msg_buffer[MAX_MSG_SIZE];
struct netns_fds netns;
#ifdef RLIMIT_NOFILE
struct rlimit fd_limits_default_set;
#endif

View File

@ -11,7 +11,7 @@ isolate-workers = @ISOLATE_WORKERS@
#banner = "Welcome"
# Use listen-host to limit to specific IPs or to the IPs of a provided hostname.
#listen-host = @ADDRESS@
listen-host = @ADDRESS@
listen-netns = @LISTEN_NS@
@ -171,7 +171,7 @@ ping-leases = false
# Leave empty to assign the default MTU of the device
# mtu =
route = 192.168.1.0/255.255.255.0
#route = 192.168.1.0/255.255.255.0
#route = 192.168.5.0/255.255.255.0
#

View File

@ -15,31 +15,37 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# we start server in ns1, but listen in ns2, and test client from ns3
SERV="${SERV:-../src/ocserv}"
PIDFILE=ocserv-pid.$$.tmp
CLIPID=oc-pid.$$.tmp
srcdir=${srcdir:-.}
LISTEN_NS="ocserv-listen-ns-tmp-$$"
OUTFILE=lsof.$$.tmp
OUTFILE=ss.$$.tmp
SS=$(which ss)
ADDRESS=10.213.2.1
ADDRESS2=10.213.2.2
CLI_ADDRESS=10.213.1.1
CLI_ADDRESS2=10.213.1.2
VPNNET=172.17.215.0/24
VPNADDR=172.17.215.1
function finish {
set +e
echo " * Cleaning up..."
test -f "${CLIPID}" && kill $(cat ${CLIPID}) >/dev/null 2>&1
test -n "${CLIPID}" && rm -f ${CLIPID} >/dev/null 2>&1
test -n "${PID}" && kill ${PID} >/dev/null 2>&1
test -n "${PIDFILE}" && rm -f ${PIDFILE} >/dev/null 2>&1
test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1
test -n "${LISTEN_NS}" && ${IP} netns delete ${LISTEN_NS} >/dev/null 2>&1
test -n "${OUTFILE}" && rm -f ${OUTFILE} >/dev/null 2>&1
}
trap finish EXIT
. `dirname $0`/common.sh
. `dirname $0`/ns.sh
LISTEN_NS=$NSNAME2
eval "${GETPORT}"
@ -48,21 +54,71 @@ if test "$VERBOSE" = 1;then
DEBUG="-d 3"
fi
echo "Seting up listen namespace"
${IP} netns add ${LISTEN_NS}
${CMDNS2} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$!
${CMDNS1} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$!
sleep 5
${IP} netns exec ${LISTEN_NS} ${SS} -tulwn>${OUTFILE}
grep "LISTEN" ${OUTFILE}
echo "server: check listen tcp"
${SS} -N ${LISTEN_NS} -nt -o state listening sport = :${PORT} src ${ADDRESS} > ${OUTFILE}
grep -E "0[ ]+[0-9]+[ ]+${ADDRESS}:${PORT}[ ]+.*?:\*" ${OUTFILE}
if test $? != 0; then
echo "ocserv is not listening in the correct namespace!"
echo "server: check listen tcp failed"
cat ${OUTFILE}
exit 1
fi
kill $PID
echo "server: check listen udp"
${SS} -N ${LISTEN_NS} -nu -o state unconnected sport = :${PORT} src ${ADDRESS} > ${OUTFILE}
grep -E "0[ ]+0[ ]+${ADDRESS}:${PORT}[ ]+.*?:\*" ${OUTFILE}
if test $? != 0; then
echo "server: check listen udp failed"
cat ${OUTFILE}
exit 1
fi
echo " connecting to server"
(echo "test" | ${CMDNS3} $OPENCONNECT $ADDRESS:$PORT -u "test" --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --pid-file=${CLIPID} -b) ||
fail $PID "could not connect to server"
sleep 5
echo "server: check estab tcp"
${SS} -N ${LISTEN_NS} -nt -o state established sport = :${PORT} src ${ADDRESS} dst ${CLI_ADDRESS2} > ${OUTFILE}
grep -E "0[ ]+0[ ]+${ADDRESS}:${PORT}[ ]+${CLI_ADDRESS2}:[0-9]+" ${OUTFILE}
if test $? != 0; then
echo "server: check estab tcp failed"
cat ${OUTFILE}
exit 1
fi
echo "server: check estab udp"
${SS} -N ${LISTEN_NS} -nu -o state established sport = :${PORT} src ${ADDRESS} dst ${CLI_ADDRESS2} > ${OUTFILE}
grep -E "0[ ]+0[ ]+${ADDRESS}:${PORT}[ ]+${CLI_ADDRESS2}:[0-9]+" ${OUTFILE}
if test $? != 0; then
echo "server: check estab udp failed"
cat ${OUTFILE}
exit 1
fi
echo "client: check estab tcp"
${SS} -N ${NSNAME3} -nt -o state established dport = :${PORT} src ${CLI_ADDRESS2} dst ${ADDRESS} > ${OUTFILE}
grep -E "0[ ]+0[ ]+${CLI_ADDRESS2}:[0-9]+[ ]+${ADDRESS}:${PORT}" ${OUTFILE}
if test $? != 0; then
echo "client: check estab tcp failed"
cat ${OUTFILE}
exit 1
fi
echo "client: check estab udp"
${SS} -N ${NSNAME3} -nu -o state established dport = :${PORT} src ${CLI_ADDRESS2} dst ${ADDRESS} > ${OUTFILE}
grep -E "0[ ]+0[ ]+${CLI_ADDRESS2}:[0-9]+[ ]+${ADDRESS}:${PORT}" ${OUTFILE}
if test $? != 0; then
echo "client: check estab udp failed"
cat ${OUTFILE}
exit 1
fi
kill $(cat ${CLIPID})
kill ${PID}
wait
exit 0