I rewrite flow-merge.c to extend it with easy flow filter (switch -f).
Filter have flexible and simple syntax:

start           : sp
                | sp cond sp
                ;
cond            : expr
                | cond spaces expr
                | cond sp '|' '|' sp expr
                ;
expr            : 'p' 'r' 'o' 't' 'o' spaces protos
                | 'i' 'p' spaces ipnets
                | 's' 'r' 'c' '-' 'i' 'p' spaces ipnets
                | 'd' 's' 't' '-' 'i' 'p' spaces ipnets
                | 'p' 'o' 'r' 't' spaces ports
                | 's' 'r' 'c' '-' 'p' 'o' 'r' 't' spaces ports
                | 'd' 's' 't' '-' 'p' 'o' 'r' 't' spaces ports
                | '!' sp expr
                | '(' sp cond sp ')'
                ;
protos          : proto
                | '{' sp listprotos sp '}'
                ;
listprotos      : proto
                | listprotos sp ',' sp proto
                ;
proto           : 'i' 'p'
                | 'i' 'c' 'm' 'p'
                | 't' 'c' 'p'
                | 'u' 'd' 'p'
                | 'i' 'p' 'e' 'n' 'c' 'a' 'p'
                | 'g' 'r' 'e'
                | '*'
                | number
                | '!' proto
                ;
ipnets          : ipnet
                | '{' sp listipnets sp '}'
                ;
listipnets      : ipnet
                | listipnets sp ',' sp ipnet
                ;
ipnet           : ipaddr
                | ipaddr '/' netbits
                | ipaddr '/' netmask
                | '*'
                | '!' ipnet
                ;
ports           : port
                | '{' sp listports sp '}'
                ;
listports       : port
                | listports sp ',' sp port
                ;
port            : number
                | '*'
                | '!' port
                ;
netbits         : number
                ;
netmask         : byte '.' byte '.' byte '.' byte
                ;
ipaddr          : byte '.' byte '.' byte '.' byte
                ;
byte            : number
                ;
number          : DIGIT 
                | number DIGIT 
                ;
sp              :
                | spaces
                ;
spaces          : SP
                | spaces SP
                ;

filter exmple:
1. src-ip 10.1.1.1 dst-ip {!10.1.1.1,10.0.0.0/8} proto {tcp,udp}
2. src-ip !10.1.1.0/24 (src-port {10,11,12} || dst-port 5)
3. ! src-ip 10.1.1.1
4. src-ip !10.1.1.1
5. src-ip !{10.1.1.1,10.1.1.2}
6. src-ip {!10.1.1.1,10.1.1.2}
7. ! src-ip ! {!10.1.1.1}
8. proto *

flow-merge2 example:
flow-merge2 -f 'src-ip 10.1.1.1 ! dst-ip { 10.0.0.0/8 }' /var/db/flows

I want to contribute this code to flow-tools. Please.

/swp
/*
 * Copyright (c) 2001 E. Larry Lidz
 * Copyright (c) 2008 Alexander Mitrokhin
 * Portions Copyright (c) 2001 Mark Fullmer and The Ohio State University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      $Id: flow-merge.c,v 1.9 2003/04/02 18:03:02 maf Exp $
 */

#include "ftconfig.h"
#include <ftlib.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>

#include "ippf.h"

#if HAVE_STRINGS_H
 #include <strings.h>
#endif

#if HAVE_STRING_H
  #include <string.h>
#endif

static inline
void
usage(void)
{
	fprintf(stderr, "Usage: flow-merge [-aghm] [-b big|little] [-C comment] [-d debug_level]\n"
			"       [-o filename] [-z z_level] [file|directory ...]\n");
}

int debug;

struct ftio_table {
	char *cur_entry;
	struct ftio ftio_data;
};
int find_earliest(struct ftio_table ftio_entry[], int num_entries);

