diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -448,7 +448,7 @@ struct tidx *tstate); void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen); -void fill_unreach6_code(u_short *codep, char *str); +uint16_t fill_unreach6_code(char *str); 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/ipfw2.c b/sbin/ipfw/ipfw2.c --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include /* ctime */ #include /* _long_to_time */ @@ -76,6 +77,122 @@ static int ipfw_socket = -1; +struct linear_buffer { + char *base; /* Base allocated memory pointer */ + uint32_t offset; /* Currently used offset */ + uint32_t size; /* Total buffer size */ + struct linear_buffer *next; /* Buffer chaining */ +}; + +static inline struct linear_buffer * +lb_init(uint32_t size) +{ + struct linear_buffer *lb = calloc(1, size); + + if (lb != NULL) { + lb->base = (char *)(lb + 1); + lb->size = size - sizeof(*lb); + } + + return (lb); +} + +static inline void +lb_free(struct linear_buffer *lb) +{ + free(lb); +} + +static inline char * +lb_allocz(struct linear_buffer *lb, int len) +{ + len = roundup2(len, sizeof(uint64_t)); + if (lb->offset + len > lb->size) + return (NULL); + void *data = (void *)(lb->base + lb->offset); + lb->offset += len; + return (data); +} + +static inline void +lb_clear(struct linear_buffer *lb) +{ + memset(lb->base, 0, lb->offset); + lb->offset = 0; +} + + +struct ipfw_ctx { + struct linear_buffer *lb; +}; + +typedef struct ipfw_ctx ipfw_ctx_t; + +static bool +lipfw_ctx_init(struct ipfw_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->lb = lb_init(65536); + if (ctx->lb == NULL) { + return (false); + } + + return (true); +} + +static inline void * +ipfw_allocz(struct ipfw_ctx *ctx, int len) +{ + void *data = lb_allocz(ctx->lb, len); + + if (data == NULL) { + uint32_t size = ctx->lb->size * 2; + + while (size < len + sizeof(struct linear_buffer)) + size *= 2; + + struct linear_buffer *lb = lb_init(size); + + if (lb == NULL) + err(EX_UNAVAILABLE, "malloc(%d) failed", len); + + lb->next = ctx->lb; + ctx->lb = lb; + data = lb_allocz(ctx->lb, len); + } + + return (data); +} + +static inline void +lipfw_clear_lb(struct ipfw_ctx *ctx) +{ + struct linear_buffer *lb = ctx->lb; + + lb_clear(lb); + lb = lb->next; + ctx->lb->next = NULL; + /* Remove all linear bufs except the largest one */ + while (lb != NULL) { + struct linear_buffer *lb_next = lb->next; + lb_free(lb); + lb = lb_next; + } +} + +static inline void +lipfw_ctx_free(struct ipfw_ctx *ctx) +{ + lipfw_clear_lb(ctx); + lb_free(ctx->lb); +} + + + + + + #define CHECK_LENGTH(v, len) do { \ if ((v) < (len)) \ errx(EX_DATAERR, "Rule too long"); \ @@ -1203,8 +1320,8 @@ { NULL, 0 } }; -static void -fill_reject_code(u_short *codep, char *str) +static uint16_t +fill_reject_code(char *str) { int val; char *s; @@ -1214,8 +1331,7 @@ val = match_token(icmpcodes, str); if (val < 0) errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); - *codep = val; - return; + return (val); } static void @@ -3110,6 +3226,7 @@ uint32_t size; uint16_t counter; uint8_t set; + ipfw_ctx_t *ctx; }; int @@ -3179,11 +3296,14 @@ } if (tstate->count + 1 > tstate->size) { - tstate->size += 4; - tstate->idx = realloc(tstate->idx, tstate->size * - sizeof(ipfw_obj_ntlv)); - if (tstate->idx == NULL) - return (0); + int new_size = MAX(tstate->size * 2, 4); + void *idx; + + idx = ipfw_allocz(tstate->ctx, new_size * sizeof(ipfw_obj_ntlv)); + memcpy(idx, tstate->idx, tstate->size * sizeof(ipfw_obj_ntlv)); + + tstate->idx = idx; + tstate->size = new_size; } ntlv = &tstate->idx[i]; @@ -3937,6 +4057,98 @@ return ret; } +struct cmd_buffer { + char *data; + int off; + int size; + int count; +}; + +enum lipfw_ir_buffer { + LIPFW_IR_CMD, + LIPFW_IR_ACT, + LIPFW_IR_SACT, + LIPFW_IR_STA, + __LIPFW_IR_MAX, +}; + +struct ipfw_rule_ir { + ipfw_ctx_t *ctx; + ipfw_insn *state_insn; + struct cmd_buffer buffers[4]; + uint32_t rule_num; + uint32_t set_id; + double match_prob; +}; + +static inline ipfw_insn * +buf_reserve(struct ipfw_rule_ir *ir, struct cmd_buffer *buf, int len) +{ + if (__predict_false(buf->off + len > buf->size)) { + /* Realloc */ + int new_size = MAX(buf->size * 2, 4); + char *data = ipfw_allocz(ir->ctx, new_size); + + memcpy(data, buf->data, buf->off); + buf->data = data; + buf->size = new_size; + } + + ipfw_insn *cmd = (ipfw_insn *)(void *)&buf->data[buf->off]; + buf->off += len; + buf->count++; + + return (cmd); +} + +static struct ipfw_rule_ir * +lipfw_rule_alloc(ipfw_ctx_t *ctx) +{ + struct ipfw_rule_ir *ir = ipfw_allocz(ctx, sizeof(*ir)); + + ir->ctx = ctx; + ir->match_prob = 1; + + return (ir); +} + +static ipfw_insn * +buf_get(struct ipfw_rule_ir *ir, uint8_t btype) +{ + if (ir->buffers[btype].off == 0) + return (NULL); + return ((ipfw_insn *)ir->buffers[btype].data); +} + +static bool +buf_is_empty(const struct ipfw_rule_ir *ir, uint8_t btype) +{ + return (ir->buffers[btype].off != 0); +} + +static ipfw_insn * +lipfw_rule_add_cmd_raw(struct ipfw_rule_ir *ir, uint8_t btype, const ipfw_insn *cmd) +{ + ipfw_insn *new_cmd = buf_reserve(ir, &ir->buffers[btype], F_LEN(cmd) * 4); + + memcpy(new_cmd, cmd, F_LEN(cmd) * 4); + + return (new_cmd); +} + +static ipfw_insn * +lipfw_rule_add_cmd(struct ipfw_rule_ir *ir, uint8_t btype, uint8_t opcode, + uint8_t flags, uint16_t arg1) +{ + ipfw_insn *cmd = buf_reserve(ir, &ir->buffers[btype], sizeof(ipfw_insn)); + + cmd->opcode = opcode; + cmd->len = 1 | (flags & (F_NOT | F_OR)); + cmd->arg1 = arg1; + + return (cmd); +} + static inline int arg_or_targ_relaxed(const char *arg, const char *action) { @@ -3965,23 +4177,20 @@ return (arg1); } -static void -fill_divert_port(ipfw_insn *cmd, char *arg, const char *action) +static uint16_t +fill_divert_port(char *arg, const char *action) { uint32_t arg1 = arg_or_targ_relaxed(arg, action); - if (arg1 != (uint32_t)(-1)) { - cmd->arg1 = arg1; - return; - } + if (arg1 != (uint32_t)(-1)) + return (arg1); struct servent *s; setservent(1); s = getservbyname(arg, "divert"); - if (s != NULL) - cmd->arg1 = ntohs(s->s_port); - else + if (s == NULL) errx(EX_DATAERR, "illegal divert/tee port"); + return (ntohs(s->s_port)); } /* @@ -3997,7 +4206,7 @@ * */ static void -compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) +compile_rule(ipfw_ctx_t *ctx, char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) { /* * rules are added into the 'rulebuf' and then copied in @@ -4005,10 +4214,10 @@ * Some things that need to go out of order (prob, action etc.) * go into actbuf[]. */ - static uint32_t actbuf[255], cmdbuf[255]; - int rblen, ablen, cblen; + static uint32_t cmdbuf[255]; + int rblen, cblen; - ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; + ipfw_insn *src, *dst, *cmd, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ struct ip_fw_rule *rule; @@ -4016,7 +4225,6 @@ /* * various flags used to record that we entered some fields. */ - ipfw_insn *have_state = NULL; /* any state-related option */ int have_rstate = 0; ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; ipfw_insn *have_skipcmd = NULL; @@ -4031,28 +4239,29 @@ double match_prob = 1; /* match probability, default is always match */ - bzero(actbuf, sizeof(actbuf)); /* actions go here */ bzero(cmdbuf, sizeof(cmdbuf)); bzero(rbuf, *rbufsize); rule = (struct ip_fw_rule *)rbuf; cmd = (ipfw_insn *)cmdbuf; - action = (ipfw_insn *)actbuf; rblen = *rbufsize / sizeof(uint32_t); rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); - 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) + struct ipfw_rule_ir *ir = lipfw_rule_alloc(ctx); + uint16_t arg1; + av++; /* [rule N] -- Rule number optional */ if (av[0] && isdigit(**av)) { rule->rulenum = atoi(*av); + ir->rule_num = atoi(*av); av++; } @@ -4063,12 +4272,14 @@ errx(EX_DATAERR, "illegal set %s", av[1]); rule->set = set; tstate->set = set; + ir->set_id = set; av += 2; } /* [prob D] -- match probability, optional */ if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) { match_prob = strtod(av[1], NULL); + ir->match_prob = match_prob; if (match_prob <= 0 || match_prob > 1) errx(EX_DATAERR, "illegal match prob. %s", av[1]); @@ -4079,27 +4290,23 @@ NEED1("missing action"); i = match_token(rule_actions, *av); av++; - action->len = 1; /* default */ - CHECK_ACTLEN; switch(i) { case TOK_CHECKSTATE: - have_state = action; - action->opcode = O_CHECK_STATE; - if (*av == NULL || - match_token(rule_options, *av) == TOK_COMMENT) { - action->arg1 = pack_object(tstate, - default_state_name, IPFW_TLV_STATE_NAME); + if (*av == NULL || match_token(rule_options, *av) == TOK_COMMENT) { + arg1 = pack_object(tstate, default_state_name, + IPFW_TLV_STATE_NAME); + lipfw_rule_add_cmd(ir, LIPFW_IR_STA, O_CHECK_STATE, 0, arg1); break; } if (*av[0] == ':') { - if (strcmp(*av + 1, "any") == 0) - action->arg1 = 0; - else if (state_check_name(*av + 1) == 0) - action->arg1 = pack_object(tstate, *av + 1, + if (strcmp(*av + 1, "any") == 0) { + arg1 = 0; + } else if (state_check_name(*av + 1) == 0) { + arg1 = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); - else - errx(EX_DATAERR, "Invalid state name %s", - *av); + } else + errx(EX_DATAERR, "Invalid state name %s", *av); + lipfw_rule_add_cmd(ir, LIPFW_IR_STA, O_CHECK_STATE, 0, arg1); av++; break; } @@ -4107,116 +4314,119 @@ break; case TOK_ABORT: - action->opcode = O_REJECT; - action->arg1 = ICMP_REJECT_ABORT; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_REJECT, 0, ICMP_REJECT_ABORT); break; case TOK_ABORT6: - action->opcode = O_UNREACH6; - action->arg1 = ICMP6_UNREACH_ABORT; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_UNREACH6, 0, ICMP6_UNREACH_ABORT); break; case TOK_ACCEPT: - action->opcode = O_ACCEPT; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_ACCEPT, 0, 0); break; case TOK_DENY: - action->opcode = O_DENY; - action->arg1 = 0; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_DENY, 0, 0); break; case TOK_REJECT: - action->opcode = O_REJECT; - action->arg1 = ICMP_UNREACH_HOST; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_REJECT, 0, ICMP_UNREACH_HOST); break; case TOK_RESET: - action->opcode = O_REJECT; - action->arg1 = ICMP_REJECT_RST; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_REJECT, 0, ICMP_REJECT_RST); break; case TOK_RESET6: - action->opcode = O_UNREACH6; - action->arg1 = ICMP6_UNREACH_RST; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_UNREACH6, 0, ICMP6_UNREACH_RST); break; case TOK_UNREACH: - action->opcode = O_REJECT; NEED1("missing reject code"); - fill_reject_code(&action->arg1, *av); + arg1 = fill_reject_code(*av); av++; - if (action->arg1 == ICMP_UNREACH_NEEDFRAG && isdigit(**av)) { + if (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; + + const ipfw_insn_u16 cmd_unreach = { + .o.opcode = O_REJECT, + .o.len = F_INSN_SIZE(ipfw_insn_u16), + .o.arg1 = arg1, + .ports = { mtu, }, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_ACT, &cmd_unreach.o); av++; - } + } else + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_REJECT, 0, arg1); break; case TOK_UNREACH6: - action->opcode = O_UNREACH6; NEED1("missing unreach code"); - fill_unreach6_code(&action->arg1, *av); + arg1 = fill_unreach6_code(*av); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_UNREACH6, 0, arg1); av++; break; case TOK_COUNT: - action->opcode = O_COUNT; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_COUNT, 0, 0); break; case TOK_NAT: - action->opcode = O_NAT; - action->len = F_INSN_SIZE(ipfw_insn_nat); - CHECK_ACTLEN; if (*av != NULL && _substrcmp(*av, "global") == 0) - action->arg1 = IP_FW_NAT44_GLOBAL; + arg1 = IP_FW_NAT44_GLOBAL; else - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + const ipfw_insn_nat cmd_nat = { + .o.opcode = O_NAT, + .o.len = F_INSN_SIZE(ipfw_insn_nat), + .o.arg1 = arg1, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_ACT, &cmd_nat.o); av++; break; case TOK_QUEUE: - action->opcode = O_QUEUE; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_QUEUE, 0, arg1); av++; break; case TOK_PIPE: - action->opcode = O_PIPE; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_PIPE, 0, arg1); av++; break; case TOK_SKIPTO: - action->opcode = O_SKIPTO; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_SKIPTO, 0, arg1); av++; break; case TOK_NETGRAPH: - action->opcode = O_NETGRAPH; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_NETGRAPH, 0, arg1); av++; break; case TOK_NGTEE: - action->opcode = O_NGTEE; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_NGTEE, 0, arg1); av++; break; case TOK_DIVERT: - action->opcode = O_DIVERT; - fill_divert_port(action, av[0], *(av - 1)); + arg1 = fill_divert_port(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_DIVERT, 0, arg1); av++; break; case TOK_TEE: - action->opcode = O_TEE; - fill_divert_port(action, av[0], *(av - 1)); + arg1 = fill_divert_port(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_TEE, 0, arg1); av++; break; case TOK_CALL: - action->opcode = O_CALLRETURN; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + arg1 = arg_or_targ(av[0], *(av - 1)); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_CALLRETURN, 0, arg1); av++; break; @@ -4288,37 +4498,35 @@ freeaddrinfo(res); if (family == PF_INET) { - ipfw_insn_sa *p = (ipfw_insn_sa *)action; - - action->opcode = O_FORWARD_IP; - action->len = F_INSN_SIZE(ipfw_insn_sa); - CHECK_ACTLEN; + struct sockaddr_in *sin = (struct sockaddr_in *)&result; /* * In the kernel we assume AF_INET and use only * sin_port and sin_addr. Remember to set sin_len as * the routing code seems to use it too. */ - p->sa.sin_len = sizeof(struct sockaddr_in); - p->sa.sin_family = AF_INET; - p->sa.sin_port = port_number; - p->sa.sin_addr.s_addr = - ((struct sockaddr_in *)&result)->sin_addr.s_addr; + const ipfw_insn_sa cmd_fwd_inet = { + .o.opcode = O_FORWARD_IP, + .o.len = F_INSN_SIZE(ipfw_insn_sa), + .sa.sin_len = sizeof(struct sockaddr_in), + .sa.sin_family = AF_INET, + .sa.sin_port = port_number, + .sa.sin_addr = sin->sin_addr, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_ACT, &cmd_fwd_inet.o); } else if (family == PF_INET6) { - ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action; - - 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; - p->sa.sin6_port = port_number; - p->sa.sin6_flowinfo = 0; - p->sa.sin6_scope_id = - ((struct sockaddr_in6 *)&result)->sin6_scope_id; - bcopy(&((struct sockaddr_in6*)&result)->sin6_addr, - &p->sa.sin6_addr, sizeof(p->sa.sin6_addr)); + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&result; + + const ipfw_insn_sa6 cmd_fwd_inet6 = { + .o.opcode = O_FORWARD_IP6, + .o.len = F_INSN_SIZE(ipfw_insn_sa6), + .sa.sin6_len = sizeof(struct sockaddr_in6), + .sa.sin6_family = AF_INET6, + .sa.sin6_port = port_number, + .sa.sin6_scope_id = sin6->sin6_scope_id, + .sa.sin6_addr = sin6->sin6_addr, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_ACT, &cmd_fwd_inet6.o); } else { errx(EX_DATAERR, "Invalid address family in forward action"); } @@ -4327,7 +4535,7 @@ } case TOK_COMMENT: /* pretend it is a 'count' rule followed by the comment */ - action->opcode = O_COUNT; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_COUNT, 0, 0); av--; /* go back... */ break; @@ -4336,20 +4544,20 @@ int numfibs; size_t intsize = sizeof(int); - action->opcode = O_SETFIB; NEED1("missing fib number"); if (_substrcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TARG; + arg1 = IP_FW_TARG; } else { - action->arg1 = strtoul(*av, NULL, 10); + arg1 = strtoul(*av, NULL, 10); if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) errx(EX_DATAERR, "fibs not supported.\n"); - if (action->arg1 >= numfibs) /* Temporary */ + if (arg1 >= numfibs) /* Temporary */ errx(EX_DATAERR, "fib too large.\n"); /* Add high-order bit to fib to make room for tablearg*/ - action->arg1 |= 0x8000; + arg1 |= 0x8000; } + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_SETFIB, 0, arg1); av++; break; } @@ -4358,47 +4566,53 @@ { int code; - action->opcode = O_SETDSCP; NEED1("missing DSCP code"); if (_substrcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TARG; + arg1 = IP_FW_TARG; } else { if (isalpha(*av[0])) { if ((code = match_token(f_ipdscp, *av)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); - action->arg1 = code; + arg1 = code; } else - action->arg1 = strtoul(*av, NULL, 10); + arg1 = strtoul(*av, NULL, 10); /* * Add high-order bit to DSCP to make room * for tablearg */ - action->arg1 |= 0x8000; + arg1 |= 0x8000; } + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_SETDSCP, 0, arg1); av++; break; } case TOK_REASS: - action->opcode = O_REASS; + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_REASS, 0, 0); break; case TOK_RETURN: - fill_cmd(action, O_CALLRETURN, F_NOT, 0); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_CALLRETURN, F_NOT, 0); break; case TOK_SETMARK: { - action->opcode = O_SETMARK; - action->len = F_INSN_SIZE(ipfw_insn_u32); NEED1("missing mark"); + uint32_t mark = 0; + if (strcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TARG; + arg1 = IP_FW_TARG; } else { - ((ipfw_insn_u32 *)action)->d[0] = - strtoul(*av, NULL, 0); + mark = strtoul(*av, NULL, 0); /* This is not a tablearg */ - action->arg1 |= 0x8000; + arg1 = 0x8000; } + const ipfw_insn_u32 cmd_mark = { + .o.opcode = O_SETMARK, + .o.len = F_INSN_SIZE(ipfw_insn_u32), + .o.arg1 = arg1, + .d = { mark }, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_ACT, &cmd_mark.o); av++; CHECK_CMDLEN; break; @@ -4411,15 +4625,12 @@ idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_EXTERNAL_ACTION, 0, idx); NEED1("Missing MSS value"); - action = next_cmd(action, &ablen); - action->len = 1; - CHECK_ACTLEN; mss = strtoul(*av, NULL, 10); if (mss == 0 || mss > UINT16_MAX) errx(EX_USAGE, "invalid MSS value %s", *av); - fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_EXTERNAL_DATA, 0, mss); av++; break; } @@ -4445,12 +4656,9 @@ idx = pack_object(tstate, *av, IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_EXTERNAL_ACTION, 0, idx); av++; NEED1("Missing eaction instance name"); - action = next_cmd(action, &ablen); - action->len = 1; - CHECK_ACTLEN; if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction instance name %s", *av); @@ -4462,11 +4670,10 @@ idx = pack_object(tstate, *av, 0); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx); + lipfw_rule_add_cmd(ir, LIPFW_IR_ACT, O_EXTERNAL_INSTANCE, 0, idx); av++; } } - action = next_cmd(action, &ablen); /* * [altq queuename] -- altq tag, optional @@ -4554,7 +4761,7 @@ cmd = next_cmd(cmd, &cblen); } - if (have_state) { /* must be a check-state, we are done */ + if (buf_is_empty(ir, LIPFW_IR_STA)) { /* must be a check-state, we are done */ if (*av != NULL && match_token(rule_options, *av) == TOK_COMMENT) { /* check-state has a comment */ @@ -5038,7 +5245,7 @@ if (open_par) errx(EX_USAGE, "keep-state or record-state cannot be part " "of an or block"); - if (have_state) + if (buf_is_empty(ir, LIPFW_IR_STA)) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); if (*av != NULL && *av[0] == ':') { @@ -5051,9 +5258,9 @@ } else uidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); - have_state = cmd; have_rstate = i == TOK_RECORDSTATE; fill_cmd(cmd, O_KEEP_STATE, 0, uidx); + lipfw_rule_add_cmd(ir, LIPFW_IR_STA, O_KEEP_STATE, 0, uidx); break; } @@ -5065,10 +5272,9 @@ if (open_par) errx(EX_USAGE, "limit or set-limit cannot be part of an or block"); - if (have_state) + if (buf_is_empty(ir, LIPFW_IR_STA)) errx(EX_USAGE, "only one of keep-state, record-state, " " limit and set-limit is allowed"); - have_state = cmd; have_rstate = i == TOK_SETLIMIT; cmd->len = F_INSN_SIZE(ipfw_insn_limit); @@ -5100,6 +5306,13 @@ } else cmd->arg1 = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); + const ipfw_insn_limit cmd_limit = { + .o.opcode = O_LIMIT, + .o.len = F_INSN_SIZE(ipfw_insn_limit), + .limit_mask = c->limit_mask, + .conn_limit = c->conn_limit, + }; + lipfw_rule_add_cmd_raw(ir, LIPFW_IR_STA, &cmd_limit.o); break; } @@ -5308,7 +5521,7 @@ done: - if (!have_state && have_skipcmd) + if (!buf_is_empty(ir, LIPFW_IR_STA) && have_skipcmd) warnx("Rule contains \"defer-immediate-action\" " "and doesn't contain any state-related options."); @@ -5334,9 +5547,13 @@ /* * generate O_PROBE_STATE if necessary */ - if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { - fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); - dst = next_cmd(dst, &rblen); + if (buf_is_empty(ir, LIPFW_IR_STA)) { + ipfw_insn *tmp = buf_get(ir, LIPFW_IR_STA); + + if (tmp->opcode != O_CHECK_STATE && !have_rstate) { + fill_cmd(dst, O_PROBE_STATE, 0, tmp->arg1); + dst = next_cmd(dst, &rblen); + } } /* @@ -5364,11 +5581,15 @@ /* * put back the have_state command as last opcode */ - 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; + if (buf_is_empty(ir, LIPFW_IR_STA)) { + ipfw_insn *tmp = buf_get(ir, LIPFW_IR_STA); + + if (tmp->opcode != O_CHECK_STATE) { + i = F_LEN(ir->state_insn); + CHECK_RBUFLEN(i); + bcopy(tmp, dst, i * sizeof(uint32_t)); + dst += i; + } } /* @@ -5409,7 +5630,9 @@ /* * copy all other actions */ - for (src = (ipfw_insn *)actbuf; src != action; src += i) { + struct cmd_buffer *_abuf = &ir->buffers[LIPFW_IR_ACT]; + ipfw_insn *action = (ipfw_insn *)(void *)&_abuf->data[_abuf->off]; + for (src = (ipfw_insn *)(void *)_abuf->data; src != action; src += i) { i = F_LEN(src); CHECK_RBUFLEN(i); bcopy(src, dst, i * sizeof(uint32_t)); @@ -5544,15 +5767,17 @@ uint32_t rulebuf[1024]; int rbufsize, default_off, tlen, rlen; size_t sz; - struct tidx ts; struct ip_fw_rule *rule; caddr_t tbuf; ip_fw3_opheader *op3; ipfw_obj_ctlv *ctlv, *tstate; + ipfw_ctx_t ctx; + struct tidx ts = { .ctx = &ctx }; + + lipfw_ctx_init(&ctx); rbufsize = sizeof(rulebuf); memset(rulebuf, 0, rbufsize); - memset(&ts, 0, sizeof(ts)); /* Optimize case with no tables */ default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader); @@ -5561,7 +5786,7 @@ rule = (struct ip_fw_rule *)(ctlv + 1); rbufsize -= default_off; - compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); + compile_rule(&ctx, av, (uint32_t *)rule, &rbufsize, &ts); /* Align rule size to u64 boundary */ rlen = roundup2(rbufsize, sizeof(uint64_t)); @@ -5615,11 +5840,10 @@ bp_free(&bp); } + lipfw_ctx_free(&ctx); + if (tbuf != NULL) free(tbuf); - - if (ts.idx != NULL) - free(ts.idx); } /* diff --git a/sbin/ipfw/ipv6.c b/sbin/ipfw/ipv6.c --- a/sbin/ipfw/ipv6.c +++ b/sbin/ipfw/ipv6.c @@ -55,8 +55,8 @@ { NULL, 0 } }; -void -fill_unreach6_code(u_short *codep, char *str) +uint16_t +fill_unreach6_code(char *str) { int val; char *s; @@ -66,8 +66,7 @@ val = match_token(icmp6codes, str); if (val < 0) errx(EX_DATAERR, "unknown ICMPv6 unreachable code ``%s''", str); - *codep = val; - return; + return (val); } void diff --git a/sbin/ipfw/tests/test_add_rule.py b/sbin/ipfw/tests/test_add_rule.py --- a/sbin/ipfw/tests/test_add_rule.py +++ b/sbin/ipfw/tests/test_add_rule.py @@ -173,6 +173,39 @@ }, id="test_comment", ), + pytest.param( + { + "in": "add tcp-setmss 123 ip from any to 1.2.3.4", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_EACTION, idx=1, name="tcp-setmss"), + ], + "insns": [ + InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"), + Insn(IpFwOpcode.O_EXTERNAL_ACTION, arg1=1), + Insn(IpFwOpcode.O_EXTERNAL_DATA, arg1=123), + ], + }, + }, + id="test_eaction_tcp-setmss", + ), + pytest.param( + { + "in": "add eaction ntpv6 AAA ip from any to 1.2.3.4", + "out": { + "objs": [ + NTlv(IpFwTlvType.IPFW_TLV_EACTION, idx=1, name="ntpv6"), + NTlv(0, idx=2, name="AAA"), + ], + "insns": [ + InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"), + Insn(IpFwOpcode.O_EXTERNAL_ACTION, arg1=1), + Insn(IpFwOpcode.O_EXTERNAL_INSTANCE, arg1=2), + ], + }, + }, + id="test_eaction_ntp", + ), ], ) def test_add_rule(self, rule): diff --git a/tests/atf_python/sys/netpfil/ipfw/ioctl.py b/tests/atf_python/sys/netpfil/ipfw/ioctl.py --- a/tests/atf_python/sys/netpfil/ipfw/ioctl.py +++ b/tests/atf_python/sys/netpfil/ipfw/ioctl.py @@ -90,6 +90,11 @@ for obj in self.obj_list: obj.print_obj(prepend) + def print_obj_hex(self, prepend=""): + print(prepend) + print() + print(" ".join(["x{:02X}".format(b) for b in bytes(self)])) + @classmethod def _validate(cls, data): if len(data) < sizeof(IpFwObjTlv): @@ -487,6 +492,7 @@ [ AttrDescr(IpFwTlvType.IPFW_TLV_TBL_NAME, NTlv), AttrDescr(IpFwTlvType.IPFW_TLV_STATE_NAME, NTlv), + AttrDescr(IpFwTlvType.IPFW_TLV_EACTION, NTlv), ], True, ),