예제 #1
0
/**
 * QUERY.
 *
 */
static query_state
query_process_query(query_type* q, ldns_rr_type qtype, engine_type* engine)
{
    dnsout_type* dnsout = NULL;
    if (!q || !q->zone) {
        return QUERY_DISCARDED;
    }
    ods_log_assert(q->zone->name);
    ods_log_debug("[%s] incoming query qtype=%s for zone %s", query_str,
        rrset_type2str(qtype), q->zone->name);
    /* sanity checks */
    if (buffer_pkt_qdcount(q->buffer) != 1 || buffer_pkt_tc(q->buffer)) {
        buffer_pkt_set_flags(q->buffer, 0);
        return query_formerr(q);
    }
    if (buffer_pkt_ancount(q->buffer) != 0 ||
        (qtype != LDNS_RR_TYPE_IXFR && buffer_pkt_nscount(q->buffer) != 0)) {
        buffer_pkt_set_flags(q->buffer, 0);
        return query_formerr(q);
    }
    /* acl */
    if (!q->zone->adoutbound || q->zone->adoutbound->type != ADAPTER_DNS) {
        ods_log_error("[%s] zone %s is not configured to have output dns "
            "adapter", query_str, q->zone->name);
        return query_refused(q);
    }
    ods_log_assert(q->zone->adoutbound->config);
    dnsout = (dnsout_type*) q->zone->adoutbound->config;
    /* acl also in use for soa and other queries */
    if (!acl_find(dnsout->provide_xfr, &q->addr, q->tsig_rr)) {
        return query_refused(q);
    }
    /* ixfr? */
    if (qtype == LDNS_RR_TYPE_IXFR) {
        if (query_process_ixfr(q) != QUERY_PROCESSED) {
            buffer_pkt_set_flags(q->buffer, 0);
            return query_formerr(q);
        }
        query_prepare(q);
        ods_log_assert(q->zone->name);
        ods_log_debug("[%s] incoming ixfr request serial=%u for zone %s",
            query_str, q->serial, q->zone->name);
        return ixfr(q, engine);
    }

    query_prepare(q);
    /* axfr? */
    if (qtype == LDNS_RR_TYPE_AXFR) {
        ods_log_assert(q->zone->name);
        ods_log_debug("[%s] incoming axfr request for zone %s",
            query_str, q->zone->name);
        return axfr(q, engine);
    }
    /* (soa) query */
    return query_response(q, qtype);
}
예제 #2
0
// standard ACL
int cliAclIcmpCmd(CliMode mode, int argc, char **argv) {
	char *data = (char *) pktout + sizeof(RcpPkt);
	*data = '\0';
	
	//
	// store incoming data in a temporary structure
	//
	RcpAclIcmp in;
	memset(&in, 0, sizeof(RcpAclIcmp));
	
	// no form
	int noform = 0;
	int index = 1;
	if (strcmp(argv[0], "no") == 0) {
		noform = 1;
		index++;
	}
		
	// list number
	uint8_t list_number = atoi(argv[index++]);
	
	// action
	uint8_t action;
	if (strcmp(argv[index++], "deny") == 0)
		action = RCPACL_ACTION_DENY;
	else
		action = RCPACL_ACTION_PERMIT;
	
	// mac address
	uint8_t mac[6];
	if (atomac(argv[index], mac) == 0) {
		memcpy(in.mac, mac, 6);
		index++;
	}
	else

	// skip "icmp"
	index++;

	// source address
	if (strcmp(argv[index], "any") == 0)
		;
	else {
		uint32_t ip;
		uint32_t mask;
		if (atocidr(argv[index], &ip, &mask)) {
			sprintf(data, "Error: invalid source IP address\n");
			return RCPERR;
		}
		uint8_t mask_bits = mask2bits(mask);
		in.ip = ip;
		in.mask_bits = mask_bits;
	}
	index++;
	
	// destination address/output interface
	if (strcmp(argv[index], "any") == 0)
		;
	else {
		uint32_t ip;
		uint32_t mask;
		if (atocidr(argv[index], &ip, &mask)) {
			sprintf(data, "Error: invalid destination IP address\n");
			return RCPERR;
		}
		uint8_t mask_bits = mask2bits(mask);
		in.dest_ip = ip;
		in.dest_mask_bits = mask_bits;
	}
	index++;

	// icmp type
	if ((argc - 1) == index)
		in.icmp_type = atoi(argv[index]);
	
	//
	// process acl
	//
	
	// find if this is an existing acl
	RcpAcl *found = acl_find(&in, RCP_ACL_TYPE_ICMP, action, list_number);
	
	// process command
	if (noform) {
		if (found == NULL)
			return 0;
			
		// remove acl
		found->valid = 0;
	}
	else {
		// already there?
		if (found != NULL)
			return 0;

		// allocate new acl
		RcpAcl *newacl = acl_find_empty();
		if (newacl == NULL) {
			strcpy(data, "Error: cannot configure ACL entry, limit reached");
			return RCPERR;
		}

		// copy the data
		memcpy(&newacl->u.icmp, &in, sizeof(RcpAclIcmp));
		newacl->valid = 1;
		newacl->type = RCP_ACL_TYPE_ICMP;
		newacl->action = action;
		newacl->list_number = list_number;
	}		
		
	// trigger netfilter configuration update
	netfilter_update = 2; 
	
	return 0;
}
예제 #3
0
// extended ACL
int cliAclCmd(CliMode mode, int argc, char **argv) {
	char *data = (char *) pktout + sizeof(RcpPkt);
	*data = '\0';
	
	//
	// store incoming data in a temporary structure
	//
	RcpAclExtended in;
	memset(&in, 0, sizeof(RcpAclExtended));
	
	// no form
	int noform = 0;
	int index = 1;
	if (strcmp(argv[0], "no") == 0) {
		noform = 1;
		index++;
	}
		
	// list number
	uint8_t list_number = atoi(argv[index++]);
	
	// action
	uint8_t action;
	if (strcmp(argv[index++], "deny") == 0)
		action = RCPACL_ACTION_DENY;
	else
		action = RCPACL_ACTION_PERMIT;
	

	// mac address
	uint8_t mac[6];
	if (atomac(argv[index], mac) == 0) {
		memcpy(in.mac, mac, 6);
		index++;
	}

	// protocol
	if (isnum(argv[index]))
		in.protocol = atoi(argv[index++]);
	else if (strcmp(argv[index], "tcp") == 0) {
		in.protocol = RCPACL_PROTOCOL_TCP;
		index++;
	}
	else if (strcmp(argv[index], "udp") == 0) {
		in.protocol = RCPACL_PROTOCOL_UDP;
		index++;
	}
	else
		in.protocol = RCPACL_PROTOCOL_ANY;
		
	// source address
	if (strcmp(argv[index], "any") == 0)
		;
	else {
		uint32_t ip;
		uint32_t mask;
		if (atocidr(argv[index], &ip, &mask)) {
			sprintf(data, "Error: invalid source IP address\n");
			return RCPERR;
		}
		uint8_t mask_bits = mask2bits(mask);
		in.ip = ip;
		in.mask_bits = mask_bits;
	}
	index++;
	
	// source port
	if (isnum(argv[index]))
		in.port = atoi(argv[index++]);
	
	// destination address/output interface
	if (strcmp(argv[index], "any") == 0)
		;
	else if (strcmp(argv[index], "out-interface") == 0) {
		strncpy(in.out_interface, argv[index + 1], RCP_MAX_IF_NAME);
		index++;
	}
	else {
		uint32_t ip;
		uint32_t mask;
		if (atocidr(argv[index], &ip, &mask)) {
			sprintf(data, "Error: invalid destination IP address\n");
			return RCPERR;
		}
		uint8_t mask_bits = mask2bits(mask);
		in.dest_ip = ip;
		in.dest_mask_bits = mask_bits;
	}
	index++;

	// destination port
	if (index < argc && isnum(argv[index]))
		in.dest_port = atoi(argv[index++]);
	
	// connection state
	if (index < argc) {
		// parsing
		char *str = argv[index];
		char *ptr = strtok(str, ",");
		in.constate = 0;
		while(ptr != NULL) {
			if (strcmp(ptr, "new") == 0)
				in.constate |= RCP_ACL_CONSTATE_NEW;
			else if (strcmp(ptr, "established") == 0)
				in.constate |= RCP_ACL_CONSTATE_ESTABLISHED;
			else if (strcmp(ptr, "related") == 0)
				in.constate |= RCP_ACL_CONSTATE_RELATED;
			else if (strcmp(ptr, "invalid") == 0)
				in.constate |= RCP_ACL_CONSTATE_INVALID;
			else {
				sprintf(data, "Error: invalid connecton state\n");
				return RCPERR;
			}
			
			ptr = strtok(NULL, ",");
		}
	} // this should be the last one, argv was modified by strtok


	//
	// process acl
	//
	
	// find if this is an existing acl
	RcpAcl *found = acl_find(&in, RCP_ACL_TYPE_EXTENDED, action, list_number);
	
	// process command
	if (noform) {
		if (found == NULL)
			return 0;
			
		// remove acl
		found->valid = 0;
	}
	else {
		// already there?
		if (found != NULL)
			return 0;

		// allocate new acl
		RcpAcl *newacl = acl_find_empty();
		if (newacl == NULL) {
			strcpy(data, "Error: cannot configure ACL entry, limit reached");
			return RCPERR;
		}

		// copy the data
		memcpy(&newacl->u.extended, &in, sizeof(RcpAclExtended));
		newacl->valid = 1;
		newacl->type = RCP_ACL_TYPE_EXTENDED;
		newacl->action = action;
		newacl->list_number = list_number;
	}		
		
	// trigger netfilter configuration update
	netfilter_update = 2; 
	
	return 0;
}
예제 #4
0
/**
 * NOTIFY.
 *
 */