int main(int argc, char **argv)
{
	struct stat sb;
	struct ftio ftio_out;
	struct ftprof ftp;
	struct ftver ftv, ftv2;
	struct ftset ftset;
	struct ftfile_entries *fte;
	struct ftfile_entry *fty;
	struct ftio_table *ftio_array;
	int out_fd, out_fd_plain, in_fd, disable_mmap, in_fd_plain, sort;
	int ftio_entries;
	int fd, flags, fte_entries;
	char *fname, *out_fname;
	u_int32 total_flows;
	u_int32 time_start, time_end, time_tmp;
	struct ippf *f;

	fterr_setid(argv[0]);
	ftprof_start (&ftp);

	bzero(&ftv, sizeof ftv);
	bzero(&ftv2, sizeof ftv2);

	/* defaults + no compression */
	ftset_init(&ftset, 0);

	total_flows = 0;
	out_fd_plain = 0;
	out_fname = (char*)0L;
	out_fd = -1;
	time_start = -1; /* MAXINT */
	time_end = 0;
	disable_mmap = 0;
	sort = 0; /* global sorting disabled by default */
	fte_entries = 0;
	flags = FT_FILE_INIT | FT_FILE_SORT | FT_FILE_SKIPTMP;
	ftio_entries = 0;
	f = 0;

	do {
		int c;

		while ((c = getopt(argc, argv, "ab:C:d:f:gh?mo:i:z:")) != -1)
			switch (c) {
			case 'a': /* all files, even those with tmp */
				flags &= ~FT_FILE_SKIPTMP;
				break;

			case 'b': /* output byte order */
				if (!strcasecmp(optarg, "little"))
					ftset.byte_order = FT_HEADER_LITTLE_ENDIAN;
				else if (!strcasecmp(optarg, "big"))
					ftset.byte_order = FT_HEADER_BIG_ENDIAN;
				else
					fterr_errx(1, "expecting \"big\" or \"little\"");
				break;

			case 'C': /* comment field */
				ftset.comments = optarg;
				break;

			case 'd': /* debug */
				debug = atoi(optarg);
				break;

			case 'f':
				f = ippf_create_str(optarg);
				if (!f)
					fterr_errx(1, "filter compilation error");
				break;

			case 'g': /* global sort */
				sort = 1;
				break;

			case 'm': /* disable mmap */
				disable_mmap = 1;
				break;

			case 'o': /* output filename */
				out_fname = optarg;
				break;

			case 'z': /* compress level */
				ftset.z_level = atoi(optarg);
				if ((ftset.z_level < 0) || (ftset.z_level > 9))
					fterr_errx(1, "Compression level must be between 0 and 9");
				break;

			case 'h': /* help */
			case '?': 
			default:
				usage();
				exit (1);
				break;
			} /* switch */
	} while (0);

	/* if out_fname is not set, then use stdout */
	if (out_fname) {
		if ((out_fd = open(out_fname,  O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
			fterr_err(1, "open(%s)", out_fname);

		if (fstat(out_fd, &sb) == -1)
			fterr_err(1, "fstat(%s)", out_fname);

		/* is this a plain file? */
		if (S_ISREG(sb.st_mode))
			out_fd_plain = 1;
	} else 
		out_fd = 1;

	/* output to out_fd */    
	if (ftio_init(&ftio_out, out_fd, FT_IO_FLAG_WRITE |
			((ftset.z_level) ? FT_IO_FLAG_ZINIT : 0) ) < 0)
		fterr_errx(1, "ftio_init(): failed");

	ftio_set_comment(&ftio_out, ftset.comments);
	ftio_set_byte_order(&ftio_out, ftset.byte_order);
	ftio_set_z_level(&ftio_out, ftset.z_level);
	ftio_set_streaming(&ftio_out, 1);
	ftio_set_debug(&ftio_out, debug);

	/* header must be full size on initial write */
	if (out_fd_plain) {
		ftio_set_cap_time(&ftio_out, time_start, time_end);
		ftio_set_flows_count(&ftio_out, total_flows);
	}

	/* number of file lists */
	fte_entries = argc - optind;

	/* always at least one entry */
	if (fte_entries <= 0)
		fte_entries = 1;

	if (sort)
		fte_entries = 1;

	/* allocate space for ftfile_entries lists */
	if (!(fte = malloc(sizeof (struct ftfile_entries) * fte_entries)))
		fterr_err(1, "malloc()");
	bzero(fte, sizeof(struct ftfile_entries) * fte_entries);

	/* handle special case of stdin (no args) */
	if (optind >= argc) {
		/* "" will be treated as stdin below */
		if (ftfile_loadfile(&fte[0], "", flags) < 0)
			fterr_errx(1, "ftfile_loadfile(%s)", "");
	} else {
		int i, x;

		for (i = optind, x = 0; i < argc; ++i) {
			fname = argv[i];

			/* special case, stdin */
			if ((fname[0] == '-') && (fname[1] == 0)) {
				/* "" will be treated as stdin below */
				if (ftfile_loadfile(fte + x, "", flags) < 0)
					fterr_errx(1, "ftfile_loadfile(%s)", "");
				goto skip1;
			}

			if (stat(fname, &sb) < 0)
				fterr_err(1, "fstat(%s)", fname);

			if (S_ISREG(sb.st_mode)) {
				/* is this a plain file? */
				if (ftfile_loadfile(fte + x, fname, flags) < 0)
					fterr_errx(1, "ftfile_loadfile(%s)", fname);
			} else if (S_ISDIR(sb.st_mode)) {
				/* else is this a directory? */
				if (ftfile_loaddir(fte + x, fname, flags) < 0)
					fterr_errx(1, "ftfile_loaddir(%s), fname");
			} else {
				fterr_warnx("odd file, skipping: %s", fname);
				continue;
			}

		skip1:
			/* if not sorting, use next list (sorting requires one list) */
			if (!sort)
				++x;
			else 
				/* else, the list is initialized, don't do it again */
				flags &= ~FT_FILE_INIT;
		}
	}

	/* dump list of files loaded */
	if (debug > 5) {
		int i;

		for (i = 0; i < fte_entries; ++i) {
			fterr_info(" i=%d", i);
			ftfile_dump(fte + i);
		}
	}

	do {
		int i;

		/* count total entries */
		for (i = 0; i < fte_entries; ++i)
			/* foreach file in the list */
			FT_TAILQ_FOREACH(fty, &fte[i].head, chain)
				ftio_entries++;
	} while (0);

	ftio_array = malloc(ftio_entries * sizeof(struct ftio_table));
	if (!ftio_array)
		fterr_errx(1, "malloc()");
	bzero(ftio_array, ftio_entries * sizeof(struct ftio_table));

	do {
		int i, j;

		/* foreach list */
		for (i = j = 0; i < fte_entries; ++i) {
			/* foreach file in the list */
			FT_TAILQ_FOREACH(fty, &fte[i].head, chain) {
				/* stdin / real file? */
				if (fty->name[0]) {
					in_fd_plain = 1;
					if ((in_fd = open(fty->name, O_RDONLY, 0)) == -1) {
						fterr_warn("open(%s)", fty->name);
						continue;
					}
				} else {
					in_fd_plain = 0;
					in_fd = 0; /* stdin */
				}

				/* initialize ftio stream */
				if (ftio_init(&ftio_array[j].ftio_data, in_fd, FT_IO_FLAG_READ |
						((in_fd_plain && !disable_mmap) ? FT_IO_FLAG_MMAP : 0)) < 0)
					fterr_errx(1, "ftio_init(): failed");

				/* ensure required fields */
				if (ftio_check_xfield(&ftio_array[j].ftio_data,
					FT_XFIELD_UNIX_SECS|FT_XFIELD_UNIX_NSECS|FT_XFIELD_SYSUPTIME|
						FT_XFIELD_FIRST|FT_XFIELD_LAST))
					fterr_errx(1, "Flow record missing required field.");

				/* get version from stream */
				ftio_get_ver(&ftio_array[j].ftio_data, &ftv2);

				/* record smallest time */
				time_tmp = ftio_get_cap_start(&ftio_array[j].ftio_data);
				if (time_tmp < time_start)
					time_start = time_tmp;

				/* record largest time */
				time_tmp = ftio_get_cap_end(&ftio_array[j].ftio_data);
				if (time_tmp > time_end)
					time_end = time_tmp;

				/* first time through loop? */
				if (!ftv.d_version) {
					/*
					 * is this really the right thing to do here.  Reading a v1
					 * stream gets handled by ftio_read(), but ftio_* leaves the
					 * s_version at 1.
					 */
					ftv2.s_version = FT_IO_SVERSION;

					/* set the version information in the io stream */
					if (ftio_set_ver(&ftio_out, &ftv2) < 0)
						fterr_errx(1, "ftio_set_ver(): failed");

					/* save for later compare */
					bcopy(&ftv2, &ftv, sizeof ftv);

					/* header first */
					if (ftio_write_header(&ftio_out) < 0)
						fterr_errx(1, "ftio_write_header(): failed");
				} else {
					/* ensure previous version == current version */
					if ((ftv.d_version != ftv2.d_version) ||
							(ftv.agg_method != ftv2.agg_method))
						fterr_errx(1, "data version or sub version changed!");
				}

				/* get the next element into the cur_entry field  */
				/* this is so it can be compared in find_earliest */
				ftio_array[j].cur_entry = ftio_read(&ftio_array[j].ftio_data);
				j++;
			}
		}
	} while (0);

	do {
		int entry;

#define FT_FIELD(ftio, r, field, type) \
		(*(type *)(((char *)(r)) + (ftio)->fo.field))
#define FT_SRCADDR(ftio, r) FT_FIELD(ftio, r, srcaddr, in_addr_t)
#define FT_DSTADDR(ftio, r) FT_FIELD(ftio, r, dstaddr, in_addr_t)
#define FT_SRCPORT(ftio, r) FT_FIELD(ftio, r, srcport, uint16_t)
#define FT_DSTPORT(ftio, r) FT_FIELD(ftio, r, dstport, uint16_t)
#define FT_DOCTETS(ftio, r) FT_FIELD(ftio, r, dOctets, uint32_t)
#define FT_IPPROTO(ftio, r) FT_FIELD(ftio, r, prot, uint8_t)
		
		for (; (entry = find_earliest(ftio_array, ftio_entries)) >= 0; ) {
			if (f && ippf_calc(f, 
					FT_IPPROTO(&ftio_array[entry].ftio_data, ftio_array[entry].cur_entry), 
					FT_SRCADDR(&ftio_array[entry].ftio_data, ftio_array[entry].cur_entry), 
					FT_SRCPORT(&ftio_array[entry].ftio_data, ftio_array[entry].cur_entry), 
					FT_DSTADDR(&ftio_array[entry].ftio_data, ftio_array[entry].cur_entry), 
					FT_DSTPORT(&ftio_array[entry].ftio_data, ftio_array[entry].cur_entry)))
				/* copy the earliest entry in ftio_array */
				if (ftio_write(&ftio_out, ftio_array[entry].cur_entry) < 0)
					fterr_errx(1, "ftio_write(): failed");

			/* get the next element into the cur_entry field */
			ftio_array[entry].cur_entry = ftio_read(&ftio_array[entry].ftio_data);

			++total_flows;
		}
	} while (0);

	do { 
		int i;

		for (i = 0; i < ftio_entries; i++)
			/* done with input stream */
			if (ftio_close(&ftio_array[i].ftio_data) < 0)
				fterr_errx(1, "ftio_close(): failed");
	} while (0);

	/*
	 * if the output file descriptor was a real file, re-write the
	 * flow_header with the correct # of total flows
	 */
	if (out_fd_plain) {
		ftio_set_cap_time(&ftio_out, time_start, time_end);
		ftio_set_flows_count(&ftio_out, total_flows);
		ftio_set_streaming(&ftio_out, 0);

		if (ftio_write_header(&ftio_out) < 0)
			fterr_errx(1, "ftio_write_header(): failed");
	}

	/* done with output stream */
	if (ftio_close(&ftio_out) < 0)
		fterr_errx(1, "ftio_close(): failed");

	if (debug > 0) {
		ftprof_end (&ftp, total_flows);
		ftprof_print(&ftp, argv[0], stderr);
	}

	free(ftio_array);
	free(fte);

	if (f)
		ippf_destroy(f);

	return 0;
}

int 
find_earliest(struct ftio_table ftio_entry[], int num_entries)
{
	unsigned int i, earliest_offset;
  	register struct timeval earliest_time;
  	struct fts3rec_all cur;
  	struct fttime ftt;
  	int first;

  	earliest_offset = -1;
  	first = 0;

  	for (i = 0; i < num_entries; i++) {
    		/* already read all the entries in this file */
		if (ftio_entry[i].cur_entry == NULL) 
			continue;

		cur.unix_secs = ((u_int32*)(ftio_entry[i].cur_entry+
					ftio_entry[i].ftio_data.fo.unix_secs));
		cur.unix_nsecs = ((u_int32*)(ftio_entry[i].cur_entry+
					ftio_entry[i].ftio_data.fo.unix_nsecs));
		cur.sysUpTime = ((u_int32*)(ftio_entry[i].cur_entry+
					ftio_entry[i].ftio_data.fo.sysUpTime));
		cur.Last = ((u_int32*)(ftio_entry[i].cur_entry+
					ftio_entry[i].ftio_data.fo.Last));

		ftt = ftltime(*cur.sysUpTime, *cur.unix_secs, 
					*cur.unix_nsecs, *cur.Last);

    		if (first == 0) { /* first entry is the earliest by default */
      			earliest_offset = i;
			earliest_time.tv_sec = ftt.secs;
			earliest_time.tv_usec = ftt.msecs;
			first = 1;
		} else
			if ((ftt.secs < earliest_time.tv_sec) ||
				(ftt.secs == earliest_time.tv_sec &&
					(ftt.msecs < earliest_time.tv_usec))) {
				earliest_offset = i;
				earliest_time.tv_sec = ftt.secs;
				earliest_time.tv_usec = ftt.msecs;
			}
	}
	return earliest_offset;
}
%{
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <err.h>

#define ELOG(fmt, args...) warnx(fmt, ##args)
#define WLOG(fmt, args...) warnx(fmt, ##args)

#include "ippf.h"

enum action 
        { ALLOW = 1, DENY = 0 };
enum ltype 
        { LTYPE_PROTO, LTYPE_IPNET, LTYPE_PORT };

struct proto {
        enum action     act;
        int             proto;
};
struct ipnet {
        enum action     act;
        in_addr_t       addr;
        in_addr_t       mask;
};
struct port {
        enum action     act;
        int             port;
};

struct node {
        int     n;
        union {
                enum action     act;
                struct proto    proto;
                struct ipnet    ipnet;
                struct port     port;
        }       ar[0x10];
        STAILQ_ENTRY(node) ent;
};
STAILQ_HEAD(list, node);


static
int
list_add(struct list *list, enum ltype ltype, ...)
{
        struct node *p;
        va_list ap;

        p = STAILQ_LAST(list, node, ent);
        if (!p || p->n == sizeof p->ar / sizeof p->ar[0]) {
                p = malloc(sizeof *p);
                if (!p)
                        return 1;
                p->n = 0;
                STAILQ_INSERT_TAIL(list, p, ent);
        }
        va_start(ap, ltype);
        if (ltype == LTYPE_PROTO)
                p->ar[p->n].proto = *va_arg(ap, typeof(p->ar[0].proto) *);
        else if (ltype == LTYPE_IPNET)
                p->ar[p->n].ipnet = *va_arg(ap, typeof(p->ar[0].ipnet) *);
        else /* if (ltype == LTYPE_PORT) */
                p->ar[p->n].port = *va_arg(ap, typeof(p->ar[0].port) *);
        va_end(ap);
        p->n++;
        return 0;
}

static
void
list_clear(struct list *list)
{
        struct node *p, *q;

        for (p = STAILQ_FIRST(list); p; p = q) {
             q = STAILQ_NEXT(p, ent);
             free(p);
        }
        STAILQ_INIT(list);
}


enum exprtype {
        EXPR_NONE,
        EXPR_PROTO,
        EXPR_IP,
        EXPR_SRCIP,
        EXPR_DSTIP,
        EXPR_PORT,
        EXPR_SRCPORT,
        EXPR_DSTPORT,
        EXPR_NOT,
        EXPR_OR,
        EXPR_AND
};

struct expr_proto {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_ip {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_srcip {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_dstip {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_port {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_srcport {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_dstport {
        enum exprtype   type;
        struct list     list[1];
};
struct expr_not {
        enum exprtype   type;
        union expr *    op;
};
struct expr_or {
        enum exprtype   type;
        union expr *    op[2];
};
struct expr_and {
        enum exprtype   type;
        union expr *    op[2];
};

union expr {
        enum exprtype           type;
        struct expr_proto       expr_proto;
        struct expr_ip          expr_ip;
        struct expr_srcip       expr_srcip;
        struct expr_dstip       expr_dstip;
        struct expr_port        expr_port;
        struct expr_srcport     expr_srcport;
        struct expr_dstport     expr_dstport;
        struct expr_not         expr_not;
        struct expr_or          expr_or;
        struct expr_and         expr_and;
};

static
void
expr_clear(union expr *expr)
{
        if (expr->type == EXPR_PROTO)
                list_clear(expr->expr_proto.list);
        else if (expr->type == EXPR_IP)
                list_clear(expr->expr_ip.list);
        else if (expr->type == EXPR_SRCIP)
                list_clear(expr->expr_srcip.list);
        else if (expr->type == EXPR_DSTIP)
                list_clear(expr->expr_dstip.list);
        else if (expr->type == EXPR_PORT)
                list_clear(expr->expr_port.list);
        else if (expr->type == EXPR_SRCPORT)
                list_clear(expr->expr_srcport.list);
        else if (expr->type == EXPR_DSTPORT)
                list_clear(expr->expr_dstport.list);
        else if (expr->type == EXPR_NOT) {
                expr_clear(expr->expr_not.op);
                free(expr->expr_not.op);
        } else if (expr->type == EXPR_OR) {
                expr_clear(expr->expr_or.op[0]);
                free(expr->expr_or.op[0]);
                expr_clear(expr->expr_or.op[1]);
                free(expr->expr_or.op[1]);
        } else if (expr->type == EXPR_AND) {
                expr_clear(expr->expr_and.op[0]);
                free(expr->expr_and.op[0]);
                expr_clear(expr->expr_and.op[1]);
                free(expr->expr_and.op[1]);
        } else if (expr->type == EXPR_NONE)
                WLOG("expr_clear(): EXPR_NONE: ???");
        else
                WLOG("expr_clear(): %d: unknown enum exprtype", expr->type);
        expr->type = EXPR_NONE;
}
static
void
expr_destroy(union expr *expr)
{
        if (expr) {
                expr_clear(expr);
                free(expr);
        }
}

static
int
expr_calc(union expr *expr, int proto, in_addr_t srcip, int srcport, in_addr_t 
dstip, int dstport)
{
        int rc;
        struct node *p;
        int i;

        rc = DENY;
        if (!expr)
                rc = ALLOW;
        else if (expr->type == EXPR_PROTO) {
                STAILQ_FOREACH(p, expr->expr_proto.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (!p->ar[i].proto.proto || 
p->ar[i].proto.proto == proto) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_IP) {
                STAILQ_FOREACH(p, expr->expr_ip.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].ipnet.addr == (srcip & 
p->ar[i].ipnet.mask) || 
                                                p->ar[i].ipnet.addr == (dstip & 
p->ar[i].ipnet.mask)) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_SRCIP) {
                STAILQ_FOREACH(p, expr->expr_srcip.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].ipnet.addr == (srcip & 
p->ar[i].ipnet.mask)) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_DSTIP) {
                STAILQ_FOREACH(p, expr->expr_dstip.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].ipnet.addr == (dstip & 
p->ar[i].ipnet.mask)) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_PORT) {
                STAILQ_FOREACH(p, expr->expr_port.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].port.port == 0 
                                        || p->ar[i].port.port == srcport 
                                                || p->ar[i].port.port == 
dstport) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_SRCPORT) {
                STAILQ_FOREACH(p, expr->expr_srcport.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].port.port == 0 || 
p->ar[i].port.port == srcport) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_DSTPORT) {
                STAILQ_FOREACH(p, expr->expr_dstport.list, ent)
                        for (i = 0; i < p->n; i++)
                                if (p->ar[i].port.port == 0 || 
p->ar[i].port.port == dstport) {
                                        rc = p->ar[i].act;
                                        goto L1;
                                }
        } else if (expr->type == EXPR_NOT)
                rc = !expr_calc(expr->expr_not.op, proto, srcip, srcport, 
dstip, dstport);
        else if (expr->type == EXPR_OR)
                rc = expr_calc(expr->expr_or.op[0], proto, srcip, srcport, 
dstip, dstport) ||
                        expr_calc(expr->expr_or.op[1], proto, srcip, srcport, 
dstip, dstport);
        else if (expr->type == EXPR_AND)
                rc = expr_calc(expr->expr_and.op[0], proto, srcip, srcport, 
dstip, dstport) &&
                        expr_calc(expr->expr_and.op[1], proto, srcip, srcport, 
dstip, dstport);
L1:     
        return rc;
}
%}

%union {
        union expr *            expr_p;
        struct list             list;
        struct proto            proto;
        struct ipnet            ipnet;
        struct port             port;
        in_addr_t               ipaddr;
        int                     d;
}

%type <expr_p>          cond
%type <expr_p>          expr
%type <list>            protos
%type <list>            listprotos
%type <list>            ipnets
%type <list>            listipnets
%type <list>            ports
%type <list>            listports
%type <proto>           proto
%type <ipnet>           ipnet
%type <port>            port
%type <ipaddr>          ipaddr
%type <d>               netbits
%type <d>               netmask
%type <d>               byte
%type <d>               number

%token <d>              DIGIT
%token                  SP


%destructor { expr_destroy($$); } expr cond
%destructor { list_clear(&$$); } listports ports listipnets ipnets listprotos 
protos

%pure-parser
%locations
%parse-param { FILE *fp }
%parse-param { union expr **expr }
%lex-param { FILE *fp }
%error-verbose
%initial-action
{
}

%{
static int yyparse(FILE *fp, union expr **expr);
static int yyerror(YYLTYPE *llocp, FILE *fp, union expr **expr, char const 
*msg);
static int yylex(YYSTYPE *lvalp, YYLTYPE *llocp, FILE *fp);
%}

%%

start           : sp
                        {
                                *expr = 0;
                        }
                | sp cond sp
                        {
                                *expr = $2;
                                $2 = 0;
                        }
                ;

cond            : expr
                        {
                                $$ = $1;
                                $1 = 0;
                        }
                | cond spaces expr
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_and",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_AND;
                                $$->expr_and.op[0] = $1;
                                $1 = 0;
                                $$->expr_and.op[1] = $3;
                                $3 = 0;
                                        
                        }
                | cond sp '|' '|' sp expr
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_and",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_OR;
                                $$->expr_or.op[0] = $1;
                                $1 = 0;
                                $$->expr_or.op[1] = $6;
                                $6 = 0;
                                        
                        }
                ;


expr            : 'p' 'r' 'o' 't' 'o' spaces protos
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_proto",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_PROTO;
                                STAILQ_INIT($$->expr_proto.list);
                                STAILQ_CONCAT($$->expr_proto.list, &$7);
                                STAILQ_INIT(&$7);
                        }
                | 'i' 'p' spaces ipnets
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create expr_ip",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_IP;
                                STAILQ_INIT($$->expr_ip.list);
                                STAILQ_CONCAT($$->expr_ip.list, &$4);
                                STAILQ_INIT(&$4);
                        }
                | 's' 'r' 'c' '-' 'i' 'p' spaces ipnets
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_srcip",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_SRCIP;
                                STAILQ_INIT($$->expr_srcip.list);
                                STAILQ_CONCAT($$->expr_srcip.list, &$8);
                                STAILQ_INIT(&$8);
                        }
                | 'd' 's' 't' '-' 'i' 'p' spaces ipnets
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_dstip",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_DSTIP;
                                STAILQ_INIT($$->expr_dstip.list);
                                STAILQ_CONCAT($$->expr_dstip.list, &$8);
                                STAILQ_INIT(&$8);
                        }
                | 'p' 'o' 'r' 't' spaces ports
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_srcport",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_PORT;
                                STAILQ_INIT($$->expr_srcport.list);
                                STAILQ_CONCAT($$->expr_srcport.list, &$6);
                                STAILQ_INIT(&$6);
                        }
                | 's' 'r' 'c' '-' 'p' 'o' 'r' 't' spaces ports
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_srcport",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_SRCPORT;
                                STAILQ_INIT($$->expr_srcport.list);
                                STAILQ_CONCAT($$->expr_srcport.list, &$10);
                                STAILQ_INIT(&$10);
                        }
                | 'd' 's' 't' '-' 'p' 'o' 'r' 't' spaces ports
                        {
                                if (!($$ = malloc(sizeof *$$))) {
                                        ELOG("(l%d,c%d): unable create 
expr_dstport",
                                                        @$.first_line, 
@$.first_column);
                                        YYABORT;
                                }
                                $$->type = EXPR_DSTPORT;
                                STAILQ_INIT($$->expr_dstport.list);
                                STAILQ_CONCAT($$->expr_dstport.list, &$10);
                                STAILQ_INIT(&$10);
                        }
                | '!' sp expr
                        {
                                if ($3->type == EXPR_NOT) {
                                        $$ = $3->expr_not.op;
                                        free($3);
                                } else {
                                        if (!($$ = malloc(sizeof *$$))) {
                                                ELOG("(l%d,c%d): unable create 
expr_not",
                                                        @$.first_line, 
@$.first_column);
                                                YYABORT;
                                        }
                                        $$->type = EXPR_NOT;
                                        $$->expr_not.op = $3;
                                        $3 = 0;
                                }
                        }
                | '(' sp cond sp ')'
                        {
                                $$ = $3;
                                $3 = 0;
                        }
                ;

