netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* netfilter support in libnl
@ 2007-09-03  5:09 Philip Craig
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
                   ` (3 more replies)
  0 siblings, 4 replies; 22+ messages in thread
From: Philip Craig @ 2007-09-03  5:09 UTC (permalink / raw)
  To: Netfilter Developer Mailing List

There have been comments in the past on this list about using libnl
for the netfilter netlink support, and since I would like to use a
common library api for all the netlink interfaces, I decided to give
it a go.  These patches are the result of that.

Any comments welcome, particularly about whether this is still
something that is desired.

Some notes about the patches:
- only tested on an ARM platform
- only conntrack and log implemented (not queue)
- only dumping and monitoring implemented (no modifications)
- monitoring ct/log events requires copying all the netlink message
data into another object, which seems a bit inefficient
- I defined a cache to monitor log messages, so that it can find the
parser automatically, but not sure that caching log events makes sense
- I've added 'test' operations for some ct/log fields.  None of the
other objects in libnl have these, but I think they are needed.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/3] libnl: add netfilter support
  2007-09-03  5:09 netfilter support in libnl Philip Craig
@ 2007-09-03  5:11 ` Philip Craig
  2007-09-03  9:50   ` Patrick McHardy
                     ` (2 more replies)
  2007-09-03  5:11 ` [PATCH 2/3] libnl: add netfilter conntrack support Philip Craig
                   ` (2 subsequent siblings)
  3 siblings, 3 replies; 22+ messages in thread
From: Philip Craig @ 2007-09-03  5:11 UTC (permalink / raw)
  To: Netfilter Developer Mailing List

[-- Attachment #1: Type: text/plain, Size: 1 bytes --]



[-- Attachment #2: nfnl.patch --]
[-- Type: text/x-diff, Size: 13989 bytes --]

---
 include/linux/netfilter/nfnetlink.h |  106 +++++++++++++++
 include/netlink/netfilter/nfnl.h    |   44 ++++++
 include/netlink/netlink.h           |    1 
 lib/Makefile                        |    2 
 lib/attr.c                          |    3 
 lib/netfilter/nfnl.c                |  246 ++++++++++++++++++++++++++++++++++++
 6 files changed, 401 insertions(+), 1 deletion(-)

Index: libnl/lib/Makefile
===================================================================
--- libnl.orig/lib/Makefile	2007-09-03 14:24:29.000000000 +1000
+++ libnl/lib/Makefile	2007-09-03 14:24:45.000000000 +1000
@@ -25,6 +25,8 @@ CIN      += $(wildcard route/cls/*.c)
 CIN      += $(wildcard genl/*.c)
 # fib lookup
 CIN      += $(wildcard fib_lookup/*.c)
+# Netfilter
+CIN      += $(wildcard netfilter/*.c)
 
 DEPS     := $(CIN:%.c=%.d)
 OBJ      := $(CIN:%.c=%.o)
Index: libnl/lib/attr.c
===================================================================
--- libnl.orig/lib/attr.c	2007-09-03 14:24:29.000000000 +1000
+++ libnl/lib/attr.c	2007-09-03 14:24:45.000000000 +1000
@@ -261,7 +261,8 @@ int nla_parse(struct nlattr *tb[], int m
 	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
 
 	nla_for_each_attr(nla, head, len, rem) {
-		uint16_t type = nla->nla_type;
+		/* Ignore NFNL_NFA_NEST bit, hope nothing else uses it */
+		uint16_t type = nla->nla_type & 0x7fff;
 
 		if (type == 0) {
 			fprintf(stderr, "Illegal nla->nla_type == 0\n");
Index: libnl/include/netlink/netfilter/nfnl.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/netlink/netfilter/nfnl.h	2007-09-03 14:24:45.000000000 +1000
@@ -0,0 +1,44 @@
+/*
+ * netlink/nfnl/nfnl.h		Netfilter Netlink
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#ifndef NETLINK_NFNL_H_
+#define NETLINK_NFNL_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NFNL_HDRLEN NLMSG_ALIGN(sizeof(struct nfgenmsg))
+#define NFNLMSG_TYPE(subsys, subtype) (((subsys) << 8) | (subtype))
+
+extern int		nfnl_connect(struct nl_handle *);
+
+extern uint8_t		nfnlmsg_subsys(struct nlmsghdr *);
+extern uint8_t		nfnlmsg_subtype(struct nlmsghdr *);
+extern uint8_t		nfnlmsg_family(struct nlmsghdr *);
+extern uint16_t		nfnlmsg_res_id(struct nlmsghdr *);
+
+extern int		nfnl_send_simple(struct nl_handle *, uint8_t, uint8_t,
+					 int, uint8_t, uint16_t);
+extern struct nl_msg *	nfnlmsg_alloc_simple(uint8_t, uint8_t, int,
+					     uint8_t, uint16_t);
+extern int		nfnlmsg_put(struct nl_msg *, uint32_t, uint32_t,
+				    uint8_t, uint8_t, int, uint8_t, uint16_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: libnl/lib/netfilter/nfnl.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/lib/netfilter/nfnl.c	2007-09-03 14:24:45.000000000 +1000
@@ -0,0 +1,246 @@
+/*
+ * lib/netfilter/nfnl.c		Netfilter Netlink
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+/**
+ * @ingroup nlfam
+ * @defgroup nfnl Netfilter Netlink
+ *
+ * @par Message Format
+ * @code
+ *  <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * |           Header           | Pad |       Payload       | Pad |
+ * |      struct nlmsghdr       |     |                     |     |
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * @endcode
+ * @code
+ *  <-------- NFNL_HDRLEN --------->
+ * +--------------------------+- - -+------------+
+ * | Netfilter Netlink Header | Pad | Attributes |
+ * |    struct nfgenmsg       |     |            |
+ * +--------------------------+- - -+------------+
+ * nfnlmsg_attrdata(nfg, hdrlen)-----^
+ * @endcode
+ *
+ * @par 1) Creating a new netfilter netlink message
+ * @code
+ * struct nl_msg *msg;
+ *
+ * // Create a new empty netlink message
+ * msg = nlmsg_alloc();
+ *
+ * // Append the netlink and netfilter netlink message header
+ * hdr = nfnlmsg_put(msg, PID, SEQ, SUBSYS, TYPE, NLM_F_ECHO,
+ *                   FAMILY, RES_ID);
+ *
+ * // Append the attributes.
+ * nla_put_u32(msg, 1, 0x10);
+ *
+ * // Message is ready to be sent.
+ * nl_send_auto_complete(nl_handle, msg);
+ *
+ * // All done? Free the message.
+ * nlmsg_free(msg);
+ * @endcode
+ *
+ * @par 2) Sending of trivial messages
+ * @code
+ * // For trivial messages not requiring any subsys specific header or
+ * // attributes, nfnl_send_simple() may be used to send messages directly.
+ * nfnl_send_simple(nl_handle, SUBSYS, TYPE, 0, FAMILY, RES_ID);
+ * @endcode
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/netfilter/nfnl.h>
+
+/**
+ * @name Socket Creating
+ * @{
+ */
+
+/**
+ * Create and connect netfilter netlink socket.
+ * @arg handle		Netlink handle.
+ *
+ * Creates a NETLINK_NETFILTER netlink socket, binds the socket and
+ * issues a connection attempt.
+ *
+ * @see nl_connect()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nfnl_connect(struct nl_handle *handle)
+{
+	return nl_connect(handle, NETLINK_NETFILTER);
+}
+
+/** @} */
+
+/**
+ * @name Sending
+ * @{
+ */
+
+/**
+ * Send trivial netfilter netlink message
+ * @arg handle		Netlink handle.
+ * @arg subsys_id	nfnetlink subsystem
+ * @arg type		nfnetlink message type
+ * @arg flags		message flags
+ * @arg family		nfnetlink address family
+ * @arg res_id		nfnetlink resource id
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+int nfnl_send_simple(struct nl_handle *handle, uint8_t subsys_id, uint8_t type,
+		     int flags, uint8_t family, uint16_t res_id)
+{
+	struct nfgenmsg hdr = {
+		.nfgen_family = family,
+		.version = NFNETLINK_V0,
+		.res_id = htons(res_id),
+	};
+
+	return nl_send_simple(handle, NFNLMSG_TYPE(subsys_id, type), flags,
+			      &hdr, sizeof(hdr));
+}
+
+/** @} */
+
+/**
+ * @name Message Parsing
+ * @{
+ */
+
+/**
+ * Get netfilter subsystem id from message
+ * @arg nlh	netlink messsage header
+ */
+uint8_t nfnlmsg_subsys(struct nlmsghdr *nlh)
+{
+	return NFNL_SUBSYS_ID(nlh->nlmsg_type);
+}
+
+/**
+ * Get netfilter message type from message
+ * @arg nlh	netlink messsage header
+ */
+uint8_t nfnlmsg_subtype(struct nlmsghdr *nlh)
+{
+	return NFNL_MSG_TYPE(nlh->nlmsg_type);
+}
+
+/**
+ * Get netfilter family from message
+ * @arg nlh	netlink messsage header
+ */
+uint8_t nfnlmsg_family(struct nlmsghdr *nlh)
+{
+	struct nfgenmsg *nfg = nlmsg_data(nlh);
+
+	return nfg->nfgen_family;
+}
+
+/**
+ * Get netfilter resource id from message
+ * @arg nlh	netlink messsage header
+ */
+uint16_t nfnlmsg_res_id(struct nlmsghdr *nlh)
+{
+	struct nfgenmsg *nfg = nlmsg_data(nlh);
+
+	return ntohs(nfg->res_id);
+}
+
+/** @} */
+
+/**
+ * @name Message Building
+ * @{
+ */
+
+static int nfnlmsg_append(struct nl_msg *msg, uint8_t family, uint16_t res_id)
+{
+	struct nfgenmsg *nfg;
+
+	nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO);
+	if (nfg == NULL)
+		return nl_errno(ENOMEM);
+
+	nfg->nfgen_family = family;
+	nfg->version = NFNETLINK_V0;
+	nfg->res_id = htons(res_id);
+	NL_DBG(2, "msg %p: Added nfnetlink header family=%d res_id=%d\n",
+	       msg, family, res_id);
+	return 0;
+}
+
+/**
+ * Allocate a new netfilter netlink message
+ * @arg subsys_id	nfnetlink subsystem
+ * @arg type		nfnetlink message type
+ * @arg flags		message flags
+ * @arg family		nfnetlink address family
+ * @arg res_id		nfnetlink resource id
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+struct nl_msg *nfnlmsg_alloc_simple(uint8_t subsys_id, uint8_t type, int flags,
+				    uint8_t family, uint16_t res_id)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc_simple(NFNLMSG_TYPE(subsys_id, type), flags);
+	if (msg == NULL)
+		return NULL;
+
+	if (nfnlmsg_append(msg, family, res_id) < 0)
+		goto nla_put_failure;
+
+	return msg;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+/**
+ * Add netlink and netfilter netlink headers to netlink message
+ * @arg msg		netlink message
+ * @arg pid		netlink process id
+ * @arg seq		sequence number of message
+ * @arg subsys_id	nfnetlink subsystem
+ * @arg type		nfnetlink message type
+ * @arg flags		message flags
+ * @arg family		nfnetlink address family
+ * @arg res_id		nfnetlink resource id
+ */
+int nfnlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq,
+		uint8_t subsys_id, uint8_t type, int flags, uint8_t family,
+		uint16_t res_id)
+{
+	struct nlmsghdr *nlh;
+
+	nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags);
+	if (nlh == NULL)
+		return nl_get_errno();
+
+	return nfnlmsg_append(msg, family, res_id);
+}
+
+/** @} */
+
+/** @} */
Index: libnl/include/netlink/netlink.h
===================================================================
--- libnl.orig/include/netlink/netlink.h	2007-09-03 14:24:29.000000000 +1000
+++ libnl/include/netlink/netlink.h	2007-09-03 14:24:45.000000000 +1000
@@ -25,6 +25,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/genetlink.h>
 #include <linux/ip_mp_alg.h>
+#include <linux/netfilter/nfnetlink.h>
 #include <netlink/types.h>
 #include <netlink/handlers.h>
 #include <netlink/socket.h>
Index: libnl/include/linux/netfilter/nfnetlink.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/linux/netfilter/nfnetlink.h	2007-09-03 14:24:45.000000000 +1000
@@ -0,0 +1,106 @@
+#ifndef _NFNETLINK_H
+#define _NFNETLINK_H
+#include <linux/types.h>
+
+#ifndef __KERNEL__
+/* nfnetlink groups: Up to 32 maximum - backwards compatibility for userspace */
+#define NF_NETLINK_CONNTRACK_NEW 		0x00000001
+#define NF_NETLINK_CONNTRACK_UPDATE		0x00000002
+#define NF_NETLINK_CONNTRACK_DESTROY		0x00000004
+#define NF_NETLINK_CONNTRACK_EXP_NEW		0x00000008
+#define NF_NETLINK_CONNTRACK_EXP_UPDATE		0x00000010
+#define NF_NETLINK_CONNTRACK_EXP_DESTROY	0x00000020
+#endif
+
+enum nfnetlink_groups {
+	NFNLGRP_NONE,
+#define NFNLGRP_NONE			NFNLGRP_NONE
+	NFNLGRP_CONNTRACK_NEW,
+#define NFNLGRP_CONNTRACK_NEW		NFNLGRP_CONNTRACK_NEW
+	NFNLGRP_CONNTRACK_UPDATE,
+#define NFNLGRP_CONNTRACK_UPDATE	NFNLGRP_CONNTRACK_UPDATE
+	NFNLGRP_CONNTRACK_DESTROY,
+#define NFNLGRP_CONNTRACK_DESTROY	NFNLGRP_CONNTRACK_DESTROY
+	NFNLGRP_CONNTRACK_EXP_NEW,
+#define	NFNLGRP_CONNTRACK_EXP_NEW	NFNLGRP_CONNTRACK_EXP_NEW
+	NFNLGRP_CONNTRACK_EXP_UPDATE,
+#define NFNLGRP_CONNTRACK_EXP_UPDATE	NFNLGRP_CONNTRACK_EXP_UPDATE
+	NFNLGRP_CONNTRACK_EXP_DESTROY,
+#define NFNLGRP_CONNTRACK_EXP_DESTROY	NFNLGRP_CONNTRACK_EXP_DESTROY
+	__NFNLGRP_MAX,
+};
+#define NFNLGRP_MAX	(__NFNLGRP_MAX - 1)
+
+/* Generic structure for encapsulation optional netfilter information.
+ * It is reminiscent of sockaddr, but with sa_family replaced
+ * with attribute type. 
+ * ! This should someday be put somewhere generic as now rtnetlink and
+ * ! nfnetlink use the same attributes methods. - J. Schulist.
+ */
+
+struct nfattr
+{
+	u_int16_t nfa_len;
+	u_int16_t nfa_type;	/* we use 15 bits for the type, and the highest
+				 * bit to indicate whether the payload is nested */
+};
+
+/* FIXME: Apart from NFNL_NFA_NESTED shamelessly copy and pasted from
+ * rtnetlink.h, it's time to put this in a generic file */
+
+#define NFNL_NFA_NEST	0x8000
+#define NFA_TYPE(attr) 	((attr)->nfa_type & 0x7fff)
+
+#define NFA_ALIGNTO     4
+#define NFA_ALIGN(len)	(((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1))
+#define NFA_OK(nfa,len)	((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \
+	&& (nfa)->nfa_len <= (len))
+#define NFA_NEXT(nfa,attrlen)	((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \
+	(struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len)))
+#define NFA_LENGTH(len)	(NFA_ALIGN(sizeof(struct nfattr)) + (len))
+#define NFA_SPACE(len)	NFA_ALIGN(NFA_LENGTH(len))
+#define NFA_DATA(nfa)   ((void *)(((char *)(nfa)) + NFA_LENGTH(0)))
+#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
+#define NFA_NEST(skb, type) \
+({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
+	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
+	__start;  })
+#define NFA_NEST_END(skb, start) \
+({      (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \
+        (skb)->len; })
+#define NFA_NEST_CANCEL(skb, start) \
+({      if (start) \
+                skb_trim(skb, (unsigned char *) (start) - (skb)->data); \
+        -1; })
+
+/* General form of address family dependent message.
+ */
+struct nfgenmsg {
+	u_int8_t  nfgen_family;		/* AF_xxx */
+	u_int8_t  version;		/* nfnetlink version */
+	__be16    res_id;		/* resource id */
+};
+
+#define NFNETLINK_V0	0
+
+#define NFM_NFA(n)      ((struct nfattr *)(((char *)(n)) \
+        + NLMSG_ALIGN(sizeof(struct nfgenmsg))))
+#define NFM_PAYLOAD(n)  NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg))
+
+/* netfilter netlink message types are split in two pieces:
+ * 8 bit subsystem, 8bit operation.
+ */
+
+#define NFNL_SUBSYS_ID(x)	((x & 0xff00) >> 8)
+#define NFNL_MSG_TYPE(x)	(x & 0x00ff)
+
+/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS()
+ * won't work anymore */
+#define NFNL_SUBSYS_NONE 		0
+#define NFNL_SUBSYS_CTNETLINK		1
+#define NFNL_SUBSYS_CTNETLINK_EXP	2
+#define NFNL_SUBSYS_QUEUE		3
+#define NFNL_SUBSYS_ULOG		4
+#define NFNL_SUBSYS_COUNT		5
+
+#endif	/* _NFNETLINK_H */

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 2/3] libnl: add netfilter conntrack support
  2007-09-03  5:09 netfilter support in libnl Philip Craig
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
@ 2007-09-03  5:11 ` Philip Craig
  2007-09-04 16:45   ` Thomas Graf
  2007-09-03  5:12 ` [PATCH 3/3] libnl: add netfilter log support Philip Craig
  2007-09-03  9:30 ` netfilter support in libnl Patrick McHardy
  3 siblings, 1 reply; 22+ messages in thread
