/* This file is part of Netsukuku * (c) Copyright 2005 Andrea Lo Pumo aka AlpT * * 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= 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