protos          : proto
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_PORT, &$1)) {
                                        ELOG("(l%d,c%d): %d: unable insert 
proto in list",
                                                @$.first_line, @$.first_column, 
$1.proto);
                                        YYABORT;
                                }
                        }
                | '{' sp listprotos sp '}'
                        {
                                STAILQ_INIT(&$$);
                                STAILQ_CONCAT(&$$, &$3);
                                STAILQ_INIT(&$3);
                        }
                ;

listprotos      : proto
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_PORT, &$1)) {
                                        ELOG("(l%d,c%d): %d: unable insert 
proto in list",
                                                @$.first_line, @$.first_column, 
$1.proto);
                                        YYABORT;
                                }
                        }
                | listprotos sp ',' sp proto
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$1, LTYPE_PORT, &$5)) {
                                        ELOG("(l%d,c%d): %d: unable insert 
proto in list",
                                                @$.first_line, @$.first_column, 
$5.proto);
                                        YYABORT;
                                }
                                STAILQ_CONCAT(&$$, &$1);
                                STAILQ_INIT(&$1);
                        }
                ;

proto           : 'i' 'p'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("ip"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"ip\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | 'i' 'c' 'm' 'p'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("icmp"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"icmp\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | 't' 'c' 'p'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("tcp"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"tcp\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | 'u' 'd' 'p'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("udp"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"udp\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | 'i' 'p' 'e' 'n' 'c' 'a' 'p'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("ipencap"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"ipencap\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | 'g' 'r' 'e'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("gre"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"gre\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | '*'
                        {
                                struct protoent *p;
                                if (!(p = getprotobyname("ip"))) {
                                        ELOG("(l%d,c%d): 
getprotobyname(\"ip\"): not found\n",
                                                @$.first_line, @$.first_column);
                                        YYABORT;
                                }
                                $$.proto = p->p_proto;
                                $$.act = ALLOW;
                        }
                | number
                        {
                                $$.proto = $1;
                                $$.act = ALLOW;
                        }
                | '!' proto
                        {
                                $$.proto = $2.proto;
                                $$.act = !$2.act;
                        }
                ;


ipnets          : ipnet
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_IPNET, &$1)) {
                                        in_addr_t addr = htonl($1.addr);
                                        in_addr_t mask = htonl($1.mask);
                                        char addrbuf[0x10], maskbuf[0x10];

                                        ELOG("(l%d,c%d): %s/%s: unable insert 
ipnetwork in list",
                                                @$.first_line, @$.first_column, 
                                                        inet_ntop(AF_INET, 
&addr, addrbuf, sizeof addrbuf),
                                                        inet_ntop(AF_INET, 
&mask, maskbuf, sizeof maskbuf));
                                        YYABORT;
                                }
                        }
                | '{' sp listipnets sp '}'
                        {
                                STAILQ_INIT(&$$);
                                STAILQ_CONCAT(&$$, &$3);
                                STAILQ_INIT(&$3);
                        }
                ;

listipnets      : ipnet
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_IPNET, &$1)) {
                                        in_addr_t addr = htonl($1.addr);
                                        in_addr_t mask = htonl($1.mask);
                                        char addrbuf[0x10], maskbuf[0x10];

                                        ELOG("(l%d,c%d): %s/%s: unable insert 
ipnetwork in list",
                                                @$.first_line, @$.first_column, 
                                                        inet_ntop(AF_INET, 
&addr, addrbuf, sizeof addrbuf),
                                                        inet_ntop(AF_INET, 
&mask, maskbuf, sizeof maskbuf));
                                        YYABORT;
                                }
                        }
                | listipnets sp ',' sp ipnet
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$1, LTYPE_IPNET, &$5)) {
                                        in_addr_t addr = htonl($5.addr);
                                        in_addr_t mask = htonl($5.mask);
                                        char addrbuf[0x10], maskbuf[0x10];

                                        ELOG("(l%d,c%d): %s/%s: unable insert 
ipnetwork in list",
                                                @$.first_line, @$.first_column, 
                                                        inet_ntop(AF_INET, 
&addr, addrbuf, sizeof addrbuf),
                                                        inet_ntop(AF_INET, 
&mask, maskbuf, sizeof maskbuf));
                                        YYABORT;
                                }
                                STAILQ_CONCAT(&$$, &$1);
                                STAILQ_INIT(&$1);
                        }
                ;