From: Philip Craig @ 2007-09-03  5:11 UTC (permalink / raw)
  To: Netfilter Developer Mailing List

[-- Attachment #1: Type: text/plain, Size: 1 bytes --]



[-- Attachment #2: nfnl_ct.patch --]
[-- Type: text/x-diff, Size: 53540 bytes --]

---
 include/linux/netfilter/nfnetlink_conntrack.h |  140 +++++
 include/netlink-types.h                       |   44 +
 include/netlink/addr.h                        |    1 
 include/netlink/netfilter/ct.h                |  115 ++++
 include/netlink/utils.h                       |    4 
 lib/addr.c                                    |   28 +
 lib/netfilter/ct.c                            |  422 ++++++++++++++++
 lib/netfilter/ct_obj.c                        |  667 ++++++++++++++++++++++++++
 lib/utils.c                                   |   36 +
 src/Makefile                                  |    6 
 src/f_ct.c                                    |  153 +++++
 src/nf-ct-dump.c                              |   88 +++
 src/nf-monitor.c                              |  115 ++++
 13 files changed, 1818 insertions(+), 1 deletion(-)

Index: libnl/include/netlink-types.h
===================================================================
--- libnl.orig/include/netlink-types.h	2007-09-03 14:24:29.000000000 +1000
+++ libnl/include/netlink-types.h	2007-09-03 14:24:52.000000000 +1000
@@ -795,4 +795,48 @@ struct genl_family
 	struct nl_list_head	gf_ops;
 };
 
+union nfnl_ct_proto
+{
+	struct {
+		uint16_t	src;
+		uint16_t	dst;
+	} port;
+	struct {
+		uint16_t	id;
+		uint8_t		type;
+		uint8_t		code;
+	} icmp;
+};
+
+struct nfnl_ct_dir {
+	struct nl_addr *	src;
+	struct nl_addr *	dst;
+	union nfnl_ct_proto	proto;
+	uint64_t		packets;
+	uint64_t		bytes;
+};
+
+union nfnl_ct_protoinfo {
+	struct {
+		uint8_t		state;
+	} tcp;
+};
+
+struct nfnl_ct {
+	NLHDR_COMMON
+
+	uint8_t			ct_family;
+	uint8_t			ct_proto;
+	union nfnl_ct_protoinfo	ct_protoinfo;
+
+	uint32_t		ct_status;
+	uint32_t		ct_timeout;
+	uint32_t		ct_mark;
+	uint32_t		ct_use;
+	uint32_t		ct_id;
+
+	struct nfnl_ct_dir	ct_orig;
+	struct nfnl_ct_dir	ct_repl;
+};
+
 #endif
Index: libnl/include/netlink/netfilter/ct.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/netlink/netfilter/ct.h	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,115 @@
+/*
+ * netlink/netfilter/ct.h	Conntrack
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#ifndef NETLINK_CT_H_
+#define NETLINK_CT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/addr.h>
+#include <netlink/cache.h>
+#include <netlink/msg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nfnl_ct;
+
+extern struct nl_object_ops ct_obj_ops;
+
+/* General */
+extern struct nfnl_ct *	nfnl_ct_alloc(void);
+extern struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *);
+
+extern int		nfnlmsg_ct_group(struct nlmsghdr *);
+extern struct nfnl_ct *	nfnlmsg_ct_parse(struct nlmsghdr *);
+
+extern void		nfnl_ct_get(struct nfnl_ct *);
+extern void		nfnl_ct_put(struct nfnl_ct *);
+
+extern int		nfnl_ct_dump_request(struct nl_handle *);
+
+extern void		nfnl_ct_set_family(struct nfnl_ct *, uint8_t);
+extern uint8_t		nfnl_ct_get_family(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_proto(struct nfnl_ct *, uint8_t);
+extern int		nfnl_ct_test_proto(const struct nfnl_ct *);
+extern uint8_t		nfnl_ct_get_proto(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t);
+extern int		nfnl_ct_test_tcp_state(const struct nfnl_ct *);
+extern uint8_t		nfnl_ct_get_tcp_state(const struct nfnl_ct *);
+extern char *		nfnl_ct_tcp_state2str(uint8_t, char *, size_t);
+extern int		nfnl_ct_str2tcp_state(const char *name);
+
+extern void		nfnl_ct_set_status(struct nfnl_ct *, uint32_t);
+extern int		nfnl_ct_test_status(const struct nfnl_ct *);
+extern uint32_t		nfnl_ct_get_status(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t);
+extern int		nfnl_ct_test_timeout(const struct nfnl_ct *);
+extern uint32_t		nfnl_ct_get_timeout(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_mark(struct nfnl_ct *, uint32_t);
+extern int		nfnl_ct_test_mark(const struct nfnl_ct *);
+extern uint32_t		nfnl_ct_get_mark(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_use(struct nfnl_ct *, uint32_t);
+extern int		nfnl_ct_test_use(const struct nfnl_ct *);
+extern uint32_t		nfnl_ct_get_use(const struct nfnl_ct *);
+
+extern void		nfnl_ct_set_id(struct nfnl_ct *, uint32_t);
+extern int		nfnl_ct_test_id(const struct nfnl_ct *);
+extern uint32_t		nfnl_ct_get_id(const struct nfnl_ct *);
+
+extern int		nfnl_ct_set_src(struct nfnl_ct *, int,
+					struct nl_addr *);
+extern struct nl_addr *	nfnl_ct_get_src(const struct nfnl_ct *, int);
+
+extern int		nfnl_ct_set_dst(struct nfnl_ct *, int,
+					struct nl_addr *);
+extern struct nl_addr *	nfnl_ct_get_dst(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t);
+extern int		nfnl_ct_test_src_port(const struct nfnl_ct *, int);
+extern uint16_t		nfnl_ct_get_src_port(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t);
+extern int		nfnl_ct_test_dst_port(const struct nfnl_ct *, int);
+extern uint16_t		nfnl_ct_get_dst_port(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t);
+extern int		nfnl_ct_test_icmp_id(const struct nfnl_ct *, int);
+extern uint16_t		nfnl_ct_get_icmp_id(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t);
+extern int		nfnl_ct_test_icmp_type(const struct nfnl_ct *, int);
+extern uint8_t		nfnl_ct_get_icmp_type(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t);
+extern int		nfnl_ct_test_icmp_code(const struct nfnl_ct *, int);
+extern uint8_t		nfnl_ct_get_icmp_code(const struct nfnl_ct *, int);
+
+extern void		nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t);
+extern int		nfnl_ct_test_packets(const struct nfnl_ct *, int);
+extern uint64_t		nfnl_ct_get_packets(const struct nfnl_ct *,int);
+
+extern void		nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t);
+extern int		nfnl_ct_test_bytes(const struct nfnl_ct *, int);
+extern uint64_t		nfnl_ct_get_bytes(const struct nfnl_ct *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: libnl/lib/netfilter/ct.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/lib/netfilter/ct.c	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,422 @@
+/*
+ * lib/netfilter/ct.c	Conntrack
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup ct Conntrack
+ * @brief
+ * @{
+ */
+
+#include <byteswap.h>
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/ct.h>
+
+static struct nl_cache_ops nfnl_ct_ops;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return x;
+}
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return __bswap_64(x);
+}
+#endif
+
+static struct nla_policy ct_policy[CTA_MAX+1] = {
+	[CTA_TUPLE_ORIG]	= { .type = NLA_NESTED },
+	[CTA_TUPLE_REPLY]	= { .type = NLA_NESTED },
+	[CTA_STATUS]		= { .type = NLA_U32 },
+	[CTA_PROTOINFO]		= { .type = NLA_NESTED },
+	//[CTA_HELP]
+	//[CTA_NAT_SRC]
+	[CTA_TIMEOUT]		= { .type = NLA_U32 },
+	[CTA_MARK]		= { .type = NLA_U32 },
+	[CTA_COUNTERS_ORIG]	= { .type = NLA_NESTED },
+	[CTA_COUNTERS_REPLY]	= { .type = NLA_NESTED },
+	[CTA_USE]		= { .type = NLA_U32 },
+	[CTA_ID]		= { .type = NLA_U32 },
+	//[CTA_NAT_DST]
+};
+
+static struct nla_policy ct_tuple_policy[CTA_TUPLE_MAX+1] = {
+	[CTA_TUPLE_IP]		= { .type = NLA_NESTED },
+	[CTA_TUPLE_PROTO]	= { .type = NLA_NESTED },
+};
+
+static struct nla_policy ct_ip_policy[CTA_IP_MAX+1] = {
+	[CTA_IP_V4_SRC]		= { .type = NLA_U32 },
+	[CTA_IP_V4_DST]		= { .type = NLA_U32 },
+	[CTA_IP_V6_SRC]		= { .minlen = 16 },
+	[CTA_IP_V6_DST]		= { .minlen = 16 },
+};
+
+static struct nla_policy ct_proto_policy[CTA_PROTO_MAX+1] = {
+	[CTA_PROTO_NUM]		= { .type = NLA_U8 },
+	[CTA_PROTO_SRC_PORT]	= { .type = NLA_U16 },
+	[CTA_PROTO_DST_PORT]	= { .type = NLA_U16 },
+	[CTA_PROTO_ICMP_ID]	= { .type = NLA_U16 },
+	[CTA_PROTO_ICMP_TYPE]	= { .type = NLA_U8 },
+	[CTA_PROTO_ICMP_CODE]	= { .type = NLA_U8 },
+	[CTA_PROTO_ICMPV6_ID]	= { .type = NLA_U16 },
+	[CTA_PROTO_ICMPV6_TYPE]	= { .type = NLA_U8 },
+	[CTA_PROTO_ICMPV6_CODE]	= { .type = NLA_U8 },
+};
+
+static struct nla_policy ct_protoinfo_policy[CTA_PROTOINFO_MAX+1] = {
+	[CTA_PROTOINFO_TCP]	= { .type = NLA_NESTED },
+};
+
+static struct nla_policy ct_protoinfo_tcp_policy[CTA_PROTOINFO_TCP_MAX+1] = {
+	[CTA_PROTOINFO_TCP_STATE]		= { .type = NLA_U8 },
+	[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]	= { .type = NLA_U8 },
+	[CTA_PROTOINFO_TCP_WSCALE_REPLY]	= { .type = NLA_U8 },
+	[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]	= { .minlen = 2 },
+	[CTA_PROTOINFO_TCP_FLAGS_REPLY]		= { .minlen = 2 },
+
+};
+
+static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = {
+	[CTA_COUNTERS_PACKETS]	= { .type = NLA_U64 },
+	[CTA_COUNTERS_BYTES]	= { .type = NLA_U64 },
+	[CTA_COUNTERS32_PACKETS]= { .type = NLA_U32 },
+	[CTA_COUNTERS32_BYTES]	= { .type = NLA_U32 },
+};
+
+static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_IP_MAX+1];
+	struct nl_addr *addr;
+	int err;
+
+        nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy);
+
+	if (tb[CTA_IP_V4_SRC]) {
+		addr = nla_get_addr(tb[CTA_IP_V4_SRC], AF_INET);
+		if (addr == NULL)
+			goto errout_errno;
+		err = nfnl_ct_set_src(ct, repl, addr);
+		nl_addr_put(addr);
+		if (err < 0)
+			goto errout;
+	}
+	if (tb[CTA_IP_V4_DST]) {
+		addr = nla_get_addr(tb[CTA_IP_V4_DST], AF_INET);
+		if (addr == NULL)
+			goto errout_errno;
+		err = nfnl_ct_set_dst(ct, repl, addr);
+		nl_addr_put(addr);
+		if (err < 0)
+			goto errout;
+	}
+	if (tb[CTA_IP_V6_SRC]) {
+		addr = nla_get_addr(tb[CTA_IP_V6_SRC], AF_INET6);
+		if (addr == NULL)
+			goto errout_errno;
+		err = nfnl_ct_set_src(ct, repl, addr);
+		nl_addr_put(addr);
+		if (err < 0)
+			goto errout;
+	}
+	if (tb[CTA_IP_V6_DST]) {
+		addr = nla_get_addr(tb[CTA_IP_V6_DST], AF_INET6);
+		if (addr == NULL)
+			goto errout_errno;
+		err = nfnl_ct_set_dst(ct, repl, addr);
+		nl_addr_put(addr);
+		if (err < 0)
+			goto errout;
+	}
+
+	return 0;
+
+errout_errno:
+	return nl_get_errno();
+errout:
+	return err;
+}
+
+static void ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_PROTO_MAX+1];
+
+	nla_parse_nested(tb, CTA_PROTO_MAX, attr, ct_proto_policy);
+
+	if (!repl && tb[CTA_PROTO_NUM])
+		nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM]));
+	if (tb[CTA_PROTO_SRC_PORT])
+		nfnl_ct_set_src_port(ct, repl,
+				nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
+	if (tb[CTA_PROTO_DST_PORT])
+		nfnl_ct_set_dst_port(ct, repl,
+				nla_get_u16(tb[CTA_PROTO_DST_PORT]));
+	if (tb[CTA_PROTO_ICMP_ID])
+		nfnl_ct_set_icmp_id(ct, repl,
+				nla_get_u16(tb[CTA_PROTO_ICMP_ID]));
+	if (tb[CTA_PROTO_ICMP_TYPE])
+		nfnl_ct_set_icmp_type(ct, repl,
+				nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]));
+	if (tb[CTA_PROTO_ICMP_CODE])
+		nfnl_ct_set_icmp_code(ct, repl,
+				nla_get_u8(tb[CTA_PROTO_ICMP_CODE]));
+}
+
+static int ct_parse_tuple(struct nfnl_ct *ct, int repl, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_TUPLE_MAX+1];
+	int err;
+
+	nla_parse_nested(tb, CTA_TUPLE_MAX, attr, ct_tuple_policy);
+
+	if (tb[CTA_TUPLE_IP]) {
+		err = ct_parse_ip(ct, repl, tb[CTA_TUPLE_IP]);
+		if (err < 0)
+			return err;
+	}
+	if (tb[CTA_TUPLE_PROTO])
+		ct_parse_proto(ct, repl, tb[CTA_TUPLE_PROTO]);
+	return 0;
+}
+
+static void ct_parse_protoinfo_tcp(struct nfnl_ct *ct, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1];
+
+	nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr,
+			 ct_protoinfo_tcp_policy);
+
+	if (tb[CTA_PROTOINFO_TCP_STATE])
+		nfnl_ct_set_tcp_state(ct,
+				nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]));
+}
+
+static void ct_parse_protoinfo(struct nfnl_ct *ct, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_PROTOINFO_MAX+1];
+
+	nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, ct_protoinfo_policy);
+
+	if (tb[CTA_PROTOINFO_TCP])
+		ct_parse_protoinfo_tcp(ct, tb[CTA_PROTOINFO_TCP]);
+}
+
+static void ct_parse_counters(struct nfnl_ct *ct, int repl, struct nlattr *attr)
+{
+	struct nlattr *tb[CTA_COUNTERS_MAX+1];
+
+	nla_parse_nested(tb, CTA_COUNTERS_MAX, attr, ct_counters_policy);
+
+	if (tb[CTA_COUNTERS_PACKETS])
+		nfnl_ct_set_packets(ct, repl,
+			ntohll(nla_get_u64(tb[CTA_COUNTERS_PACKETS])));
+	if (tb[CTA_COUNTERS32_PACKETS])
+		nfnl_ct_set_packets(ct, repl,
+			ntohl(nla_get_u32(tb[CTA_COUNTERS32_PACKETS])));
+	if (tb[CTA_COUNTERS_BYTES])
+		nfnl_ct_set_bytes(ct, repl,
+			ntohll(nla_get_u64(tb[CTA_COUNTERS_BYTES])));
+	if (tb[CTA_COUNTERS32_BYTES])
+		nfnl_ct_set_bytes(ct, repl,
+			ntohl(nla_get_u32(tb[CTA_COUNTERS32_BYTES])));
+}
+
+int nfnlmsg_ct_group(struct nlmsghdr *nlh)
+{
+	switch (nfnlmsg_subtype(nlh)) {
+	case IPCTNL_MSG_CT_NEW:
+		if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
+			return NFNLGRP_CONNTRACK_NEW;
+		else
+			return NFNLGRP_CONNTRACK_UPDATE;
+	case IPCTNL_MSG_CT_DELETE:
+		return NFNLGRP_CONNTRACK_DESTROY;
+	default:
+		return NFNLGRP_NONE;
+	}
+}
+
+struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh)
+{
+	struct nfnl_ct *ct;
+	struct nlattr *tb[CTA_MAX+1];
+	int err;
+
+	ct = nfnl_ct_alloc();
+	if (!ct)
+		return NULL;
+
+	ct->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_MAX,
+			  ct_policy);
+	if (err < 0)
+		goto errout;
+
+	nfnl_ct_set_family(ct, nfnlmsg_family(nlh));
+
+	if (tb[CTA_TUPLE_ORIG]) {
+		err = ct_parse_tuple(ct, 0, tb[CTA_TUPLE_ORIG]);
+		if (err < 0)
+			goto errout;
+	}
+	if (tb[CTA_TUPLE_REPLY]) {
+		err = ct_parse_tuple(ct, 1, tb[CTA_TUPLE_REPLY]);
+		if (err < 0)
+			goto errout;
+	}
+
+	if (tb[CTA_PROTOINFO])
+		ct_parse_protoinfo(ct, tb[CTA_PROTOINFO]);
+
+	if (tb[CTA_STATUS])
+		nfnl_ct_set_status(ct, ntohl(nla_get_u32(tb[CTA_STATUS])));
+	if (tb[CTA_TIMEOUT])
+		nfnl_ct_set_timeout(ct, ntohl(nla_get_u32(tb[CTA_TIMEOUT])));
+	if (tb[CTA_MARK])
+		nfnl_ct_set_mark(ct, ntohl(nla_get_u32(tb[CTA_MARK])));
+	if (tb[CTA_USE])
+		nfnl_ct_set_use(ct, ntohl(nla_get_u32(tb[CTA_USE])));
+	if (tb[CTA_ID])
+		nfnl_ct_set_id(ct, ntohl(nla_get_u32(tb[CTA_ID])));
+
+	if (tb[CTA_COUNTERS_ORIG])
+		ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]);
+	if (tb[CTA_COUNTERS_REPLY])
+		ct_parse_counters(ct, 1, tb[CTA_COUNTERS_REPLY]);
+
+	return ct;
+
+errout:
+	nfnl_ct_put(ct);
+	return NULL;
+}
+
+static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			 struct nlmsghdr *nlh, void *arg)
+{
+	struct nl_parser_param *pp = arg;
+	struct nfnl_ct *ct;
+	int err;
+
+	ct = nfnlmsg_ct_parse(nlh);
+	if (ct == NULL)
+		goto errout_errno;
+
+	err = pp->pp_cb((struct nl_object *) ct, pp);
+	if (err < 0)
+		goto errout;
+
+	return P_ACCEPT;
+
+errout_errno:
+	err = nl_get_errno();
+errout:
+	nfnl_ct_put(ct);
+	return err;
+}
+
+int nfnl_ct_dump_request(struct nl_handle *h)
+{
+	return nfnl_send_simple(h, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET,
+				NLM_F_DUMP, AF_UNSPEC, 0);
+}
+
+static int ct_request_update(struct nl_cache *c, struct nl_handle *h)
+{
+	return nfnl_ct_dump_request(h);
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+/**
+ * Build a conntrack cache holding all conntrack currently in the kernel
+ * @arg handle		netlink handle
+ *
+ * Allocates a new cache, initializes it properly and updates it to
+ * contain all conntracks currently in the kernel.
+ *
+ * @note The caller is responsible for destroying and freeing the
+ *       cache after using it.
+ * @return The cache or NULL if an error has occured.
+ */
+struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *handle)
+{
+	struct nl_cache *cache;
+
+	cache = nl_cache_alloc(&nfnl_ct_ops);
+	if (!cache)
+		return NULL;
+
+	if (handle && nl_cache_refill(handle, cache) < 0) {
+		free(cache);
+		return NULL;
+	}
+
+	return cache;
+}
+
+/** @} */
+
+/**
+ * @name Conntrack Addition
+ * @{
+ */
+
+/** @} */
+
+static struct nl_af_group ct_groups[] = {
+	{ 0, NFNLGRP_CONNTRACK_NEW },
+	{ 0, NFNLGRP_CONNTRACK_UPDATE },
+	{ 0, NFNLGRP_CONNTRACK_DESTROY },
+	{ END_OF_GROUP_LIST },
+};
+
+#define NFNLMSG_CT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, (type))
+static struct nl_cache_ops nfnl_ct_ops = {
+	.co_name		= "netfilter/ct",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_NEW), NL_ACT_NEW, "new" },
+		{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_GET), NL_ACT_GET, "get" },
+		{ NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_DELETE), NL_ACT_DEL, "del" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_groups		= ct_groups,
+	.co_request_update	= ct_request_update,
+	.co_msg_parser		= ct_msg_parser,
+	.co_obj_ops		= &ct_obj_ops,
+};
+
+static void __init ct_init(void)
+{
+	nl_cache_mngt_register(&nfnl_ct_ops);
+}
+
+static void __exit ct_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_ct_ops);
+}
+
+/** @} */
Index: libnl/lib/netfilter/ct_obj.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/lib/netfilter/ct_obj.c	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,667 @@
+/*
+ * lib/netfilter/ct_obj.c	Conntrack Object
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+
+#include <netlink-local.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/ct.h>
+
+/** @cond SKIP */
+#define CT_ATTR_FAMILY		(1UL << 0)
+#define CT_ATTR_PROTO		(1UL << 1)
+
+#define CT_ATTR_TCP_STATE	(1UL << 2)
+
+#define CT_ATTR_STATUS		(1UL << 3)
+#define CT_ATTR_TIMEOUT		(1UL << 4)
+#define CT_ATTR_MARK		(1UL << 5)
+#define CT_ATTR_USE		(1UL << 6)
+#define CT_ATTR_ID		(1UL << 7)
+
+#define CT_ATTR_ORIG_SRC	(1UL << 8)
+#define CT_ATTR_ORIG_DST	(1UL << 9)
+#define CT_ATTR_ORIG_SRC_PORT	(1UL << 10)
+#define CT_ATTR_ORIG_DST_PORT	(1UL << 11)
+#define CT_ATTR_ORIG_ICMP_ID	(1UL << 12)
+#define CT_ATTR_ORIG_ICMP_TYPE	(1UL << 13)
+#define CT_ATTR_ORIG_ICMP_CODE	(1UL << 14)
+#define CT_ATTR_ORIG_PACKETS	(1UL << 15)
+#define CT_ATTR_ORIG_BYTES	(1UL << 16)
+
+#define CT_ATTR_REPL_SRC	(1UL << 17)
+#define CT_ATTR_REPL_DST	(1UL << 18)
+#define CT_ATTR_REPL_SRC_PORT	(1UL << 19)
+#define CT_ATTR_REPL_DST_PORT	(1UL << 20)
+#define CT_ATTR_REPL_ICMP_ID	(1UL << 21)
+#define CT_ATTR_REPL_ICMP_TYPE	(1UL << 22)
+#define CT_ATTR_REPL_ICMP_CODE	(1UL << 23)
+#define CT_ATTR_REPL_PACKETS	(1UL << 24)
+#define CT_ATTR_REPL_BYTES	(1UL << 25)
+/** @endcond */
+
+static void ct_free_data(struct nl_object *c)
+{
+	struct nfnl_ct *ct = (struct nfnl_ct *) c;
+
+	if (ct == NULL)
+		return;
+
+	nl_addr_put(ct->ct_orig.src);
+	nl_addr_put(ct->ct_orig.dst);
+	nl_addr_put(ct->ct_repl.src);
+	nl_addr_put(ct->ct_repl.dst);
+}
+
+static int ct_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_ct *dst = (struct nfnl_ct *) _dst;
+	struct nfnl_ct *src = (struct nfnl_ct *) _src;
+	struct nl_addr *addr;
+
+	if (src->ct_orig.src) {
+		addr = nl_addr_clone(src->ct_orig.src);
+		if (!addr)
+			goto errout;
+		dst->ct_orig.src = addr;
+	}
+
+	if (src->ct_orig.dst) {
+		addr = nl_addr_clone(src->ct_orig.dst);
+		if (!addr)
+			goto errout;
+		dst->ct_orig.dst = addr;
+	}
+
+	if (src->ct_repl.src) {
+		addr = nl_addr_clone(src->ct_repl.src);
+		if (!addr)
+			goto errout;
+		dst->ct_repl.src = addr;
+	}
+
+	if (src->ct_repl.dst) {
+		addr = nl_addr_clone(src->ct_repl.dst);
+		if (!addr)
+			goto errout;
+		dst->ct_repl.dst = addr;
+	}
+
+	return 0;
+errout:
+	return nl_get_errno();
+}
+
+static void ct_dump_dir(struct nfnl_ct *ct, int repl,
+			struct nl_dump_params *p)
+{
+	struct nl_addr *addr;
+	char addrbuf[64];
+
+	addr = nfnl_ct_get_src(ct, repl);
+	if (addr)
+		dp_dump(p, "src=%s ",
+			nl_addr2str(addr, addrbuf, sizeof(addrbuf)));
+
+	addr = nfnl_ct_get_dst(ct, repl);
+	if (addr)
+		dp_dump(p, "dst=%s ",
+			nl_addr2str(addr, addrbuf, sizeof(addrbuf)));
+
+	if (nfnl_ct_test_src_port(ct, repl))
+		dp_dump(p, "sport=%u ", ntohs(nfnl_ct_get_src_port(ct, repl)));
+	if (nfnl_ct_test_dst_port(ct, repl))
+		dp_dump(p, "dport=%u ", ntohs(nfnl_ct_get_dst_port(ct, repl)));
+
+	if (nfnl_ct_test_icmp_type(ct, repl))
+		dp_dump(p, "type=%d ", nfnl_ct_get_icmp_type(ct, repl));
+	if (nfnl_ct_test_icmp_type(ct, repl))
+		dp_dump(p, "code=%d ", nfnl_ct_get_icmp_code(ct, repl));
+	if (nfnl_ct_test_icmp_type(ct, repl))
+		dp_dump(p, "id=%d ", ntohs(nfnl_ct_get_icmp_id(ct, repl)));
+
+	if (nfnl_ct_test_packets(ct, repl))
+		dp_dump(p, "packets=%llu ", nfnl_ct_get_packets(ct, repl));
+	if (nfnl_ct_test_bytes(ct, repl))
+		dp_dump(p, "bytes=%llu ", nfnl_ct_get_bytes(ct, repl));
+}
+
+/* Compatible with /proc/net/nf_conntrack */
+static int ct_dump(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_ct *ct = (struct nfnl_ct *) a;
+	char buf[64];
+	uint32_t status;
+	uint8_t family;
+	uint8_t proto;
+
+	family = nfnl_ct_get_family(ct);
+	dp_dump(p, "%-8s %u ", nl_af2str(family, buf, sizeof(buf)), family);
+
+	if (nfnl_ct_test_proto(ct)) {
+		proto = nfnl_ct_get_proto(ct);
+		dp_dump(p, "%-8s %u ",
+			nl_ip_proto2str(proto, buf, sizeof(buf)), proto);
+	}
+
+	if (nfnl_ct_test_timeout(ct))
+		dp_dump(p, "%ld ", nfnl_ct_get_timeout(ct));
+
+	if (nfnl_ct_test_tcp_state(ct))
+		dp_dump(p, "%s ",
+			nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct),
+					      buf, sizeof(buf)));
+
+	ct_dump_dir(ct, 0, p);
+
+	status = nfnl_ct_get_status(ct);
+	if (!(status & IPS_SEEN_REPLY))
+		dp_dump(p, "[UNREPLIED] ");
+
+	ct_dump_dir(ct, 1, p);
+
+	if (status & IPS_ASSURED)
+		dp_dump(p, "[ASSURED] ");
+
+	if (nfnl_ct_test_mark(ct))
+		dp_dump(p, "mark=%u ", nfnl_ct_get_mark(ct));
+
+	if (nfnl_ct_test_use(ct))
+		dp_dump(p, "use=%u ", nfnl_ct_get_use(ct));
+
+	dp_dump(p, "\n");
+
+	return 1;
+}
+
+static int ct_compare(struct nl_object *_a, struct nl_object *_b,
+			uint32_t attrs, int flags)
+{
+	struct nfnl_ct *a = (struct nfnl_ct *) _a;
+	struct nfnl_ct *b = (struct nfnl_ct *) _b;
+	int diff = 0;
+
+#define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR)
+#define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD)
+#define CT_DIFF_ADDR(ATTR, FIELD) \
+	((flags & LOOSE_FLAG_COMPARISON) \
+		? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \
+		: CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD)))
+
+	diff |= CT_DIFF_VAL(FAMILY,		ct_family);
+	diff |= CT_DIFF_VAL(PROTO,		ct_proto);
+	diff |= CT_DIFF_VAL(TCP_STATE,		ct_protoinfo.tcp.state);
+	diff |= CT_DIFF_VAL(STATUS,		ct_status);
+	diff |= CT_DIFF_VAL(TIMEOUT,		ct_timeout);
+	diff |= CT_DIFF_VAL(MARK,		ct_mark);
+	diff |= CT_DIFF_VAL(USE,		ct_use);
+	diff |= CT_DIFF_VAL(ID,			ct_id);
+	diff |= CT_DIFF_ADDR(ORIG_SRC,		ct_orig.src);
+	diff |= CT_DIFF_ADDR(ORIG_DST,		ct_orig.dst);
+	diff |= CT_DIFF_VAL(ORIG_SRC_PORT,	ct_orig.proto.port.src);
+	diff |= CT_DIFF_VAL(ORIG_DST_PORT,	ct_orig.proto.port.dst);
+	diff |= CT_DIFF_VAL(ORIG_ICMP_ID,	ct_orig.proto.icmp.id);
+	diff |= CT_DIFF_VAL(ORIG_ICMP_TYPE,	ct_orig.proto.icmp.type);
+	diff |= CT_DIFF_VAL(ORIG_ICMP_CODE,	ct_orig.proto.icmp.code);
+	diff |= CT_DIFF_VAL(ORIG_PACKETS,	ct_orig.packets);
+	diff |= CT_DIFF_VAL(ORIG_BYTES,		ct_orig.bytes);
+	diff |= CT_DIFF_ADDR(REPL_SRC,		ct_repl.src);
+	diff |= CT_DIFF_ADDR(ORIG_DST,		ct_repl.dst);
+	diff |= CT_DIFF_VAL(REPL_SRC_PORT,	ct_repl.proto.port.src);
+	diff |= CT_DIFF_VAL(REPL_DST_PORT,	ct_repl.proto.port.dst);
+	diff |= CT_DIFF_VAL(REPL_ICMP_ID,	ct_repl.proto.icmp.id);
+	diff |= CT_DIFF_VAL(REPL_ICMP_TYPE,	ct_repl.proto.icmp.type);
+	diff |= CT_DIFF_VAL(REPL_ICMP_CODE,	ct_repl.proto.icmp.code);
+	diff |= CT_DIFF_VAL(REPL_PACKETS,	ct_repl.packets);
+	diff |= CT_DIFF_VAL(REPL_BYTES,		ct_repl.bytes);
+
+#undef CT_DIFF
+#undef CT_DIFF_VAL
+#undef CT_DIFF_ADDR
+
+	return diff;
+}
+
+static struct trans_tbl ct_attrs[] = {
+	__ADD(CT_ATTR_FAMILY,		family)
+	__ADD(CT_ATTR_PROTO,		proto)
+	__ADD(CT_ATTR_TCP_STATE,	tcpstate)
+	__ADD(CT_ATTR_STATUS,		status)
+	__ADD(CT_ATTR_TIMEOUT,		timeout)
+	__ADD(CT_ATTR_MARK,		mark)
+	__ADD(CT_ATTR_USE,		use)
+	__ADD(CT_ATTR_ID,		id)
+	__ADD(CT_ATTR_ORIG_SRC,		origsrc)
+	__ADD(CT_ATTR_ORIG_DST,		origdst)
+	__ADD(CT_ATTR_ORIG_SRC_PORT,	origsrcport)
+	__ADD(CT_ATTR_ORIG_DST_PORT,	origdstport)
+	__ADD(CT_ATTR_ORIG_ICMP_ID,	origicmpid)
+	__ADD(CT_ATTR_ORIG_ICMP_TYPE,	origicmptype)
+	__ADD(CT_ATTR_ORIG_ICMP_CODE,	origicmpcode)
+	__ADD(CT_ATTR_ORIG_PACKETS,	origpackets)
+	__ADD(CT_ATTR_ORIG_BYTES,	origbytes)
+	__ADD(CT_ATTR_REPL_SRC,		replysrc)
+	__ADD(CT_ATTR_REPL_DST,		replydst)
+	__ADD(CT_ATTR_REPL_SRC_PORT,	replysrcport)
+	__ADD(CT_ATTR_REPL_DST_PORT,	replydstport)
+	__ADD(CT_ATTR_REPL_ICMP_ID,	replyicmpid)
+	__ADD(CT_ATTR_REPL_ICMP_TYPE,	replyicmptype)
+	__ADD(CT_ATTR_REPL_ICMP_CODE,	replyicmpcode)
+	__ADD(CT_ATTR_REPL_PACKETS,	replypackets)
+	__ADD(CT_ATTR_REPL_BYTES,	replybytes)
+};
+
+static char *ct_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, ct_attrs, ARRAY_SIZE(ct_attrs));
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_ct *nfnl_ct_alloc(void)
+{
+	return (struct nfnl_ct *) nl_object_alloc(&ct_obj_ops);
+}
+
+void nfnl_ct_get(struct nfnl_ct *ct)
+{
+	nl_object_get((struct nl_object *) ct);
+}
+
+void nfnl_ct_put(struct nfnl_ct *ct)
+{
+	nl_object_put((struct nl_object *) ct);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_ct_set_family(struct nfnl_ct *ct, uint8_t family)
+{
+	ct->ct_family = family;
+	ct->ce_mask |= CT_ATTR_FAMILY;
+}
+
+uint8_t nfnl_ct_get_family(const struct nfnl_ct *ct)
+{
+	if (ct->ce_mask & CT_ATTR_FAMILY)
+		return ct->ct_family;
+	else
+		return AF_UNSPEC;
+}
+
+void nfnl_ct_set_proto(struct nfnl_ct *ct, uint8_t proto)
+{
+	ct->ct_proto = proto;
+	ct->ce_mask |= CT_ATTR_PROTO;
+}
+
+int nfnl_ct_test_proto(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_PROTO);
+}
+
+uint8_t nfnl_ct_get_proto(const struct nfnl_ct *ct)
+{
+	return ct->ct_proto;
+}
+
+void nfnl_ct_set_tcp_state(struct nfnl_ct *ct, uint8_t state)
+{
+	ct->ct_protoinfo.tcp.state = state;
+	ct->ce_mask |= CT_ATTR_TCP_STATE;
+}
+
+int nfnl_ct_test_tcp_state(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_TCP_STATE);
+}
+
+uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct)
+{
+	return ct->ct_protoinfo.tcp.state;
+}
+
+static struct trans_tbl tcp_states[] = {
+	__ADD(TCP_CONNTRACK_NONE,NONE)
+	__ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT)
+	__ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV)
+	__ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED)
+	__ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT)
+	__ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT)
+	__ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK)
+	__ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT)
+	__ADD(TCP_CONNTRACK_CLOSE,CLOSE)
+	__ADD(TCP_CONNTRACK_LISTEN,LISTEN)
+};
+
+char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len)
+{
+	return __type2str(state, buf, len, tcp_states, ARRAY_SIZE(tcp_states));
+}
+
+int nfnl_ct_str2tcp_state(const char *name)
+{
+        return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states));
+}
+
+void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status)
+{
+	ct->ct_status = status;
+	ct->ce_mask |= CT_ATTR_STATUS;
+}
+
+int nfnl_ct_test_status(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_STATUS);
+}
+
+uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct)
+{
+	return ct->ct_status;
+}
+
+void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout)
+{
+	ct->ct_timeout = timeout;
+	ct->ce_mask |= CT_ATTR_TIMEOUT;
+}
+
+int nfnl_ct_test_timeout(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_TIMEOUT);
+}
+
+uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *ct)
+{
+	return ct->ct_timeout;
+}
+
+void nfnl_ct_set_mark(struct nfnl_ct *ct, uint32_t mark)
+{
+	ct->ct_mark = mark;
+	ct->ce_mask |= CT_ATTR_MARK;
+}
+
+int nfnl_ct_test_mark(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_MARK);
+}
+
+uint32_t nfnl_ct_get_mark(const struct nfnl_ct *ct)
+{
+	return ct->ct_mark;
+}
+
+void nfnl_ct_set_use(struct nfnl_ct *ct, uint32_t use)
+{
+	ct->ct_use = use;
+	ct->ce_mask |= CT_ATTR_USE;
+}
+
+int nfnl_ct_test_use(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_USE);
+}
+
+uint32_t nfnl_ct_get_use(const struct nfnl_ct *ct)
+{
+	return ct->ct_use;
+}
+
+void nfnl_ct_set_id(struct nfnl_ct *ct, uint32_t id)
+{
+	ct->ct_id = id;
+	ct->ce_mask |= CT_ATTR_ID;
+}
+
+int nfnl_ct_test_id(const struct nfnl_ct *ct)
+{
+	return !!(ct->ce_mask & CT_ATTR_ID);
+}
+
+uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct)
+{
+	return ct->ct_id;
+}
+
+static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr,
+		int attr, struct nl_addr ** ct_addr)
+{
+	if (ct->ce_mask & CT_ATTR_FAMILY) {
+		if (addr->a_family != ct->ct_family)
+			return nl_error(EINVAL, "Address family mismatch");
+	} else
+		nfnl_ct_set_family(ct, addr->a_family);
+
+	if (*ct_addr)
+		nl_addr_put(*ct_addr);
+
+	nl_addr_get(addr);
+	*ct_addr = addr;
+	ct->ce_mask |= attr;
+
+	return 0;
+}
+
+int nfnl_ct_set_src(struct nfnl_ct *ct, int repl, struct nl_addr *addr)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC;
+	return ct_set_addr(ct, addr, attr, &dir->src);
+}
+
+int nfnl_ct_set_dst(struct nfnl_ct *ct, int repl, struct nl_addr *addr)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST;
+	return ct_set_addr(ct, addr, attr, &dir->dst);
+}
+
+struct nl_addr *nfnl_ct_get_src(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC;
+	if (!(ct->ce_mask & attr))
+		return NULL;
+	return dir->src;
+}
+
+struct nl_addr *nfnl_ct_get_dst(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST;
+	if (!(ct->ce_mask & attr))
+		return NULL;
+	return dir->dst;
+}
+
+void nfnl_ct_set_src_port(struct nfnl_ct *ct, int repl, uint16_t port)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT;
+
+	dir->proto.port.src = port;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_src_port(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT;
+	return !!(ct->ce_mask & attr);
+}
+
+uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->proto.port.src;
+}
+
+void nfnl_ct_set_dst_port(struct nfnl_ct *ct, int repl, uint16_t port)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT;
+
+	dir->proto.port.dst = port;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_dst_port(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT;
+	return !!(ct->ce_mask & attr);
+}
+
+uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->proto.port.dst;
+}
+
+void nfnl_ct_set_icmp_id(struct nfnl_ct *ct, int repl, uint16_t id)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID;
+
+	dir->proto.icmp.id = id;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_icmp_id(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID;
+	return !!(ct->ce_mask & attr);
+}
+
+uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->proto.icmp.id;
+}
+
+void nfnl_ct_set_icmp_type(struct nfnl_ct *ct, int repl, uint8_t type)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE;
+
+	dir->proto.icmp.type = type;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_icmp_type(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE;
+	return !!(ct->ce_mask & attr);
+}
+
+uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->proto.icmp.type;
+}
+
+void nfnl_ct_set_icmp_code(struct nfnl_ct *ct, int repl, uint8_t code)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE;
+
+	dir->proto.icmp.code = code;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_icmp_code(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE;
+	return !!(ct->ce_mask & attr);
+}
+
+uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->proto.icmp.code;
+}
+
+void nfnl_ct_set_packets(struct nfnl_ct *ct, int repl, uint64_t packets)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS;
+
+	dir->packets = packets;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_packets(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS;
+	return !!(ct->ce_mask & attr);
+}
+
+uint64_t nfnl_ct_get_packets(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->packets;
+}
+
+void nfnl_ct_set_bytes(struct nfnl_ct *ct, int repl, uint64_t bytes)
+{
+	struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+	int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES;
+
+	dir->bytes = bytes;
+	ct->ce_mask |= attr;
+}
+
+int nfnl_ct_test_bytes(const struct nfnl_ct *ct, int repl)
+{
+	int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES;
+	return !!(ct->ce_mask & attr);
+}
+
+uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl)
+{
+	const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig;
+
+	return dir->bytes;
+}
+
+/** @} */
+
+struct nl_object_ops ct_obj_ops = {
+	.oo_name		= "netfilter/ct",
+	.oo_size		= sizeof(struct nfnl_ct),
+	.oo_free_data		= ct_free_data,
+	.oo_clone		= ct_clone,
+	.oo_dump[NL_DUMP_BRIEF]	= ct_dump,
+	.oo_dump[NL_DUMP_FULL]	= ct_dump,
+	.oo_dump[NL_DUMP_STATS]	= ct_dump,
+	.oo_compare		= ct_compare,
+	.oo_attrs2str		= ct_attrs2str,
+};
+
+/** @} */
Index: libnl/src/Makefile
===================================================================
--- libnl.orig/src/Makefile	2007-09-03 14:24:29.000000000 +1000
+++ libnl/src/Makefile	2007-09-03 14:24:52.000000000 +1000
@@ -14,7 +14,7 @@ ifeq ($(shell [ ! -r ../Makefile.opts ] 
 endif
 
 LDFLAGS	+= -L../lib -lnl utils.o
-CIN 	:= $(wildcard nl-*.c) $(wildcard genl-*.c)
+CIN 	:= $(wildcard nl-*.c) $(wildcard genl-*.c) $(wildcard nf-*.c)
 TOOLS	:= $(CIN:%.c=%)
 
 all: $(TOOLS)
@@ -29,6 +29,10 @@ genl-%: genl-%.c
 	@echo "  LD $@"; \
 	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
 
+nf-%: nf-%.c
+	@echo "  LD $@"; \
+	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
 clean:
 	@echo "  CLEAN src"; \
 	rm -f $(TOOLS) utils.o
Index: libnl/src/f_ct.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/src/f_ct.c	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,153 @@
+/*
+ * src/f_ct.c     	Conntrack Filter
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+static void get_filter(struct nfnl_ct *ct, int argc, char **argv, int idx)
+{
+	struct nl_addr *a;
+
+	while (argc > idx) {
+		if (arg_match("family")) {
+			if (argc > ++idx) {
+				int family = nl_str2af(argv[idx++]);
+				if (family == AF_UNSPEC)
+					goto err_invaf;
+				nfnl_ct_set_family(ct, family);
+			}
+		} else if (arg_match("proto")) {
+			if (argc > ++idx) {
+				int proto = nl_str2ip_proto(argv[idx++]);
+				if (proto < 0)
+					goto err_invproto;
+				nfnl_ct_set_proto(ct, proto);
+			}
+		} else if (arg_match("tcpstate")) {
+			if (argc > ++idx) {
+				int state = nfnl_ct_str2tcp_state(argv[idx++]);
+				if (state < 0)
+					goto err_invtcpstate;
+				nfnl_ct_set_tcp_state(ct, state);
+			}
+		} else if (arg_match("status")) {
+			if (argc > ++idx)
+				nfnl_ct_set_status(ct, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("timeout")) {
+			if (argc > ++idx)
+				nfnl_ct_set_timeout(ct, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("mark")) {
+			if (argc > ++idx)
+				nfnl_ct_set_mark(ct, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("use")) {
+			if (argc > ++idx)
+				nfnl_ct_set_use(ct, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("id")) {
+			if (argc > ++idx)
+				nfnl_ct_set_id(ct, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origsrc")) {
+			if (argc > ++idx) {
+				a = nl_addr_parse(argv[idx++],
+						  nfnl_ct_get_family(ct));
+				if (!a)
+					goto err_invaddr;
+				nfnl_ct_set_src(ct, 0, a);
+				nl_addr_put(a);
+			}
+		} else if (arg_match("origdst")) {
+			if (argc > ++idx) {
+				a = nl_addr_parse(argv[idx++],
+						  nfnl_ct_get_family(ct));
+				if (!a)
+					goto err_invaddr;
+				nfnl_ct_set_dst(ct, 0, a);
+				nl_addr_put(a);
+			}
+		} else if (arg_match("origsrcport")) {
+			if (argc > ++idx)
+				nfnl_ct_set_src_port(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origdstport")) {
+			if (argc > ++idx)
+				nfnl_ct_set_dst_port(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origicmpid")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_id(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origicmptype")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_type(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origicmpcode")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_code(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origpackets")) {
+			if (argc > ++idx)
+				nfnl_ct_set_packets(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("origbytes")) {
+			if (argc > ++idx)
+				nfnl_ct_set_bytes(ct, 0, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replysrc")) {
+			if (argc > ++idx) {
+				a = nl_addr_parse(argv[idx++],
+						  nfnl_ct_get_family(ct));
+				if (!a)
+					goto err_invaddr;
+				nfnl_ct_set_src(ct, 1, a);
+				nl_addr_put(a);
+			}
+		} else if (arg_match("replydst")) {
+			if (argc > ++idx) {
+				a = nl_addr_parse(argv[idx++],
+						  nfnl_ct_get_family(ct));
+				if (!a)
+					goto err_invaddr;
+				nfnl_ct_set_dst(ct, 1, a);
+				nl_addr_put(a);
+			}
+		} else if (arg_match("replysrcport")) {
+			if (argc > ++idx)
+				nfnl_ct_set_src_port(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replydstport")) {
+			if (argc > ++idx)
+				nfnl_ct_set_dst_port(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmpid")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_id(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmptype")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_type(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replyicmpcode")) {
+			if (argc > ++idx)
+				nfnl_ct_set_icmp_code(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replypackets")) {
+			if (argc > ++idx)
+				nfnl_ct_set_packets(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else if (arg_match("replybytes")) {
+			if (argc > ++idx)
+				nfnl_ct_set_bytes(ct, 1, strtoul(argv[idx++], NULL, 0));
+		} else {
+			fprintf(stderr, "What is '%s'?\n", argv[idx]);
+			exit(1);
+		}
+	}
+
+	return;
+
+err_invproto:
+	fprintf(stderr, "Invalid IP protocol \"%s\".\n", argv[idx-1]);
+	exit(1);
+err_invtcpstate:
+	fprintf(stderr, "Invalid TCP state \"%s\".\n", argv[idx-1]);
+	exit(1);
+err_invaf:
+	fprintf(stderr, "Invalid address family \"%s\"\n", argv[idx-1]);
+	exit(1);
+err_invaddr:
+	fprintf(stderr, "Invalid address \"%s\": %s\n", argv[idx-1], nl_geterror());
+	exit(1);
+}
Index: libnl/src/nf-ct-dump.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/src/nf-ct-dump.c	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,88 @@
+/*
+ * src/nf-ct-dump.c     Dump conntrack attributes
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include "utils.h"
+#include <netlink/netfilter/ct.h>
+
+#include "f_ct.c"
+
+static void print_usage(void)
+{
+	printf(
+	"Usage: nf-ct-dump <mode> [<filter>]\n"
+	"  mode := { brief | detailed | stats | xml }\n"
+	"  filter := [family FAMILY] [proto PROTO] [tcpstate TCPSTATE]\n"
+	"            [status STATUS] [timeout TIMEOUT] [mark MARK] [use USE] [id ID]\n"
+	"            [origsrc ADDR] [origdst ADDR] [origsrcport PORT] [origdstport PORT]\n"
+	"            [origicmpid ID] [origicmptype TYPE] [origicmpcode CODE]\n"
+	"            [origpackets PACKETS] [origbytes BYTES]\n"
+	"            [replysrc ADDR] [replydst ADDR] [replysrcport PORT] [replydstport PORT]\n"
+	"            [replyicmpid ID] [replyicmptype TYPE] [replyicmpcode CODE]\n"
+	"            [replypackets PACKETS] [replybytes BYTES]\n");
+	exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_handle *nlh;
+	struct nl_cache *ct_cache;
+	struct nfnl_ct *ct;
+	struct nl_dump_params params = {
+		.dp_fd = stdout,
+		.dp_type = NL_DUMP_BRIEF
+	};
+	int err = 1;
+
+	if (nltool_init(argc, argv) < 0)
+		return -1;
+
+	if (argc < 2 || !strcmp(argv[1], "-h"))
+		print_usage();
+
+	nlh = nltool_alloc_handle();
+	if (!nlh)
+		return -1;
+
+	ct = nfnl_ct_alloc();
+	if (!ct)
+		goto errout;
+
+	if (nltool_connect(nlh, NETLINK_NETFILTER) < 0)
+		goto errout_free;
+
+	ct_cache = nfnl_ct_alloc_cache(nlh);
+        if (!ct_cache) {
+		fprintf(stderr, "Unable to retrieve ct cache: %s\n",
+			nl_geterror());
+		goto errout_close;
+	}
+	nl_cache_mngt_provide(ct_cache);
+
+	params.dp_type = nltool_parse_dumptype(argv[1]);
+	if (params.dp_type < 0)
+		goto errout_ct_cache;
+
+	get_filter(ct, argc, argv, 2);
+	nl_cache_dump_filter(ct_cache, &params, (struct nl_object *) ct);
+
+	err = 0;
+
+errout_ct_cache:
+	nl_cache_free(ct_cache);
+errout_close:
+	nl_close(nlh);
+errout_free:
+	nfnl_ct_put(ct);
+errout:
+	return err;
+}
Index: libnl/src/nf-monitor.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/src/nf-monitor.c	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,115 @@
+/*
+ * src/nf-monitor.c     Monitor netfilter events
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include "utils.h"
+#include <netlink/netfilter/nfnl.h>
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_STATS,
+		.dp_fd = stdout,
+		.dp_dump_msgtype = 1,
+	};
+
+	nl_object_dump(obj, &dp);
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+	if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+		fprintf(stderr, "<<EVENT>> Unknown message type\n");
+
+	/* Exit nl_recvmsgs_def() and return to the main select() */
+	return NL_EXIT;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_handle *nlh;
+	int err = 1;
+	int i, idx;
+
+	static const struct {
+		enum nfnetlink_groups gr_id;
+		const char* gr_name;
+	} known_groups[] = {
+		{ NFNLGRP_CONNTRACK_NEW, "ct-new" },
+		{ NFNLGRP_CONNTRACK_UPDATE, "ct-update" },
+		{ NFNLGRP_CONNTRACK_DESTROY, "ct-destroy" },
+		{ NFNLGRP_NONE, NULL }
+	};
+
+	if (nltool_init(argc, argv) < 0)
+		return -1;
+
+	nlh = nltool_alloc_handle();
+	if (nlh == NULL)
+		return -1;
+
+	nl_disable_sequence_check(nlh);
+
+	nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+
+	if (argc > 1 && !strcasecmp(argv[1], "-h")) {
+		printf("Usage: nf-monitor [<groups>]\n");
+
+		printf("Known groups:");
+		for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++)
+			printf(" %s", known_groups[i].gr_name);
+		printf("\n");
+		return 2;
+	}
+
+	if (nfnl_connect(nlh) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	for (idx = 1; argc > idx; idx++) {
+		for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) {
+			if (!strcmp(argv[idx], known_groups[i].gr_name)) {
+
+				if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) {
+					fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror());
+					goto errout;
+				}
+
+				break;
+			}
+		}
+		if (known_groups[i].gr_id == NFNLGRP_NONE)
+			fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]);
+	}
+
+	while (1) {
+		fd_set rfds;
+		int fd, retval;
+
+		fd = nl_socket_get_fd(nlh);
+
+		FD_ZERO(&rfds);
+		FD_SET(fd, &rfds);
+		/* wait for an incoming message on the netlink socket */
+		retval = select(fd+1, &rfds, NULL, NULL, NULL);
+
+		if (retval) {
+			/* FD_ISSET(fd, &rfds) will be true */
+			nl_recvmsgs_def(nlh);
+		}
+	}
+
+	nl_close(nlh);
+errout:
+	return err;
+}
Index: libnl/include/linux/netfilter/nfnetlink_conntrack.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/linux/netfilter/nfnetlink_conntrack.h	2007-09-03 14:24:52.000000000 +1000
@@ -0,0 +1,140 @@
+#ifndef _IPCONNTRACK_NETLINK_H
+#define _IPCONNTRACK_NETLINK_H
+#include <linux/netfilter/nfnetlink.h>
+
+enum cntl_msg_types {
+	IPCTNL_MSG_CT_NEW,
+	IPCTNL_MSG_CT_GET,
+	IPCTNL_MSG_CT_DELETE,
+	IPCTNL_MSG_CT_GET_CTRZERO,
+
+	IPCTNL_MSG_MAX
+};
+
+enum ctnl_exp_msg_types {
+	IPCTNL_MSG_EXP_NEW,
+	IPCTNL_MSG_EXP_GET,
+	IPCTNL_MSG_EXP_DELETE,
+
+	IPCTNL_MSG_EXP_MAX
+};
+
+
+enum ctattr_type {
+	CTA_UNSPEC,
+	CTA_TUPLE_ORIG,
+	CTA_TUPLE_REPLY,
+	CTA_STATUS,
+	CTA_PROTOINFO,
+	CTA_HELP,
+	CTA_NAT_SRC,
+#define CTA_NAT	CTA_NAT_SRC	/* backwards compatibility */
+	CTA_TIMEOUT,
+	CTA_MARK,
+	CTA_COUNTERS_ORIG,
+	CTA_COUNTERS_REPLY,
+	CTA_USE,
+	CTA_ID,
+	CTA_NAT_DST,
+	__CTA_MAX
+};
+#define CTA_MAX (__CTA_MAX - 1)
+
+enum ctattr_tuple {
+	CTA_TUPLE_UNSPEC,
+	CTA_TUPLE_IP,
+	CTA_TUPLE_PROTO,
+	__CTA_TUPLE_MAX
+};
+#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1)
+
+enum ctattr_ip {
+	CTA_IP_UNSPEC,
+	CTA_IP_V4_SRC,
+	CTA_IP_V4_DST,
+	CTA_IP_V6_SRC,
+	CTA_IP_V6_DST,
+	__CTA_IP_MAX
+};
+#define CTA_IP_MAX (__CTA_IP_MAX - 1)
+
+enum ctattr_l4proto {
+	CTA_PROTO_UNSPEC,
+	CTA_PROTO_NUM,
+	CTA_PROTO_SRC_PORT,
+	CTA_PROTO_DST_PORT,
+	CTA_PROTO_ICMP_ID,
+	CTA_PROTO_ICMP_TYPE,
+	CTA_PROTO_ICMP_CODE,
+	CTA_PROTO_ICMPV6_ID,
+	CTA_PROTO_ICMPV6_TYPE,
+	CTA_PROTO_ICMPV6_CODE,
+	__CTA_PROTO_MAX
+};
+#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1)
+
+enum ctattr_protoinfo {
+	CTA_PROTOINFO_UNSPEC,
+	CTA_PROTOINFO_TCP,
+	__CTA_PROTOINFO_MAX
+};
+#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
+
+enum ctattr_protoinfo_tcp {
+	CTA_PROTOINFO_TCP_UNSPEC,
+	CTA_PROTOINFO_TCP_STATE,
+	CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
+	CTA_PROTOINFO_TCP_WSCALE_REPLY,
+	CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+	CTA_PROTOINFO_TCP_FLAGS_REPLY,
+	__CTA_PROTOINFO_TCP_MAX
+};
+#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+
+enum ctattr_counters {
+	CTA_COUNTERS_UNSPEC,
+	CTA_COUNTERS_PACKETS,		/* old 64bit counters */
+	CTA_COUNTERS_BYTES,		/* old 64bit counters */
+	CTA_COUNTERS32_PACKETS,
+	CTA_COUNTERS32_BYTES,
+	__CTA_COUNTERS_MAX
+};
+#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
+
+enum ctattr_nat {
+	CTA_NAT_UNSPEC,
+	CTA_NAT_MINIP,
+	CTA_NAT_MAXIP,
+	CTA_NAT_PROTO,
+	__CTA_NAT_MAX
+};
+#define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
+
+enum ctattr_protonat {
+	CTA_PROTONAT_UNSPEC,
+	CTA_PROTONAT_PORT_MIN,
+	CTA_PROTONAT_PORT_MAX,
+	__CTA_PROTONAT_MAX
+};
+#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
+
+enum ctattr_expect {
+	CTA_EXPECT_UNSPEC,
+	CTA_EXPECT_MASTER,
+	CTA_EXPECT_TUPLE,
+	CTA_EXPECT_MASK,
+	CTA_EXPECT_TIMEOUT,
+	CTA_EXPECT_ID,
+	CTA_EXPECT_HELP_NAME,
+	__CTA_EXPECT_MAX
+};
+#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
+
+enum ctattr_help {
+	CTA_HELP_UNSPEC,
+	CTA_HELP_NAME,
+	__CTA_HELP_MAX
+};
+#define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
+
+#endif /* _IPCONNTRACK_NETLINK_H */
Index: libnl/include/netlink/addr.h
===================================================================
--- libnl.orig/include/netlink/addr.h	2007-09-03 14:24:40.000000000 +1000
+++ libnl/include/netlink/addr.h	2007-09-03 14:24:52.000000000 +1000
@@ -35,6 +35,7 @@ extern void		nl_addr_put(struct nl_addr 
 extern int		nl_addr_shared(struct nl_addr *);
 
 extern int		nl_addr_cmp(struct nl_addr *, struct nl_addr *);
+extern int		nl_addr_cmp_prefix(struct nl_addr *, struct nl_addr *);
 extern int		nl_addr_valid(char *, int);
 extern int      	nl_addr_guess_family(struct nl_addr *);
 extern int		nl_addr_fill_sockaddr(struct nl_addr *,
Index: libnl/include/netlink/utils.h
===================================================================
--- libnl.orig/include/netlink/utils.h	2007-09-03 14:24:29.000000000 +1000
+++ libnl/include/netlink/utils.h	2007-09-03 14:24:52.000000000 +1000
@@ -65,6 +65,10 @@ extern int	nl_str2llproto(const char *);
 extern char *	nl_ether_proto2str(int, char *, size_t);
 extern int	nl_str2ether_proto(const char *);
 
+/* IP protocol translations */
+extern char *	nl_ip_proto2str(int, char *, size_t);
+extern int	nl_str2ip_proto(const char *);
+
 #ifdef __cplusplus
 }
 #endif
Index: libnl/lib/addr.c
===================================================================
--- libnl.orig/lib/addr.c	2007-09-03 14:24:40.000000000 +1000
+++ libnl/lib/addr.c	2007-09-03 14:24:52.000000000 +1000
@@ -501,6 +501,34 @@ int nl_addr_cmp(struct nl_addr *a, struc
 }
 
 /**
+ * Compares the prefix of two abstract address objects.
+ * @arg a		A abstract address object.
+ * @arg b		Another abstract address object.
+ *
+ * @return Integer less than, equal to or greather than zero if \c is found,
+ *         respectively to be less than, to, or be greater than \c b.
+ */
+int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b)
+{
+	int d = a->a_family - b->a_family;
+
+	if (d == 0) {
+		int len = min(a->a_prefixlen, b->a_prefixlen);
+		int bytes = len / 8;
+
+		d = memcmp(a->a_addr, b->a_addr, bytes);
+		if (d == 0) {
+			int mask = (1UL << (len % 8)) - 1UL;
+
+			d = (a->a_addr[bytes] & mask) -
+			    (b->a_addr[bytes] & mask);
+		}
+	}
+
+	return d;
+}
+
+/**
  * Check if an address matches a certain family.
  * @arg addr		Address represented as character string.
  * @arg family		Desired address family.
Index: libnl/lib/utils.c
===================================================================
--- libnl.orig/lib/utils.c	2007-09-03 14:24:29.000000000 +1000
+++ libnl/lib/utils.c	2007-09-03 14:24:52.000000000 +1000
@@ -662,4 +662,40 @@ int nl_str2ether_proto(const char *name)
 
 /** @} */
 
