netsukuku/src/daemon.c

463 lines
11 KiB
C
Raw Normal View History

2013-09-16 09:53:25 +00:00
/* This file is part of Netsukuku
* (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include "common.h"
#include "inet.h"
#include "request.h"
#include "if.h"
#include "pkts.h"
#include "bmap.h"
#include "daemon.h"
#include "netsukuku.h"
#include "accept.h"
extern int errno;
/*
* prepare_listen_socket:
* It creates a new socket of the desired `family' and binds it to the
* specified `port'. It sets also the reuseaddr and NONBLOCK
* socket options, because this new socket shall be used to listen() and
* accept().
* If `dev' is not null, the socket will be binded to the device named
* `dev'->dev_name with the SO_BINDTODEVICE socket option.
* The created socket is returned.
*/
int prepare_listen_socket(int family, int socktype, u_short port,
interface *dev)
{
struct addrinfo hints, *ai, *aitop;
char strport[NI_MAXSERV];
int err, s;
setzero(&hints, sizeof(struct addrinfo));
hints.ai_family=family;
hints.ai_socktype=socktype;
hints.ai_flags=AI_PASSIVE;
snprintf(strport, NI_MAXSERV, "%u", port);
err=getaddrinfo(NULL, strport, &hints, &aitop);
if(err) {
error("Getaddrinfo error: %s", gai_strerror(err));
return -1;
}
for (ai = aitop; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s == -1)
/* Maybe we can use another socket...*/
continue;
/* Bind the created socket to the device named dev->dev_name */
if(dev && (set_bindtodevice_sk(s, dev->dev_name) < 0)) {
inet_close(&s);
continue;
}
if(set_reuseaddr_sk(s) < 0) {
inet_close(&s);
continue;
}
/* Let's bind it! */
if(bind(s, ai->ai_addr, ai->ai_addrlen) < 0) {
error("Cannot bind the port %d: %s. Trying another "
"socket...", port, strerror(errno));
inet_close(&s);
continue;
}
freeaddrinfo(aitop);
return s;
}
error("Cannot open inbound socket on port %d: %s", port, strerror(errno));
freeaddrinfo(aitop);
return -1;
}
/*
* sockets_all_ifs
*
* creates a socket for each interface which is in the `ifs' array.
* The array has `ifs_n' members.
* Each created socket is stored in the `dev_sk' array, which has `ifs_n'#
* members.
* The created socket will be bound to the relative interface.
* In `max_sk_idx' is stored the index of the `dev_sk' array, which has the
* biggest dev_sk.
*
* On error 0 is returned, otherwise the number of utilised interfaces is
* returned.
* If the error is fatal, a negative value is returned.
*/
int sockets_all_ifs(int family, int socktype, u_short port,
interface *ifs, int ifs_n,
int *dev_sk, int *max_sk_idx)
{
int i, n, e=0;
*max_sk_idx=0;
for(i=0, n=0; i<ifs_n; i++) {
dev_sk[i] = prepare_listen_socket(family, socktype, port,
&ifs[i]);
if(dev_sk[i] < 0) {
error("Cannot create a socket on the %s interface! "
"Ignoring it", ifs[i].dev_name);
dev_sk[i]=0;
e++;
continue;
}
if(dev_sk[i] >= dev_sk[*max_sk_idx])
*max_sk_idx=i;
n++;
}
if(e == ifs_n)
return -1;
return n;
}
/*
* udp_exec_pkt: passes the received udp packet to pkt_exec().
* `passed_argv' is a pointer to a udp_exec_pkt_argv struct
*/
void *udp_exec_pkt(void *passed_argv)
{
struct udp_exec_pkt_argv argv;
PACKET rpkt;
const char *ntop;
memcpy(&argv, passed_argv, sizeof(struct udp_exec_pkt_argv));
memcpy(&rpkt, argv.recv_pkt, sizeof(PACKET));
if(argv.flags & UDP_THREAD_FOR_EACH_PKT)
pthread_mutex_unlock(&udp_exec_lock);
/* Drop any packet we sent in broadcast */
if(!memcmp(rpkt.from.data, me.cur_ip.data, MAX_IP_SZ)) {
pkt_free(&rpkt, 0);
return 0;
}
if(add_accept(rpkt.from, 1)) {
ntop=inet_to_str(rpkt.from);
debug(DBG_NORMAL, "ACPT: dropped UDP pkt from %s: "
"Accept table full.", ntop);
return 0;
}
pkt_exec(rpkt, argv.acpt_idx);
pkt_free(&rpkt, 0);
return 0;
}
/*
* udp_daemon: Takes care of receiving udp packets.
* `passed_argv' is a pointer to a udp_daemon_argv struct
*/
void *udp_daemon(void *passed_argv)
{
struct udp_daemon_argv argv;
struct udp_exec_pkt_argv exec_pkt_argv;
interface *ifs;
int max_sk_idx, dev_sk[me.cur_ifs_n];
PACKET rpkt;
fd_set fdset;
int ret, i, err;
u_short udp_port;
pthread_t thread;
pthread_attr_t t_attr;
#ifdef DEBUG
int select_errors=0;
#endif
memcpy(&argv, passed_argv, sizeof(struct udp_daemon_argv));
udp_port=argv.port;
setzero(&exec_pkt_argv, sizeof(struct udp_exec_pkt_argv));
if(argv.flags & UDP_THREAD_FOR_EACH_PKT) {
pthread_attr_init(&t_attr);
pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED);
exec_pkt_argv.flags|=UDP_THREAD_FOR_EACH_PKT;
}
debug(DBG_SOFT, "Preparing the udp listening socket on port %d", udp_port);
err=sockets_all_ifs(my_family, SOCK_DGRAM, udp_port, me.cur_ifs,
me.cur_ifs_n, dev_sk, &max_sk_idx);
if(!err)
return NULL;
else if(err < 0)
fatal("Creation of the %s daemon aborted. "
"Is there another ntkd running?", "udp");
debug(DBG_NORMAL, "Udp daemon on port %d up & running", udp_port);
pthread_mutex_unlock(&udp_daemon_lock);
pthread_mutex_init(&udp_exec_lock, 0);
for(;;) {
FD_ZERO(&fdset);
if(!me.cur_ifs_n) {
/* All the devices have been removed while ntkd was
* running, sleep well */
sleep(1);
continue;
}
for(i=0; i < me.cur_ifs_n; i++)
if(dev_sk[i])
FD_SET(dev_sk[i], &fdset);
ret=select(dev_sk[max_sk_idx]+1, &fdset, NULL, NULL, NULL);
if(sigterm_timestamp)
/* NetsukukuD has been closed */
break;
if (ret < 0) {
#ifdef DEBUG
if(select_errors > 20)
break;
select_errors++;
#endif
error("daemon_udp: select error: %s", strerror(errno));
continue;
}
for(i=0; i < me.cur_ifs_n; i++) {
ifs=&me.cur_ifs[i];
if(!dev_sk[i])
continue;
if(!FD_ISSET(dev_sk[i], &fdset))
continue;
setzero(&rpkt, sizeof(PACKET));
pkt_addsk(&rpkt, my_family, dev_sk[i], SKT_UDP);
pkt_add_dev(&rpkt, ifs, 0);
rpkt.flags=MSG_WAITALL;
pkt_addport(&rpkt, udp_port);
if(pkt_recv(&rpkt) < 0) {
pkt_free(&rpkt, 0);
continue;
}
exec_pkt_argv.acpt_idx=accept_idx;
exec_pkt_argv.acpt_sidx=accept_sidx;
if(argv.flags & UDP_THREAD_FOR_EACH_PKT) {
exec_pkt_argv.recv_pkt=&rpkt;
pthread_mutex_lock(&udp_exec_lock);
pthread_create(&thread, &t_attr, udp_exec_pkt,
&exec_pkt_argv);
pthread_mutex_lock(&udp_exec_lock);
pthread_mutex_unlock(&udp_exec_lock);
} else {
exec_pkt_argv.recv_pkt=&rpkt;
udp_exec_pkt(&exec_pkt_argv);
}
}
}
destroy_accept_tbl();
return NULL;
}
void *tcp_recv_loop(void *recv_pkt)
{
PACKET rpkt;
int acpt_idx, acpt_sidx;
acpt_idx=accept_idx;
acpt_sidx=accept_sidx;
memcpy(&rpkt, recv_pkt, sizeof(PACKET));
pthread_mutex_unlock(&tcp_exec_lock);
#if 0
add_accept_pid(getpid(), acpt_idx, acpt_sidx);
#endif
while( pkt_recv(&rpkt) != -1 ) {
if(pkt_exec(rpkt, acpt_idx) < 0) {
goto close;
break;
} else
pkt_free(&rpkt, 0);
}
close:
pkt_free(&rpkt, 1);
close_accept(acpt_idx, acpt_sidx);
return NULL;
}
void *tcp_daemon(void *door)
{
pthread_t thread;
pthread_attr_t t_attr;
PACKET rpkt;
struct sockaddr_storage addr;
socklen_t addrlen = sizeof addr;
inet_prefix ip;
fd_set fdset;
int fd, ret, err, i;
interface *ifs;
int max_sk_idx, dev_sk[me.cur_ifs_n];
u_short tcp_port=*(u_short *)door;
const char *ntop;
pthread_attr_init(&t_attr);
pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED);
debug(DBG_SOFT, "Preparing the tcp listening socket on port %d", tcp_port);
err=sockets_all_ifs(my_family, SOCK_STREAM, tcp_port, me.cur_ifs,
me.cur_ifs_n, dev_sk, &max_sk_idx);
if(!err)
return NULL;
else if(err < 0)
fatal("Creation of the %s daemon aborted. "
"Is there another ntkd running?", "tcp");
pthread_mutex_init(&tcp_exec_lock, 0);
for(i=0; i<me.cur_ifs_n; i++) {
if(!dev_sk[i])
continue;
/*
* While we are accepting the connections we keep the socket non
* blocking.
*/
if(set_nonblock_sk(dev_sk[i])) {
pthread_mutex_unlock(&tcp_daemon_lock);
return NULL;
2013-09-16 09:53:25 +00:00
}
/* Shhh, it's listening... */
if(listen(dev_sk[i], 5) == -1) {
inet_close(&dev_sk[i]);
pthread_mutex_unlock(&tcp_daemon_lock);
return NULL;
}
}
2013-09-16 09:53:25 +00:00
debug(DBG_NORMAL, "Tcp daemon on port %d up & running", tcp_port);
pthread_mutex_unlock(&tcp_daemon_lock);
for(;;) {
FD_ZERO(&fdset);
if(!me.cur_ifs_n) {
/* All the devices have been removed while ntkd was
* running, sleep well */
sleep(1);
continue;
}
for(i=0; i < me.cur_ifs_n; i++)
if(dev_sk[i])
FD_SET(dev_sk[i], &fdset);
ret=select(dev_sk[max_sk_idx]+1, &fdset, NULL, NULL, NULL);
if(sigterm_timestamp)
/* NetsukukuD has been closed */
break;
if(ret < 0 && errno != EINTR)
error("daemon_tcp: select error: %s", strerror(errno));
if(ret < 0)
continue;
for(i=0; i < me.cur_ifs_n; i++) {
ifs=&me.cur_ifs[i];
if(!dev_sk[i])
continue;
if(!FD_ISSET(dev_sk[i], &fdset))
continue;
fd=accept(dev_sk[i], (struct sockaddr *)&addr, &addrlen);
if(fd == -1) {
if (errno != EINTR && errno != EWOULDBLOCK)
error("daemon_tcp: accept(): %s", strerror(errno));
continue;
}
setzero(&rpkt, sizeof(PACKET));
pkt_addsk(&rpkt, my_family, fd, SKT_TCP);
pkt_add_dev(&rpkt, ifs, 0);
rpkt.flags=MSG_WAITALL;
pkt_addport(&rpkt, tcp_port);
ntop=0;
sockaddr_to_inet((struct sockaddr *)&addr, &ip, 0);
pkt_addfrom(&rpkt, &ip);
if(server_opt.dbg_lvl)
ntop=inet_to_str(ip);
if((ret=add_accept(ip, 0))) {
debug(DBG_NORMAL, "ACPT: drop connection with %s: "
"Accept table full.", ntop);
/* Omg, we cannot take it anymore, go away: ACK_NEGATIVE */
pkt_err(rpkt, ret, 1);
inet_close(&fd);
continue;
} else {
/*
* Ok, the connection is good, send back the
* ACK_AFFERMATIVE.
*/
pkt_addto(&rpkt, &rpkt.from);
send_rq(&rpkt, 0, ACK_AFFERMATIVE, 0, 0, 0, 0);
}
if(unset_nonblock_sk(fd))
continue;
pthread_mutex_lock(&tcp_exec_lock);
err=pthread_create(&thread, &t_attr, tcp_recv_loop, (void *)&rpkt);
pthread_detach(thread);
pthread_mutex_lock(&tcp_exec_lock);
pthread_mutex_unlock(&tcp_exec_lock);
}
}
return NULL;
}