ipnet           : ipaddr
                        {
                                $$.addr = $1;
                                $$.mask = -1;
                                $$.act = ALLOW;
                        }
                | ipaddr '/' netbits
                        {
                                $$.addr = $1;
                                $$.mask = $3 ? -1<<(32-$3) : 0;
                                $$.act = ALLOW;
                        }
                | ipaddr '/' netmask
                        {
                                $$.addr = $1;
                                $$.mask = $3;
                                $$.act = ALLOW;
                        }
                | '*'
                        {
                                $$.addr = 0;
                                $$.mask = 0;
                                $$.act = ALLOW;
                        }
                | '!' ipnet
                        {
                                $$.addr = $2.addr;
                                $$.mask = $2.mask;
                                $$.act = !$2.act;
                        }
                ;


ports           : port
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_PORT, &$1)) {
                                        ELOG("(l%d,c%d): %d: unable insert port 
in list",
                                                @$.first_line, @$.first_column, 
$1.port);
                                        YYABORT;
                                }
                        }
                | '{' sp listports sp '}'
                        {
                                STAILQ_INIT(&$$);
                                STAILQ_CONCAT(&$$, &$3);
                                STAILQ_INIT(&$3);
                        }
                ;

listports       : port
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$$, LTYPE_PORT, &$1)) {
                                        ELOG("(l%d,c%d): %d: unable insert port 
in list",
                                                @$.first_line, @$.first_column, 
$1.port);
                                        YYABORT;
                                }
                        }
                | listports sp ',' sp port
                        {
                                STAILQ_INIT(&$$);
                                if (list_add(&$1, LTYPE_PORT, &$5)) {
                                        ELOG("(l%d,c%d): %d: unable insert port 
in list",
                                                @$.first_line, @$.first_column, 
$5.port);
                                        YYABORT;
                                }
                                STAILQ_CONCAT(&$$, &$1);
                                STAILQ_INIT(&$1);
                        }
                ;