+/**
+ * @name IP Protocol Translations
+ * @{
+ */
+
+char *nl_ip_proto2str(int proto, char *buf, size_t len)
+{
+	struct protoent *p = getprotobynumber(proto);
+
+	if (p) {
+		snprintf(buf, len, "%s", p->p_name);
+		return buf;
+	}
+
+	snprintf(buf, len, "0x%x", proto);
+	return buf;
+}
+
+int nl_str2ip_proto(const char *name)
+{
+	struct protoent *p = getprotobyname(name);
+	unsigned long l;
+	char *end;
+
+	if (p)
+		return p->p_proto;
+
+	l = strtoul(name, &end, 0);
+	if (l == ULONG_MAX || *end != '\0')
+		return -1;
+
+	return (int) l;
+}
+
+/** @} */
+
 /** @} */

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 3/3] libnl: add netfilter log support
  2007-09-03  5:09 netfilter support in libnl Philip Craig
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
  2007-09-03  5:11 ` [PATCH 2/3] libnl: add netfilter conntrack support Philip Craig
@ 2007-09-03  5:12 ` Philip Craig
  2007-09-04 16:48   ` Thomas Graf
  2007-09-03  9:30 ` netfilter support in libnl Patrick McHardy
  3 siblings, 1 reply; 22+ messages in thread
