/* tuneacct 1.0
 * (C) Ulrich Callmeier 10/1994
 *
 * 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.
 *
 */

/* Derived from Net-3 and route.c, by Fred N. van Kempen, Alan Cox,
 * Linus Torvalds and others
 * Based on ipacct.c, Copyright (C) 1994 Lars Fenneberg 
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/in.h>

struct inet_acct_flags
{
    unsigned long int exclude_network;
    unsigned long int exclude_netmask;
};

/* Inet accounting configuration calls */
#define SIOCGACCTFLAGS	0x8980		/* Get accounting flags		*/
#define SIOCSACCTFLAGS	0x8981		/* Set accounting flags		*/

char pname[255];
int skfd = -1;

void usage(void)
{
  fprintf(stderr,"Usage: %s set <addr> [mask <addr>]\n",pname);
  fprintf(stderr,"       %s show\n",pname);
  exit(1);
}

void error(char *str)
{
  fprintf(stderr,"%s: %s.\n",pname, str);
  exit(1);
}

int is_addr(char *str)
{
  while(isdigit(*str)) str++;
  if (*(str++) != '.') return 0; 
  while(isdigit(*str)) str++;
  if (*(str++) != '.') return 0; 
  while(isdigit(*str)) str++;
  if (*(str++) != '.') return 0; 
  while(isdigit(*str)) str++;
  if (!isdigit(*str) && (*str !=  '\0')) return 0;
  return 1;	
}

/*
 *	Display an IP address in readable format. 
 */
 
char *in_ntoa(unsigned long in)
{
	static char buff[18];
	char *p;

	p = (char *) &in;
	sprintf(buff, "%d.%d.%d.%d",
		(p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
	return(buff);
}

/*
 *	Convert an ASCII string to binary IP. 
 */
 
unsigned long in_aton(char *str)
{
	unsigned long l;
	unsigned int val;
	int i;

	l = 0;
	for (i = 0; i < 4; i++) 
	{
		l <<= 8;
		if (*str != '\0') 
		{
			val = 0;
			while (*str != '\0' && *str != '.') 
			{
				val *= 10;
				val += *str - '0';
				str++;
			}
			l |= val;
			if (*str != '\0') 
				str++;
		}
	}
	return(htonl(l));
}

unsigned long default_mask(unsigned long dst)
{
	dst = ntohl(dst);
	if ((dst & 0xff) != 0) return htonl(0xffffffff);
	if (IN_CLASSA(dst))
		return htonl(IN_CLASSA_NET);
	if (IN_CLASSB(dst))
		return htonl(IN_CLASSB_NET);
	return htonl(IN_CLASSC_NET);
}

struct inet_acct_flags flgs;

int set_flags()
{
	if (ioctl(skfd, SIOCSACCTFLAGS, &flgs) < 0) {
		fprintf(stderr, "SIOSACCTFLAGS: %s\n", strerror(errno));
		return (1);
	}
	return (0);
} 

int get_flags()
{
	
	if (ioctl(skfd, SIOCGACCTFLAGS, &flgs) < 0) {
		fprintf(stderr, "SIOGACCTFLAGS: %s\n", strerror(errno));
		return (1);
	}
	return (0);
} 


int main(int argc, char **argv)
{
    extern int optind; 
    int c, n, nmax, add=0, def;
    unsigned long waddr = 0;
    unsigned long wmask = 0;
    
    strcpy(pname,argv[0]);
    
    if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 
	{
	    perror("socket");
	    return 1;
	}
    
    while ((c = getopt(argc,argv,"h")) > 0) 
	{
	    switch (c)
		{
  		case 'h':
  		default:
		    usage();
		    break; /* never reached */
		}
	}
    
    n = optind;
    nmax = argc-1;

    if(n>nmax)
	{
	    usage();
	}
    
    if (!strcasecmp(argv[n],"set")) 
	{
	    add = 1;
	}
    else if (!strcasecmp(argv[n],"show"))
	{
	    add = 0;
	}
    else error("wrong command (use 'set' or 'show')");
    
    n++;
    if ((n>nmax) && add) 
	error("address missing");
    
    if(add)
	{
	    if (add && !strcasecmp(argv[n],"default"))
		{
		    waddr = htonl(0x00000000);
		    wmask = htonl(0xffffffff);
		    def = 1;
		}
	    else
		{
		    if (!is_addr(argv[n])) error("address has wrong format");
		    waddr = in_aton(argv[n]);
		    def = 0;
		}
	    
	    n++;	
	    while (n<=nmax)
		{
		    if (!def && !strcasecmp(argv[n],"mask")) 
			{
			    n++; if (n>nmax) error("attribute 'mask' requires an argument");
			    if (!is_addr(argv[n])) error("attribute 'mask': address has wrong format");
			    wmask = in_aton(argv[n]);
			}
		    n++;
		}
	    
	    if (wmask==0) wmask = default_mask(waddr);

	    flgs.exclude_network = waddr;
	    flgs.exclude_netmask = wmask;
	    
	    if(set_flags()<0)
		error("error setting flags");
   
	}  
    else
	{
	    char buff1[18],buff2[18];
	    if(get_flags()<0)
		error("error getting flags");
	    
	    strcpy(buff1,in_ntoa(flgs.exclude_network));
	    strcpy(buff2,in_ntoa(flgs.exclude_netmask));

	    printf("Kernel inet accounting exclusion entry:\nnetwork\t\t\tnetmask\n");
	    printf("%-18s\t%-18s\n",buff1,buff2);

	}
    
  return 0;
}