port            : number
                        {
                                if ($1 > 65535) {
                                        ELOG("(l%d,c%d): %d: wrong port number",
                                                @$.first_line, @$.first_column, 
$1);
                                        YYABORT;
                                }
                                $$.port = $1;
                                $$.act = ALLOW;
                        }
                | '*'
                        {
                                $$.port = 0;
                                $$.act = ALLOW;
                        }
                | '!' port
                        {
                                $$.port = $2.port;
                                $$.act = !$2.act;
                        }
                ;

netbits         : number
                        {
                                $$ = $1;
                                if ($1 > 32) {
                                        ELOG("(l%d,c%d): %d: wrong bitmask",
                                                @$.first_line, @$.first_column, 
$1);
                                        YYABORT;
                                }
                        }
                ;

netmask         : byte '.' byte '.' byte '.' byte
                        {
                                $$ = (($7&0377)<<24) | (($5&0377)<<16) | 
(($3&0377)<<8) | ($1&0377);
                                $$ = ntohl($$);
                        }
                ;

ipaddr          : byte '.' byte '.' byte '.' byte
                        {
                                $$ = (($7&0377)<<24) | (($5&0377)<<16) | 
(($3&0377)<<8) | ($1&0377);
                                $$ = ntohl($$);
                        }
                ;

