summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2019-07-04 20:01:02 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2019-07-04 20:04:56 +0200
commite33b8a01fc122a3dbc281629c1d3691d05f5d844 (patch)
tree96d264f2ad4c37b04ecd403e3b318c7d3c180f2b
parent12116390acb3b924393fca6ed45101f305b4d35c (diff)
ipopt: missing ipopt.h and ipopt.c files
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>
-rw-r--r--include/ipopt.h29
-rw-r--r--src/ipopt.c159
2 files changed, 188 insertions, 0 deletions
diff --git a/include/ipopt.h b/include/ipopt.h
new file mode 100644
index 00000000..d8d48066
--- /dev/null
+++ b/include/ipopt.h
@@ -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
index 00000000..b3d0279d
--- /dev/null
+++ b/src/ipopt.c
@@ -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;
+}