/* * tcpvsadm - TCP Virtual Server ADMinistration program * * Version: $Id$ * * Authors: Wensong Zhang * * Note that most code is taken from ipvsadm.c. * * 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. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #undef __KERNEL__ /* Makefile lazyness ;) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For __uXX types */ #include #include #include #include #include "tcp_vs.h" #define TCPVS_PROC_FILE "/proc/net/ktcpvs/config" #if 0 struct tcp_vs_ctl { int cmd; char name[KTCPVS_VS_NAME_MAXLEN]; char sched_name[KTCPVS_SCHEDNAME_MAXLEN]; int serverport; /* server port number */ unsigned timeout; /* timeout in ticks */ int maxSpareServers; int minSpareServers; int startServers; int maxClients; int keepAlive; int maxKeepAliveRequests; int keepAliveTimeout; u_int32_t daddr; u_int16_t dport; int weight; }; #endif /* default scheduler */ #define DEF_SCHED "wlc" /* printing format flags */ #define FMT_NONE 0x0000 #define FMT_NUMERIC 0x0001 int string_to_number(const char *s, int min, int max); int host_to_addr(const char *name, struct in_addr *addr); char * addr_to_host(struct in_addr *addr); char * addr_to_anyname(struct in_addr *addr); int service_to_port(const char *name, unsigned short proto); char * port_to_service(int port, unsigned short proto); char * port_to_anyname(int port, unsigned short proto); int parse_service(char *buf, u_int16_t proto, u_int32_t *addr, u_int16_t *port); int parse_netmask(char *buf, u_int32_t *addr); int parse_timeout(char *buf, unsigned *timeout); void list_vs(unsigned int options); int write_vs(struct tcp_vs_ctl *ctl); void usage_exit(char *program); void fail(int err, char *text); static struct option long_options[] = { {"add-tcpvs", 0, 0, 'A'}, {"edit-tcpvs", 0, 0, 'E'}, {"delete-tcpvs", 0, 0, 'D'}, {"clear", 0, 0, 'C'}, {"list", 0, 0, 'L'}, {"add-server", 0, 0, 'a'}, {"edit-server", 0, 0, 'e'}, {"delete-server", 0, 0, 'd'}, {"help", 0, 0, 'h'}, {"ident", 1, 0, 'i'}, {"scheduler", 1, 0, 's'}, {"persistent", 2, 0, 'p'}, {"real-server", 1, 0, 'r'}, {"netmask", 1, 0, 'M'}, {"weight", 1, 0, 'w'}, {"numeric", 0, 0, 'n'}, {0, 0, 0, 0} }; int main(int argc, char **argv) { int cmd, c; int parse; char *optstr = ""; int result; unsigned int options = FMT_NONE; struct tcp_vs_ctl ctl; /* * If no other arguement, list TCPVS_PROC_FILE */ if (argc == 1){ list_vs(options); exit(0); } memset(&ctl, 0, sizeof(struct tcp_vs_ctl)); /* * Want user virtual server control */ if ((cmd = getopt_long(argc, argv, "AEDCaedlLh", long_options, NULL)) == EOF) usage_exit(argv[0]); switch (cmd) { case 'A': ctl.cmd = TCP_VS_CMD_ADD; optstr = "i:s:M:p::"; break; case 'E': ctl.cmd = TCP_VS_CMD_SET; optstr = "i:s:M:p::"; break; case 'D': ctl.cmd = TCP_VS_CMD_DEL; optstr = "i:"; break; case 'a': ctl.cmd = TCP_VS_CMD_ADD_DEST; optstr = "i:w:r:R:"; break; case 'e': ctl.cmd = TCP_VS_CMD_SET_DEST; optstr = "i:w:r:R:"; break; case 'd': ctl.cmd = TCP_VS_CMD_DEL_DEST; optstr = "i:w:r:R:"; break; case 'C': ctl.cmd = TCP_VS_CMD_FLUSH; optstr = ""; break; case 'L': case 'l': ctl.cmd = TCP_VS_CMD_LIST; optstr = "n"; break; default: usage_exit(argv[0]); } /* weight=0 is allowed, which means that server is quiesced */ ctl.weight = -1; /* the default serverport, will implement to configure it later */ ctl.serverport = 8080; /* * Set the default persistent granularity to /32 masking */ ctl.netmask = ((unsigned long int) 0xffffffff); while ((c=getopt_long(argc, argv, optstr, long_options, NULL)) != EOF) { switch (c) { case 'i': if (strlen(ctl.name) != 0) fail(2, "multiple tcpvs specified"); strncpy(ctl.name, optarg, KTCPVS_VSNAME_MAXLEN); break; case 's': if (strlen(ctl.sched_name) != 0) fail(2, "multiple scheduler specified"); strncpy(ctl.sched_name, optarg, KTCPVS_SCHEDNAME_MAXLEN); break; case 'p': /* ctl.flags = IP_VS_SVC_F_PERSISTENT; */ if (!optarg && optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') optarg = argv[optind++]; parse = parse_timeout(optarg, &ctl.timeout); if (parse == 0) fail(2, "illegal persistent timeout"); break; case 'M': parse = parse_netmask(optarg, &ctl.netmask); if (parse != 1) fail(2, "illegal persistent mask specified"); break; case 'r': case 'R': parse = parse_service(optarg, IPPROTO_TCP, &ctl.daddr, &ctl.dport); if (parse == 0) fail(2, "illegal dest address:port specified"); /* copy vport to dport if none specified */ if (parse == 1) ctl.dport = ctl.serverport; break; case 'w': if (ctl.weight != -1) fail(2, "multiple server weights specified"); if ((ctl.weight= string_to_number(optarg,0,65535)) == -1) fail(2, "illegal weight specified"); break; case 'n': options |= FMT_NUMERIC; break; default: fail(2, "invalid option"); } } if (optind < argc) fail(2, "unknown arguments found in command line"); if (ctl.cmd == TCP_VS_CMD_LIST) { list_vs(options); exit(0); } if (ctl.cmd == TCP_VS_CMD_ADD || ctl.cmd == TCP_VS_CMD_SET) { /* * Set the default scheduling algorithm if not specified */ if (strlen(ctl.sched_name) == 0) strcpy(ctl.sched_name, DEF_SCHED); } if (ctl.cmd == TCP_VS_CMD_ADD_DEST || ctl.cmd == TCP_VS_CMD_SET_DEST) { /* * Set the default weight 1 if not specified */ if (ctl.weight == -1) ctl.weight = 1; } result = write_vs(&ctl); return result; } void list_vs(unsigned int format) { static char buffer[1024]; FILE *handle; handle = fopen(TCPVS_PROC_FILE, "r"); if (!handle) { fprintf(stderr, "Could not open the %s file\n" "Are you sure that the KTCPVS module is inserted " "in the kernel?\n", TCPVS_PROC_FILE); exit(1); } /* * Print the KTCPVS information */ while (!feof(handle)) { if (fgets(buffer, sizeof(buffer), handle)) printf("%s", buffer); } fclose(handle); } int write_vs(struct tcp_vs_ctl *ctl) { FILE *handle; int ret; handle = fopen(TCPVS_PROC_FILE, "w"); if (!handle) { fprintf(stderr, "Could not open the %s file\n" "Are you sure that the KTCPVS module is inserted " "in the kernel?\n", TCPVS_PROC_FILE); exit(1); } ret = fwrite(ctl, sizeof(*ctl), 1, handle); if (ret < 0) { /* * We will add some code to parse the error no here later */ fprintf(stderr, "write the %s file error.\n", TCPVS_PROC_FILE); exit (1); } fclose(handle); return ret; } int string_to_number(const char *s, int min, int max) { int number; char *end; number = (int)strtol(s, &end, 10); if (*end == '\0' && end != s) { /* we parsed a number, let's see if we want this */ if (min <= number && number <= max) return number; else return -1; } else return -1; } /* * Get netmask. * Return 0 if failed, * 1 if addr read */ int parse_netmask(char *buf, u_int32_t *addr) { struct in_addr inaddr; if (inet_aton(buf, &inaddr) != 0) *addr = inaddr.s_addr; else if (host_to_addr(buf, &inaddr) != -1) *addr = inaddr.s_addr; else return 0; return 1; } /* * Get IP address and port from the argument. * Return 0 if failed, * 1 if addr read * 2 if addr and port read */ int parse_service(char *buf, u_int16_t proto, u_int32_t *addr, u_int16_t *port) { char *pp; long prt; struct in_addr inaddr; pp = strchr(buf,':'); if (pp) *pp = '\0'; if (inet_aton(buf, &inaddr) != 0) *addr = inaddr.s_addr; else if (host_to_addr(buf, &inaddr) != -1) *addr = inaddr.s_addr; else return 0; if (pp == NULL) return 1; if ((prt=string_to_number(pp+1, 0, 65535)) != -1) *port = htons(prt); else if ((prt=service_to_port(pp+1, proto)) != -1) *port = htons(prt); else return 0; return 2; } /* * Get the timeout of persistent service. * Return 0 if failed(the timeout value is less or equal than zero). * 1 if succeed. */ int parse_timeout(char *buf, unsigned *timeout) { int i; if (buf == NULL) { *timeout = TCP_VS_TEMPLATE_TIMEOUT; return 1; } if ((i=string_to_number(buf, 1, 86400*31)) == -1) return 0; *timeout = i * HZ; return 1; } void usage_exit(char *program) { printf("tcpvsadm v0.0.1\n" "help will be added later. :-)\n"); exit(0); } void fail(int err, char *text) { printf("%s\n",text); exit(err); } int host_to_addr(const char *name, struct in_addr *addr) { struct hostent *host; if ((host = gethostbyname(name)) != NULL) { if (host->h_addrtype != AF_INET || host->h_length != sizeof(struct in_addr)) return -1; /* warning: we just handle h_addr_list[0] here */ memcpy(addr, host->h_addr_list[0], sizeof(struct in_addr)); return 0; } return -1; } char * addr_to_host(struct in_addr *addr) { struct hostent *host; if ((host = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET)) != NULL) return (char *) host->h_name; else return (char *) NULL; } char * addr_to_anyname(struct in_addr *addr) { char *name; if ((name = addr_to_host(addr)) != NULL) return name; else return inet_ntoa(*addr); } int service_to_port(const char *name, unsigned short proto) { struct servent *service; if (proto == IPPROTO_TCP && (service = getservbyname(name, "tcp")) != NULL) return ntohs((unsigned short) service->s_port); else if (proto == IPPROTO_UDP && (service = getservbyname(name, "udp")) != NULL) return ntohs((unsigned short) service->s_port); else return -1; } char * port_to_service(int port, unsigned short proto) { struct servent *service; if (proto == IPPROTO_TCP && (service = getservbyport(htons(port), "tcp")) != NULL) return service->s_name; else if (proto == IPPROTO_UDP && (service = getservbyport(htons(port), "udp")) != NULL) return service->s_name; else return (char *) NULL; } char * port_to_anyname(int port, unsigned short proto) { char *name; static char buf[10]; if ((name = port_to_service(port, proto)) != NULL) return name; else { sprintf(buf, "%d", port); return buf; } }