/* * @(#) $Id: rsvp_main.c,v 4.67 1998/08/19 19:45:38 lindell Exp $ */ /************************ rsvp_main.c ******************************* * * * RSVP daemon: main routine, initialization, and I/O control * * routines. Generally, system-dependent code. * * * *********************************************************************/ /**************************************************************************** RSVPD -- ReSerVation Protocol Daemon USC Information Sciences Institute Marina del Rey, California Original Version: Shai Herzog, Nov. 1993. Current Version: Steven Berson & Bob Braden, May 1996 Copyright (c) 1996 by the University of Southern California All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation in source and binary forms for any purpose and without fee is hereby granted, provided that both the above copyright notice and this permission notice appear in all copies, and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed in part by the University of Southern California, Information Sciences Institute. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Other copyrights might apply to parts of this software and are so noted when applicable. ********************************************************************/ #define __MAIN__ #include "rsvp_daemon.h" #include "rsvp_api.h" #include #include #include #ifdef SOLARIS #include #include /* for SIOCGIFCONF (for SOLARIS) */ #include /* for TIOCNOTTY (for SOLARIS) */ #endif /* SOLARIS */ static struct rlimit limits; #ifdef LABEL #include #endif #define RSVP_LOCK_PORT 5434 /* port number for rsvpd lock */ static int lockfd = SYS_ERROR; /* fd for holding rsvpd lock */ static char rsvp_log_suffix[16]; /* Data structure for main select() loop */ static fd_set fds; /* bit vector for select */ /* Sockets and addresses */ static int api_socket; /* Unix listen sock for API */ /* static int api_udp_socket; */ /* UDP listen sock for API */ int probe_socket; /* Sock to rcv status probe */ int mstat_ttl = 1; /* IP TTL for multicasting status: * Note small default! */ int refresh_default = REFRESH_DEFAULT; /* Default R */ int api_refresh_t = API_REFRESH_T; /* Default API refresh T */ char recv_buff[MAX_PKT + sizeof(struct ip)]; extern struct in_addr encap_group; /* Gu */ extern u_int16_t encap_port; /* Pu */ extern u_int16_t encap_portp; /* Pu' */ extern net_addr *encap_router; extern net_addr encap_mc_addr; #ifdef LABEL extern int cbqfd; #endif #ifdef USE_IPV6 extern struct in6_addr encap_group6; /* Gu */ extern net_addr encap_mc_addr6; #endif /* USE_IPV6 */ #ifdef SOLARIS char *solconfigfile = "/etc/rsvpd.conf"; char *stdconfigfile = "/etc/rsvpd.isi.conf"; #endif #ifdef ALTQ char *altqconfigfile = "/etc/altq.conf"; #endif #if defined(SOLARIS) || defined(ALTQ) int Allow_Guaranteed = 0; #endif /* extern int use_udp; */ /* * Test facility variables */ #define MAX_DEBUG_FILTER 10 net_addr debug_filters[MAX_DEBUG_FILTER]; int debug_filter_num = 0; int debug_filter = 0; int Test_mode = 0; net_addr *tsttun_vec; int *tst_sock2if; /* * Global RSRR variables. */ #ifndef HOST_ONLY extern struct sockaddr_un cli_addr_v4; /* Client address */ #ifdef USE_IPV6 extern struct sockaddr_un cli_addr_v6; /* Client address */ #endif /* USE_IPV6 */ #endif /* ! HOST_ONLY */ #ifdef LABEL /* Label definitions */ extern int Init_Table(); #endif /* External RSRR definitions. */ extern int rsrr_socket; /* interface to reservation protocol */ #ifdef USE_IPV6 extern int rsrr_socket_v6; #endif /* USE_IPV6 */ extern int rsrr_initial_reply; extern int rsrr_dispatch(); #ifndef HOST_ONLY extern int rsrr_interface_query(); /* RSRR Interface Query */ #if defined(PF_ROUTE) && !defined(sgi_53) extern int rskt; #endif /* PF_ROUTE */ #endif /* ! HOST_ONLY */ /* External declarations */ int process_api_req(int, rsvp_req *, int); int api_status(rsvp_req *); extern char vers_comp_date[]; /* version & compilation date/time */ void hexf(); void del_from_timer(); u_long next_event_time(); void init_route(); void init_probe_socket(); void send_rsvp_state(char *, int, struct sockaddr_in *); void api_free_packet(struct packet *); int next_word(char **, char *), pfxcmp(char *, char *); void KernTC_if_init(int); void ntoh_api_request(rsvp_req *, int); #ifdef RTAP void rtap_init(), rtap_loop(fd_set *), rtap_command(); void rtap_dispatch(); int rtap_insock, rtap_fd; #endif /* forward declarations */ void rsvp_api_close(int); static int init_rsvp(void); int save_pid(void); static void sigpipe(int); static void final(); int api_input(int); int status_probe(int ifd); int start_UDP_encap(int); int api_cmd(int, rapi_cmd_t *); static void sigint_handler(int); int find_sid(int fd, int pid, int client_sid); int find_free_sid(int fd, int pid, int client_sid); void print_ifs(); void read_config_file(char *); void cfig_action(int, char *, char *); void cfig_do(char **, char *); KEY_ASSOC *load_key(char *, char *); void ntoh_rapi_cmd(rapi_cmd_t *); void Usage(char *namep) { #if defined(SOLARIS) || defined(ALTQ) fprintf(stderr, "Usage: %s [-D] [-d debug_bits] [-l debug_level] [-t mstat_ttl] [-R encap_addr] [-f cbq_config_file] [-g]\n", namep); #else fprintf(stderr, "Usage: %s [-D] [-d debug_bits] [-l debug_level] [-t mstat_ttl] \n", namep); #endif } /* * Define table giving syntax of configuration file */ typedef struct { char *cfig_keywd; char cfig_flags; #define CFF_CMDOP 0x01 #define CFF_HASPARM 0x02 #define CFF_HASPARM2 0x04 char cfig_actno; } Cfig_element; enum { CfAct_iface, CfAct_police, CfAct_udp, CfAct_udpttl, CfAct_intgr, CfAct_disable, CfAct_sndttl, CfAct_refresh, CfAct_sendkey,CfAct_recvkey }; /* interface [police] [udp] [udpttl <#>] [integrity] * [disable] [sendttl <#>] [refresh <#>] * * XXX Should add: [type ] => call appropriate * XXX LL interface vector initialization routine: * XXX _if_init(if#) * XXX Default is: KernTC_if_init(if#) */ Cfig_element Cfig_Table[] = { {"interface", CFF_CMDOP+CFF_HASPARM, CfAct_iface}, {"police", 0, CfAct_police}, {"udpencap", 0, CfAct_udp}, {"udpttl", CFF_HASPARM, CfAct_udpttl}, {"integrity", 0, CfAct_intgr}, {"disable", 0, CfAct_disable}, {"refresh", CFF_HASPARM, CfAct_refresh}, {"sendkey", CFF_HASPARM2, CfAct_sendkey}, {"sendttl", CFF_HASPARM, CfAct_sndttl}, {"recvkey", CFF_CMDOP+CFF_HASPARM2, CfAct_recvkey}, {NULL, 0, 0} /* Fence: ends table */ }; int Cfig_if; /* Global interface number for config file */ Cfig_element *cfig_search(char *); /* * * The main() routine for rsvpd, the RSVP daemon. * */ int main(int argc, char *argv[]) { int fd_wid, rc, fd_val, newsock, zero = 0; fd_set tmp_fds, rset; extern char *optarg; extern int optind; u_int c; struct timeval t_intvl; struct timeval t_old, t_new; int delta; int Daemonize = 1; int Rflag = 0; char *R_arg; /* * Set defaults */ m_debug = l_debug = -1; Max_rsvp_msg = MAX_PKT; /* * Process options */ #if defined(SOLARIS) || defined(ALTQ) while ((c = getopt(argc, argv, "Dd:l:t:R:r:s:xf:g")) != -1) { #else while ((c = getopt(argc, argv, "Dd:l:t:R:r:")) != -1) { #endif switch (c) { #if defined(SOLARIS) || defined(ALTQ) case 'f': #if defined(SOLARIS) solconfigfile = optarg; #elif defined(ALTQ) altqconfigfile = optarg; #endif break; case 'g': Allow_Guaranteed = 1; break; #endif case 'D': /* -D => debug mode, i.e. do not daemonize */ Daemonize = 0; break; case 'd': m_debug = atoi(optarg); break; case 'l': l_debug = atoi(optarg); break; case 't': /* -t: specify TTL for multicasting status info */ mstat_ttl = atoi(optarg); break; case 'R': /* -R: specify name/address of RSVP-capable * router to receive encapsulated Path messages. */ Rflag = 1; R_arg = optarg; break; #ifdef DEBUG case 's': /* max message size (for debugging) */ Max_rsvp_msg = atoi(optarg); break; case 'x': /* -x: test mode. */ Test_mode = 1; break; case 'r': /*default refresh timeout period in ms */ refresh_default = /* api_refresh_t = */ atoi(optarg); break; #endif case '?': default: Usage(argv[0]); exit(1); } } if (optind != argc) { Usage(argv[0]); exit(1); } /* * If there is already an RSVP daemon running (as shown by existence * of pid file), exit immediately. */ if ((rc = test_alive())) { fprintf(stderr, "RSVP daemon is already running, with PID=%d\n", rc); exit(-1); } /* Set debugging defaults if none given */ if (m_debug < 0) m_debug = (Daemonize)? BK_DFLT_DEBUG_MASK : FG_DFLT_DEBUG_MASK; if (l_debug < 0) l_debug = (Daemonize)? BK_DFLT_LOGGING_LEVEL: FG_DFLT_LOGGING_LEVEL; (void) getrlimit(RLIMIT_NOFILE, &limits); /* * Daemonize, if requested. */ if (Daemonize) { #if BSD >= 199306 daemon(1, 0); #else #ifdef sgi _daemonize(0, -1, -1, -1); #else int i; if (fork()) exit(0); for (i = 0; i < limits.rlim_cur; i++) if (i != lockfd) (void) close(i); (void) open("/", O_RDONLY); (void) dup2(0, 1); (void) dup2(0, 2); i = open("/dev/tty", O_RDWR); if (i >= 0) { (void) ioctl(i, (int) TIOCNOTTY, (char *)0); (void) close(i); } #endif /* sgi */ #endif /* BSD */ } /* * Initialize RSVP */ signal(SIGPIPE, sigpipe); signal(SIGINT, sigint_handler); signal(SIGTERM, sigint_handler); #ifdef SunOS on_exit(final,0); #else /* SunOS */ atexit(final); #endif /* SunOS */ reset_log(1); /* Initialize log and print header */ if (Rflag) { static net_addr addr; if (net_addr_ascii(&addr,R_arg)) encap_router = &addr; else log(LOG_ERR, 0, "Unknown router %s", R_arg); } if (save_pid() == -1) { log(LOG_ERR, 0, "save_pid() failed!\n"); exit(-1); } if (init_rsvp() == -1) { log(LOG_ERR, 0, "RSVP initialization failed.\n", 0); exit(-1); } if (!NoV4Mroute) if (geteuid() != 0) { log(LOG_ERR, 0, "RSVP must be run with root privilege on a router.\n", 0); exit(-1); } FD_SET(api_socket, &fds); /* FD_SET(api_udp_socket, &fds); */ FD_SET(probe_socket, &fds); #ifndef HOST_ONLY if (!NoV4Mroute) { FD_SET(rsrr_socket, &fds); } #ifdef USE_IPV6 if (!NoV6Mroute) { FD_SET(rsrr_socket_v6, &fds); } #endif /* USE_IPV6 */ #if defined(PF_ROUTE) && !defined(sgi_53) /* if (!NoUnicast) { FD_SET(rskt, &fds); } */ #endif /* PF_ROUTE */ #endif /* ! HOST_ONLY */ #ifdef RTAP rtap_fd = rtap_insock = -1; if (!Daemonize) rtap_init(); #endif /* XXX Configure max # simultaneous API sessions (=> fd_wid) ?? */ /* * Main control loop */ #ifdef SOLARIS fd_wid = sysconf(_SC_OPEN_MAX); #else fd_wid = getdtablesize(); #endif for (;;) { memcpy((char *) &tmp_fds, (char *) &fds, sizeof(fds)); #ifdef RTAP if (!Daemonize) rtap_loop(&tmp_fds); #endif gettimeofday(&t_old, NULL); delta = next_event_time(); if (delta == -1) rc = select(fd_wid, &tmp_fds, (fd_set *) NULL, (fd_set *) NULL, NULL); else { delta -= time_now; t_intvl.tv_sec = delta/1000; t_intvl.tv_usec = 1000*(delta%1000); rc = select(fd_wid, &tmp_fds, (fd_set *) NULL, (fd_set *) NULL, &t_intvl); } if (rc == -1) { log(LOG_ERR, errno, "Bad return from select()\n"); assert(errno == EINTR); continue; } gettimeofday(&t_new, NULL); delta = (t_new.tv_sec - t_old.tv_sec) * 1000 + (t_new.tv_usec - t_old.tv_usec)/1000; time_now += delta; timer_wakeup(0); assert(rc >= 0); net_poll_list(&rset); for (fd_val = 0; rc--; fd_val++) { /* Locate the next fd bit that is set, then dispatch * on it. */ while (!FD_ISSET(fd_val, &tmp_fds)) fd_val++; #ifndef HOST_ONLY if (fd_val == rsrr_socket && !NoV4Mroute) { rsrr_dispatch(); } else #ifdef USE_IPV6 if (fd_val == rsrr_socket_v6 && !NoV6Mroute) { rsrr_dispatch(); } else #endif /* USE_IPV6 */ #endif /* ! HOST_ONLY */ if (fd_val == api_socket) { /* * API connection request from client; get a * new socket for this connection and add it * to the select list. */ newsock = accept(api_socket, (struct sockaddr *) NULL, &zero); if (newsock == -1) { log(LOG_ERR, errno, "accept error\n"); continue; } FD_SET(newsock, &fds); } else if (fd_val == probe_socket) { /* Status probe packet arrived. */ status_probe(fd_val); } #ifdef RTAP else if (fd_val == rtap_insock) rtap_command(); else if (fd_val == rtap_fd) rtap_dispatch(); #endif /* RTAP */ else if (FD_ISSET(fd_val,&rset)) rsvp_input(fd_val); /* else if (fd_val == api_udp_socket) { use_udp = 1; api_input(fd_val); } */ #if defined(PF_ROUTE) && !defined(sgi_53) else if (fd_val != rskt) { /* * RSVP API input from UNIX socket */ /* use_udp = 0; */ api_input(fd_val); } else rsrr_dispatch(); #else else { /* * RSVP API input from UNIX socket */ /* use_udp = 0; */ api_input(fd_val); } #endif /* PF_ROUTE */ } } } static void final() { net_final(); #ifndef HOST_ONLY /* Clean up the RSRR socket. */ unlink(cli_addr_v4.sun_path); #ifdef USE_IPV6 unlink(cli_addr_v6.sun_path); #endif /* USE_IPV6 */ #endif /* ! HOST_ONLY */ unlink(RSVP_PID_FILE); unlink(SNAME); } static void sigpipe(int sig) { log(LOG_DEBUG, 0, "Got a SIGPIPE\n"); signal(SIGPIPE, sigpipe); } static void sigint_handler(int sig) { log(LOG_ERR, 0, "Exiting on signal %d\n", sig); final(); _exit(sig); } /* * init_rsvp(): Initialize rsvpd. Read the vif info from the * kernel, initialize the sockets, and start the timer. */ static int init_rsvp(void) { int i; extern STYLE Style_Obj; /* Zero out and initialize headers of some global objects */ Init_Object(&Style_Obj, STYLE, STYLE_CTYPE); FD_ZERO(&fds); /* Misc initializations */ memset(session_hash, 0, SESS_HASH_SIZE*sizeof(Session *)); memset(key_assoc_table, 0, KEY_TABLE_SIZE * sizeof(KEY_ASSOC)); Key_Assoc_Max = 0; /* * Initialize encapsulation group G* and ports Pu, Pu', and * set up encapsulation socket addr structure. */ if (inet_pton(AF_INET,RSVP_ENCAP_GROUP,&encap_group) != 1){ log(LOG_ERR, errno, "Encap grp addr", 0); return(-1); } encap_port = hton16((u_int16_t) RSVP_ENCAP_PORT); encap_portp = hton16((u_int16_t) RSVP_ENCAP_PORTP); NET_SET_ADDR3_UDP_IPv4(&encap_mc_addr,encap_group,encap_port) #ifdef USE_IPV6 if (inet_pton(AF_INET6,RSVP_ENCAP_GROUP6,&encap_group6) != 1){ log(LOG_ERR, errno, "Encap grp addr 6", 0); return(-1); } NET_SET_ADDR3_UDP_IPv6(&encap_mc_addr6,encap_group6,encap_port) #endif /* USE_IPV6 */ rsrr_interface_query(1); #ifdef USE_IPV6 if (FAILED(net_init(encap_port,encap_portp, &encap_group,&encap_group6,FALSE))) { #else /* USE_IPV6 */ if (FAILED(net_init(encap_port,encap_portp,&encap_group,FALSE))) { #endif /* USE_IPV6 */ log(LOG_ERR,errno,"net_init"); return(SYS_ERROR); } /* In case mrouted is slow */ rsrr_initial_reply = 1; rsrr_dispatch(); /* * Look for a configuration file and apply it. For interfaces * whose LL init routine is not specified in config file, set * default LL (pt-pt|LAN) */ read_config_file("rsvpd.conf"); for (i = 0; i < if_num; i++) { if (IsNumAPI(i)) continue; if (*if_vec[i].if_LLifv.LL_NewFlow_p == NULL) KernTC_if_init(i); /* default traffic control */ } print_ifs(); #ifdef LABEL /* * Build table of labels that can be used for label- * switching on the interfaces of this machine * */ if(Init_Table() < 0) { log(LOG_ALWAYS, 0, "No Labelinitialization\n"); return -1; } #endif if (FAILED(net_poll_list(&fds))) { log(LOG_ERR,errno,"net_poll_list"); return(SYS_ERROR); } /* Initialize: * -- probe_socket: socket for receiving status probes * -- api_socket: API listen socket * -- Timer routines * -- kernel traffic control */ init_probe_socket(); init_api(); init_timer(MAX_TIMER_Q); return (0); } int status_probe(int ifd) { char recv_buf[MAX_PKT]; struct sockaddr_in from; int from_len, recv_len; from_len = sizeof(from); memset((char *) &from, 0, from_len); recv_len = recvfrom(ifd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) & from, &from_len); if (recv_len < 0) { log(LOG_ERR, errno, "recvfrom", 0); return(-1); } send_rsvp_state(recv_buf, recv_len, &from); return(0); } /* * start_UDP_encap(vif_no): Called to initialize for UDP encapsulation * on specified vif number. */ int start_UDP_encap(int vif) { IF_FLAGS(vif) |= IF_FLAG_UseUDP; return(0); } #define Clear_LL_Vector(n) memset(&if_vec[n].if_LLifv, 0, sizeof(LLDAL_calls_t)) /* * if_list_init(): * Initialize table describing physical interfaces, * if_vec[0...if_num]. Return number of interfaces if_num. * The last element corresponds to the API. */ int if_list_init() { int if_num = 0; struct ifreq ifr; struct if_nameindex *p,*q; struct if_attributes *l; struct sockaddr *s; net_addr addr; #if DEBUG if (Test_mode) { /* Test mode: read file named .rsvp.ifs containing: * */ FILE *fp = fopen(".rsvp.ifs", "r"); char buff[80], ipaddrstr[64], ifname[32], rmtaddrstr[64]; if (fp == NULL) { fprintf(stderr, "Error reading .rsvpifs\n"); exit(1); } if_vec = (if_rec *) calloc(MAX_INTERFACES, sizeof(if_rec)); tsttun_vec = (net_addr *) calloc(RSRR_MAX_VIFS_V2+1, sizeof(net_addr)); tst_sock2if = (int *) calloc(256, sizeof(int)); memset(tst_sock2if, -1, 4*256); while (fgets(buff, sizeof(buff), fp)) { if (if_num >= (MAX_INTERFACES - 1)) { fprintf(stderr, ".rsvpifs file too big\n"); exit(1); } sscanf(buff, "%s %s %s", ifname, ipaddrstr, rmtaddrstr); if_vec[if_num].if_flags = 0; if_vec[if_num].if_index = if_num; if_vec[if_num].if_unicast = if_num; strncpy(if_vec[if_num].if_name, ifname, IFNAMSIZ); net_addr_ascii(&addr,ipaddrstr); NET_SET_IF_PHY(&if_vec[if_num].if_addr,addr,if_num); net_addr_ascii(&tsttun_vec[if_num],rmtaddrstr); if_num++; } strcpy(if_vec[if_num].if_name, "API"); if_vec[if_num].if_unicast = if_num; api_num = if_num; return(++if_num); } #endif p = if_nameindex(); if(p == NULL) return(0); if_vec = (if_rec *) calloc(MAX_INTERFACES, sizeof(if_rec)); for(q = p; q->if_name != NULL; q++) { if (strncmp("lo0",q->if_name,IFNAMSIZ) == 0) continue; #ifdef SOLARIS if (strncmp("lo0#v6",q->if_name,IFNAMSIZ) == 0) continue; #endif /* SOLARIS */ l = if_attributes(q->if_index); for(s = l->addr; s != NULL; s = (++l)->addr) { switch (s->sa_family) { case AF_INET: if (local_v4 == -1) if (l->flags&IFF_UP) local_v4 = if_num; break; #ifdef USE_IPV6 case AF_INET6: if (local_v6 == -1) if (l->flags&IFF_UP) local_v6 = if_num; break; #endif /* USE_IPV6 */ default: continue; } if (if_num == (MAX_INTERFACES - 1)) { log(LOG_ERR,0,"Too Many Network Interfaces"); break; } strncpy(if_vec[if_num].if_name, q->if_name, IFNAMSIZ); net_addr_assign(&addr,s); NET_SET_IF_PHY(&if_vec[if_num].if_addr,addr, q->if_index); if_vec[if_num].if_index = q->if_index; if_vec[if_num].if_unicast = if_num; strncpy(ifr.ifr_name,q->if_name,sizeof(ifr.ifr_name)); if_vec[if_num].if_flags = (l->flags&IFF_UP) ? IF_FLAG_IFF_UP : 0; if_vec[if_num].if_flags |= (l->flags&IFF_MULTICAST) ? IF_FLAG_IFF_MC : 0; if_vec[if_num].if_udpttl = RSVP_TTL_ENCAP; if_vec[if_num].prefix = l->prefix; Clear_LL_Vector(if_num); /* for safety */ if_num++; } } if_freenameindex(p); /* Add one more entry corresponding to API */ Clear_LL_Vector(if_num); /* No intserv link layer for API */ strcpy(if_vec[if_num].if_name, "API"); if_vec[if_num].if_unicast = if_num; if_vec[if_num].if_flags = IF_FLAG_IFF_UP; /* Well, yes */ if_vec[if_num].prefix = 0; NET_SET_IF_PHY(&if_vec[if_num].if_addr,api_address,0); api_num = if_num; return (++if_num); } /* * init_api() initializes the api between the daemon and client applications. * The daemon listens for incoming requests (coming on a unix socket) from * application, and stores some local info about these requests. This info * includes a copy of an RSVP packet with the needed info, that will be * refreshed and throughn in every refresh period. */ void init_api() { int i; struct sockaddr_un server; /* struct sockaddr_in myaddr; */ int addr_len; memset(api_table, 0, sizeof(api_table)); /* if ((api_udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { log(LOG_WARNING, errno, "opening api_udp_socket\n"); } else { myaddr.sin_family = htons(AF_INET); myaddr.sin_port = htons(API_PORT); myaddr.sin_addr.s_addr = 0; if (FAILED(bind(api_udp_socket,(struct sockaddr *)&myaddr, sizeof(myaddr)))) { log(LOG_WARNING, errno, "bind() for api_udp_socket\n"); } } */ (void) unlink(SNAME); /* The unix socket name */ if ((api_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { log(LOG_WARNING, errno, "opening socket\n"); assert(NULL); /* i.e. abort() */ } server.sun_family = AF_UNIX; (void) strcpy(server.sun_path, SNAME); #ifdef STANDARD_C_LIBRARY /* * This is only necessary for Net/2 and later, but it's never * incorrect on earlier systems, so do it. */ addr_len = (offsetof(struct sockaddr_un, sun_path) + strlen(server.sun_path)); #else addr_len = sizeof(server.sun_family) + strlen(server.sun_path); #endif #ifdef SOCKADDR_LEN server.sun_len = addr_len; #endif if (bind(api_socket, (struct sockaddr *) & server, addr_len) < 0) { log(LOG_WARNING, errno, "bind"); assert(NULL); /* i.e. abort() */ } if (chmod(SNAME,SMODE) == -1) log(LOG_WARNING, errno, "chmod of API socket"); i = 1; #ifdef SOLARIS if (fcntl(api_socket, F_SETFL, (fcntl(api_socket, F_GETFL) | O_NONBLOCK)) == -1) { log(LOG_ERR, errno, "Setting Non-blocking I/O\n"); exit(-1); } #else if (ioctl(api_socket, FIONBIO, &i) == -1) { log(LOG_ERR, errno, "Setting Non-blocking I/O\n"); exit(-1); } #endif /* SOLARIS */ (void) listen(api_socket, 10); /* Maximum pending req. at one time */ } /* * api_input(): reads an API request from UNIX pipe, and processes it. * First 4 bytes are length of following request. */ int api_input(int fd) { int rc, len, nr; int sid; char recv_buf[MAX_MSG]; rsvp_req *req = (rsvp_req *) recv_buf; int ret_val = 0; /* if (fd == api_udp_socket) rc = recvfrom(fd,recv_buf,sizeof(recv_buf),0,NULL,NULL); else { */ rc = read(fd, &len, sizeof(int)); #ifdef USE_NET_BO NTOH32(len); #endif if (rc != sizeof(int)) { if (rc == 0) { /* * Application quit or died rather than closed * the RSVP connection; not an error. */ ret_val = 0; goto err_exit; } else { log(LOG_ERR, errno, "Error in API read\n"); ret_val = -1; goto err_exit; } } nr = len; while (nr) { rc = read(fd, (char *) req, nr); if (rc <= 0) { ret_val = RAPI_ERR_NORSVP; goto err_exit; } nr -= rc; } /* } */ if (req->rq_type == API_DEBUG) { char *cmd = (char *)req + sizeof(rsvp_req); ntoh_rapi_cmd((rapi_cmd_t *)cmd); return(api_cmd(fd, (rapi_cmd_t *)cmd)); } else if (req->rq_type == API2_STAT) /* Trigger event upcalls to return status */ return(api_status(req)); /* if (NetByteOrderReq(req) || fd == api_udp_socket) */ if (NetByteOrderReq(req)) ntoh_api_request(req, rc); /* Convert to host byte order */ /* Locate or create local sid for this request, given the * fd (Unix pipe) on which it arrived, the process from which * it came, and client's sid. */ sid = find_sid(fd, req->rq_pid, req->rq_a_sid); if (sid < 0 && req->rq_type == API2_REGISTER) { /* First message; look for available slot. */ sid = find_free_sid(fd, req->rq_pid, req->rq_a_sid); if (sid < 0) { /* XXX api error */ log(LOG_WARNING, 0, "API: too many sessions\n"); return (-1); } memset(&api_table[sid], 0, sizeof(api_rec)); api_table[sid].api_fd = fd; api_table[sid].api_pid = req->rq_pid; api_table[sid].api_a_sid = req->rq_a_sid; num_sessions++; } else if (sid < 0) { log(LOG_ERR, 0, "API: req %d from ?? pid= %dAsid= %d\n", req->rq_type, req->rq_pid, req->rq_a_sid); return(-1); } if (process_api_req(sid, req, rc) < 0) { /* Return code = -1 => release the session. */ api_table[sid].api_fd = 0; rsvp_api_close(sid); if (req->rq_type != API_CLOSE) { /* Even though only one session had error, * close the Unix socket to the process. */ (void) close(fd); FD_CLR(fd, &fds); return(-1); } } return(0); err_exit: /* No message from which to derive the SID, but we * have to close all sids that used that fd. */ for (sid = 0; sid < API_TABLE_SIZE; sid++) { if (api_table[sid].api_fd == fd) { api_table[sid].api_fd = 0; /* prevent upcall */ process_api_req(sid, NULL, 0); rsvp_api_close(sid); } } /* Now close the socket. */ FD_CLR(fd, &fds); (void) close(fd); return ret_val; } int sid_hash(int fd, int pid, int client_sid) { u_int32_t ufd = fd; u_int32_t upid = pid; u_int32_t u_client_sid = client_sid; return ((((ufd*65599 + upid)*65599) + u_client_sid)*65599) % API_TABLE_SIZE; } /* * find_sid(): Search API table to locate sid for this fd, pid, and client_sid * * Use open hash table. Return -1 if no match. */ int find_sid(int fd, int pid, int client_sid) { int first_sid = sid_hash(fd, pid, client_sid); int test_sid, i; api_rec * test_rec; for (i=0, test_sid=first_sid; i= API_TABLE_SIZE) test_sid -= API_TABLE_SIZE; test_rec = &api_table[test_sid]; if (test_rec->api_fd == fd && test_rec->api_pid == pid && test_rec->api_a_sid == client_sid) return test_sid; else if (test_rec->api_fd == 0) return(-1); } return -1; /* no match */ } /* find_free_sid(): Returns sid for a free slot in API table api_table[], * for this fd, pid and client_sid. Does not allocate. */ int find_free_sid(int fd, int pid, int client_sid) { int first_sid = sid_hash(fd, pid, client_sid); int test_sid, i; api_rec * test_rec; /* do not allow hash table to get more than half full */ if (num_sessions >= MAX_SESSIONS) return -1; for (i=0, test_sid=first_sid; i= API_TABLE_SIZE) test_sid -= API_TABLE_SIZE; test_rec = &api_table[test_sid]; if (test_rec->api_fd == 0) return test_sid; } return -1; /* no match */ } /* * Release API session */ void rsvp_api_close(int sid) { api_rec *recp = &api_table[sid]; del_from_timer((char *) (unsigned long)sid, TIMEV_API); api_free_packet(&recp->api_p_packet); api_free_packet(&recp->api_r_packet); num_sessions--; } /* * refresh_api(): Called from process_api_req when new request * arrives from API, and periodically from timer. It injects * stored API packets into the processing as if they had arrived * from the network (except API packets are in host byte order). * * Returns 0 normally to restart timer, -1 to kill timer. */ int refresh_api(int sid) { api_rec *recp = &api_table[sid]; int rc; /* * Make sure the process still exists before keeping the message * alive. */ if (kill(recp->api_pid, 0) == -1) if (errno == ESRCH) return(-1); /* Also make sure we still have record for it */ if (recp->api_p_packet.pkt_data == NULL && recp->api_r_packet.pkt_data == NULL) return(-1); if ((recp->api_p_packet.pkt_data)) { rc = rsvp_pkt_process(&recp->api_p_packet, NULL, api_num); if (rc < 0) { /* Internal error */ log(LOG_ERR, 0, "\nParse error %d in API pkt\n", rc); hexf(stdout, (char *)recp->api_p_packet.pkt_data, recp->api_p_packet.pkt_len); } } if ((recp->api_r_packet.pkt_data)) { rc = rsvp_pkt_process(&recp->api_r_packet, NULL, api_num); if (rc < 0) { /* Internal error */ log(LOG_ERR, 0, "\nParse error %d in API pkt\n", rc); hexf(stdout, (char *)recp->api_r_packet.pkt_data, recp->api_r_packet.pkt_len); } } return (0); } /* * reset_log() resets a log, if it is too long. It renames it to another name * and opens a new log. This way there is a limit on the size of the logs, * and at most 2 logs exists at any given time. * * The date and time are written in the header of each logfile. */ void reset_log(int init) { char file[256],prev[256]; char header[81]; time_t clock; if (!init) { fprintf(logfp, ".\n.\n= END OF LOG, ... start a new log =\n"); (void) fclose(logfp); } strcpy(file,RSVP_LOG); strcat(file,rsvp_log_suffix); strcpy(prev,RSVP_LOG_PREV); strcat(prev,rsvp_log_suffix); (void) unlink(prev); (void) rename(file, prev); if ((logfp = fopen(file, "w")) == NULL) { fprintf(stderr, "RSVP: can\'t open log file %s\n", file); sprintf(rsvp_log_suffix,".%d",(int) getpid()); strcat(file,rsvp_log_suffix); if ((logfp = fopen(file, "w")) == NULL) { fprintf(stderr, "RSVP: can\'t open log file %s\n",file); exit(1); } } setbuf(logfp, (char *) NULL); sprintf(header, "%s\n", vers_comp_date); log(LOG_ALWAYS, 0, header); clock = time((time_t *) NULL); log(LOG_ALWAYS, 0, "Log level:%d Debug mask:%d Start time: %s", l_debug, m_debug, ctime(&clock)); } /* * api_cmd() takes a file descriptor and a command message from the API * executes the command in the message. * * DON'T FORGET to set the response code, 'old' */ int api_cmd(int fd, rapi_cmd_t *cmd) { int old; if (cmd->rapi_cmd_type == RAPI_CMD_DEBUG_MASK) { old = m_debug; if (cmd->rapi_filler != (u_short)-1) { m_debug = cmd->rapi_filler; log(LOG_INFO, 0, "Debug Mask changed from %d to %d\n", old, m_debug); } else log(LOG_INFO, 0, "Debug Mask unchanged at %d\n", old); } else if (cmd->rapi_cmd_type == RAPI_CMD_DEBUG_LEVEL) { old = l_debug; if (cmd->rapi_filler != (u_short)-1) { l_debug = cmd->rapi_filler; log(LOG_INFO, 0, "Debug Level changed from %d to %d\n", old, l_debug); } else log(LOG_INFO, 0, "Debug Level unchanged at %d\n", old); } else if (cmd->rapi_cmd_type == RAPI_CMD_FILTER_ON) { old = debug_filter; if (cmd->rapi_filler != (u_short)-1) { debug_filter = 1; log(LOG_INFO, 0, "Debug filter now 1, was %d\n", old); } else log(LOG_INFO, 0, "Debug filter unchanged at %d\n", old); } else if (cmd->rapi_cmd_type == RAPI_CMD_FILTER_OFF) { old = debug_filter; if (cmd->rapi_filler != (u_short)-1) { debug_filter = 0; debug_filter_num = 0; log(LOG_INFO, 0, "Debug filter now 0, was %d\n", old); } else log(LOG_INFO, 0, "Debug filter unchanged at %d\n", old); } else if (cmd->rapi_cmd_type == RAPI_CMD_ADD_FILTER) { if (debug_filter != 1) { log(LOG_INFO, 0, "debug_filter should be 1\n"); old = -1; } else { int i; struct in_addr in; net_addr addr; in.s_addr = cmd->rapi_data[0]; NET_SET_ADDR_IPv4(&addr,in); for (i = 0; i < debug_filter_num; i++) if (net_addr_equal(&addr,&debug_filters[i])) break; if (i == debug_filter_num) { if (debug_filter_num >= MAX_DEBUG_FILTER) { log(LOG_ERR, 0, "Too many filters\n"); old = -1; } else { debug_filters[debug_filter_num++] = addr; old = debug_filter_num; } } } } else if (cmd->rapi_cmd_type == RAPI_CMD_DEL_FILTER) { if (debug_filter != 1) { log(LOG_INFO, 0, "debug_filter should be 1\n"); old = -1; } else { int i; struct in_addr in; net_addr addr; in.s_addr = cmd->rapi_data[0]; for (i = 0; i < debug_filter_num; i++) if (net_addr_equal(&addr,&debug_filters[i])) break; if (i == debug_filter_num) { log(LOG_ERR, 0, "No filter to delete\n"); old = -1; } else { old = --debug_filter_num; debug_filters[i] = debug_filters[old]; } } } /**** Don't tell... Ack the application with the old debug value if (write(fd, (char *) &old, sizeof(old)) == -1) log(LOG_ERR, errno, "write() error\n"); ****/ FD_CLR(fd, &fds); (void) close(fd); return (0); } /* * test_alive(): Check whether daemon is already running, return its pid; * else create file containing my pid and return 0. */ int test_alive() { int pid; FILE *fp; struct sockaddr_in addr; NET_SOCKADDR_UDP_IPv4(&addr,inaddr_any,RSVP_LOCK_PORT); lockfd = socket(AF_INET,SOCK_DGRAM,PF_UNSPEC); if (FAILED(lockfd)) return(-1); if (!FAILED(bind(lockfd,(struct sockaddr *) &addr,sizeof(addr)))) return(0); close(lockfd); if ((fp = fopen(RSVP_PID_FILE, "r")) == NULL) return(-1); if (fscanf(fp, "%u", &pid) != 1) return(-1); (void) fclose(fp); return (pid); } int save_pid() { FILE *fp; int pid = getpid(); if (unlink(RSVP_PID_FILE) == -1) { if (errno != ENOENT) perror("unlink"); } if ((fp = fopen(RSVP_PID_FILE, "w")) == NULL) { perror("fopen"); return (-1); } fprintf(fp, "%u\n", pid); (void) fclose(fp); return (0); } net_addr * get_if(int index) { net_addr *addr; net_if *inf; inf = &GET_IF(index); switch(NET_GET_TYPE(inf)) { case NET_IF_PHY: addr = &NET_GET_IF_PHY_ADDR(inf); break; case NET_IF_VIF: addr = &NET_GET_IF_VIF_ADDR(inf); break; default: return(NULL); } return(addr); } /* Map IP address into local interface number, or -1 */ int map_if_addr(net_addr *addr) { int i; net_addr *addr2; net_if *inf; addr2 = net_addr_ip(addr); for (i = 0; i < if_num; i++) { if (IsNumAPI(i)) continue; if (IF_DOWN(i)) continue; inf = &GET_IF(i); if (NET_GET_TYPE(inf) != NET_IF_PHY) continue; if (net_addr_equal(addr2,&NET_GET_IF_PHY_ADDR(inf))) return(i); } return(-1); } /* Map kernel if number into local interface number, or -1 */ int map_if_id(int id) { int i; net_if *inf; for (i = 0; i < if_num; i++) { if (IsNumAPI(i)) continue; if (IF_DOWN(i)) continue; inf = &GET_IF(i); if (NET_GET_TYPE(inf) != NET_IF_PHY) continue; if (id == NET_GET_IF_PHY_ID(inf)) return(i); } return(-1); } /* Map kernel vif number into local interface number, or -1 */ int map_if_vif(int vif) { int i; net_if *inf; for (i = 0; i < if_num; i++) { if (IsNumAPI(i)) continue; if (IF_DOWN(i)) continue; inf = &GET_IF(i); if (NET_GET_TYPE(inf) != NET_IF_VIF) continue; if (vif == NET_GET_IF_VIF_ID(inf)) return(i); } return(-1); } void print_ifs() { int i; char flgs[20]; log(LOG_ALWAYS, 0, "Physical, Virtual, and API interfaces are:\n"); for (i=0; i < if_num; i++) { flgs[0] = '\0'; if (IF_DOWN(i)) strcat(flgs, "DOWN "); else if (if_vec[i].if_up) strcat(flgs, "TCup "); else strcat(flgs, "NoIS "); if (if_vec[i].if_flags & IF_FLAG_UseUDP) strcat(flgs, "UDP "); if (if_vec[i].if_flags & IF_FLAG_Intgrty) strcat(flgs, "Intgry "); if (if_vec[i].if_flags & IF_FLAG_Police) strcat(flgs, "Police "); if (if_vec[i].if_flags & IF_FLAG_IFF_MC) strcat(flgs, "M"); if (if_vec[i].if_flags & IF_FLAG_Disable) strcpy(flgs, "RSVP Disabled!"); log(LOG_ALWAYS, 0, "%2d %-12.12s %38.38s/%-3d %s\n", i, if_vec[i].if_name, net_if_print(&GET_IF(i)), if_vec[i].prefix, flgs); } } /* Read and parse a configuration file. */ void read_config_file(char *fname) { FILE *fd; char cfig_line[80], word[64]; char *cp; fd = fopen(fname, "r"); if (!fd) { if (errno != ENOENT) log(LOG_ERR, 0, "Cannot open config file\n"); return; } while (fgets(cfig_line, sizeof(cfig_line), fd)) { cp = cfig_line; if (*cp == '#' || !next_word(&cp, word)) continue; cfig_do(&cp, word); /* Loop to parse keywords in line */ while (*cp != '\n' && *cp != '#'&& *cp != '\0') { next_word(&cp, word); cfig_do(&cp, word); } if (*cp == '\0') { log(LOG_ERR, 0, "Config file line 2 long: %s\n",cfig_line); exit(1); } } log(LOG_INFO, 0, "Used configuration file \n", fname); } /* This does not do as much syntax-checking as one might like... */ void cfig_do(char **cpp, char *word) { Cfig_element *cep, *tcep; char parm1[64], parm2[64]; for (cep = Cfig_Table; cep->cfig_keywd; cep++) if (!pfxcmp(word, cep->cfig_keywd)) break; if (!cep->cfig_keywd) { log(LOG_ERR, 0, "Unknown config verb: %s\n", word); exit(1); } for (tcep = cep+1; tcep->cfig_keywd; tcep++) if (!pfxcmp(word, tcep->cfig_keywd)) { log(LOG_ERR, 0, "Ambiguous keyword: %s\n", word); exit(1); } if (cep->cfig_flags & (CFF_HASPARM|CFF_HASPARM2)) { if (!next_word(cpp, parm1)) { log(LOG_ERR, 0, "Missing parm for %s\n", word); exit(1); } } if (cep->cfig_flags & CFF_HASPARM2) { if (!next_word(cpp, parm2)) { log(LOG_ERR, 0, "Missing parm for %s\n", word); exit(1); } } cfig_action(cep->cfig_actno, parm1, parm2); } void cfig_action(int act, char *parm1, char *parm2) { int i; KEY_ASSOC *kap; switch (act) { case CfAct_iface: for (i = 0; i < if_num; i++) { if (!strcmp(parm1, if_vec[i].if_name)) break; } if (i >= if_num) { log(LOG_ERR, 0, "Unknown interface %s\n", parm1); exit(1); } Cfig_if = i; break; case CfAct_police: if_vec[Cfig_if].if_flags |= IF_FLAG_Police; break; case CfAct_udp: if_vec[Cfig_if].if_flags |= IF_FLAG_UseUDP; break; case CfAct_udpttl: if_vec[Cfig_if].if_flags |= IF_FLAG_UseUDP; if_vec[Cfig_if].if_udpttl = atoi(parm1); break; case CfAct_intgr: /* integrity -- require INTEGRITY object in message * received through this interface */ if_vec[Cfig_if].if_flags |= IF_FLAG_Intgrty; break; case CfAct_disable: if_vec[Cfig_if].if_flags |= IF_FLAG_Disable; log(LOG_WARNING, 0, "disable config parm not yet supported\n"); break; case CfAct_sndttl: /* Set default TTL for Resv messages, etc. */ log(LOG_WARNING, 0, "sndttl config parm not yet supported\n"); break; case CfAct_sendkey: /* sendkey -- * keyid and key for sending message to this * interface. */ kap = load_key(parm1, parm2); if (!kap) exit(1); kap->kas_if = Cfig_if; break; case CfAct_recvkey: /* recvkey -- * keyid and key for receiving message */ kap = load_key(parm1, parm2); if (!kap) exit(1); kap->kas_if = -1; break; case CfAct_refresh: log(LOG_WARNING, 0, "refresh config parm not yet supported\n"); if_vec[Cfig_if].if_Rdefault = atoi(parm1); break; default: assert(0); } } #ifndef RTAP /* * Skip leading blanks, then copy next word (delimited by blank or zero, but * no longer than 63 bytes) into buffer b, set scan pointer to following * non-blank (or end of string), and return 1. If there is no non-blank text, * set scan ptr to point to 0 byte and return 0. */ int next_word(char **cpp, char *b) { char *tp; int L; *cpp += strspn(*cpp, " \t"); if (**cpp == '\0' || **cpp == '\n' || **cpp == '#') return(0); tp = strpbrk(*cpp, " \t\n#"); L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63); strncpy(b, *cpp, L); *(b + L) = '\0'; *cpp += L; *cpp += strspn(*cpp, " \t"); return (1); } /* * Prefix string comparison: Return 0 if s1 string is prefix of s2 string, 1 * otherwise. */ int pfxcmp(s1, s2) register char *s1, *s2; { while (*s1) if (*s1++ != *s2++) return (1); return (0); } #endif #ifndef SECURITY KEY_ASSOC * load_key(char *keyidstr, char *keystr) { log(LOG_ERR, 0, "Keys unsupported\n"); return(NULL); } #endif void ntoh_rapi_cmd(rapi_cmd_t *cmd) { }