/* get-next-event loop * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998, 1999 D. Hugh Redelmeier. * * This program 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. See . * * This program 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. See the GNU General Public License * for more details. * * RCSID $Id: server.c,v 1.46 2000/06/21 18:24:35 dhr Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #ifdef SOLARIS # include /* for Solaris 2.6: defines SIOCGIFCONF */ #endif #include #include #include #include #include #include #include #include #include "constants.h" #include "defs.h" #include "state.h" #include "id.h" #include "connections.h" /* needs id.h */ #include "kernel.h" /* for no_klips */ #include "log.h" #include "server.h" #include "timer.h" #include "packet.h" #include "demux.h" /* needs packet.h */ #include "kernel_comm.h" #include "whack.h" /* for RC_LOG_SERIOUS */ #include #include #include /* * Server main loop and socket initialization routines. */ /* address of control (whack) socket */ struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX }; void delete_ctl_socket(void) { unlink(ctl_addr.sun_path); /* is noting failure useful? */ } bool listening = FALSE; /* should we pay attention to IKE messages? */ struct iface *interfaces = NULL; /* public interfaces */ static const int on = TRUE; /* by-reference parameter */ int pfkeyfd = NULL_FD; static int init_pfkeyfd(void) { int s = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); if (s == -1) exit_log_errno((e, "socket() in init_pfkeyfd()")); DBG(DBG_KLIPS, DBG_log("listening for PFKEY_V2 on file descriptor %d\n",s)); return s; } /* Initialize the Whack socket. * Note: although it appears that Whack and IKE use the same port, * IKE's is UDP and Whack's is TCP. */ static int init_whackfd(void) { int s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) exit_log_errno((e, "socket() in init_whackfd()")); if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on)) < 0) exit_log_errno((e, "setsockopt() in init_whackfd()")); DBG(DBG_KLIPS, DBG_log("listening for Whack on %s, file descriptor %d" , ctl_addr.sun_path, s)); /* to keep control socket secure, use umask */ { mode_t ou = umask(~S_IRWXU); if (bind(s, (struct sockaddr *)&ctl_addr , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0) exit_log_errno((e, "bind() in init_whackfd()")); umask(ou); } /* 5 is a haphazardly chosen limit for the backlog. * Rumour has it that this is the max on BSD systems. */ if (listen(s, 5) < 0) exit_log_errno((e, "listen() in init_whackfd()")); return s; } /* Initialize the interface sockets. */ #ifndef IPSECDEVPREFIX # define IPSECDEVPREFIX "ipsec" #endif void find_ifaces(void) { int j; /* index into buf */ struct ifconf ifconf; struct ifreq buf[100]; /* for list of interfaces */ struct iface *new = NULL; /* get list of interfaces from system */ { /* Get a UDP socket */ int master_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); struct sockaddr_in sin; if (master_sock == -1) exit_log_errno((e, "socket() failed in find_ifaces()")); if (setsockopt(master_sock, SOL_SOCKET, SO_REUSEADDR , (const void *)&on, sizeof(on)) < 0) exit_log_errno((e, "setsockopt() in find_ifaces()")); /* bind the socket */ mksin(sin, htonl(INADDR_ANY), pluto_port); if (bind(master_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) exit_log_errno((e, "bind() failed in find_ifaces()")); /* Get local interfaces */ ifconf.ifc_len = sizeof(buf); ifconf.ifc_buf = (void *) buf; memset(buf, '\0', sizeof(buf)); if (ioctl(master_sock, SIOCGIFCONF, &ifconf) == -1) exit_log_errno((e, "ioctl(SIOCGIFCONF) in find_ifaces()")); close(master_sock); } /* find all virtual/real interface pairs. */ for (j = 0; (j+1) * sizeof(*buf) <= (size_t)ifconf.ifc_len; j++) { const struct sockaddr_in *rs = (struct sockaddr_in *) &buf[j].ifr_addr; int k; char rname[IFNAMSIZ + 1]; char vname[IFNAMSIZ + 1]; /* build a NUL-terminated copy of the rname field */ memcpy(rname, buf[j].ifr_name, IFNAMSIZ); rname[IFNAMSIZ] = '\0'; /* ignore all but AF_NET interfaces */ if (rs->sin_family != AF_INET) continue; /* not interesting */ /* ignore if virtual (ipsec*) interface */ if (strncmp(rname, IPSECDEVPREFIX, sizeof(IPSECDEVPREFIX)-1) == 0) continue; /* ignore unconfigured interfaces */ if (rs->sin_addr.s_addr == 0) { log("IP interface %s has address 0.0.0.0 -- ignored", rname); continue; } /* look for a corresponding virtual (ipsec?) interface */ vname[0] = '\0'; /* mark as empty */ for (k = 0; (k+1) * sizeof(*buf) <= (size_t)ifconf.ifc_len; k++) { const struct sockaddr_in *vs = (struct sockaddr_in *) &buf[k].ifr_addr; if (k != j && vs->sin_family == rs->sin_family && vs->sin_addr.s_addr == rs->sin_addr.s_addr) { if (strncmp(buf[k].ifr_name, IPSECDEVPREFIX, sizeof(IPSECDEVPREFIX)-1) == 0) { if (vname[0] != '\0') { loglog(RC_LOG_SERIOUS, "ipsec interfaces %s and %.*s share same address %s" , vname, IFNAMSIZ, buf[k].ifr_name, inet_ntoa(vs->sin_addr)); } else { /* build a NUL-terminated copy of the vname field */ memcpy(vname, buf[k].ifr_name, IFNAMSIZ); vname[IFNAMSIZ] = '\0'; } } else { loglog(RC_LOG_SERIOUS, "IP interfaces %s and %.*s share address %s!" , rname, IFNAMSIZ, buf[k].ifr_name, inet_ntoa(vs->sin_addr)); } } } /* did we find a virtual interface? */ if (vname[0] == '\0') { if (no_klips) { /* kludge: invent a virtual device */ snprintf(vname, sizeof(vname), "virtual%s" , inet_ntoa(rs->sin_addr)); } else { DBG(DBG_CONTROL , DBG_log("IP interface %s %s has no matching ipsec* interface -- ignored" , rname, inet_ntoa(rs->sin_addr))); continue; } } /* we've got all we need; see if this is a new thing */ { struct iface **p = &interfaces; for (;;) { struct iface *q = *p; if (q == NULL) { /* matches nothing -- create a new entry */ struct sockaddr_in sin; int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { log_errno((e, "socket() in find_ifaces()")); break; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR , (const void *)&on, sizeof(on)) < 0) { log_errno((e, "setsockopt() in find_ifaces()")); break; } mksin(sin, rs->sin_addr.s_addr, pluto_port); if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { log_errno((e, "bind() for %s in find_ifaces()" , show_sa_in(&sin))); break; } q = alloc_thing(struct iface, "struct iface"); q->vname = clone_str(vname, "virtual device name"); q->rname = clone_str(rname, "real device name"); q->addr = rs->sin_addr; q->fd = fd; q->next = new; new = q; log("adding interface %s/%s %s" , q->vname, q->rname, inet_ntoa(q->addr)); break; } if (strcmp(q->rname, rname) == 0 && strcmp(q->vname, vname) == 0 && q->addr.s_addr == rs->sin_addr.s_addr) { /* matches -- steal old entry */ *p = q->next; q->next = new; new = q; break; } p = &q->next; } } } free_ifaces(); /* ditch remaining old entries */ interfaces = new; if (interfaces == NULL) loglog(RC_LOG_SERIOUS, "no public interfaces found"); } void free_ifaces(void) { struct iface *p = interfaces; while (p != NULL) { struct iface *q = p->next; log("shutting down interface %s/%s %s" , p->vname, p->rname, inet_ntoa(p->addr)); release_interface(p); pfree(p->vname); pfree(p->rname); close(p->fd); pfree(p); p = q; } } static volatile sig_atomic_t hupflag = FALSE; static void huphandler(int sig UNUSED) { hupflag = TRUE; (void)signal(SIGHUP, &huphandler); } /* call_server listens for incoming ISAKMP packets and Whack messages, * and handles timer events. */ void call_server(void) { int whackfd = init_whackfd(); struct iface *ifp; if (!no_klips) pfkeyfd = init_pfkeyfd(); /* dumb folks think poking any daemon with SIGHUP is polite */ (void)signal(SIGHUP, &huphandler); for (;;) { fd_set readfds; int ndes; /* wait for next interesting thing */ for (;;) { long next_time = next_event(); /* time to any pending timer event */ int maxfd = whackfd; if (hupflag) { hupflag = FALSE; log("I ignore SIGHUP -- perhaps you want \"whack --listen\""); } FD_ZERO(&readfds); FD_SET(whackfd, &readfds); if (!no_klips) { if (maxfd < pfkeyfd) maxfd = pfkeyfd; FD_SET(pfkeyfd, &readfds); } if (listening) { for (ifp = interfaces; ifp != NULL; ifp = ifp->next) { if (maxfd < ifp->fd) maxfd = ifp->fd; FD_SET(ifp->fd, &readfds); } } if (next_time == -1) { /* select without timer */ ndes = select(maxfd + 1, &readfds, NULL, NULL, NULL); } else if (next_time == 0) { /* timer without select: there is a timer event pending, * and it should fire now so don't bother to do the select. */ ndes = 0; /* signify timer expiration */ } else { /* select with timer */ struct timeval tm; tm.tv_sec = next_time; tm.tv_usec = 0; ndes = select(maxfd + 1, &readfds, NULL, NULL, &tm); } if (ndes != -1) break; /* success */ if (errno != EINTR) exit_log_errno((e, "select() failed in call_server()")); /* retry if terminated by signal */ } /* figure out what is interesting */ if (ndes == 0) { /* timer event */ DBG(DBG_CONTROL, DBG_log(BLANK_FORMAT); DBG_log("*time to handle event")); event_handle(); passert(GLOBALS_ARE_RESET()); } else { /* at least one file descriptor is ready */ for (ifp = interfaces; ifp != NULL; ifp = ifp->next) { if (FD_ISSET(ifp->fd, &readfds)) { /* comm_handle will print DBG_CONTROL intro, * with more info than we have here. */ comm_handle(ifp); passert(GLOBALS_ARE_RESET()); } } if (FD_ISSET(whackfd, &readfds)) { DBG(DBG_CONTROL, DBG_log(BLANK_FORMAT); DBG_log("*received whack message")); whack_handle(whackfd); passert(GLOBALS_ARE_RESET()); } if (!no_klips && FD_ISSET(pfkeyfd, &readfds)) { DBG(DBG_CONTROL, DBG_log(BLANK_FORMAT); DBG_log("*received pfkey message")); pfkey_handle(); passert(GLOBALS_ARE_RESET()); } } } }