byte            : number
                        {
                                $$ = $1;
                                if ($1 > 255) {
                                        ELOG("(l%d,c%d): %d: expect byte",
                                                @$.first_line, @$.first_column, 
$1);
                                        YYABORT;
                                }
                        }
                ;

number          : DIGIT 
                        { 
                                $$ = $1; 
                        }
                | number DIGIT 
                        {
                                $$ = $1 * 10 + $2; 
                                if ($$ < $1) {
                                        ELOG("(l%d,c%d): big number", 
@$.first_line, @$.first_column);
                                        YYABORT;
                                }
                        }
                ;

sp              :
                | spaces
                ;
spaces          : SP
                | spaces SP
                ;

%%

int
yyerror(YYLTYPE *llocp, FILE *fp, union expr **expr, char const *msg)
{
        fprintf(stderr, "ERROR(%d:%d-%d:%d): %s\n", 
                llocp->first_line, llocp->first_column, 
                llocp->last_line,  llocp->last_column, 
                        msg);
        return 0;
}

int
yylex(YYSTYPE *lvalp, YYLTYPE *llocp, FILE *fp)
{
        int c;

        if ((c = fgetc(fp)) >= 0) {
                if (c == '\n') {
                        llocp->first_line = ++llocp->last_line;
                        llocp->first_column = llocp->last_column = 0;
                } else
                        llocp->first_column = ++llocp->last_column;

                if (isspace(c)) {
                        c = SP;
                } else if (isdigit(c)) {
                        lvalp->d = c - '0';
                        c = DIGIT;
                }
        } 
        return c;
}

