lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 28 Jan 2019 10:22:52 -0800
From:   Tom Herbert <tom@...bertland.com>
To:     davem@...emloft.net, netdev@...r.kernel.org
Cc:     Tom Herbert <tom@...ntonium.net>
Subject: [PATCH v2 net-next 2/5] exthdrs: Registration of TLV handlers and parameters

Create a single TLV parameter table that holds meta information for IPv6
Hop-by-Hop and Destination TLVs. The data structure is composed of a 256
element array of u8's (one entry for each TLV type to allow O(1)
lookup). Each entry provides an offset into an array of TLV parameter
data structures which follows the array of u8s. TLV. The TLV parameter
data structure contains parameters and handler functions for receiving
and transmitting TLVs. The receive and transmit properties can be
managed independently. The zeroth element in the TLV parameter array
provides default parameters for TLVs.

TLV transmit properties include a description of limits, alignment, and
preferred ordering. TLV receive properties provide the receiver handler.
A class attribute is defined in both receive and transmit properties
that indicates the type of extension header in which the TLV may be used
(e.g. Hop-by-Hop options, Destination options, or Destination options
before the routing header).

Receive TLV lookup and processing is modified to be a lookup in the TLV
parameter table. tlv_{set,unset}_{rx,tx}_param function can be used to
set attributes in the TLV table. A table containing parameters for TLVs
supported by the kernel and is used to initialize the TLV table.
---
 include/net/ipv6.h         |  64 +++++++-
 include/uapi/linux/in6.h   |  17 ++
 net/ipv6/exthdrs.c         |  47 +++---
 net/ipv6/exthdrs_options.c | 384 ++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 465 insertions(+), 47 deletions(-)

diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 8abdcdb..9992f17 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -379,13 +379,67 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk,
 struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
 					  struct ipv6_txoptions *opt);
 
-struct tlvtype_proc {
-	int	type;
-	bool	(*func)(struct sk_buff *skb, int offset);
+struct tlv_tx_param {
+	unsigned char preferred_order;
+	unsigned char admin_perm : 2;
+	unsigned char user_perm : 2;
+	unsigned char class : 3;
+	unsigned char align_mult : 4;
+	unsigned char align_off : 4;
+	unsigned char data_len_mult : 4;
+	unsigned char data_len_off : 4;
+	unsigned char min_data_len;
+	unsigned char max_data_len;
 };
 
-extern const struct tlvtype_proc tlvprocdestopt_lst[];
-extern const struct tlvtype_proc tlvprochopopt_lst[];
+struct tlv_rx_param {
+	unsigned char class: 3;
+	bool (*func)(unsigned int class, struct sk_buff *skb, int offset);
+};
+
+struct tlv_param {
+	struct tlv_tx_param tx_params;
+	struct tlv_rx_param rx_params;
+};
+
+struct tlv_param_table {
+	unsigned char entries[256];
+	unsigned char count;
+	struct rcu_head rcu;
+	struct tlv_param params[0];
+};
+
+extern struct tlv_param_table __rcu *tlv_param_table;
+
+/* Preferred TLV ordering (placed by increasing order) */
+#define TLV_PREF_ORDER_HAO		10
+#define TLV_PREF_ORDER_ROUTERALERT	20
+#define TLV_PREF_ORDER_JUMBO		30
+#define TLV_PREF_ORDER_CALIPSO		40
+
+/* tlv_deref_rx_params assume rcu_read_lock is held */
+static inline struct tlv_rx_param *tlv_deref_rx_params(unsigned int type)
+{
+	struct tlv_param_table *tpt = rcu_dereference(tlv_param_table);
+
+	return &tpt->params[tpt->entries[type]].rx_params;
+}
+
+/* tlv_deref_tx_params assume rcu_read_lock is held */
+static inline struct tlv_tx_param *tlv_deref_tx_params(unsigned int type)
+{
+	struct tlv_param_table *tpt = rcu_dereference(tlv_param_table);
+
+	return &tpt->params[tpt->entries[type]].tx_params;
+}
+
+int tlv_set_param(unsigned char type,
+		  const struct tlv_rx_param *rx_param_tmpl,
+		  const struct tlv_tx_param *tx_param_tmpl);
+int tlv_unset_rx_param(unsigned char type);
+int tlv_set_rx_param(unsigned char type, struct tlv_rx_param *rx_param_tmpl);
+int tlv_unset_tx_param(unsigned char type);
+int tlv_set_tx_param(unsigned char type, struct tlv_tx_param *tx_param_tmpl);
 
 bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb,
 		       const struct inet6_skb_parm *opt);
diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h
index 71d82fe..38e8e63 100644
--- a/include/uapi/linux/in6.h
+++ b/include/uapi/linux/in6.h
@@ -296,4 +296,21 @@ struct in6_flowlabel_req {
  * ...
  * MRT6_MAX
  */
+
+/* TLV permissions values */
+enum {
+	IPV6_TLV_PERM_NONE,
+	IPV6_TLV_PERM_WITH_CHECK,
+	IPV6_TLV_PERM_NO_CHECK,
+	IPV6_TLV_PERM_MAX = IPV6_TLV_PERM_NO_CHECK
+};
+
+/* Flags for EH type that can use a TLV option */
+#define IPV6_TLV_CLASS_FLAG_HOPOPT	0x1
+#define IPV6_TLV_CLASS_FLAG_RTRDSTOPT	0x2
+#define IPV6_TLV_CLASS_FLAG_DSTOPT	0x4
+#define IPV6_TLV_CLASS_MAX		0x7
+
+#define IPV6_TLV_CLASS_ANY_DSTOPT	(IPV6_TLV_CLASS_FLAG_RTRDSTOPT | \
+					 IPV6_TLV_CLASS_FLAG_DSTOPT)
 #endif /* _UAPI_LINUX_IN6_H */
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6dbacf1..af4152e 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -100,15 +100,14 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
 
 /* Parse tlv encoded option header (hop-by-hop or destination) */
 
-static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
-			  struct sk_buff *skb,
+static bool ip6_parse_tlv(unsigned int class, struct sk_buff *skb,
 			  int max_count)
 {
 	int len = (skb_transport_header(skb)[1] + 1) << 3;
 	const unsigned char *nh = skb_network_header(skb);
 	int off = skb_network_header_len(skb);
-	const struct tlvtype_proc *curr;
 	bool disallow_unknowns = false;
+	struct tlv_rx_param *tprx;
 	int tlv_count = 0;
 	int padlen = 0;
 
@@ -117,12 +116,16 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
 		max_count = -max_count;
 	}
 
-	if (skb_transport_offset(skb) + len > skb_headlen(skb))
-		goto bad;
+	if (skb_transport_offset(skb) + len > skb_headlen(skb)) {
+		kfree_skb(skb);
+		return false;
+	}
 
 	off += 2;
 	len -= 2;
 
+	rcu_read_unlock();
+
 	while (len > 0) {
 		int optlen = nh[off + 1] + 2;
 		int i;
@@ -162,19 +165,19 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
 			if (tlv_count > max_count)
 				goto bad;
 
-			for (curr = procs; curr->type >= 0; curr++) {
-				if (curr->type == nh[off]) {
-					/* type specific length/alignment
-					   checks will be performed in the
-					   func(). */
-					if (curr->func(skb, off) == false)
-						return false;
-					break;
-				}
+			tprx = tlv_deref_rx_params(nh[off]);
+
+			if ((tprx->class & class) && tprx->func) {
+				/* Handler will apply additional checks to
+				 * the TLV
+				 */
+				if (!tprx->func(class, skb, off))
+					goto bad_nofree;
+			} else if (!ip6_tlvopt_unknown(skb, off,
+						       disallow_unknowns)) {
+				/* No appropriate handler, TLV is unknown */
+				goto bad_nofree;
 			}
-			if (curr->type < 0 &&
-			    !ip6_tlvopt_unknown(skb, off, disallow_unknowns))
-				return false;
 
 			padlen = 0;
 			break;
@@ -183,10 +186,14 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
 		len -= optlen;
 	}
 
-	if (len == 0)
+	if (len == 0) {
+		rcu_read_unlock();
 		return true;
+	}
 bad:
 	kfree_skb(skb);
+bad_nofree:
+	rcu_read_unlock();
 	return false;
 }
 