static query_state
query_process_notify(query_type* q, ldns_rr_type qtype, void* engine)
{
    engine_type* e = (engine_type*) engine;
    dnsin_type* dnsin = NULL;
    uint16_t count = 0;
    uint16_t rrcount = 0;
    uint32_t serial = 0;
    size_t pos = 0;
    char address[128];
    if (!e || !q || !q->zone) {
        return QUERY_DISCARDED;
    }
    ods_log_assert(e->dnshandler);
    ods_log_assert(q->zone->name);
    ods_log_debug("[%s] incoming notify for zone %s", query_str,
        q->zone->name);
    if (buffer_pkt_rcode(q->buffer) != LDNS_RCODE_NOERROR ||
        buffer_pkt_qr(q->buffer) ||
        !buffer_pkt_aa(q->buffer) ||
        buffer_pkt_tc(q->buffer) ||
        buffer_pkt_rd(q->buffer) ||
        buffer_pkt_ra(q->buffer) ||
        buffer_pkt_ad(q->buffer) ||
        buffer_pkt_cd(q->buffer) ||
        buffer_pkt_qdcount(q->buffer) != 1 ||
        buffer_pkt_ancount(q->buffer) > 1 ||
        qtype != LDNS_RR_TYPE_SOA) {
        return query_formerr(q);
    }
    if (!q->zone->adinbound || q->zone->adinbound->type != ADAPTER_DNS) {
        ods_log_error("[%s] zone %s is not configured to have input dns "
            "adapter", query_str, q->zone->name);
        return query_notauth(q);
    }
    ods_log_assert(q->zone->adinbound->config);
    dnsin = (dnsin_type*) q->zone->adinbound->config;
    if (!acl_find(dnsin->allow_notify, &q->addr, q->tsig_rr)) {
        if (addr2ip(q->addr, address, sizeof(address))) {
            ods_log_info("[%s] unauthorized notify for zone %s from client %s: "
                "no acl matches", query_str, q->zone->name, address);
        } else {
            ods_log_info("[%s] unauthorized notify for zone %s from unknown "
                "client: no acl matches", query_str, q->zone->name);
        }
        return query_notauth(q);
    }
    ods_log_assert(q->zone->xfrd);
    /* skip header and question section */
    buffer_skip(q->buffer, BUFFER_PKT_HEADER_SIZE);
    count = buffer_pkt_qdcount(q->buffer);
    for (rrcount = 0; rrcount < count; rrcount++) {
        if (!buffer_skip_rr(q->buffer, 1)) {
            ods_log_error("[%s] dropped packet: zone %s received bad notify "
                "(bad question section)", query_str, q->zone->name);
            return QUERY_DISCARDED;
        }
    }
    pos = buffer_position(q->buffer);

    /* examine answer section */
    count = buffer_pkt_ancount(q->buffer);
    if (count) {
        if (!buffer_skip_dname(q->buffer) ||
            !query_parse_soa(q->buffer, &serial)) {
            ods_log_error("[%s] dropped packet: zone %s received bad notify "
                "(bad soa in answer section)", query_str, q->zone->name);
            return QUERY_DISCARDED;
        }
        lock_basic_lock(&q->zone->xfrd->serial_lock);
        q->zone->xfrd->serial_notify = serial;
        q->zone->xfrd->serial_notify_acquired = time_now();
        if (!util_serial_gt(q->zone->xfrd->serial_notify,
            q->zone->xfrd->serial_disk)) {
            ods_log_debug("[%s] ignore notify: already got zone %s serial "
                "%u on disk", query_str, q->zone->name,
                q->zone->xfrd->serial_notify);
            lock_basic_unlock(&q->zone->xfrd->serial_lock);
            goto send_notify_ok;
        }
        lock_basic_unlock(&q->zone->xfrd->serial_lock);
    } else {
        lock_basic_lock(&q->zone->xfrd->serial_lock);
        q->zone->xfrd->serial_notify = 0;
        q->zone->xfrd->serial_notify_acquired = 0;
        lock_basic_unlock(&q->zone->xfrd->serial_lock);
    }
    /* forward notify to xfrd */
    xfrd_set_timer_now(q->zone->xfrd);
    dnshandler_fwd_notify(e->dnshandler, buffer_begin(q->buffer),
        buffer_remaining(q->buffer));

send_notify_ok:
    /* send notify ok */
    buffer_pkt_set_qr(q->buffer);
    buffer_pkt_set_aa(q->buffer);
    buffer_pkt_set_ancount(q->buffer, 0);

    buffer_clear(q->buffer); /* lim = pos, pos = 0; */
    buffer_set_position(q->buffer, pos);
    buffer_set_limit(q->buffer, buffer_capacity(q->buffer));
    q->reserved_space = edns_rr_reserved_space(q->edns_rr);
    q->reserved_space += tsig_rr_reserved_space(q->tsig_rr);
    return QUERY_PROCESSED;
}
예제 #5
0
int main(int argc, char **argv)
{
  struct fts3rec_all cur;
  struct fts3rec_offsets fo;
  struct ftio ftio_in, ftio_out;
  struct ftset ftset;
  struct ftver ftv;
  struct ftprof ftp;
  uint32_t time_start, time_end, exaddr;
  int i, ret;
  char *acl_fname, *acl_std_src_name, *acl_std_dst_name;
  char *acl_std_nexthop_name;
  char *acl_ext_name, *str, *strm;
  int acl_std_src_index, acl_std_src_index2;
  int acl_std_dst_index, acl_std_dst_index2;
  int acl_std_nexthop_index, acl_std_nexthop_index2;
  int acl_ext_index, acl_ext_index2;
  struct acl_ip_ext_entry tmp_ext;
  int keep_input_time;
  int filter_input, filter_output, filter_srcport, filter_dstport;
  int filter_prot, filter_srcas, filter_dstas, filter_tos, filter_tcp_flags;
  int filter_exaddr;
  char in_tbl[65536], out_tbl[65536], src_tbl[65536], dst_tbl[65536];
  char srcas_tbl[65536], dstas_tbl[65536], tos_tbl[65536];
  char tcp_flags_tbl[65536];
  char prot_tbl[256];
  u_char tos_mask, tos, tcp_flags_mask, tcp_flags;
  uint64_t total_flows, xflag;
  char *rec;
  int first_match = 0;

  /* init fterr */
  fterr_setid(argv[0]);

  bzero(&ftv, sizeof ftv);

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

  /* init */
  bzero(&acl_list, sizeof acl_list);
  acl_fname = acl_std_src_name = acl_std_dst_name = (char*)0L;
  acl_std_nexthop_name = (char*)0L;
  acl_ext_name = (char*)0L;
  acl_std_src_index = acl_std_dst_index = -1;
  acl_std_nexthop_index = -1;
  acl_ext_index = -1;
  bzero(&tmp_ext, sizeof tmp_ext);
  total_flows = 0;
  tos_mask = 0xff;
  tcp_flags_mask = 0xff;
  keep_input_time = 0;

  filter_input = filter_output = filter_srcport = filter_dstport = 0;
  filter_prot = filter_srcas = filter_dstas = filter_tos = filter_exaddr = 0;
  filter_tcp_flags = 0;

  while ((i = getopt(argc, argv, "a:A:b:C:d:D:e:E:f:i:I:kop:P:r:S:t:T:x:z:"))
    != -1)
    switch (i) {

    case 'a': /* src AS filter list */
      if (load_lookup(optarg, 65536, srcas_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_srcas = 1;
      break;

    case 'A': /* dst AS filter list */
      if (load_lookup(optarg, 65536, dstas_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_dstas = 1;
      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': /* dst ip standard access list filter */
      acl_std_dst_name = optarg;
      break;

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

    case 'e': /* export IP address */
      filter_exaddr = 1;
      exaddr = scan_ip(optarg);
      break;

    case 'E': /* extended access list filter */
      acl_ext_name = optarg;
      break;

    case 'f': /* acl file name */
      acl_fname = optarg;
      break;

    case 'i': /* input filter interface list */
      if (load_lookup(optarg, 65536, in_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_input = 1;
      break;

    case 'I': /* output filter interface list */
      if (load_lookup(optarg, 65536, out_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_output = 1;
      break;

    case 'k': /* keep the start/end time from the input */
      keep_input_time = 1;
      break;

    case 'o': /* do logical OR between different statements (first match) */
      first_match = 1;
      break;


    case 'P': /* filter dstport */
      if (load_lookup(optarg, 65536, dst_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_dstport = 1;
      break;

    case 'p': /* filter srcport */
      if (load_lookup(optarg, 65536, src_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_srcport = 1;
      break;

    case 'r': /* filter protocol */
      if (load_lookup(optarg, 256, prot_tbl))
        fterr_errx(1, "load_lookup(): failed");

      filter_prot = 1;
      break;

    case 'S': /* src ip standard access list filter */
      acl_std_src_name = optarg;
      break;

    case 't': /* ToS Filter */
      if (!(str = malloc(strlen(optarg+1))))
        fterr_err(1, "malloc()");

      strcpy(str, optarg);

      /* search for mask option */
      if ((strm = index(str, '/'))) {
        ++strm;
        tos_mask = (u_char)strtol(strm, (char**)0L, 0);
        --strm;
        *strm = 0;
      }

      if (load_lookup(str, 65536, tos_tbl)) {
        free(str);
        fterr_errx(1, "load_lookup(): failed");
      }

      free(str);
      filter_tos = 1;
      break;

    case 'T': /* tcp flags filter */
      if (!(str = malloc(strlen(optarg+1))))
        fterr_err(1, "malloc()");

      strcpy(str, optarg);

      /* search for mask option */
      if ((strm = index(str, '/'))) {
        ++strm;
        tcp_flags_mask = (u_char)strtol(strm, (char**)0L, 0);
        --strm;
        *strm = 0;
      }

      if (load_lookup(str, 65536, tcp_flags_tbl)) {
        free(str);
        fterr_errx(1, "load_lookup(): failed");
      }

      free(str);
      filter_tcp_flags = 1;
      break;

    case 'x': /* nexthop ip standard access list filter */
      acl_std_nexthop_name = optarg;
      break;

    case 'h': /* help */
    case '?':
      usage();
      exit (0);
      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;

    default:
      usage();
      exit (1);
      break;

    } /* switch */

  if (argc - optind)
    fterr_errx(1, "Extra arguments starting with %s.", argv[optind]);

  /* input from stdin */
  if (ftio_init(&ftio_in, 0, FT_IO_FLAG_READ) < 0)
    fterr_errx(1, "ftio_init(): failed");

  ftio_get_ver(&ftio_in, &ftv);
  ftv.s_version = FT_IO_SVERSION;

  xflag = 0;
  if (filter_input) xflag |= FT_XFIELD_INPUT;
  if (filter_output) xflag |= FT_XFIELD_OUTPUT;
  if (filter_srcport) xflag |= FT_XFIELD_SRCPORT;
  if (filter_dstport) xflag |= FT_XFIELD_DSTPORT;
  if (filter_exaddr) xflag |= FT_XFIELD_EXADDR;
  if (filter_prot) xflag |= FT_XFIELD_PROT;
  if (filter_srcas) xflag |= FT_XFIELD_SRC_AS;
  if (filter_dstas) xflag |= FT_XFIELD_DST_AS;
  if (filter_tos) xflag |= FT_XFIELD_TOS;
  if (filter_tcp_flags) xflag |= FT_XFIELD_TCP_FLAGS;
  if (acl_std_nexthop_name) xflag |= FT_XFIELD_NEXTHOP;
  if (acl_std_src_name) xflag |= FT_XFIELD_SRCADDR;
  if (acl_std_dst_name) xflag |= FT_XFIELD_DSTADDR;
  if (acl_ext_name) xflag |= (FT_XFIELD_SRCADDR | FT_XFIELD_DSTADDR);

  if (ftio_check_xfield(&ftio_in, xflag)) {
    fterr_warnx("Flow record missing required field for format.");
    exit (1);
  }

  fts3rec_compute_offsets(&fo, &ftv);

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

  /* preserve start/end time from input stream? */
  if (keep_input_time) {

    time_start = ftio_get_cap_start(&ftio_in);
    time_end = ftio_get_cap_end(&ftio_in);

    if (time_start && time_end) {

      ftio_set_preloaded(&ftio_out, 1);
      ftio_set_cap_time(&ftio_out, time_start, time_end);

    }

  } /* keep_input_time */

  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);

  if (ftio_set_ver(&ftio_out, &ftv) < 0)
    fterr_errx(1, "ftio_set_ver(): failed");

  /*
   * load acl's
   * XXX add fname check and close
   */
  if ((yyin = fopen(acl_fname ? acl_fname : "flow.acl", "r")))
    while (!feof(yyin))
      yyparse();

  /*
   * normalize masks
   */
  /* XXX TODO */

  if (debug > 5) 
    acl_dump(acl_list);

  if (acl_std_src_name) {
    if ((acl_std_src_index = acl_find(acl_list, acl_std_src_name)) == -1)
      fterr_errx(1, "Couldn't find list %s\n", acl_std_src_name);

    acl_std_src_index2 = acl_list.names[acl_std_src_index].num;

  }

  if (acl_std_dst_name) {
    if ((acl_std_dst_index = acl_find(acl_list, acl_std_dst_name)) == -1)
      fterr_errx(1, "Couldn't find list %s\n", acl_std_dst_name);

    acl_std_dst_index2 = acl_list.names[acl_std_dst_index].num;
  }

  if (acl_ext_name) {
    if ((acl_ext_index = acl_find(acl_list, acl_ext_name)) == -1)
      fterr_errx(1, "Couldn't find list %s\n", acl_ext_name);

    acl_ext_index2 = acl_list.names[acl_ext_index].num;
  }

 if (acl_std_nexthop_name) {
    if ((acl_std_nexthop_index = acl_find(acl_list, acl_std_nexthop_name))
      == -1)
      fterr_errx(1, "Couldn't find list %s\n", acl_std_nexthop_name);

    acl_std_nexthop_index2 = acl_list.names[acl_std_nexthop_index].num;
  }

  /* header first */
  if (ftio_write_header(&ftio_out) < 0)
    fterr_errx(1, "ftio_write_header(): failed");

  /* profile */
  ftprof_start (&ftp);

  /* grab 1 flow */
  while ((rec = ftio_read(&ftio_in))) {

    cur.srcaddr = ((uint32_t*)(rec+fo.srcaddr));
    cur.dstaddr = ((uint32_t*)(rec+fo.dstaddr));
    cur.exaddr = ((uint32_t*)(rec+fo.exaddr));
    cur.nexthop = ((uint32_t*)(rec+fo.nexthop));
    cur.input = ((uint16_t*)(rec+fo.input));
    cur.output = ((uint16_t*)(rec+fo.output));
    cur.srcport = ((uint16_t*)(rec+fo.srcport));
    cur.dstport = ((uint16_t*)(rec+fo.dstport));
    cur.src_as = ((uint16_t*)(rec+fo.src_as));
    cur.dst_as = ((uint16_t*)(rec+fo.dst_as));
    cur.prot = ((uint8_t*)(rec+fo.prot));
    cur.tcp_flags = ((uint8_t*)(rec+fo.tcp_flags));
    cur.tos = ((uint8_t*)(rec+fo.tos));

    ++ total_flows;

    /* filter on input interface */
    if (filter_input) {
      if (!in_tbl[*cur.input]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on output interface */
    if (filter_output) {
      if (!out_tbl[*cur.output]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on src port */
    if (filter_srcport) {
      if (!src_tbl[*cur.srcport]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on dst port */
    if (filter_dstport) {
      if (!dst_tbl[*cur.dstport]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on exporter addr */
    if (filter_exaddr) {
      if (*cur.exaddr != exaddr) {
        if (!first_match)
          continue;
      } else if (first_match) {
          goto found;
      }
    }


    /* filter on protocol */
    if (filter_prot) {
      if (!prot_tbl[*cur.prot]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on ToS */
      if (filter_tos) {
        tos = *cur.tos & tos_mask;
        if (!tos_tbl[tos]) {
          if (!first_match)
            continue;
        } else if (first_match) {
          goto found;
        }
      }

    /* filter on tcp_flags */
      if (filter_tcp_flags && (*cur.prot == IPPROTO_TCP)) {
        tcp_flags = *cur.tcp_flags & tcp_flags_mask;
        if (!tcp_flags_tbl[tcp_flags]) {
          if (!first_match)
            continue;
        } else if (first_match) {
          goto found;
        }
      }

    if (filter_srcas) {
      if (!srcas_tbl[*cur.src_as]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* filter on src AS */
    if (filter_dstas) {
      if (!dstas_tbl[*cur.dst_as]) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

   /* eval flow nexthop addr and nexthop standard acl */
   if (acl_std_nexthop_index != -1) {
      ret = acl_eval_std(acl_list, acl_std_nexthop_index2, *cur.nexthop);
      if (ret == 1) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* eval flow src addr and source standard acl */
    if (acl_std_src_index != -1) {
      ret = acl_eval_std(acl_list, acl_std_src_index2, *cur.srcaddr);
      if (ret == 1) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* eval flow dst addr and destination standard acl */
    if (acl_std_dst_index != -1) {
      ret = acl_eval_std(acl_list, acl_std_dst_index2, *cur.dstaddr);
      if (ret == 1) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    /* eval flow and extended acl */
    if (acl_ext_index != -1) {

      tmp_ext.protocol = *cur.prot;
      tmp_ext.tos = *cur.tos;

      /* XXX */
      tmp_ext.type = 0;
      tmp_ext.type_code = 0;
      tmp_ext.message = 0;

      tmp_ext.src_addr = *cur.srcaddr;
      tmp_ext.src_port = *cur.srcport;


      tmp_ext.dst_addr = *cur.dstaddr;
      tmp_ext.dst_port = *cur.dstport;

      ret = acl_eval_ext(acl_list, acl_ext_index2, tmp_ext);
      if (ret == 1) {
        if (!first_match)
          continue;
      } else if (first_match) {
        goto found;
      }
    }

    if (first_match) /* No matches yet? next try.. */
      continue;

    /*
     * made it by the filters, write it
     */

found:
    if (ftio_write(&ftio_out, rec) < 0)
      fterr_errx(1, "ftio_write(): failed");

  } /* while more flows to read */

  if (ftio_close(&ftio_in) < 0)
    fterr_errx(1, "ftio_close(): failed");

  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);
  }   

  if (debug > 1) {
    acl_dump_std(acl_list, acl_std_src_index);
    acl_dump_std(acl_list, acl_std_dst_index);
    acl_dump_std(acl_list, acl_std_nexthop_index);
  }

  return 0;

} /* main */