struct ippf *
ippf_create(FILE *fp)
{
        union expr *expr;

        if (yyparse(fp, &expr)) {
                expr_destroy(expr);
                expr = 0;
        }
        return (struct ippf *)expr;
}
void
ippf_destroy(struct ippf *filt)
{
        if (filt)
                expr_destroy((union expr *)filt);
}

static
int
str_readfn(void *cookie, char *buf, int n)
{
        int r;

        r = strlcpy(buf, *(char **)cookie, n);
        *(char **)cookie += r;
        return r;
}

struct ippf *
ippf_create_str(char const *s)
{
        struct ippf *filt;
        FILE *fp;

        filt = 0;
        if ((fp = fropen(&s, str_readfn)) != 0) {
                filt = ippf_create(fp);
                fclose(fp);
        }
        return filt;
}

int
ippf_calc(struct ippf *filt, int proto, in_addr_t srcip, int srcport, in_addr_t 
dstip, int dstport)
{
        return expr_calc((union expr *)filt, proto, srcip, srcport, dstip, 
dstport);
}
/* $Id: ippf.h,v 1.1 2007/08/13 17:55:33 swp Exp $ */

#ifndef __ippf_h__
#define __ippf_h__

#include <sys/cdefs.h>

struct ippf;

__BEGIN_DECLS
struct ippf *	ippf_create(FILE *);
struct ippf *	ippf_create_str(char const *);
void 		ippf_destroy(struct ippf *);
int		ippf_calc(struct ippf *, int, in_addr_t, int, in_addr_t, int);
__END_DECLS

#endif
_______________________________________________
Flow-tools mailing list
[EMAIL PROTECTED]
http://mailman.splintered.net/mailman/listinfo/flow-tools

Reply via email to