]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
ipopt: missing ipopt.h and ipopt.c files
authorPablo Neira Ayuso <pablo@netfilter.org>
Thu, 4 Jul 2019 18:01:02 +0000 (20:01 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Thu, 4 Jul 2019 18:04:56 +0000 (20:04 +0200)
Fixes: 226a0e072d5c ("exthdr: add support for matching IPv4 options")
Reported-by: Phil Sutter <phil@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/ipopt.h [new file with mode: 0644]
src/ipopt.c [new file with mode: 0644]

diff --git a/include/ipopt.h b/include/ipopt.h
new file mode 100644 (file)
index 0000000..d8d4806
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef NFTABLES_IPOPT_H
+#define NFTABLES_IPOPT_H
+
+#include <proto.h>
+#include <exthdr.h>
+#include <statement.h>
+
+extern struct expr *ipopt_expr_alloc(const struct location *loc,
+                                     uint8_t type, uint8_t field, uint8_t ptr);
+
+extern void ipopt_init_raw(struct expr *expr, uint8_t type,
+                           unsigned int offset, unsigned int len,
+                           uint32_t flags, bool set_unknown);
+
+extern bool ipopt_find_template(struct expr *expr, unsigned int offset,
+                         unsigned int len);
+
+enum ipopt_fields {
+       IPOPT_FIELD_INVALID,
+       IPOPT_FIELD_TYPE,
+       IPOPT_FIELD_LENGTH,
+       IPOPT_FIELD_VALUE,
+       IPOPT_FIELD_PTR,
+       IPOPT_FIELD_ADDR_0,
+};
+
+extern const struct exthdr_desc *ipopt_protocols[UINT8_MAX];
+
+#endif /* NFTABLES_IPOPT_H */
diff --git a/src/ipopt.c b/src/ipopt.c
new file mode 100644 (file)
index 0000000..b3d0279
--- /dev/null
@@ -0,0 +1,159 @@
+#include <stdint.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+
+#include <utils.h>
+#include <headers.h>
+#include <expression.h>
+#include <ipopt.h>
+
+static const struct proto_hdr_template ipopt_unknown_template =
+       PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0);
+
+#define PHT(__token, __offset, __len) \
+       PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \
+                          __offset, __len)
+static const struct exthdr_desc ipopt_lsrr = {
+       .name           = "lsrr",
+       .type           = IPOPT_LSRR,
+       .templates      = {
+               [IPOPT_FIELD_TYPE]              = PHT("type",    0,  8),
+               [IPOPT_FIELD_LENGTH]            = PHT("length",  8,  8),
+               [IPOPT_FIELD_PTR]               = PHT("ptr",    16,  8),
+               [IPOPT_FIELD_ADDR_0]            = PHT("addr",   24, 32),
+       },
+};
+
+static const struct exthdr_desc ipopt_rr = {
+       .name           = "rr",
+       .type           = IPOPT_RR,
+       .templates      = {
+               [IPOPT_FIELD_TYPE]              = PHT("type",   0,   8),
+               [IPOPT_FIELD_LENGTH]            = PHT("length",  8,  8),
+               [IPOPT_FIELD_PTR]               = PHT("ptr",    16,  8),
+               [IPOPT_FIELD_ADDR_0]            = PHT("addr",   24, 32),
+       },
+};
+
+static const struct exthdr_desc ipopt_ssrr = {
+       .name           = "ssrr",
+       .type           = IPOPT_SSRR,
+       .templates      = {
+               [IPOPT_FIELD_TYPE]              = PHT("type",   0,   8),
+               [IPOPT_FIELD_LENGTH]            = PHT("length",  8,  8),
+               [IPOPT_FIELD_PTR]               = PHT("ptr",    16,  8),
+               [IPOPT_FIELD_ADDR_0]            = PHT("addr",   24, 32),
+       },
+};
+
+static const struct exthdr_desc ipopt_ra = {
+       .name           = "ra",
+       .type           = IPOPT_RA,
+       .templates      = {
+               [IPOPT_FIELD_TYPE]              = PHT("type",   0,   8),
+               [IPOPT_FIELD_LENGTH]            = PHT("length", 8,   8),
+               [IPOPT_FIELD_VALUE]             = PHT("value",  16, 16),
+       },
+};
+
+const struct exthdr_desc *ipopt_protocols[UINT8_MAX] = {
+       [IPOPT_LSRR]            = &ipopt_lsrr,
+       [IPOPT_RR]              = &ipopt_rr,
+       [IPOPT_SSRR]            = &ipopt_ssrr,
+       [IPOPT_RA]              = &ipopt_ra,
+};
+
+static unsigned int calc_offset(const struct exthdr_desc *desc,
+                               const struct proto_hdr_template *tmpl,
+                               unsigned int arg)
+{
+       if (!desc || tmpl == &ipopt_unknown_template)
+               return 0;
+
+       switch (desc->type) {
+       case IPOPT_RR:
+       case IPOPT_LSRR:
+       case IPOPT_SSRR:
+               if (tmpl == &desc->templates[IPOPT_FIELD_ADDR_0])
+                       return (tmpl->offset < 24) ? 0 : arg;
+               return 0;
+       default:
+               return 0;
+       }
+}
+
+struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type,
+                              uint8_t field, uint8_t ptr)
+{
+       const struct proto_hdr_template *tmpl;
+       const struct exthdr_desc *desc;
+       struct expr *expr;
+
+       desc = ipopt_protocols[type];
+       tmpl = &desc->templates[field];
+       if (!tmpl)
+               return NULL;
+
+       expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
+                         BYTEORDER_BIG_ENDIAN, tmpl->len);
+       expr->exthdr.desc   = desc;
+       expr->exthdr.tmpl   = tmpl;
+       expr->exthdr.op     = NFT_EXTHDR_OP_IPV4;
+       expr->exthdr.offset = calc_offset(desc, tmpl, ptr);
+
+       return expr;
+}
+
+void ipopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset,
+                    unsigned int len, uint32_t flags, bool set_unknown)
+{
+       const struct proto_hdr_template *tmpl;
+       unsigned int i;
+
+       assert(expr->etype == EXPR_EXTHDR);
+
+       expr->len = len;
+       expr->exthdr.flags = flags;
+       expr->exthdr.offset = offset;
+
+       assert(type < array_size(ipopt_protocols));
+       expr->exthdr.desc = ipopt_protocols[type];
+       expr->exthdr.flags = flags;
+
+       for (i = 0; i < array_size(expr->exthdr.desc->templates); ++i) {
+               tmpl = &expr->exthdr.desc->templates[i];
+
+               /* Make sure that it's the right template based on offset and len */
+               if (tmpl->offset != offset || tmpl->len != len)
+                       continue;
+
+               if (flags & NFT_EXTHDR_F_PRESENT)
+                       expr->dtype = &boolean_type;
+               else
+                       expr->dtype = tmpl->dtype;
+               expr->exthdr.tmpl = tmpl;
+               expr->exthdr.op   = NFT_EXTHDR_OP_IPV4;
+               break;
+       }
+       if (i == array_size(expr->exthdr.desc->templates) && set_unknown) {
+               expr->exthdr.tmpl = &ipopt_unknown_template;
+               expr->exthdr.op   = NFT_EXTHDR_OP_IPV4;
+       }
+}
+
+bool ipopt_find_template(struct expr *expr, unsigned int offset,
+                         unsigned int len)
+{
+       if (expr->exthdr.tmpl != &ipopt_unknown_template)
+               return false;
+
+       ipopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0, false);
+
+       if (expr->exthdr.tmpl == &ipopt_unknown_template)
+               return false;
+
+       return true;
+}