Codebase list filtergen / HEAD gen.c
HEAD

Tree @HEAD (Download .tar.gz)

gen.c @HEADraw · history · blame

/* filter compilation routines
 *
 * Copyright (c) 2002 Matthew Kirkwood
 * Copyright (c) 2003,2004 Jamie Wilkinson <jaq@spacepants.org>
 *
 * 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
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "filter.h"

void applydefaults(struct filterent *e, long flags) {
  if (!e->rtype) {
    if (flags & FF_LOCAL)
      e->rtype = LOCALONLY;
    else if (flags & FF_ROUTE)
      e->rtype = ROUTEDONLY;
  }
}

int checkmatch(const struct filterent *e) {
  int r = 0;
#define MUST(t)                                                                \
  do                                                                           \
    if (!(e->t)) {                                                             \
      fprintf(stderr, "%s missing from filter\n", #t);                         \
      r++;                                                                     \
    }                                                                          \
  while (0)
  if (!e->subgroup)
    MUST(target);
  if (!e->groupname) {
    MUST(direction);
    MUST(iface);
  }
#undef MUST

  if ((e->u.ports.src.minstr || e->u.ports.dst.minstr) &&
      (e->proto.num != IPPROTO_TCP) && (e->proto.num != IPPROTO_UDP)) {
    fprintf(stderr, "can only use ports with tcp or udp\n");
    r++;
  }

  if (e->u.icmp && (e->proto.num != IPPROTO_ICMP)) {
    fprintf(stderr, "icmptype can only be used with icmp\n");
    r++;
  }

  if ((e->rtype == LOCALONLY) && (e->target == MASQ)) {
    fprintf(stderr, "\"local\" and masquerading are incompatible\n");
    r++;
  }

  return r;
}

int __fg_apply(struct filterent *e, const struct filter *f, fg_callback *cb,
               struct fg_misc *misc);

int __fg_applylist(struct filterent *e, const struct filter *f, fg_callback *cb,
                   struct fg_misc *misc) {
  /* This is the interesting one.  The filters are
   * unrolled by now, so there's only one way to
   * follow it */
  int c = 0;
  for (; f; f = f->next) {
    int _c = __fg_apply(e, f, cb, misc);
    if (_c < 0)
      return _c;
    c += _c;
  }
  return c;
}

int __fg_applyone(struct filterent *e, const struct filter *f, fg_callback *cb,
                  struct fg_misc *misc) {
#define _NA(t, f)                                                              \
  if (f) {                                                                     \
    fprintf(stderr, "filter has already defined a %s\n", t);                   \
    return -1;                                                                 \
  }
#define NA(t) _NA(#t, e->t)

#define NC(type, str)                                                          \
  case F_##type:                                                               \
    _NA(str, ESET(e, type));                                                   \
    e->whats_set |= (1 << F_##type);

  switch (f->type) {
    NC(TARGET, "target")
    e->target = f->u.target;
    break;

    NC(SUBGROUP, "subgroup") {
      struct filterent fe;
      int r;

      if (e->subgroup) {
        fprintf(stderr, "cannot compose subgroups\n");
        return -1;
      }
      if (!cb->group) {
        fprintf(
            stderr,
            "backend doesn't support grouping, but hasn't removed groups\n");
        return -1;
      }
      e->target = f->type;
      e->subgroup = f->u.sub.name;

      memset(&fe, 0, sizeof(fe));
      fe.groupname = f->u.sub.name;
      /* direction is special -- we need to save it */
      fe.direction = e->direction;
      cb->group(fe.groupname);
      if ((r = __fg_applylist(&fe, f->u.sub.list, cb, misc)) < 0)
        return r;

      break;
    }

    NC(DIRECTION, "direction and interface")
    NA(iface);
    e->direction = f->u.ifinfo.direction;
    e->iface = f->u.ifinfo.iface;
    break;

  case F_LOG:
    e->whats_set |= (1 << F_LOG);
    e->logmsg = f->u.logmsg;
    break;

#define _DV(tag, str, test, targ, source)                                      \
  case F_##tag:                                                                \
    _NA(str, e->test);                                                         \
    e->targ = f->u.source;                                                     \
    break
#define DV(tag, targ, source) _DV(tag, #targ, targ, targ, source)

    _DV(PROTO, "protocol", proto.name, proto, proto);
    _DV(SOURCE, "source address", srcaddr.addrstr, srcaddr, addrs);
    _DV(DEST, "destination address", dstaddr.addrstr, dstaddr, addrs);
    _DV(SPORT, "source port", u.ports.src.minstr, u.ports.src, ports);
    _DV(DPORT, "destination port", u.ports.src.maxstr, u.ports.dst, ports);
    DV(ICMPTYPE, u.icmp, icmp);
    DV(RTYPE, rtype, rtype);

  case F_ONEWAY:
    e->oneway = 1;
    break;

  case F_SIBLIST:
    return __fg_applylist(e, f->u.sib, cb, misc);

  default:
    fprintf(stderr, "invalid filter type %d\n", f->type);
    return -1;
  }

  if (f->negate)
    e->whats_negated |= (1 << f->type);

  return 0;
}

int __fg_apply(struct filterent *_e, const struct filter *f, fg_callback *cb,
               struct fg_misc *misc) {
  struct filterent e = *_e;

  /* Looks like we're all done */
  if (!f) {
    applydefaults(&e, misc->flags);
    if (checkmatch(&e)) {
      fprintf(stderr, "filter definition incomplete\n");
      return -1;
    }
    return cb->rule(&e, misc);
  }

  return __fg_applyone(&e, f, cb, misc) ? __fg_applyone(&e, f, cb, misc)
                                        : __fg_apply(&e, f->child, cb, misc);
}

int filtergen_cprod(struct filter *filter, fg_callback *cb,
                    struct fg_misc *misc) {
  struct filterent e;
  memset(&e, 0, sizeof(e));
  return __fg_applylist(&e, filter, cb, misc);
}