/* * babd.c - Babylon Config Daemon source * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: babd.cc,v 1.15 2004/10/20 05:09:54 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "babd.h" #include "d_aps_if.h" #include "aps_if.h" #include "link.h" #include "iface.h" #include "ctrlfd.h" #include "radius.h" #include "selectops.h" SelectEventHandler *selectObjs[2][BAB_OPEN_MAX]; extern void radius_GetConfig(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options); extern int addchan(char *, char *, char *, int); extern void write_link_state(int, CLink *); extern void write_link_options(int, CLink *); extern "C" { #include "md5.h" }; #define MAX_IFS 5000 #define FLAGS_GOOD (IFF_UP | IFF_BROADCAST) #define FLAGS_MASK (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP) #define RANGE_TAG "range" #define INCLUDE_TAG "include" #define EXCLUDE_TAG "exclude" #define MAX_NAME_LENGTH 16 #define CONTROL_PATH "/tmp/.bab_control" /* FIXME: relocate in /dev/? */ #define BPPP_PREFIX "/dev/bppp/" typedef struct ip_address{ unsigned int address; void *ident; LIST_ENTRY(ip_address) ptr; } ip_address_t; typedef LIST_HEAD(ip_pool_head, ip_address) ip_pool_head_t; typedef struct dynamic_pool{ char name[MAX_NAME_LENGTH]; ip_pool_head_t ip_list; ip_pool_head_t in_use; LIST_ENTRY(dynamic_pool) ptr; } dynamic_pool_t; typedef LIST_HEAD(dynamic_pool_head, dynamic_pool) dynamic_pool_head_t; /* IP pool pointer */ dynamic_pool_head_t ip_pool; char *sysname = ""; static int finished = 0; extern int disable_check_ifaces; int b_control_fd, b_sock_fd; int am_radius_client = 0; int am_radius_acct_client = 0; void get_config(void); void do_chap_auth(CfgMessage_t *active, CfgMessage_t *auth_buf); int parse_dynamic(); ip_pool_head_t *shuffle_ip(ip_pool_head_t *, ip_pool_head_t *); unsigned int request_ip_address(char *, void *); void release_ip_address(void *); void print_usage(); void print_debuglevels(); int broken_acfc=0; /* Don't turn off ACFC after LCP if we were told to if 1 */ u32 *dyn_ips = NULL; int dyn_ip_enabled = -1; /* set if we've enabled dynamic ips; * -1=default -- don't warn if config file * doesn't exist. */ /* Options we support (see the man page) */ static char shrt_options[] = "a:kfd:rpinhsu:c:y:" #ifdef USE_L2TP "2:" #endif ; static struct option long_options[] = { { "radius-acct", required_argument, NULL, 'a' }, { "no-fork", no_argument, NULL, 'f' }, { "debug", required_argument, NULL, 'd' }, { "enable-routing", no_argument, NULL, 'R' }, { "enable-proxyarp", no_argument, NULL, 'P' }, { "disable-routing", no_argument, NULL, 'r' }, { "disable-proxyarp", no_argument, NULL, 'p' }, { "disable-down-check", no_argument, NULL, 'k' }, { "enable-dynamicip", no_argument, NULL, 'i' }, { "disable-dynamicip", no_argument, NULL, 'I' }, { "scan-chan", no_argument, NULL, 's' }, { "broken-acfc", no_argument, NULL, 'B' }, #ifdef USE_L2TP { "enable-l2tp", required_argument, NULL, '2' }, #endif { "system-name", required_argument, NULL, 'n' }, { "help", no_argument, NULL, 'h' }, { "help-debug", no_argument, NULL, 'D' }, { "config-file", required_argument, NULL, 'c' }, { "dynamic-file", required_argument, NULL, 'y' }, { "radius", required_argument, NULL, 'u' }, }; static char *cmdname; const char DEFAULT_CFG_FILE[]= "/etc/babylon/babylon.conf"; const char DEFAULT_DYN_FILE[]= "/etc/babylon/dynamic_ip.conf"; static char configFile[MAXPATHLEN]; static char dynamicFile[MAXPATHLEN]; void print_pool() { dynamic_pool_t *temp1 = ip_pool.lh_first; ip_address_t *temp2; while(temp1 != NULL) { syslog(LOG_DEBUG,"Pool: %s:", temp1->name); temp2 = temp1->ip_list.lh_first; while(temp2 != NULL) { syslog(LOG_DEBUG,"%s", iptostr(temp2->address)); temp2 = temp2->ptr.le_next; } temp1 = temp1->ptr.le_next; } } int do_bdial(int fd, ctrlfd_t *cfd, const char *site, const char *phone, Call *call) { OptionSet_t *options = new OptionSet_t; call->ident = ++callIdent; call->cfd = cfd; if (cfd) { if (cfd->c_call) { cfd->c_call = NULL; } cfd->c_call = call; } Log(LF_DEBUG|LF_CALL, "do_bdial: call=%p #%d site: %s num: %s...", call, call->ident, site, phone); strncpy(options->site, site, SITE_NAME_LEN-1); strncpy(options->phone, phone, PHONE_LEN-1); options->call = call; babd_GetConfig(DialGotConfig, NULL, options); delete options; return 0; } static char *getword(char **p) { char *s = *p, *word; for (; *s && isspace(*s); s++) ; if (!*s) return NULL; word = s; for (; *s && !isspace(*s); s++) ; if (*s) *s++ = 0; *p = s; return word; } static int load_channels(int quiet) { FILE *ifp = fopen("/proc/bab_chan", "r"); char buf[256], *s; int i = 0; fgets(buf, 256, ifp); while ((s=fgets(buf, 256, ifp))) { char devname[256]; char *devidx = getword(&s); char *babname = getword(&s); char *devclass = getword(&s); strcpy(devname, "/dev/bab/"); strcat(devname, devidx); /* Add all channels that are not isdn_d class */ /* This should pick up any new classes as added */ if (strcmp("isdn_d",devclass)!=0) { addchan(devname, babname, devclass, 0); i++; if (!quiet) { Log(LF_ERROR|LF_CALL, "addchan(%s, %s) failed", devname, babname); } } } fclose(ifp); return i; } void do_bchan(int fd, ctrlfd_t *cfd, char *arg) { extern void delchan(channel_t *); extern channel_t *findchan(char *); char tmp[256]; char *str = getword(&arg); int add = 0, hangup = 0, failed = 0, i = 0; int no_auth = 0; if (!str) goto help; if (!strcmp(str, "add")) add = 1; else if (!strcmp(str, "hangup")) hangup = 1; else if (strcmp(str, "del")) { help: strcpy(tmp, "[-1] -1: Usage: bchan [add|del|hangup] \n"); write(fd, tmp, strlen(tmp)); delete(cfd); return; } while ((str=getword(&arg))) { if (!strcmp("--pre-auth", str)) { no_auth = 1; } else if (add) { if (addchan(str, str, "isdn_b", no_auth) || !++i) { snprintf(tmp, sizeof(tmp), "[-1] 0: %s: channel already registered\n", str); write(fd, tmp, strlen(tmp)); failed = 1; } } else { channel_t *ch = findchan(str); if (!ch) { snprintf(tmp, sizeof(tmp), "[-1] 0: %s: channel not found\n", str); write(fd, tmp, strlen(tmp)); failed = 1; continue; } if (hangup) ch->link->Hangup(); else delchan(ch); i++; } } sprintf(tmp, "[-1] %d: %d channel%s %sed\n", failed ? -2 : -1, i, (1 == i) ? "" : "s", add ? "add" : hangup ? "disconnect" : "delet" ); write(fd, tmp, strlen(tmp)); delete(cfd); } static void link_user_options(int fd, char *name) { CLink *link; for (link = linkListHead ; link != NULL ; link = link->m_next) { if (name && (!(link->m_wereAcked || link->m_peerAcked) || strcmp(link->ifOptions.user, name))) continue; write_link_options(fd, link); } } void link_dev_options(int fd, char *name) { CInterface *i; CLink *link; for (i = ifListHead; i != NULL; i = i->next) if (!strcmp(name, i->m_name)) break; for (link=(i ? i->links : NULL); link; link=link->m_nextBundled) write_link_options(fd, link); } static void link_query_user(int fd, char *name) { CLink *link; for (link = linkListHead ; link != NULL ; link = link->m_next) { if (name && (!(link->m_wereAcked || link->m_peerAcked) || strcmp(link->ifOptions.user, name))) continue; write_link_state(fd, link); } } void link_query_dev(int fd, char *name) { CInterface *i; CLink *link; for (i = ifListHead; i != NULL; i = i->next) if (!strcmp(name, i->m_name)) break; for (link=(i ? i->links : NULL); link; link=link->m_nextBundled) write_link_state(fd, link); } static void do_bquery(int fd, ctrlfd_t *cfd, char *arg) { char *str, tmp[1024]; char opt[MAX_USER_LEN]; int got_user = 0, help = 0, got_opt=0, got_dev=0; while (NULL != (str=getword(&arg))) { if (!strncmp(str, "--user=", 7)) { strncpy(opt, str+7, MAX_USER_LEN); got_user=1; } else if (!strncmp(str, "--dev=", 6)) { strncpy(opt, str+6, MAX_USER_LEN); got_dev=1; } else if (!strncmp(str, "--options", 9)) { got_opt=1; } else help = 1; } if (help) { strcpy(tmp, "[-1] -1: Usage: bquery [--user= | --dev=] [--options]\n"); write(fd, tmp, strlen(tmp)); delete(cfd); return; } if (!got_opt) { int l = sprintf(tmp, "[-1] 0: %-10s %-15s %-15s %-13s %-7s %-6s %-8s %-7s\n", "user", "local_ip", "remote_ip", "state", "class", "dev", "site", "name" ); write(fd, tmp, l); } else { int l = sprintf(tmp, "[-1] 0: %-10s %-4s %-10s %-6s %-6s %-6s %-6s %-6s %-6s %-6s\n", "channel", " ", "magic", "mrru", "pfc", "acfc", "auth", "answer", "ssn", "echo req" ); write(fd, tmp, l); } if (got_opt) { if (got_user) link_user_options(fd, opt); else if (got_dev) link_dev_options(fd, opt); else link_user_options(fd, NULL); } else if (got_dev) { link_query_dev(fd, opt); } else if (got_user) { link_query_user(fd, opt); } else { sprintf(tmp, "[-1] -1:\n"); if (!got_user) link_query_user(fd, NULL); } sprintf(tmp, "[-1] -1:\n"); write(fd, tmp, strlen(tmp)); delete(cfd); } //void b_ctrlfd_read(int fd, void *data) void ctrlfd_t::SelectEvent(int fd, SelectEventType event) { if (event & SEL_READ) { char buf[1024]; int len, i; if (c_fd != fd) Log(LF_ALERT|LF_IPC, "cfd->fd (%d) != fd (%d)", c_fd, fd); len = read(c_fd, buf, sizeof(buf)); Log(LF_DEBUG|LF_IPC, "b_ctrlfd_read: %d got %d", c_fd, len); if (len <= 0) { Log(LF_DEBUG|LF_IPC, "b_ctrlfd_read: closing %d %p", c_fd, this); delete(this); return; } /* FIXME: this won't always work. */ for (i=0; i= 7 && !strncmp(c_buf, "bhangup", 7)) do_bchan(c_fd, this, c_buf+1); else if (pos >= 5 && !strncmp(c_buf, "bchan", 5)) do_bchan(c_fd, this, c_buf+6); else if (pos >= 6 && !strncmp(c_buf, "bquery", 6)) do_bquery(c_fd, this, c_buf+7); else if (pos > 6 && !strncmp(c_buf, "bdial ", 6)) { char *site, *num, *p; for (p=c_buf+6; *p && isspace(*p); p++) ; site = p; for (; *p && !isspace(*p); p++) ; if (*p) *p++ = 0; for (; *p && isspace(*p); p++) ; num = p; for (; *p && !isspace(*p); p++) ; *p = 0; do_bdial(c_fd, this, site, num, new Call); #ifdef USE_L2TP } else if (pos >= 7 && !strncmp(c_buf, "l2tpctl", 7)) { do_l2tpctl(this, c_buf+7); #endif } else { char tmp[256]; snprintf(tmp, 256, "[-1] -1: unknown command %s\n", c_buf); write(c_fd, tmp, strlen(tmp)); } } else if (c_pos+1 < sizeof(c_buf)) c_buf[c_pos++] = buf[i]; else c_err = 1; } } } //ctrlfd_listener *cfd_listen = new ctrlfd_listener(b_control_fd); class ctrlfd_listener : public SelectEventHandler { public: ctrlfd_listener(int fd) { SelectSetEvents(fd, SEL_READ); } void SelectEvent(int fd, SelectEventType event); }; void ctrlfd_listener::SelectEvent(int fd, SelectEventType event) { ctrlfd_t *cfd; int newfd; newfd = accept(fd, NULL, 0); if (newfd < 0) { syslog(LOG_ERR, "b_control_wakeup: error accepting connection: %s\n", strerror(errno)); return; } cfd = new ctrlfd_t(newfd); //setup_select(newfd, cfd, b_ctrlfd_read, NULL); cfd->SelectSetEvents(newfd, SEL_READ); Log(LF_DEBUG|LF_IPC, "accepted connection on control socket(%d): %d %p", fd, newfd, cfd); } static void sigchld_handler(int sig) { int status; while (waitpid(-1, &status, WNOHANG) > 0) ; signal(SIGCHLD, sigchld_handler); } static void do_cmd(char *cmd, char *dev, char *port, char *user) { struct stat s; pid_t pid; if (0 != stat(cmd, &s)) return; if (!(S_IXUSR & s.st_mode)) return; pid = fork(); if (0 == pid) { execl(cmd, cmd, dev, port, user, NULL); syslog(LOG_ERR, "execl(%s):%m", cmd); } else if (-1 == pid) syslog(LOG_ERR, "fork():%m"); } void exit_handler(int foo) { if (foo); unlink(CONTROL_PATH); exit(0); } int main(int argc, char **argv) { struct sockaddr_un addr; int do_fork = 1; /* Daemon forks to background */ int do_route = 1; /* Daemon adds routes */ int do_proxy = 1; /* Daemon adds proxy */ int do_load_chans = 0; char *l2tp_file = "/etc/babylon/l2tp.conf", *l2tp_default_file = l2tp_file; int i; pid_t pid; int dbg_flags = LOG_INFO; int opt; int opt_indx; /* Set the command name for the print_usage routine */ cmdname = rindex(argv[0], '/'); if (cmdname) { cmdname++; } else { cmdname = argv[0]; } debug=0; /* Set the default filenames */ strcpy(configFile,DEFAULT_CFG_FILE); strcpy(dynamicFile,DEFAULT_DYN_FILE); while((opt = getopt_long(argc, argv, shrt_options, long_options, &opt_indx)) != EOF) { switch(opt) { #ifdef USE_L2TP case '2': /* turn on l2tp */ if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; l2tp_file = optarg; break; #endif case 'k': disable_check_ifaces = 1; break; case 'f': /* Don't fork */ do_fork = 0; break; case 'r': /* Don't do routing */ do_route = 0; break; case 'p': /* Don't do proxy arp */ do_proxy = 0; break; case 'R': /* Do do routing */ do_route = 1; break; case 'P': /* Do do proxy arp */ do_proxy = 1; break; case 'a': case 'u': if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; setup_radius(optarg, (opt == 'a')); break; case 'd': /* Enable debugging messages to stderr */ { if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; debug = strtol(optarg,NULL,0); if ((debug == LONG_MAX || debug == LONG_MIN) && (errno == ERANGE)) { fprintf(stderr, "Invalid debug parameter '%s' -- must be a number.\n", optarg); print_usage(); } break; } case 'D': print_debuglevels(); /* Should never get here */ break; case 'i': /* Do dynamic IP allocation for incoming calls */ dyn_ip_enabled = 1; break; case 'I': /* Don't do dynamic IP allocation for incoming calls */ dyn_ip_enabled = 0; break; case 'n': /* Specify system name */ if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; if (optarg) { sysname = optarg; } else { fprintf(stderr, "Missing system name for -n parameter.\n"); print_usage(); } break; case 's': /* Do channel scan on load */ do_load_chans = 1; break; case 'B': /* Enable broken acfc support */ broken_acfc=1; break; case 'c': if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; if (optarg) { strncpy(configFile,optarg,MAXPATHLEN); } else { fprintf(stderr,"Missing config file name.\n"); print_usage(); } /* Make sure the file does exist */ if (access(configFile,R_OK)!=0) { fprintf(stderr,"Cannot find/read config file: %s.\n",configFile); print_usage(); } break; case 'y': if (!optarg && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; if (optarg) { strncpy(dynamicFile,optarg,MAXPATHLEN); } else { fprintf(stderr,"Missing dynamic IP file name.\n"); print_usage(); } /* Make sure the file does exist */ if (access(dynamicFile,R_OK)!=0) { fprintf(stderr,"Cannot find/read dynamic IP file: %s.\n",dynamicFile); print_usage(); } break; default: print_usage(); return -1; } } if (getuid()!=0) { fprintf(stderr,"\n%s much be run as root.\n\n",cmdname); exit(1); } openlog("babylond", LOG_NDELAY | LOG_PID , LOG_AUTHPRIV); setlogmask(LOG_UPTO(dbg_flags)); /* Set up the dynamic ip pool -- if there is one */ if (dyn_ip_enabled && 0 != parse_dynamic()) { syslog(LOG_ERR, " Error parsing file: /etc/babylon/dynamic_ip.conf"); return -1; } i = open(BPPP_PREFIX "0", O_RDWR | O_NONBLOCK); if (-1 == i) { int err = errno; syslog(LOG_ERR, "open(" BPPP_PREFIX "0): %s\n", strerror(err)); fprintf(stderr, "Babylon kernel module not found: open(" BPPP_PREFIX "0): %s\n", strerror(err)); exit(1); } close(i); b_sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == b_sock_fd) { syslog(LOG_ERR, "socket(AF_INET, SOCK_STREAM, 0): %s\n", strerror(errno)); exit(1); } if ((b_control_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "error creating control socket: %s\n", strerror(errno)); return 1; } addr.sun_family = AF_UNIX; strcpy(addr.sun_path, CONTROL_PATH); unlink(CONTROL_PATH); umask(0077); if (bind(b_control_fd, (struct sockaddr *)&addr, sizeof(addr.sun_family)+strlen(addr.sun_path)) < 0) { syslog(LOG_ERR, "error binding control socket to %s: %s\n", CONTROL_PATH, strerror(errno)); return 1; } if (listen(b_control_fd, 64) < 0) { syslog(LOG_ERR, "listen(control socket): %s\n", strerror(errno)); return 1; } //setup_select(b_control_fd, NULL, b_control_accept, NULL); ctrlfd_listener *cfd_listen = new ctrlfd_listener(b_control_fd); aps_init(); if (do_load_chans) load_channels(0); #ifdef USE_L2TP FILE *l2tp_cfg = fopen(l2tp_file, "r"); if (l2tp_cfg) { int ret = l2tp_setup(l2tp_cfg); fclose(l2tp_cfg); if (ret) return ret; } else if (l2tp_file != l2tp_default_file) { const char *err = strerror(errno); fprintf(stderr, "babylon: unable to open '%s' : %s\n", l2tp_file, err); return 1; } #endif /* * Fork and run in the background */ if (do_fork) { pid = fork(); if (pid > 0) return 0; /* Parent exits */ if (pid < 0) { syslog(LOG_ERR, "Error switching to background: fork: %s\n", strerror(errno)); return -1; } #if 0 close(0); close(1); close(2); open("/dev/null", O_RDWR); dup(0); dup(0); #endif } /* last thing before the main loop... */ signal(SIGCHLD, sigchld_handler); signal(SIGPIPE, SIG_IGN); signal(SIGINT, exit_handler); signal(SIGQUIT, exit_handler); signal(SIGTERM, exit_handler); while (!finished) { bab_poll(); } delete cfd_listen; aps_cleanup(); unlink(CONTROL_PATH); return 0; } void SendAccounting(AcctMessage_t *acct_buf) { if (acct_buf->type == ACCT_START) { syslog(LOG_INFO, "Service started for %s on port %s (%s)", acct_buf->user, acct_buf->port, acct_buf->dev_class); do_cmd("/etc/babylon/bab-up", acct_buf->ifname, acct_buf->port, acct_buf->user); } else { syslog(LOG_INFO, "Service ended for %s on port %s (%s)", acct_buf->user, acct_buf->port, acct_buf->dev_class); syslog(LOG_INFO, "Termination cause: %s", acct_buf->reason); release_ip_address(acct_buf->call); syslog(LOG_INFO, "Rx Stats: Packets: %d, Octets: %Lu", acct_buf->in_packets, (unsigned long long)acct_buf->in_octets); syslog(LOG_INFO, "Tx Stats: Packets: %d, Octets: %Lu", acct_buf->out_packets, (unsigned long long)acct_buf->out_octets); do_cmd("/etc/babylon/bab-down", acct_buf->ifname, acct_buf->port, acct_buf->user); } if (am_radius_acct_client) radius_acct(acct_buf); delete(acct_buf); } /* * Get the hardware address of a device on the same subnet * as remote ip. */ static int get_ether_addr(u32 rem_ip, struct sockaddr *hwaddr, char *name) { int sockfd = -1; struct ifreq *ifr = NULL, *ifend = NULL; u32 ina = 0, mask = 0; struct ifreq ifreq; struct ifconf ifc; struct ifreq ifs[MAX_IFS]; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { syslog(LOG_ERR, "Unable to open socket"); return 0; } memset(&ifreq, 0, sizeof(struct ifreq)); memset(&ifc, 0, sizeof(struct ifconf)); /* * Request all the devices configured on system */ ifc.ifc_len = sizeof(ifs); ifc.ifc_ifcu.ifcu_req = ifs; if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) { syslog(LOG_ERR, "ioctl(SIOCGIFCONF) :%m"); return 0; } syslog(LOG_DEBUG, "Scanning %ld interfaces for ip 0x%x", (long)(ifc.ifc_len / sizeof(struct ifreq)), rem_ip); /* * Scan through looking for an interface with same subnet * as rem_ip */ ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); for(ifr = ifc.ifc_ifcu.ifcu_req; ifr < ifend; ifr++) { if(ifr->ifr_ifru.ifru_addr.sa_family == AF_INET) { ina = ((struct sockaddr_in *)&ifr->ifr_ifru.ifru_addr)->sin_addr.s_addr; strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); syslog(LOG_DEBUG, "Proxy arp examining interface %s", ifreq.ifr_name); if(ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0) { continue; } if(((ifreq.ifr_ifru.ifru_flags ^ FLAGS_GOOD) & FLAGS_MASK) != 0) { continue; } /* * Get the netmask and check that its the right subnet */ mask = ((struct sockaddr_in *) &ifreq.ifr_ifru.ifru_addr)->sin_addr.s_addr; syslog(LOG_DEBUG, "Proxy arp interface address %lx mask %lx", (unsigned long)ina, (unsigned long)ntohl(mask)); if(((rem_ip ^ ina) & mask) != 0) { continue; } break; } } if(ifr >= ifend) { return 0; } memcpy(name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); syslog(LOG_DEBUG, "Found interface %s for proxy arp", name); /* * Now get the hardware address */ memset(&ifreq.ifr_ifru.ifru_hwaddr, 0, sizeof(struct sockaddr)); if(ioctl(sockfd, SIOCGIFHWADDR, &ifreq) < 0) { syslog(LOG_ERR, "SIOCGIFHWADDR(%s) :%m", ifreq.ifr_name); return 0; } #if 0 #ifdef FREEBSD #elif defined(LINUX) #else # error "Don't know how to get etherner hardware address." #endif #endif *hwaddr = ifreq.ifr_ifru.ifru_hwaddr; syslog(LOG_DEBUG, "Successfully got hardware address"); return 1; } /* * Add a proxy arp */ void AddProxy(ProxyMsg_t *proxy_buf) { struct arpreq arp; int sock_fd = -1; sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) { syslog(LOG_ERR, "Error opening socket for route add %m(%d)", errno); return; } syslog(LOG_DEBUG, "Opened socket %#x", sock_fd); memset(&arp, 0, sizeof(struct arpreq)); syslog(LOG_DEBUG, "get_ether_addr with dst_addr %x", proxy_buf->dst_addr); if (!get_ether_addr(proxy_buf->dst_addr, &arp.arp_ha, arp.arp_dev)) { syslog(LOG_ERR, "Cannot determine ethernet address for proxy arp"); return; } arp.arp_pa.sa_family = AF_INET; ((struct sockaddr_in *)&arp.arp_pa)->sin_addr.s_addr = proxy_buf->dst_addr; arp.arp_flags = ATF_PERM | ATF_PUBL; if (ioctl(sock_fd, SIOCSARP, (caddr_t)&arp) < 0) syslog(LOG_ERR, "Error adding proxy %m(%d)", errno); else syslog(LOG_INFO, "Proxy added"); close(sock_fd); } /* * Add a route */ void AddRoute(RouteMsg_t *rt_buf) { struct rtentry rt; memset(&rt, 0, sizeof(struct rtentry)); if (rt_buf->flags == ROUTE_HOST) { syslog(LOG_DEBUG, "Adding host route"); rt.rt_dst.sa_family = AF_INET; ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = rt_buf->dst_addr; rt.rt_genmask.sa_family = AF_INET; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = rt_buf->mask; rt.rt_dev = rt_buf->dev; rt.rt_metric = rt_buf->metric; rt.rt_flags = RTF_UP | RTF_HOST; } else if(rt_buf->flags == ROUTE_NET) { syslog(LOG_DEBUG, "Adding net route"); rt.rt_dst.sa_family = AF_INET; ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = rt_buf->dst_addr; rt.rt_genmask.sa_family = AF_INET; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = rt_buf->mask; rt.rt_dev = rt_buf->dev; rt.rt_metric = rt_buf->metric; rt.rt_flags = RTF_UP; } else if (rt_buf->flags == ROUTE_GATEWAY) { syslog(LOG_DEBUG, "Adding default route (gateway: 0x%lx)", (unsigned long)rt_buf->gw_addr); rt.rt_dev = rt_buf->dev; rt.rt_flags = RTF_UP | RTF_GATEWAY; rt.rt_dst.sa_family = AF_INET; rt.rt_gateway.sa_family = AF_INET; ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = rt_buf->gw_addr; } if(ioctl(b_sock_fd, SIOCADDRT, &rt) < 0) { syslog(LOG_ERR, "Error adding route %m(%d)", errno); } else { syslog(LOG_INFO, "Route added to %s through %s", iptostr(ntohl(rt_buf->dst_addr)), rt_buf->dev); } } /* * Read a configuration from the config file and validate * the users credentials */ void babd_GetConfig(void (*cbf)(void *, OptionSet_t *), void *obj, OptionSet_t *options) { CfgMessage_t *auth_buf, fooble; FILE *cfg_file; CfgMessage_t dflt; CfgMessage_t current; CfgMessage_t *active = ¤t; char entry[256]; char dynamic_name[MAX_NAME_LENGTH]; char nstr_buf[1024]; char *nstr = nstr_buf, *pstr; int i, t_count = 0, l_count = 0; fooble.reserved1 = cbf; fooble.reserved2 = obj; auth_buf = &fooble; /* we still have to use babylon.conf for dialout */ if (!options->site[0] && am_radius_client) { RadiusReq *req = new RadiusReq(); fooble.m_options = options; req->Go(fooble); return; } fooble.options = *options; cfg_file = fopen(configFile, "r"); if(cfg_file == NULL) { syslog(LOG_ERR, "Error opening config file %s", configFile); goto out_failed; } while (!feof(cfg_file)) { int len; current = dflt; active = ¤t; l_count++; fgets(entry, 256, cfg_file); entry[255] = 0; len = strlen(entry); if (len && entry[len-1] == '\n') entry[--len] = '\0'; syslog(LOG_DEBUG, "%d: %s", l_count, entry); /* * Filter out comments and blanks */ if((entry[0] == '#') || (len == 0)) continue; nstr = nstr_buf; strcpy(nstr, entry); /* 32 is enough to convert all ':'s in the string to nuls. */ memset(nstr+len, '\0', 32); pstr = nstr; /* * convert the tokens to nulls */ t_count = 0; for (i = 0 ; nstr[i] != '\0'; i++) { if (nstr[i] == ':') { t_count++; nstr[i] = '\0'; } } /* * Make sure we have enough fields */ if (t_count < 1 || t_count > 15) { syslog(LOG_WARNING, "Parse error reading config line %d, ignored", l_count); continue; } /* * Get the user name */ strcpy(active->options.user, nstr); nstr += strlen(nstr) + 1; if (active->options.user[0] == '\0') { /* * default entry */ active = &dflt; } syslog(LOG_DEBUG, "User: %s", active->options.user); /* Get Password */ strcpy(active->options.passwd, nstr); nstr += strlen(nstr) + 1; syslog(LOG_DEBUG, "Password: %s", active->options.passwd); /* Get Site */ if (active == &dflt && *nstr) active = ¤t; strcpy(active->options.site, nstr); nstr += strlen(nstr) + 1; syslog(LOG_DEBUG, "Site: %s", active->options.site); /* Get Local IP */ if (nstr[0] == '\0') active->options.loc_ip = dflt.options.loc_ip; else active->options.loc_ip = strtoip(nstr); nstr += strlen(nstr) + 1; /* Get Remote IP */ if (nstr[0] == '\0') active->options.rem_ip = dflt.options.rem_ip; else { if(0 == strncasecmp(nstr, "dynamic", strlen("dynamic"))) { nstr += strlen("dynamic"); while((*nstr == '\t') || (*nstr == ' ')) nstr++; strncpy(dynamic_name, nstr, MAX_NAME_LENGTH); active->options.rem_ip = 0xffffffffU; } else { /* Use static ip address */ active->options.rem_ip = strtoip(nstr); } } nstr += strlen(nstr) + 1; /* Get Netmask */ if (nstr[0] == '\0') active->options.netmask = dflt.options.netmask; else active->options.netmask = strtoip(nstr); nstr += strlen(nstr) + 1; /* Get Primary DNS */ if (nstr[0] == '\0') active->options.dns_primary = dflt.options.dns_primary; else active->options.dns_primary = strtoip(nstr); nstr += strlen(nstr) + 1; /* Get Secondary DNS */ if (nstr[0] == '\0') active->options.dns_second = dflt.options.dns_second; else active->options.dns_second = strtoip(nstr); nstr += strlen(nstr) + 1; /* Get default/net route flag */ if(nstr[0] == '\0') { active->options.defroute = dflt.options.defroute; active->options.netroute = dflt.options.netroute; } else { if(nstr[0] == 'b' || nstr[0] == 'B') { // both routes active->options.defroute = 1; active->options.netroute = 1; } else if(nstr[0] == 'n' || nstr[0] == 'N') { // Add network route active->options.netroute = 1; } else if(nstr[0] == 'd' || nstr[0] == 'D') { // Add default route active->options.defroute = 1; } } nstr += strlen(nstr) + 1; /* Get proxy ARP flag */ if (nstr[0] == '\0') active->options.proxy = dflt.options.proxy; else active->options.proxy = strtol(nstr, NULL, 10); nstr += strlen(nstr) + 1; /* Get the device class */ if (nstr[0] == '\0') strcpy(active->options.dev_class, dflt.options.dev_class); else strcpy(active->options.dev_class, nstr); nstr += strlen(nstr) + 1; /* Get the device name */ if (nstr[0] == '\0') strcpy(active->options.port, dflt.options.port); else strcpy(active->options.port, nstr); nstr += strlen(nstr) + 1; /* Get static paramters */ if (nstr[0] != '\0') { int *ints[] = { &active->options.min_links, &active->options.max_links, &active->options.addtime, &active->options.droptime, &active->options.rx_drop_bpls, &active->options.rx_raise_bpls, &active->options.tx_drop_bpls, &active->options.tx_raise_bpls }; char *sep = strchr(nstr, ','); int i; active->options.min_links = active->options.max_links = atoi(nstr); active->options.addtime = 30; active->options.droptime = 0; for (i=1; sep && i<=7; i++) { if (',' != sep[1]) *ints[i] = atoi(sep+1); sep = strchr(sep+1, ','); } } nstr += strlen(nstr) + 1; /* Get class-specific additional call info */ if (nstr[0] != '\0') { active->options.call_info = atoi(nstr); } nstr += strlen(nstr) + 1; /* Get the phone number for dial out only */ if(nstr[0] != '\0') { strcpy(active->options.phone, nstr); syslog(LOG_DEBUG, "Phone: %s", active->options.phone); } /* * If we filled defaults, we're done */ if (active == &dflt) continue; /* * If the site is set, get options */ if (auth_buf->options.site[0] != '\0') { syslog(LOG_DEBUG, "Checking site %s == %s", auth_buf->options.site, active->options.site); if(strcmp(auth_buf->options.site, active->options.site)) { continue; } else if(auth_buf->options.proto_id == 0xc223) { syslog(LOG_INFO, "Doing chap auth for site %s\n", auth_buf->options.site); strcpy(auth_buf->options.user, active->options.user); do_chap_auth(active, auth_buf); fclose(cfg_file); return; } /* * Now its time to get a dynamic ip address */ if (0xffffffffU == active->options.rem_ip) active->options.rem_ip = request_ip_address(dynamic_name, active->options.call); syslog(LOG_INFO, "%s assigned IP address %s", auth_buf->options.user, iptostr(active->options.rem_ip)); syslog(LOG_INFO, "Dial-out request for site %s, phone %s", auth_buf->options.site,active->options.phone); active->reserved1 = auth_buf->reserved1; active->reserved2 = auth_buf->reserved2; if (auth_buf->options.phone[0]) strcpy(active->options.phone, auth_buf->options.phone); active->options.call = auth_buf->options.call; active->options.pol = auth_buf->options.pol; active->options.is_valid = 1; active->reserved1(active->reserved2, &active->options); fclose(cfg_file); return; } if (auth_buf->options.user[0] == '\0') { /* * Not sure what to do, reject */ syslog(LOG_ERR, "Configure request failed, insufficient information: Username blank?"); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } /* * The user name is set, authenticate */ syslog(LOG_DEBUG, "Checking %s == %s", auth_buf->options.user, active->options.user); if (strcmp(auth_buf->options.user, active->options.user)) continue; /* * Now its time to get a dynamic ip address */ if (0xffffffffU == active->options.rem_ip) active->options.rem_ip = request_ip_address(dynamic_name, active->options.call); syslog(LOG_INFO, "%s assigned IP address %s", auth_buf->options.user, iptostr(active->options.rem_ip)); /* Check the password */ if(((active->options.passwd[0] != '*') && (strlen(active->options.passwd) != 1))) { if(auth_buf->options.proto_id == 0xc023) { /* * Authenticate from the configuration as clear text */ if(strcmp(active->options.passwd, auth_buf->options.passwd)) { syslog(LOG_NOTICE, "PAP Login failure for %s on port %s remote passwd %s, " "invalid password", auth_buf->options.user, auth_buf->options.port, auth_buf->options.passwd); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } else { /* * Password authentication succeeded * check the class and port */ if(active->options.port[0] != '\0') { if(strcasecmp(active->options.port, auth_buf->options.port)) { /* Port check failed */ syslog(LOG_NOTICE, "PAP Login failure for %s on port %s, " "unauthorized port", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } } else if(active->options.dev_class[0] != '\0') { if(strcasecmp(active->options.dev_class, auth_buf->options.dev_class)) { /* Class check failed */ syslog(LOG_NOTICE, "PAP Login failure for %s on port %s, " "unauthorized device class", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } } /* * Log user in */ syslog(LOG_INFO, "PAP Login successful for %s on port %s", auth_buf->options.user, auth_buf->options.port); active->reserved1 = auth_buf->reserved1; active->reserved2 = auth_buf->reserved2; strcpy(active->options.site, auth_buf->options.site); active->options.call = auth_buf->options.call; active->options.pol = auth_buf->options.pol; active->options.is_valid = 1; active->reserved1(active->reserved2, &active->options); fclose(cfg_file); return; } } /* * CHAP */ else if(auth_buf->options.proto_id == 0xc223) { do_chap_auth(active, auth_buf); fclose(cfg_file); return; } /* * Unknown authentication protocol */ else { syslog(LOG_NOTICE, "Login failure for %s on port %s, " "unsupported protocol (%#x)", auth_buf->options.user, auth_buf->options.port, auth_buf->options.proto_id); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } } /* * Get the password from PAM */ else { syslog(LOG_NOTICE, "Login failure for %s on port %s, " "PAM unsupported", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); fclose(cfg_file); return; } } /* * Didn't find a match, reject */ syslog(LOG_NOTICE, "Configure request failed, no user[%s]/site[%s] found", auth_buf->options.user, auth_buf->options.site); out_failed: auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); if (cfg_file) fclose(cfg_file); } char ipbuf[16]; char *iptostr(unsigned int ip) { memset(ipbuf, '\0', sizeof(ipbuf)); sprintf(ipbuf, "%d.%d.%d.%d", ((ip & 0xff000000) >> 24), ((ip & 0x00ff0000) >> 16), ((ip & 0x0000ff00) >> 8), (ip & 0x000000ff)); return ipbuf; } #if 1 unsigned long strtoip(const char *str) { unsigned long a, b, c, d; if (4 != sscanf(str, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) { fprintf(stderr, "invalid ip address '%s'.\n", str); return -1L; } return (a << 24 | b << 16 | c << 8 | d); } #else unsigned int strtoip(const char *str) { int b_count = 3; char *d; unsigned int a = 0; char *nstr; nstr = strdup(str); for(d = strtok(nstr, "."); d != NULL && b_count >= 0; d = strtok(NULL, ".")) a |= (strtoul(d, NULL, 10) << (b_count-- * 8)); free(nstr); return a; } #endif void print_usage() { fprintf(stderr, "Usage: %s [options]\n", cmdname); fprintf(stderr, " -h, --help display this info\n"); fprintf(stderr, " --help-debug display the available debugging levels\n"); fprintf(stderr, " -f, --no-fork don't fork a daemon, run in foreground\n"); fprintf(stderr, " -c, --config-file use file instead of %s\n",DEFAULT_CFG_FILE); fprintf(stderr, " -d, --debug set debug level to (in dec, hex, or oct)\n"); fprintf(stderr, " -n, --system-name set system name to \n"); fprintf(stderr, " -p, --disable-proxyarp don't setup proxy arp for links\n"); fprintf(stderr, " -r, --disable-routing disable routing\n"); fprintf(stderr, " -s, --scan-chan scan for available channels automatically\n"); fprintf(stderr, " -a, --radius-acct= use as radius accounting server\n"); fprintf(stderr, " -u, --radius= use as radius server\n"); fprintf(stderr, " --disable-dynamicip don't use allocate IP nums dynamically for incoming\n"); fprintf(stderr, " calls (default)\n"); fprintf(stderr, " --enable-routing enable routing (default)\n"); fprintf(stderr, " --enable-proxyarp do setup proxy arp for links (default)\n"); fprintf(stderr, " -i, --enable-dynamicip don't use allocate IP nums dynamically for incoming\n"); fprintf(stderr, " calls (default)\n"); fprintf(stderr, " --broken-acfc don't setup ACFC after LCP (enables operation with\n"); fprintf(stderr, " some broken routers)\n"); fprintf(stderr, " -y, --dynamic-file use instead of %s\n",DEFAULT_DYN_FILE); fprintf(stderr, " -k, --disable-down-check disable checking of interface up/down state\n"); #ifdef USE_L2TP fprintf(stderr, " -2, --enable-l2tp Load as l2tp config (/etc/babylon/l2tp.conf).\n"); #endif fprintf(stderr, " See %s(8) for details.\n", cmdname); fprintf(stderr, " See dynamic_ip.conf(5) for details on dynamic IP\n"); exit(2); } void print_debuglevels() { fprintf(stderr,"Babylond debugging levels. OR items together to get appropriate information.\n"), fprintf(stderr,"Level Description\n"), fprintf(stderr,"0x0000 Log Nothing\n"); fprintf(stderr,"0x0001 Log all Protocol transactions\n"); fprintf(stderr,"0x0002 Log all received packets\n"); fprintf(stderr,"0x0004 Log all transmitted packets\n"); fprintf(stderr,"0x0008 Log all state transitions\n"); fprintf(stderr,"0x0010 Log all phase transitions\n"); fprintf(stderr,"0x0020 Log and track allocs and frees\n"); fprintf(stderr,"0x0040 Log function call flow\n"); fprintf(stderr,"0x0080 Log function call statistics\n"); fprintf(stderr,"0x0100 Log driver-daemon communcations\n"); fprintf(stderr,"0x0200 Log list management functions\n"); fprintf(stderr,"0x0400 Log call control activities\n"); fprintf(stderr,"0x0800 Log configuration functions\n"); fprintf(stderr,"\nNOTES:\n1) To log everything, use 0x0FFF\n"); fprintf(stderr,"2) When making problem reports to Spellcaster Support, please use 0x0007\n"); fprintf(stderr," and caption this information.\n"); exit(3); } void do_chap_auth(CfgMessage_t *active, CfgMessage_t *auth_buf) { MD5_CTX md5; unsigned char in[512]; unsigned char *pin = in; if(auth_buf->options.challenge) { /* * compute a CHAP response */ MD5Init(&md5); pin = in; memcpy(pin, &auth_buf->options.auth_info, 1); // Ident syslog(LOG_DEBUG, "CHAP Ident = %#x", auth_buf->options.auth_info); memcpy(pin + 1, active->options.passwd, strlen(active->options.passwd)); syslog(LOG_DEBUG, "CHAP Password = %s", active->options.passwd); pin += (1 + strlen(active->options.passwd)); memcpy(pin, auth_buf->options.chap_rvalue, auth_buf->options.chap_rvalue_len); pin += auth_buf->options.chap_rvalue_len; /* syslog(LOG_DEBUG, "CHAP Value = %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", */ MD5Update(&md5, in, (uint) (pin - &in[0])); MD5Final(&md5); memcpy(active->options.chap_rvalue, md5.digest, 16); active->options.chap_rvalue_len = 16; active->reserved1 = auth_buf->reserved1; active->reserved2 = auth_buf->reserved2; active->options.auth_info = auth_buf->options.auth_info; strncpy(active->options.site, auth_buf->options.site, SITE_NAME_LEN); strcpy(active->options.dev_class, auth_buf->options.dev_class); strcpy(active->options.port, auth_buf->options.port); active->options.call = auth_buf->options.call; active->options.pol = auth_buf->options.pol; active->options.is_valid = 1; active->options.challenge = 1; active->options.proto_id = auth_buf->options.proto_id; active->reserved1(active->reserved2, &active->options); return; } else { /* * Compute the expected digest */ MD5Init(&md5); pin = in; *pin = auth_buf->options.auth_info; // Ident memcpy(pin + 1, active->options.passwd, strlen(active->options.passwd)); pin += (1 + strlen(active->options.passwd)); memcpy(pin, auth_buf->options.chap_lvalue, auth_buf->options.chap_lvalue_len); pin += auth_buf->options.chap_lvalue_len; MD5Update(&md5, in, (uint)(pin - in)); MD5Final(&md5); /* * Compare with what we got */ if(auth_buf->options.chap_rvalue_len != 16 || memcmp(auth_buf->options.chap_rvalue, md5.digest, 16)) { syslog(LOG_NOTICE, "CHAP Challenge failed for %s on port %s, " "invalid response", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); return; } /* * Password authentication succeeded * check the class and port */ if(active->options.port[0] != '\0') { if(strcasecmp(active->options.port, auth_buf->options.port)) { /* Port check failed */ syslog(LOG_NOTICE, "CHAP Challenge failed for %s on port %s, " "unauthorized port", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); return; } } else if(active->options.dev_class[0] != '\0') { if(strcasecmp(active->options.dev_class, auth_buf->options.dev_class)) { /* Class check failed */ syslog(LOG_NOTICE, "CHAP Challenge failed for %s on port %s, " "unauthorized device class", auth_buf->options.user, auth_buf->options.port); auth_buf->reserved1(auth_buf->reserved2, &auth_buf->options); return; } } /* * Log user in */ syslog(LOG_INFO, "CHAP Challenge successful for %s on port %s", auth_buf->options.user, auth_buf->options.port); active->reserved1 = auth_buf->reserved1; active->reserved2 = auth_buf->reserved2; active->options.auth_info = auth_buf->options.auth_info; strcpy(active->options.site, auth_buf->options.site); active->options.call = auth_buf->options.call; active->options.pol = auth_buf->options.pol; active->options.is_valid = 1; active->reserved1(active->reserved2, &active->options); return; } } /* * Read a configuration from the config file and validate * the users credentials */ int parse_dynamic() { FILE *dyn_file = fopen(dynamicFile, "r"); int l_count = 0; char entry[256]; char nstr_buf[1024]; char *pstr, *nstr, *tstr; unsigned int begin_ip, end_ip, i; dynamic_pool_t *new_pool = NULL; ip_address_t *cur_include, *cur_exclude; char *beg, *end; /* list headers */ ip_pool_head_t include_pool_head; ip_pool_head_t exclude_pool_head; dynamic_pool_head_t new_pool_head; /* Initialize lists */ LIST_INIT(&ip_pool); LIST_INIT(&exclude_pool_head); LIST_INIT(&include_pool_head); LIST_INIT(&new_pool_head); if (dyn_file == NULL) { if (-1 == dyn_ip_enabled) { dyn_ip_enabled = 0; return 0; } syslog(LOG_ERR, "Error opening config file %s",dynamicFile); return -1; } while (!feof(dyn_file)) { memset(entry, '\0', 256); l_count++; fgets(entry, 256, dyn_file); if (strlen(entry)) entry[strlen(entry) - 1] = '\0'; syslog(LOG_DEBUG, "%d: %s", l_count, entry); /* * Filter out comments and blanks */ if((entry[0] == '#') || (strlen(entry) == 0)) continue; pstr = nstr = nstr_buf; memset(nstr, '\0', strlen(entry) * 2); for (tstr = entry; *tstr != '\0'; tstr++) { if((*tstr != ' ') && (*tstr != '\t')) { *pstr = *tstr; pstr++; } } *pstr = '\0'; pstr = nstr; /* * Look for 'range' tag */ if (0 == strncasecmp(pstr, RANGE_TAG, strlen(RANGE_TAG))) { if (new_pool == NULL) { /* * We have a new range and we don't need * to sort out inc/exc from last range. */ pstr += strlen(RANGE_TAG); new_pool = new dynamic_pool_t; if (NULL == new_pool) { syslog(LOG_ERR, "Out of memory reading config file"); return -1; } LIST_INIT(&new_pool->ip_list); LIST_INIT(&new_pool->in_use); strncpy(new_pool->name, pstr, MAX_NAME_LENGTH); } else { /* * We have a new pool to deal with but must queue the old * one first */ if(NULL == shuffle_ip(&include_pool_head, &exclude_pool_head)) { syslog(LOG_ERR, "Error in config file"); return -1; } new_pool->ip_list.lh_first = include_pool_head.lh_first; include_pool_head.lh_first->ptr.le_prev = &new_pool->ip_list.lh_first; if(ip_pool.lh_first == NULL) LIST_INSERT_HEAD(&ip_pool, new_pool, ptr); else LIST_INSERT_AFTER(ip_pool.lh_first, new_pool, ptr); LIST_INIT(&include_pool_head); LIST_INIT(&exclude_pool_head); /* * Now that we've got the previous pool out of the way * deal with the next range */ pstr += strlen(RANGE_TAG); new_pool = new dynamic_pool_t; if (NULL == new_pool) { syslog(LOG_ERR, "Out of memory reading config file"); return -1; } LIST_INIT(&new_pool->ip_list); LIST_INIT(&new_pool->in_use); strncpy(new_pool->name, pstr, MAX_NAME_LENGTH); } continue; } else if((0 == strncasecmp(pstr, INCLUDE_TAG, strlen(INCLUDE_TAG))) && (new_pool != NULL)) { pstr += strlen(INCLUDE_TAG); if(NULL == (beg = strtok(pstr, "-"))) return -1; end = strtok(NULL, "-"); begin_ip = strtoip(beg); if(end != NULL) end_ip = strtoip(end); else end_ip = begin_ip; if (NULL == include_pool_head.lh_first) { /* * We're dealing with the first include * batch. */ cur_include = new ip_address_t; if (NULL == cur_include) { syslog(LOG_ERR, "Out of memory reading ip config file"); return -1; } LIST_INSERT_HEAD(&include_pool_head, cur_include, ptr); cur_include->address = begin_ip; cur_include->ident = 0; begin_ip++; } for(i = begin_ip; i <= end_ip; i++) { cur_include = new ip_address_t; if (NULL == cur_include) { syslog(LOG_ERR, "Out of memory reading ip config file"); return -1; } cur_include->address = i; cur_include->ident = 0; LIST_INSERT_AFTER(include_pool_head.lh_first, cur_include, ptr); } continue; } else if((0 == strncasecmp(pstr, EXCLUDE_TAG, strlen(EXCLUDE_TAG))) && (new_pool != NULL)) { pstr += strlen(EXCLUDE_TAG); if(NULL == (beg = strtok(pstr, "-"))) return -1; end = strtok(NULL, "-"); begin_ip = strtoip(beg); if(end != NULL) end_ip = strtoip(end); else end_ip = begin_ip; if(NULL == exclude_pool_head.lh_first) { /* * We're dealing with the first exclude * batch. */ cur_exclude = new ip_address_t; if (NULL == cur_exclude) { syslog(LOG_ERR, "Out of memory reading ip config file"); return -1; } cur_exclude->address = begin_ip; cur_exclude->ident = 0; LIST_INSERT_HEAD(&exclude_pool_head, cur_exclude, ptr); begin_ip++; } for(i = begin_ip; i <= end_ip; i++) { cur_exclude = new ip_address_t; if (NULL == cur_exclude) { syslog(LOG_ERR, "Out of memory reading ip config file"); return -1; } cur_exclude->address = i; cur_exclude->ident = 0; LIST_INSERT_AFTER(exclude_pool_head.lh_first, cur_exclude, ptr); } // free(nstr); continue; } else { syslog(LOG_ERR, "Parse error on config file line %d.", l_count); return -1; } } /* * Unfortunately we have to make sure that we don't need to collate(??) * one final round of inc/exc ip addresses */ if(new_pool != NULL) { if(NULL == shuffle_ip(&include_pool_head, &exclude_pool_head)) { syslog(LOG_ERR, "Error in config file"); return -1; } /* * Get around some LIST_ problems and insert the new * ip list into the pool. */ new_pool->ip_list.lh_first = include_pool_head.lh_first; include_pool_head.lh_first->ptr.le_prev = &new_pool->ip_list.lh_first; if(ip_pool.lh_first == NULL) LIST_INSERT_HEAD(&ip_pool, new_pool, ptr); else LIST_INSERT_AFTER(ip_pool.lh_first, new_pool, ptr); /* No longer need the new_pool pointer, so reset it */ LIST_INIT(&include_pool_head); LIST_INIT(&exclude_pool_head); new_pool = NULL; } print_pool(); return 0; } ip_pool_head_t *shuffle_ip(ip_pool_head_t *include, ip_pool_head_t *exclude) { ip_address_t *cur_include, *cur_exclude, *rem_inc; if(include->lh_first == NULL) { /* No includes in last range def'n -- error */ return (NULL); } else if(exclude->lh_first == NULL) { /* No ip addresses to exclude */ return (include); } else { /* Must exclude some ip address */ cur_exclude = exclude->lh_first; while(cur_exclude != NULL) { cur_include = include->lh_first; while(cur_include != NULL) { if(cur_exclude->address == cur_include->address) { /* Remove this ip address */ rem_inc = cur_include; cur_include = cur_include->ptr.le_next; LIST_REMOVE(rem_inc, ptr); delete rem_inc; break; } else { /* Go on to next address */ cur_include = cur_include->ptr.le_next; } } LIST_REMOVE(cur_exclude, ptr); delete cur_exclude; cur_exclude = exclude->lh_first; } return (include); } /* If we get here, something went wrong! */ return (NULL); } unsigned int request_ip_address(char *name, void *id) { dynamic_pool_t *current = ip_pool.lh_first; ip_address_t *found_ip; unsigned int addr; while(current) { if(0 == strncasecmp(current->name, name, MAX_NAME_LENGTH)) { /* * We've found a match for the pool so now * grab the first free ip address */ found_ip = current->ip_list.lh_first; if(NULL != found_ip) { /* * Not NULL so we have at lease one * address to assign. */ addr = found_ip->address; LIST_REMOVE(found_ip, ptr); found_ip->ident = id; if(current->in_use.lh_first == NULL) LIST_INSERT_HEAD(¤t->in_use, found_ip, ptr); else LIST_INSERT_AFTER(current->in_use.lh_first, found_ip, ptr); return (addr); } else return 0; } else { /* Go on to next */ current = current->ptr.le_next; } } return 0; } void release_ip_address(void *id) { dynamic_pool_t *current = ip_pool.lh_first; ip_address_t *curr_ip, *new_ip; while(current) { /* * We've found a match for the pool so now * find the id. */ curr_ip = current->in_use.lh_first; while((curr_ip != NULL) && (curr_ip->ident != id)) { curr_ip = curr_ip->ptr.le_next; } if(curr_ip != NULL) { /* * Found the in use IP */ LIST_REMOVE(curr_ip, ptr); curr_ip->ident = 0; if(current->ip_list.lh_first == NULL) { LIST_INSERT_HEAD(¤t->ip_list, curr_ip, ptr); } else { /* In the interest of good re-use, add the IP to the end */ new_ip = current->ip_list.lh_first; while(new_ip->ptr.le_next != NULL) new_ip = new_ip->ptr.le_next; LIST_INSERT_AFTER(new_ip, curr_ip, ptr); } return; } else { /* Go on to next */ current = current->ptr.le_next; } } }