aboutsummaryrefslogtreecommitdiff
path: root/sbin/ipfw/ipfw2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/ipfw/ipfw2.c')
-rw-r--r--sbin/ipfw/ipfw2.c270
1 files changed, 223 insertions, 47 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 7d820698a25d..72927e336fa8 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -16,8 +16,6 @@
* This software is provided ``AS IS'' without any warranties of any kind.
*
* NEW command line interface for IP firewall facility
- *
- * $FreeBSD$
*/
#include <sys/types.h>
@@ -190,6 +188,7 @@ struct _s_x f_ipdscp[] = {
{ "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */
{ "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */
{ "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
+ { "va", IPTOS_DSCP_VA >> 2 }, /* 101100 */
{ "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */
{ "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */
{ "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */
@@ -287,6 +286,7 @@ static struct _s_x rule_actions[] = {
{ "return", TOK_RETURN },
{ "eaction", TOK_EACTION },
{ "tcp-setmss", TOK_TCPSETMSS },
+ { "setmark", TOK_SETMARK },
{ NULL, 0 } /* terminator */
};
@@ -312,6 +312,7 @@ static struct _s_x lookup_keys[] = {
{ "uid", LOOKUP_UID },
{ "jail", LOOKUP_JAIL },
{ "dscp", LOOKUP_DSCP },
+ { "mark", LOOKUP_MARK },
{ NULL, 0 },
};
@@ -390,6 +391,7 @@ static struct _s_x rule_options[] = {
{ "src-ip6", TOK_SRCIP6 },
{ "lookup", TOK_LOOKUP },
{ "flow", TOK_FLOW },
+ { "mark", TOK_MARK },
{ "defer-action", TOK_SKIPACTION },
{ "defer-immediate-action", TOK_SKIPACTION },
{ "//", TOK_COMMENT },
@@ -583,6 +585,13 @@ stringnum_cmp(const char *a, const char *b)
return (strcmp(a, b));
}
+struct debug_header {
+ uint16_t cmd_type;
+ uint16_t spare1;
+ uint32_t opt_name;
+ uint32_t total_len;
+ uint32_t spare2;
+};
/*
* conditionally runs the command.
@@ -593,8 +602,18 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
{
int i;
+ if (g_co.debug_only) {
+ struct debug_header dbg = {
+ .cmd_type = 1,
+ .opt_name = optname,
+ .total_len = optlen + sizeof(struct debug_header),
+ };
+ write(1, &dbg, sizeof(dbg));
+ write(1, optval, optlen);
+ }
+
if (g_co.test_only)
- return 0;
+ return (0);
if (ipfw_socket == -1)
ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
@@ -613,7 +632,7 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
} else {
i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen);
}
- return i;
+ return (i);
}
/*
@@ -630,6 +649,18 @@ int
do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
{
+ op3->opcode = optname;
+
+ if (g_co.debug_only) {
+ struct debug_header dbg = {
+ .cmd_type = 2,
+ .opt_name = optname,
+ .total_len = optlen, sizeof(struct debug_header),
+ };
+ write(1, &dbg, sizeof(dbg));
+ write(1, op3, optlen);
+ }
+
if (g_co.test_only)
return (0);
@@ -638,7 +669,6 @@ do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
if (ipfw_socket < 0)
err(EX_UNAVAILABLE, "socket");
- op3->opcode = optname;
return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
}
@@ -659,6 +689,18 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
int error;
socklen_t len;
+ op3->opcode = optname;
+
+ if (g_co.debug_only) {
+ struct debug_header dbg = {
+ .cmd_type = 3,
+ .opt_name = optname,
+ .total_len = *optlen + sizeof(struct debug_header),
+ };
+ write(1, &dbg, sizeof(dbg));
+ write(1, op3, *optlen);
+ }
+
if (g_co.test_only)
return (0);
@@ -667,7 +709,6 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
if (ipfw_socket < 0)
err(EX_UNAVAILABLE, "socket");
- op3->opcode = optname;
len = *optlen;
error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len);
@@ -1101,6 +1142,45 @@ fill_dscp(ipfw_insn *cmd, char *av, int cblen)
}
}
+/*
+ * Fill the body of the command with mark value and mask.
+ */
+static void
+fill_mark(ipfw_insn *cmd, char *av, int cblen)
+{
+ uint32_t *value, *mask;
+ char *value_str;
+
+ cmd->opcode = O_MARK;
+ cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1;
+
+ CHECK_CMDLEN;
+
+ value = (uint32_t *)(cmd + 1);
+ mask = value + 1;
+
+ value_str = strsep(&av, ":");
+
+ if (strcmp(value_str, "tablearg") == 0) {
+ cmd->arg1 = IP_FW_TARG;
+ *value = 0;
+ } else {
+ /* This is not a tablearg */
+ cmd->arg1 |= 0x8000;
+ *value = strtoul(value_str, NULL, 0);
+ }
+ if (av)
+ *mask = strtoul(av, NULL, 0);
+ else
+ *mask = 0xFFFFFFFF;
+
+ if ((*value & *mask) != *value)
+ errx(EX_DATAERR, "Static mark value: some bits in value are"
+ " set that will be masked out by mask "
+ "(%#x & %#x) = %#x != %#x",
+ *value, *mask, (*value & *mask), *value);
+}
+
static struct _s_x icmpcodes[] = {
{ "net", ICMP_UNREACH_NET },
{ "host", ICMP_UNREACH_HOST },
@@ -1121,8 +1201,8 @@ static struct _s_x icmpcodes[] = {
{ NULL, 0 }
};
-static void
-fill_reject_code(u_short *codep, char *str)
+static uint16_t
+get_reject_code(const char *str)
{
int val;
char *s;
@@ -1132,8 +1212,7 @@ fill_reject_code(u_short *codep, char *str)
val = match_token(icmpcodes, str);
if (val < 0)
errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
- *codep = val;
- return;
+ return (val);
}
static void
@@ -1787,6 +1866,19 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo,
case O_SKIP_ACTION:
bprintf(bp, " defer-immediate-action");
break;
+ case O_MARK:
+ bprintf(bp, " mark");
+ if (cmd->arg1 == IP_FW_TARG)
+ bprintf(bp, " tablearg");
+ else
+ bprintf(bp, " %#x",
+ ((const ipfw_insn_u32 *)cmd)->d[0]);
+
+ if (((const ipfw_insn_u32 *)cmd)->d[1] != 0xFFFFFFFF)
+ bprintf(bp, ":%#x",
+ ((const ipfw_insn_u32 *)cmd)->d[1]);
+ break;
+
default:
bprintf(bp, " [opcode %d len %d]", cmd->opcode,
cmd->len);
@@ -1890,6 +1982,10 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
bprintf(bp, "abort");
else if (cmd->arg1 == ICMP_UNREACH_HOST)
bprintf(bp, "reject");
+ else if (cmd->arg1 == ICMP_UNREACH_NEEDFRAG &&
+ cmd->len == F_INSN_SIZE(ipfw_insn_u16))
+ bprintf(bp, "needfrag %u",
+ ((const ipfw_insn_u16 *)cmd)->ports[0]);
else
print_reject_code(bp, cmd->arg1);
break;
@@ -2026,6 +2122,13 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
else
bprint_uint_arg(bp, "call ", cmd->arg1);
break;
+ case O_SETMARK:
+ if (cmd->arg1 == IP_FW_TARG) {
+ bprintf(bp, "setmark tablearg");
+ break;
+ }
+ bprintf(bp, "setmark %#x", ((const ipfw_insn_u32 *)cmd)->d[0]);
+ break;
default:
bprintf(bp, "** unrecognized action %d len %d ",
cmd->opcode, cmd->len);
@@ -2170,7 +2273,7 @@ static const int action_opcodes[] = {
O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT,
O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE,
O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT,
- O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN,
+ O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN, O_SETMARK,
/* keep the following opcodes at the end of the list */
O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA
};
@@ -2784,7 +2887,7 @@ ipfw_list(int ac, char *av[], int show_counters)
}
}
- /* get configuraion from kernel */
+ /* get configuration from kernel */
cfg = NULL;
sfo.show_counters = show_counters;
sfo.show_time = g_co.do_time;
@@ -3419,7 +3522,7 @@ ipfw_delete(char *av[])
if (g_co.do_quiet)
continue;
if (rt.start_rule != rt.end_rule)
- warnx("no rules rules in %u-%u range",
+ warnx("no rules in %u-%u range",
rt.start_rule, rt.end_rule);
else
warnx("rule %u not found",
@@ -3831,6 +3934,50 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
return ret;
}
+static inline int
+arg_or_targ_relaxed(const char *arg, const char *action)
+{
+ uint32_t arg1 = (uint32_t)(-1);
+
+ if (arg == NULL)
+ errx(EX_USAGE, "missing argument for %s", action);
+ if (isdigit(arg[0])) {
+ arg1 = strtoul(arg, NULL, 10);
+ if (arg1 <= 0 || arg1 >= IP_FW_TABLEARG)
+ errx(EX_DATAERR, "illegal argument %s(%u) for %s",
+ arg, arg1, action);
+ } else if (_substrcmp(arg, "tablearg") == 0)
+ arg1 = IP_FW_TARG;
+ return (arg1);
+}
+
+static inline uint16_t
+arg_or_targ(const char *arg, const char *action)
+{
+ uint32_t arg1 = arg_or_targ_relaxed(arg, action);
+
+ if (arg1 == (uint32_t)(-1))
+ errx(EX_DATAERR, "illegal argument %s(%u) for %s",
+ arg, arg1, action);
+ return (arg1);
+}
+
+static uint16_t
+get_divert_port(const char *arg, const char *action)
+{
+ uint32_t arg1 = arg_or_targ_relaxed(arg, action);
+
+ if (arg1 != (uint32_t)(-1))
+ return (arg1);
+
+ struct servent *s;
+ setservent(1);
+ s = getservbyname(arg, "divert");
+ if (s == NULL)
+ errx(EX_DATAERR, "illegal divert/tee port");
+ return (ntohs(s->s_port));
+}
+
/*
* Parse arguments and assemble the microinstructions which make up a rule.
* Rules are added into the 'rulebuf' and then copied in the correct order
@@ -3990,14 +4137,25 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
case TOK_UNREACH:
action->opcode = O_REJECT;
NEED1("missing reject code");
- fill_reject_code(&action->arg1, *av);
+ action->arg1 = get_reject_code(*av);
av++;
+ if (action->arg1 == ICMP_UNREACH_NEEDFRAG && isdigit(**av)) {
+ uint16_t mtu;
+
+ mtu = strtoul(*av, NULL, 10);
+ if (mtu < 68 || mtu >= IP_MAXPACKET)
+ errx(EX_DATAERR, "illegal argument for %s",
+ *(av - 1));
+ action->len = F_INSN_SIZE(ipfw_insn_u16);
+ ((ipfw_insn_u16 *)action)->ports[0] = mtu;
+ av++;
+ }
break;
case TOK_UNREACH6:
action->opcode = O_UNREACH6;
NEED1("missing unreach code");
- fill_unreach6_code(&action->arg1, *av);
+ action->arg1 = get_unreach6_code(*av);
av++;
break;
@@ -4009,55 +4167,50 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
action->opcode = O_NAT;
action->len = F_INSN_SIZE(ipfw_insn_nat);
CHECK_ACTLEN;
- if (*av != NULL && _substrcmp(*av, "global") == 0) {
+ if (*av != NULL && _substrcmp(*av, "global") == 0)
action->arg1 = IP_FW_NAT44_GLOBAL;
- av++;
- break;
- } else
- goto chkarg;
+ else
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_QUEUE:
action->opcode = O_QUEUE;
- goto chkarg;
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_PIPE:
action->opcode = O_PIPE;
- goto chkarg;
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_SKIPTO:
action->opcode = O_SKIPTO;
- goto chkarg;
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_NETGRAPH:
action->opcode = O_NETGRAPH;
- goto chkarg;
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_NGTEE:
action->opcode = O_NGTEE;
- goto chkarg;
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
+ av++;
+ break;
case TOK_DIVERT:
action->opcode = O_DIVERT;
- goto chkarg;
+ action->arg1 = get_divert_port(av[0], *(av - 1));
+ av++;
+ break;
case TOK_TEE:
action->opcode = O_TEE;
- goto chkarg;
+ action->arg1 = get_divert_port(av[0], *(av - 1));
+ av++;
+ break;
case TOK_CALL:
action->opcode = O_CALLRETURN;
-chkarg:
- if (!av[0])
- errx(EX_USAGE, "missing argument for %s", *(av - 1));
- if (isdigit(**av)) {
- action->arg1 = strtoul(*av, NULL, 10);
- if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG)
- errx(EX_DATAERR, "illegal argument for %s",
- *(av - 1));
- } else if (_substrcmp(*av, "tablearg") == 0) {
- action->arg1 = IP_FW_TARG;
- } else if (i == TOK_DIVERT || i == TOK_TEE) {
- struct servent *s;
- setservent(1);
- s = getservbyname(av[0], "divert");
- if (s != NULL)
- action->arg1 = ntohs(s->s_port);
- else
- errx(EX_DATAERR, "illegal divert/tee port");
- } else
- errx(EX_DATAERR, "illegal argument for %s", *(av - 1));
+ action->arg1 = arg_or_targ(av[0], *(av - 1));
av++;
break;
@@ -4228,6 +4381,23 @@ chkarg:
fill_cmd(action, O_CALLRETURN, F_NOT, 0);
break;
+ case TOK_SETMARK: {
+ action->opcode = O_SETMARK;
+ action->len = F_INSN_SIZE(ipfw_insn_u32);
+ NEED1("missing mark");
+ if (strcmp(*av, "tablearg") == 0) {
+ action->arg1 = IP_FW_TARG;
+ } else {
+ ((ipfw_insn_u32 *)action)->d[0] =
+ strtoul(*av, NULL, 0);
+ /* This is not a tablearg */
+ action->arg1 |= 0x8000;
+ }
+ av++;
+ CHECK_CMDLEN;
+ break;
+ }
+
case TOK_TCPSETMSS: {
u_long mss;
uint16_t idx;
@@ -5115,6 +5285,12 @@ read_options:
fill_cmd(cmd, O_SKIP_ACTION, 0, 0);
break;
+ case TOK_MARK:
+ NEED1("missing mark value:mask");
+ fill_mark(cmd, *av, cblen);
+ av++;
+ break;
+
default:
errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
}