Codebase list filtergen / HEAD filtergen.c
HEAD

Tree @HEAD (Download .tar.gz)

filtergen.c @HEADraw · history · blame

/* packet filter compiler
 *
 * Copyright (c) 2002 Matthew Kirkwood
 * Copyright (c) 2003,2004 Jamie Wilkinson
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdarg.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#include "filter.h"
#include "ast.h"
#include "resolver.h"

int yyparse(void *);
void yyrestart(FILE *);
extern struct filter *convert(struct ast_s *n, struct filtergen_opts *o);

static FILE *outfile;

void usage(char *prog) {
  fprintf(stderr, "Usage: %s [-chV] [-t backend] [-o output] input\n", prog);
  fprintf(stderr, "       %s [-chV] [-t backend] [-o output] -F policy\n\n",
          prog);
  fprintf(stderr, "Options:\n");

#ifdef HAVE_GETOPT_H
  fprintf(stderr, " --compile/-c              compile only, no generate\n");
#else
  fprintf(stderr, "           -c              compile only, no generate\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(stderr,
          " --no-resolve/-R           don't resolve hostnames or portnames\n");
#else
  fprintf(stderr,
          "              -R           don't resolve hostnames or portnames\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(
      stderr,
      " --target/-t target        generate for target (default: iptables)\n");
#else
  fprintf(
      stderr,
      "          -t target        generate for target (default: iptables)\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(
      stderr,
      " --flush/-F policy         don't process input, generate flush rules\n");
#else
  fprintf(
      stderr,
      "         -F policy         don't process input, generate flush rules\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(stderr, " --output/-o filename      write the generated packet "
                  "filter to filename\n");
#else
  fprintf(stderr, "          -o filename      write the generated packet "
                  "filter to filename\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(stderr, " --help/-h                 show this help\n");
#else
  fprintf(stderr, "        -h                 show this help\n");
#endif

#ifdef HAVE_GETOPT_H
  fprintf(stderr, " --version/-V              show program version\n");
#else
  fprintf(stderr, "           -V              show program version\n");
#endif
}

int oputs(const char *s) {
  int r = 0;
  if (s) {
    r = fputs(s, outfile);
    if (r > 0) {
      fputc('\n', outfile);
      r++;
    }
  }
  return r;
}

int oprintf(const char *fmt, ...) {
  va_list args;
  va_start(args, fmt);
  return vfprintf(outfile, fmt, args);
}

struct filtyp {
  const char *name;
  sa_family_t family;
  filtergen *compiler;
  filter_flush *flusher;
} filter_types[] = {
    {"iptables", AF_INET, fg_iptables, flush_iptables},
    {"ip6tables", AF_INET6, fg_ip6tables, flush_ip6tables},
    {"iptables-restore", AF_INET, fg_iptrestore, flush_iptrestore},
    {"ip6tables-restore", AF_INET6, fg_ip6trestore, flush_ip6trestore},
    {"ipchains", AF_INET, fg_ipchains, flush_ipchains},
    {"ipfilter", AF_INET, fg_ipfilter, NULL},
    {"cisco", AF_INET, fg_cisco, NULL},
    {NULL, 0, NULL, NULL},
};

#ifdef HAVE_GETOPT_H
static struct option long_options[] = {{"help", no_argument, 0, 'h'},
                                       {"compile", no_argument, 0, 'c'},
                                       {"target", required_argument, 0, 't'},
                                       {"output", required_argument, 0, 'o'},
                                       {"no-resolve", no_argument, 0, 'R'},
                                       {"flush", required_argument, 0, 'F'},
                                       {"version", no_argument, 0, 'V'},
                                       {0, 0, 0, 0}};
#define GETOPT(x, y, z) getopt_long(x, y, z, long_options, NULL)
#else
#define GETOPT(x, y, z) getopt(x, y, z)
#endif

int main(int argc, char **argv) {
  struct filter *f;
  int l;
  time_t t;
  char buf[100];
  char *filename = NULL, *ftn = NULL, *ofn = NULL;
  struct filtyp *ft = NULL;
  int flags = 0;
  char *progname;
  int arg;
  enum filtertype flushpol = T_ACCEPT;

  progname = argv[0];

  while ((arg = GETOPT(argc, argv, "hco:t:F:VR")) > 0) {
    switch (arg) {
    case ':':
      usage(progname);
      exit(1);
      break;
    case 'h':
      usage(progname);
      exit(0);
      break;
    case 'c':
      flags |= FF_NOSKEL;
      break;
    case 'o':
      ofn = strdup(optarg);
      break;
    case 'R':
      flags |= FF_NORESOLVE;
      break;
    case 't':
      ftn = strdup(optarg);
      break;
    case 'F':
      flags |= FF_FLUSH;
      if (!strcasecmp(optarg, "accept")) {
        flushpol = T_ACCEPT;
      } else if (!strcasecmp(optarg, "drop")) {
        flushpol = DROP;
      } else if (!strcasecmp(optarg, "reject")) {
        flushpol = T_REJECT;
      } else {
        fprintf(stderr, "%s: flush policy unrecofgnised: %s\n", progname,
                optarg);
        usage(progname);
        exit(1);
      }
      break;
    case 'V':
      printf(PACKAGE " " VERSION "\n");
      exit(0);
      break;
    default:
      break;
    }
  }
  if (!(flags & FF_FLUSH)) {
    if (optind >= argc) {
      usage(progname);
      exit(1);
    } else
      filename = argv[optind++];
  }

  if (ofn) {
    /* XXX - open a different tempfile, and rename on success */
    outfile = fopen(ofn, "w");
    if (!outfile) {
      fprintf(stderr, "%s: can't open output file \"%s\"\n", progname, ofn);
      return 1;
    }
  } else
    outfile = stdout;

  if (!ftn || !*ftn)
    ftn = strdup("iptables");
  for (ft = filter_types; ft->name; ft++)
    if (!strcmp(ftn, ft->name))
      break;
  free(ftn);
  if (!ft->name) {
    fprintf(stderr, "%s: target filter unrecognised: %s\n", progname, ftn);
    usage(progname);
    exit(1);
  }

  /* What to do, then? */
  if (flags & FF_FLUSH) {
    /* Just flush it */
    l = ft->flusher(flushpol);
  } else {
    /* Compile from a file */
    if (filename && !strcmp(filename, "-"))
      filename = NULL;

    if (filter_fopen(filename))
      return 1;

    {
      struct ast_s ast;

      if (yyparse(&ast) == 0) {
        struct filtergen_opts o;
        memset(&o, 0, sizeof o);
        o.family = ft->family;

        if (!(flags & FF_NORESOLVE)) {
          resolve(&ast, &o);
        }

        f = convert(&ast, &o);
        if (!f) {
          fprintf(stderr, "couldn't convert file\n");
          return 1;
        }
      } else {
        fprintf(stderr, "couldn't parse file\n");
        return 1;
      }
    }

    strftime(buf, sizeof(buf) - 1, "%a %b %e %H:%M:%S %Z %Y",
             localtime((time(&t), &t)));
    oprintf("# filter generated from %s via %s backend at %s\n",
            filename ? filename : "standard input", ft->name, buf);
    l = ft->compiler(f, flags);
  }

  if (ofn)
    fclose(outfile);

  if (l < 0) {
    fprintf(
        stderr,
        "an error occurred: most likely the filters defined make no sense\n");
    if (ofn)
      unlink(ofn);
    return 1;
  }
  if (ofn) {
    free(ofn);
  }
  fprintf(stderr, "generated %d rules\n", l);
  return 0;
}