diff options
author | wensong <wensong@36f5d8da-7431-0410-8ca5-ec586ed2521a> | 1999-07-01 09:13:04 +0000 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2013-05-22 15:03:32 +0900 |
commit | 5a868fd4d95d20fcc5b901681cdbd5a7382f6aba (patch) | |
tree | 15de41b5a3186ecbb8bd2d20784bc21be3757165 | |
parent | 13da3fbd884cad8afff6b7b09eef536e26e69442 (diff) | |
download | ipvsadm-5a868fd4d95d20fcc5b901681cdbd5a7382f6aba.tar.gz |
ipvsadm v1.12 releasev1.12
git-svn-id: http://svn.linuxvirtualserver.org/repos/ipvsadm/trunk@39 36f5d8da-7431-0410-8ca5-ec586ed2521a
-rw-r--r-- | Makefile | 112 | ||||
-rw-r--r-- | README | 51 | ||||
-rw-r--r-- | config_stream.c | 107 | ||||
-rw-r--r-- | config_stream.h | 19 | ||||
-rw-r--r-- | dynamic_array.c | 256 | ||||
-rw-r--r-- | dynamic_array.h | 177 | ||||
-rw-r--r-- | ipvsadm-restore | 27 | ||||
-rw-r--r-- | ipvsadm-save | 27 | ||||
-rw-r--r-- | ipvsadm.8 | 412 | ||||
-rw-r--r-- | ipvsadm.c | 1464 | ||||
-rw-r--r-- | ipvsadm.sh | 83 | ||||
-rw-r--r-- | ipvsadm.spec.in | 79 |
12 files changed, 2814 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca1b264 --- /dev/null +++ b/Makefile @@ -0,0 +1,112 @@ +# +# ipvsadm - IP Virtual Server ADMinistration program +# +# Version: $Id$ +# +# Authors: Wensong Zhang <wensong@iinchina.net> +# Peter Kese <peter.kese@ijs.si> +# +# This file: +# +# ChangeLog +# +# Wensong : Modified the Makefile and the spec files so +# : that rpms can be created with ipvsadm alone +# P.Copeland : Modified the Makefile and the spec files so +# : that it is possible to create rpms on the fly +# : using 'make rpms' +# : Also added NAME, VERSION and RELEASE numbers to +# : the Makefile +# Horms : Updated to add config_stream.c dynamic_array.c +# : Added autodetection of libpot +# : Added BUILD_ROOT support +# Wensong : Changed the OBJS according to detection +# Horms : Moved ipvsadm back into /sbin where it belongs +# as it is more or less analogous to both route +# and ipchains both of which reside in /sbin. +# Added rpm target whose only dependancy is +# the rpms target +# + +NAME = ipvsadm +VERSION = 1.12 +RELEASE = 1 + +CC = gcc +CFLAGS = -Wall -Wunused -g -O2 +SBIN = $(BUILD_ROOT)/sbin +MAN = $(BUILD_ROOT)/usr/man/man8 +MKDIR = mkdir +INSTALL = install +INCLUDE = -I/usr/src/linux/include +LIB_SEARCH = /lib /usr/lib /usr/local/lib + +# Where to install INIT scripts +# Will only install files here if these directories already exist +# as if the directories don't exist then the system is unlikely to +# use the files +INIT = $(BUILD_ROOT)/etc/rc.d/init.d + +##################################### +# No servicable parts below this line + +POPT_LIB = $(shell for i in $(LIB_SEARCH); do \ + if [ -f $$i/libpopt.a ]; then \ + if nm $$i/libpopt.a | fgrep -q poptGetContext; then \ + echo "-L$$i -lpopt"; \ + fi; \ + fi; \ +done) + +ifneq (,$(POPT_LIB)) +POPT_DEFINE = -DHAVE_POPT +OBJS = config_stream.o dynamic_array.o ipvsadm.o +else +OBJS = ipvsadm.o +endif + +LIBS = $(POPT_LIB) +DEFINES = $(POPT_DEFINE) + +.PHONY = all clean install + +all: ipvsadm + +ipvsadm: $(OBJS) + $(CC) $(CFLAGS) -o ipvsadm $(OBJS) $(LIBS) + +install: ipvsadm + strip ipvsadm + if [ ! -d $(SBIN) ]; then $(MKDIR) -p $(SBIN); fi + $(INSTALL) -m 0755 ipvsadm $(SBIN) + $(INSTALL) -m 0755 ipvsadm-save $(SBIN) + $(INSTALL) -m 0755 ipvsadm-restore $(SBIN) + if [ ! -d $(MAN) ]; then $(MKDIR) -p $(MAN); fi + $(INSTALL) -m 0644 ipvsadm.8 $(MAN) + if [ -d $(INIT) ]; then \ + $(INSTALL) -m 0755 ipvsadm.sh $(INIT)/ipvsadm ;\ + fi + +clean: + rm -f ipvsadm *.o core *~ $(NAME).spec \ + $(NAME)-$(VERSION).tar.gz + +dist: clean + sed -e "s/@@VERSION@@/$(VERSION)/g" \ + -e "s/@@RELEASE@@/$(RELEASE)/g" \ + < ipvsadm.spec.in > ipvsadm.spec + ( cd .. ; tar czvf $(NAME)-$(VERSION).tar.gz \ + --exclude CVS \ + --exclude $(NAME)-$(VERSION).tar.gz \ + ipvsadm ; \ + mv $(NAME)-$(VERSION).tar.gz ipvsadm ) + +rpm: rpms + +rpms: dist + cp $(NAME)-$(VERSION).tar.gz /usr/src/redhat/SOURCES/ + cp $(NAME).spec /usr/src/redhat/SPECS/ + (cd /usr/src/redhat/SPECS/ ; rpm -ba $(NAME).spec) + +%.o: %.c + $(CC) $(CFLAGS) $(INCLUDE) $(DEFINES) -c $< @@ -0,0 +1,51 @@ +-------------------------------------------------------------------------- + +ipvsadm - Version 1.11 - 16th June 2000 + +This is free software. See below for details. + +ippfvsadm is a utility to administer the IP virtual server services +offered by the Linux kernel with virtual server patch. + +This version of ipvsadm requires Linux kernel version 2.2.14 plus IPVS +patch 0.9.10 or later. Check out the Linux Virtual Server Project +home page on the World Wide Web: + http://www.LinuxVirtualServer.org/ + or + http://www.Linux-VS.org/ +for the most recent information and original sources about ipvsadm. + +To make, make sure your Linux kernel is already patched with IPVS and +"make menuconfig" or "make xconfig" to setup the right compiling options, +see the README of the virtual server patch for detail, then simply type + make +in the source directory. + +Install to your liking. We suggest the following pathnames: + /sbin/ipvsadm + /usr/man/man8/ipvsadm.8 +This will be done automatically when calling + make install +in the source directory. + + +Wensong Zhang <wensong@iinchina.net> + + +-------------------------------------------------------------------------- + +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. + +-------------------------------------------------------------------- diff --git a/config_stream.c b/config_stream.c new file mode 100644 index 0000000..7692ccc --- /dev/null +++ b/config_stream.c @@ -0,0 +1,107 @@ +/* + * Code to convert a stream input into a dynamic array + * that can be parsed as argc and argv. + * + * Authors: Horms <horms@vergenet.net> + * + * Released under the terms of the GNU GPL + */ + +#include "config_stream.h" + + +/********************************************************************** + * config_stream_read + * Read in a config file and put elements in a dynamic array + * pre: stream: stream to read configuration from + * return: dynamic array whose elements are the space delimited + * tokens read from the stream. Result is returned + * once a newline is reached so multiple calls + * will be required to read an entire stream. + * Everthing including and after a hash (#) on a line is + * ignored + **********************************************************************/ + +dynamic_array_t *config_stream_read (FILE *stream, const char *first_element){ + char token[MAX_LINE_LENGTH]; + char tail[2]; + char format[MAX_LINE_LENGTH]; + char format_whitespace[MAX_LINE_LENGTH]; + int status; + int ntoken; + int comment=0; + char *s; + int c; + int flag; + dynamic_array_t *a; + + extern int errno; + + if((a=dynamic_array_create((size_t)0))==NULL){ + perror("config_file_read: dynamic_array_create"); + return(NULL); + } + + /*insert a argv[0] into the dynamic array*/ + if((a=dynamic_array_add_element( + a, + (first_element!=NULL?first_element:""), + DESTROY_STR, + DUP_STR + ))==NULL){ + perror("config_file_read: dynamic_array_add_element"); + return(NULL); + } + + sprintf(format, "%%%d[^ \t\n\r]%%1[ \t\n\r]", MAX_LINE_LENGTH); + sprintf(format_whitespace, "%%%d[ \t\r]%%1[\n]", MAX_LINE_LENGTH); + + ntoken=0; + while((status=fscanf(stream, format, token, tail))!=EOF){ + if(status==0) { + flag=1; + while(flag){ + c=fgetc(stream); + switch(c){ + case EOF: + case '\n': + return(a); + case '\t': + case '\r': + case ' ': + break; + default: + ungetc(c, stream); + flag=0; + } + } + continue; + } + if(!comment && strcmp(token, "ipvsadm")){ + ntoken++; + if((a=dynamic_array_add_element( + a, + token, + DESTROY_STR, + DUP_STR + ))==NULL){ + perror("config_file_read: dynamic_array_add_element"); + dynamic_array_destroy(a, DESTROY_STR); + return(NULL); + } + } + if((s=strrchr(tail, '\n'))!=NULL){ + return(a); + } + if(!comment){ + comment=(strchr((s!=NULL?s:tail), '#')==NULL)?0:1; + } + } + + if(ntoken==0){ + dynamic_array_destroy(a, DESTROY_STR); + return(NULL); + } + + return(a); +} diff --git a/config_stream.h b/config_stream.h new file mode 100644 index 0000000..528c01f --- /dev/null +++ b/config_stream.h @@ -0,0 +1,19 @@ +/* + * Code to convert a stream input into a dynamic array + * that can be parsed as argc and argv. + * + * Authors: Horms <horms@vergenet.net> + * + * Released under the terms of the GNU GPL + */ + +#ifndef CONFIG_STREAM_FLIM +#define CONFIG_STREAM_FLIM + +#include "dynamic_array.h" + +#define MAX_LINE_LENGTH 4096 + +dynamic_array_t *config_stream_read (FILE *stream, const char *first_element); + +#endif diff --git a/dynamic_array.c b/dynamic_array.c new file mode 100644 index 0000000..c8980db --- /dev/null +++ b/dynamic_array.c @@ -0,0 +1,256 @@ +/* + * Dynamic array, to store all your flims in Includes macros required + * to create an array of strings but as the primitive type for the + * array is void * providing your own duplicate_primitive and + * destroy_primitive functions will allow you to use the dynamic_array + * API to have a dynamic array containing any primitive + * + * Authors: Horms <horms@vergenet.net> + * + * Released under the terms of the GNU GPL + * + */ + +#include "dynamic_array.h" + + +/********************************************************************** + * dynamic_array_create + * Create a dynamic array + * pre: block_size: blocking size to use. + * DEFAULT_DYNAMIC_ARRAY_BLOCK_SIZE is used if block_size is 0 + * Block size refers to how many elements are prealocated + * each time the array is grown. + * return: An empty dynamic array + * NULL on error + **********************************************************************/ + +dynamic_array_t *dynamic_array_create(size_t block_size){ + dynamic_array_t *a; + + extern int errno; + + if((a=(dynamic_array_t *)malloc(sizeof(dynamic_array_t)))==NULL){ + return(NULL); + } + + a->vector=NULL; + a->count=0; + a->allocated_size=0; + a->block_size=block_size?block_size:DEFAULT_DYNAMIC_ARRAY_BLOCK_SIZE; + + return(a); +} + + +/********************************************************************** + * dynamic_array_destroy + * Free an array an all the elements held within + * pre: a: array to destroy + * destroy_element: pointer to funtion to destroy array elements + * Function should take an argument of a pointer + * and free the memory allocated to the structure + * pointed to. + * post: array is freed and destroy_element is called for all elements + * of the array. + * Nothing if a is NULL + **********************************************************************/ + +void dynamic_array_destroy(dynamic_array_t *a, void (*destroy_element)(void *)){ + if(a==NULL) return; + while(a->count-->0){ + destroy_element(*(a->vector+a->count)); + } + if(a->allocated_size>0){ + free(a->vector); + } +} + + +/********************************************************************** + * dynamic_array_add_element + * Add an element to a dynamic array + * pre: a: dynamic array to add element to + * e: element to add + * destroy_element: pointer to a function to destroy an element + * passed to dynamic_array_destroy on error + * duplicate_element: pointer to a function to duplicate an + * element should take a pointer to an element + * to duplicate as the only element and return + * a copy of the element Any memory allocation + * required should be done by this function. + * post: element in inserted in the first unused position in the array + * array size is incresaed by a->block_size if there is + * insufficient room in the array to add the element. + * Nothing is done if e is NULL + * return: a on success + * NULL if a is NULL or an error occurs + **********************************************************************/ + +dynamic_array_t *dynamic_array_add_element( + dynamic_array_t *a, + const void *e, + void (*destroy_element)(void *s), + void *(*duplicate_element)(const void *s) +){ + extern int errno; + + if(a==NULL) return(NULL); + if(e==NULL) return(a); + if(a->count==a->allocated_size){ + a->allocated_size+=a->block_size; + if( + (a->vector=(void**)realloc(a->vector,a->allocated_size*sizeof(void*))) + ==NULL + ){ + dynamic_array_destroy(a, destroy_element); + return(NULL); + } + } + if((*(a->vector+a->count)=(void *)duplicate_element(e))==NULL){ + return(NULL); + } + a->count++; + + return(a); +} + + +/********************************************************************** + * dynamic_array_display + * Print the contents of a dynamic array to a string + * pre: a: dynamic array to display + * delimiter: character to place between elements of the array + * display_element: pointer to a function to display an element + * element_length: pointer to a function to return the + * length of an element + * post: If a is NULL or there are no elements in a then nothing is done + * Else a character buffer is alocated and the contents + * of each array element, separated by delimiter is placed + * in the '\0' termintated buffer returned. It is up to the + * user to free this buffer. + * return: Allocated buffer as above + * NULL on error, NULL a or empty a + **********************************************************************/ + +char *dynamic_array_display( + dynamic_array_t *a, + char delimiter, + void (*display_element)(char *, void *), + size_t (*element_length)(void *) +){ + void **a_current; + void **a_top; + char *buffer; + char *buffer_current; + size_t nochar; + size_t len=0; + + if(a==NULL || a->count==0){ return(NULL); } + a_top=a->vector+a->count; + nochar=a->count; + for(a_current=a->vector;a_current<a_top;a_current++){ + nochar+=(len=element_length(*a_current)); + if(!len){ + nochar--; + } + } + if((buffer=(char*)malloc(nochar))==NULL){ + return(NULL); + } + buffer_current=buffer; + for(a_current=a->vector;a_current<a_top;a_current++){ + if((len=element_length(*a_current))){ + display_element(buffer_current, *a_current); + buffer_current+=element_length(*a_current); + *buffer_current++=delimiter; + } + } + if(len){ + buffer_current--; + } + *buffer_current='\0'; + return(buffer); +} + + +/********************************************************************** + * dynamic_array_get_element + * Get an element from an array + * pre: a: array to retrieve element from + * elementno: index element in array to retrieve + * post: no change is made to a + * return: element requested + * NULL if element is beyond the number of elements in the arary + **********************************************************************/ + +void *dynamic_array_get_element(dynamic_array_t *a, size_t elementno){ + if(elementno>a->count) return(NULL); + return(*((a->vector)+elementno)); +} + + +/********************************************************************** + * dynamic_array_get_count + * Get the number of elements in the array + * pre: array to find the number of elements in + * return: number of elements in the array + * -1 if a is NULL + **********************************************************************/ + +size_t dynamic_array_get_count(dynamic_array_t *a){ + if(a==NULL) return(-1); + return(a->count); +} + + +/********************************************************************** + * dynamic_array_get_vector + * Get the array contained in the dynamic array + * pre: array to find the vector of + * return: vector + * NULL if a is NULL + **********************************************************************/ + +void *dynamic_array_get_vector(dynamic_array_t *a){ + if(a==NULL) return(NULL); + return(a->vector); +} + + +/********************************************************************** + * dynamic_array_split_str + * Split a string into substrings on a delimiter + * pre: str: string to split + * delimiter: character to split string on + * post: string is split. + * Note: The string is modified. + * return: dynamic array containing sub_strings + * NULL on error + * string being NULL is an error state + **********************************************************************/ + +dynamic_array_t *dynamic_array_split_str(char *string, const char delimiter){ + dynamic_array_t *a; + char *sub_string; + + if(string==NULL){ return(NULL); } + if((a=dynamic_array_create(0))==NULL){ + return(NULL); + } + while((sub_string=strchr(string, delimiter))!=NULL){ + *sub_string='\0'; + if(dynamic_array_add_element(a, string, DESTROY_STR, DUP_STR)==NULL){ + return(NULL); + } + string=sub_string+1; + } + if( + *string!='\0' && + dynamic_array_add_element(a, string, DESTROY_STR, DUP_STR)==NULL + ){ + return(NULL); + } + return(a); +} + diff --git a/dynamic_array.h b/dynamic_array.h new file mode 100644 index 0000000..f1344d7 --- /dev/null +++ b/dynamic_array.h @@ -0,0 +1,177 @@ +/* + * Dynamic array, to store all your flims in Includes macros required + * to create an array of strings but as the primitive type for the + * array is void * providing your own duplicate_primitive and + * destroy_primitive functions will allow you to use the dynamic_array + * API to have a dynamic array containing any primitive + * + * Authors: Horms <horms@vergenet.net> + * + * Released under the terms of the GNU GPL + * + */ + +#ifndef DYNAMIC_ARRAY_FLIM +#define DYNAMIC_ARRAY_FLIM + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + + +/* + * Default blocking size for dynamic array + * can be overriden when array is created + */ +#define DEFAULT_DYNAMIC_ARRAY_BLOCK_SIZE (size_t)7 + + +/* #defines to destroy and dupilcate strings */ +#define DESTROY_STR (void (*)(void *s))free +#define DUP_STR (void *(*)(const void *s))strdup +#define DISPLAY_STR (void (*)(char *d, void *s))strcpy +#define LEN_STR (size_t (*)(void *s))strlen + + +typedef struct { + void **vector; + size_t count; + size_t allocated_size; + size_t block_size; +} dynamic_array_t; + + +/********************************************************************** + * dynamic_array_create + * Create a dynamic array + * pre: block_size: blocking size to use. + * DEFAULT_DYNAMIC_ARRAY_BLOCK_SIZE is used if + * block_size is 0 + * Block size refers to how many elements are prealocated + * each time the array is grown. + * return: An empty dynamic array + * NULL on error + **********************************************************************/ + +dynamic_array_t *dynamic_array_create(size_t block_size); + + +/********************************************************************** + * dynamic_array_destroy + * Free an array an all the elements held within + * pre: a: array to destroy + * destroy_element: pointer to funtion to destroy array elements + * Function should take an argument of a pointer + * and free the memory allocated to the structure + * pointed to. + * post: array is freed and destroy_element is called for all elements + * of the array. + * Nothing if a is NULL + **********************************************************************/ + +void dynamic_array_destroy(dynamic_array_t *a, void (*destroy_element)(void *)); + + +/********************************************************************** + * dynamic_array_add_element + * Add an element to a dynamic array + * pre: a: dynamic array to add element to + * e: element to add + * destroy_element: pointer to a function to destroy an element + * passed to dynamic_array_destroy on error + * duplicate_element: pointer to a function to duplicate an + * element should take a pointer to an element + * to duplicate as the only element and return + * a copy of the element Any memory allocation + * required should be done by this function. + * post: element in inserted in the first unused position in the array + * array size is incresaed by a->block_size if there is + * insufficient room in the array to add the element. + * Nothing is done if e is NULL + * return: a on success + * NULL if a is NULL or an error occurs + **********************************************************************/ + +dynamic_array_t *dynamic_array_add_element( + dynamic_array_t *a, + const void *e, + void (*destroy_element)(void *s), + void *(*duplicate_element)(const void *s) +); + + +/********************************************************************** + * dynamic_array_display + * Print the contents of a dynamic array to a string + * pre: a: dynamic array to display + * delimiter: character to place between elements of the array + * display_element: pointer to a function to display an element + * element_length: pointer to a function to return the + * length of an element + * post: If a is NULL or there are no elements in a then nothing is done + * Else a character buffer is alocated and the contents + * of each array element, separated by delimiter is placed + * in the '\0' termintated buffer returned. It is up to the + * user to free this buffer. + * return: Allocated buffer as above + * NULL on error, NULL a or empty a + **********************************************************************/ + +char *dynamic_array_display( + dynamic_array_t *a, + char delimiter, + void (*display_element)(char *, void *), + size_t (*element_length)(void *) +); + + +/********************************************************************** + * dynamic_array_get_element + * Get an element from an array + * pre: a: array to retrieve element from + * elementno: index element in array to retrieve + * post: no change is made to a + * return: element requested + * NULL if element is beyond the number of elements in the arary + **********************************************************************/ + +void *dynamic_array_get_element(dynamic_array_t *a, size_t elementno); + + +/********************************************************************** + * dynamic_array_get_count + * Get the number of elements in the array + * pre: array to find the number of elements in + * return: number of elements in the array + * -1 if a is NULL + **********************************************************************/ + +size_t dynamic_array_get_count(dynamic_array_t *a); + +/********************************************************************** + * dynamic_array_get_vector + * Get the array contained in the dynamic array + * pre: array to find the vector of + * return: vector + * NULL if a is NULL + **********************************************************************/ + +void *dynamic_array_get_vector(dynamic_array_t *a); + + +/********************************************************************** + * dynamic_array_split_str + * Split a string into substrings on a delimiter + * pre: str: string to split + * delimiter: character to split string on + * post: string is split. + * Note: The string is modified. + * return: dynamic array containing sub_strings + * NULL on error + * string being NULL is an error state + **********************************************************************/ + +dynamic_array_t *dynamic_array_split_str(char *string, const char delimiter); + +#endif diff --git a/ipvsadm-restore b/ipvsadm-restore new file mode 100644 index 0000000..9abf406 --- /dev/null +++ b/ipvsadm-restore @@ -0,0 +1,27 @@ +#!/bin/bash +# ipvsadm-save - Save IPVS rules +# +# A very simple wrapper to save IPVS rules +# Inspired by ipchains-save. +# +# Version: $Id$ +# +# Script Author: Horms <horms@vergenet.net> +# +# This file: +# +# ChangeLog +# +# + +# The path is fixed, this should probably be generated at +# compile time but since the Makefile isn't autogenereated +# having static values is ok. /sbin is the default location +# for ipvsadm, /usr/sbin is where some distributions like +# to put it + +PATH=/sbin:/usr/sbin + +# All the work is actually done in ipvsadm, horay + +ipvsadm -R diff --git a/ipvsadm-save b/ipvsadm-save new file mode 100644 index 0000000..6663852 --- /dev/null +++ b/ipvsadm-save @@ -0,0 +1,27 @@ +#!/bin/bash +# ipvsadm-save - Save IPVS rules +# +# A very simple wrapper to save IPVS rules +# Inspired by ipchains-save. +# +# Version: $Id$ +# +# Script Author: Horms <horms@vergenet.net> +# +# This file: +# +# ChangeLog +# +# + +# The path is fixed, this should probably be generated at +# compile time but since the Makefile isn't autogenereated +# having static values is ok. /sbin is the default location +# for ipvsadm, /usr/sbin is where some distributions like +# to put it + +PATH=/sbin:/usr/sbin + +# All the work is actually done in ipvsadm, horay + +ipvsadm -S diff --git a/ipvsadm.8 b/ipvsadm.8 new file mode 100644 index 0000000..42368ee --- /dev/null +++ b/ipvsadm.8 @@ -0,0 +1,412 @@ +.\" +.\" ipvsadm(8) manual page +.\" +.\" $Id$ +.\" +.\" Authors: Mike Wangsmo <wanger@redhat.com> +.\" Wensong Zhang <wensong@iinchina.net> +.\" Horms <horms@valinux.com> +.\" +.\" Changes: +.\" Horms : Updated to reflect recent change of ipvsadm +.\" : Style guidance taken from ipchains(8) +.\" where appropriate. +.\" Wensong Zhang : Added a short note about the defense strategies +.\" Horms : Tidy up some of the description and the +.\" grammar in the -f and sysctl sections +.\" Horms : Fixed minor grammatical and technical errors. +.\" Added description of usefulness of fwmark services +.\" Added note on using persistence and +.\" ip_masq_ftp in conjunction with FTP. +.\" Added example for fwmark services +.\" Wensong Zhang : Added description about the lblc scheduler +.\" +.\" 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. +.\" +.\" +.TH IPVSADM 8 "20th October 2000" "LVS Administration" " Linux Administrator's Guide" +.UC 4 +.SH NAME +ipvsadm \- Linux Virtual Server administration +.SH SYNOPSIS +.B ipvsadm -[A|E] -[t|u|f] \fIservice-address\fP [-s \fIscheduler\fP] +.ti 15 +.B [-p [\fItimeout\fP]] [-M \fInetmask\fP] +.br +.B ipvsadm -D -[t|u|f] \fIservice-address\fP +.br +.B ipvsadm -C +.br +.B ipvsadm -R +.br +.B ipvsadm -S [-n] +.br +.B ipvsadm -[a|e] -[t|u|f] \fIservice-address\fP +.ti 15 +.B -[r|R] \fIserver-address\fP [-g|-i|-m] [-w \fIweight\fP] +.br +.B ipvsadm -d -[t|u|f] \fIservice-address\fP -[r|R] \fIserver-address\fP +.br +.B ipvsadm -[L|l] [-n] +.br +.B ipvsadm -h +.SH DESCRIPTION +\fBIpvsadm\fR(8) is used to set up, maintain or inspect the virtual +server table in the Linux kernel. The Linux Virtual Server can be used +to build scalable network services based on a cluster of two or more +nodes. The active node of the cluster redirects service requests to a +collection of server hosts that will actually perform the +services. Supported features include two protocols (TCP and UDP), +three packet-forwarding methods (NAT, tunnelling, and direct routing), +and four load balancing algorithms (round robin, weighted round robin, +least-connection and weighted least-connection). +.PP +The command has two basic formats for execution: +.TP +.B ipvsadm \fICOMMAND\fP [\fIprotocol\fP] \fIservice-address\fP +.ti 15 +.B [\fIscheduling-method\fP] [\fIpersistence options\fP] +.TP +.B ipvsadm \fIcommand\fP [\fIprotocol\fP] \fIservice-address\fP +.ti 15 +.B \fIserver-address\fP [\fIpacket-forwarding-method\fP] +.ti 15 +.B [\fIweight options\fP] +.PP +The first format manipulates a virtual service and the algorithm for +assigning service requests to real servers. Optionally, a +persistent timeout and netmask mask for the granularity of a persistent +service may be specified. The second format manipulates a real server +that is associated with an existing virtual service. When specifying +a real server, the packet-forwarding method and the weight of the real +server, relative to other real servers for the virtual service, may be +specified, otherwise defaults will be used. +.SS COMMANDS +\fBipvsadm\fR(8) recognises the commands described below. Upper-case +commands maintain virtual services. Lower-case commands maintain real +servers that are associated with a virtual service. +.TP +\fB-A, --add-service\fR +Add a virtual service. A service address is uniquely defined by a +triplet: IP address, port number, and protocol. Alternatively, a +virtual service may be defined by a firewall-mark. +.TP +\fB-E, --edit-service\fR +Edit a virtual service. +.TP +\fB-D, --delete-service\fR +Delete a virtual service, along with any associated real servers. +.TP +\fB-C, --clear\fR +Clear the virtual server table. +.TP +\fB-R, --restore\fR +Restore Linux Virtual Server rules from stdin. Each line read from stdin +will be treated as the command line options to a separate invocation of +\fIipvsadm\fP. Lines read from stdin can optionally begin with "ipvsadm". +This option is useful to avoid executing a large number or \fIipvsadm\fP +commands when constructing an extensive routing table. +.sp +This option only works if \fIipvsadm\fP is compiled against \fBpopt\fR(3). +.TP +\fB-S, --save\fR +Dump the Linux Virtual Server rules to stdout in a format that can be read +by -R|--restore. +.sp +This option only works if \fIipvsadm\fP is compiled against \fBpopt\fR(3). +.TP +\fB-a, --add-server\fR +Add a real server to a virtual service. +.TP +\fB-d, --delete-server\fR +Remove a real server from a virtual service. +.TP +\fB-L, -l, --list\fR +Display the virtual server table. +.TP +\fB-h, --help\fR +Display a description of the command syntax. +.SS PARAMETERS +The commands above accept or require zero or more of the following +parameters. +.TP +.B -t, --tcp-service \fIservice-address\fP +Use TCP service. The \fIservice-address\fP is of the form +\fIhost[:port]\fP. \fIHost\fP may be either an IP address or a +hostname. \fIPort\fP may be either a port number or the service name +of port. The \fIPort\fP may be omitted, in which case zero will be +used. A \fIPort\fP of zero is only valid if the service is persistent +as per the -p|--persistent option, in which case it is a wild-card +port, that is connections will be accepted to any port. +.TP +.B -u, --udp-service \fIservice-address\fP +Use UDP service. See the -t|--tcp-service for the description of +the \fIservice-address\fP. +.TP +.B -f, --fwmark-service \fIinteger\fP +Use a firewall-mark, an integer value greater than zero, to denote a +virtual service instead of an address, port and protocol (UDP or +TCP). The marking of packets with a firewall-mark is configured using +the -m|--mark option to \fBipchains\fR(8). It can be used to build a +virtual service associated with the same real servers, covering +multiple IP addresses, port and protocol triplets. +.sp +Using firewall-mark virtual services provides a convenient method of +grouping together different IP addresses, ports and protocols into a +single virtual service. This is useful for both simplifying +configuration if a large number of virtual services are required and +grouping persistence across what would otherwise be multiple virtual +services. +.TP +.B -s, --scheduler \fIscheduling-method\fP +\fIscheduling-method\fP Algorithm for allocating TCP connections and +UDP datagrams to real servers. Scheduling algorithms are implemented +as kernel modules. Five are shipped with the Linux Virtual Server: +.sp +\fBrr\fR - Robin Robin: distribute jobs equally amongst the +available real servers. +.sp +\fBwrr\fR - Weighted Round Robin: assign jobs to real servers +proportionally to there real servers' weight. Servers with higher +weights receive new jobs first and get more jobs than servers with +lower weights. Servers with equal weights get an equal distribution +of new jobs. +.sp +\fBlc\fR - Least-Connection: assign more jobs to real servers with +fewer active jobs. +.sp +\fBwlc\fR - Weighted Least-Connection: assign more jobs to servers +with fewer jobs and relative to the real servers' weight. This is the +default. +.sp +\fBlblc\fR - Locality-Based Least-Connection: assign jobs destined for +the same IP address to the same server if the server is not overloaded +and available; otherwise assign jobs to servers with fewer jobs, and +keep it for future assignment. +.TP +.B -p, --persistent [\fItimeout\fP] +Specify that a virtual service is persistent. If this option is +specified, multiple requests from a client are redirected to the same +real server selected for the first request. Optionally, the +\fItimeout\fP of persistent sessions may be specified given in +seconds, otherwise the default of 300 seconds will be used. This +option may be used in conjunction with protocols such as FTP +where it is important that clients consistently connect with the same +real server. +.sp +\fBNote:\fR If a virtual service is to handle FTP connections then +persistence must be set for the virtual service if Direct Routing or +NAT is used as the forwarding mechanism. If masquerading is used in +conjunction with an FTP service than persistence is not necessary, but +the ip_masq_ftp kernel module must be used. This module may be +manually inserted into the kernel using insmod(8). +.TP +.B -M, --netmask \fInetmask\fP +Specify the granularity with which clients are grouped for persistent +virtual services. The source address of the request is masked with +this netmask to direct all clients from a network to the same real +server. The default is \fI255.255.255.255\fP, that is, the persistence +granularity is per client host. Less specific netmasks may be used to +resolve problems with non-persistent cache clusters on the client side. +.TP +.B -r, -R, --real-server \fIserver-address\fP +Real server that a request for service may be assigned. The +\fIserver-address\fP is of the form \fIhost[:port]\fP. \fIHost\fP is +the address of a real server and may be ither an IP address or a +hostname. \fIPort\fP can be either a port number or the service name +of port. In the case of the masquerading method, the host address is +usually an RFC 1918 private IP address, and the port can be different +from that of the associated service. With the tunnelling and direct +routing methods, \fIport\fP must be equal to that of the service +address. For normal services, the port specified in the service +address will be used if \fIport\fP is not specified. For fwmark +services, \fIport\fP may be , in which case the destination port on +the real server will be the destination port of the request sent to +the virtual service. +.TP +.B [packet-forwarding-method] +.sp +\fB-g, --gatewaying\fR Use gatewaying (direct routing). This is the default. +.sp +\fB-i, --ipip\fR Use ipip encapsulation (tunnelling). +.sp +\fB-m, --masquerading\fR Use masquerading (network access translation, or NAT). +.sp +\fBNote:\fR Regardless of the packet-forwarding mechanism specified, +real servers for addresses for which there are interfaces on the local +node will be use the local forwarding method. This cannot be specified +by \fIipvsadm\fP, rather it set by the kernel as real servers are +added or modified. +.TP +.B -w, --weight \fIweight\fP +\fIWeight\fP is an integer specifying the capacity of a server +relative to the others in the pool. The valid values of \fIweight\fP +are 0 through to 65535. The default is 1. Quiescent servers are +specified with a weight of zero. A quiescent server will receive no +new jobs but still serve the existing jobs, for all scheduling +algorithms distributed with the Linux Virtual Server. Setting a +quiescent server may be useful if the server is overloaded or needs +to be taken out of service for maintenance. +.TP +.B -n, --numeric +Numeric output. IP addresses and port numbers will be printed in +numeric format rather than as as host names and services respectively, +which is the default. +.SH EXAMPLE 1 - Simple Virtual Service +The following commands configure a Linux Director to distribute incoming +requests addressed to port 80 on 207.175.44.110 equally to port 80 on +five real servers. The forwarding method used in this example +is NAT, with each of the real servers being masqueraded by the Linux +Director. +.PP +.nf +ipvsadm -A -t 207.175.44.110:80 -s rr +ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.1:80 -m +ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.2:80 -m +ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.3:80 -m +ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.4:80 -m +ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.5:80 -m +.fi +.PP +Alternatively, this could be achieved in a single ipvsadm command. +.PP +.nf +echo " +-A -t 207.175.44.110:80 -s rr +-a -t 207.175.44.110:80 -r 192.168.10.1:80 -m +-a -t 207.175.44.110:80 -r 192.168.10.2:80 -m +-a -t 207.175.44.110:80 -r 192.168.10.3:80 -m +-a -t 207.175.44.110:80 -r 192.168.10.4:80 -m +-a -t 207.175.44.110:80 -r 192.168.10.5:80 -m +" | ipvsadm -R +.fi +r +.PP +As masquerading is used as the forwarding mechanism in this example, the +default route of the real servers must be set to the linux director, which +will need to be configured to forward and masquerade packets. This can be +achieved using the following commands: +.PP +.nf +echo "1" > /proc/sys/net/ipv4/ip_forward +ipchains -A forward -j MASQ -s 192.168.10.0/24 -d 0.0.0.0/0 +.fi +.SH EXAMPLE 2 - Firewall-Mark Virtual Service +The following commands configure a Linux Director to distribute incoming +requests addressed to any port on 207.175.44.110 or 207.175.44.111 equally +to the corresponding port on five real servers. As per the previous +example, the forwarding method used in this example is NAT, with each of +the real servers being masqueraded by the Linux Director. +.PP +.nf +ipvsadm -A -f 1 -s rr +ipvsadm -a -t 1 -r 192.168.10.1:0 -m +ipvsadm -a -t 1 -r 192.168.10.2:0 -m +ipvsadm -a -t 1 -r 192.168.10.3:0 -m +ipvsadm -a -t 1 -r 192.168.10.4:0 -m +ipvsadm -a -t 1 -r 192.168.10.5:0 -m +.fi +.PP +As masquerading is used as the forwarding mechanism in this example, +the default route of the real servers must be set to the linux +director, which will need to be configured to forward and masquerade +packets. The real server should also be configured to mark incoming +packets addressed to any port on 207.175.44.110 and 207.175.44.111 +with firewall-mark 1. If FTP traffic is to be handled by this virtual +service, then the ip_masq_ftp kernel module needs to be inserted into +the kernel. These operations can be achieved using the following +commands: +.PP +.nf +echo "1" > /proc/sys/net/ipv4/ip_forward +ipchains -A forward -j MASQ -s 192.168.10.0/24 -d 0.0.0.0/0 +ipchains -A input -j ACCEPT -s 0.0.0.0/0 -d 207.175.44.110/31 -m 1 +modprobe ip_masq_ftp +.fi +.SH NOTES +The Linux Virtual Server implements three defense strategies against +some types of denial of service (DoS) attacks. The Linux Director +creates an entry for each connection in order to keep its state, and +each entry occupies 128 bytes effective memory. LVS's vulnerability to +a DoS attack lies in the potential to increase the number entries as +much as possible until the linux director runs out of memory. The +three defense strategies against the attack are: Randomly drop some +entries in the table. Drop 1/rate packets before forwarding them. And +use secure tcp state transition table and short timeouts. The +strategies are controlled by sysctl variables and corresponding +entries in the /proc filesystem: +.sp +/proc/sys/net/ipv4/vs/drop_entry +/proc/sys/net/ipv4/vs/drop_packet +/proc/sys/net/ipv4/vs/secure_tcp +.PP +Valid values for each variable are 0 through to 3. The default value +is 0, which disables the respective defense strategy. 1 and 2 are +automatic modes - when there is no enough available memory, the +respective strategy will be enabled and the variable is automatically +set to 2, otherwise the strategy is disabled and the variable is set +to 1. A value of 3 denotes that the respective strategy is always +enabled. The available memory threshold and secure TCP timeouts can +be tuned using the sysctl variables and corresponding entries in the +/proc filesystem: +.sp +/proc/sys/net/ipv4/vs/amemthresh +/proc/sys/net/ipv4/vs/timeout_* +.SH FILES +.I /proc/net/ip_masq/vs +.br +.I /proc/sys/net/ipv4/vs/am_droprate +.br +.I /proc/sys/net/ipv4/vs/amemthresh +.br +.I /proc/sys/net/ipv4/vs/drop_entry +.br +.I /proc/sys/net/ipv4/vs/drop_packet +.br +.I /proc/sys/net/ipv4/vs/secure_tcp +.br +.I /proc/sys/net/ipv4/vs/timeout_close +.br +.I /proc/sys/net/ipv4/vs/timeout_closewait +.br +.I /proc/sys/net/ipv4/vs/timeout_established +.br +.I /proc/sys/net/ipv4/vs/timeout_finwait +.br +.I /proc/sys/net/ipv4/vs/timeout_icmp +.br +.I /proc/sys/net/ipv4/vs/timeout_lastack +.br +.I /proc/sys/net/ipv4/vs/timeout_listen +.br +.I /proc/sys/net/ipv4/vs/timeout_synack +.br +.I /proc/sys/net/ipv4/vs/timeout_synrecv +.br +.I /proc/sys/net/ipv4/vs/timeout_synsent +.br +.I /proc/sys/net/ipv4/vs/timeout_timewait +.br +.I /proc/sys/net/ipv4/vs/timeout_udp +.SH SEE ALSO +\fBpopt\fP(3), \fBipchains\fP(8), \fBinsmod\fP(8) +.SH AUTHORS +.nf +ipvsadm - Wensong Zhang <wensong@gnuchina.org> + Peter Kese <peter.kese@ijs.si> +man page - Mike Wangsmo <wanger@redhat.com> + Wensong Zhang <wensong@gnuchina.org> + Horms <horms@valinux.com> +.fi diff --git a/ipvsadm.c b/ipvsadm.c new file mode 100644 index 0000000..5ec4794 --- /dev/null +++ b/ipvsadm.c @@ -0,0 +1,1464 @@ +/* + * ipvsadm - IP Virtual Server ADMinistration program + * + * Version: $Id$ + * + * Authors: Wensong Zhang <wensong@iinchina.net> + * Peter Kese <peter.kese@ijs.si> + * + * This program is based on ippfvsadm. + * + * Changes: + * Wensong Zhang : added the editting service & destination support + * Wensong Zhang : added the feature to specify persistent port + * Jacob Rief : found the bug that masquerading dest of + * different vport and dport cannot be deleted. + * Wensong Zhang : fixed it and changed some cosmetic things + * Wensong Zhang : added the timeout setting for persistent service + * Wensong Zhang : added specifying the dest weight zero + * Wensong Zhang : fixed the -E and -e options + * Wensong Zhang : added the long options + * Wensong Zhang : added the hostname and portname input + * Wensong Zhang : added the hostname and portname output + * Lars Marowsky-Brée : added persistence granularity support + * Julian Anastasov : fixed the (null) print for unknown services + * Wensong Zhang : added the port_to_anyname function + * Horms : added option to read commands from stdin + * Horms : modified usage function so it prints to + * : stdout if an exit value of 0 is used and + * : stdout otherwise. Program is then terminated + * : with the supplied exit value. + * Horms : updated manpage and usage funtion so + * : the reflect the options available + * Wensong Zhang : added option to write rules to stdout + * Horms : added ability to specify a fwmark + * : instead of a server and port for + * : a virtual service + * Horms : tightened up checking of services + * : in parse_service + * Horms : ensure that a -r is passed when needed + * Wensong Zhang : fixed the output of fwmark rules + * Horms : added kernel version verification + * Horms : Specifying command and option options + * (e.g. -Ln or -At) in one short option + * with popt problem fixed. + * Wensong Zhang : split the process_options and make + * two versions of parse_options. + * Horms : attempting to save or restore when + * : compiled against getopt_long now results + * in an informative error message rather + * than the usage information + * + * + * ippfvsadm - Port Fowarding & Virtual Server ADMinistration program + * + * Copyright (c) 1998 Wensong Zhang + * All rights reserved. + * + * Author: Wensong Zhang <wensong@iinchina.net> + * + * This ippfvsadm is derived from Steven Clarke's ipportfw program. + * + * portfw - Port Forwarding Table Editing v1.1 + * + * Copyright (c) 1997 Steven Clarke + * All rights reserved. + * + * Author: Steven Clarke <steven@monmouth.demon.co.uk> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/param.h> +#include <arpa/inet.h> + +#include <asm/types.h> /* For __uXX types */ +#include <net/if.h> +#include <netinet/ip_icmp.h> +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <linux/ip_fw.h> /* For IP_FW_MASQ_CTL */ +#include <linux/ip_masq.h> /* For specific masq defs */ +#include <net/ip_masq.h> +#include <net/ip_vs.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_POPT +#include "popt.h" +#include "config_stream.h" + +#define IPVS_OPTION_PROCESSING "popt" +#else +#define IPVS_OPTION_PROCESSING "getopt_long" +#endif + +#define IPVSADM_VERSION_NO "v1.12" +#define IPVSADM_VERSION_DATE "2000/11/02" +#define IPVSADM_VERSION IPVSADM_VERSION_NO " " IPVSADM_VERSION_DATE + +#define MINIMUM_IPVS_VERSION_MAJOR 0 +#define MINIMUM_IPVS_VERSION_MINOR 9 +#define MINIMUM_IPVS_VERSION_PATCH 10 + +#ifndef IPVS_VERSION +#define IPVS_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) +#endif + +/* default scheduler */ +#define DEF_SCHED "wlc" + +/* printing format flags */ +#define FMT_NONE 0x0000 +#define FMT_NUMERIC 0x0001 +#define FMT_RULE 0x0002 + +#define SERVICE_NONE 0x0 +#define SERVICE_ADDR 0x1 +#define SERVICE_PORT 0x2 + +#define VS_PROC_FILE "/proc/net/ip_masq/vs" + +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); +char * addrport_to_anyname(struct in_addr *addr, int port, + unsigned short proto, unsigned int format); + +int parse_service(char *buf, u_int16_t proto, + u_int32_t *addr, u_int16_t *port); +int parse_fwmark(char *buf, u_int32_t *fwmark); +int parse_netmask(char *buf, u_int32_t *addr); +int parse_timeout(char *buf, unsigned *timeout); + +void usage_exit(const char *program, const int exit_status); +void fail(int err, char *text); +void check_ipvs_version(void); +void list_vs(unsigned int options); +void print_vsinfo(char *buf, unsigned int format); +int process_options(int argc, char **argv, int reading_stdin); +int str_is_digit(const char *str); + + +int main(int argc, char **argv) +{ + /* + * If no other arguement, list VS_PROC_FILE + */ + if (argc == 1){ + list_vs(FMT_NONE); + exit(0); + } + + /* + * Warn the user if the IPVS version is out of date + */ + check_ipvs_version(); + + /* + * Process command line arguments + */ + return process_options(argc, argv, 0); +} + + +#ifdef HAVE_POPT + +int parse_options(int argc, char **argv, int reading_stdin, + struct ip_masq_ctl *mc, unsigned int *format) +{ + int c, cmd, parse; + int result=0; + int forward_set=0; + int destination_set=0; + int read_stdin=0; + int write_stdout=0; + dynamic_array_t *a; + poptContext context; + char *optarg=NULL; + + struct poptOption add_service_option = + {"add-service", 'A', POPT_ARG_NONE, NULL, 'A'}; + struct poptOption edit_service_option = + {"edit-service", 'E', POPT_ARG_NONE, NULL, 'E'}; + struct poptOption delete_service_option = + {"delete-service", 'D', POPT_ARG_NONE, NULL, 'D'}; + struct poptOption clear_option = + {"clear", 'C', POPT_ARG_NONE, NULL, 'C'}; + struct poptOption list_option = + {"list", 'L', POPT_ARG_NONE, NULL, 'L'}; + struct poptOption list2_option = + {"list", 'l', POPT_ARG_NONE, NULL, 'l'}; + struct poptOption add_server_option = + {"add-server", 'a', POPT_ARG_NONE, NULL, 'a'}; + struct poptOption edit_server_option = + {"edit-server", 'e', POPT_ARG_NONE, NULL, 'e'}; + struct poptOption delete_server_option = + {"delete-server", 'd', POPT_ARG_NONE, NULL, 'd'}; + struct poptOption help_option = + {"help", 'h', POPT_ARG_NONE, NULL, 'h'}; + struct poptOption read_stdin_option = + {"restore", 'R', POPT_ARG_NONE, NULL, 'R'}; + struct poptOption write_stdout_option = + {"save", 'S', POPT_ARG_NONE, NULL, 'S'}; + struct poptOption tcp_service_option = + {"tcp-service", 't', POPT_ARG_STRING, &optarg, 't'}; + struct poptOption udp_service_option = + {"udp-service", 'u', POPT_ARG_STRING, &optarg, 'u'}; + struct poptOption fwmark_service_option = + {"fwmark-service", 'f', POPT_ARG_STRING, &optarg, 'f'}; + struct poptOption scheduler_option = + {"scheduler", 's', POPT_ARG_STRING, &optarg, 's'}; + struct poptOption persistent_option = + {"persistent", 'p', POPT_ARG_NONE, NULL, 'p'}; + struct poptOption netmask_option = + {"netmask", 'M', POPT_ARG_STRING, &optarg, 'M'}; + struct poptOption real_server_option = + {"real-server", 'r', POPT_ARG_STRING, &optarg, 'r'}; + struct poptOption real_server2_option = + {"real-server", 'R', POPT_ARG_STRING, &optarg, 'R'}; + struct poptOption masquerading_option = + {"masquerading", 'm', POPT_ARG_NONE, NULL, 'm'}; + struct poptOption ipip_option = + {"ipip", 'i', POPT_ARG_NONE, NULL, 'i'}; + struct poptOption gatewaying_option = + {"gatewaying",'g', POPT_ARG_NONE, NULL, 'g'}; + struct poptOption weight_option = + {"weight", 'w', POPT_ARG_STRING, &optarg, 'w'}; + struct poptOption numeric_option = + {"numeric", 'n', POPT_ARG_NONE, NULL, 'n'}; + struct poptOption NULL_option = + {NULL, 0, 0, NULL, 0}; + + struct poptOption *options_sub=NULL; + + struct poptOption options_main[] = + { + add_service_option, + edit_service_option, + delete_service_option, + clear_option, + list_option, + list2_option, + add_server_option, + edit_server_option, + delete_server_option, + help_option, + read_stdin_option, + write_stdout_option, + NULL_option + }; + + struct poptOption options_delete_service[] = + { + tcp_service_option, + udp_service_option, + fwmark_service_option, + NULL_option + }; + + struct poptOption options_service[] = + { + tcp_service_option, + udp_service_option, + fwmark_service_option, + scheduler_option, + persistent_option, + netmask_option, + NULL_option + }; + + struct poptOption options_delete_server[] = + { + tcp_service_option, + udp_service_option, + fwmark_service_option, + weight_option, + real_server_option, + real_server2_option, + NULL_option + }; + + struct poptOption options_server[] = + { + tcp_service_option, + udp_service_option, + fwmark_service_option, + weight_option, + real_server_option, + real_server2_option, + gatewaying_option, + masquerading_option, + ipip_option, + NULL_option + }; + + struct poptOption options_list[] = + { + numeric_option, + NULL_option + }; + + struct poptOption options_NULL[] = + { + NULL_option + }; + + context= poptGetContext("ipvsadm", argc, argv, options_main, 0); + + if ((cmd = poptGetNextOpt(context)) < 0) + usage_exit(argv[0], -1); + + switch (cmd) { + case 'A': + mc->m_cmd = IP_MASQ_CMD_ADD; + options_sub = options_service; + break; + case 'E': + mc->m_cmd = IP_MASQ_CMD_SET; + options_sub = options_service; + break; + case 'D': + mc->m_cmd = IP_MASQ_CMD_DEL; + options_sub = options_delete_service; + break; + case 'a': + mc->m_cmd = IP_MASQ_CMD_ADD_DEST; + options_sub = options_server; + break; + case 'e': + mc->m_cmd = IP_MASQ_CMD_SET_DEST; + options_sub = options_server; + break; + case 'd': + mc->m_cmd = IP_MASQ_CMD_DEL_DEST; + mc->m_target = IP_MASQ_TARGET_VS; + options_sub = options_delete_server; + break; + case 'C': + mc->m_cmd = IP_MASQ_CMD_FLUSH; + options_sub = options_NULL; + break; + case 'L': + case 'l': + mc->m_cmd = IP_MASQ_CMD_LIST; + options_sub = options_list; + break; + case 'R': + read_stdin = 1; + options_sub = options_NULL; + break; + case 'S': + write_stdout = 1; + options_sub = options_list; + break; + case 'h': + usage_exit(argv[0], 0); + break; + default: + usage_exit(argv[0], -1); + } + + poptFreeContext(context); + context = poptGetContext("ipvsadm", argc, argv, options_sub, 0); + + /* + * Mangle the first argument + * The first option from this argument has been read, + * but there may be others + */ + c = strlen(argv[1]); + if (c > 2) { + if(argv[1][1] != '-'){ + /* Suffle first option out of argument */ + memmove(argv[1]+1, argv[1]+2, c-2); + argv[1][c-1]='\0'; + } + } else { + /* Skip argument */ + poptGetNextOpt(context); + } + + while ((c=poptGetNextOpt(context)) >= 0){ + switch (c) { + case 't': + case 'u': + if (mc->u.vs_user.vfwmark != 0) + fail(2, "fwmark already specified"); + if (mc->u.vs_user.protocol != 0) + fail(2, "protocol already specified"); + mc->u.vs_user.protocol = + (c=='t' ? IPPROTO_TCP : IPPROTO_UDP); + parse = parse_service(optarg, + mc->u.vs_user.protocol, + &mc->u.vs_user.vaddr, + &mc->u.vs_user.vport); + if (!(parse & SERVICE_ADDR )) + fail(2, "illegal virtual server " + "address[:port] specified"); + break; + case 'f': + if (mc->u.vs_user.vfwmark != 0) + fail(2, "fwmark already specified"); + if (mc->u.vs_user.protocol != 0) + fail(2, "protocol already specified"); + /* + * Set prtocol to a sane values, even + * though it is not used + */ + mc->u.vs_user.protocol = IPPROTO_TCP; + /* + * Get the fwmark + */ + parse = parse_fwmark(optarg, &mc->u.vs_user.vfwmark); + if (parse == 0 || mc->u.vs_user.vfwmark == 0 ) + fail(2, "illegal virtual server " + "fwmark specified"); + break; + case 's': + if (strlen(mc->m_tname) != 0) + fail(2, "multiple scheduling modules specified"); + strncpy(mc->m_tname, optarg, IP_MASQ_TNAME_MAX); + break; + case 'p': + mc->u.vs_user.vs_flags = IP_VS_SVC_F_PERSISTENT; + break; + case 'M': + parse = parse_netmask(optarg, + &mc->u.vs_user.netmask); + if (parse != 1) + fail(2, "illegal virtual server " + "persistent mask specified"); + break; + case 'r': + case 'R': + if (destination_set) + fail(2, "Destination already set"); + destination_set=1; + parse = parse_service(optarg, + mc->u.vs_user.protocol, + &mc->u.vs_user.daddr, + &mc->u.vs_user.dport); + if (!(parse&SERVICE_ADDR)) + fail(2, "illegal real server " + "address[:port] specified"); + /* copy vport to dport if none specified */ + if (parse == 1) + mc->u.vs_user.dport = mc->u.vs_user.vport; + break; + case 'i': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = IP_MASQ_F_VS_TUNNEL; + break; + case 'g': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = IP_MASQ_F_VS_DROUTE; + break; + case 'm': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = 0; + break; + case 'w': + if (mc->u.vs_user.weight != -1) + fail(2, "multiple server weights specified"); + if ((mc->u.vs_user.weight= + string_to_number(optarg,0,65535)) == -1) + fail(2, "illegal weight specified"); + break; + case 'n': + *format |= FMT_NUMERIC; + break; + default: + fail(2, "invalid option"); + } + } + + if (c < -1) { + /* an error occurred during option processing */ + fprintf(stderr, "%s: %s\n", + poptBadOption(context, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + poptFreeContext(context); + return -1; + } + + if (read_stdin) { + /* avoid infinite loop */ + if (reading_stdin != 0) + usage_exit(argv[0], -1); + + while ((a = config_stream_read(stdin, argv[0])) != NULL) { + int i; + if ((i = (int)dynamic_array_get_count(a)) > 1) + result = process_options + (i, + (char **)dynamic_array_get_vector(a), + 1); + dynamic_array_destroy(a, DESTROY_STR); + } + poptFreeContext(context); + exit(result); + } + + if (write_stdout) { + *format |= FMT_RULE; + list_vs(*format); + poptFreeContext(context); + exit(0); + } + + /* + * If popt is used then optional arguments (persistent timeout) + * has to be handled last. This has the interesting + * side effect that the first non-option argument will + * be used as the timeout, regardless of its position + * in the argument list + */ + if (mc->u.vs_user.vs_flags == IP_VS_SVC_F_PERSISTENT){ + optarg=poptGetArg(context); + parse = parse_timeout(optarg, &mc->u.vs_user.timeout); + if (parse == 0) + fail(2, "illegal timeout for persistent service"); + } + poptFreeContext(context); + return 0; +} + +#else + +int parse_options(int argc, char **argv, int reading_stdin, + struct ip_masq_ctl *mc, unsigned int *format) +{ + int c, cmd, parse; + int forward_set=0; + int destination_set=0; + char *optstr = ""; + + struct option long_options[] = + { + {"add-service", 0, 0, 'A'}, + {"edit-service", 0, 0, 'E'}, + {"delete-service", 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'}, + {"save", 0, 0, 'S'}, + {"restore", 0, 0, 'R'}, + {"tcp-service", 1, 0, 't'}, + {"udp-service", 1, 0, 'u'}, + {"fwmark-service", 1, 0, 'f'}, + {"scheduler", 1, 0, 's'}, + {"persistent", 2, 0, 'p'}, + {"real-server", 1, 0, 'r'}, + {"masquerading", 0, 0, 'm'}, + {"netmask", 1, 0, 'M'}, + {"ipip", 0, 0, 'i'}, + {"gatewaying", 0, 0, 'g'}, + {"weight", 1, 0, 'w'}, + {"numeric", 0, 0, 'n'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + + extern char *optarg; + extern int optind; + extern int opterr; + extern int optopt; + + /* Re-process the arguments each time options is called*/ + optind = 1; + + if ((cmd = getopt_long(argc, argv, "AEDCSRaedlLh", + long_options, NULL)) == EOF) + usage_exit(argv[0], -1); + + switch (cmd) { + case 'A': + mc->m_cmd = IP_MASQ_CMD_ADD; + optstr = "t:u:f:s:M:p::"; + break; + case 'E': + mc->m_cmd = IP_MASQ_CMD_SET; + optstr = "t:u:f:s:M:p::"; + break; + case 'D': + mc->m_cmd = IP_MASQ_CMD_DEL; + optstr = "t:u:f:"; + break; + case 'a': + mc->m_cmd = IP_MASQ_CMD_ADD_DEST; + optstr = "t:u:f:w:r:R:gmi"; + break; + case 'e': + mc->m_cmd = IP_MASQ_CMD_SET_DEST; + optstr = "t:u:f:w:r:R:gmi"; + break; + case 'd': + mc->m_cmd = IP_MASQ_CMD_DEL_DEST; + optstr = "t:u:f:w:r:R:"; + break; + case 'C': + mc->m_cmd = IP_MASQ_CMD_FLUSH; + optstr = ""; + break; + case 'L': + case 'l': + mc->m_cmd = IP_MASQ_CMD_LIST; + optstr = "n"; + break; + case 'h': + usage_exit(argv[0], 0); + break; + case 'S': + fprintf(stderr, + "ipvsadm: Invalid option: -S or --save\n" + " Saving of ipvsadm rules is only supported when\n" + " ipvsadm is compiled against libpopt.\n"); + exit(-1); + break; + case 'R': + fprintf(stderr, + "ipvsadm: Invalid option: -R or --restore\n" + "Restoring ipvsadm rules is only supported when \n" + "ipvsadm is compiled against libpopt.\n"); + exit(-1); + break; + default: + usage_exit(argv[0], -1); + } + + while ((c=getopt_long(argc, argv, optstr, + long_options, NULL)) != EOF) { + switch (c) { + case 't': + case 'u': + if (mc->u.vs_user.vfwmark != 0) + fail(2, "fwmark already specified"); + if (mc->u.vs_user.protocol != 0) + fail(2, "protocol already specified"); + mc->u.vs_user.protocol = + (c=='t' ? IPPROTO_TCP : IPPROTO_UDP); + parse = parse_service(optarg, + mc->u.vs_user.protocol, + &mc->u.vs_user.vaddr, + &mc->u.vs_user.vport); + if (!(parse & SERVICE_ADDR )) + fail(2, "illegal virtual server " + "address[:port] specified"); + break; + case 'f': + if (mc->u.vs_user.vfwmark != 0) + fail(2, "fwmark already specified"); + if (mc->u.vs_user.protocol != 0) + fail(2, "protocol already specified"); + /* + * Set prtocol to a sane values, even + * though it is not used + */ + mc->u.vs_user.protocol = IPPROTO_TCP; + /* + * Get the fwmark + */ + parse = parse_fwmark(optarg, &mc->u.vs_user.vfwmark); + if (parse == 0 || mc->u.vs_user.vfwmark == 0 ) + fail(2, "illegal virtual server " + "fwmark specified"); + break; + case 's': + if (strlen(mc->m_tname) != 0) + fail(2, "multiple scheduling modules specified"); + strncpy(mc->m_tname, optarg, IP_MASQ_TNAME_MAX); + break; + case 'p': + mc->u.vs_user.vs_flags = IP_VS_SVC_F_PERSISTENT; + if (!optarg && optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + optarg = argv[optind++]; + parse = parse_timeout(optarg, + &mc->u.vs_user.timeout); + if (parse == 0) + fail(2, "illegal timeout " + "for persistent service"); + break; + case 'M': + parse = parse_netmask(optarg, + &mc->u.vs_user.netmask); + if (parse != 1) + fail(2, "illegal virtual server " + "persistent mask specified"); + break; + case 'r': + case 'R': + if (destination_set) + fail(2, "Destination already set"); + destination_set=1; + parse = parse_service(optarg, + mc->u.vs_user.protocol, + &mc->u.vs_user.daddr, + &mc->u.vs_user.dport); + if (!(parse&SERVICE_ADDR)) + fail(2, "illegal real server " + "address[:port] specified"); + /* copy vport to dport if none specified */ + if (parse == 1) + mc->u.vs_user.dport = mc->u.vs_user.vport; + break; + case 'i': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = IP_MASQ_F_VS_TUNNEL; + break; + case 'g': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = IP_MASQ_F_VS_DROUTE; + break; + case 'm': + if(forward_set) + fail(2, "multiple forward mechanims set"); + forward_set=1; + mc->u.vs_user.masq_flags = 0; + break; + case 'w': + if (mc->u.vs_user.weight != -1) + fail(2, "multiple server weights specified"); + if ((mc->u.vs_user.weight= + string_to_number(optarg,0,65535)) == -1) + fail(2, "illegal weight specified"); + break; + case 'n': + *format |= FMT_NUMERIC; + break; + default: + fail(2, "invalid option"); + } + } + + if (optind < argc) + fail(2, "unknown arguments found in command line"); + return 0; +} + +#endif /* HAVE_POPT */ + + +int process_options(int argc, char **argv, int reading_stdin) +{ + struct ip_masq_ctl ctl; + int result=0; + int sockfd; + unsigned int format=FMT_NONE; + + memset(&ctl, 0, sizeof(struct ip_masq_ctl)); + ctl.m_target = IP_MASQ_TARGET_VS; + /* weight=0 is allowed, which means that server is quiesced */ + ctl.u.vs_user.weight = -1; + /* Set direct routing as default forwarding method */ + ctl.u.vs_user.masq_flags = IP_MASQ_F_VS_DROUTE; + /* Set the default persistent granularity to /32 masking */ + ctl.u.vs_user.netmask = ((u_int32_t) 0xffffffff); + + if (parse_options(argc, argv, reading_stdin, &ctl, &format)) + return -1; + + if (ctl.m_cmd == IP_MASQ_CMD_LIST) { + list_vs(format); + return 0; + } + + if (ctl.m_cmd == IP_MASQ_CMD_ADD || ctl.m_cmd == IP_MASQ_CMD_SET) { + /* + * Make sure that port zero service is persistent + */ + if (!ctl.u.vs_user.vfwmark && + !ctl.u.vs_user.vport && + (ctl.u.vs_user.vs_flags != IP_VS_SVC_F_PERSISTENT)) + fail(2, "Zero port specified " + "for non-persistent service"); + + /* + * Set the default scheduling algorithm if not specified + */ + if (strlen(ctl.m_tname) == 0) + strcpy(ctl.m_tname,DEF_SCHED); + } + + /* + * Make sure that a destination is specified as required + * i.e. make sure that a -r accompanies a -[t|u|f] + */ + if ((ctl.m_cmd == IP_MASQ_CMD_ADD_DEST + || ctl.m_cmd == IP_MASQ_CMD_SET_DEST + || ctl.m_cmd == IP_MASQ_CMD_DEL_DEST) + && !ctl.u.vs_user.daddr) { + fail(2, "No destination specified"); + } + + if (ctl.m_cmd == IP_MASQ_CMD_ADD_DEST + || ctl.m_cmd == IP_MASQ_CMD_SET_DEST) { + /* + * Set the default weight 1 if not specified + */ + if (ctl.u.vs_user.weight == -1) + ctl.u.vs_user.weight = 1; + + /* + * The destination port must be equal to the service port + * if the IP_MASQ_F_VS_TUNNEL or IP_MASQ_F_VS_DROUTE is set. + * Don't worry about this if fwmark is used. + */ + if (!ctl.u.vs_user.vfwmark && + (ctl.u.vs_user.masq_flags == IP_MASQ_F_VS_TUNNEL + || ctl.u.vs_user.masq_flags == IP_MASQ_F_VS_DROUTE)) + ctl.u.vs_user.dport = ctl.u.vs_user.vport; + } + + sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sockfd==-1) { + perror("socket creation failed"); + exit(1); + } + + result = setsockopt(sockfd, IPPROTO_IP, + IP_FW_MASQ_CTL, (char *)&ctl, sizeof(ctl)); + if (result) { + perror("setsockopt failed"); + + /* + * Print most common error messages + */ + switch (ctl.m_cmd) { + case IP_MASQ_CMD_ADD: + if (errno == EEXIST) + printf("Service already exists\n"); + else if (errno == ENOENT) + printf("Scheduler not found: ip_vs_%s.o\n", + ctl.m_tname); + break; + case IP_MASQ_CMD_SET: + if (errno==ESRCH) + printf("No such service\n"); + else if (errno == ENOENT) + printf("Scheduler not found: ip_vs_%s.o\n", + ctl.m_tname); + break; + case IP_MASQ_CMD_DEL: + if (errno==ESRCH) + printf("No such service\n"); + break; + case IP_MASQ_CMD_ADD_DEST: + if (errno == ESRCH) + printf("Service not defined\n"); + else if (errno == EEXIST) + printf("Destination already exists\n"); + break; + case IP_MASQ_CMD_SET_DEST: + if (errno==ESRCH) + printf("Service not defined\n"); + else if (errno == ENOENT) + printf("No such destination\n"); + break; + case IP_MASQ_CMD_DEL_DEST: + if (errno==ESRCH) + printf("Service not defined\n"); + else if (errno == ENOENT) + printf("No such destination\n"); + break; + } + } + + close(sockfd); + return result; +} + + +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 max <= min then ignore ranges + */ + if (max <= min || ( 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(buf==NULL) + return 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; + + return 1; +} + +/* + * Get IP fwmark from the argument. + * Result is + * 1 on success + * 0 on error + */ +int parse_fwmark(char *buf, u_int32_t *fwmark) +{ + long tmpl; + + if ((tmpl=string_to_number(buf, 0, 0)) == -1) + return 0; + *fwmark=tmpl; + + return 1; +} + + +/* + * Get IP address and port from the argument. + * Result is a logical or of + * SERVICE_NONE: no service elements set/error + * SERVICE_ADDR: addr set + * SERVICE_PORT: port set + */ +int parse_service(char *buf, u_int16_t proto, u_int32_t *addr, u_int16_t *port) +{ + char *portp; + long portn; + int result=SERVICE_NONE; + struct in_addr inaddr; + + if(buf==NULL || str_is_digit(buf)) + return SERVICE_NONE; + + portp = strchr(buf, ':'); + if (portp != NULL) + *portp = '\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 SERVICE_NONE; + + result|=SERVICE_ADDR; + + if (portp != NULL){ + result|=SERVICE_PORT; + + if ((portn=string_to_number(portp+1, 0, 65535)) != -1) + *port = htons(portn); + else if ((portn=service_to_port(portp+1, proto)) != -1) + *port = htons(portn); + else + return SERVICE_NONE; + } + + return result; +} + + +/* + * 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 = IP_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(const char *program, const int exit_status) { + FILE *stream; + + if (exit_status != 0) + stream = stderr; + else + stream = stdout; + + fprintf(stream, + "ipvsadm " IPVSADM_VERSION " (compiled with " + IPVS_OPTION_PROCESSING " and IPVS v%d.%d.%d)\n" + "Usage: %s -[A|E] -[t|u|f] service-address [-s scheduler] [-p [timeout]] [-M netmask]\n" + " %s -D -[t|u|f] service-address\n" + " %s -C\n" +#ifdef HAVE_POPT + " %s -R\n" + " %s -S [-n]\n" +#endif + " %s -[a|e] -[t|u|f] service-address -[r|R] server-address [-g|-i|-m] [-w weight]\n" + " %s -d -[t|u|f] service-address -[r|R] server-address\n" + " %s -[L|l] [-n]\n" + " %s -h\n\n", + NVERSION(IP_VS_VERSION_CODE), +#ifdef HAVE_POPT + program, program, +#endif + program, program, program, program, program, program, program); + + fprintf(stream, + "Commands:\n" + "Either long or short options are allowed.\n" + " --add-service -A add virtual service with options\n" + " --edit-service -E edit virtual service with options\n" + " --delete-service -D delete virtual service\n" + " --clear -C clear the whole table\n" +#ifdef HAVE_POPT + " --restore -R restore rules from stdin\n" + " --save -S save rules to stdout\n" +#endif + " --add-server -a add real server with options\n" + " --edit-server -e edit real server with options\n" + " --delete-server -d delete real server\n" + " --list -L|-l list the table\n" + " --help -h display this help message\n\n" + ); + + fprintf(stream, + "Options:\n" + " --tcp-service -t service-address service-address is host[:port]\n" + " --udp-service -u service-address service-address is host[:port]\n" + " --fwmark-service -f fwmark fwmark is an integer greater than zero\n" + " --scheduler -s <scheduler> one of rr|wrr|lc|wlc,\n" + " the default scheduler is %s.\n" + " --persistent -p [timeout] persistent service\n" + " --netmask -M [netmask] persistent granularity mask\n" + " --real-server -r|-R server-address server-address is host (and port)\n" + " --gatewaying -g gatewaying (direct routing) (default)\n" + " --ipip -i ipip encapsulation (tunneling)\n" + " --masquerading -m masquerading (NAT)\n" + " --weight: -w <weight> capacity of real server\n" + " --numeric -n numeric output of addresses and ports\n", + DEF_SCHED); + + exit(exit_status); +} + + +void fail(int err, char *text) { + printf("%s\n",text); + exit(err); +} + + +void check_ipvs_version(void) +{ + static char buffer[1024]; + int major; + int minor; + int patch; + FILE *handle; + + handle = fopen(VS_PROC_FILE, "r"); + if (!handle) { + fprintf(stderr, "Could not open the %s file\n" + "Are you sure that IP Virtual Server is supported " + "by the kernel?\n", VS_PROC_FILE); + exit(1); + } + + /* + * Read the first line and verify the IPVS version + */ + if (!feof(handle) && fgets(buffer, sizeof(buffer), handle)) { + sscanf(buffer, "%*1024[a-z A-Z] %d.%d.%d", + &major, &minor, &patch); + if (IPVS_VERSION(major,minor,patch) < + IPVS_VERSION(MINIMUM_IPVS_VERSION_MAJOR, + MINIMUM_IPVS_VERSION_MINOR, + MINIMUM_IPVS_VERSION_PATCH)) { + fprintf(stderr, + "Warning: IPVS version missmatch: \n" + " Kernel compiled with IPVS version %d.%d.%d\n" + " ipvsadm " IPVSADM_VERSION_NO + " requires minimum IPVS version %d.%d.%d\n\n", + major, minor, patch, + MINIMUM_IPVS_VERSION_MAJOR, + MINIMUM_IPVS_VERSION_MINOR, + MINIMUM_IPVS_VERSION_PATCH); + } + buffer[strlen(buffer)-1]='\0'; + } + fclose(handle); +} + + +void list_vs(unsigned int format) +{ + static char buffer[1024]; + FILE *handle; + int i; + + handle = fopen(VS_PROC_FILE, "r"); + if (!handle) { + fprintf(stderr, "Could not open the %s file\n" + "Are you sure that IP Virtual Server is supported " + "by the kernel?\n", VS_PROC_FILE); + exit(1); + } + + /* + * Read and print the first three head lines + */ + for (i=0; i<2 && !feof(handle); i++) { + if (fgets(buffer, sizeof(buffer), handle) + && !(format & FMT_RULE)) + printf("%s", buffer); + } + if (fgets(buffer, sizeof(buffer), handle) && !(format & FMT_RULE)) + printf(" -> RemoteAddress:Port " + "Forward Weight ActiveConn InActConn\n"); + + /* + * Print the VS information according to the format + */ + while (!feof(handle)) { + if (fgets(buffer, sizeof(buffer), handle)) + print_vsinfo(buffer, format); + } + + fclose(handle); +} + + +char *get_fwd_switch(char *fwd) +{ + char *swt; + + switch (fwd[0]) { + case 'M': + swt = "-m"; break; + case 'T': + swt = "-i"; break; + default: + swt = "-g"; break; + } + return swt; +} + +void print_vsinfo(char *buf, unsigned int format) +{ + /* virtual service variables */ + char protocol[10]; + static unsigned short proto = 0; + static struct in_addr vaddr; + static unsigned short vport; + static u_int32_t fwmark; + char scheduler[10]; + char flags[40]; + unsigned int timeout; + struct in_addr vmask; + + /* destination variables */ + static char arrow[10]; + struct in_addr daddr; + unsigned short dport; + static char fwd[10]; + int weight; + int activeconns; + int inactconns; + + int n; + unsigned long temp; + unsigned long temp2; + char *vname; + char *dname; + + if (buf[0] == ' ') { + /* destination entry */ + if ((n = sscanf(buf, " %s %lX:%hX %s %d %d %d", + arrow, &temp, &dport, fwd, &weight, + &activeconns, &inactconns)) == -1) + exit(1); + if (n != 7) + fail(2, "unexpected input data"); + + daddr.s_addr = (__u32) htonl(temp); + + if (!(dname=addrport_to_anyname(&daddr, dport, proto, format))) + exit(1); + if (format & FMT_RULE) { + if (fwmark == 0) { + if (!(vname=addrport_to_anyname + (&vaddr, vport, proto, format))) + exit(1); + + printf("-a %s %s -r %s %s -w %d\n", + proto==IPPROTO_TCP?"-t":"-u", + vname, dname, get_fwd_switch(fwd), weight); + free(vname); + } else { + printf("-a -f %d -r %s %s -w %d\n", fwmark, + dname, get_fwd_switch(fwd), weight); + } + } else { + printf(" -> %-27s %-7s %-6d %-10d %-10d\n", + dname , fwd, weight, activeconns, inactconns); + } + free(dname); + } else if (buf[0] == 'F') { + /* fwmark virtual service entry */ + if ((n = sscanf(buf, "%s %X %s %s %d %lX", protocol, + &fwmark, scheduler, flags, + &timeout, &temp2)) == -1) + exit(1); + if (n!=6 && n!=3) + fail(2, "unexpected input data"); + + vmask.s_addr = (__u32) htonl(temp2); + + if (format & FMT_RULE) + printf("-A -f %d -s %s", fwmark, scheduler); + else + printf("%s %d %s", protocol, fwmark, scheduler); + + if (n == 3) + printf("\n"); + else { + + printf(" %s %d", + format&FMT_RULE?"-p":flags, timeout/HZ); + + if (vmask.s_addr == (unsigned long int) 0xffffffff) + printf("\n"); + else + printf(" %s %s\n", + format&FMT_RULE?"-M":"mask", + inet_ntoa(vmask)); + + } + } else { + /* TCP/UDP virtual service entry */ + fwmark=0; /* Reset firewall mark to unused */ + + if ((n = sscanf(buf, "%s %lX:%hX %s %s %d %lX", + protocol, &temp, &vport, scheduler, + flags, &timeout, &temp2)) == -1) + exit(1); + if (n!=7 && n!=4) + fail(2, "unexpected input data"); + + vaddr.s_addr = (__u32) htonl(temp); + vmask.s_addr = (__u32) htonl(temp2); + if (strcmp(protocol, "TCP") == 0) proto = IPPROTO_TCP; + else if (strcmp(protocol, "UDP") == 0) proto = IPPROTO_UDP; + else proto = 0; + + + if (!(vname=addrport_to_anyname(&vaddr, vport, proto, format))) + exit(1); + + if (format & FMT_RULE) + printf("-A %s %s -s %s", proto==IPPROTO_TCP?"-t":"-u", + vname, scheduler); + else + printf("%s %s %s", protocol, vname, scheduler); + + if (n == 4) + printf("\n"); + else { + printf(" %s %d", + format&FMT_RULE?"-p":flags, timeout/HZ); + + if (vmask.s_addr == (unsigned long int) 0xffffffff) + printf("\n"); + else + printf(" %s %s\n", + format&FMT_RULE?"-M":"mask", + inet_ntoa(vmask)); + } + free(vname); + } +} + + +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; + } +} + + +char * addrport_to_anyname(struct in_addr *addr, int port, + unsigned short proto, unsigned int format) +{ + char *buf; + + if (!(buf=malloc(60))) + return NULL; + + if (format & FMT_NUMERIC) { + snprintf(buf, 60, "%s:%u", + inet_ntoa(*addr), port); + } else { + snprintf(buf, 60, "%s:%s", addr_to_anyname(addr), + port_to_anyname(port, proto)); + } + + return buf; +} + + +int str_is_digit(const char *str) +{ + size_t offset; + size_t top; + + top = strlen(str); + for (offset=0; offset<top; offset++) { + if (!isdigit((int)*(str+offset))) { + break; + } + } + + return((offset<top)?0:1); +} diff --git a/ipvsadm.sh b/ipvsadm.sh new file mode 100644 index 0000000..db3147a --- /dev/null +++ b/ipvsadm.sh @@ -0,0 +1,83 @@ +#!/bin/sh +# +# Startup script handle the initialisation of IPVS +# +# chkconfig: - 08 92 +# +# description: Initialise the Linux Virtual Server +# http://www.linuxvirtualserver.org/ +# +# Script Author: Horms <horms@vergenet.net> +# +# Based on init script for ipchains by Joshua Jensen <joshua@redhat.com> +# +# config: /etc/sysconfig/ipvsadm + + +IPVSADM_CONFIG=/etc/sysconfig/ipvsadm + +# Exit silently if there is no configuration file +if [ ! -f $IPVSADM_CONFIG ]; then + exit +fi + +# Use the funtions provided by Red Hat +# This should be made distribution agnostic +. /etc/rc.d/init.d/functions + +# Check for ipvsadm in both /sbin and /usr/sbin +# The default install puts it in /sbin, as it is analogos to commands such +# as route and ipchains that live in /sbin. Some vendors, most notibly +# Red Hat insist on movint it to /usr/sbin +if [ ! -x /sbin/ipvsadm -a ! -x /usr/sbin/ipvsadm ]; then + exit 0 +fi + +case "$1" in + start) + # If we don't clear these first, we might be adding to + # pre-existing rules. + action "Clearing the current IPVS table:" ipvsadm -C + echo -n "Applying IPVS configuration: " + grep -v "^#" $IPVSADM_CONFIG | ipvsadm-restore -p -f && \ + success "Applying IPVS configuration" || \ + failure "Applying IPVS configuration" + echo + touch /var/lock/subsys/ipvsadm + ;; + + stop) + action "Clearing the current IPVS table:" ipvsadm -C + rm -f /var/lock/subsys/ipvsadm + ;; + + restart) + #Start should flush everything + $0 start + ;; + + panic) + # I'm not sure what panic does but in the case of IPVS + # it makes sense just to clear everything + action "Clearing the current IPVS table:" ipvsadm -C + ;; + + status) + ipvsadm -L -n + ;; + + save) + echo -n "Saving IPVS table to $IPVSADM_CONFIG: " + ipvsadm-save > $IPVSADM_CONFIG 2>/dev/null && \ + success "Saving IPVS table to $IPVSADM_CONFIG" || \ + failure "Saving IPVS table to $IPVSADM_CONFIG" + echo + ;; + + *) + echo "Usage: $0 {start|stop|restart|status|panic|save}" + exit 1 +esac + +exit 0 + diff --git a/ipvsadm.spec.in b/ipvsadm.spec.in new file mode 100644 index 0000000..32dfac9 --- /dev/null +++ b/ipvsadm.spec.in @@ -0,0 +1,79 @@ +%define prefix /usr + +Summary: Utility to administer the Linux Virtual Server +Name: ipvsadm +Version: @@VERSION@@ +Release: @@RELEASE@@ +Copyright: GNU General Public Licence +URL: http://www.LinuxVirtualServer.org/ +Packager: Horms <horms@vergenet.net> P.Copeland <copeland@redhat.com> +Group: Applications/System +Source0: http://www.LinuxVirutalServer.org/software/ipvsadm-%{version}.tar.gz +BuildRoot: /var/tmp/%name-%{PACKAGE_VERSION}-root +Docdir: %{prefix}/doc +Provides: %{name}-%{version} +Conflicts: piranha <= 0.4.14 + +%description +ipvsadm is a utility to administer the IP virtual server services +offered by the Linux kernel augmented with the virtual server patch. + + +%prep +%setup -n ipvsadm + + +%build +CFLAGS="${RPM_OPT_FLAGS}" make + + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p ${RPM_BUILD_ROOT}/{sbin,usr/man/man8,etc/rc.d/init.d} +BUILD_ROOT=${RPM_BUILD_ROOT} make install + +#File finding code thanks to Samuel Flory of VA Linux Systems +cd ${RPM_BUILD_ROOT} +# Directories +find . -type d | sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' \ + > ${RPM_BUILD_DIR}/%{name}-%{version}-%{release}.files +# Files +find . -type f | sed "s,^\.\(.*\),%attr (-\,root\,root) \1," \ + >> ${RPM_BUILD_DIR}/%{name}-%{version}-%{release}.files +# Symbolic links +find . -type l | sed 's,^\.,\%attr(-\,root\,root) ,' \ + >> ${RPM_BUILD_DIR}/%{name}-%{version}-%{release}.files + + +%clean +rm -rf $RPM_BUILD_DIR/%{name} +rm -rf $RPM_BUILD_ROOT +rm ${RPM_BUILD_DIR}/%{name}-%{version}-%{release}.files + + +%files -f ../%{name}-%{version}-%{release}.files +%doc README + + +%changelog +* Wed Aug 9 2000 Horms <horms@vergenet.net> +- Removed Obseletes tag as ipvsadm is back in /sbin where it belongs + as it is more or less analogous to both route and ipchains both of + which reside in /sbin. +- Create directory to install init script into. Init scripts won't install + into build directory unless this is done + +* Thu Jul 6 2000 Wensong Zhang <wensong@linuxvirtualserver.org> +- Changed to build rpms on the ipvsadm tar ball directly + +* Wed Jun 21 2000 P.Copeland <copeland@redhat.com> +- fixed silly install permission settings + +* Mon Jun 19 2000 P.Copeland <copeland@redhat.com> +- Added 'dist' and 'rpms' to the Makefile +- Added Obsoletes tag since there were early versions + of ipvsadm-*.rpm that installed in /sbin +- Obsolete tag was a bit vicious re: piranha + +* Mon Apr 10 2000 Horms <horms@vergenet.net> +- created for version 1.9 |