diff options
Diffstat (limited to 'src/exthdr.c')
-rw-r--r-- | src/exthdr.c | 157 |
1 files changed, 108 insertions, 49 deletions
diff --git a/src/exthdr.c b/src/exthdr.c index 0b23e0d3..60c7cd1e 100644 --- a/src/exthdr.c +++ b/src/exthdr.c @@ -10,11 +10,10 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ +#include <nft.h> + #include <stddef.h> -#include <stdlib.h> #include <stdio.h> -#include <stdint.h> -#include <string.h> #include <netinet/in.h> #include <netinet/ip6.h> @@ -22,6 +21,7 @@ #include <headers.h> #include <expression.h> #include <statement.h> +#include <sctp_chunk.h> static const struct exthdr_desc *exthdr_definitions[PROTO_DESC_MAX + 1] = { [EXTHDR_DESC_HBH] = &exthdr_hbh, @@ -45,6 +45,9 @@ static const struct exthdr_desc *exthdr_find_desc(enum exthdr_desc_id desc_id) static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx) { + const char *name = expr->exthdr.desc ? + expr->exthdr.desc->name : "unknown-exthdr"; + if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) { /* Offset calculation is a bit hacky at this point. * There might be a tcp option one day with another @@ -52,23 +55,42 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx) */ unsigned int offset = expr->exthdr.offset / 64; - nft_print(octx, "tcp option %s", expr->exthdr.desc->name); + if (expr->exthdr.desc == NULL) { + if (expr->exthdr.offset == 0 && + expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) { + nft_print(octx, "tcp option %d", expr->exthdr.raw_type); + return; + } + + nft_print(octx, "tcp option @%u,%u,%u", expr->exthdr.raw_type, + expr->exthdr.offset, expr->len); + return; + } + + nft_print(octx, "tcp option %s", name); if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) return; if (offset) nft_print(octx, "%d", offset); nft_print(octx, " %s", expr->exthdr.tmpl->token); } else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) { - nft_print(octx, "ip option %s", expr->exthdr.desc->name); + nft_print(octx, "ip option %s", name); + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + return; + nft_print(octx, " %s", expr->exthdr.tmpl->token); + } else if (expr->exthdr.op == NFT_EXTHDR_OP_SCTP) { + nft_print(octx, "sctp chunk %s", expr->exthdr.desc->name); if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) return; nft_print(octx, " %s", expr->exthdr.tmpl->token); + } else if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) { + nft_print(octx, "dccp option %d", expr->exthdr.raw_type); + return; } else { if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) - nft_print(octx, "exthdr %s", expr->exthdr.desc->name); + nft_print(octx, "exthdr %s", name); else { - nft_print(octx, "%s %s", - expr->exthdr.desc ? expr->exthdr.desc->name : "unknown-exthdr", + nft_print(octx, "%s %s", name, expr->exthdr.tmpl->token); } } @@ -79,6 +101,7 @@ static bool exthdr_expr_cmp(const struct expr *e1, const struct expr *e2) return e1->exthdr.desc == e2->exthdr.desc && e1->exthdr.tmpl == e2->exthdr.tmpl && e1->exthdr.op == e2->exthdr.op && + e1->exthdr.raw_type == e2->exthdr.raw_type && e1->exthdr.flags == e2->exthdr.flags; } @@ -89,11 +112,13 @@ static void exthdr_expr_clone(struct expr *new, const struct expr *expr) new->exthdr.offset = expr->exthdr.offset; new->exthdr.op = expr->exthdr.op; new->exthdr.flags = expr->exthdr.flags; + new->exthdr.raw_type = expr->exthdr.raw_type; } #define NFTNL_UDATA_EXTHDR_DESC 0 #define NFTNL_UDATA_EXTHDR_TYPE 1 -#define NFTNL_UDATA_EXTHDR_MAX 2 +#define NFTNL_UDATA_EXTHDR_OP 2 +#define NFTNL_UDATA_EXTHDR_MAX 3 static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data) { @@ -104,6 +129,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data) switch (type) { case NFTNL_UDATA_EXTHDR_DESC: case NFTNL_UDATA_EXTHDR_TYPE: + case NFTNL_UDATA_EXTHDR_OP: if (len != sizeof(uint32_t)) return -1; break; @@ -118,6 +144,7 @@ static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data) static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr) { const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {}; + enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6; const struct exthdr_desc *desc; unsigned int type; uint32_t desc_id; @@ -132,22 +159,39 @@ static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr) !ud[NFTNL_UDATA_EXTHDR_TYPE]) return NULL; - desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]); - desc = exthdr_find_desc(desc_id); - if (!desc) - return NULL; + if (ud[NFTNL_UDATA_EXTHDR_OP]) + op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]); + desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]); type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]); - return exthdr_expr_alloc(&internal_location, desc, type); + switch (op) { + case NFT_EXTHDR_OP_IPV6: + desc = exthdr_find_desc(desc_id); + + return exthdr_expr_alloc(&internal_location, desc, type); + case NFT_EXTHDR_OP_TCPOPT: + return tcpopt_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_IPV4: + return ipopt_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_SCTP: + return sctp_chunk_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_DCCP: + return dccpopt_expr_alloc(&internal_location, type); + case __NFT_EXTHDR_OP_MAX: + return NULL; + } + + return NULL; } static unsigned int expr_exthdr_type(const struct exthdr_desc *desc, const struct proto_hdr_template *tmpl) { - unsigned int offset = (unsigned int)(tmpl - &desc->templates[0]); - - return offset / sizeof(*tmpl); + return (unsigned int)(tmpl - &desc->templates[0]); } static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf, @@ -156,9 +200,23 @@ static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf, const struct proto_hdr_template *tmpl = expr->exthdr.tmpl; const struct exthdr_desc *desc = expr->exthdr.desc; unsigned int type = expr_exthdr_type(desc, tmpl); + enum nft_exthdr_op op = expr->exthdr.op; - nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id); nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type); + switch (op) { + case NFT_EXTHDR_OP_IPV6: + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id); + break; + case NFT_EXTHDR_OP_TCPOPT: + case NFT_EXTHDR_OP_IPV4: + case NFT_EXTHDR_OP_SCTP: + case NFT_EXTHDR_OP_DCCP: + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type); + break; + default: + return -1; + } return 0; } @@ -192,7 +250,9 @@ struct expr *exthdr_expr_alloc(const struct location *loc, expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, BYTEORDER_BIG_ENDIAN, tmpl->len); expr->exthdr.desc = desc; + expr->exthdr.raw_type = desc ? desc->type : 0; expr->exthdr.tmpl = tmpl; + expr->exthdr.offset = tmpl->offset; return expr; } @@ -228,7 +288,7 @@ struct stmt *exthdr_stmt_alloc(const struct location *loc, return stmt; } -static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = { +static const struct exthdr_desc *exthdr_protocols[UINT8_MAX + 1] = { [IPPROTO_HOPOPTS] = &exthdr_hbh, [IPPROTO_ROUTING] = &exthdr_rt, [IPPROTO_FRAGMENT] = &exthdr_frag, @@ -269,18 +329,23 @@ void exthdr_init_raw(struct expr *expr, uint8_t type, unsigned int i; assert(expr->etype == EXPR_EXTHDR); + expr->exthdr.raw_type = type; + if (op == NFT_EXTHDR_OP_TCPOPT) return tcpopt_init_raw(expr, type, offset, len, flags); if (op == NFT_EXTHDR_OP_IPV4) return ipopt_init_raw(expr, type, offset, len, flags, true); + if (op == NFT_EXTHDR_OP_SCTP) + return sctp_chunk_init_raw(expr, type, offset, len, flags); + if (op == NFT_EXTHDR_OP_DCCP) + return dccpopt_init_raw(expr, type, offset, len); expr->len = len; expr->exthdr.flags = flags; expr->exthdr.offset = offset; expr->exthdr.desc = NULL; - if (type < array_size(exthdr_protocols)) - expr->exthdr.desc = exthdr_protocols[type]; + expr->exthdr.desc = exthdr_protocols[type]; if (expr->exthdr.desc == NULL) goto out; @@ -322,16 +387,7 @@ static unsigned int mask_length(const struct expr *mask) bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift) { unsigned int off, mask_offset, mask_len; - - if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 && - expr->exthdr.tmpl != &exthdr_unknown_template) - return false; - - /* In case we are handling tcp options instead of the default ipv6 - * extension headers. - */ - if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) - return tcpopt_find_template(expr, mask, shift); + bool found; mask_offset = mpz_scan1(mask->value, 0); mask_len = mask_length(mask); @@ -340,24 +396,31 @@ bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned i off += round_up(mask->len, BITS_PER_BYTE) - mask_len; /* Handle ip options after the offset and mask have been calculated. */ - if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) { - if (ipopt_find_template(expr, off, mask_len - mask_offset)) { - *shift = mask_offset; - return true; - } else { + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_IPV4: + found = ipopt_find_template(expr, off, mask_len - mask_offset); + break; + case NFT_EXTHDR_OP_TCPOPT: + found = tcpopt_find_template(expr, off, mask_len - mask_offset); + break; + case NFT_EXTHDR_OP_IPV6: + exthdr_init_raw(expr, expr->exthdr.raw_type, + off, mask_len - mask_offset, expr->exthdr.op, 0); + + /* still failed to find a template... Bug. */ + if (expr->exthdr.tmpl == &exthdr_unknown_template) return false; - } + found = true; + break; + default: + found = false; + break; } - exthdr_init_raw(expr, expr->exthdr.desc->type, - off, mask_len - mask_offset, expr->exthdr.op, 0); - - /* still failed to find a template... Bug. */ - if (expr->exthdr.tmpl == &exthdr_unknown_template) - return false; + if (found) + *shift = mask_offset; - *shift = mask_offset; - return true; + return found; } #define HDR_TEMPLATE(__name, __dtype, __type, __member) \ @@ -391,7 +454,6 @@ const struct exthdr_desc exthdr_rt2 = { .name = "rt2", .id = EXTHDR_DESC_RT2, .type = IPPROTO_ROUTING, - .proto_key = 2, .templates = { [RT2HDR_RESERVED] = {}, [RT2HDR_ADDR] = {}, @@ -405,7 +467,6 @@ const struct exthdr_desc exthdr_rt0 = { .name = "rt0", .id = EXTHDR_DESC_RT0, .type = IPPROTO_ROUTING, - .proto_key = 0, .templates = { [RT0HDR_RESERVED] = RT0_FIELD("reserved", ip6r0_reserved, &integer_type), [RT0HDR_ADDR_1] = RT0_FIELD("addr[1]", ip6r0_addr[0], &ip6addr_type), @@ -421,7 +482,6 @@ const struct exthdr_desc exthdr_rt4 = { .name = "srh", .id = EXTHDR_DESC_SRH, .type = IPPROTO_ROUTING, - .proto_key = 4, .templates = { [RT4HDR_LASTENT] = RT4_FIELD("last-entry", ip6r4_last_entry, &integer_type), [RT4HDR_FLAGS] = RT4_FIELD("flags", ip6r4_flags, &integer_type), @@ -440,7 +500,6 @@ const struct exthdr_desc exthdr_rt = { .name = "rt", .id = EXTHDR_DESC_RT, .type = IPPROTO_ROUTING, - .proto_key = -1, #if 0 .protocol_key = RTHDR_TYPE, .protocols = { |