summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2013-04-12 20:48:55 +0000
committersjg <sjg@FreeBSD.org>2013-04-12 20:48:55 +0000
commit97d8b9495668afa398ab17c8c5f7e223b5fd2e89 (patch)
tree54038c9ac32a45f8741dcc23fb9a8ffc0e15ff89 /sbin/ipfw
parent5ee3bfdb338e7c80af29a67f4425c4be24c7b866 (diff)
parent086d73aef6d0ab7d21daa2076fdc8d25961f9b05 (diff)
downloadFreeBSD-src-97d8b9495668afa398ab17c8c5f7e223b5fd2e89.zip
FreeBSD-src-97d8b9495668afa398ab17c8c5f7e223b5fd2e89.tar.gz
sync from head
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/ipfw.885
-rw-r--r--sbin/ipfw/ipfw2.c455
-rw-r--r--sbin/ipfw/ipfw2.h9
-rw-r--r--sbin/ipfw/ipv6.c25
4 files changed, 452 insertions, 122 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index db0dfc0..2047385 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -854,7 +854,7 @@ So, to prevent endless loops in case of mistakes, both
and
.Cm return
actions don't do any jumps and simply go to the next rule if memory
-can't be allocated or stack overflowed/undeflowed.
+cannot be allocated or stack overflowed/underflowed.
.Pp
Internally stack for rule numbers is implemented using
.Xr mbuf_tags 9
@@ -960,6 +960,61 @@ It is possible to use the
keyword with setfib.
If the tablearg value is not within the compiled range of fibs,
the packet's fib is set to 0.
+.It Cm setdscp Ar DSCP | number | tablearg
+Set specified DiffServ codepoint for an IPv4/IPv6 packet.
+Processing continues at the next rule.
+Supported values are:
+.Pp
+.Cm CS0
+.Pq Dv 000000 ,
+.Cm CS1
+.Pq Dv 001000 ,
+.Cm CS2
+.Pq Dv 010000 ,
+.Cm CS3
+.Pq Dv 011000 ,
+.Cm CS4
+.Pq Dv 100000 ,
+.Cm CS5
+.Pq Dv 101000 ,
+.Cm CS6
+.Pq Dv 110000 ,
+.Cm CS7
+.Pq Dv 111000 ,
+.Cm AF11
+.Pq Dv 001010 ,
+.Cm AF12
+.Pq Dv 001100 ,
+.Cm AF13
+.Pq Dv 001110 ,
+.Cm AF21
+.Pq Dv 010010 ,
+.Cm AF22
+.Pq Dv 010100 ,
+.Cm AF23
+.Pq Dv 010110 ,
+.Cm AF31
+.Pq Dv 011010 ,
+.Cm AF32
+.Pq Dv 011100 ,
+.Cm AF33
+.Pq Dv 011110 ,
+.Cm AF41
+.Pq Dv 100010 ,
+.Cm AF42
+.Pq Dv 100100 ,
+.Cm AF43
+.Pq Dv 100110 ,
+.Cm EF
+.Pq Dv 101110 ,
+.Cm BE
+.Pq Dv 000000 .
+Additionally, DSCP value can be specified by number (0..64).
+It is also possible to use the
+.Cm tablearg
+keyword with setdscp.
+If the tablearg value is not within the 0..64 range, lower 6 bits of supplied
+value are used.
.It Cm reass
Queue and reassemble IP fragments.
If the packet is not fragmented, counters are updated and
@@ -1454,6 +1509,17 @@ The supported IP types of service are:
The absence of a particular type may be denoted
with a
.Ql \&! .
+.It Cm dscp spec Ns Op , Ns Ar spec
+Matches IPv4/IPv6 packets whose
+.Cm DS
+field value is contained in
+.Ar spec
+mask.
+Multiple values can be specified via
+the comma separated list.
+Value can be one of keywords used in
+.Cm setdscp
+action or exact number.
.It Cm ipttl Ar ttl-list
Matches IPv4 packets whose time to live is included in
.Ar ttl-list ,
@@ -2976,6 +3042,23 @@ configured on
but coming in on
.Li fxp1
would be dropped.
+.Pp
+The
+.Cm setdscp
+option could be used to (re)mark user traffic,
+by adding the following to the appropriate place in ruleset:
+.Pp
+.Dl "ipfw add setdscp be ip from any to any dscp af11,af21"
+.Pp
+This rule drops all incoming packets that appear to be coming from another
+directly connected system but on the wrong interface.
+For example, a packet with a source address of
+.Li 192.168.0.0/24 ,
+configured on
+.Li fxp0 ,
+but coming in on
+.Li fxp1
+would be dropped.
.Ss DYNAMIC RULES
In order to protect a site from flood attacks involving fake
TCP packets, it is safer to use dynamic rules:
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 530f319..5b37995 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -64,6 +64,22 @@ int ipfw_socket = -1;
#define s6_addr32 __u6_addr.__u6_addr32
#endif
+#define CHECK_LENGTH(v, len) do { \
+ if ((v) < (len)) \
+ errx(EX_DATAERR, "Rule too long"); \
+ } while (0)
+/*
+ * Check if we have enough space in cmd buffer. Note that since
+ * first 8? u32 words are reserved by reserved header, full cmd
+ * buffer can't be used, so we need to protect from buffer overrun
+ * only. At the beginnig, cblen is less than actual buffer size by
+ * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need
+ * for checking small instructions fitting in given range.
+ * We also (ab)use the fact that ipfw_insn is always the first field
+ * for any custom instruction.
+ */
+#define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd))
+
#define GET_UINT_ARG(arg, min, max, tok, s_x) do { \
if (!av[0]) \
errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
@@ -151,6 +167,32 @@ static struct _s_x f_iptos[] = {
{ NULL, 0 }
};
+static struct _s_x f_ipdscp[] = {
+ { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */
+ { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */
+ { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */
+ { "af21", IPTOS_DSCP_AF21 >> 2 }, /* 010010 */
+ { "af22", IPTOS_DSCP_AF22 >> 2 }, /* 010100 */
+ { "af23", IPTOS_DSCP_AF23 >> 2 }, /* 010110 */
+ { "af31", IPTOS_DSCP_AF31 >> 2 }, /* 011010 */
+ { "af32", IPTOS_DSCP_AF32 >> 2 }, /* 011100 */
+ { "af33", IPTOS_DSCP_AF33 >> 2 }, /* 011110 */
+ { "af41", IPTOS_DSCP_AF41 >> 2 }, /* 100010 */
+ { "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */
+ { "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */
+ { "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
+ { "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */
+ { "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
+ { "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */
+ { "cs2", IPTOS_DSCP_CS2 >> 2 }, /* 010000 */
+ { "cs3", IPTOS_DSCP_CS3 >> 2 }, /* 011000 */
+ { "cs4", IPTOS_DSCP_CS4 >> 2 }, /* 100000 */
+ { "cs5", IPTOS_DSCP_CS5 >> 2 }, /* 101000 */
+ { "cs6", IPTOS_DSCP_CS6 >> 2 }, /* 110000 */
+ { "cs7", IPTOS_DSCP_CS7 >> 2 }, /* 100000 */
+ { NULL, 0 }
+};
+
static struct _s_x limit_masks[] = {
{"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT},
{"src-addr", DYN_SRC_ADDR},
@@ -221,6 +263,7 @@ static struct _s_x rule_actions[] = {
{ "nat", TOK_NAT },
{ "reass", TOK_REASS },
{ "setfib", TOK_SETFIB },
+ { "setdscp", TOK_SETDSCP },
{ "call", TOK_CALL },
{ "return", TOK_RETURN },
{ NULL, 0 } /* terminator */
@@ -653,7 +696,7 @@ strtoport(char *s, char **end, int base, int proto)
* Fill the body of the command with the list of port ranges.
*/
static int
-fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
+fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen)
{
uint16_t a, b, *p = cmd->ports;
int i = 0;
@@ -664,6 +707,8 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
if (s == av) /* empty or invalid argument */
return (0);
+ CHECK_LENGTH(cblen, i + 2);
+
switch (*s) {
case '-': /* a range */
av = s + 1;
@@ -696,6 +741,51 @@ fill_newports(ipfw_insn_u16 *cmd, char *av, int proto)
return (i);
}
+/*
+ * Fill the body of the command with the list of DiffServ codepoints.
+ */
+static void
+fill_dscp(ipfw_insn *cmd, char *av, int cblen)
+{
+ uint32_t *low, *high;
+ char *s = av, *a;
+ int code;
+
+ cmd->opcode = O_DSCP;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1;
+
+ CHECK_CMDLEN;
+
+ low = (uint32_t *)(cmd + 1);
+ high = low + 1;
+
+ *low = 0;
+ *high = 0;
+
+ while (s != NULL) {
+ a = strchr(s, ',');
+
+ if (a != NULL)
+ *a++ = '\0';
+
+ if (isalpha(*s)) {
+ if ((code = match_token(f_ipdscp, s)) == -1)
+ errx(EX_DATAERR, "Unknown DSCP code");
+ } else {
+ code = strtoul(s, NULL, 10);
+ if (code < 0 || code > 63)
+ errx(EX_DATAERR, "Invalid DSCP value");
+ }
+
+ if (code > 32)
+ *high |= 1 << (code - 32);
+ else
+ *low |= 1 << code;
+
+ s = a;
+ }
+}
+
static struct _s_x icmpcodes[] = {
{ "net", ICMP_UNREACH_NET },
{ "host", ICMP_UNREACH_HOST },
@@ -954,6 +1044,32 @@ print_icmptypes(ipfw_insn_u32 *cmd)
}
}
+static void
+print_dscp(ipfw_insn_u32 *cmd)
+{
+ int i, c;
+ uint32_t *v;
+ char sep= ' ';
+ const char *code;
+
+ printf(" dscp");
+ i = 0;
+ c = 0;
+ v = cmd->d;
+ while (i < 64) {
+ if (*v & (1 << i)) {
+ if ((code = match_value(f_ipdscp, i)) != NULL)
+ printf("%c%s", sep, code);
+ else
+ printf("%c%d", sep, i);
+ sep = ',';
+ }
+
+ if ((++i % 32) == 0)
+ v++;
+ }
+}
+
/*
* show_ipfw() prints the body of an ipfw rule.
* Because the standard rule has at least proto src_ip dst_ip, we use
@@ -1187,6 +1303,17 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
PRINT_UINT_ARG("setfib ", cmd->arg1);
break;
+ case O_SETDSCP:
+ {
+ const char *code;
+
+ if ((code = match_value(f_ipdscp, cmd->arg1)) != NULL)
+ printf("setdscp %s", code);
+ else
+ PRINT_UINT_ARG("setdscp ", cmd->arg1);
+ }
+ break;
+
case O_REASS:
printf("reass");
break;
@@ -1482,6 +1609,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
printf(" ipprecedence %u", (cmd->arg1) >> 5 );
break;
+ case O_DSCP:
+ print_dscp((ipfw_insn_u32 *)cmd);
+ break;
+
case O_IPLEN:
if (F_LEN(cmd) == 1)
printf(" iplen %u", cmd->arg1 );
@@ -2068,7 +2199,7 @@ lookup_host (char *host, struct in_addr *ipaddr)
* We can have multiple comma-separated address/mask entries.
*/
static void
-fill_ip(ipfw_insn_ip *cmd, char *av)
+fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
{
int len = 0;
uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
@@ -2108,6 +2239,8 @@ fill_ip(ipfw_insn_ip *cmd, char *av)
int masklen;
char md, nd = '\0';
+ CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn) + 2 + len);
+
if (p) {
md = *p;
*p++ = '\0';
@@ -2366,11 +2499,13 @@ ipfw_delete(char *av[])
* patterns which match interfaces.
*/
static void
-fill_iface(ipfw_insn_if *cmd, char *arg)
+fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
{
cmd->name[0] = '\0';
cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
+ CHECK_CMDLEN;
+
/* Parse the interface or address */
if (strcmp(arg, "any") == 0)
cmd->o.len = 0; /* effectively ignore this command */
@@ -2441,8 +2576,10 @@ get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask)
* the new command in case it has been clobbered before.
*/
static ipfw_insn *
-next_cmd(ipfw_insn *cmd)
+next_cmd(ipfw_insn *cmd, int *len)
{
+ *len -= F_LEN(cmd);
+ CHECK_LENGTH(*len, 0);
cmd += F_LEN(cmd);
bzero(cmd, sizeof(*cmd));
return cmd;
@@ -2452,7 +2589,7 @@ next_cmd(ipfw_insn *cmd)
* Takes arguments and copies them into a comment
*/
static void
-fill_comment(ipfw_insn *cmd, char **av)
+fill_comment(ipfw_insn *cmd, char **av, int cblen)
{
int i, l;
char *p = (char *)(cmd + 1);
@@ -2470,6 +2607,8 @@ fill_comment(ipfw_insn *cmd, char **av)
"comment too long (max 80 chars)");
l = 1 + (l+3)/4;
cmd->len = (cmd->len & (F_NOT | F_OR)) | l;
+ CHECK_CMDLEN;
+
for (i = 0; av[i] != NULL; i++) {
strcpy(p, av[i]);
p += strlen(av[i]);
@@ -2495,7 +2634,7 @@ fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg)
* two microinstructions, and returns the pointer to the last one.
*/
static ipfw_insn *
-add_mac(ipfw_insn *cmd, char *av[])
+add_mac(ipfw_insn *cmd, char *av[], int cblen)
{
ipfw_insn_mac *mac;
@@ -2504,6 +2643,7 @@ add_mac(ipfw_insn *cmd, char *av[])
cmd->opcode = O_MACADDR2;
cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac);
+ CHECK_CMDLEN;
mac = (ipfw_insn_mac *)cmd;
get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */
@@ -2513,12 +2653,13 @@ add_mac(ipfw_insn *cmd, char *av[])
}
static ipfw_insn *
-add_mactype(ipfw_insn *cmd, char *av)
+add_mactype(ipfw_insn *cmd, char *av, int cblen)
{
if (!av)
errx(EX_DATAERR, "missing MAC type");
if (strcmp(av, "any") != 0) { /* we have a non-null type */
- fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE);
+ fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE,
+ cblen);
cmd->opcode = O_MAC_TYPE;
return cmd;
} else
@@ -2587,9 +2728,9 @@ add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
}
static ipfw_insn *
-add_srcip(ipfw_insn *cmd, char *av)
+add_srcip(ipfw_insn *cmd, char *av, int cblen)
{
- fill_ip((ipfw_insn_ip *)cmd, av);
+ fill_ip((ipfw_insn_ip *)cmd, av, cblen);
if (cmd->opcode == O_IP_DST_SET) /* set */
cmd->opcode = O_IP_SRC_SET;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@@ -2604,9 +2745,9 @@ add_srcip(ipfw_insn *cmd, char *av)
}
static ipfw_insn *
-add_dstip(ipfw_insn *cmd, char *av)
+add_dstip(ipfw_insn *cmd, char *av, int cblen)
{
- fill_ip((ipfw_insn_ip *)cmd, av);
+ fill_ip((ipfw_insn_ip *)cmd, av, cblen);
if (cmd->opcode == O_IP_DST_SET) /* set */
;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@@ -2621,12 +2762,12 @@ add_dstip(ipfw_insn *cmd, char *av)
}
static ipfw_insn *
-add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
+add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
{
/* XXX "any" is trapped before. Perhaps "to" */
if (_substrcmp(av, "any") == 0) {
return NULL;
- } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) {
+ } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) {
/* XXX todo: check that we have a protocol with ports */
cmd->opcode = opcode;
return cmd;
@@ -2635,7 +2776,7 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode)
}
static ipfw_insn *
-add_src(ipfw_insn *cmd, char *av, u_char proto)
+add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
{
struct in6_addr a;
char *host, *ch;
@@ -2648,11 +2789,11 @@ add_src(ipfw_insn *cmd, char *av, u_char proto)
if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
inet_pton(AF_INET6, host, &a) == 1)
- ret = add_srcip6(cmd, av);
+ ret = add_srcip6(cmd, av, cblen);
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
- ret = add_srcip(cmd, av);
+ ret = add_srcip(cmd, av, cblen);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@@ -2661,7 +2802,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto)
}
static ipfw_insn *
-add_dst(ipfw_insn *cmd, char *av, u_char proto)
+add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
{
struct in6_addr a;
char *host, *ch;
@@ -2674,11 +2815,11 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto)
if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 ||
inet_pton(AF_INET6, host, &a) == 1)
- ret = add_dstip6(cmd, av);
+ ret = add_dstip6(cmd, av, cblen);
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
- ret = add_dstip(cmd, av);
+ ret = add_dstip(cmd, av, cblen);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@@ -2708,6 +2849,7 @@ ipfw_add(char *av[])
* go into actbuf[].
*/
static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
+ int rblen, ablen, cblen;
ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
ipfw_insn *first_cmd; /* first match pattern */
@@ -2738,6 +2880,15 @@ ipfw_add(char *av[])
cmd = (ipfw_insn *)cmdbuf;
action = (ipfw_insn *)actbuf;
+ rblen = sizeof(rulebuf) / sizeof(rulebuf[0]);
+ rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]);
+ ablen = sizeof(actbuf) / sizeof(actbuf[0]);
+ cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
+ cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
+
+#define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; }
+#define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len)
+
av++;
/* [rule N] -- Rule number optional */
@@ -2769,6 +2920,7 @@ ipfw_add(char *av[])
i = match_token(rule_actions, *av);
av++;
action->len = 1; /* default */
+ CHECK_ACTLEN;
switch(i) {
case TOK_CHECKSTATE:
have_state = action;
@@ -2820,6 +2972,7 @@ ipfw_add(char *av[])
case TOK_NAT:
action->opcode = O_NAT;
action->len = F_INSN_SIZE(ipfw_insn_nat);
+ CHECK_ACTLEN;
if (_substrcmp(*av, "global") == 0) {
action->arg1 = 0;
av++;
@@ -2936,6 +3089,7 @@ chkarg:
action->opcode = O_FORWARD_IP;
action->len = F_INSN_SIZE(ipfw_insn_sa);
+ CHECK_ACTLEN;
/*
* In the kernel we assume AF_INET and use only
@@ -2952,6 +3106,7 @@ chkarg:
action->opcode = O_FORWARD_IP6;
action->len = F_INSN_SIZE(ipfw_insn_sa6);
+ CHECK_ACTLEN;
p->sa.sin6_len = sizeof(struct sockaddr_in6);
p->sa.sin6_family = AF_INET6;
@@ -2994,6 +3149,24 @@ chkarg:
break;
}
+ case TOK_SETDSCP:
+ {
+ int code;
+
+ action->opcode = O_SETDSCP;
+ NEED1("missing DSCP code");
+ if (_substrcmp(*av, "tablearg") == 0) {
+ action->arg1 = IP_FW_TABLEARG;
+ } else if (isalpha(*av[0])) {
+ if ((code = match_token(f_ipdscp, *av)) == -1)
+ errx(EX_DATAERR, "Unknown DSCP code");
+ action->arg1 = code;
+ } else
+ action->arg1 = strtoul(*av, NULL, 10);
+ av++;
+ break;
+ }
+
case TOK_REASS:
action->opcode = O_REASS;
break;
@@ -3005,7 +3178,7 @@ chkarg:
default:
errx(EX_DATAERR, "invalid action %s\n", av[-1]);
}
- action = next_cmd(action);
+ action = next_cmd(action, &ablen);
/*
* [altq queuename] -- altq tag, optional
@@ -3027,6 +3200,7 @@ chkarg:
"log cannot be specified more than once");
have_log = (ipfw_insn *)c;
cmd->len = F_INSN_SIZE(ipfw_insn_log);
+ CHECK_CMDLEN;
cmd->opcode = O_LOG;
if (av[0] && _substrcmp(*av, "logamount") == 0) {
av++;
@@ -3040,9 +3214,14 @@ chkarg:
} else {
len = sizeof(c->max_log);
if (sysctlbyname("net.inet.ip.fw.verbose_limit",
- &c->max_log, &len, NULL, 0) == -1)
+ &c->max_log, &len, NULL, 0) == -1) {
+ if (co.test_only) {
+ c->max_log = 0;
+ break;
+ }
errx(1, "sysctlbyname(\"%s\")",
"net.inet.ip.fw.verbose_limit");
+ }
}
}
break;
@@ -3058,6 +3237,7 @@ chkarg:
"altq cannot be specified more than once");
have_altq = (ipfw_insn *)a;
cmd->len = F_INSN_SIZE(ipfw_insn_altq);
+ CHECK_CMDLEN;
cmd->opcode = O_ALTQ;
a->qid = altq_name_to_qid(*av);
av++;
@@ -3083,7 +3263,7 @@ chkarg:
default:
abort();
}
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
if (have_state) /* must be a check-state, we are done */
@@ -3168,7 +3348,7 @@ chkarg:
av++;
if (F_LEN(cmd) != 0) {
prev = cmd;
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
} else if (first_cmd != cmd) {
errx(EX_DATAERR, "invalid protocol ``%s''", *av);
@@ -3189,11 +3369,11 @@ chkarg:
OR_START(source_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing source address");
- if (add_src(cmd, *av, proto)) {
+ if (add_src(cmd, *av, proto, cblen)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
} else
errx(EX_USAGE, "bad source address %s", *av);
@@ -3205,10 +3385,10 @@ chkarg:
NOT_BLOCK; /* optional "not" */
if ( av[0] != NULL ) {
if (_substrcmp(*av, "any") == 0 ||
- add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+ add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
av++;
if (F_LEN(cmd) != 0)
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
}
@@ -3225,11 +3405,11 @@ chkarg:
OR_START(dest_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing dst address");
- if (add_dst(cmd, *av, proto)) {
+ if (add_dst(cmd, *av, proto, cblen)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
} else
errx( EX_USAGE, "bad destination address %s", *av);
@@ -3241,10 +3421,10 @@ chkarg:
NOT_BLOCK; /* optional "not" */
if (av[0]) {
if (_substrcmp(*av, "any") == 0 ||
- add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+ add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
av++;
if (F_LEN(cmd) != 0)
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
}
@@ -3332,7 +3512,7 @@ read_options:
case TOK_VIA:
NEED1("recv, xmit, via require interface name"
" or address");
- fill_iface((ipfw_insn_if *)cmd, av[0]);
+ fill_iface((ipfw_insn_if *)cmd, av[0], cblen);
av++;
if (F_LEN(cmd) == 0) /* not a valid address */
break;
@@ -3352,14 +3532,14 @@ read_options:
case TOK_ICMP6TYPES:
NEED1("icmptypes requires list of types");
- fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av);
+ fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen);
av++;
break;
case TOK_IPTTL:
NEED1("ipttl requires TTL");
if (strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_IPTTL))
+ if (!add_ports(cmd, *av, 0, O_IPTTL, cblen))
errx(EX_DATAERR, "invalid ipttl %s", *av);
} else
fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0));
@@ -3369,7 +3549,7 @@ read_options:
case TOK_IPID:
NEED1("ipid requires id");
if (strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_IPID))
+ if (!add_ports(cmd, *av, 0, O_IPID, cblen))
errx(EX_DATAERR, "invalid ipid %s", *av);
} else
fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0));
@@ -3379,7 +3559,7 @@ read_options:
case TOK_IPLEN:
NEED1("iplen requires length");
if (strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_IPLEN))
+ if (!add_ports(cmd, *av, 0, O_IPLEN, cblen))
errx(EX_DATAERR, "invalid ip len %s", *av);
} else
fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0));
@@ -3399,6 +3579,12 @@ read_options:
av++;
break;
+ case TOK_DSCP:
+ NEED1("missing DSCP code");
+ fill_dscp(cmd, *av, cblen);
+ av++;
+ break;
+
case TOK_IPOPTS:
NEED1("missing argument for ipoptions");
fill_flags(cmd, O_IPOPT, f_ipopts, *av);
@@ -3475,7 +3661,7 @@ read_options:
case TOK_TCPDATALEN:
NEED1("tcpdatalen requires length");
if (strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_TCPDATALEN))
+ if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen))
errx(EX_DATAERR, "invalid tcpdata len %s", *av);
} else
fill_cmd(cmd, O_TCPDATALEN, 0,
@@ -3501,7 +3687,7 @@ read_options:
case TOK_TCPWIN:
NEED1("tcpwin requires length");
if (strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_TCPWIN))
+ if (!add_ports(cmd, *av, 0, O_TCPWIN, cblen))
errx(EX_DATAERR, "invalid tcpwin len %s", *av);
} else
fill_cmd(cmd, O_TCPWIN, 0,
@@ -3540,6 +3726,7 @@ read_options:
have_state = cmd;
cmd->len = F_INSN_SIZE(ipfw_insn_limit);
+ CHECK_CMDLEN;
cmd->opcode = O_LIMIT;
c->limit_mask = c->conn_limit = 0;
@@ -3571,28 +3758,28 @@ read_options:
case TOK_SRCIP:
NEED1("missing source IP");
- if (add_srcip(cmd, *av)) {
+ if (add_srcip(cmd, *av, cblen)) {
av++;
}
break;
case TOK_DSTIP:
NEED1("missing destination IP");
- if (add_dstip(cmd, *av)) {
+ if (add_dstip(cmd, *av, cblen)) {
av++;
}
break;
case TOK_SRCIP6:
NEED1("missing source IP6");
- if (add_srcip6(cmd, *av)) {
+ if (add_srcip6(cmd, *av, cblen)) {
av++;
}
break;
case TOK_DSTIP6:
NEED1("missing destination IP6");
- if (add_dstip6(cmd, *av)) {
+ if (add_dstip6(cmd, *av, cblen)) {
av++;
}
break;
@@ -3600,7 +3787,7 @@ read_options:
case TOK_SRCPORT:
NEED1("missing source port");
if (_substrcmp(*av, "any") == 0 ||
- add_ports(cmd, *av, proto, O_IP_SRCPORT)) {
+ add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) {
av++;
} else
errx(EX_DATAERR, "invalid source port %s", *av);
@@ -3609,7 +3796,7 @@ read_options:
case TOK_DSTPORT:
NEED1("missing destination port");
if (_substrcmp(*av, "any") == 0 ||
- add_ports(cmd, *av, proto, O_IP_DSTPORT)) {
+ add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) {
av++;
} else
errx(EX_DATAERR, "invalid destination port %s",
@@ -3617,13 +3804,13 @@ read_options:
break;
case TOK_MAC:
- if (add_mac(cmd, av))
+ if (add_mac(cmd, av, cblen))
av += 2;
break;
case TOK_MACTYPE:
NEED1("missing mac type");
- if (!add_mactype(cmd, *av))
+ if (!add_mactype(cmd, *av, cblen))
errx(EX_DATAERR, "invalid mac type %s", *av);
av++;
break;
@@ -3661,18 +3848,18 @@ read_options:
if (proto != IPPROTO_IPV6 )
errx( EX_USAGE, "flow-id filter is active "
"only for ipv6 protocol\n");
- fill_flow6( (ipfw_insn_u32 *) cmd, *av );
+ fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen);
av++;
break;
case TOK_COMMENT:
- fill_comment(cmd, av);
+ fill_comment(cmd, av, cblen);
av[0]=NULL;
break;
case TOK_TAGGED:
if (av[0] && strpbrk(*av, "-,")) {
- if (!add_ports(cmd, *av, 0, O_TAGGED))
+ if (!add_ports(cmd, *av, 0, O_TAGGED, cblen))
errx(EX_DATAERR, "tagged: invalid tag"
" list: %s", *av);
}
@@ -3725,7 +3912,7 @@ read_options:
}
if (F_LEN(cmd) > 0) { /* prepare to advance */
prev = cmd;
- cmd = next_cmd(cmd);
+ cmd = next_cmd(cmd, &cblen);
}
}
@@ -3754,12 +3941,13 @@ done:
*/
if (have_state && have_state->opcode != O_CHECK_STATE) {
fill_cmd(dst, O_PROBE_STATE, 0, 0);
- dst = next_cmd(dst);
+ dst = next_cmd(dst, &rblen);
}
/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
i = F_LEN(src);
+ CHECK_RBUFLEN(i);
switch (src->opcode) {
case O_LOG:
@@ -3779,6 +3967,7 @@ done:
*/
if (have_state && have_state->opcode != O_CHECK_STATE) {
i = F_LEN(have_state);
+ CHECK_RBUFLEN(i);
bcopy(have_state, dst, i * sizeof(uint32_t));
dst += i;
}
@@ -3790,24 +3979,29 @@ done:
/* put back O_LOG, O_ALTQ, O_TAG if necessary */
if (have_log) {
i = F_LEN(have_log);
+ CHECK_RBUFLEN(i);
bcopy(have_log, dst, i * sizeof(uint32_t));
dst += i;
}
if (have_altq) {
i = F_LEN(have_altq);
+ CHECK_RBUFLEN(i);
bcopy(have_altq, dst, i * sizeof(uint32_t));
dst += i;
}
if (have_tag) {
i = F_LEN(have_tag);
+ CHECK_RBUFLEN(i);
bcopy(have_tag, dst, i * sizeof(uint32_t));
dst += i;
}
+
/*
* copy all other actions
*/
for (src = (ipfw_insn *)actbuf; src != action; src += i) {
i = F_LEN(src);
+ CHECK_RBUFLEN(i);
bcopy(src, dst, i * sizeof(uint32_t));
dst += i;
}
@@ -3912,6 +4106,7 @@ ipfw_flush(int force)
static void table_list(uint16_t num, int need_header);
+static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
/*
* This one handles all table-related commands
@@ -3927,16 +4122,18 @@ ipfw_table_handler(int ac, char *av[])
int do_add;
int is_all;
size_t len;
- char *p;
- uint32_t a, type, mask, addrlen;
+ uint32_t a;
uint32_t tables_max;
- mask = 0; // XXX uninitialized ?
len = sizeof(tables_max);
if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
- NULL, 0) == -1)
- errx(1, "Can't determine maximum number of ipfw tables. "
- "Perhaps you forgot to load ipfw module?");
+ NULL, 0) == -1) {
+ if (co.test_only)
+ tables_max = 128; /* Old conservative default */
+ else
+ errx(1, "Can't determine maximum number of ipfw tables."
+ " Perhaps you forgot to load ipfw module?");
+ }
memset(&xent, 0, sizeof(xent));
@@ -3965,57 +4162,8 @@ ipfw_table_handler(int ac, char *av[])
ac--; av++;
if (!ac)
errx(EX_USAGE, "address required");
- /*
- * Let's try to guess type by agrument.
- * Possible types:
- * 1) IPv4[/mask]
- * 2) IPv6[/mask]
- * 3) interface name
- * 4) port ?
- */
- type = 0;
- if (ishexnumber(*av[0])) {
- /* Remove / if exists */
- if ((p = strchr(*av, '/')) != NULL) {
- *p = '\0';
- mask = atoi(p + 1);
- }
-
- if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) {
- type = IPFW_TABLE_CIDR;
- if ((p != NULL) && (mask > 32))
- errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1);
- xent.masklen = p ? mask : 32;
- addrlen = sizeof(struct in_addr);
- } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 1) {
- type = IPFW_TABLE_CIDR;
- if ((p != NULL) && (mask > 128))
- errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1);
- xent.masklen = p ? mask : 128;
- addrlen = sizeof(struct in6_addr);
- }
- }
-
- if ((type == 0) && (strchr(*av, '.') == NULL)) {
- /* Assume interface name. Copy significant data only */
- mask = MIN(strlen(*av), IF_NAMESIZE - 1);
- memcpy(xent.k.iface, *av, mask);
- /* Set mask to exact match */
- xent.masklen = 8 * IF_NAMESIZE;
- type = IPFW_TABLE_INTERFACE;
- addrlen = IF_NAMESIZE;
- }
- if (type == 0) {
- if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) != 0)
- errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
- xent.masklen = 32;
- type = IPFW_TABLE_CIDR;
- addrlen = sizeof(struct in_addr);
- }
-
- xent.type = type;
- xent.len = offsetof(ipfw_table_xentry, k) + addrlen;
+ table_fill_xentry(*av, &xent);
ac--; av++;
if (do_add && ac) {
@@ -4065,6 +4213,93 @@ ipfw_table_handler(int ac, char *av[])
}
static void
+table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+{
+ int addrlen, mask, masklen, type;
+ struct in6_addr *paddr;
+ uint32_t *pkey;
+ char *p;
+ uint32_t key;
+
+ mask = 0;
+ type = 0;
+ addrlen = 0;
+ masklen = 0;
+
+ /*
+ * Let's try to guess type by agrument.
+ * Possible types:
+ * 1) IPv4[/mask]
+ * 2) IPv6[/mask]
+ * 3) interface name
+ * 4) port, uid/gid or other u32 key (base 10 format)
+ * 5) hostname
+ */
+ paddr = &xent->k.addr6;
+ if (ishexnumber(*arg) != 0 || *arg == ':') {
+ /* Remove / if exists */
+ if ((p = strchr(arg, '/')) != NULL) {
+ *p = '\0';
+ mask = atoi(p + 1);
+ }
+
+ if (inet_pton(AF_INET, arg, paddr) == 1) {
+ if (p != NULL && mask > 32)
+ errx(EX_DATAERR, "bad IPv4 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 32;
+ addrlen = sizeof(struct in_addr);
+ } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
+ if (IN6_IS_ADDR_V4COMPAT(paddr))
+ errx(EX_DATAERR,
+ "Use IPv4 instead of v4-compatible");
+ if (p != NULL && mask > 128)
+ errx(EX_DATAERR, "bad IPv6 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 128;
+ addrlen = sizeof(struct in6_addr);
+ } else {
+ /* Port or any other key */
+ key = strtol(arg, &p, 10);
+ /* Skip non-base 10 entries like 'fa1' */
+ if (p != arg) {
+ pkey = (uint32_t *)paddr;
+ *pkey = htonl(key);
+ type = IPFW_TABLE_CIDR;
+ addrlen = sizeof(uint32_t);
+ }
+ }
+ }
+
+ if (type == 0 && strchr(arg, '.') == NULL) {
+ /* Assume interface name. Copy significant data only */
+ mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+ memcpy(xent->k.iface, arg, mask);
+ /* Set mask to exact match */
+ masklen = 8 * IF_NAMESIZE;
+ type = IPFW_TABLE_INTERFACE;
+ addrlen = IF_NAMESIZE;
+ }
+
+ if (type == 0) {
+ if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+ masklen = 32;
+ type = IPFW_TABLE_CIDR;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ xent->type = type;
+ xent->masklen = masklen;
+ xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+}
+
+static void
table_list(uint16_t num, int need_header)
{
ipfw_xtable *tbl;
@@ -4107,8 +4342,8 @@ table_list(uint16_t num, int need_header)
tval = xent->value;
addr6 = &xent->k.addr6;
- if ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] == 0) &&
- (addr6->s6_addr32[2] == 0)) {
+
+ if (IN6_IS_ADDR_V4COMPAT(addr6)) {
/* IPv4 address */
inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf));
} else {
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index bade0dd..d592930 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -203,6 +203,7 @@ enum tokens {
TOK_SETFIB,
TOK_LOOKUP,
TOK_SOCKARG,
+ TOK_SETDSCP,
};
/*
* the following macro returns an error message if we run out of
@@ -283,10 +284,10 @@ void print_flow6id(struct _ipfw_insn_u32 *cmd);
void print_icmp6types(struct _ipfw_insn_u32 *cmd);
void print_ext6hdr(struct _ipfw_insn *cmd );
-struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av);
-struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av);
+struct _ipfw_insn *add_srcip6(struct _ipfw_insn *cmd, char *av, int cblen);
+struct _ipfw_insn *add_dstip6(struct _ipfw_insn *cmd, char *av, int cblen);
-void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av );
+void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen);
void fill_unreach6_code(u_short *codep, char *str);
-void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av);
+void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen);
int fill_ext6hdr(struct _ipfw_insn *cmd, char *av);
diff --git a/sbin/ipfw/ipv6.c b/sbin/ipfw/ipv6.c
index 6326590..ee9bb62 100644
--- a/sbin/ipfw/ipv6.c
+++ b/sbin/ipfw/ipv6.c
@@ -42,6 +42,11 @@
#include <netinet/ip_fw.h>
#include <arpa/inet.h>
+#define CHECK_LENGTH(v, len) do { \
+ if ((v) < (len)) \
+ errx(EX_DATAERR, "Rule too long"); \
+ } while (0)
+
static struct _s_x icmp6codes[] = {
{ "no-route", ICMP6_DST_UNREACH_NOROUTE },
{ "admin-prohib", ICMP6_DST_UNREACH_ADMIN },
@@ -131,10 +136,12 @@ print_ip6(ipfw_insn_ip6 *cmd, char const *s)
}
void
-fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av)
+fill_icmp6types(ipfw_insn_icmp6 *cmd, char *av, int cblen)
{
uint8_t type;
+ CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_icmp6));
+
bzero(cmd, sizeof(*cmd));
while (*av) {
if (*av == ',')
@@ -327,7 +334,7 @@ lookup_host6 (char *host, struct in6_addr *ip6addr)
* Return 1 on success, 0 on failure.
*/
static int
-fill_ip6(ipfw_insn_ip6 *cmd, char *av)
+fill_ip6(ipfw_insn_ip6 *cmd, char *av, int cblen)
{
int len = 0;
struct in6_addr *d = &(cmd->addr6);
@@ -379,6 +386,8 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av)
int masklen;
char md = '\0';
+ CHECK_LENGTH(cblen, 1 + len + 2 * F_INSN_SIZE(struct in6_addr));
+
if ((p = strpbrk(av, "/,")) ) {
md = *p; /* save the separator */
*p = '\0'; /* terminate address string */
@@ -453,7 +462,7 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av)
* additional flow-id we want to filter, the basic is 1
*/
void
-fill_flow6( ipfw_insn_u32 *cmd, char *av )
+fill_flow6( ipfw_insn_u32 *cmd, char *av, int cblen)
{
u_int32_t type; /* Current flow number */
u_int16_t nflow = 0; /* Current flow index */
@@ -461,6 +470,8 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av )
cmd->d[0] = 0; /* Initializing the base number*/
while (s) {
+ CHECK_LENGTH(cblen, F_INSN_SIZE(ipfw_insn_u32) + nflow + 1);
+
av = strsep( &s, ",") ;
type = strtoul(av, &av, 0);
if (*av != ',' && *av != '\0')
@@ -481,10 +492,10 @@ fill_flow6( ipfw_insn_u32 *cmd, char *av )
}
ipfw_insn *
-add_srcip6(ipfw_insn *cmd, char *av)
+add_srcip6(ipfw_insn *cmd, char *av, int cblen)
{
- fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
if (cmd->opcode == O_IP_DST_SET) /* set */
cmd->opcode = O_IP_SRC_SET;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@@ -503,10 +514,10 @@ add_srcip6(ipfw_insn *cmd, char *av)
}
ipfw_insn *
-add_dstip6(ipfw_insn *cmd, char *av)
+add_dstip6(ipfw_insn *cmd, char *av, int cblen)
{
- fill_ip6((ipfw_insn_ip6 *)cmd, av);
+ fill_ip6((ipfw_insn_ip6 *)cmd, av, cblen);
if (cmd->opcode == O_IP_DST_SET) /* set */
;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
OpenPOWER on IntegriCloud