755 lines
17 KiB
C
755 lines
17 KiB
C
/* $NetBSD: parse.c,v 1.1.1.1 2004/03/28 08:56:20 martti Exp $ */
|
|
|
|
/*
|
|
* Copyright (C) 1993-2001 by Darren Reed.
|
|
*
|
|
* See the IPFILTER.LICENCE file for details on licencing.
|
|
*
|
|
* Id: parse.c,v 1.34 2003/12/01 01:58:12 darrenr Exp
|
|
*/
|
|
#include <ctype.h>
|
|
#include "ipf.h"
|
|
#include "opts.h"
|
|
|
|
static frentry_t *fp = NULL;
|
|
|
|
/* parse()
|
|
*
|
|
* parse a line read from the input filter rule file
|
|
*/
|
|
struct frentry *parse(line, linenum)
|
|
char *line;
|
|
int linenum;
|
|
{
|
|
static fripf_t fip;
|
|
char *cps[31], **cpp, *endptr, *proto = NULL, *s;
|
|
struct protoent *p = NULL;
|
|
int i, cnt = 1, j;
|
|
u_int k;
|
|
|
|
if (fp == NULL) {
|
|
fp = malloc(sizeof(*fp));
|
|
if (fp == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
while (*line && isspace(*line))
|
|
line++;
|
|
if (!*line)
|
|
return NULL;
|
|
|
|
bzero((char *)fp, sizeof(*fp));
|
|
bzero((char *)&fip, sizeof(fip));
|
|
fp->fr_v = use_inet6 ? 6 : 4;
|
|
fp->fr_ipf = &fip;
|
|
fp->fr_dsize = sizeof(fip);
|
|
fp->fr_ip.fi_v = fp->fr_v;
|
|
fp->fr_mip.fi_v = 0xf;
|
|
fp->fr_type = FR_T_NONE;
|
|
fp->fr_loglevel = 0xffff;
|
|
fp->fr_isc = (void *)-1;
|
|
fp->fr_tag = FR_NOTAG;
|
|
|
|
/*
|
|
* break line up into max of 20 segments
|
|
*/
|
|
if (opts & OPT_DEBUG)
|
|
fprintf(stderr, "parse [%s]\n", line);
|
|
for (i = 0, *cps = strtok(line, " \b\t\r\n"); cps[i] && i < 30; cnt++)
|
|
cps[++i] = strtok(NULL, " \b\t\r\n");
|
|
cps[i] = NULL;
|
|
|
|
if (cnt < 3) {
|
|
fprintf(stderr, "%d: not enough segments in line\n", linenum);
|
|
return NULL;
|
|
}
|
|
|
|
cpp = cps;
|
|
/*
|
|
* The presence of an '@' followed by a number gives the position in
|
|
* the current rule list to insert this one.
|
|
*/
|
|
if (**cpp == '@')
|
|
fp->fr_hits = (U_QUAD_T)atoi(*cpp++ + 1) + 1;
|
|
|
|
/*
|
|
* Check the first keyword in the rule and any options that are
|
|
* expected to follow it.
|
|
*/
|
|
if (!strcasecmp("block", *cpp)) {
|
|
fp->fr_flags |= FR_BLOCK;
|
|
if (!strncasecmp(*(cpp+1), "return-icmp-as-dest", 19) &&
|
|
(i = 19))
|
|
fp->fr_flags |= FR_FAKEICMP;
|
|
else if (!strncasecmp(*(cpp+1), "return-icmp", 11) && (i = 11))
|
|
fp->fr_flags |= FR_RETICMP;
|
|
if (fp->fr_flags & FR_RETICMP) {
|
|
cpp++;
|
|
if (strlen(*cpp) == i) {
|
|
if (*(cpp + 1) && **(cpp +1) == '(') {
|
|
cpp++;
|
|
i = 0;
|
|
} else
|
|
i = -1;
|
|
}
|
|
|
|
/*
|
|
* The ICMP code is not required to follow in ()'s
|
|
*/
|
|
if ((i >= 0) && (*(*cpp + i) == '(')) {
|
|
i++;
|
|
j = icmpcode(*cpp + i);
|
|
if (j == -1) {
|
|
fprintf(stderr,
|
|
"%d: unrecognised icmp code %s\n",
|
|
linenum, *cpp + 20);
|
|
return NULL;
|
|
}
|
|
fp->fr_icode = j;
|
|
}
|
|
} else if (!strncasecmp(*(cpp+1), "return-rst", 10)) {
|
|
fp->fr_flags |= FR_RETRST;
|
|
cpp++;
|
|
}
|
|
} else if (!strcasecmp("count", *cpp)) {
|
|
fp->fr_flags |= FR_ACCOUNT;
|
|
} else if (!strcasecmp("pass", *cpp)) {
|
|
fp->fr_flags |= FR_PASS;
|
|
} else if (!strcasecmp("auth", *cpp)) {
|
|
fp->fr_flags |= FR_AUTH;
|
|
} else if (fp->fr_arg != 0) {
|
|
printf("skip %u", fp->fr_arg);
|
|
} else if (!strcasecmp("preauth", *cpp)) {
|
|
fp->fr_flags |= FR_PREAUTH;
|
|
} else if (!strcasecmp("nomatch", *cpp)) {
|
|
fp->fr_flags |= FR_NOMATCH;
|
|
} else if (!strcasecmp("skip", *cpp)) {
|
|
cpp++;
|
|
if (ratoui(*cpp, &k, 0, UINT_MAX))
|
|
fp->fr_arg = k;
|
|
else {
|
|
fprintf(stderr, "%d: integer must follow skip\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
} else if (!strcasecmp("log", *cpp)) {
|
|
fp->fr_flags |= FR_LOG;
|
|
if (!strcasecmp(*(cpp+1), "body")) {
|
|
fp->fr_flags |= FR_LOGBODY;
|
|
cpp++;
|
|
}
|
|
if (!strcasecmp(*(cpp+1), "first")) {
|
|
fp->fr_flags |= FR_LOGFIRST;
|
|
cpp++;
|
|
}
|
|
if (*cpp && !strcasecmp(*(cpp+1), "or-block")) {
|
|
fp->fr_flags |= FR_LOGORBLOCK;
|
|
cpp++;
|
|
}
|
|
if (!strcasecmp(*(cpp+1), "level")) {
|
|
cpp++;
|
|
if (loglevel(cpp, &fp->fr_loglevel, linenum) == -1)
|
|
return NULL;
|
|
cpp++;
|
|
}
|
|
} else {
|
|
/*
|
|
* Doesn't start with one of the action words
|
|
*/
|
|
fprintf(stderr, "%d: unknown keyword (%s)\n", linenum, *cpp);
|
|
return NULL;
|
|
}
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: missing 'in'/'out' keyword\n", linenum);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Get the direction for filtering. Impose restrictions on direction
|
|
* if blocking with returning ICMP or an RST has been requested.
|
|
*/
|
|
if (!strcasecmp("in", *cpp))
|
|
fp->fr_flags |= FR_INQUE;
|
|
else if (!strcasecmp("out", *cpp)) {
|
|
fp->fr_flags |= FR_OUTQUE;
|
|
if (fp->fr_flags & FR_RETICMP) {
|
|
fprintf(stderr,
|
|
"%d: Can only use return-icmp with 'in'\n",
|
|
linenum);
|
|
return NULL;
|
|
} else if (fp->fr_flags & FR_RETRST) {
|
|
fprintf(stderr,
|
|
"%d: Can only use return-rst with 'in'\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
}
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: missing source specification\n", linenum);
|
|
return NULL;
|
|
}
|
|
|
|
if (!strcasecmp("log", *cpp)) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: missing source specification\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
if (FR_ISPASS(fp->fr_flags))
|
|
fp->fr_flags |= FR_LOGP;
|
|
else if (FR_ISBLOCK(fp->fr_flags))
|
|
fp->fr_flags |= FR_LOGB;
|
|
if (*cpp && !strcasecmp(*cpp, "body")) {
|
|
fp->fr_flags |= FR_LOGBODY;
|
|
cpp++;
|
|
}
|
|
if (*cpp && !strcasecmp(*cpp, "first")) {
|
|
fp->fr_flags |= FR_LOGFIRST;
|
|
cpp++;
|
|
}
|
|
if (*cpp && !strcasecmp(*cpp, "or-block")) {
|
|
if (!FR_ISPASS(fp->fr_flags)) {
|
|
fprintf(stderr,
|
|
"%d: or-block must be used with pass\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_flags |= FR_LOGORBLOCK;
|
|
cpp++;
|
|
}
|
|
if (*cpp && !strcasecmp(*cpp, "level")) {
|
|
if (loglevel(cpp, &fp->fr_loglevel, linenum) == -1)
|
|
return NULL;
|
|
cpp++;
|
|
cpp++;
|
|
}
|
|
}
|
|
|
|
if (*cpp && !strcasecmp("quick", *cpp)) {
|
|
if (fp->fr_arg != 0) {
|
|
fprintf(stderr, "%d: cannot use skip with quick\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
cpp++;
|
|
fp->fr_flags |= FR_QUICK;
|
|
}
|
|
|
|
/*
|
|
* Parse rule options that are available if a rule is tied to an
|
|
* interface.
|
|
*/
|
|
*fp->fr_ifname = '\0';
|
|
*fp->fr_oifname = '\0';
|
|
if (*cpp && !strcasecmp(*cpp, "on")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: interface name missing\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
(void)strncpy(fp->fr_ifname, *cpp, IFNAMSIZ-1);
|
|
fp->fr_ifname[IFNAMSIZ-1] = '\0';
|
|
cpp++;
|
|
if (!*cpp) {
|
|
if ((fp->fr_flags & FR_RETMASK) == FR_RETRST) {
|
|
fprintf(stderr,
|
|
"%d: %s can only be used with TCP\n",
|
|
linenum, "return-rst");
|
|
return NULL;
|
|
}
|
|
return fp;
|
|
}
|
|
|
|
if (!strcasecmp(*cpp, "out-via")) {
|
|
if (fp->fr_flags & FR_OUTQUE) {
|
|
fprintf(stderr,
|
|
"out-via must be used with in\n");
|
|
return NULL;
|
|
}
|
|
cpp++;
|
|
(void)strncpy(fp->fr_oifname, *cpp, IFNAMSIZ-1);
|
|
fp->fr_oifname[IFNAMSIZ-1] = '\0';
|
|
cpp++;
|
|
} else if (!strcasecmp(*cpp, "in-via")) {
|
|
if (fp->fr_flags & FR_INQUE) {
|
|
fprintf(stderr,
|
|
"in-via must be used with out\n");
|
|
return NULL;
|
|
}
|
|
cpp++;
|
|
(void)strncpy(fp->fr_oifname, *cpp, IFNAMSIZ-1);
|
|
fp->fr_oifname[IFNAMSIZ-1] = '\0';
|
|
cpp++;
|
|
}
|
|
|
|
if (!strcasecmp(*cpp, "dup-to") && *(cpp + 1)) {
|
|
cpp++;
|
|
if (to_interface(&fp->fr_dif, *cpp, linenum))
|
|
return NULL;
|
|
cpp++;
|
|
}
|
|
if (*cpp && !strcasecmp(*cpp, "to") && *(cpp + 1)) {
|
|
cpp++;
|
|
if (to_interface(&fp->fr_tif, *cpp, linenum))
|
|
return NULL;
|
|
cpp++;
|
|
} else if (*cpp && !strcasecmp(*cpp, "fastroute")) {
|
|
if (!(fp->fr_flags & FR_INQUE)) {
|
|
fprintf(stderr,
|
|
"can only use %s with 'in'\n",
|
|
"fastroute");
|
|
return NULL;
|
|
}
|
|
fp->fr_flags |= FR_FASTROUTE;
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* Set the "other" interface name. Lets you specify both
|
|
* inbound and outbound interfaces for state rules. Do not
|
|
* prevent both interfaces from being the same.
|
|
*/
|
|
strcpy(fp->fr_ifnames[3], "*");
|
|
if ((*cpp != NULL) && (*(cpp + 1) != NULL) &&
|
|
((((fp->fr_flags & FR_INQUE) != 0) &&
|
|
(strcasecmp(*cpp, "out-via") == 0)) ||
|
|
(((fp->fr_flags & FR_OUTQUE) != 0) &&
|
|
(strcasecmp(*cpp, "in-via") == 0)))) {
|
|
cpp++;
|
|
|
|
s = strchr(*cpp, ',');
|
|
if (s != NULL) {
|
|
*s++ = '\0';
|
|
(void)strncpy(fp->fr_ifnames[3], s,
|
|
IFNAMSIZ - 1);
|
|
fp->fr_ifnames[3][IFNAMSIZ - 1] = '\0';
|
|
}
|
|
|
|
(void)strncpy(fp->fr_ifnames[2], *cpp, IFNAMSIZ - 1);
|
|
fp->fr_ifnames[2][IFNAMSIZ - 1] = '\0';
|
|
cpp++;
|
|
} else
|
|
strcpy(fp->fr_ifnames[2], "*");
|
|
|
|
}
|
|
|
|
if (*cpp && !strcasecmp(*cpp, "tos")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: tos missing value\n", linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_tos = strtol(*cpp, NULL, 0);
|
|
fp->fr_mip.fi_tos = 0xff;
|
|
cpp++;
|
|
}
|
|
|
|
if (*cpp && !strcasecmp(*cpp, "ttl")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: ttl missing hopcount value\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
if (ratoi(*cpp, &i, 0, 255))
|
|
fp->fr_ttl = i;
|
|
else {
|
|
fprintf(stderr, "%d: invalid ttl (%s)\n",
|
|
linenum, *cpp);
|
|
return NULL;
|
|
}
|
|
fp->fr_mip.fi_ttl = 0xff;
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* check for "proto <protoname>" only decode udp/tcp/icmp as protoname
|
|
*/
|
|
if (*cpp && !strcasecmp(*cpp, "proto")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: protocol name missing\n", linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_type = FR_T_IPF;
|
|
proto = *cpp++;
|
|
if (!strcasecmp(proto, "tcp/udp")) {
|
|
fp->fr_flx |= FI_TCPUDP;
|
|
fp->fr_mflx |= FI_TCPUDP;
|
|
} else if (use_inet6 && !strcasecmp(proto, "icmp")) {
|
|
fprintf(stderr,
|
|
"%d: use proto ipv6-icmp with IPv6 (or use proto 1 if you really mean icmp)\n",
|
|
linenum);
|
|
return NULL;
|
|
} else {
|
|
fp->fr_proto = getproto(proto);
|
|
fp->fr_mip.fi_p = 0xff;
|
|
}
|
|
}
|
|
if ((fp->fr_proto != IPPROTO_TCP) &&
|
|
((fp->fr_flags & FR_RETMASK) == FR_RETRST)) {
|
|
fprintf(stderr, "%d: %s can only be used with TCP\n",
|
|
linenum, "return-rst");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* get the from host and bit mask to use against packets
|
|
*/
|
|
|
|
if (!*cpp) {
|
|
fprintf(stderr, "%d: missing source specification\n", linenum);
|
|
return NULL;
|
|
}
|
|
if (!strcasecmp(*cpp, "all")) {
|
|
cpp++;
|
|
if (!*cpp) {
|
|
if (fp->fr_type == FR_T_NONE) {
|
|
fp->fr_dsize = 0;
|
|
fp->fr_data = NULL;
|
|
}
|
|
return fp;
|
|
}
|
|
fp->fr_type = FR_T_IPF;
|
|
#ifdef IPFILTER_BPF
|
|
} else if (!strcmp(*cpp, "{")) {
|
|
struct bpf_program bpf;
|
|
struct pcap *p;
|
|
char **cp;
|
|
u_32_t l;
|
|
|
|
if (fp->fr_type != FR_T_NONE) {
|
|
fprintf(stderr,
|
|
"%d: cannot mix BPF/ipf matching\n", linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_type = FR_T_BPFOPC;
|
|
cpp++;
|
|
if (!strncmp(*cpp, "0x", 2)) {
|
|
fp->fr_data = malloc(4);
|
|
for (cp = cpp, i = 0; *cp; cp++, i++) {
|
|
if (!strcmp(*cp, "}"))
|
|
break;
|
|
fp->fr_data = realloc(fp->fr_data,
|
|
(i + 1) * 4);
|
|
l = strtoul(*cp, NULL, 0);
|
|
((u_32_t *)fp->fr_data)[i] = l;
|
|
}
|
|
if (!*cp) {
|
|
fprintf(stderr, "Missing closing '}'\n");
|
|
return NULL;
|
|
}
|
|
fp->fr_dsize = i * sizeof(l);
|
|
bpf.bf_insns = fp->fr_data;
|
|
bpf.bf_len = fp->fr_dsize / sizeof(struct bpf_insn);
|
|
} else {
|
|
for (cp = cpp; *cp; cp++) {
|
|
if (!strcmp(*cp, "}"))
|
|
break;
|
|
(*cp)[-1] = ' ';
|
|
}
|
|
if (!*cp) {
|
|
fprintf(stderr, "Missing closing '}'\n");
|
|
return NULL;
|
|
}
|
|
|
|
bzero((char *)&bpf, sizeof(bpf));
|
|
p = pcap_open_dead(DLT_RAW, 1);
|
|
if (!p) {
|
|
fprintf(stderr, "pcap_open_dead failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (pcap_compile(p, &bpf, *cpp, 1, 0xffffffff)) {
|
|
pcap_perror(p, "ipf");
|
|
pcap_close(p);
|
|
fprintf(stderr, "pcap parsing failed\n");
|
|
return NULL;
|
|
}
|
|
pcap_close(p);
|
|
fp->fr_dsize = bpf.bf_len * sizeof(struct bpf_insn);
|
|
fp->fr_data = bpf.bf_insns;
|
|
if (!bpf_validate(fp->fr_data, bpf.bf_len)) {
|
|
fprintf(stderr, "BPF validation failed\n");
|
|
return NULL;
|
|
}
|
|
if (opts & OPT_DEBUG)
|
|
bpf_dump(&bpf, 0);
|
|
}
|
|
cpp = cp;
|
|
(*cpp)++;
|
|
#endif
|
|
} else {
|
|
fp->fr_type = FR_T_IPF;
|
|
|
|
if (strcasecmp(*cpp, "from")) {
|
|
fprintf(stderr, "%d: unexpected keyword (%s) - from\n",
|
|
linenum, *cpp);
|
|
return NULL;
|
|
}
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: missing host after from\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
if (**cpp == '!') {
|
|
fp->fr_flags |= FR_NOTSRCIP;
|
|
(*cpp)++;
|
|
} else if (!strcmp(*cpp, "!")) {
|
|
fp->fr_flags |= FR_NOTSRCIP;
|
|
cpp++;
|
|
}
|
|
|
|
s = *cpp;
|
|
i = hostmask(&cpp, proto, fp->fr_ifname, (u_32_t *)&fp->fr_src,
|
|
(u_32_t *)&fp->fr_smsk, linenum);
|
|
if (i == -1)
|
|
return NULL;
|
|
if (*fp->fr_ifname && !strcasecmp(s, fp->fr_ifname))
|
|
fp->fr_satype = FRI_DYNAMIC;
|
|
if (i == 1) {
|
|
if (fp->fr_v == 6) {
|
|
fprintf(stderr,
|
|
"can only use pools with ipv4\n");
|
|
return NULL;
|
|
}
|
|
fp->fr_satype = FRI_LOOKUP;
|
|
}
|
|
|
|
if (ports(&cpp, proto, &fp->fr_sport, &fp->fr_scmp,
|
|
&fp->fr_stop, linenum))
|
|
return NULL;
|
|
|
|
if (!*cpp) {
|
|
fprintf(stderr, "%d: missing to fields\n", linenum);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* do the same for the to field (destination host)
|
|
*/
|
|
if (strcasecmp(*cpp, "to")) {
|
|
fprintf(stderr, "%d: unexpected keyword (%s) - to\n",
|
|
linenum, *cpp);
|
|
return NULL;
|
|
}
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: missing host after to\n", linenum);
|
|
return NULL;
|
|
}
|
|
|
|
if (**cpp == '!') {
|
|
fp->fr_flags |= FR_NOTDSTIP;
|
|
(*cpp)++;
|
|
} else if (!strcmp(*cpp, "!")) {
|
|
fp->fr_flags |= FR_NOTDSTIP;
|
|
cpp++;
|
|
}
|
|
|
|
s = *cpp;
|
|
i = hostmask(&cpp, proto, fp->fr_ifname, (u_32_t *)&fp->fr_dst,
|
|
(u_32_t *)&fp->fr_dmsk, linenum);
|
|
if (i == -1)
|
|
return NULL;
|
|
if (*fp->fr_ifname && !strcasecmp(s, fp->fr_ifname))
|
|
fp->fr_datype = FRI_DYNAMIC;
|
|
if (i == 1) {
|
|
if (fp->fr_v == 6) {
|
|
fprintf(stderr,
|
|
"can only use pools with ipv4\n");
|
|
return NULL;
|
|
}
|
|
fp->fr_datype = FRI_LOOKUP;
|
|
}
|
|
|
|
if (ports(&cpp, proto, &fp->fr_dport, &fp->fr_dcmp,
|
|
&fp->fr_dtop, linenum))
|
|
return NULL;
|
|
}
|
|
|
|
if (fp->fr_type == FR_T_IPF) {
|
|
/*
|
|
* check some sanity, make sure we don't have icmp checks
|
|
* with tcp or udp or visa versa.
|
|
*/
|
|
if (fp->fr_proto && (fp->fr_dcmp || fp->fr_scmp) &&
|
|
fp->fr_proto != IPPROTO_TCP &&
|
|
fp->fr_proto != IPPROTO_UDP) {
|
|
fprintf(stderr,
|
|
"%d: port operation on non tcp/udp\n",linenum);
|
|
return NULL;
|
|
}
|
|
if (fp->fr_icmp && fp->fr_proto != IPPROTO_ICMP) {
|
|
fprintf(stderr,
|
|
"%d: icmp comparisons on wrong protocol\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
|
|
if (!*cpp)
|
|
return fp;
|
|
|
|
if (*cpp && (fp->fr_type == FR_T_IPF) &&
|
|
!strcasecmp(*cpp, "flags")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: no flags present\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_tcpf = tcp_flags(*cpp, &fp->fr_tcpfm, linenum);
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* extras...
|
|
*/
|
|
if ((fp->fr_v == 4) && *cpp && (!strcasecmp(*cpp, "with") ||
|
|
!strcasecmp(*cpp, "and")))
|
|
if (extras(&cpp, fp, linenum))
|
|
return NULL;
|
|
|
|
/*
|
|
* icmp types for use with the icmp protocol
|
|
*/
|
|
if (*cpp && !strcasecmp(*cpp, "icmp-type")) {
|
|
if (fp->fr_proto != IPPROTO_ICMP &&
|
|
fp->fr_proto != IPPROTO_ICMPV6) {
|
|
fprintf(stderr,
|
|
"%d: icmp with wrong protocol (%d)\n",
|
|
linenum, fp->fr_proto);
|
|
return NULL;
|
|
}
|
|
if (addicmp(&cpp, fp, linenum))
|
|
return NULL;
|
|
fp->fr_icmp = htons(fp->fr_icmp);
|
|
fp->fr_icmpm = htons(fp->fr_icmpm);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Keep something...
|
|
*/
|
|
while (*cpp && !strcasecmp(*cpp, "keep"))
|
|
if (addkeep(&cpp, fp, linenum))
|
|
return NULL;
|
|
|
|
/*
|
|
* This is here to enforce the old interface binding behaviour.
|
|
* That is, "on X" is equivalent to "<dir> on X <!dir>-via -,X"
|
|
*/
|
|
if (fp->fr_flags & FR_KEEPSTATE) {
|
|
if (*fp->fr_ifnames[0] && !*fp->fr_ifnames[3]) {
|
|
bcopy(fp->fr_ifnames[0], fp->fr_ifnames[3],
|
|
sizeof(fp->fr_ifnames[3]));
|
|
strncpy(fp->fr_ifnames[2], "*",
|
|
sizeof(fp->fr_ifnames[3]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* head of a new group ?
|
|
*/
|
|
if (*cpp && !strcasecmp(*cpp, "head")) {
|
|
if (fp->fr_arg != 0) {
|
|
fprintf(stderr, "%d: cannot use skip with head\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: head without group #\n", linenum);
|
|
return NULL;
|
|
}
|
|
if (strlen(*cpp) > FR_GROUPLEN) {
|
|
fprintf(stderr, "%d: head name too long #\n", linenum);
|
|
return NULL;
|
|
}
|
|
strncpy(fp->fr_grhead, *cpp, FR_GROUPLEN);
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* reference to an already existing group ?
|
|
*/
|
|
if (*cpp && !strcasecmp(*cpp, "group")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: group without group #\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
if (strlen(*cpp) > FR_GROUPLEN) {
|
|
fprintf(stderr, "%d: group name too long #\n", linenum);
|
|
return NULL;
|
|
}
|
|
strncpy(fp->fr_group, *cpp, FR_GROUPLEN);
|
|
cpp++;
|
|
}
|
|
|
|
if (*cpp && !strcasecmp(*cpp, "tag")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: tag id missing value\n", linenum);
|
|
return NULL;
|
|
}
|
|
fp->fr_tag = strtol(*cpp, NULL, 0);
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* pps counter
|
|
*/
|
|
if (*cpp && !strcasecmp(*cpp, "pps")) {
|
|
if (!*++cpp) {
|
|
fprintf(stderr, "%d: pps without rate\n", linenum);
|
|
return NULL;
|
|
}
|
|
if (ratoui(*cpp, &k, 0, INT_MAX))
|
|
fp->fr_pps = k;
|
|
else {
|
|
fprintf(stderr, "%d: invalid pps rate (%s)\n",
|
|
linenum, *cpp);
|
|
return NULL;
|
|
}
|
|
cpp++;
|
|
}
|
|
|
|
/*
|
|
* leftovers...yuck
|
|
*/
|
|
if (*cpp && **cpp) {
|
|
fprintf(stderr, "%d: unknown words at end: [", linenum);
|
|
for (; *cpp; cpp++)
|
|
fprintf(stderr, "%s ", *cpp);
|
|
fprintf(stderr, "]\n");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* lazy users...
|
|
*/
|
|
if (fp->fr_type == FR_T_IPF) {
|
|
if ((fp->fr_tcpf || fp->fr_tcpfm) &&
|
|
(fp->fr_proto != IPPROTO_TCP)) {
|
|
fprintf(stderr,
|
|
"%d: TCP protocol not specified\n", linenum);
|
|
return NULL;
|
|
}
|
|
if (!(fp->fr_flx & FI_TCPUDP) &&
|
|
(fp->fr_proto != IPPROTO_TCP) &&
|
|
(fp->fr_proto != IPPROTO_UDP) &&
|
|
(fp->fr_dcmp || fp->fr_scmp)) {
|
|
if (!fp->fr_proto) {
|
|
fp->fr_flx |= FI_TCPUDP;
|
|
fp->fr_mflx |= FI_TCPUDP;
|
|
} else {
|
|
fprintf(stderr,
|
|
"%d: port check for non-TCP/UDP\n",
|
|
linenum);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
if (*fp->fr_oifname && strcmp(fp->fr_oifname, "*") &&
|
|
!(fp->fr_flags & FR_KEEPSTATE)) {
|
|
fprintf(stderr, "%d: *-via <if> must be used %s\n",
|
|
linenum, "with keep-state");
|
|
return NULL;
|
|
}
|
|
return fp;
|
|
}
|