diff options
Diffstat (limited to 'sbin/ipfw/ipfw2.c')
-rw-r--r-- | sbin/ipfw/ipfw2.c | 270 |
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); } |