blob: 0158f38ff75c44f99a2aaf696807b0119bb453c6 [file] [log] [blame]
/*
* Copyright 2010
* Princeton University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY PRINCETON UNIVERSITY ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON UNIVERSITY OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of Princeton University.
*
*/
/*
* @(#)host_daemon.c (Princeton) 11/10/2010
*
* * A daemon for UpTCP. The daemon uses a local Unix domain to
* communicate with UpTCP's Socket API Layer (SAL). The daemon
* is responsible for
*
* 1) global initialization (mbuf, ifnet etc.)
* 2) setup interrupt handler thread
* 3) setup timer thread
* 4) create a server socket
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <sys/shm.h>
#include <signal.h>
#include "uptcp_daemon.h"
#include "uptcp_api.h"
#define AUTO_EXIT_FOR_DEBUG
extern unsigned long long stat_raw_cnt;
extern unsigned long long stat_raw_time;
extern unsigned long long stat_rx_cnt;
extern unsigned long long stat_rx_time;
extern unsigned long long stat_sock_cnt;
extern unsigned long long stat_sock_time;
extern u_long sb_max;
extern u_long tcp_sendspace;
extern u_long tcp_recvspace;
extern int uptcp_initialized;
extern void uptcp_init();
extern void clock_intr_handler(int sig);
extern int bsd_syscall_socket(int sockfd, struct sal_socket_args *args/*int domain, int type, int protocol*/);
extern int bsd_syscall_bind(int sockfd, struct sal_bind_args *args/* caddr_t name, int namelen*/);
extern int bsd_syscall_listen(int sockfd, struct sal_listen_args *args/* int backlog*/);
extern int bsd_syscall_accept(int sockfd, struct sal_accept_args *args, struct sal_accept_res *retval/* struct sockaddr *name, socklen_t *anamelen*/);
extern int bsd_syscall_connect(int sockfd, struct sal_connect_args *args/* caddr_t name, int namelen*/);
extern int bsd_syscall_socketpair(int sockfd, struct sal_socketpair_args *args/*int domain, int type, int protocol, int *rsv*/);
extern int bsd_syscall_sendto(int sockfd, struct sal_sendto_args *args/* caddr_t buf, size_t len, int flags, caddr_t to, int tolen*/);
extern int bsd_syscall_send(int sockfd, struct sal_send_args *args, char *sendbuf/* caddr_t buf, int len, int flags*/);
extern int bsd_syscall_sendmsg(int sockfd, struct sal_sendmsg_args *args/* caddr_t msg, int flags*/);
extern int bsd_syscall_recvfrom(int sockfd, struct sal_recvfrom_args *args/* caddr_t buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlenaddr*/);
extern int bsd_syscall_recv(int sockfd, struct sal_recv_args *args, char* recvbuf/* caddr_t buf, int len, int flags*/);
extern int bsd_syscall_recvmsg(int sockfd, struct sal_recvmsg_args *args/* struct msghdr *msg, int flags*/);
extern int bsd_syscall_shutdown(int sockfd, struct sal_shutdown_args *args/* int how*/);
extern int bsd_syscall_setsockopt(int sockfd, struct sal_setsockopt_args *args/* int level, int name, caddr_t val, int valsize*/);
extern int bsd_syscall_getsockopt(int sockfd, struct sal_getsockopt_args *args/* int level, int name, void * val, socklen_t *avalsize*/);
extern int bsd_syscall_getsockname(int sockfd, struct sal_getsockname_args *args/* struct sockaddr *asa, socklen_t *alen*/) ;
extern int bsd_syscall_getpeername(int sockfd, struct sal_getpeername_args *args/* struct sockaddr *asa, socklen_t *alen*/);
//#define UPTCP_DAEMON_DEBUG
#ifndef UPTCP_DAEMON_DEBUG
#define UPTCP_DAEMON_DEBUG_PRINT(fmt, args...)
#else
#define UPTCP_DAEMON_DEBUG_PRINT(fmt, args...) \
do{ \
char str[256]; \
sprintf(str, "[DAMON] %s", (fmt)); \
printf(str, ## args); \
}while(0);
#endif
int gblopt_timer = 1; /* option for setuping global timer */
pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; /**/
char *req_descriptor [] ={
"\0",
"SOCKET",
"BIND",
"LISTEN",
"ACCEPT",
"CONNECT",
"SOCKETPAIR",
"SENDTO",
"SEND",
"SENDMSG",
"RECVFROM",
"RECV",
"RECVMSG",
"SHUTDOWN",
"SETSOCKOPT",
"GETSOCKOPT",
"GETSOCKNAME",
"GETPEERNAME",
"SOCKFD",
"MALLOC",
"FREE"
};
void* connection_handler(void * accept_sockfd);
void* interrupt_handler(void* data);
void display_errno(int num);
void usage(char* prog)
{
printf("usage: %s [-h] [-t]\n",prog);
printf("-t\t\tset global timer\n");
printf("-h \t\t\thelp\n");
}
int main(int argc, char ** argv)
{
int server_sockfd, accept_sockfd;
struct sockaddr_un serveraddr;
int res;
char ch;
UPTCP_DAEMON_DEBUG_PRINT(">> main()\n")
/* parse the args */
opterr = 0;
optind = 1;
while (-1 != (ch = getopt(argc, argv, "tphx:r:b:"))) {
switch (ch) {
case 't':
gblopt_timer = 0;
break;
case 'b':
sb_max = atoi(optarg) << 10;
break;
case 'x':
tcp_sendspace = atoi(optarg) << 10;
break;
case 'r':
tcp_recvspace = atoi(optarg) << 10;
break;
case 'h':
usage(argv[0]);
return -1;
case '?':
printf("main(): Unknown option `-%c'.\n", optopt);
usage(argv[0]);
return -1;
}
}
/* initialize all TCP/IP stack */
if(!uptcp_initialized){
/* initialize TCP/IP stack */
uptcp_init();
/* setup clock */
if(gblopt_timer){
struct itimerval old, new;
/* Establish a handler for SIGALRM signals. */
signal (SIGALRM, clock_intr_handler);
new.it_interval.tv_usec = 10000; /* 10ms*/
new.it_interval.tv_sec = 0;
new.it_value.tv_usec = 10000;
new.it_value.tv_sec = 0;
if (setitimer (ITIMER_REAL, &new, &old) < 0){
printf("main(): setitimer() failed\n");
}
}
uptcp_initialized = 1;
}
printf("[sb_max, tcp_sendspace, tcp_recvspace] = %dK %dK %dK\n",
(sb_max >> 10),(tcp_sendspace >> 10), (tcp_recvspace >> 10));
if(unlink(SERVER_PATH) < 0){
printf("main(): unlink() failed\n");
}
/* create a Unix domain server socket */
server_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (server_sockfd < 0){
printf("main(): create AF_UNIX socket failed\n");
return -1;
}
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sun_family = PF_UNIX;
strcpy(serveraddr.sun_path, SERVER_PATH);
res = bind(server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (res < 0){
printf("main(): bind() failed\n");
close(server_sockfd);
return -1;
}
res = listen(server_sockfd, MAX_SAL_CLIENTS);
if (res < 0){
printf("main(): listen() failed\n");
close(server_sockfd);
return -1;
}
while(1){
pthread_t * pthread;
accept_sockfd = accept(server_sockfd, NULL, NULL);
if(accept_sockfd < 0){
printf("main(): accept() failed\n");
display_errno(errno);
continue;
}
/* we create a new thread to handle the accept socket */
pthread = (pthread_t*) malloc(sizeof(pthread_t));
if(!pthread){
printf("main(): malloc() for pthread failed\n");
close(accept_sockfd);
continue;
}
memset(pthread, 0, sizeof(pthread_t));
if(pthread_create(pthread, NULL, connection_handler, &accept_sockfd) < 0){
printf("main(): pthread_create() failed\n");
free(pthread);
close(accept_sockfd);
return -1;
}
/* wait until the connection_handler thread get accept_sockfd */
while(accept_sockfd != SOCKFD_USED){
usleep(10);
}
}
return 0;
}
/*
* Once application sends a socket() requests, a thread with
* connection_handler() is created to deal with the application's
* socket.
*
* connection_handler()'s main routines are
* 1) receive requests from app's SAL via local socket;
* 2) call corresponding functions based the request type;
* 3) send results to app's SAL via local socket.
*
*/
void* connection_handler(void * accept_sockfd)
{
int sockfd = *(int*)accept_sockfd;
int shmid;
char *sockbuf = NULL;
char *shm_sockbuf = NULL;
int res, reslen;
struct sal_api_req *sal_req;
struct sal_api_res *daemon_res;
char shm_key_file[64];
unsigned long shm_sockbuf_size;
key_t shm_key;
struct shmid_ds shmid_buf;
sigset_t signal_set;
#ifdef AUTO_EXIT_FOR_DEBUG
int can_exit = 0;
#endif
/*
* Block all signals in this thread.
*/
sigfillset ( &signal_set );
pthread_sigmask (SIG_BLOCK, &signal_set, NULL );
UPTCP_DAEMON_DEBUG_PRINT(">> connection_handler(): new connection\n")
*(int*)accept_sockfd = SOCKFD_USED; /* let main thread continue to run */
sockbuf = (char*)malloc(MAX_SOCKBUF_LEN);
/*
* Although both sal_req and daemon_res point to sockbuf, the sockbuf
* is used exclusively, either sal_api_req for receving or sal_api_res for
* sending results.
*/
sal_req = (struct sal_api_req*)sockbuf;
daemon_res = (struct sal_api_res*)sockbuf;
while((res = recv(sockfd, sockbuf, MAX_SOCKBUF_LEN, 0)) > 0){
switch(sal_req->type){
case UPTCP_REQ_SEND:
UPTCP_DAEMON_DEBUG_PRINT(">> connection_handler(): sockfd = %d, type = %s, len = %d\n",
sockfd, req_descriptor[sal_req->type], ((struct sal_send_args *)sal_req->args)->len)
break;
default:
UPTCP_DAEMON_DEBUG_PRINT(">> connection_handler(): sockfd = %d, type = %s\n",
sockfd, req_descriptor[sal_req->type]);
}
reslen = sizeof(int); /* daemon_res->res*/
/* call freebsd's TCP/IP stack */
switch(sal_req->type){
case UPTCP_REQ_SOCKET:
res = bsd_syscall_socket(sockfd, &sal_req->args);
break;
case UPTCP_REQ_BIND :
res = bsd_syscall_bind(sockfd, &sal_req->args);
break;
case UPTCP_REQ_LISTEN:
res = bsd_syscall_listen(sockfd, &sal_req->args);
#ifdef AUTO_EXIT_FOR_DEBUG
can_exit = 1;
#endif
break;
case UPTCP_REQ_ACCEPT:
res = bsd_syscall_accept(sockfd, &sal_req->args, &daemon_res->retval);
if(res >= 0)
reslen += sizeof(struct sal_accept_res);
break;
case UPTCP_REQ_CONNECT:
res = bsd_syscall_connect(sockfd, &sal_req->args);
break;
case UPTCP_REQ_SOCKETPAIR:
res = bsd_syscall_socketpair(sockfd, &sal_req->args);
break;
case UPTCP_REQ_SENDTO:
res = bsd_syscall_sendto(sockfd, &sal_req->args);
break;
case UPTCP_REQ_SEND:
res = bsd_syscall_send(sockfd, &sal_req->args, shm_sockbuf);
break;
case UPTCP_REQ_SENDMSG:
res = bsd_syscall_sendmsg(sockfd, &sal_req->args);
break;
case UPTCP_REQ_RECVFROM:
res = bsd_syscall_recvfrom(sockfd, &sal_req->args);
break;
case UPTCP_REQ_RECV:
res = bsd_syscall_recv(sockfd, &sal_req->args, shm_sockbuf);
break;
case UPTCP_REQ_RECVMSG:
res = bsd_syscall_recvmsg(sockfd, &sal_req->args);
break;
case UPTCP_REQ_SHUTDOWN:
res = bsd_syscall_shutdown(sockfd, &sal_req->args);
#ifdef AUTO_EXIT_FOR_DEBUG
can_exit = 2;
#endif
break;
case UPTCP_REQ_SETSOCKOPT:
res = bsd_syscall_setsockopt(sockfd, &sal_req->args);
break;
case UPTCP_REQ_GETSOCKOPT:
res = bsd_syscall_getsockopt(sockfd, &sal_req->args);
break;
case UPTCP_REQ_GETSOCKNAME:
res = bsd_syscall_getsockname(sockfd, &sal_req->args);
break;
case UPTCP_REQ_GETPEERNAME:
res = bsd_syscall_getpeername(sockfd, &sal_req->args);
break;
case UPTCP_REQ_SOCKFD:
res = sockfd;
break;
case UPTCP_REQ_MALLOC:
/* allocate share-memory */
#ifdef UPTCP_CLIENT
sprintf(shm_key_file, "/tmp/uptcp_shmkey_c%d.dat", sockfd);
#else
sprintf(shm_key_file, "/tmp/uptcp_shmkey_s%d.dat", sockfd);
#endif
shm_key = ftok(shm_key_file, 1);
shm_sockbuf_size = *((unsigned long*)(&sal_req->args));
if((shmid = shmget(shm_key, shm_sockbuf_size, IPC_CREAT | S_IRUSR | S_IWUSR)) < 0){
printf("Error: shmget error\n");
goto err;
}
if((shm_sockbuf = (void *) shmat(shmid, NULL, 0)) == (void*)-1){
printf("Error: shmat error\n");
goto err;
}
res = 0;
break;
case UPTCP_REQ_FREE:
shmctl(shmid, IPC_RMID, &shmid_buf);
if(shm_sockbuf != NULL){
shmdt(shm_sockbuf);
shm_sockbuf = NULL;
}
res = 0;
break;
default:
printf("connection_handler(): received SAL_req type error\n");
break;
}
UPTCP_DAEMON_DEBUG_PRINT("~~ connection_handler(): sockfd = %d, type = %s, req__res = %d\n",
sockfd, req_descriptor[sal_req->type], res)
/* send results to app's SAL */
daemon_res->res = res;
res = send(sockfd, sockbuf, reslen, 0);
if (res < 0){
printf("connection_handler: send results failed\n");
goto failed;
}
#ifdef AUTO_EXIT_FOR_DEBUG
if(can_exit == 2){
//printf("raw = %lld, tim = %lld, %8.2f\n", stat_raw_cnt, stat_raw_time/stat_raw_cnt, stat_raw_time/stat_raw_cnt/2600.0);
//printf("rx = %lld, tim = %lld, %8.2f\n", stat_rx_cnt, stat_rx_time/stat_rx_cnt, stat_rx_time/stat_rx_cnt/2600.0);
//if(stat_sock_cnt != 0)
// printf("sock = %lld, tim = %lld, %8.2f\n", stat_sock_cnt, stat_sock_time/stat_sock_cnt, stat_sock_time/stat_sock_cnt/2600.0);
//exit(-1); /*just for DEBUG: leave memory unfree*/
goto done;
}
#endif
}
failed:
if (res < 0){
printf("connection_handler(): recv() failed\n");
close(sockfd);
free(sockbuf);
}
err:
done:
close(sockfd);
if(sockbuf != NULL)
free(sockbuf);
exit(-1);
pthread_exit(NULL);
return NULL;
}
void* interrupt_handler(void* data)
{
pthread_exit(NULL);
return NULL;
}
void display_errno(int num)
{
switch(errno){
case EAGAIN: printf("errno: EAGAIN\n"); break;
case EBADF: printf("errno: EBADF\n"); break;
case ECONNABORTED: printf("errno: ECONNABORTED\n"); break;
case EINTR: printf("errno: EINTR\n"); break;
case EINVAL: printf("errno: EINVAL\n"); break;
case EMFILE: printf("errno: EMFILE\n"); break;
case ENFILE: printf("errno: ENFILE\n"); break;
case ENOTSOCK: printf("errno: ENOTSOCK\n"); break;
case EOPNOTSUPP: printf("errno: EOPNOTSUPP\n"); break;
case EFAULT: printf("errno: EFAULT\n"); break;
case ENOBUFS: printf("errno: ENOBUFS\n"); break;
case ENOMEM: printf("errno: ENOMEM\n"); break;
case EPROTO: printf("errno: EPROTO\n"); break;
case EPERM: printf("errno: EPERM\n"); break;
default: printf("errno: Unknown\n");
}
}
/*
* Backup Codes
*/
#if 0
int rawsock_id;
/* register signal handler */
struct sigaction sa[2];
const char * key_file = "/tmp/uptcp_key.dat";
int msqid;
key_t msg_key;
int tmpfd;
struct mymsgbuf msg_recvbuf;
int error;
char sockbuf[MAX_MSGSIZE];
int pktlen;
/* create a raw socket */
rawsock_id = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_ALL));
if(rawsock_id == -1){
printf("Cann't create a packet socket\n");
return -1;
}
/* create a msgqueue */
if((tmpfd = open(key_file, O_CREAT | O_RDWR, S_IRWXU)) < 0){
printf("Error: open keyFile error\n");
return(-1);
}
close(tmpfd);
msg_key = ftok(key_file, 1);
if((msqid = msgget(msg_key, (IPC_CREAT | 0666) )) < 0){
printf("Error: msgget error\n");
switch(errno){
case EACCES: printf("EACCESS\n"); break;
case EEXIST: printf("EEXIST\n"); break;
case ENOENT: printf("ENOENT\n"); break;
case ENOMEM: printf("ENOMEM\n"); break;
case ENOSPC: printf("ENOSPC\n"); break;
}
return(-1);
}
/* receive msg */
while(1) {
/* check msg queue */
error = msgrcv(msqid, &msg_recvbuf, MAX_MSGSIZE, 0, IPC_NOWAIT);
if(error < 0 && errno != ENOMSG){
printf("ERROR: msgrcv\n");
switch(errno){
case E2BIG: printf("E2BIG\n"); break;
case EACCES: printf("EACCES\n"); break;
case EAGAIN: printf("EAGAIN\n"); break;
case EFAULT: printf("EFAULT\n"); break;
case EIDRM: printf("EIDRM\n"); break;
case EINTR: printf("EINTR\n"); break;
case EINVAL: printf("EINVAL\n"); break;
case ENOMSG: printf("ENOMSG\n"); break;
default: printf("errno=%d\n", errno); break;
}
return -1;
}
if(error >= 0){
/* we receive a msg */
switch(msg_recvbuf.mtype){
case UPTCP_REQ_SEND:
break;
case UPTCP_REQ_HASHTBL:
break;
default:
break;
}
}
/* check incoming data from raw socket*/
while ((pktlen = read (rawsock_id, sockbuf, MAX_MSGSIZE)) > 0){
//lookup hash table to find a msgid
//send to up layer via msgid
//printf("pktlen = %d\n", pktlen);
}
}
close(msqid);
#endif
/*
* Discard the following codes
*/
#if 0
/*
* Signals
*/
int daemon_SIGUSR1_raised = 0; /* for request */
int daemon_SIGUSR2_raised = 0; /* for interrupt */
#endif //0
#if 0
int main()
{
memset(sa, 0, 2 * sizeof(sigaction));
sa[0].sa_handler = daemon_SIGUSR1_handler;
sa[1].sa_handler = daemon_SIGUSR2_handler;
sigaction(SIGUSR1, &sa[0], NULL);
sigaction(SIGUSR2, &sa[1], NULL);
for(;;){
if(!daemon_SIGUSR1_raised && !daemon_SIGUSR2_raised
&& !daemon_SIGALRM_raised){
pause();
}
if(daemon_SIGUSR1_raised){
daemon_SIGUSR1_raised = 0;
foward_signal_to_request_handler_thread();
}
if(daemon_SIGUSR2_raised){
daemon_SIGUSR2_raised = 0;
foward_signal_to_interrupt_handler_thread();
}
}
}
#endif //0
#if 0
void daemon_SIGUSR1_handler()
{
daemon_SIGUSR1_raised = 1; /* for request */
}
void daemon_SIGUSR2_handler()
{
daemon_SIGUSR2_raised = 1; /* for interrupt */
}
int foward_signal_to_request_handler_thread()
{
int i;
for(i = 0; i < UPTCPIP_MAX_SOCKET; i ++){
if(req_queue[i].state == UPTCPIP_ACTIVE){
}
}
return 0;
}
int foward_signal_to_interrupt_handler_thread()
{
return 0;
}
int handle_timer()
{
return 0;
}
#endif //0