#include #include #include #include #include #include #include #include #include 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; }