From fac10ea799fe9b6158d74f66d6ad46536d38a545 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 18 Mar 2009 04:55:00 +0100 Subject: Initial commit --- src/payload.c | 908 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 908 insertions(+) create mode 100644 src/payload.c (limited to 'src/payload.c') diff --git a/src/payload.c b/src/payload.c new file mode 100644 index 00000000..b7fbcb36 --- /dev/null +++ b/src/payload.c @@ -0,0 +1,908 @@ +/* + * Payload expression protocol and type definitions and related functions. + * + * Copyright (c) 2008 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static const char *payload_base_names[] = { + [PAYLOAD_BASE_INVALID] = "invalid", + [PAYLOAD_BASE_LL_HDR] = "link layer", + [PAYLOAD_BASE_NETWORK_HDR] = "network layer", + [PAYLOAD_BASE_TRANSPORT_HDR] = "transport layer", +}; + +static const char *payload_base_tokens[] = { + [PAYLOAD_BASE_INVALID] = "invalid", + [PAYLOAD_BASE_LL_HDR] = "ll", + [PAYLOAD_BASE_NETWORK_HDR] = "nh", + [PAYLOAD_BASE_TRANSPORT_HDR] = "th", +}; + +static const struct payload_template payload_unknown_template = + PAYLOAD_TEMPLATE("unknown", &invalid_type, 0, 0); + +static const struct payload_desc payload_unknown_desc = { + .name = "unknown", + .base = PAYLOAD_BASE_INVALID, +}; + +static void payload_expr_print(const struct expr *expr) +{ + const struct payload_desc *desc; + const struct payload_template *tmpl; + + desc = expr->payload.desc; + tmpl = expr->payload.tmpl; + if (desc != NULL && tmpl != NULL) + printf("%s %s", desc->name, tmpl->token); + else + printf("payload @%s,%u,%u", + payload_base_tokens[expr->payload.base], + expr->payload.offset, expr->len); +} + +static const struct expr_ops payload_expr_ops = { + .type = EXPR_PAYLOAD, + .name = "payload", + .print = payload_expr_print, +}; + +struct expr *payload_expr_alloc(const struct location *loc, + const struct payload_desc *desc, + unsigned int type) +{ + const struct payload_template *tmpl; + enum payload_bases base; + struct expr *expr; + unsigned int flags = 0; + + if (desc != NULL) { + tmpl = &desc->templates[type]; + base = desc->base; + if (type == desc->protocol_key) + flags = PAYLOAD_PROTOCOL_EXPR; + } else { + tmpl = &payload_unknown_template; + base = PAYLOAD_BASE_INVALID; + } + + expr = expr_alloc(loc, &payload_expr_ops, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, tmpl->len); + expr->payload.desc = desc; + expr->payload.tmpl = tmpl; + expr->payload.base = base; + expr->payload.offset = tmpl->offset; + expr->payload.flags = flags; + return expr; +} + +void payload_init_raw(struct expr *expr, enum payload_bases base, + unsigned int offset, unsigned int len) +{ + expr->payload.base = base; + expr->payload.offset = offset; + expr->len = len; +} + +/** + * payload_select_proto - find protocol description by protocol value linking + * it to lower layer protocol + * + * @base: lower layer protocol description + * @num: protocol value + */ +static const struct payload_desc * +payload_select_proto(const struct payload_desc *base, unsigned int num) +{ + unsigned int i; + + for (i = 0; i < array_size(base->protocols); i++) { + if (base->protocols[i].num == num) + return base->protocols[i].desc; + } + return NULL; +} + +/** + * payload_proto_val - return protocol number linking two protocols together + * + * @base: lower layer protocol description + * @desc: upper layer protocol description + */ +static unsigned int payload_proto_val(const struct payload_desc *base, + const struct payload_desc *desc) +{ + unsigned int i; + + for (i = 0; i < array_size(base->protocols); i++) { + if (base->protocols[i].desc == desc) + return base->protocols[i].num; + } + return 0; +} + +static const struct dev_payload_desc dev_payload_desc[] = { + DEV_PAYLOAD_DESC(ARPHRD_ETHER, &payload_eth), +}; + +/** + * payload_dev_type - return arphrd type linking a device and a protocol together + * + * @desc: the protocol description + * @res: pointer to result + */ +static int payload_dev_type(const struct payload_desc *desc, uint16_t *res) +{ + unsigned int i; + + for (i = 0; i < array_size(dev_payload_desc); i++) { + if (dev_payload_desc[i].desc == desc) { + *res = dev_payload_desc[i].type; + return 0; + } + } + return -1; +} + +/** + * payload_dev_desc - return protocol description for an arphrd type + * + * @type: the arphrd type + */ +static const struct payload_desc *payload_dev_desc(uint16_t type) +{ + unsigned int i; + + for (i = 0; i < array_size(dev_payload_desc); i++) { + if (dev_payload_desc[i].type == type) + return dev_payload_desc[i].desc; + } + return NULL; +} + +static const struct payload_hook_desc payload_hooks[] = { + [NFPROTO_BRIDGE] = PAYLOAD_HOOK(PAYLOAD_BASE_LL_HDR, &payload_eth), + [NFPROTO_IPV4] = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_ip), + [NFPROTO_IPV6] = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_ip6), + [NFPROTO_ARP] = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_arp), +}; + +/** + * payload_ctx_init - initialize payload context for a given hook family + * + * @ctx: payload context + * @family: hook family + */ +void payload_ctx_init(struct payload_ctx *ctx, unsigned int family) +{ + const struct payload_hook_desc *h = &payload_hooks[family]; + + memset(ctx, 0, sizeof(*ctx)); + ctx->family = family; + ctx->protocol[h->base].desc = h->desc; +} + +/** + * payload_ctx_update_meta - update payload context with meta expression + * + * @ctx: payload context + * @expr: relational meta expression + * + * Update LL payload context based on IIFTYPE meta match in non-LL hooks. + */ +void payload_ctx_update_meta(struct payload_ctx *ctx, const struct expr *expr) +{ + const struct payload_hook_desc *h = &payload_hooks[ctx->family]; + const struct expr *left = expr->left, *right = expr->right; + const struct payload_desc *desc; + + if (left->meta.key != NFT_META_IIFTYPE) + return; + + assert(expr->op == OP_EQ); + if (h->base < PAYLOAD_BASE_NETWORK_HDR) + return; + + desc = payload_dev_desc(mpz_get_uint16(right->value)); + if (desc == NULL) + desc = &payload_unknown_desc; + + ctx->protocol[PAYLOAD_BASE_LL_HDR].location = expr->location; + ctx->protocol[PAYLOAD_BASE_LL_HDR].desc = desc; +} + +/** + * payload_ctx_update - update payload context + * + * @ctx: payload context + * @expr: relational payload expression + * + * Update payload context for relational payload expressions. + */ +void payload_ctx_update(struct payload_ctx *ctx, const struct expr *expr) +{ + const struct expr *left = expr->left, *right = expr->right; + const struct payload_desc *base, *desc; + + if (!(left->payload.flags & PAYLOAD_PROTOCOL_EXPR)) + return; + + assert(expr->op == OP_EQ); + base = ctx->protocol[left->payload.base].desc; + desc = payload_select_proto(base, mpz_get_uint32(right->value)); + + ctx->protocol[left->payload.base + 1].location = expr->location; + ctx->protocol[left->payload.base + 1].desc = desc; +} + +/** + * payload_gen_dependency - generate match expression on payload dependency + * + * @ctx: evaluation context + * @expr: payload expression + * @res: dependency expression + * + * Generate matches on protocol dependencies. There are two different kinds + * of dependencies: + * + * - A payload expression for a base above the hook base requires a match + * on the protocol value in the lower layer header. + * + * - A payload expression for a base below the hook base is invalid in the + * output path since the lower layer header does not exist when the packet + * is classified. In the input path a payload expressions for a base exactly + * one below the hook base is valid. In this case a match on the device type + * is required to verify that we're dealing with the expected protocol. + * + * Note: since it is unknown to userspace which hooks a chain is called from, + * it is not explicitly verified. The NFT_META_IIFTYPE match will only match + * in the input path though. + */ +int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr, + struct expr **res) +{ + const struct payload_hook_desc *h = &payload_hooks[ctx->pctx.family]; + const struct payload_desc *desc; + const struct payload_template *tmpl; + struct expr *dep, *left, *right; + unsigned int protocol; + uint16_t type; + + if (expr->payload.base < h->base) { + if (expr->payload.base < h->base - 1) + return expr_error(ctx, expr, + "payload base is invalid for this " + "family"); + + if (payload_dev_type(expr->payload.desc, &type) < 0) + return expr_error(ctx, expr, + "protocol specification is invalid " + "for this family"); + + left = meta_expr_alloc(&expr->location, NFT_META_IIFTYPE); + right = constant_expr_alloc(&expr->location, &arphrd_type, + BYTEORDER_HOST_ENDIAN, + 2 * BITS_PER_BYTE, &type); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + *res = dep; + return 0; + } + + desc = ctx->pctx.protocol[expr->payload.base - 1].desc; + if (desc == NULL) + return expr_error(ctx, expr, + "ambiguous payload specification: " + "no %s protocol specified", + payload_base_names[expr->payload.base - 1]); + + tmpl = &desc->templates[desc->protocol_key]; + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + protocol = payload_proto_val(desc, expr->payload.desc); + right = constant_expr_alloc(&expr->location, tmpl->dtype, + BYTEORDER_HOST_ENDIAN, + tmpl->len, &protocol); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + payload_ctx_update(&ctx->pctx, dep); + *res = dep; + return 0; +} + +/** + * payload_expr_complete - fill in type information of a raw payload expr + * + * @expr: the payload expression + * @ctx: payload context + * + * Complete the type of a raw payload expression based on the context. If + * insufficient information is available the expression remains unchanged. + */ +void payload_expr_complete(struct expr *expr, const struct payload_ctx *ctx) +{ + const struct payload_desc *desc; + const struct payload_template *tmpl; + unsigned int i; + + assert(expr->ops->type == EXPR_PAYLOAD); + + desc = ctx->protocol[expr->payload.base].desc; + if (desc == NULL) + return; + assert(desc->base == expr->payload.base); + + for (i = 0; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset != expr->payload.offset || + tmpl->len != expr->len) + continue; + expr->dtype = tmpl->dtype; + expr->payload.desc = desc; + expr->payload.tmpl = tmpl; + return; + } +} + +/** + * payload_expr_expand - expand raw merged adjacent payload expressions into its + * original components + * + * @list: list to append expanded payload expressions to + * @expr: the payload expression to expand + * @ctx: payload context + * + * Expand a merged adjacent payload expression into its original components + * by splitting elements off the beginning matching a payload template. + * + * Note: this requires all payload templates to be specified in ascending + * offset order. + */ +void payload_expr_expand(struct list_head *list, struct expr *expr, + const struct payload_ctx *ctx) +{ + const struct payload_desc *desc; + const struct payload_template *tmpl; + struct expr *new; + unsigned int i; + + assert(expr->ops->type == EXPR_PAYLOAD); + + desc = ctx->protocol[expr->payload.base].desc; + if (desc == NULL) + goto raw; + assert(desc->base == expr->payload.base); + + for (i = 1; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset != expr->payload.offset) + continue; + + if (tmpl->len <= expr->len) { + new = payload_expr_alloc(&expr->location, desc, i); + list_add_tail(&new->list, list); + expr->len -= tmpl->len; + expr->payload.offset += tmpl->len; + if (expr->len == 0) + return; + } else + break; + } +raw: + new = payload_expr_alloc(&expr->location, NULL, 0); + payload_init_raw(new, expr->payload.base, expr->payload.offset, + expr->len); + list_add_tail(&new->list, list); +} + +/** + * payload_is_adjacent - return whether two payload expressions refer to + * adjacent header locations + * + * @e1: first payload expression + * @e2: second payload expression + */ +bool payload_is_adjacent(const struct expr *e1, const struct expr *e2) +{ + if (e1->payload.base == e2->payload.base && + e1->payload.offset + e1->len == e2->payload.offset) + return true; + return false; +} + +/** + * payload_expr_join - join two adjacent payload expressions + * + * @e1: first payload expression + * @e2: second payload expression + */ +struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2) +{ + struct expr *expr; + + assert(payload_is_adjacent(e1, e2)); + + expr = payload_expr_alloc(&internal_location, NULL, 0); + expr->payload.base = e1->payload.base; + expr->payload.offset = e1->payload.offset; + expr->len = e1->len + e2->len; + return expr; +} + +#define HDR_TEMPLATE(__name, __dtype, __type, __member) \ + PAYLOAD_TEMPLATE(__name, __dtype, \ + offsetof(__type, __member) * 8, \ + field_sizeof(__type, __member) * 8) + +#define HDR_FIELD(__name, __struct, __member) \ + HDR_TEMPLATE(__name, &integer_type, __struct, __member) +#define HDR_BITFIELD(__name, __dtype, __offset, __len) \ + PAYLOAD_TEMPLATE(__name, __dtype, __offset, __len) +#define HDR_TYPE(__name, __dtype, __struct, __member) \ + HDR_TEMPLATE(__name, __dtype, __struct, __member) + +#define INET_PROTOCOL(__name, __struct, __member) \ + HDR_TYPE(__name, &inet_protocol_type, __struct, __member) +#define INET_SERVICE(__name, __struct, __member) \ + HDR_TYPE(__name, &inet_service_type, __struct, __member) + +/* + * AH + */ + +#define AHHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct ip_auth_hdr, __member) + +const struct payload_desc payload_ah = { + .name = "ah", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .protocol_key = AHHDR_NEXTHDR, + .protocols = { + PAYLOAD_PROTO(IPPROTO_ESP, &payload_esp), + PAYLOAD_PROTO(IPPROTO_AH, &payload_ah), + PAYLOAD_PROTO(IPPROTO_COMP, &payload_comp), + PAYLOAD_PROTO(IPPROTO_UDP, &payload_udp), + PAYLOAD_PROTO(IPPROTO_UDPLITE, &payload_udplite), + PAYLOAD_PROTO(IPPROTO_TCP, &payload_tcp), + PAYLOAD_PROTO(IPPROTO_DCCP, &payload_dccp), + PAYLOAD_PROTO(IPPROTO_SCTP, &payload_sctp), + }, + .templates = { + [AHHDR_NEXTHDR] = INET_PROTOCOL("nexthdr", struct ip_auth_hdr, nexthdr), + [AHHDR_HDRLENGTH] = AHHDR_FIELD("hdrlength", hdrlen), + [AHHDR_RESERVED] = AHHDR_FIELD("reserved", reserved), + [AHHDR_SPI] = AHHDR_FIELD("spi", spi), + [AHHDR_SEQUENCE] = AHHDR_FIELD("sequence", seq_no), + }, +}; + +/* + * ESP + */ + +#define ESPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct ip_esp_hdr, __member) + +const struct payload_desc payload_esp = { + .name = "esp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [ESPHDR_SPI] = ESPHDR_FIELD("spi", spi), + [ESPHDR_SEQUENCE] = ESPHDR_FIELD("sequence", seq_no), + }, +}; + +/* + * IPCOMP + */ + +#define COMPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct ip_comp_hdr, __member) + +const struct payload_desc payload_comp = { + .name = "comp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .protocol_key = COMPHDR_NEXTHDR, + .protocols = { + PAYLOAD_PROTO(IPPROTO_ESP, &payload_esp), + PAYLOAD_PROTO(IPPROTO_AH, &payload_ah), + PAYLOAD_PROTO(IPPROTO_COMP, &payload_comp), + PAYLOAD_PROTO(IPPROTO_UDP, &payload_udp), + PAYLOAD_PROTO(IPPROTO_UDPLITE, &payload_udplite), + PAYLOAD_PROTO(IPPROTO_TCP, &payload_tcp), + PAYLOAD_PROTO(IPPROTO_DCCP, &payload_dccp), + PAYLOAD_PROTO(IPPROTO_SCTP, &payload_sctp), + }, + .templates = { + [COMPHDR_NEXTHDR] = INET_PROTOCOL("nexthdr", struct ip_comp_hdr, nexthdr), + [COMPHDR_FLAGS] = COMPHDR_FIELD("flags", flags), + [COMPHDR_CPI] = COMPHDR_FIELD("cpi", cpi), + }, +}; + +/* + * ICMP + */ + +#include + +static const struct symbol_table icmp_type_tbl = { + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .symbols = { + SYMBOL("echo-reply", ICMP_ECHOREPLY), + SYMBOL("destination-unreachable", ICMP_DEST_UNREACH), + SYMBOL("source-quench", ICMP_SOURCE_QUENCH), + SYMBOL("redirect", ICMP_REDIRECT), + SYMBOL("echo-request", ICMP_ECHO), + SYMBOL("time-exceeded", ICMP_TIME_EXCEEDED), + SYMBOL("parameter-problem", ICMP_PARAMETERPROB), + SYMBOL("timestamp-request", ICMP_TIMESTAMP), + SYMBOL("timestamp-reply", ICMP_TIMESTAMPREPLY), + SYMBOL("info-request", ICMP_INFO_REQUEST), + SYMBOL("info-reply", ICMP_INFO_REPLY), + SYMBOL("address-mask-request", ICMP_ADDRESS), + SYMBOL("address-mask-reply", ICMP_ADDRESSREPLY), + SYMBOL_LIST_END + }, +}; + +static const struct datatype icmp_type_type = { + .type = TYPE_ICMP_TYPE, + .name = "ICMP type", + .basetype = &integer_type, + .sym_tbl = &icmp_type_tbl, +}; + +#define ICMPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct icmphdr, __member) +#define ICMPHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct icmphdr, __member) + +const struct payload_desc payload_icmp = { + .name = "icmp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [ICMPHDR_TYPE] = ICMPHDR_TYPE("type", &icmp_type_type, type), + [ICMPHDR_CODE] = ICMPHDR_FIELD("code", code), + [ICMPHDR_CHECKSUM] = ICMPHDR_FIELD("checksum", checksum), + [ICMPHDR_ID] = ICMPHDR_FIELD("id", un.echo.id), + [ICMPHDR_SEQ] = ICMPHDR_FIELD("sequence", un.echo.sequence), + [ICMPHDR_GATEWAY] = ICMPHDR_FIELD("gateway", un.gateway), + [ICMPHDR_MTU] = ICMPHDR_FIELD("mtu", un.frag.mtu), + }, +}; + +/* + * UDP/UDP-Lite + */ + +#include +#define UDPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct udphdr, __member) + +const struct payload_desc payload_udp = { + .name = "udp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source), + [UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest), + [UDPHDR_LENGTH] = UDPHDR_FIELD("length", len), + [UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check), + }, +}; + +const struct payload_desc payload_udplite = { + .name = "udplite", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source), + [UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest), + [UDPHDR_CSUMCOV] = UDPHDR_FIELD("csumcov", len), + [UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check), + }, +}; + +/* + * TCP + */ + +#include + +static const struct symbol_table tcp_flag_tbl = { + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .symbols = { + SYMBOL("fin", TCP_FLAG_FIN), + SYMBOL("syn", TCP_FLAG_SYN), + SYMBOL("rst", TCP_FLAG_RST), + SYMBOL("psh", TCP_FLAG_PSH), + SYMBOL("ack", TCP_FLAG_ACK), + SYMBOL("urh", TCP_FLAG_URG), + SYMBOL("ecn", TCP_FLAG_ECN), + SYMBOL("cwr", TCP_FLAG_CWR), + SYMBOL_LIST_END + }, +}; + +static const struct datatype tcp_flag_type = { + .type = TYPE_TCP_FLAG, + .name = "TCP flag", + .basetype = &bitmask_type, + .sym_tbl = &tcp_flag_tbl, +}; + +#define TCPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct tcphdr, __member) + +const struct payload_desc payload_tcp = { + .name = "tcp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [TCPHDR_SPORT] = INET_SERVICE("sport", struct tcphdr, source), + [TCPHDR_DPORT] = INET_SERVICE("dport", struct tcphdr, dest), + [TCPHDR_SEQ] = TCPHDR_FIELD("sequence", seq), + [TCPHDR_ACKSEQ] = TCPHDR_FIELD("ackseq", ack_seq), + [TCPHDR_DOFF] = {}, + [TCPHDR_RESERVED] = {}, + [TCPHDR_FLAGS] = HDR_BITFIELD("flags", &tcp_flag_type, + 13 * BITS_PER_BYTE, + BITS_PER_BYTE), + [TCPHDR_WINDOW] = TCPHDR_FIELD("window", window), + [TCPHDR_CHECKSUM] = TCPHDR_FIELD("checksum", check), + [TCPHDR_URGPTR] = TCPHDR_FIELD("urgptr", urg_ptr), + }, +}; + +/* + * DCCP + */ + +#define DCCPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct dccp_hdr, __member) + +const struct payload_desc payload_dccp = { + .name = "dccp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [DCCPHDR_SPORT] = INET_SERVICE("sport", struct dccp_hdr, dccph_sport), + [DCCPHDR_DPORT] = INET_SERVICE("dport", struct dccp_hdr, dccph_dport), + }, +}; + +/* + * SCTP + */ + +#define SCTPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct sctphdr, __member) + +const struct payload_desc payload_sctp = { + .name = "sctp", + .base = PAYLOAD_BASE_TRANSPORT_HDR, + .templates = { + [SCTPHDR_SPORT] = INET_SERVICE("sport", struct sctphdr, source), + [SCTPHDR_DPORT] = INET_SERVICE("dport", struct sctphdr, dest), + [SCTPHDR_VTAG] = SCTPHDR_FIELD("vtag", vtag), + [SCTPHDR_CHECKSUM] = SCTPHDR_FIELD("checksum", checksum), + }, +}; + +/* + * IPv4 + */ + +#include +#define IPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct iphdr, __member) +#define IPHDR_ADDR(__name, __member) \ + HDR_TYPE(__name, &ipaddr_type, struct iphdr, __member) + +const struct payload_desc payload_ip = { + .name = "ip", + .base = PAYLOAD_BASE_NETWORK_HDR, + .protocol_key = IPHDR_PROTOCOL, + .protocols = { + PAYLOAD_PROTO(IPPROTO_ICMP, &payload_icmp), + PAYLOAD_PROTO(IPPROTO_ESP, &payload_esp), + PAYLOAD_PROTO(IPPROTO_AH, &payload_ah), + PAYLOAD_PROTO(IPPROTO_COMP, &payload_comp), + PAYLOAD_PROTO(IPPROTO_UDP, &payload_udp), + PAYLOAD_PROTO(IPPROTO_UDPLITE, &payload_udplite), + PAYLOAD_PROTO(IPPROTO_TCP, &payload_tcp), + PAYLOAD_PROTO(IPPROTO_DCCP, &payload_dccp), + PAYLOAD_PROTO(IPPROTO_SCTP, &payload_sctp), + }, + .templates = { + [IPHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 0, 4), + [IPHDR_HDRLENGTH] = HDR_BITFIELD("hdrlength", &integer_type, 4, 4), + [IPHDR_TOS] = IPHDR_FIELD("tos", tos), + [IPHDR_LENGTH] = IPHDR_FIELD("length", tot_len), + [IPHDR_ID] = IPHDR_FIELD("id", id), + [IPHDR_FRAG_OFF] = IPHDR_FIELD("frag-off", frag_off), + [IPHDR_TTL] = IPHDR_FIELD("ttl", ttl), + [IPHDR_PROTOCOL] = INET_PROTOCOL("protocol", struct iphdr, protocol), + [IPHDR_CHECKSUM] = IPHDR_FIELD("checksum", check), + [IPHDR_SADDR] = IPHDR_ADDR("saddr", saddr), + [IPHDR_DADDR] = IPHDR_ADDR("daddr", daddr), + }, +}; + +/* + * IPv6 + */ + +#define IP6HDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct ipv6hdr, __member) +#define IP6HDR_ADDR(__name, __member) \ + HDR_TYPE(__name, &ip6addr_type, struct ipv6hdr, __member) +#define IP6HDR_PROTOCOL(__name, __member) \ + HDR_TYPE(__name, &inet_service_type, struct ipv6hdr, __member) + +const struct payload_desc payload_ip6 = { + .name = "ip6", + .base = PAYLOAD_BASE_NETWORK_HDR, + .protocol_key = IP6HDR_NEXTHDR, + .protocols = { + PAYLOAD_PROTO(IPPROTO_ESP, &payload_esp), + PAYLOAD_PROTO(IPPROTO_AH, &payload_ah), + PAYLOAD_PROTO(IPPROTO_COMP, &payload_comp), + PAYLOAD_PROTO(IPPROTO_UDP, &payload_udp), + PAYLOAD_PROTO(IPPROTO_UDPLITE, &payload_udplite), + PAYLOAD_PROTO(IPPROTO_TCP, &payload_tcp), + PAYLOAD_PROTO(IPPROTO_DCCP, &payload_dccp), + PAYLOAD_PROTO(IPPROTO_SCTP, &payload_sctp), + }, + .templates = { + [IP6HDR_VERSION] = HDR_BITFIELD("version", &integer_type, 0, 4), + [IP6HDR_PRIORITY] = HDR_BITFIELD("priority", &integer_type, 4, 4), + [IP6HDR_FLOWLABEL] = IP6HDR_FIELD("flowlabel", flow_lbl), + [IP6HDR_LENGTH] = IP6HDR_FIELD("length", payload_len), + [IP6HDR_NEXTHDR] = INET_PROTOCOL("nexthdr", struct ipv6hdr, nexthdr), + [IP6HDR_HOPLIMIT] = IP6HDR_FIELD("hoplimit", hop_limit), + [IP6HDR_SADDR] = IP6HDR_ADDR("saddr", saddr), + [IP6HDR_DADDR] = IP6HDR_ADDR("daddr", daddr), + }, +}; + +/* + * ARP + */ + +#include + +static const struct symbol_table arpop_tbl = { + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .symbols = { + SYMBOL("request", ARPOP_REQUEST), + SYMBOL("reply", ARPOP_REPLY), + SYMBOL("rrequest", ARPOP_RREQUEST), + SYMBOL("rreply", ARPOP_REPLY), + SYMBOL("inrequest", ARPOP_InREQUEST), + SYMBOL("inreply", ARPOP_InREPLY), + SYMBOL("nak", ARPOP_NAK), + }, +}; + +static const struct datatype arpop_type = { + .type = TYPE_ARPOP, + .name = "ARP operation", + .basetype = &integer_type, + .sym_tbl = &arpop_tbl, +}; + +#define ARPHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct arphdr, __member) +#define ARPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct arphdr, __member) + +const struct payload_desc payload_arp = { + .name = "arp", + .base = PAYLOAD_BASE_NETWORK_HDR, + .templates = { + [ARPHDR_HRD] = ARPHDR_FIELD("htype", ar_hrd), + [ARPHDR_PRO] = ARPHDR_TYPE("ptype", ðertype_type, ar_pro), + [ARPHDR_HLN] = ARPHDR_FIELD("hlen", ar_hln), + [ARPHDR_PLN] = ARPHDR_FIELD("plen", ar_pln), + [ARPHDR_OP] = ARPHDR_TYPE("operation", &arpop_type, ar_op), + }, +}; + +/* + * VLAN + */ + +#include + +#define VLANHDR_BITFIELD(__name, __offset, __len) \ + HDR_BITFIELD(__name, &integer_type, __offset, __len) +#define VLANHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct vlan_hdr, __member) + +const struct payload_desc payload_vlan = { + .name = "vlan", + .base = PAYLOAD_BASE_LL_HDR, + .protocol_key = VLANHDR_TYPE, + .protocols = { + PAYLOAD_PROTO(ETH_P_IP, &payload_ip), + PAYLOAD_PROTO(ETH_P_ARP, &payload_arp), + PAYLOAD_PROTO(ETH_P_IPV6, &payload_ip6), + PAYLOAD_PROTO(ETH_P_8021Q, &payload_vlan), + + }, + .templates = { + [VLANHDR_VID] = VLANHDR_BITFIELD("id", 0, 12), + [VLANHDR_CFI] = VLANHDR_BITFIELD("cfi", 12, 1), + [VLANHDR_PCP] = VLANHDR_BITFIELD("pcp", 13, 3), + [VLANHDR_TYPE] = VLANHDR_TYPE("type", ðertype_type, vlan_type), + }, +}; + +/* + * Ethernet + */ + +static const struct symbol_table ethertype_tbl = { + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .symbols = { + SYMBOL("ip", ETH_P_IP), + SYMBOL("arp", ETH_P_ARP), + SYMBOL("ipv6", ETH_P_IPV6), + SYMBOL("vlan", ETH_P_8021Q), + SYMBOL_LIST_END + }, +}; + +const struct datatype ethertype_type = { + .type = TYPE_ETHERTYPE, + .name = "Ethernet protocol", + .basetype = &integer_type, + .sym_tbl = ðertype_tbl, +}; + +#define ETHHDR_TEMPLATE(__name, __dtype, __member) \ + HDR_TEMPLATE(__name, __dtype, struct ether_header, __member) +#define ETHHDR_TYPE(__name, __member) \ + ETHHDR_TEMPLATE(__name, ðertype_type, __member) +#define ETHHDR_ADDR(__name, __member) \ + ETHHDR_TEMPLATE(__name, &lladdr_type, __member) + +const struct payload_desc payload_eth = { + .name = "eth", + .base = PAYLOAD_BASE_LL_HDR, + .protocol_key = ETHHDR_TYPE, + .protocols = { + PAYLOAD_PROTO(ETH_P_IP, &payload_ip), + PAYLOAD_PROTO(ETH_P_ARP, &payload_arp), + PAYLOAD_PROTO(ETH_P_IPV6, &payload_ip6), + PAYLOAD_PROTO(ETH_P_8021Q, &payload_vlan), + }, + .templates = { + [ETHHDR_DADDR] = ETHHDR_ADDR("daddr", ether_dhost), + [ETHHDR_SADDR] = ETHHDR_ADDR("saddr", ether_shost), + [ETHHDR_TYPE] = ETHHDR_TYPE("type", ether_type), + }, +}; -- cgit v1.2.3