@@ -220,7 +227,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
 	dstbuf = opt->dst1;
 #endif
 
-	if (ip6_parse_tlv(tlvprocdestopt_lst, skb,
+	if (ip6_parse_tlv(IPV6_TLV_CLASS_FLAG_DSTOPT, skb,
 			  init_net.ipv6.sysctl.max_dst_opts_cnt)) {
 		skb->transport_header += extlen;
 		opt = IP6CB(skb);
@@ -643,7 +650,7 @@ int ipv6_parse_hopopts(struct sk_buff *skb)
 		goto fail_and_free;
 
 	opt->flags |= IP6SKB_HOPBYHOP;
-	if (ip6_parse_tlv(tlvprochopopt_lst, skb,
+	if (ip6_parse_tlv(IPV6_TLV_CLASS_FLAG_HOPOPT, skb,
 			  init_net.ipv6.sysctl.max_hbh_opts_cnt)) {
 		skb->transport_header += extlen;
 		opt = IP6CB(skb);
diff --git a/net/ipv6/exthdrs_options.c b/net/ipv6/exthdrs_options.c
index 70266a6..397d9b3 100644
--- a/net/ipv6/exthdrs_options.c
+++ b/net/ipv6/exthdrs_options.c
@@ -11,6 +11,7 @@
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
 #include <net/xfrm.h>
 #endif
+#include <uapi/linux/in.h>
 
 /*	Parsing tlv encoded headers.
  *
@@ -19,6 +20,8 @@
  *	It MUST NOT touch skb->h.
  */
 
+struct tlv_param_table __rcu *tlv_param_table;
+
 struct ipv6_txoptions *
 ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
 {
@@ -160,7 +163,7 @@ EXPORT_SYMBOL_GPL(ipv6_fixup_options);
 /* Destination options header */
 
 #if IS_ENABLED(CONFIG_IPV6_MIP6)
-static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
+static bool ipv6_dest_hao(unsigned int class, struct sk_buff *skb, int optoff)
 {
 	struct ipv6_destopt_hao *hao;
 	struct inet6_skb_parm *opt = IP6CB(skb);
@@ -219,16 +222,6 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
 }
 #endif
 
-const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-	{
-		.type	= IPV6_TLV_HAO,
-		.func	= ipv6_dest_hao,
-	},
-#endif
-	{-1,			NULL}
-};
-
 /* Hop-by-hop options */
 
 /* Note: we cannot rely on skb_dst(skb) before we assign it in
@@ -247,7 +240,7 @@ static inline struct net *ipv6_skb_net(struct sk_buff *skb)
 
 /* Router Alert as of RFC 2711 */
 
-static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_ra(unsigned int class, struct sk_buff *skb, int optoff)
 {
 	const unsigned char *nh = skb_network_header(skb);
 
@@ -265,7 +258,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
 
 /* Jumbo payload */
 
-static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_jumbo(unsigned int class, struct sk_buff *skb, int optoff)
 {
 	const unsigned char *nh = skb_network_header(skb);
 	struct inet6_dev *idev = __in6_dev_get_safely(skb->dev);
@@ -309,7 +302,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
 
 /* CALIPSO RFC 5570 */
 
-static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
+static bool ipv6_hop_calipso(unsigned int class, struct sk_buff *skb,
+			     int optoff)
 {
 	const unsigned char *nh = skb_network_header(skb);
 
@@ -329,18 +323,364 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
 	return false;
 }
 
-const struct tlvtype_proc tlvprochopopt_lst[] = {
+/* TLV parameter table functions and structures */
+
+static void tlv_param_table_release(struct rcu_head *rcu)
+{
+	struct tlv_param_table *tpt =
+	    container_of(rcu, struct tlv_param_table, rcu);
+
+	vfree(tpt);
+}
+
+/* Default (unset) values for TX TLV parameters */
+static const struct tlv_param tlv_default_param = {
+	.tx_params.preferred_order = 0,
+	.tx_params.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+	.tx_params.user_perm = IPV6_TLV_PERM_NONE,
+	.tx_params.class = 0,
+	.tx_params.align_mult = (4 - 1), /* Default alignment: 4n + 2 */
+	.tx_params.align_off = 2,
+	.tx_params.min_data_len = 0,
+	.tx_params.max_data_len = 255,
+	.tx_params.data_len_mult = (1 - 1), /* No default length align */
+	.tx_params.data_len_off = 0,
+};
+
+static DEFINE_MUTEX(tlv_mutex);
+
+static size_t tlv_param_table_size(unsigned char count)
+{
+	return sizeof(struct tlv_param_table) +
+	    (count * sizeof(struct tlv_param));
+}
+
+/* mutex held */
+static int __tlv_set_param(unsigned char type,
+			   const struct tlv_rx_param *rx_param_tmpl,
+			   const struct tlv_tx_param *tx_param_tmpl)
+{
+	struct tlv_param_table *tpt, *old;
+	unsigned char count, pos;
+	struct tlv_param *tp;
+
+	old = rcu_dereference_protected(tlv_param_table,
+					lockdep_is_held(&tlv_mutex));
+
+	if (old->entries[type]) {
+		pos = old->entries[type];
+		count = old->count;
+	} else {
+		pos = old->count;
+		count = pos + 1;
+	}
+
+	tpt = vmalloc(tlv_param_table_size(count));
+	if (!tpt)
+		return -ENOMEM;
+
+	memcpy(tpt, old, tlv_param_table_size(old->count));
+
+	tp = &tpt->params[pos];
+	memcpy(&tp->rx_params, rx_param_tmpl, sizeof(tp->rx_params));
+	memcpy(&tp->tx_params, tx_param_tmpl, sizeof(tp->tx_params));
+
+	tpt->entries[type] = pos;
+	tpt->count = count;
+
+	rcu_assign_pointer(tlv_param_table, tpt);
+
+	call_rcu(&old->rcu, tlv_param_table_release);
+
+	return 0;
+}
+
+/* mutex held */
+static int __tlv_unset_param(unsigned char type)
+{
+	struct tlv_param_table *tpt, *old;
+	struct tlv_param *tp;
+	unsigned char pos;
+	int i;
+
+	old = rcu_dereference_protected(tlv_param_table,
+					lockdep_is_held(&tlv_mutex));
+
+	if (!old->entries[type])
+		return 0;
+
+	tpt = vmalloc(tlv_param_table_size(old->count - 1));
+	if (!tpt)
+		return -ENOMEM;
+
+	pos = old->entries[type];
+
+	memcpy(tpt->params, old->params, pos * sizeof(*tp));
+	memcpy(&tpt->params[pos], &old->params[pos + 1],
+	       (old->count - pos - 1) * sizeof(*tp));
+
+	for (i = 0; i < 256; i++) {
+		if (old->entries[i] > pos)
+			tpt->entries[i] = old->entries[i] - 1;
+		else
+			tpt->entries[i] = old->entries[i];
+	}
+
+	tpt->entries[type] = 0;
+
+	tpt->count = old->count - 1;
+
+	rcu_assign_pointer(tlv_param_table, tpt);
+
+	call_rcu(&old->rcu, tlv_param_table_release);
+
+	return 0;
+}
+
+void __tlv_destroy_param_table(void)
+{
+	struct tlv_param_table *tpt;
+
+	mutex_lock(&tlv_mutex);
+
+	tpt = rcu_dereference_protected(tlv_param_table,
+					lockdep_is_held(&tlv_mutex));
+	if (tpt) {
+		rcu_assign_pointer(tlv_param_table, tpt);
+		call_rcu(&tpt->rcu, tlv_param_table_release);
+	}
+
+	mutex_unlock(&tlv_mutex);
+}
+
+int tlv_set_param(unsigned char type,
+		  const struct tlv_rx_param *rx_param_tmpl,
+		  const struct tlv_tx_param *tx_param_tmpl)
+{
+	int ret;
+
+	if (type < 2)
+		return -EINVAL;
+
+	mutex_lock(&tlv_mutex);
+
+	ret = __tlv_set_param(type, rx_param_tmpl, tx_param_tmpl);
+
+	mutex_unlock(&tlv_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(tlv_set_param);
+
+int tlv_unset_rx_param(unsigned char type)
+{
+	struct tlv_tx_param *tptx;
+	int ret;
+
+	if (type < 2)
+		return -EINVAL;
+
+	mutex_lock(&tlv_mutex);
+
+	tptx = tlv_deref_tx_params(type);
+
+	if (!tptx->preferred_order)
+		ret = __tlv_unset_param(type);
+	else
+		ret = __tlv_set_param(type, &tlv_default_param.rx_params, tptx);
+
+	mutex_unlock(&tlv_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(tlv_unset_rx_param);
+
+int tlv_set_rx_param(unsigned char type, struct tlv_rx_param *rx_param_tmpl)
+{
+	int ret;
+
+	if (type < 2)
+		return -EINVAL;
+
+	mutex_lock(&tlv_mutex);
+
+	ret = __tlv_set_param(type, rx_param_tmpl, tlv_deref_tx_params(type));
+
+	mutex_unlock(&tlv_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(tlv_set_rx_param);
+
+int tlv_unset_tx_param(unsigned char type)
+{
+	struct tlv_rx_param *tprx;
+	int ret;
+
+	if (type < 2)
+		return -EINVAL;
+
+	mutex_lock(&tlv_mutex);
+
+	tprx = tlv_deref_rx_params(type);
+
+	if (!tprx->class)
+		ret = __tlv_unset_param(type);
+	else
+		ret = __tlv_set_param(type, tprx, &tlv_default_param.tx_params);
+
+	mutex_unlock(&tlv_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(tlv_unset_tx_param);
+
+int tlv_set_tx_param(unsigned char type, struct tlv_tx_param *tx_param_tmpl)
+{
+	int ret;
+
+	if (type < 2)
+		return -EINVAL;
+
+	mutex_lock(&tlv_mutex);
+
+	ret = __tlv_set_param(type, tlv_deref_rx_params(type), tx_param_tmpl);
+
+	mutex_unlock(&tlv_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(tlv_set_tx_param);
+
+struct tlv_init_params {
+	int type;
+	struct tlv_tx_param t;
+	struct tlv_rx_param r;
+};
+
+static const struct tlv_init_params tlv_init_params[] __initconst = {
 	{
-		.type	= IPV6_TLV_ROUTERALERT,
-		.func	= ipv6_hop_ra,
+		.type = IPV6_TLV_HAO,
+
+		.t.preferred_order = TLV_PREF_ORDER_HAO,
+		.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+		.t.user_perm = IPV6_TLV_PERM_NONE,
+		.t.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
+		.t.align_mult = (8 - 1), /* Align to 8n + 6 */
+		.t.align_off = 6,
+		.t.min_data_len = 16,
+		.t.max_data_len = 16,
+		.t.data_len_mult = (1 - 1), /* Fixed length */
+		.t.data_len_off = 0,
+
+		.r.func = ipv6_dest_hao,
+		.r.class = IPV6_TLV_CLASS_FLAG_DSTOPT,
 	},
 	{
-		.type	= IPV6_TLV_JUMBO,
-		.func	= ipv6_hop_jumbo,
+		.type = IPV6_TLV_ROUTERALERT,
+
+		.t.preferred_order = TLV_PREF_ORDER_ROUTERALERT,
+		.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+		.t.user_perm = IPV6_TLV_PERM_NONE,
+		.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+		.t.align_mult = (2 - 1), /* Align to 2n */
+		.t.align_off = 0,
+		.t.min_data_len = 2,
+		.t.max_data_len = 2,
+		.t.data_len_mult = (1 - 1), /* Fixed length */
+		.t.data_len_off = 0,
+
+		.r.func = ipv6_hop_ra,
+		.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
 	},
 	{
-		.type	= IPV6_TLV_CALIPSO,
-		.func	= ipv6_hop_calipso,
+		.type = IPV6_TLV_JUMBO,
+
+		.t.preferred_order = TLV_PREF_ORDER_JUMBO,
+		.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+		.t.user_perm = IPV6_TLV_PERM_NONE,
+		.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+		.t.align_mult = (4 - 1), /* Align to 4n + 2 */
+		.t.align_off = 2,
+		.t.min_data_len = 4,
+		.t.max_data_len = 4,
+		.t.data_len_mult = (1 - 1), /* Fixed length */
+		.t.data_len_off = 0,
+
+		.r.func = ipv6_hop_jumbo,
+		.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
 	},
-	{ -1, }
+	{
+		.type = IPV6_TLV_CALIPSO,
+
+		.t.preferred_order = TLV_PREF_ORDER_CALIPSO,
+		.t.admin_perm = IPV6_TLV_PERM_NO_CHECK,
+		.t.user_perm = IPV6_TLV_PERM_NONE,
+		.t.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+		.t.align_mult = (4 - 1), /* Align to 4n + 2 */
+		.t.align_off = 2,
+		.t.min_data_len = 8,
+		.t.max_data_len = 252,
+		.t.data_len_mult = (4 - 1), /* Length is multiple of 4 */
+		.t.data_len_off = 0,
+
+		.r.func = ipv6_hop_calipso,
+		.r.class = IPV6_TLV_CLASS_FLAG_HOPOPT,
+	}
 };
+
+static int __init exthdrs_init(void)
+{
+	unsigned long check_map[BITS_TO_LONGS(256)];
+	struct tlv_param_table *tpt;
+	size_t tsize;
+	int i;
+
+	memset(check_map, 0, sizeof(check_map));
+
+	tsize = tlv_param_table_size(ARRAY_SIZE(tlv_init_params) + 1);
+
+	tpt = vmalloc(tsize);
+	if (!tpt)
+		return -ENOMEM;
+
+	memset(tpt, 0, tsize);
+
+	/* Zeroth TLV parameter entry is default */
+	tpt->params[0] = tlv_default_param;
+
+	for (i = 0; i < ARRAY_SIZE(tlv_init_params); i++) {
+		const struct tlv_init_params *tpi = &tlv_init_params[i];
+		unsigned int order = tpi->t.preferred_order;
+		struct tlv_param *tp = &tpt->params[i + 1];
+
+		WARN_ON(tpi->type < 2); /* Padding TLV initialized? */
+
+		if (order) {
+			WARN_ON(test_bit(order, check_map));
+			set_bit(order, check_map);
+			tp->tx_params = tpi->t;
+		} else {
+			tp->tx_params = tlv_default_param.tx_params;
+		}
+
+		if (tpi->r.class)
+			tp->rx_params = tpi->r;
+		else
+			tp->rx_params = tlv_default_param.rx_params;
+
+		tpt->entries[tpi->type] = i + 1;
+	}
+
+	tpt->count = ARRAY_SIZE(tlv_init_params) + 1;
+
+	RCU_INIT_POINTER(tlv_param_table, tpt);
+
+	return 0;
+}
+module_init(exthdrs_init);
+
+static void __exit exthdrs_fini(void)
+{
+}
+module_exit(exthdrs_fini);
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