Blame src/exthdr.c

Packit c5a612
/*
Packit c5a612
 * Exthdr expression protocol and type definitions and related functions.
Packit c5a612
 *
Packit c5a612
 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
Packit c5a612
 *
Packit c5a612
 * This program is free software; you can redistribute it and/or modify
Packit c5a612
 * it under the terms of the GNU General Public License version 2 as
Packit c5a612
 * published by the Free Software Foundation.
Packit c5a612
 *
Packit c5a612
 * Development of this code funded by Astaro AG (http://www.astaro.com/)
Packit c5a612
 */
Packit c5a612
Packit c5a612
#include <stddef.h>
Packit c5a612
#include <stdlib.h>
Packit c5a612
#include <stdio.h>
Packit c5a612
#include <stdint.h>
Packit c5a612
#include <string.h>
Packit c5a612
#include <netinet/in.h>
Packit c5a612
#include <netinet/ip6.h>
Packit c5a612
Packit c5a612
#include <utils.h>
Packit c5a612
#include <headers.h>
Packit c5a612
#include <expression.h>
Packit c5a612
#include <statement.h>
Packit c5a612
Packit c5a612
static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) {
Packit c5a612
		/* Offset calculation is a bit hacky at this point.
Packit c5a612
		 * There might be a tcp option one day with another
Packit c5a612
		 * multiplicator
Packit c5a612
		 */
Packit c5a612
		unsigned int offset = expr->exthdr.offset / 64;
Packit c5a612
Packit c5a612
		nft_print(octx, "tcp option %s", expr->exthdr.desc->name);
Packit c5a612
		if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
Packit c5a612
			return;
Packit c5a612
		if (offset)
Packit c5a612
			nft_print(octx, "%d", offset);
Packit c5a612
		nft_print(octx, " %s", expr->exthdr.tmpl->token);
Packit c5a612
	} else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
Packit c5a612
		nft_print(octx, "ip option %s", expr->exthdr.desc->name);
Packit c5a612
		if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
Packit c5a612
			return;
Packit c5a612
		nft_print(octx, " %s", expr->exthdr.tmpl->token);
Packit c5a612
	} else {
Packit c5a612
		if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)
Packit c5a612
			nft_print(octx, "exthdr %s", expr->exthdr.desc->name);