From: Philip Craig @ 2007-09-03  5:12 UTC (permalink / raw)
  To: Netfilter Developer Mailing List

[-- Attachment #1: Type: text/plain, Size: 1 bytes --]



[-- Attachment #2: nfnl_log.patch --]
[-- Type: text/x-diff, Size: 29757 bytes --]

---
 include/linux/netfilter/nfnetlink_log.h |   96 +++++++
 include/netlink-types.h                 |   22 +
 include/netlink/netfilter/log.h         |  105 +++++++
 lib/netfilter/log.c                     |  349 ++++++++++++++++++++++++++
 lib/netfilter/log_obj.c                 |  425 ++++++++++++++++++++++++++++++++
 src/nf-log.c                            |  142 ++++++++++
 6 files changed, 1139 insertions(+)

Index: libnl/include/netlink-types.h
===================================================================
--- libnl.orig/include/netlink-types.h	2007-09-03 14:32:20.000000000 +1000
+++ libnl/include/netlink-types.h	2007-09-03 14:32:21.000000000 +1000
@@ -839,4 +839,26 @@ struct nfnl_ct {
 	struct nfnl_ct_dir	ct_repl;
 };
 
+struct nfnl_log {
+	NLHDR_COMMON
+
+	uint8_t			log_family;
+	uint8_t			log_hook;
+	uint16_t		log_hwproto;
+	uint32_t		log_mark;
+	struct timeval		log_timestamp;
+	uint32_t		log_indev;
+	uint32_t		log_outdev;
+	uint32_t		log_physindev;
+	uint32_t		log_physoutdev;
+	uint8_t			log_hwaddr[8];
+	int			log_hwaddr_len;
+	void *			log_payload;
+	int			log_payload_len;
+	char *			log_prefix;
+	uint32_t		log_uid;
+	uint32_t		log_seq;
+	uint32_t		log_seq_global;
+};
+
 #endif
Index: libnl/include/netlink/netfilter/log.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/netlink/netfilter/log.h	2007-09-03 14:32:21.000000000 +1000
@@ -0,0 +1,105 @@
+/*
+ * netlink/netfilter/log.h	Netfilter Log
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#ifndef NETLINK_LOG_H_
+#define NETLINK_LOG_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_handle;
+struct nlmsghdr;
+struct nfnl_log;
+
+extern struct nl_object_ops log_obj_ops;
+
+/* General */
+extern struct nfnl_log *nfnl_log_alloc(void);
+extern struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *);
+
+extern void		nfnl_log_get(struct nfnl_log *);
+extern void		nfnl_log_put(struct nfnl_log *);
+
+extern struct nl_msg *	nfnl_log_build_bind(uint16_t);;
+extern int		nfnl_log_bind(struct nl_handle *, uint16_t);
+extern struct nl_msg *	nfnl_log_build_unbind(uint16_t);
+extern int		nfnl_log_unbind(struct nl_handle *, uint16_t);
+extern struct nl_msg *	nfnl_log_build_pf_bind(uint8_t);
+extern int		nfnl_log_pf_bind(struct nl_handle *, uint8_t);
+extern struct nl_msg *	nfnl_log_build_pf_unbind(uint8_t);
+extern int		nfnl_log_pf_unbind(struct nl_handle *, uint8_t);
+extern struct nl_msg *	nfnl_log_build_mode(uint16_t, uint8_t, uint32_t);
+extern int		nfnl_log_set_mode(struct nl_handle *, uint16_t,
+					  uint8_t, uint32_t);
+
+extern void		nfnl_log_set_family(struct nfnl_log *, uint8_t);
+extern uint8_t		nfnl_log_get_family(const struct nfnl_log *);
+
+extern void		nfnl_log_set_hwproto(struct nfnl_log *, uint16_t);
+extern int		nfnl_log_test_hwproto(const struct nfnl_log *);
+extern uint16_t		nfnl_log_get_hwproto(const struct nfnl_log *);
+
+extern void		nfnl_log_set_hook(struct nfnl_log *, uint8_t);
+extern int		nfnl_log_test_hook(const struct nfnl_log *);
+extern uint8_t		nfnl_log_get_hook(const struct nfnl_log *);
+
+extern void		nfnl_log_set_mark(struct nfnl_log *, uint32_t);
+extern int		nfnl_log_test_mark(const struct nfnl_log *);
+extern uint32_t		nfnl_log_get_mark(const struct nfnl_log *);
+
+extern void		nfnl_log_set_timestamp(struct nfnl_log *,
+					       struct timeval *);
+extern const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *);
+
+extern void		nfnl_log_set_indev(struct nfnl_log *, uint32_t);
+extern uint32_t		nfnl_log_get_indev(const struct nfnl_log *);
+
+extern void		nfnl_log_set_outdev(struct nfnl_log *, uint32_t);
+extern uint32_t		nfnl_log_get_outdev(const struct nfnl_log *);
+
+extern void		nfnl_log_set_physindev(struct nfnl_log *, uint32_t);
+extern uint32_t		nfnl_log_get_physindev(const struct nfnl_log *);
+
+extern void		nfnl_log_set_physoutdev(struct nfnl_log *, uint32_t);
+extern uint32_t		nfnl_log_get_physoutdev(const struct nfnl_log *);
+
+extern void		nfnl_log_set_hwaddr(struct nfnl_log *, uint8_t *, int);
+extern const uint8_t *	nfnl_log_get_hwaddr(const struct nfnl_log *, int *);
+
+extern int		nfnl_log_set_payload(struct nfnl_log *, uint8_t *, int);
+extern const void *	nfnl_log_get_payload(const struct nfnl_log *, int *);
+
+extern int		nfnl_log_set_prefix(struct nfnl_log *, void *);
+extern const char *	nfnl_log_get_prefix(const struct nfnl_log *);
+
+extern void		nfnl_log_set_uid(struct nfnl_log *, uint32_t);
+extern int		nfnl_log_test_uid(const struct nfnl_log *);
+extern uint32_t		nfnl_log_get_uid(const struct nfnl_log *);
+
+extern void		nfnl_log_set_seq(struct nfnl_log *, uint32_t);
+extern int		nfnl_log_test_seq(const struct nfnl_log *);
+extern uint32_t		nfnl_log_get_seq(const struct nfnl_log *);
+
+extern void		nfnl_log_set_seq_global(struct nfnl_log *, uint32_t);
+extern int		nfnl_log_test_seq_global(const struct nfnl_log *);
+extern uint32_t		nfnl_log_get_seq_global(const struct nfnl_log *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
Index: libnl/lib/netfilter/log.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/lib/netfilter/log.c	2007-09-03 14:32:21.000000000 +1000
@@ -0,0 +1,349 @@
+/*
+ * lib/netfilter/log.c	Netfilter Log
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup log Log
+ * @brief
+ * @{
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+#include <netlink-local.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/log.h>
+
+static struct nl_cache_ops nfnl_log_ops;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return x;
+}
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+static uint64_t ntohll(uint64_t x)
+{
+	return __bswap_64(x);
+}
+#endif
+
+static struct nla_policy log_policy[NFULA_MAX+1] = {
+	[NFULA_PACKET_HDR]		= {
+		.minlen = sizeof(struct nfulnl_msg_packet_hdr)
+	},
+	[NFULA_MARK]			= { .type = NLA_U32 },
+	[NFULA_TIMESTAMP]		= {
+		.minlen = sizeof(struct nfulnl_msg_packet_timestamp)
+	},
+	[NFULA_IFINDEX_INDEV]		= { .type = NLA_U32 },
+	[NFULA_IFINDEX_OUTDEV]		= { .type = NLA_U32 },
+	[NFULA_IFINDEX_PHYSINDEV]	= { .type = NLA_U32 },
+	[NFULA_IFINDEX_PHYSOUTDEV]	= { .type = NLA_U32 },
+	[NFULA_HWADDR]			= {
+		.minlen = sizeof(struct nfulnl_msg_packet_hw)
+	},
+	//[NFULA_PAYLOAD]
+	[NFULA_PREFIX]			= { .type = NLA_STRING, },
+	[NFULA_UID]			= { .type = NLA_U32 },
+	[NFULA_SEQ]			= { .type = NLA_U32 },
+	[NFULA_SEQ_GLOBAL]		= { .type = NLA_U32 },
+};
+
+struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *nlh)
+{
+	struct nfnl_log *log;
+	struct nlattr *tb[NFULA_MAX+1];
+	struct nlattr *attr;
+	int err;
+
+	log = nfnl_log_alloc();
+	if (!log)
+		return NULL;
+
+	log->ce_msgtype = nlh->nlmsg_type;
+
+	err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX,
+			  log_policy);
+	if (err < 0)
+		goto errout;
+
+	nfnl_log_set_family(log, nfnlmsg_family(nlh));
+
+	attr = tb[NFULA_PACKET_HDR];
+	if (attr) {
+		struct nfulnl_msg_packet_hdr *hdr = nla_data(attr);
+
+		nfnl_log_set_hwproto(log, hdr->hw_protocol);
+		nfnl_log_set_hook(log, hdr->hook);
+	}
+
+	attr = tb[NFULA_MARK];
+	if (attr)
+		nfnl_log_set_mark(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_TIMESTAMP];
+	if (attr) {
+		struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr);
+		struct timeval tv;
+
+		tv.tv_sec = ntohll(timestamp->sec);
+		tv.tv_usec = ntohll(timestamp->usec);
+		nfnl_log_set_timestamp(log, &tv);
+	}
+
+	attr = tb[NFULA_IFINDEX_INDEV];
+	if (attr)
+		nfnl_log_set_indev(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_OUTDEV];
+	if (attr)
+		nfnl_log_set_outdev(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_PHYSINDEV];
+	if (attr)
+		nfnl_log_set_physindev(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_IFINDEX_PHYSOUTDEV];
+	if (attr)
+		nfnl_log_set_physoutdev(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_HWADDR];
+	if (attr) {
+		struct nfulnl_msg_packet_hw *hw = nla_data(attr);
+
+		nfnl_log_set_hwaddr(log, hw->hw_addr, ntohs(hw->hw_addrlen));
+	}
+
+	attr = tb[NFULA_PAYLOAD];
+	if (attr) {
+		err = nfnl_log_set_payload(log, nla_data(attr), nla_len(attr));
+		if (err < 0)
+			goto errout;
+	}
+
+	attr = tb[NFULA_PREFIX];
+	if (attr) {
+		err = nfnl_log_set_prefix(log, nla_data(attr));
+		if (err < 0)
+			goto errout;
+	}
+
+	attr = tb[NFULA_UID];
+	if (attr)
+		nfnl_log_set_uid(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_SEQ];
+	if (attr)
+		nfnl_log_set_seq(log, ntohl(nla_get_u32(attr)));
+
+	attr = tb[NFULA_SEQ_GLOBAL];
+	if (attr)
+		nfnl_log_set_seq_global(log, ntohl(nla_get_u32(attr)));
+
+	return log;
+
+errout:
+	nfnl_log_put(log);
+	return NULL;
+}
+
+static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			 struct nlmsghdr *nlh, void *arg)
+{
+	struct nl_parser_param *pp = arg;
+	struct nfnl_log *log;
+	int err;
+
+	log = nfnlmsg_log_parse(nlh);
+	if (log == NULL)
+		goto errout_errno;
+
+	err = pp->pp_cb((struct nl_object *) log, pp);
+	if (err < 0)
+		goto errout;
+
+	return P_ACCEPT;
+
+errout_errno:
+	err = nl_get_errno();
+errout:
+	nfnl_log_put(log);
+	return err;
+}
+
+/**
+ * @name Log Commands
+ * @{
+ */
+
+static struct nl_msg *build_log_cmd_msg(uint8_t family, uint16_t queuenum,
+					uint8_t command)
+{
+	struct nl_msg *msg;
+	struct nfulnl_msg_config_cmd cmd;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0,
+				   family, queuenum);
+	if (msg == NULL)
+		return NULL;
+
+	cmd.command = command;
+	if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0)
+		goto nla_put_failure;
+
+	return msg;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+static int send_log_msg(struct nl_handle *handle, struct nl_msg *msg)
+{
+	int err;
+
+	err = nl_send_auto_complete(handle, msg);
+	nlmsg_free(msg);
+	if (err < 0)
+		return err;
+
+	return nl_wait_for_ack(handle);
+}
+
+struct nl_msg *nfnl_log_build_bind(uint16_t queuenum)
+{
+	return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_BIND);
+}
+
+int nfnl_log_bind(struct nl_handle *nlh, uint16_t queuenum)
+{
+	struct nl_msg *msg;
+
+	msg = nfnl_log_build_bind(queuenum);
+	if (!msg)
+		return nl_get_errno();
+
+	return send_log_msg(nlh, msg);
+}
+
+struct nl_msg *nfnl_log_build_unbind(uint16_t queuenum)
+{
+	return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_UNBIND);
+}
+
+int nfnl_log_unbind(struct nl_handle *nlh, uint16_t queuenum)
+{
+	struct nl_msg *msg;
+
+	msg = nfnl_log_build_bind(queuenum);
+	if (!msg)
+		return nl_get_errno();
+
+	return send_log_msg(nlh, msg);
+}
+
+struct nl_msg *nfnl_log_build_pf_bind(uint8_t pf)
+{
+	return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_BIND);
+}
+
+int nfnl_log_pf_bind(struct nl_handle *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+
+	msg = nfnl_log_build_pf_bind(pf);
+	if (!msg)
+		return nl_get_errno();
+
+	return send_log_msg(nlh, msg);
+}
+
+struct nl_msg *nfnl_log_build_pf_unbind(uint8_t pf)
+{
+	return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_UNBIND);
+}
+
+int nfnl_log_pf_unbind(struct nl_handle *nlh, uint8_t pf)
+{
+	struct nl_msg *msg;
+
+	msg = nfnl_log_build_pf_unbind(pf);
+	if (!msg)
+		return nl_get_errno();
+
+	return send_log_msg(nlh, msg);
+}
+
+struct nl_msg *nfnl_log_build_mode(uint16_t queuenum, uint8_t copy_mode,
+				   uint32_t copy_range)
+{
+	struct nl_msg *msg;
+	struct nfulnl_msg_config_mode mode;
+
+	msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0,
+			0, queuenum);
+	if (msg == NULL)
+		return NULL;
+
+	mode.copy_mode = copy_mode;
+	mode.copy_range = htonl(copy_range);
+	if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0)
+		goto nla_put_failure;
+
+	return msg;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+int nfnl_log_set_mode(struct nl_handle *nlh, uint16_t queuenum,
+		      uint8_t copy_mode, uint32_t copy_range)
+{
+	struct nl_msg *msg;
+
+	msg = nfnl_log_build_mode(queuenum, copy_mode, copy_range);
+	if (!msg)
+		return nl_get_errno();
+	return send_log_msg(nlh, msg);
+}
+
+/** @} */
+
+#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type))
+static struct nl_cache_ops nfnl_log_ops = {
+	.co_name		= "netfilter/log",
+	.co_hdrsize		= NFNL_HDRLEN,
+	.co_msgtypes		= {
+		{ NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" },
+		END_OF_MSGTYPES_LIST,
+	},
+	.co_protocol		= NETLINK_NETFILTER,
+	.co_msg_parser		= log_msg_parser,
+	.co_obj_ops		= &log_obj_ops,
+};
+
+static void __init log_init(void)
+{
+	nl_cache_mngt_register(&nfnl_log_ops);
+}
+
+static void __exit log_exit(void)
+{
+	nl_cache_mngt_unregister(&nfnl_log_ops);
+}
+
+/** @} */
Index: libnl/lib/netfilter/log_obj.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/lib/netfilter/log_obj.c	2007-09-03 14:32:21.000000000 +1000
@@ -0,0 +1,425 @@
+/*
+ * lib/netfilter/log_obj.c	Netfilter Log Object
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <netlink-local.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/log.h>
+
+/** @cond SKIP */
+#define LOG_ATTR_FAMILY			(1UL << 0)
+#define LOG_ATTR_HWPROTO		(1UL << 1)
+#define LOG_ATTR_HOOK			(1UL << 2)
+#define LOG_ATTR_MARK			(1UL << 3)
+#define LOG_ATTR_TIMESTAMP		(1UL << 4)
+#define LOG_ATTR_INDEV			(1UL << 5)
+#define LOG_ATTR_OUTDEV			(1UL << 6)
+#define LOG_ATTR_PHYSINDEV		(1UL << 7)
+#define LOG_ATTR_PHYSOUTDEV		(1UL << 8)
+#define LOG_ATTR_HWADDR			(1UL << 9)
+#define LOG_ATTR_PAYLOAD		(1UL << 10)
+#define LOG_ATTR_PREFIX			(1UL << 11)
+#define LOG_ATTR_UID			(1UL << 12)
+#define LOG_ATTR_SEQ			(1UL << 13)
+#define LOG_ATTR_SEQ_GLOBAL		(1UL << 14)
+/** @endcond */
+
+static void log_free_data(struct nl_object *c)
+{
+	struct nfnl_log *log = (struct nfnl_log *) c;
+
+	if (log == NULL)
+		return;
+
+	free(log->log_payload);
+	free(log->log_prefix);
+}
+
+static int log_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct nfnl_log *dst = (struct nfnl_log *) _dst;
+	struct nfnl_log *src = (struct nfnl_log *) _src;
+	int err;
+
+	if (src->log_payload) {
+		err = nfnl_log_set_payload(dst, src->log_payload,
+					   src->log_payload_len);
+		if (err < 0)
+			goto errout;
+	}
+
+	if (src->log_prefix) {
+		err = nfnl_log_set_prefix(dst, src->log_prefix);
+		if (err < 0)
+			goto errout;
+	}
+
+	return 0;
+errout:
+	return err;
+}
+
+static int log_dump(struct nl_object *a, struct nl_dump_params *p)
+{
+	struct nfnl_log *log = (struct nfnl_log *) a;
+	struct nl_cache *link_cache;
+	char buf[64];
+
+	link_cache = nl_cache_mngt_require("route/link");
+
+	if (log->ce_mask & LOG_ATTR_PREFIX)
+		dp_dump(p, "%s", log->log_prefix);
+
+	if (log->ce_mask & LOG_ATTR_INDEV) {
+		if (link_cache)
+			dp_dump(p, "IN=%s ",
+				rtnl_link_i2name(link_cache, log->log_indev,
+						 buf, sizeof(buf)));
+		else
+			dp_dump(p, "IN=%d ", log->log_indev);
+	}
+
+	if (log->ce_mask & LOG_ATTR_PHYSINDEV) {
+		if (link_cache)
+			dp_dump(p, "PHYSIN=%s ",
+				rtnl_link_i2name(link_cache, log->log_physindev,
+						 buf, sizeof(buf)));
+		else
+			dp_dump(p, "IN=%d ", log->log_physindev);
+	}
+
+	if (log->ce_mask & LOG_ATTR_OUTDEV) {
+		if (link_cache)
+			dp_dump(p, "OUT=%s ",
+				rtnl_link_i2name(link_cache, log->log_outdev,
+						 buf, sizeof(buf)));
+		else
+			dp_dump(p, "OUT=%d ", log->log_outdev);
+	}
+
+	if (log->ce_mask & LOG_ATTR_PHYSOUTDEV) {
+		if (link_cache)
+			dp_dump(p, "PHYSOUT=%s ",
+				rtnl_link_i2name(link_cache,log->log_physoutdev,
+						 buf, sizeof(buf)));
+		else
+			dp_dump(p, "PHYSOUT=%d ", log->log_physoutdev);
+	}
+
+	if (log->ce_mask & LOG_ATTR_HWADDR) {
+		int i;
+
+		dp_dump(p, "MAC");
+		for (i = 0; i < log->log_hwaddr_len; i++)
+			dp_dump(p, "%c%02x", i?':':'=', log->log_hwaddr[i]);
+		dp_dump(p, " ");
+	}
+
+	/* FIXME: parse the payload to get iptables LOG compatible format */
+
+	if (log->ce_mask & LOG_ATTR_FAMILY)
+		dp_dump(p, "FAMILY=%s ",
+			nl_af2str(log->log_family, buf, sizeof(buf)));
+
+	if (log->ce_mask & LOG_ATTR_HWPROTO)
+		dp_dump(p, "HWPROTO=%s ",
+			nl_ether_proto2str(ntohs(log->log_hwproto),
+					   buf, sizeof(buf)));
+
+	if (log->ce_mask & LOG_ATTR_HOOK)
+		dp_dump(p, "HOOK=%d ", log->log_hook);
+
+	if (log->ce_mask & LOG_ATTR_MARK)
+		dp_dump(p, "MARK=%d ", log->log_mark);
+
+	if (log->ce_mask & LOG_ATTR_PAYLOAD)
+		dp_dump(p, "PAYLOADLEN=%d ", log->log_payload_len);
+
+	if (log->ce_mask & LOG_ATTR_SEQ)
+		dp_dump(p, "SEQ=%d ", log->log_seq);
+
+	if (log->ce_mask & LOG_ATTR_SEQ_GLOBAL)
+		dp_dump(p, "SEQGLOBAL=%d ", log->log_seq_global);
+
+	dp_dump(p, "\n");
+
+	return 1;
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_log *nfnl_log_alloc(void)
+{
+	return (struct nfnl_log *) nl_object_alloc(&log_obj_ops);
+}
+
+void nfnl_log_get(struct nfnl_log *log)
+{
+	nl_object_get((struct nl_object *) log);
+}
+
+void nfnl_log_put(struct nfnl_log *log)
+{
+	nl_object_put((struct nl_object *) log);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_log_set_family(struct nfnl_log *log, uint8_t family)
+{
+	log->log_family = family;
+	log->ce_mask |= LOG_ATTR_FAMILY;
+}
+
+uint8_t nfnl_log_get_family(const struct nfnl_log *log)
+{
+	if (log->ce_mask & LOG_ATTR_FAMILY)
+		return log->log_family;
+	else
+		return AF_UNSPEC;
+}
+
+void nfnl_log_set_hwproto(struct nfnl_log *log, uint16_t hwproto)
+{
+	log->log_hwproto = hwproto;
+	log->ce_mask |= LOG_ATTR_HWPROTO;
+}
+
+int nfnl_log_test_hwproto(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_HWPROTO);
+}
+
+uint16_t nfnl_log_get_hwproto(const struct nfnl_log *log)
+{
+	return log->log_hwproto;
+}
+
+void nfnl_log_set_hook(struct nfnl_log *log, uint8_t hook)
+{
+	log->log_hook = hook;
+	log->ce_mask |= LOG_ATTR_HOOK;
+}
+
+int nfnl_log_test_hook(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_HOOK);
+}
+
+uint8_t nfnl_log_get_hook(const struct nfnl_log *log)
+{
+	return log->log_hook;
+}
+
+void nfnl_log_set_mark(struct nfnl_log *log, uint32_t mark)
+{
+	log->log_mark = mark;
+	log->ce_mask |= LOG_ATTR_MARK;
+}
+
+int nfnl_log_test_mark(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_MARK);
+}
+
+uint32_t nfnl_log_get_mark(const struct nfnl_log *log)
+{
+	return log->log_mark;
+}
+
+void nfnl_log_set_timestamp(struct nfnl_log *log, struct timeval *tv)
+{
+	log->log_timestamp.tv_sec = tv->tv_sec;
+	log->log_timestamp.tv_usec = tv->tv_usec;
+	log->ce_mask |= LOG_ATTR_TIMESTAMP;
+}
+
+const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *log)
+{
+	if (!(log->ce_mask & LOG_ATTR_TIMESTAMP))
+		return NULL;
+	return &log->log_timestamp;
+}
+
+void nfnl_log_set_indev(struct nfnl_log *log, uint32_t indev)
+{
+	log->log_indev = indev;
+	log->ce_mask |= LOG_ATTR_INDEV;
+}
+
+uint32_t nfnl_log_get_indev(const struct nfnl_log *log)
+{
+	return log->log_indev;
+}
+
+void nfnl_log_set_outdev(struct nfnl_log *log, uint32_t outdev)
+{
+	log->log_outdev = outdev;
+	log->ce_mask |= LOG_ATTR_OUTDEV;
+}
+
+uint32_t nfnl_log_get_outdev(const struct nfnl_log *log)
+{
+	return log->log_outdev;
+}
+
+void nfnl_log_set_physindev(struct nfnl_log *log, uint32_t physindev)
+{
+	log->log_physindev = physindev;
+	log->ce_mask |= LOG_ATTR_PHYSINDEV;
+}
+
+uint32_t nfnl_log_get_physindev(const struct nfnl_log *log)
+{
+	return log->log_physindev;
+}
+
+void nfnl_log_set_physoutdev(struct nfnl_log *log, uint32_t physoutdev)
+{
+	log->log_physoutdev = physoutdev;
+	log->ce_mask |= LOG_ATTR_PHYSOUTDEV;
+}
+
+uint32_t nfnl_log_get_physoutdev(const struct nfnl_log *log)
+{
+	return log->log_physoutdev;
+}
+
+void nfnl_log_set_hwaddr(struct nfnl_log *log, uint8_t *hwaddr, int len)
+{
+	if (len > sizeof(log->log_hwaddr))
+		len = sizeof(log->log_hwaddr);
+	log->log_hwaddr_len = len;
+	memcpy(log->log_hwaddr, hwaddr, len);
+	log->ce_mask |= LOG_ATTR_HWADDR;
+}
+
+const uint8_t *nfnl_log_get_hwaddr(const struct nfnl_log *log, int *len)
+{
+	if (!(log->ce_mask & LOG_ATTR_HWADDR)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = log->log_hwaddr_len;
+	return log->log_hwaddr;
+}
+
+int nfnl_log_set_payload(struct nfnl_log *log, uint8_t *payload, int len)
+{
+	free(log->log_payload);
+	log->log_payload = malloc(len);
+	if (!log->log_payload)
+		return nl_errno(ENOMEM);
+
+	memcpy(log->log_payload, payload, len);
+	log->log_payload_len = len;
+	log->ce_mask |= LOG_ATTR_PAYLOAD;
+	return 0;
+}
+
+const void *nfnl_log_get_payload(const struct nfnl_log *log, int *len)
+{
+	if (!(log->ce_mask & LOG_ATTR_PAYLOAD)) {
+		*len = 0;
+		return NULL;
+	}
+
+	*len = log->log_payload_len;
+	return log->log_payload;
+}
+
+int nfnl_log_set_prefix(struct nfnl_log *log, void *prefix)
+{
+	free(log->log_prefix);
+	log->log_prefix = strdup(prefix);
+	if (!log->log_prefix)
+		return nl_errno(ENOMEM);
+
+	log->ce_mask |= LOG_ATTR_PREFIX;
+	return 0;
+}
+
+const char *nfnl_log_get_prefix(const struct nfnl_log *log)
+{
+	return log->log_prefix;
+}
+
+void nfnl_log_set_uid(struct nfnl_log *log, uint32_t uid)
+{
+	log->log_uid = uid;
+	log->ce_mask |= LOG_ATTR_UID;
+}
+
+int nfnl_log_test_uid(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_UID);
+}
+
+uint32_t nfnl_log_get_uid(const struct nfnl_log *log)
+{
+	return log->log_uid;
+}
+
+void nfnl_log_set_seq(struct nfnl_log *log, uint32_t seq)
+{
+	log->log_seq = seq;
+	log->ce_mask |= LOG_ATTR_SEQ;
+}
+
+int nfnl_log_test_seq(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_SEQ);
+}
+
+uint32_t nfnl_log_get_seq(const struct nfnl_log *log)
+{
+	return log->log_seq;
+}
+
+void nfnl_log_set_seq_global(struct nfnl_log *log, uint32_t seq_global)
+{
+	log->log_seq_global = seq_global;
+	log->ce_mask |= LOG_ATTR_SEQ_GLOBAL;
+}
+
+int nfnl_log_test_seq_global(const struct nfnl_log *log)
+{
+	return !!(log->ce_mask & LOG_ATTR_SEQ_GLOBAL);
+}
+
+uint32_t nfnl_log_get_seq_global(const struct nfnl_log *log)
+{
+	return log->log_seq_global;
+}
+
+/** @} */
+
+struct nl_object_ops log_obj_ops = {
+	.oo_name		= "netfilter/log",
+	.oo_size		= sizeof(struct nfnl_log),
+	.oo_free_data		= log_free_data,
+	.oo_clone		= log_clone,
+	.oo_dump[NL_DUMP_BRIEF]	= log_dump,
+	.oo_dump[NL_DUMP_FULL]	= log_dump,
+	.oo_dump[NL_DUMP_STATS]	= log_dump,
+};
+
+/** @} */
Index: libnl/src/nf-log.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/src/nf-log.c	2007-09-03 14:39:32.000000000 +1000
@@ -0,0 +1,142 @@
+/*
+ * src/nf-log.c     Monitor netfilter log events
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+#include "utils.h"
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/log.h>
+
+static void obj_input(struct nl_object *obj, void *arg)
+{
+	struct nl_dump_params dp = {
+		.dp_type = NL_DUMP_STATS,
+		.dp_fd = stdout,
+		.dp_dump_msgtype = 1,
+	};
+
+	nl_object_dump(obj, &dp);
+}
+
+static int event_input(struct nl_msg *msg, void *arg)
+{
+	if (nl_msg_parse(msg, &obj_input, NULL) < 0)
+		fprintf(stderr, "<<EVENT>> Unknown message type\n");
+
+	/* Exit nl_recvmsgs_def() and return to the main select() */
+	return NL_EXIT;
+}
+
+int main(int argc, char *argv[])
+{
+	struct nl_handle *nfnlh;
+	struct nl_handle *rtnlh;
+        struct nl_cache *link_cache;
+	int err = 1;
+	int family, group;
+
+	if (nltool_init(argc, argv) < 0)
+		return -1;
+
+	nfnlh = nltool_alloc_handle();
+	if (nfnlh == NULL)
+		return -1;
+
+	nl_disable_sequence_check(nfnlh);
+
+	nl_socket_modify_cb(nfnlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL);
+
+	if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) {
+		printf("Usage: nf-log family group\n");
+		return 2;
+	}
+
+	if (nfnl_connect(nfnlh) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	family = nl_str2af(argv[1]);
+	if (family == AF_UNSPEC) {
+		fprintf(stderr, "Unknown family: %s\n", argv[1]);
+		goto errout;
+	}
+	if (nfnl_log_pf_unbind(nfnlh, family) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+	if (nfnl_log_pf_bind(nfnlh, family) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	group = nl_str2af(argv[2]);
+	if (nfnl_log_bind(nfnlh, group) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	if (nfnl_log_set_mode(nfnlh, 0, NFULNL_COPY_PACKET, 0xffff) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	rtnlh = nltool_alloc_handle();
+	if (rtnlh == NULL) {
+		goto errout_close;
+	}
+
+	if (nl_connect(rtnlh, NETLINK_ROUTE) < 0) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout;
+	}
+
+	if ((link_cache = rtnl_link_alloc_cache(rtnlh)) == NULL) {
+		fprintf(stderr, "%s\n", nl_geterror());
+		goto errout_close;
+	}
+
+	nl_cache_mngt_provide(link_cache);
+
+	while (1) {
+		fd_set rfds;
+		int nffd, rtfd, maxfd, retval;
+
+		FD_ZERO(&rfds);
+
+		maxfd = nffd = nl_socket_get_fd(nfnlh);
+		FD_SET(nffd, &rfds);
+
+		rtfd = nl_socket_get_fd(rtnlh);
+		FD_SET(rtfd, &rfds);
+		if (maxfd < rtfd)
+			maxfd = rtfd;
+
+		/* wait for an incoming message on the netlink socket */
+		retval = select(maxfd+1, &rfds, NULL, NULL, NULL);
+
+		if (retval) {
+			if (FD_ISSET(nffd, &rfds))
+				nl_recvmsgs_def(nfnlh);
+			if (FD_ISSET(rtfd, &rfds))
+				nl_recvmsgs_def(rtnlh);
+		}
+	}
+
+	nl_close(rtnlh);
+errout_close:
+	nl_close(nfnlh);
+errout:
+	return err;
+}
Index: libnl/include/linux/netfilter/nfnetlink_log.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ libnl/include/linux/netfilter/nfnetlink_log.h	2007-09-03 14:32:21.000000000 +1000
@@ -0,0 +1,96 @@
+#ifndef _NFNETLINK_LOG_H
+#define _NFNETLINK_LOG_H
+
+/* This file describes the netlink messages (i.e. 'protocol packets'),
+ * and not any kind of function definitions.  It is shared between kernel and
+ * userspace.  Don't put kernel specific stuff in here */
+
+#ifndef aligned_be64
+#define aligned_be64 u_int64_t __attribute__((aligned(8)))
+#endif
+
+#include <linux/types.h>
+#include <linux/netfilter/nfnetlink.h>
+
+enum nfulnl_msg_types {
+	NFULNL_MSG_PACKET,		/* packet from kernel to userspace */
+	NFULNL_MSG_CONFIG,		/* connect to a particular queue */
+
+	NFULNL_MSG_MAX
+};
+
+struct nfulnl_msg_packet_hdr {
+	__be16		hw_protocol;	/* hw protocol (network order) */
+	u_int8_t	hook;		/* netfilter hook */
+	u_int8_t	_pad;
+};
+
+struct nfulnl_msg_packet_hw {
+	__be16		hw_addrlen;
+	u_int16_t	_pad;
+	u_int8_t	hw_addr[8];
+};
+
+struct nfulnl_msg_packet_timestamp {
+	aligned_be64	sec;
+	aligned_be64	usec;
+};
+
+enum nfulnl_attr_type {
+	NFULA_UNSPEC,
+	NFULA_PACKET_HDR,
+	NFULA_MARK,			/* u_int32_t nfmark */
+	NFULA_TIMESTAMP,		/* nfulnl_msg_packet_timestamp */
+	NFULA_IFINDEX_INDEV,		/* u_int32_t ifindex */
+	NFULA_IFINDEX_OUTDEV,		/* u_int32_t ifindex */
+	NFULA_IFINDEX_PHYSINDEV,	/* u_int32_t ifindex */
+	NFULA_IFINDEX_PHYSOUTDEV,	/* u_int32_t ifindex */
+	NFULA_HWADDR,			/* nfulnl_msg_packet_hw */
+	NFULA_PAYLOAD,			/* opaque data payload */
+	NFULA_PREFIX,			/* string prefix */
+	NFULA_UID,			/* user id of socket */
+	NFULA_SEQ,			/* instance-local sequence number */
+	NFULA_SEQ_GLOBAL,		/* global sequence number */
+
+	__NFULA_MAX
+};
+#define NFULA_MAX (__NFULA_MAX - 1)
+
+enum nfulnl_msg_config_cmds {
+	NFULNL_CFG_CMD_NONE,
+	NFULNL_CFG_CMD_BIND,
+	NFULNL_CFG_CMD_UNBIND,
+	NFULNL_CFG_CMD_PF_BIND,
+	NFULNL_CFG_CMD_PF_UNBIND,
+};
+
+struct nfulnl_msg_config_cmd {
+	u_int8_t	command;	/* nfulnl_msg_config_cmds */
+} __attribute__ ((packed));
+
+struct nfulnl_msg_config_mode {
+	__be32		copy_range;
+	u_int8_t	copy_mode;
+	u_int8_t	_pad;
+} __attribute__ ((packed));
+
+enum nfulnl_attr_config {
+	NFULA_CFG_UNSPEC,
+	NFULA_CFG_CMD,			/* nfulnl_msg_config_cmd */
+	NFULA_CFG_MODE,			/* nfulnl_msg_config_mode */
+	NFULA_CFG_NLBUFSIZ,		/* u_int32_t buffer size */
+	NFULA_CFG_TIMEOUT,		/* u_int32_t in 1/100 s */
+	NFULA_CFG_QTHRESH,		/* u_int32_t */
+	NFULA_CFG_FLAGS,		/* u_int16_t */
+	__NFULA_CFG_MAX
+};
+#define NFULA_CFG_MAX (__NFULA_CFG_MAX -1)
+
+#define NFULNL_COPY_NONE	0x00
+#define NFULNL_COPY_META	0x01
+#define NFULNL_COPY_PACKET	0x02
+
+#define NFULNL_CFG_F_SEQ	0x0001
+#define NFULNL_CFG_F_SEQ_GLOBAL	0x0002
+
+#endif /* _NFNETLINK_LOG_H */

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: netfilter support in libnl
  2007-09-03  5:09 netfilter support in libnl Philip Craig
                   ` (2 preceding siblings ...)
  2007-09-03  5:12 ` [PATCH 3/3] libnl: add netfilter log support Philip Craig
@ 2007-09-03  9:30 ` Patrick McHardy
  2007-09-03  9:59   ` Thomas Graf
  3 siblings, 1 reply; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03  9:30 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

Philip Craig wrote:
> There have been comments in the past on this list about using libnl
> for the netfilter netlink support, and since I would like to use a
> common library api for all the netlink interfaces, I decided to give
> it a go.  These patches are the result of that.
> 
> Any comments welcome, particularly about whether this is still
> something that is desired.


Very nice, I think its still desirable to avoid having to
use multiple pretty similar libraries in case you also need
to use other netlink subsystems.

And I think LGPL is a much better choice than GPL for stuff
like this.

If you want to merge this upstream, you should probably CC
Thomas.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
@ 2007-09-03  9:50   ` Patrick McHardy
  2007-09-03 10:01     ` Thomas Graf
  2007-09-04  1:18     ` Philip Craig
  2007-09-03 10:09   ` Thomas Graf
  2007-09-04 11:36   ` Thomas Graf
  2 siblings, 2 replies; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03  9:50 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

Philip Craig wrote:
> +#define NFA_NEST(skb, type) \
> +({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
> +	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
> +	__start;  })


The latest libnetfilter_conntrack version doesn't send the NFA_NEST
bit to the kernel anymore since we intend to get rid of it, at
least on the receive side. Please change this to not send it here
as well.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: netfilter support in libnl
  2007-09-03  9:30 ` netfilter support in libnl Patrick McHardy
@ 2007-09-03  9:59   ` Thomas Graf
  2007-09-03 10:05     ` Patrick McHardy
  0 siblings, 1 reply; 22+ messages in thread
From: Thomas Graf @ 2007-09-03  9:59 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Philip Craig

* Patrick McHardy <kaber@trash.net> 2007-09-03 11:30
> Philip Craig wrote:
> > There have been comments in the past on this list about using libnl
> > for the netfilter netlink support, and since I would like to use a
> > common library api for all the netlink interfaces, I decided to give
> > it a go.  These patches are the result of that.
> > 
> > Any comments welcome, particularly about whether this is still
> > something that is desired.
> 
> 
> Very nice, I think its still desirable to avoid having to
> use multiple pretty similar libraries in case you also need
> to use other netlink subsystems.

I agree, it would be great if we could get rid of the almost
duplicate effort of nfnl attributes etc.

> And I think LGPL is a much better choice than GPL for stuff
> like this.

This is my fault, I intedend to use LGPL as stated in the COPYING
for some brain dead reason have been copying GPL headers from file
to file. I'm fixing this up right now.

> If you want to merge this upstream, you should probably CC
> Thomas.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03  9:50   ` Patrick McHardy
@ 2007-09-03 10:01     ` Thomas Graf
  2007-09-03 10:06       ` Patrick McHardy
  2007-09-04  1:18     ` Philip Craig
  1 sibling, 1 reply; 22+ messages in thread
From: Thomas Graf @ 2007-09-03 10:01 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Philip Craig

* Patrick McHardy <kaber@trash.net> 2007-09-03 11:50
> Philip Craig wrote:
> > +#define NFA_NEST(skb, type) \
> > +({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
> > +	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
> > +	__start;  })
> 
> 
> The latest libnetfilter_conntrack version doesn't send the NFA_NEST
> bit to the kernel anymore since we intend to get rid of it, at
> least on the receive side. Please change this to not send it here
> as well.

Great, so basically the attribute interfaces could be merged on the
sending side.

I think I can live with checking for the bit on the receiving side,
the only problem it could cause is when the attribute type is used
to encode information such as a port number. So if we are willing
to take that risk the receiving path could be merged as well.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: netfilter support in libnl
  2007-09-03  9:59   ` Thomas Graf
@ 2007-09-03 10:05     ` Patrick McHardy
  2007-09-03 10:16       ` Thomas Graf
  0 siblings, 1 reply; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03 10:05 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Netfilter Developer Mailing List, Philip Craig

Thomas Graf wrote:
> * Patrick McHardy <kaber@trash.net> 2007-09-03 11:30
> 
>>And I think LGPL is a much better choice than GPL for stuff
>>like this.
> 
> 
> This is my fault, I intedend to use LGPL as stated in the COPYING
> for some brain dead reason have been copying GPL headers from file
> to file. I'm fixing this up right now.


I was actually under the impression that it already uses LGPL :)

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 10:01     ` Thomas Graf
@ 2007-09-03 10:06       ` Patrick McHardy
  2007-09-03 10:15         ` Thomas Graf
  0 siblings, 1 reply; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03 10:06 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Netfilter Developer Mailing List, Philip Craig

Thomas Graf wrote:
> * Patrick McHardy <kaber@trash.net> 2007-09-03 11:50
> 
>>Philip Craig wrote:
>>
>>>+#define NFA_NEST(skb, type) \
>>>+({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
>>>+	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
>>>+	__start;  })
>>
>>
>>The latest libnetfilter_conntrack version doesn't send the NFA_NEST
>>bit to the kernel anymore since we intend to get rid of it, at
>>least on the receive side. Please change this to not send it here
>>as well.
> 
> 
> Great, so basically the attribute interfaces could be merged on the
> sending side.


Yes, hopefully. We're using big endian for numeric values, but that
shouldn't be a problem I think.

> I think I can live with checking for the bit on the receiving side,
> the only problem it could cause is when the attribute type is used
> to encode information such as a port number. So if we are willing
> to take that risk the receiving path could be merged as well.


That kind of information should be stored as attribute value, no?

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
  2007-09-03  9:50   ` Patrick McHardy
@ 2007-09-03 10:09   ` Thomas Graf
  2007-09-04  2:12     ` Philip Craig
  2007-09-04 11:36   ` Thomas Graf
  2 siblings, 1 reply; 22+ messages in thread
From: Thomas Graf @ 2007-09-03 10:09 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

* Philip Craig <philipc@snapgear.com> 2007-09-03 15:11
> Index: libnl/lib/attr.c
> ===================================================================
> --- libnl.orig/lib/attr.c	2007-09-03 14:24:29.000000000 +1000
> +++ libnl/lib/attr.c	2007-09-03 14:24:45.000000000 +1000
> @@ -261,7 +261,8 @@ int nla_parse(struct nlattr *tb[], int m
>  	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
>  
>  	nla_for_each_attr(nla, head, len, rem) {
> -		uint16_t type = nla->nla_type;
> +		/* Ignore NFNL_NFA_NEST bit, hope nothing else uses it */
> +		uint16_t type = nla->nla_type & 0x7fff;

I wonder if it is useful to make this behaviour conditional so that
the netfilter subsystem could enable this for backwards compatibility
while other subsystems won't be affected.

It will offend a few people but it's still pre 1.0 and we can break
the API. I'd rather get this right now than having to life with
side effects for a long time.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 10:06       ` Patrick McHardy
@ 2007-09-03 10:15         ` Thomas Graf
  2007-09-03 10:53           ` Patrick McHardy
  0 siblings, 1 reply; 22+ messages in thread
From: Thomas Graf @ 2007-09-03 10:15 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Philip Craig

* Patrick McHardy <kaber@trash.net> 2007-09-03 12:06
> Thomas Graf wrote:
> > I think I can live with checking for the bit on the receiving side,
> > the only problem it could cause is when the attribute type is used
> > to encode information such as a port number. So if we are willing
> > to take that risk the receiving path could be merged as well.
> 
> 
> That kind of information should be stored as attribute value, no?

Yes it should but we've already got code like this:
for (i = 0; i < cnt; i++)
        nla_put(skb, i, ....);

It's only a matter of time until someone comes up with
something like this:
nla_put(skb, dstport, sizeof(port_info), &port_info);

Maybe we could put a WARN_ON(bit_set) in nla_put() on kernel side
to make sure such behaviour is not being introduced by accident.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: netfilter support in libnl
  2007-09-03 10:05     ` Patrick McHardy
@ 2007-09-03 10:16       ` Thomas Graf
  0 siblings, 0 replies; 22+ messages in thread
From: Thomas Graf @ 2007-09-03 10:16 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Philip Craig

* Patrick McHardy <kaber@trash.net> 2007-09-03 12:05
> Thomas Graf wrote:
> > * Patrick McHardy <kaber@trash.net> 2007-09-03 11:30
> > 
> >>And I think LGPL is a much better choice than GPL for stuff
> >>like this.
> > 
> > 
> > This is my fault, I intedend to use LGPL as stated in the COPYING
> > for some brain dead reason have been copying GPL headers from file
> > to file. I'm fixing this up right now.
> 
> 
> I was actually under the impression that it already uses LGPL :)

SVN head is LGPL, the latest release (very old) is still GPL :-)

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 10:15         ` Thomas Graf
@ 2007-09-03 10:53           ` Patrick McHardy
  2007-09-03 11:03             ` Thomas Graf
  0 siblings, 1 reply; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03 10:53 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Netfilter Developer Mailing List, Philip Craig

Thomas Graf wrote:
> * Patrick McHardy <kaber@trash.net> 2007-09-03 12:06
> 
>>Thomas Graf wrote:
>>
>>>I think I can live with checking for the bit on the receiving side,
>>>the only problem it could cause is when the attribute type is used
>>>to encode information such as a port number. So if we are willing
>>>to take that risk the receiving path could be merged as well.
>>
>>
>>That kind of information should be stored as attribute value, no?
> 
> 
> Yes it should but we've already got code like this:
> for (i = 0; i < cnt; i++)
>         nla_put(skb, i, ....);


That looks pretty much like a list of attributes, but
using different attribute types. Just out of interest:
do you have a pointer to code doing this?


> It's only a matter of time until someone comes up with
> something like this:
> nla_put(skb, dstport, sizeof(port_info), &port_info);
> 
> Maybe we could put a WARN_ON(bit_set) in nla_put() on kernel side
> to make sure such behaviour is not being introduced by accident.


That sounds like a good idea. Anyone doing this is looking
for trouble since array-based attribute parsing would require
at least 2^31 elements :)

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 10:53           ` Patrick McHardy
@ 2007-09-03 11:03             ` Thomas Graf
  2007-09-03 11:13               ` Patrick McHardy
  0 siblings, 1 reply; 22+ messages in thread
From: Thomas Graf @ 2007-09-03 11:03 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List, Philip Craig

* Patrick McHardy <kaber@trash.net> 2007-09-03 12:53
> That looks pretty much like a list of attributes, but
> using different attribute types. Just out of interest:
> do you have a pointer to code doing this?

Yes, see tcf_action_dump().

OTOH, netlabel is making heavy use of attribute lists but
the type is constant to allow for automatic policy validation.
I think this is the proper way of generating lists.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 11:03             ` Thomas Graf
@ 2007-09-03 11:13               ` Patrick McHardy
  0 siblings, 0 replies; 22+ messages in thread
From: Patrick McHardy @ 2007-09-03 11:13 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Netfilter Developer Mailing List, Philip Craig

Thomas Graf wrote:
> * Patrick McHardy <kaber@trash.net> 2007-09-03 12:53
> 
>>That looks pretty much like a list of attributes, but
>>using different attribute types. Just out of interest:
>>do you have a pointer to code doing this?
> 
> 
> Yes, see tcf_action_dump().


Right, I remeber, the entire "action order" thing. Thats a
horrible hack IMO, probably best to convert it to real lists
if possible.

> OTOH, netlabel is making heavy use of attribute lists but
> the type is constant to allow for automatic policy validation.
> I think this is the proper way of generating lists.


Fully agreed, thats also what I did in the 8021q netlink
interface.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03  9:50   ` Patrick McHardy
  2007-09-03 10:01     ` Thomas Graf
@ 2007-09-04  1:18     ` Philip Craig
  1 sibling, 0 replies; 22+ messages in thread
From: Philip Craig @ 2007-09-04  1:18 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

Patrick McHardy wrote:
> Philip Craig wrote:
>> +#define NFA_NEST(skb, type) \
>> +({	struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
>> +	NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
>> +	__start;  })
> 
> 
> The latest libnetfilter_conntrack version doesn't send the NFA_NEST
> bit to the kernel anymore since we intend to get rid of it, at
> least on the receive side. Please change this to not send it here
> as well.

This is just a copy of the kernel header (maybe an old one though).
I haven't added send support to libnl yet, but when I do I'll be
sure not to use this.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03 10:09   ` Thomas Graf
@ 2007-09-04  2:12     ` Philip Craig
  2007-09-04  9:39       ` Thomas Graf
  0 siblings, 1 reply; 22+ messages in thread
From: Philip Craig @ 2007-09-04  2:12 UTC (permalink / raw)
  To: Thomas Graf; +Cc: Netfilter Developer Mailing List

Thomas Graf wrote:
> * Philip Craig <philipc@snapgear.com> 2007-09-03 15:11
>> Index: libnl/lib/attr.c
>> ===================================================================
>> --- libnl.orig/lib/attr.c	2007-09-03 14:24:29.000000000 +1000
>> +++ libnl/lib/attr.c	2007-09-03 14:24:45.000000000 +1000
>> @@ -261,7 +261,8 @@ int nla_parse(struct nlattr *tb[], int m
>>  	memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
>>  
>>  	nla_for_each_attr(nla, head, len, rem) {
>> -		uint16_t type = nla->nla_type;
>> +		/* Ignore NFNL_NFA_NEST bit, hope nothing else uses it */
>> +		uint16_t type = nla->nla_type & 0x7fff;
> 
> I wonder if it is useful to make this behaviour conditional so that
> the netfilter subsystem could enable this for backwards compatibility
> while other subsystems won't be affected.
> 
> It will offend a few people but it's still pre 1.0 and we can break
> the API. I'd rather get this right now than having to life with
> side effects for a long time.

Yes it would be nice to only do it for subsystems that need it.
The only place I can see in the existing arguments for nla_parse()
is to overload the meaning of the unused policy[0]. The other options
are to change nla_parse() or add nla_parse_masked(). Any preference?

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-04  2:12     ` Philip Craig
@ 2007-09-04  9:39       ` Thomas Graf
  0 siblings, 0 replies; 22+ messages in thread
From: Thomas Graf @ 2007-09-04  9:39 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

* Philip Craig <philipc@snapgear.com> 2007-09-04 12:12
> Yes it would be nice to only do it for subsystems that need it.
> The only place I can see in the existing arguments for nla_parse()
> is to overload the meaning of the unused policy[0]. The other options
> are to change nla_parse() or add nla_parse_masked(). Any preference?

Actually after discussing this with Patrick a bit I think we can safely
assume that this bit is never set in any other subsystem so just leave
it as-is.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/3] libnl: add netfilter support
  2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
  2007-09-03  9:50   ` Patrick McHardy
  2007-09-03 10:09   ` Thomas Graf
@ 2007-09-04 11:36   ` Thomas Graf
  2 siblings, 0 replies; 22+ messages in thread
From: Thomas Graf @ 2007-09-04 11:36 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

* Philip Craig <philipc@snapgear.com> 2007-09-03 15:11
> 

> ---
>  include/linux/netfilter/nfnetlink.h |  106 +++++++++++++++
>  include/netlink/netfilter/nfnl.h    |   44 ++++++
>  include/netlink/netlink.h           |    1 
>  lib/Makefile                        |    2 
>  lib/attr.c                          |    3 
>  lib/netfilter/nfnl.c                |  246 ++++++++++++++++++++++++++++++++++++
>  6 files changed, 401 insertions(+), 1 deletion(-)

I've applied this patch with some minor modification which is the
removal of most of the macros from include/linux/netfilter/nfnetlink.h
for the purpose of making sure people use the generic alternatives.

Thanks a lot Philip!

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/3] libnl: add netfilter conntrack support
  2007-09-03  5:11 ` [PATCH 2/3] libnl: add netfilter conntrack support Philip Craig
@ 2007-09-04 16:45   ` Thomas Graf
  0 siblings, 0 replies; 22+ messages in thread
From: Thomas Graf @ 2007-09-04 16:45 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

* Philip Craig <philipc@snapgear.com> 2007-09-03 15:11
> 

> ---
>  include/linux/netfilter/nfnetlink_conntrack.h |  140 +++++
>  include/netlink-types.h                       |   44 +
>  include/netlink/addr.h                        |    1 
>  include/netlink/netfilter/ct.h                |  115 ++++
>  include/netlink/utils.h                       |    4 
>  lib/addr.c                                    |   28 +
>  lib/netfilter/ct.c                            |  422 ++++++++++++++++
>  lib/netfilter/ct_obj.c                        |  667 ++++++++++++++++++++++++++
>  lib/utils.c                                   |   36 +
>  src/Makefile                                  |    6 
>  src/f_ct.c                                    |  153 +++++
>  src/nf-ct-dump.c                              |   88 +++
>  src/nf-monitor.c                              |  115 ++++
>  13 files changed, 1818 insertions(+), 1 deletion(-)

Patch applied, thanks!

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 3/3] libnl: add netfilter log support
  2007-09-03  5:12 ` [PATCH 3/3] libnl: add netfilter log support Philip Craig
@ 2007-09-04 16:48   ` Thomas Graf
  0 siblings, 0 replies; 22+ messages in thread
From: Thomas Graf @ 2007-09-04 16:48 UTC (permalink / raw)
  To: Philip Craig; +Cc: Netfilter Developer Mailing List

* Philip Craig <philipc@snapgear.com> 2007-09-03 15:12
> 

> ---
>  include/linux/netfilter/nfnetlink_log.h |   96 +++++++
>  include/netlink-types.h                 |   22 +
>  include/netlink/netfilter/log.h         |  105 +++++++
>  lib/netfilter/log.c                     |  349 ++++++++++++++++++++++++++
>  lib/netfilter/log_obj.c                 |  425 ++++++++++++++++++++++++++++++++
>  src/nf-log.c                            |  142 ++++++++++
>  6 files changed, 1139 insertions(+)

Also applied, thanks for all this great work!

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2007-09-04 16:48 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-03  5:09 netfilter support in libnl Philip Craig
2007-09-03  5:11 ` [PATCH 1/3] libnl: add netfilter support Philip Craig
2007-09-03  9:50   ` Patrick McHardy
2007-09-03 10:01     ` Thomas Graf
2007-09-03 10:06       ` Patrick McHardy
2007-09-03 10:15         ` Thomas Graf
2007-09-03 10:53           ` Patrick McHardy
2007-09-03 11:03             ` Thomas Graf
2007-09-03 11:13               ` Patrick McHardy
2007-09-04  1:18     ` Philip Craig
2007-09-03 10:09   ` Thomas Graf
2007-09-04  2:12     ` Philip Craig
2007-09-04  9:39       ` Thomas Graf
2007-09-04 11:36   ` Thomas Graf
2007-09-03  5:11 ` [PATCH 2/3] libnl: add netfilter conntrack support Philip Craig
2007-09-04 16:45   ` Thomas Graf
2007-09-03  5:12 ` [PATCH 3/3] libnl: add netfilter log support Philip Craig
2007-09-04 16:48   ` Thomas Graf
2007-09-03  9:30 ` netfilter support in libnl Patrick McHardy
2007-09-03  9:59   ` Thomas Graf
2007-09-03 10:05     ` Patrick McHardy
2007-09-03 10:16       ` Thomas Graf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).