/*
$Id: filter.c,v 1.4 1999/04/30 07:01:11 jimg Exp $
$Log: filter.c,v $
Revision 1.4  1999/04/30 07:01:11  jimg
Added parsing code for the config files. Tested it in test.c
Spec should be:
	[allow|deny]
	 [[host <ipaddr>]
	  [protocol <ipprotoname>]
	  [[service <servname>]|
	   [port <num>]
	  ]
	 ]

Next time:
  need to change filter function to include IP stuff
  make function to search through ip protos.

Revision 1.3  1999/04/29 10:08:27  jimg
*** empty log message ***

Revision 1.2  1999/04/29 05:03:42  jimg
Created the filter_info structure.
Wrote the actual filter function.
	The filter entries must be added in order of their importance.
	They'll allow the user to DENY ALL, etc.
Having trouble with the include files.

Revision 0.1  1999/04/29 03:01:11  jimg
Initial revision

*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mbuf.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>

/* Rule declarations */
#define ALLOW         1
#define DENY          0

/* Type declarations */
#define HOST          10
#define SERVICE       11
#define PROTOCOL      12

/* All_whatever declarations */
#define ANY_HOST      "255.255.255.255"
#define ANY_PROTOCOL  -1
#define ANY_SERVICE   -1

/* filter specification structure */
struct filter_info {
  int                rule;      /* Allow or Deny packet */
  struct in_addr     host;      /* If it's an IP packet */
  int                protocol;  /* What kind of IP packet */
  u_short            service;   /* For TCP */;
  struct filter_info *next;
};

struct filter_info *init_filter(void);
int filter(struct mbuf *m, struct filter_info *f);

struct filter_info *init_filter(FILE *fp) {

  struct filter_info *curr, *top=NULL;
  char buf[10];
  struct if_addr addr;
  int proto;
  u_short service;
    
  /* read in the config file, populating our filter */
  fscanf(fp, "%s", buf);
  while (!(feof(fp))) {

    if(strcmp(buf, "deny") == 0) {
      rule = DENY;
    } else if (strcmp(buf, "allow") == 0) {
      rule = ALLOW;
    } else
      return NULL;

    fscanf(fp, "%s", buf);

    if (strcmp(buf, "host") == 0) {
      fscanf(fp, "%s", buf);
      /* The Host address is now in the buffer */
      if (strcmp(buf, "all") == 0)
	ascii2addr(AF_INET, ANY_HOST, &addr);
      else
	ascii2addr(AF_INET, buf, &addr);

      /* Read in the next token */
      fscanf(fp, "%s", buf);
    }

    /* Reset proto */
    proto = ANY_PROTOCOL;
    if (strcmp(buf, "protocol") == 0) {
      fscanf(fp, "%s", buf);
      /* The type of protocol is in the buffer */
      if (strcmp(buf, "any"))
	proto = ANY_PROTOCOL;
      else
	proto = atoi(buf);

      /* Read in the next token */
      fscanf(fp, "%s", buf);
    }
    
    if (strcmp(buf, "service") == 0) {
      fscanf(fp, "%s\n", buf);
      /* The buffer now has the TCP service in it */
      service = (u_short) atoi(buf);

      /* Read in the next token */
      fscanf(fp, "%s\n", buf);

    } else if (strcmp(buf, "port")) {
      fscanf(fp, "%s\n", buf);
      /* The buffer now has the TCP service in it */
      service = (u_short) atoi(buf);

      /* Read in the next token */
      fscanf(fp, "%s\n", buf);

    }

    /* Now we can assemble the filter entry */

    if (curr == NULL) {
      curr = new_entry();
    } else {
      curr->next = new_entry();
      curr = curr->next;
    }

    if (top == NULL) {
      top = curr;
    }

    curr->rule = rule;
    curr->host.s_addr = addr.s_addr;
    curr->protocol = proto;
    curr->service = service;
  }
  
  return f_list;
}

/* function to handle filtering of IP packets based on protocol,
 * takes the allocated memory buffer structure and a list of structures
 * containing the filtering specifications as parameters
 */
int filter(struct mbuf *m, struct filter_info *fil) {

  int return_val = 0;
  struct filter_info *f = fil;
  struct ip *ip_h;    /* structure to hold IP header */
  struct tcp *tcp_h;  /* structure to hold TCP header */

  while (f != NULL) {

    if (f->type == HOST) {
      /* Make the membuf look like an IP header */
      ip_h = mtod(m, struct ip *);
      
      /* Check the source address against the filter entry */
      if (f->addr.s_addr == ip_h->ip_src.s_addr) {
	/* If it matches, what do we do with it */
	if (f->rule == ALLOW) {
	  return_val = 1;
	} else {
	  return_val = -1;
	}
      }
      
    } else if (f->type == PROTOCOL) {
      if (f->type == SERVICE) {
	/* Make the membuf look like a TCP header */
	tcp_h = mtod(m, struct tcp *);
	
	/* Check the source address against the filter entry */
	if ((f->port == tcp_h->th_dport) || (f->port == ALL_SERVICES)) {
	  /* If it matches, what do we do with it */
	  if (f->rule == ALLOW) {
	    return_val = 1;
	  } else {
	    return_val = -1;
	  }
	}
      }
    }
    /* Look at the next filter entry */
    f = f->next;
  }
    
    return return_val;
}

struct filter_info *new_entry(void) {
  struct filter_info *tmp;

  tmp = (strict filter_info*) malloc(sizeof(struct filter_info));

  tmp->next = NULL;
  return tmp;
}