Packit c5a612
		else {
Packit c5a612
			nft_print(octx, "%s %s",
Packit c5a612
				  expr->exthdr.desc ? expr->exthdr.desc->name : "unknown-exthdr",
Packit c5a612
				  expr->exthdr.tmpl->token);
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static bool exthdr_expr_cmp(const struct expr *e1, const struct expr *e2)
Packit c5a612
{
Packit c5a612
	return e1->exthdr.desc == e2->exthdr.desc &&
Packit c5a612
	       e1->exthdr.tmpl == e2->exthdr.tmpl &&
Packit c5a612
	       e1->exthdr.op == e2->exthdr.op &&
Packit c5a612
	       e1->exthdr.flags == e2->exthdr.flags;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void exthdr_expr_clone(struct expr *new, const struct expr *expr)
Packit c5a612
{
Packit c5a612
	new->exthdr.desc = expr->exthdr.desc;
Packit c5a612
	new->exthdr.tmpl = expr->exthdr.tmpl;
Packit c5a612
	new->exthdr.offset = expr->exthdr.offset;
Packit c5a612
	new->exthdr.op = expr->exthdr.op;
Packit c5a612
	new->exthdr.flags = expr->exthdr.flags;
Packit c5a612
}
Packit c5a612
Packit c5a612
const struct expr_ops exthdr_expr_ops = {
Packit c5a612
	.type		= EXPR_EXTHDR,
Packit c5a612
	.name		= "exthdr",
Packit c5a612
	.print		= exthdr_expr_print,
Packit c5a612
	.json		= exthdr_expr_json,
Packit c5a612
	.cmp		= exthdr_expr_cmp,
Packit c5a612
	.clone		= exthdr_expr_clone,
Packit c5a612
};
Packit c5a612
Packit c5a612
static const struct proto_hdr_template exthdr_unknown_template =
Packit c5a612
	PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0);
Packit c5a612
Packit c5a612
struct expr *exthdr_expr_alloc(const struct location *loc,
Packit c5a612
			       const struct exthdr_desc *desc,
Packit c5a612
			       uint8_t type)
Packit c5a612
{
Packit c5a612
	const struct proto_hdr_template *tmpl;
Packit c5a612
	struct expr *expr;
Packit c5a612
Packit c5a612
	if (desc != NULL)
Packit c5a612
		tmpl = &desc->templates[type];
Packit c5a612
	else
Packit c5a612
		tmpl = &exthdr_unknown_template;
Packit c5a612
Packit c5a612
	expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype,
Packit c5a612
			  BYTEORDER_BIG_ENDIAN, tmpl->len);
Packit c5a612
	expr->exthdr.desc = desc;
Packit c5a612
	expr->exthdr.tmpl = tmpl;
Packit c5a612
	return expr;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void exthdr_stmt_print(const struct stmt *stmt, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	expr_print(stmt->exthdr.expr, octx);
Packit c5a612
	nft_print(octx, " set ");
Packit c5a612
	expr_print(stmt->exthdr.val, octx);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void exthdr_stmt_destroy(struct stmt *stmt)
Packit c5a612
{
Packit c5a612
	expr_free(stmt->exthdr.expr);
Packit c5a612
	expr_free(stmt->exthdr.val);
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct stmt_ops exthdr_stmt_ops = {
Packit c5a612
	.type		= STMT_EXTHDR,
Packit c5a612
	.name		= "exthdr",
Packit c5a612
	.print		= exthdr_stmt_print,
Packit c5a612
	.json		= exthdr_stmt_json,
Packit c5a612
	.destroy	= exthdr_stmt_destroy,
Packit c5a612
};
Packit c5a612
Packit c5a612
struct stmt *exthdr_stmt_alloc(const struct location *loc,
Packit c5a612
				struct expr *expr, struct expr *val)
Packit c5a612
{
Packit c5a612
	struct stmt *stmt;
Packit c5a612
Packit c5a612
	stmt = stmt_alloc(loc, &exthdr_stmt_ops);
Packit c5a612
	stmt->exthdr.expr = expr;
Packit c5a612
	stmt->exthdr.val  = val;
Packit c5a612
	return stmt;
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = {
Packit c5a612
	[IPPROTO_HOPOPTS]	= &exthdr_hbh,
Packit c5a612
	[IPPROTO_ROUTING]	= &exthdr_rt,
Packit c5a612
	[IPPROTO_FRAGMENT]	= &exthdr_frag,
Packit c5a612
	[IPPROTO_DSTOPTS]	= &exthdr_dst,
Packit c5a612
	[IPPROTO_MH]		= &exthdr_mh,
Packit c5a612
};
Packit c5a612
Packit c5a612
const struct exthdr_desc *exthdr_find_proto(uint8_t proto)
Packit c5a612
{
Packit c5a612
	assert(exthdr_protocols[proto]);
Packit c5a612
Packit c5a612
	return exthdr_protocols[proto];
Packit c5a612
}
Packit c5a612
Packit c5a612
static const struct proto_hdr_template *
Packit c5a612
exthdr_rt_find(struct expr *expr, const struct exthdr_desc *desc)
Packit c5a612
{
Packit c5a612
	const struct proto_hdr_template *tmpl;
Packit c5a612
	unsigned int i;
Packit c5a612
Packit c5a612
	for (i = 0; i < array_size(desc->templates); i++) {
Packit c5a612
		tmpl = &desc->templates[i];
Packit c5a612
		if (tmpl->offset == expr->exthdr.offset &&
Packit c5a612
		    tmpl->len == expr->len) {
Packit c5a612
			expr->exthdr.desc = desc;
Packit c5a612
			return tmpl;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return NULL;
Packit c5a612
}
Packit c5a612
Packit c5a612
void exthdr_init_raw(struct expr *expr, uint8_t type,
Packit c5a612
		     unsigned int offset, unsigned int len,
Packit c5a612
		     enum nft_exthdr_op op, uint32_t flags)
Packit c5a612
{
Packit c5a612
	const struct proto_hdr_template *tmpl = &exthdr_unknown_template;
Packit c5a612
	unsigned int i;
Packit c5a612
Packit c5a612
	assert(expr->etype == EXPR_EXTHDR);
Packit c5a612
	if (op == NFT_EXTHDR_OP_TCPOPT)
Packit c5a612
		return tcpopt_init_raw(expr, type, offset, len, flags);
Packit c5a612
	if (op == NFT_EXTHDR_OP_IPV4)
Packit c5a612
		return ipopt_init_raw(expr, type, offset, len, flags, true);
Packit c5a612
Packit c5a612
	expr->len = len;
Packit c5a612
	expr->exthdr.flags = flags;
Packit c5a612
	expr->exthdr.offset = offset;
Packit c5a612
	expr->exthdr.desc = NULL;
Packit c5a612
Packit c5a612
	if (type < array_size(exthdr_protocols))
Packit c5a612
		expr->exthdr.desc = exthdr_protocols[type];
Packit c5a612
Packit c5a612
	if (expr->exthdr.desc == NULL)
Packit c5a612
		goto out;
Packit c5a612
Packit c5a612
	for (i = 0; i < array_size(expr->exthdr.desc->templates); i++) {
Packit c5a612
		tmpl = &expr->exthdr.desc->templates[i];
Packit c5a612
		if (tmpl->offset == offset && tmpl->len == len)
Packit c5a612
			goto out;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (expr->exthdr.desc == &exthdr_rt) {
Packit c5a612
		tmpl = exthdr_rt_find(expr, &exthdr_rt4);
Packit c5a612
		if (tmpl)
Packit c5a612
			goto out;
Packit c5a612
		tmpl = exthdr_rt_find(expr, &exthdr_rt0);
Packit c5a612
		if (tmpl)
Packit c5a612
			goto out;
Packit c5a612
		tmpl =	exthdr_rt_find(expr, &exthdr_rt2);
Packit c5a612
		if (tmpl)
Packit c5a612
			goto out;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	tmpl = &exthdr_unknown_template;
Packit c5a612
 out:
Packit c5a612
	expr->exthdr.tmpl = tmpl;
Packit c5a612
	if (flags & NFT_EXTHDR_F_PRESENT)
Packit c5a612
		datatype_set(expr, &boolean_type);
Packit c5a612
	else
Packit c5a612
		datatype_set(expr, tmpl->dtype);
Packit c5a612
}
Packit c5a612
Packit c5a612
static unsigned int mask_length(const struct expr *mask)
Packit c5a612
{
Packit c5a612
	unsigned long off = mpz_scan1(mask->value, 0);
Packit c5a612
Packit c5a612
	return mpz_scan0(mask->value, off + 1);
Packit c5a612
}
Packit c5a612
Packit c5a612
bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift)
Packit c5a612
{
Packit c5a612
	unsigned int off, mask_offset, mask_len;
Packit c5a612
Packit c5a612
	if (expr->exthdr.op != NFT_EXTHDR_OP_IPV4 &&
Packit c5a612
	    expr->exthdr.tmpl != &exthdr_unknown_template)
Packit c5a612
		return false;
Packit c5a612
Packit c5a612
	/* In case we are handling tcp options instead of the default ipv6
Packit c5a612
	 * extension headers.
Packit c5a612
	 */
Packit c5a612
	if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT)
Packit c5a612
		return tcpopt_find_template(expr, mask, shift);
Packit c5a612
Packit c5a612
	mask_offset = mpz_scan1(mask->value, 0);
Packit c5a612
	mask_len = mask_length(mask);
Packit c5a612
Packit c5a612
	off = expr->exthdr.offset;
Packit c5a612
	off += round_up(mask->len, BITS_PER_BYTE) - mask_len;
Packit c5a612
Packit c5a612
	/* Handle ip options after the offset and mask have been calculated. */
Packit c5a612
	if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) {
Packit c5a612
		if (ipopt_find_template(expr, off, mask_len - mask_offset)) {
Packit c5a612
			*shift = mask_offset;
Packit c5a612
			return true;
Packit c5a612
		} else {
Packit c5a612
			return false;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	exthdr_init_raw(expr, expr->exthdr.desc->type,
Packit c5a612
			off, mask_len - mask_offset, expr->exthdr.op, 0);
Packit c5a612
Packit c5a612
	/* still failed to find a template... Bug. */
Packit c5a612
	if (expr->exthdr.tmpl == &exthdr_unknown_template)
Packit c5a612
		return false;
Packit c5a612
Packit c5a612
	*shift = mask_offset;
Packit c5a612
	return true;
Packit c5a612
}
Packit c5a612
Packit c5a612
#define HDR_TEMPLATE(__name, __dtype, __type, __member)			\
Packit c5a612
	PROTO_HDR_TEMPLATE(__name, __dtype,				\
Packit c5a612
			   BYTEORDER_BIG_ENDIAN,			\
Packit c5a612
			   offsetof(__type, __member) * 8,		\
Packit c5a612
			   field_sizeof(__type, __member) * 8)
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * Hop-by-hop options
Packit c5a612
 */
Packit c5a612
Packit c5a612
#define HBH_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_hbh, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_hbh = {
Packit c5a612
	.name		= "hbh",
Packit c5a612
	.type		= IPPROTO_HOPOPTS,
Packit c5a612
	.templates	= {
Packit c5a612
		[HBHHDR_NEXTHDR]	= HBH_FIELD("nexthdr", ip6h_nxt, &inet_protocol_type),
Packit c5a612
		[HBHHDR_HDRLENGTH]	= HBH_FIELD("hdrlength", ip6h_len, &integer_type),
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * Routing header
Packit c5a612
 */
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_rt2 = {
Packit c5a612
	.name           = "rt2",
Packit c5a612
	.type           = IPPROTO_ROUTING,
Packit c5a612
	.proto_key	= 2,
Packit c5a612
	.templates	= {
Packit c5a612
		[RT2HDR_RESERVED]	= {},
Packit c5a612
		[RT2HDR_ADDR]		= {},
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
#define RT0_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_rthdr0, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_rt0 = {
Packit c5a612
	.name           = "rt0",
Packit c5a612
	.type           = IPPROTO_ROUTING,
Packit c5a612
	.proto_key      = 0,
Packit c5a612
	.templates	= {
Packit c5a612
		[RT0HDR_RESERVED]	= RT0_FIELD("reserved", ip6r0_reserved, &integer_type),
Packit c5a612
		[RT0HDR_ADDR_1]		= RT0_FIELD("addr[1]", ip6r0_addr[0], &ip6addr_type),
Packit c5a612
		[RT0HDR_ADDR_1 + 1]	= RT0_FIELD("addr[2]", ip6r0_addr[1], &ip6addr_type),
Packit c5a612
		// ...
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
#define RT4_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_rt4, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_rt4 = {
Packit c5a612
	.name		= "srh",
Packit c5a612
	.type		= IPPROTO_ROUTING,
Packit c5a612
	.proto_key	= 4,
Packit c5a612
	.templates      = {
Packit c5a612
		[RT4HDR_LASTENT]	= RT4_FIELD("last-entry", ip6r4_last_entry, &integer_type),
Packit c5a612
		[RT4HDR_FLAGS]		= RT4_FIELD("flags", ip6r4_flags, &integer_type),
Packit c5a612
		[RT4HDR_TAG]		= RT4_FIELD("tag", ip6r4_tag, &integer_type),
Packit c5a612
		[RT4HDR_SID_1]		= RT4_FIELD("sid[1]", ip6r4_segments[0], &ip6addr_type),
Packit c5a612
		[RT4HDR_SID_1 + 1]	= RT4_FIELD("sid[2]", ip6r4_segments[1], &ip6addr_type),
Packit c5a612
		// ...
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
Packit c5a612
#define RT_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_rthdr, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_rt = {
Packit c5a612
	.name		= "rt",
Packit c5a612
	.type		= IPPROTO_ROUTING,
Packit c5a612
	.proto_key      = -1,
Packit c5a612
#if 0
Packit c5a612
	.protocol_key	= RTHDR_TYPE,
Packit c5a612
	.protocols	= {
Packit c5a612
		[0]	= &exthdr_rt0,
Packit c5a612
		[2]	= &exthdr_rt2,
Packit c5a612
	},
Packit c5a612
#endif
Packit c5a612
	.templates	= {
Packit c5a612
		[RTHDR_NEXTHDR]		= RT_FIELD("nexthdr", ip6r_nxt, &inet_protocol_type),
Packit c5a612
		[RTHDR_HDRLENGTH]	= RT_FIELD("hdrlength", ip6r_len, &integer_type),
Packit c5a612
		[RTHDR_TYPE]		= RT_FIELD("type", ip6r_type, &integer_type),
Packit c5a612
		[RTHDR_SEG_LEFT]	= RT_FIELD("seg-left", ip6r_segleft, &integer_type),
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * Fragment header
Packit c5a612
 */
Packit c5a612
Packit c5a612
#define FRAG_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_frag, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_frag = {
Packit c5a612
	.name		= "frag",
Packit c5a612
	.type		= IPPROTO_FRAGMENT,
Packit c5a612
	.templates	= {
Packit c5a612
		[FRAGHDR_NEXTHDR]	= FRAG_FIELD("nexthdr", ip6f_nxt, &inet_protocol_type),
Packit c5a612
		[FRAGHDR_RESERVED]	= FRAG_FIELD("reserved", ip6f_reserved, &integer_type),
Packit c5a612
		[FRAGHDR_FRAG_OFF]	= PROTO_HDR_TEMPLATE("frag-off", &integer_type,
Packit c5a612
							  BYTEORDER_BIG_ENDIAN,
Packit c5a612
							  16, 13),
Packit c5a612
		[FRAGHDR_RESERVED2]	= PROTO_HDR_TEMPLATE("reserved2", &integer_type,
Packit c5a612
							  BYTEORDER_BIG_ENDIAN,
Packit c5a612
							  29, 2),
Packit c5a612
		[FRAGHDR_MFRAGS]	= PROTO_HDR_TEMPLATE("more-fragments", &integer_type,
Packit c5a612
							  BYTEORDER_BIG_ENDIAN,
Packit c5a612
							  31, 1),
Packit c5a612
		[FRAGHDR_ID]		= FRAG_FIELD("id", ip6f_ident, &integer_type),
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * DST options
Packit c5a612
 */
Packit c5a612
Packit c5a612
#define DST_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_dest, __member)
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_dst = {
Packit c5a612
	.name		= "dst",
Packit c5a612
	.type		= IPPROTO_DSTOPTS,
Packit c5a612
	.templates	= {
Packit c5a612
		[DSTHDR_NEXTHDR]	= DST_FIELD("nexthdr", ip6d_nxt, &inet_protocol_type),
Packit c5a612
		[DSTHDR_HDRLENGTH]	= DST_FIELD("hdrlength", ip6d_len, &integer_type),
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * Mobility header
Packit c5a612
 */
Packit c5a612
Packit c5a612
#define MH_FIELD(__name, __member, __dtype) \
Packit c5a612
	HDR_TEMPLATE(__name, __dtype, struct ip6_mh, __member)
Packit c5a612
Packit c5a612
static const struct symbol_table mh_type_tbl = {
Packit c5a612
	.base		= BASE_DECIMAL,
Packit c5a612
	.symbols	= {
Packit c5a612
		SYMBOL("binding-refresh-request",	IP6_MH_TYPE_BRR),
Packit c5a612
		SYMBOL("home-test-init",		IP6_MH_TYPE_HOTI),
Packit c5a612
		SYMBOL("careof-test-init",		IP6_MH_TYPE_COTI),
Packit c5a612
		SYMBOL("home-test",			IP6_MH_TYPE_HOT),
Packit c5a612
		SYMBOL("careof-test",			IP6_MH_TYPE_COT),
Packit c5a612
		SYMBOL("binding-update",		IP6_MH_TYPE_BU),
Packit c5a612
		SYMBOL("binding-acknowledgement",	IP6_MH_TYPE_BACK),
Packit c5a612
		SYMBOL("binding-error",			IP6_MH_TYPE_BERROR),
Packit c5a612
		SYMBOL("fast-binding-update",		IP6_MH_TYPE_FBU),
Packit c5a612
		SYMBOL("fast-binding-acknowledgement",	IP6_MH_TYPE_FBACK),
Packit c5a612
		SYMBOL("fast-binding-advertisement",	IP6_MH_TYPE_FNA),
Packit c5a612
		SYMBOL("experimental-mobility-header",	IP6_MH_TYPE_EMH),
Packit c5a612
		SYMBOL("home-agent-switch-message",	IP6_MH_TYPE_HASM),
Packit c5a612
		SYMBOL_LIST_END
Packit c5a612
	},
Packit c5a612
};
Packit c5a612
Packit c5a612
const struct datatype mh_type_type = {
Packit c5a612
	.type		= TYPE_MH_TYPE,
Packit c5a612
	.name		= "mh_type",
Packit c5a612
	.desc		= "Mobility Header Type",
Packit c5a612
	.byteorder	= BYTEORDER_BIG_ENDIAN,
Packit c5a612
	.size		= BITS_PER_BYTE,
Packit c5a612
	.basetype	= &integer_type,
Packit c5a612
	.sym_tbl	= &mh_type_tbl,
Packit c5a612
};
Packit c5a612
Packit c5a612
const struct exthdr_desc exthdr_mh = {
Packit c5a612
	.name		= "mh",
Packit c5a612
	.type		= IPPROTO_MH,
Packit c5a612
	.templates	= {
Packit c5a612
		[MHHDR_NEXTHDR]		= MH_FIELD("nexthdr", ip6mh_proto, &inet_protocol_type),
Packit c5a612
		[MHHDR_HDRLENGTH]	= MH_FIELD("hdrlength", ip6mh_hdrlen, &integer_type),
Packit c5a612
		[MHHDR_TYPE]		= MH_FIELD("type", ip6mh_type, &mh_type_type),
Packit c5a612
		[MHHDR_RESERVED]	= MH_FIELD("reserved", ip6mh_reserved, &integer_type),
Packit c5a612
		[MHHDR_CHECKSUM]	= MH_FIELD("checksum", ip6mh_cksum, &integer_type),
Packit c5a612
	},
Packit c5a612
};