1/*
2 * lib/netfilter/exp.c	Conntrack Expectation
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
11 * Copyright (c) 2007 Secure Computing Corporation
12 * Copyright (c= 2008 Patrick McHardy <kaber@trash.net>
13 * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
14 */
15
16/**
17 * @ingroup nfnl
18 * @defgroup exp Expectation
19 * @brief
20 * @{
21 */
22
23#include <byteswap.h>
24#include <sys/types.h>
25#include <linux/netfilter/nfnetlink_conntrack.h>
26
27#include <netlink-private/netlink.h>
28#include <netlink/attr.h>
29#include <netlink/netfilter/nfnl.h>
30#include <netlink/netfilter/exp.h>
31
32static struct nl_cache_ops nfnl_exp_ops;
33
34static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = {
35	[CTA_EXPECT_MASTER]	= { .type = NLA_NESTED },
36	[CTA_EXPECT_TUPLE]	= { .type = NLA_NESTED },
37	[CTA_EXPECT_MASK]	= { .type = NLA_NESTED },
38	[CTA_EXPECT_TIMEOUT]	= { .type = NLA_U32 },
39	[CTA_EXPECT_ID]		= { .type = NLA_U32 },
40	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_STRING },
41	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 },
42	[CTA_EXPECT_FLAGS]	= { .type = NLA_U32 },    // Added in kernel 2.6.37
43	[CTA_EXPECT_CLASS]	= { .type = NLA_U32 },    // Added in kernel 3.5
44	[CTA_EXPECT_NAT]	= { .type = NLA_NESTED }, // Added in kernel 3.5
45	[CTA_EXPECT_FN]		= { .type = NLA_STRING }, // Added in kernel 3.5
46};
47
48static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = {
49	[CTA_TUPLE_IP]		= { .type = NLA_NESTED },
50	[CTA_TUPLE_PROTO]	= { .type = NLA_NESTED },
51};
52
53static struct nla_policy exp_ip_policy[CTA_IP_MAX+1] = {
54	[CTA_IP_V4_SRC]		= { .type = NLA_U32 },
55	[CTA_IP_V4_DST]		= { .type = NLA_U32 },
56	[CTA_IP_V6_SRC]		= { .minlen = 16 },
57	[CTA_IP_V6_DST]		= { .minlen = 16 },
58};
59
60static struct nla_policy exp_proto_policy[CTA_PROTO_MAX+1] = {
61	[CTA_PROTO_NUM]		= { .type = NLA_U8 },
62	[CTA_PROTO_SRC_PORT]	= { .type = NLA_U16 },
63	[CTA_PROTO_DST_PORT]	= { .type = NLA_U16 },
64	[CTA_PROTO_ICMP_ID]	= { .type = NLA_U16 },
65	[CTA_PROTO_ICMP_TYPE]	= { .type = NLA_U8 },
66	[CTA_PROTO_ICMP_CODE]	= { .type = NLA_U8 },
67	[CTA_PROTO_ICMPV6_ID]	= { .type = NLA_U16 },
68	[CTA_PROTO_ICMPV6_TYPE]	= { .type = NLA_U8 },
69	[CTA_PROTO_ICMPV6_CODE]	= { .type = NLA_U8 },
70};
71
72static struct nla_policy exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = {
73	[CTA_EXPECT_NAT_DIR]	= { .type = NLA_U32 },
74	[CTA_EXPECT_NAT_TUPLE]	= { .type = NLA_NESTED },
75};
76
77static int exp_parse_ip(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
78{
79	struct nlattr *tb[CTA_IP_MAX+1];
80	struct nl_addr *addr;
81	int err;
82
83	err = nla_parse_nested(tb, CTA_IP_MAX, attr, exp_ip_policy);
84	if (err < 0)
85		goto errout;
86
87	if (tb[CTA_IP_V4_SRC]) {
88		addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET);
89		if (addr == NULL)
90			goto errout_enomem;
91		err = nfnl_exp_set_src(exp, tuple, addr);
92		nl_addr_put(addr);
93		if (err < 0)
94			goto errout;
95	}
96	if (tb[CTA_IP_V4_DST]) {
97		addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET);
98		if (addr == NULL)
99			goto errout_enomem;
100		err = nfnl_exp_set_dst(exp, tuple, addr);
101		nl_addr_put(addr);
102		if (err < 0)
103			goto errout;
104	}
105	if (tb[CTA_IP_V6_SRC]) {
106		addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6);
107		if (addr == NULL)
108			goto errout_enomem;
109		err = nfnl_exp_set_src(exp, tuple, addr);
110		nl_addr_put(addr);
111		if (err < 0)
112			goto errout;
113	}
114	if (tb[CTA_IP_V6_DST]) {
115		addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6);
116		if (addr == NULL)
117			goto errout_enomem;
118		err = nfnl_exp_set_dst(exp, tuple, addr);
119		nl_addr_put(addr);
120		if (err < 0)
121			goto errout;
122	}
123
124	return 0;
125
126errout_enomem:
127	err = -NLE_NOMEM;
128errout:
129	return err;
130}
131
132static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
133{
134	struct nlattr *tb[CTA_PROTO_MAX+1];
135	int err;
136	uint16_t srcport = 0, dstport = 0, icmpid = 0;
137	uint8_t icmptype = 0, icmpcode = 0;
138
139	err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy);
140	if (err < 0)
141		return err;
142
143	if (tb[CTA_PROTO_NUM])
144		nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM]));
145
146	if (tb[CTA_PROTO_SRC_PORT])
147		srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
148	if (tb[CTA_PROTO_DST_PORT])
149		dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]));
150	if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT])
151		nfnl_exp_set_ports(exp, tuple, srcport, dstport);
152
153	if (tb[CTA_PROTO_ICMP_ID])
154		icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]));
155	if (tb[CTA_PROTO_ICMP_TYPE])
156		icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
157	if (tb[CTA_PROTO_ICMP_CODE])
158		icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
159	if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE])
160		nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode);
161	return 0;
162}
163
164static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
165{
166	struct nlattr *tb[CTA_TUPLE_MAX+1];
167	int err;
168
169	err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy);
170	if (err < 0)
171		return err;
172
173	if (tb[CTA_TUPLE_IP]) {
174		err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]);
175		if (err < 0)
176			return err;
177	}
178
179	if (tb[CTA_TUPLE_PROTO]) {
180		err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]);
181		if (err < 0)
182			return err;
183	}
184
185	return 0;
186}
187
188static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr)
189{
190	struct nlattr *tb[CTA_EXPECT_NAT_MAX+1];
191	int err;
192
193	err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy);
194	if (err < 0)
195		return err;
196
197	if (tb[CTA_EXPECT_NAT_DIR])
198		nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR]));
199
200	if (tb[CTA_EXPECT_NAT_TUPLE]) {
201		err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]);
202		if (err < 0)
203			return err;
204	}
205
206	return 0;
207}
208
209int nfnlmsg_exp_group(struct nlmsghdr *nlh)
210{
211	switch (nfnlmsg_subtype(nlh)) {
212	case IPCTNL_MSG_EXP_NEW:
213		if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
214			return NFNLGRP_CONNTRACK_EXP_NEW;
215		else
216			return NFNLGRP_CONNTRACK_EXP_UPDATE;
217	case IPCTNL_MSG_EXP_DELETE:
218		return NFNLGRP_CONNTRACK_EXP_DESTROY;
219	default:
220		return NFNLGRP_NONE;
221	}
222}
223
224int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result)
225{
226	struct nfnl_exp *exp;
227	struct nlattr *tb[CTA_MAX+1];
228	int err;
229
230	exp = nfnl_exp_alloc();
231	if (!exp)
232		return -NLE_NOMEM;
233
234	exp->ce_msgtype = nlh->nlmsg_type;
235
236	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX,
237			  exp_policy);
238	if (err < 0)
239		goto errout;
240
241	nfnl_exp_set_family(exp, nfnlmsg_family(nlh));
242
243	if (tb[CTA_EXPECT_TUPLE]) {
244		err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]);
245		if (err < 0)
246			goto errout;
247	}
248	if (tb[CTA_EXPECT_MASTER]) {
249		err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]);
250		if (err < 0)
251			goto errout;
252	}
253	if (tb[CTA_EXPECT_MASK]) {
254		err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]);
255		if (err < 0)
256			goto errout;
257	}
258
259	if (tb[CTA_EXPECT_NAT]) {
260		err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]);
261		if (err < 0)
262			goto errout;
263	}
264
265	if (tb[CTA_EXPECT_CLASS])
266		nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS])));
267
268	if (tb[CTA_EXPECT_FN])
269		nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN]));
270
271	if (tb[CTA_EXPECT_TIMEOUT])
272		nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT])));
273
274	if (tb[CTA_EXPECT_ID])
275		nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID])));
276
277	if (tb[CTA_EXPECT_HELP_NAME])
278		nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME]));
279
280	if (tb[CTA_EXPECT_ZONE])
281		nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE])));
282
283	if (tb[CTA_EXPECT_FLAGS])
284		nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS])));
285
286	*result = exp;
287	return 0;
288
289errout:
290	nfnl_exp_put(exp);
291	return err;
292}
293
294static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
295			 struct nlmsghdr *nlh, struct nl_parser_param *pp)
296{
297	struct nfnl_exp *exp;
298	int err;
299
300	if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0)
301		return err;
302
303	err = pp->pp_cb((struct nl_object *) exp, pp);
304	nfnl_exp_put(exp);
305	return err;
306}
307
308int nfnl_exp_dump_request(struct nl_sock *sk)
309{
310	return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET,
311				NLM_F_DUMP, AF_UNSPEC, 0);
312}
313
314static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk)
315{
316	return nfnl_exp_dump_request(sk);
317}
318
319static int exp_get_tuple_attr(int tuple)
320{
321	int attr = 0;
322
323	switch (tuple) {
324		case CTA_EXPECT_MASTER:
325			attr = NFNL_EXP_TUPLE_MASTER;
326			break;
327		case CTA_EXPECT_MASK:
328			attr = NFNL_EXP_TUPLE_MASK;
329			break;
330		case CTA_EXPECT_NAT:
331			attr = NFNL_EXP_TUPLE_NAT;
332			break;
333		case CTA_EXPECT_TUPLE:
334		default :
335			attr = NFNL_EXP_TUPLE_EXPECT;
336			break;
337	}
338
339	return attr;
340}
341
342static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp,
343			       int cta)
344{
345	struct nlattr *tuple, *ip, *proto;
346	struct nl_addr *addr;
347	int family;
348
349	family = nfnl_exp_get_family(exp);
350
351	int type = exp_get_tuple_attr(cta);
352
353    if (cta == CTA_EXPECT_NAT)
354        tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE);
355    else
356        tuple = nla_nest_start(msg, cta);
357
358	if (!tuple)
359		goto nla_put_failure;
360
361	ip = nla_nest_start(msg, CTA_TUPLE_IP);
362	if (!ip)
363		goto nla_put_failure;
364
365	addr = nfnl_exp_get_src(exp, type);
366	if (addr)
367		NLA_PUT_ADDR(msg,
368			     family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC,
369			     addr);
370
371	addr = nfnl_exp_get_dst(exp, type);
372	if (addr)
373		NLA_PUT_ADDR(msg,
374			     family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST,
375			     addr);
376
377	nla_nest_end(msg, ip);
378
379	proto = nla_nest_start(msg, CTA_TUPLE_PROTO);
380	if (!proto)
381		goto nla_put_failure;
382
383	if (nfnl_exp_test_l4protonum(exp, type))
384		NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type));
385
386	if (nfnl_exp_test_ports(exp, type)) {
387		NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT,
388			htons(nfnl_exp_get_src_port(exp, type)));
389
390		NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
391			htons(nfnl_exp_get_dst_port(exp, type)));
392	}
393
394	if (nfnl_exp_test_icmp(exp, type)) {
395		NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
396			htons(nfnl_exp_get_icmp_id(exp, type)));
397
398		NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
399			    nfnl_exp_get_icmp_type(exp, type));
400
401		NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
402			    nfnl_exp_get_icmp_code(exp, type));
403	}
404
405	nla_nest_end(msg, proto);
406
407	nla_nest_end(msg, tuple);
408	return 0;
409
410nla_put_failure:
411	return -NLE_MSGSIZE;
412}
413
414static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp)
415{
416	struct nlattr *nat;
417	int err;
418
419	nat = nla_nest_start(msg, CTA_EXPECT_NAT);
420
421	if (nfnl_exp_test_nat_dir(exp)) {
422		NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR,
423				nfnl_exp_get_nat_dir(exp));
424	}
425
426	if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT)) < 0)
427		goto nla_put_failure;
428
429	nla_nest_end(msg, nat);
430	return 0;
431
432nla_put_failure:
433	return -NLE_MSGSIZE;
434}
435
436static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags,
437				 struct nl_msg **result)
438{
439	struct nl_msg *msg;
440	int err;
441
442	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags,
443				   nfnl_exp_get_family(exp), 0);
444	if (msg == NULL)
445		return -NLE_NOMEM;
446
447	if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0)
448		goto err_out;
449
450	if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0)
451		goto err_out;
452
453	if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0)
454		goto err_out;
455
456	if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) {
457		if ((err = nfnl_exp_build_nat(msg, exp)) < 0)
458			goto err_out;
459	}
460
461	if (nfnl_exp_test_class(exp))
462		NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp)));
463
464	if (nfnl_exp_test_fn(exp))
465		NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp));
466
467	if (nfnl_exp_test_id(exp))
468		NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp)));
469
470	if (nfnl_exp_test_timeout(exp))
471		NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp)));
472
473	if (nfnl_exp_test_helper_name(exp))
474		NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp));
475
476	if (nfnl_exp_test_zone(exp))
477		NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp)));
478
479	if (nfnl_exp_test_flags(exp))
480		NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp)));
481
482	*result = msg;
483	return 0;
484
485nla_put_failure:
486err_out:
487	nlmsg_free(msg);
488	return err;
489}
490
491int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags,
492			      struct nl_msg **result)
493{
494	return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result);
495}
496
497int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
498{
499	struct nl_msg *msg;
500	int err;
501
502	if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0)
503		return err;
504
505	err = nl_send_auto_complete(sk, msg);
506	nlmsg_free(msg);
507	if (err < 0)
508		return err;
509
510	return wait_for_ack(sk);
511}
512
513int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags,
514				 struct nl_msg **result)
515{
516	return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result);
517}
518
519int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
520{
521	struct nl_msg *msg;
522	int err;
523
524	if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0)
525		return err;
526
527	err = nl_send_auto_complete(sk, msg);
528	nlmsg_free(msg);
529	if (err < 0)
530		return err;
531
532	return wait_for_ack(sk);
533}
534
535int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags,
536				struct nl_msg **result)
537{
538	return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result);
539}
540
541int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
542{
543	struct nl_msg *msg;
544	int err;
545
546	if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0)
547		return err;
548
549	err = nl_send_auto_complete(sk, msg);
550	nlmsg_free(msg);
551	if (err < 0)
552		return err;
553
554	return wait_for_ack(sk);
555}
556
557/**
558 * @name Cache Management
559 * @{
560 */
561
562/**
563 * Build a expectation cache holding all expectations currently in the kernel
564 * @arg sk		Netlink socket.
565 * @arg result		Pointer to store resulting cache.
566 *
567 * Allocates a new cache, initializes it properly and updates it to
568 * contain all expectations currently in the kernel.
569 *
570 * @return 0 on success or a negative error code.
571 */
572int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
573{
574	return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result);
575}
576
577/** @} */
578
579/**
580 * @name Expectation Addition
581 * @{
582 */
583
584/** @} */
585
586static struct nl_af_group exp_groups[] = {
587	{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW },
588	{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE },
589	{ AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY },
590	{ END_OF_GROUP_LIST },
591};
592
593#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type))
594static struct nl_cache_ops nfnl_exp_ops = {
595	.co_name		    = "netfilter/exp",
596	.co_hdrsize		    = NFNL_HDRLEN,
597	.co_msgtypes		= {
598		{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" },
599		{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" },
600		{ NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" },
601		END_OF_MSGTYPES_LIST,
602	},
603	.co_protocol		= NETLINK_NETFILTER,
604	.co_groups		= exp_groups,
605	.co_request_update	= exp_request_update,
606	.co_msg_parser		= exp_msg_parser,
607	.co_obj_ops		= &exp_obj_ops,
608};
609
610static void __init exp_init(void)
611{
612	nl_cache_mngt_register(&nfnl_exp_ops);
613}
614
615static void __exit exp_exit(void)
616{
617	nl_cache_mngt_unregister(&nfnl_exp_ops);
618}
619
620/** @} */
621