summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--EXAMPLES23
-rw-r--r--Makefile14
-rw-r--r--TODO27
-rw-r--r--extensions/Makefile12
-rw-r--r--extensions/libct_proto_tcp.c67
-rw-r--r--extensions/libct_proto_udp.c67
-rw-r--r--include/libct_proto.h33
-rw-r--r--include/libctnetlink.h72
-rw-r--r--include/libnfnetlink.h50
-rw-r--r--include/linux_list.h725
-rw-r--r--src/conntrack.c472
-rw-r--r--src/libct.c486
12 files changed, 2048 insertions, 0 deletions
diff --git a/EXAMPLES b/EXAMPLES
new file mode 100644
index 0000000..16e192e
--- /dev/null
+++ b/EXAMPLES
@@ -0,0 +1,23 @@
+o List conntrack table
+
+ $ conntrack -L conntrack
+
+o Get a conntrack
+
+ $ conntrack -G conntrack --reply-src 85.136.102.173 \
+ --reply-dst 66.111.58.51 -p tcp --reply-port-src 44843 \
+ --reply-port-dst 993 -i 13
+
+o Delete a conntrack
+
+ $ conntrack -D conntrack --reply-src 85.136.102.173 \
+ --reply-dst 66.111.58.51 -p tcp --reply-port-src 44843 \
+ --reply-port-dst 993 -i 13
+
+o Create a conntrack
+
+ $ conntrack -I conntrack --orig-src 1.1.1.4 --orig-dst 2.2.2.3 \
+ --reply-src 2.2.2.3 --reply-dst 1.1.1.4 -p tcp --orig-port-src 20 \
+ --orig-port-dst 10 --reply-port-src 10 --orig-port-dst 20 \
+ -u ASSURED -t 100
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..03edebc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+LINKOPTS=-ldl -lnfnetlink -lctnetlink -rdynamic
+KERNELDIR=/lib/modules/$(shell uname -r)/build/include/
+CFLAGS=-I${KERNELDIR} -Iinclude/ -g
+
+default:
+ ${CC} -c ${CFLAGS} src/conntrack.c -o src/conntrack.o
+ ${CC} -c ${CFLAGS} src/libct.c -o src/libct.o
+ ${CC} ${LINKOPTS} src/conntrack.o src/libct.o -o conntrack
+ ${MAKE} -C extensions/
+
+clean:
+ rm -rf src/*.o conntrack
+ ${MAKE} clean -C extensions/
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..12af6af
--- /dev/null
+++ b/TODO
@@ -0,0 +1,27 @@
+user space tool
+---------------
+General:
+[ ] Proper Makefiles
+[ ] Modify Event Display (-E conntrack).
+
+Extensions:
+[ ] ICMP library
+[ ] finish TCP: help and protocol specific stuff: --state, etc...
+[ ] finish UDP: help
+
+nfnetlink_conntrack:
+--------------------
+
+Now:
+[ ] Error handling (nlerrmsg)
+[X] Use id's to identify conntracks
+[ ] Split NEW and CHANGE
+[ ] Split DUMP and GET
+[ ] Kill Change API. Move locks to ip_conntrack_[protocol|helper].
+[ ] implement conntrack FLUSH
+[ ] Per CPU id. Currently racy.
+
+Later:
+[ ] convert CTA_SOMETHING-1 to CTA_SOMETHING, annoying!
+[ ] NAT handlings
+[ ] Expectations
diff --git a/extensions/Makefile b/extensions/Makefile
new file mode 100644
index 0000000..e23ed90
--- /dev/null
+++ b/extensions/Makefile
@@ -0,0 +1,12 @@
+CC=gcc
+
+all:
+ ${CC} -fPIC -Wall -g -c libct_proto_tcp.c
+ ${CC} -g -shared -Wl,-soname,libct_proto_tcp.so.0 -o libct_proto_tcp.so.0.0 libct_proto_tcp.o -lc
+ ln -sf libct_proto_tcp.so.0.0 libct_proto_tcp.so
+
+ ${CC} -fPIC -Wall -g -c libct_proto_udp.c
+ ${CC} -g -shared -Wl,-soname,libct_proto_udp.so.0 -o libct_proto_udp.so.0.0 libct_proto_udp.o -lc
+ ln -sf libct_proto_udp.so.0.0 libct_proto_udp.so
+clean:
+ rm -rf *.so *.so.* *.o
diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c
new file mode 100644
index 0000000..521a170
--- /dev/null
+++ b/extensions/libct_proto_tcp.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* For htons */
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include "../include/libct_proto.h"
+
+static struct option opts[] = {
+ {"orig-port-src", 1, 0, '1'},
+ {"orig-port-dst", 1, 0, '2'},
+ {"reply-port-src", 1, 0, '3'},
+ {"reply-port-dst", 1, 0, '4'},
+ {0, 0, 0, 0}
+};
+
+int parse(char c, char *argv[],
+ struct ip_conntrack_tuple *orig,
+ struct ip_conntrack_tuple *reply)
+{
+ switch(c) {
+ case '1':
+ if (optarg)
+ orig->src.u.tcp.port = htons(atoi(optarg));
+ break;
+ case '2':
+ if (optarg)
+ orig->dst.u.tcp.port = htons(atoi(optarg));
+ break;
+ case '3':
+ if (optarg)
+ reply->src.u.tcp.port = htons(atoi(optarg));
+ break;
+ case '4':
+ if (optarg)
+ reply->dst.u.tcp.port = htons(atoi(optarg));
+ break;
+ }
+ return 1;
+}
+
+void print(struct ip_conntrack_tuple *t)
+{
+ printf("sport=%d dport=%d ", ntohs(t->src.u.tcp.port),
+ ntohs(t->dst.u.tcp.port));
+}
+
+static struct ctproto_handler tcp = {
+ .name = "tcp",
+ .protonum = 6,
+ .parse = parse,
+ .print = print,
+ .opts = opts
+};
+
+void __attribute__ ((constructor)) init(void);
+void __attribute__ ((destructor)) fini(void);
+
+void init(void)
+{
+ register_proto(&tcp);
+}
+
+void fini(void)
+{
+ unregister_proto(&tcp);
+}
diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c
new file mode 100644
index 0000000..8022913
--- /dev/null
+++ b/extensions/libct_proto_udp.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* For htons */
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include "../include/libct_proto.h"
+
+static struct option opts[] = {
+ {"orig-port-src", 1, 0, '1'},
+ {"orig-port-dst", 1, 0, '2'},
+ {"reply-port-src", 1, 0, '3'},
+ {"reply-port-dst", 1, 0, '4'},
+ {0, 0, 0, 0}
+};
+
+int parse(char c, char *argv[],
+ struct ip_conntrack_tuple *orig,
+ struct ip_conntrack_tuple *reply)
+{
+ switch(c) {
+ case '1':
+ if (optarg)
+ orig->src.u.udp.port = htons(atoi(optarg));
+ break;
+ case '2':
+ if (optarg)
+ orig->dst.u.udp.port = htons(atoi(optarg));
+ break;
+ case '3':
+ if (optarg)
+ reply->src.u.udp.port = htons(atoi(optarg));
+ break;
+ case '4':
+ if (optarg)
+ reply->dst.u.udp.port = htons(atoi(optarg));
+ break;
+ }
+ return 1;
+}
+
+void print(struct ip_conntrack_tuple *t)
+{
+ printf("sport=%d dport=%d ", ntohs(t->src.u.udp.port),
+ ntohs(t->dst.u.udp.port));
+}
+
+static struct ctproto_handler udp = {
+ .name = "udp",
+ .protonum = 17,
+ .parse = parse,
+ .print = print,
+ .opts = opts
+};
+
+void __attribute__ ((constructor)) init(void);
+void __attribute__ ((destructor)) fini(void);
+
+void init(void)
+{
+ register_proto(&udp);
+}
+
+void fini(void)
+{
+ unregister_proto(&udp);
+}
diff --git a/include/libct_proto.h b/include/libct_proto.h
new file mode 100644
index 0000000..410a812
--- /dev/null
+++ b/include/libct_proto.h
@@ -0,0 +1,33 @@
+#ifndef _LIBCT_PROTO_H
+#define _LIBCT_PROTO_H
+
+#include "linux_list.h"
+#include <getopt.h>
+
+struct ctproto_handler {
+ struct list_head head;
+
+ char *name;
+ u_int16_t protonum;
+
+ int (*parse)(char c, char *argv[], struct ip_conntrack_tuple *orig,
+ struct ip_conntrack_tuple *reply);
+ void (*print)(struct ip_conntrack_tuple *t);
+
+ struct option *opts;
+
+ unsigned int option_offset;
+};
+
+extern void register_proto(struct ctproto_handler *h);
+extern void unregister_proto(struct ctproto_handler *h);
+
+extern struct ctproto_handler *findproto(char *name);
+
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+
+#endif
diff --git a/include/libctnetlink.h b/include/libctnetlink.h
new file mode 100644
index 0000000..2cba075
--- /dev/null
+++ b/include/libctnetlink.h
@@ -0,0 +1,72 @@
+/* libctnetlink.h: Header file for the Connection Tracking library.
+ *
+ * Jay Schulist <jschlst@samba.org>, Copyright (c) 2001.
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __LIBCTNETLINK_H
+#define __LIBCTNETLINK_H
+
+#include <netinet/in.h>
+#include <asm/types.h>
+#include <linux/if.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_netlink.h>
+#include "libnfnetlink.h"
+
+#define CTNL_BUFFSIZE 8192
+
+struct ctnl_msg_handler {
+ int type;
+ int (*handler)(struct sockaddr_nl *, struct nlmsghdr *, void *arg);
+};
+
+struct ctnl_handle {
+ struct nfnl_handle nfnlh;
+ struct ctnl_msg_handler *handler[IPCTNL_MSG_COUNT];
+};
+
+extern int ctnl_open(struct ctnl_handle *cth, unsigned subscriptions);
+extern int ctnl_close(struct ctnl_handle *cth);
+extern int ctnl_unregister_handler(struct ctnl_handle *cth, int type);
+extern int ctnl_register_handler(struct ctnl_handle *cth,
+ struct ctnl_msg_handler *hndlr);
+extern int ctnl_get_conntrack(struct ctnl_handle *cth,
+ struct ip_conntrack_tuple *tuple,
+ enum ctattr_type_t t,
+ unsigned long id);
+extern int ctnl_del_conntrack(struct ctnl_handle *cth,
+ struct ip_conntrack_tuple *tuple,
+ enum ctattr_type_t t,
+ unsigned long id);
+extern int ctnl_list_conntrack(struct ctnl_handle *cth, int family);
+
+extern int ctnl_list_expect(struct ctnl_handle *cth, int family);
+extern int ctnl_del_expect(struct ctnl_handle *cth,
+ struct ip_conntrack_tuple *t);
+
+#if 0
+extern int ctnl_listen(struct ctnl_handle *ctnl,
+ int (*handler)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *jarg);
+extern int ctnl_talk(struct ctnl_handle *ctnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *jarg);
+extern int ctnl_dump_request(struct ctnl_handle *cth, int type, void *req,
+ int len);
+extern int ctnl_dump_filter(struct ctnl_handle *cth,
+ int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+ void *arg1,
+ int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+ void *arg2);
+#endif
+
+extern int ctnl_send(struct ctnl_handle *cth, struct nlmsghdr *n);
+extern int ctnl_wilddump_request(struct ctnl_handle *cth, int family, int type);
+
+#endif /* __LIBCTNETLINK_H */
diff --git a/include/libnfnetlink.h b/include/libnfnetlink.h
new file mode 100644
index 0000000..944e607
--- /dev/null
+++ b/include/libnfnetlink.h
@@ -0,0 +1,50 @@
+/* libnfnetlink.h: Header file for generic netfilter netlink interface
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ */
+
+#ifndef __LIBNFNETLINK_H
+#define __LIBNFNETLINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+
+#define NFNL_BUFFSIZE 8192
+
+struct nfnl_handle {
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ u_int8_t subsys_id;
+ u_int32_t seq;
+ u_int32_t dump;
+};
+
+/* get a new library handle */
+extern int nfnl_open(struct nfnl_handle *, u_int8_t, unsigned int);
+extern int nfnl_close(struct nfnl_handle *);
+extern int nfnl_send(struct nfnl_handle *, struct nlmsghdr *);
+
+
+extern void nfnl_fill_hdr(struct nfnl_handle *, struct nlmsghdr *, int,
+ u_int8_t, u_int16_t, u_int16_t);
+
+extern int nfnl_listen(struct nfnl_handle *,
+ int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *);
+
+extern int nfnl_talk(struct nfnl_handle *, struct nlmsghdr *, pid_t,
+ unsigned, struct nlmsghdr *,
+ int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *),
+ void *);
+
+/* nfnl attribute handling functions */
+extern int nfnl_addattr_l(struct nlmsghdr *, int, int, void *, int);
+extern int nfnl_addattr32(struct nlmsghdr *, int, int, u_int32_t);
+extern int nfnl_nfa_addattr_l(struct nfattr *, int, int, void *, int);
+extern int nfnl_nfa_addattr32(struct nfattr *, int, int, u_int32_t);
+extern int nfnl_parse_attr(struct nfattr **, int, struct nfattr *, int);
+
+extern void nfnl_dump_packet(struct nlmsghdr *, int, char *);
+#endif /* __LIBNFNETLINK_H */
diff --git a/include/linux_list.h b/include/linux_list.h
new file mode 100644
index 0000000..57b56d7
--- /dev/null
+++ b/include/linux_list.h
@@ -0,0 +1,725 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * Check at compile time that something is of a particular type.
+ * Always evaluates to 1 so you may use it easily in comparisons.
+ */
+#define typecheck(type,x) \
+({ type __dummy; \
+ typeof(x) __dummy2; \
+ (void)(&__dummy == &__dummy2); \
+ 1; \
+})
+
+#define prefetch(x) 1
+
+/* empty define to make this work in userspace -HW */
+#ifndef smp_wmb
+#define smp_wmb()
+#endif
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head * new,
+ struct list_head * prev, struct list_head * next)
+{
+ new->next = next;
+ new->prev = prev;
+ smp_wmb();
+ next->prev = new;
+ prev->next = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+ __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+ struct list_head *head)
+{
+ __list_add_rcu(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry. Instead, either synchronize_kernel()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is
+ * empty _and_ checks that no other CPU might be
+ * in the process of still modifying either member
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ *
+ * @head: the list to test.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+ struct list_head *next = head->next;
+ return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ * list_for_each_entry_continue
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - iterate over list of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_rcu - iterate over an rcu-protected list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * list_for_each_safe_rcu - iterate over an rcu-protected list safe
+ * against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * list_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * list_for_each_continue_rcu - iterate over an rcu-protected list
+ * continuing after existing point.
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (n->pprev) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+#define hlist_del_rcu_init hlist_del_init
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+
+/**
+ * hlist_add_head_rcu - adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry(), but only if smp_read_barrier_depends()
+ * is used to prevent memory-consistency problems on Alpha CPUs.
+ * Regardless of the type of CPU, the list-traversal primitive
+ * must be guarded by rcu_read_lock().
+ *
+ * OK, so why don't we have an hlist_for_each_entry_rcu()???
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+ struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ n->pprev = &h->first;
+ smp_wmb();
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0; }) )
+
+#endif
diff --git a/src/conntrack.c b/src/conntrack.c
new file mode 100644
index 0000000..dfb3849
--- /dev/null
+++ b/src/conntrack.c
@@ -0,0 +1,472 @@
+/*
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Note:
+ * Yes, portions of this code has been stolen from iptables ;)
+ * Special thanks to the the Netfilter Core Team.
+ * Thanks to Javier de Miguel Rodriguez <jmiguel at talika.eii.us.es>
+ * for introducing me to advanced firewalling stuff.
+ *
+ * --pablo 13/04/2005
+ */
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include "libctnetlink.h"
+#include "libnfnetlink.h"
+#include "linux_list.h"
+#include "libct_proto.h"
+
+#define PROGNAME "conntrack"
+#define VERSION "0.12"
+
+#if 0
+#define DEBUGP printf
+#else
+#define DEBUGP
+#endif
+
+enum action {
+ CT_LIST_BIT = 0,
+ CT_LIST = (1 << CT_LIST_BIT),
+
+ CT_CREATE_BIT = 1,
+ CT_CREATE = (1 << CT_CREATE_BIT),
+
+ CT_DELETE_BIT = 2,
+ CT_DELETE = (1 << CT_DELETE_BIT),
+
+ CT_GET_BIT = 3,
+ CT_GET = (1 << CT_GET_BIT),
+
+ CT_FLUSH_BIT = 4,
+ CT_FLUSH = (1 << CT_FLUSH_BIT),
+
+ CT_EVENT_BIT = 5,
+ CT_EVENT = (1 << CT_EVENT_BIT)
+};
+#define NUMBER_OF_CMD 6
+
+enum options {
+ CT_OPT_ORIG_SRC_BIT = 0,
+ CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT),
+
+ CT_OPT_ORIG_DST_BIT = 1,
+ CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT),
+
+ CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST),
+
+ CT_OPT_REPL_SRC_BIT = 2,
+ CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT),
+
+ CT_OPT_REPL_DST_BIT = 3,
+ CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT),
+
+ CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST),
+
+ CT_OPT_PROTO_BIT = 4,
+ CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT),
+
+ CT_OPT_ID_BIT = 5,
+ CT_OPT_ID = (1 << CT_OPT_ID_BIT),
+
+ CT_OPT_TIMEOUT_BIT = 6,
+ CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT),
+
+ CT_OPT_STATUS_BIT = 7,
+ CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT),
+};
+#define NUMBER_OF_OPT 8
+
+static const char optflags[NUMBER_OF_OPT]
+= { 's', 'd', 'r', 'q', 'p', 'i', 't', 'u'};
+
+static struct option original_opts[] = {
+ {"dump", 1, 0, 'L'},
+ {"create", 1, 0, 'I'},
+ {"delete", 1, 0, 'D'},
+ {"get", 1, 0, 'G'},
+ {"flush", 1, 0, 'F'},
+ {"event", 1, 0, 'E'},
+ {"orig-src", 1, 0, 's'},
+ {"orig-dst", 1, 0, 'd'},
+ {"reply-src", 1, 0, 'r'},
+ {"reply-dst", 1, 0, 'q'},
+ {"protonum", 1, 0, 'p'},
+ {"timeout", 1, 0, 't'},
+ {"id", 1, 0, 'i'},
+ {"status", 1, 0, 'u'},
+ {0, 0, 0, 0}
+};
+
+#define OPTION_OFFSET 256
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ * + compulsory
+ * x illegal
+ * optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+ /* -s -d -r -q -p -i -t -u*/
+/*LIST*/ {'x','x','x','x','x','x','x','x'},
+/*CREATE*/ {'+','+','+','+','+','x','+','+'},
+/*DELETE*/ {' ',' ',' ',' ',' ','+','x','x'},
+/*GET*/ {' ',' ',' ',' ','+','+','x','x'},
+/*FLUSH*/ {'x','x','x','x','x','x','x','x'},
+/*EVENT*/ {'x','x','x','x','x','x','x','x'}
+};
+
+LIST_HEAD(proto_list);
+
+char *proto2str[] = {
+ [IPPROTO_TCP] = "tcp",
+ [IPPROTO_UDP] = "udp",
+ [IPPROTO_ICMP] = "icmp",
+ [IPPROTO_SCTP] = "sctp"
+};
+
+enum exittype {
+ OTHER_PROBLEM = 1,
+ PARAMETER_PROBLEM,
+ VERSION_PROBLEM
+};
+
+void
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ PROGNAME, PROGNAME);
+ exit(status);
+}
+
+static void
+exit_error(enum exittype status, char *msg, ...)
+{
+ va_list args;
+
+ /* On error paths, make sure that we don't leak the memory
+ * reserved during options merging */
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", PROGNAME, VERSION);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ exit(status);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+ int i, j, legal = 0;
+
+ /* Check that commands are valid with options. Complicated by the
+ * fact that if an option is legal with *any* command given, it is
+ * legal overall (ie. -z and -l).
+ */
+ for (i = 0; i < NUMBER_OF_OPT; i++) {
+ legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+ for (j = 0; j < NUMBER_OF_CMD; j++) {
+ if (!(command & (1<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == '+')
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply the "
+ "`-%c' option for this "
+ "command\n", optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 'x')
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ exit_error(PARAMETER_PROBLEM, "Illegal option `-%c'"
+ "with this command\n", optflags[i]);
+ }
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ global_option_offset += OPTION_OFFSET;
+ *option_offset = global_option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+ return merge;
+}
+
+void not_implemented_yet()
+{
+ exit_error(OTHER_PROBLEM, "Sorry, not implemented yet :(\n");
+}
+
+unsigned int check_type()
+{
+ unsigned int type = 0;
+
+ if (!optarg)
+ exit_error(PARAMETER_PROBLEM, "must specified `conntrack' or "
+ "`expect'\n");
+
+ if (strncmp("conntrack", optarg, 9) == 0)
+ type = 0;
+ else if (strncmp("expect", optarg, 6) == 0)
+ type = 1;
+ else {
+ exit_error(PARAMETER_PROBLEM, "unknown type `%s'\n", optarg);
+ }
+
+ return type;
+}
+
+void usage(char *prog) {
+printf("Tool to manipulate conntrack and expectations. Version %s\n", VERSION);
+printf("Usage: %s [commands] [options]\n", prog);
+printf("\n");
+printf("Commands:\n");
+printf("-L table List conntrack or expectation table\n");
+printf("-G table [options] Get conntrack or expectation\n");
+printf("-D table [options] Delete conntrack or expectation\n");
+printf("-I table [options] Create a conntrack or expectation\n");
+printf("-E table Show events\n");
+printf("-F table Flush table\n");
+printf("\n");
+printf("Options:\n");
+printf("--orig-src Source address from original direction\n");
+printf("--orig-dst Destination address from original direction\n");
+printf("--reply-src Source addres from reply direction\n");
+printf("--reply-dst Destination address from reply direction\n");
+printf("-p Layer 4 Protocol\n");
+printf("-t Timeout\n");
+printf("-i Conntrack ID\n");
+printf("-u Status\n");
+}
+
+int main(int argc, char *argv[])
+{
+ char c;
+ unsigned int command = 0, options = 0;
+ struct ip_conntrack_tuple orig, reply, *o = NULL, *r = NULL;
+ struct ctproto_handler *h = NULL;
+ union ip_conntrack_proto proto;
+ unsigned long timeout = 0;
+ unsigned int status = 0;
+ unsigned long id = 0;
+ unsigned int type = 0;
+
+ memset(&proto, 0, sizeof(union ip_conntrack_proto));
+ memset(&orig, 0, sizeof(struct ip_conntrack_tuple));
+ memset(&reply, 0, sizeof(struct ip_conntrack_tuple));
+ orig.dst.dir = IP_CT_DIR_ORIGINAL;
+ reply.dst.dir = IP_CT_DIR_REPLY;
+
+ while ((c = getopt_long(argc, argv,
+ "L:I:D:G:E:s:d:r:q:p:i:t:u:", opts, NULL)) != -1) {
+ switch(c) {
+ case 'L':
+ command |= CT_LIST;
+ type = check_type();
+ break;
+ case 'I':
+ command |= CT_CREATE;
+ type = check_type();
+ break;
+ case 'D':
+ command |= CT_DELETE;
+ type = check_type();
+ break;
+ case 'G':
+ command |= CT_GET;
+ type = check_type();
+ break;
+ case 'F':
+ command |= CT_FLUSH;
+ type = check_type();
+ break;
+ case 'E':
+ command |= CT_EVENT;
+ type = check_type();
+ break;
+ case 's':
+ options |= CT_OPT_ORIG_SRC;
+ if (optarg)
+ orig.src.ip = inet_addr(optarg);
+ break;
+ case 'd':
+ options |= CT_OPT_ORIG_DST;
+ if (optarg)
+ orig.dst.ip = inet_addr(optarg);
+ break;
+ case 'r':
+ options |= CT_OPT_REPL_SRC;
+ if (optarg)
+ reply.src.ip = inet_addr(optarg);
+ break;
+ case 'q':
+ options |= CT_OPT_REPL_DST;
+ if (optarg)
+ reply.dst.ip = inet_addr(optarg);
+ break;
+ case 'p':
+ options |= CT_OPT_PROTO;
+ h = findproto(optarg);
+ if (!h)
+ exit_error(PARAMETER_PROBLEM, "proto needed\n");
+ orig.dst.protonum = h->protonum;
+ reply.dst.protonum = h->protonum;
+ opts = merge_options(opts, h->opts,
+ &h->option_offset);
+ break;
+ case 'i':
+ options |= CT_OPT_ID;
+ id = atoi(optarg);
+ break;
+ case 't':
+ options |= CT_OPT_TIMEOUT;
+ if (optarg)
+ timeout = atol(optarg);
+ break;
+ case 'u': {
+ /* FIXME: NAT stuff, later... */
+ if (!optarg)
+ continue;
+
+ options |= CT_OPT_STATUS;
+ /* Just insert confirmed conntracks */
+ status |= IPS_CONFIRMED;
+ if (strncmp("SEEN_REPLY", optarg, strlen("SEEN_REPLY")) == 0)
+ status |= IPS_SEEN_REPLY;
+ else if (strncmp("ASSURED", optarg, strlen("ASSURED")) == 0)
+ status |= IPS_ASSURED;
+ else
+ exit_error(PARAMETER_PROBLEM, "Invalid status"
+ "flag: %s\n", optarg);
+ break;
+ }
+ default:
+ if (h && !h->parse(c - h->option_offset, argv,
+ &orig, &reply))
+ exit_error(PARAMETER_PROBLEM, "parse error\n");
+
+ /* Unknown argument... */
+ if (!h) {
+ usage(argv[0]);
+ exit_error(PARAMETER_PROBLEM, "Missing "
+ "arguments...\n");
+ }
+ break;
+ }
+ }
+
+ generic_opt_check(command, options);
+
+ switch(command) {
+ case CT_LIST:
+ printf("list\n");
+ if (type == 0)
+ dump_conntrack_table();
+ else
+ dump_expect_list();
+ break;
+ case CT_CREATE:
+ printf("create\n");
+ if (type == 0)
+ create_conntrack(&orig, &reply, timeout,
+ &proto, status);
+ else
+ not_implemented_yet();
+ break;
+ case CT_DELETE:
+ printf("delete\n");
+ if (type == 0) {
+ if (options & CT_OPT_ORIG)
+ delete_conntrack(&orig, CTA_ORIG, id);
+ else if (options & CT_OPT_REPL)
+ delete_conntrack(&reply, CTA_RPLY, id);
+ } else
+ not_implemented_yet();
+ break;
+ case CT_GET:
+ printf("get\n");
+ if (type == 0) {
+ if (options & CT_OPT_ORIG)
+ get_conntrack(&orig, CTA_ORIG, id);
+ else if (options & CT_OPT_REPL)
+ get_conntrack(&reply, CTA_RPLY, id);
+ } else
+ not_implemented_yet();
+ break;
+ case CT_FLUSH:
+ not_implemented_yet();
+ break;
+ case CT_EVENT:
+ printf("event\n");
+ if (type == 0)
+ event_conntrack();
+ else
+ /* and surely it won't ever... */
+ not_implemented_yet();
+ default:
+ usage(argv[0]);
+ break;
+ }
+
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+}
diff --git a/src/libct.c b/src/libct.c
new file mode 100644
index 0000000..3828c0c
--- /dev/null
+++ b/src/libct.c
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <getopt.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include "libctnetlink.h"
+#include "libnfnetlink.h"
+#include "linux_list.h"
+#include "libct_proto.h"
+
+#if 0
+#define DEBUGP printf
+#else
+#define DEBUGP
+#endif
+
+extern struct list_head proto_list;
+extern char *proto2str[];
+
+/* Built-in generic proto handler */
+
+/* FIXME: This should die... */
+static int parse(char c, char *argv[],
+ struct ip_conntrack_tuple *orig,
+ struct ip_conntrack_tuple *reply) {
+ return 0;
+}
+/* FIXME: die die too... */
+static void print(struct ip_conntrack_tuple *t) {}
+
+static struct ctproto_handler generic_handler = {
+ .name = "generic",
+ .protonum = 0,
+ .parse = parse,
+ .print = print,
+ .opts = NULL
+};
+
+static int handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg)
+{
+ struct nfgenmsg *nfmsg;
+ struct nfattr *nfa;
+ int min_len = 0;
+ struct ctproto_handler *h = NULL;
+
+ DEBUGP("netlink header\n");
+ DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n",
+ nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
+ nlh->nlmsg_seq, nlh->nlmsg_pid);
+
+ nfmsg = NLMSG_DATA(nlh);
+ DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family);
+
+ min_len = sizeof(struct nfgenmsg);
+ if (nlh->nlmsg_len < min_len)
+ return -EINVAL;
+
+ DEBUGP("size:%d\n", nlh->nlmsg_len);
+
+ while (nlh->nlmsg_len > min_len) {
+ struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+ struct ip_conntrack_tuple *orig, *reply;
+ unsigned long *status, *timeout;
+ struct cta_proto *proto;
+ unsigned long *id;
+
+ while (NFA_OK(attr, attrlen)) {
+ switch(attr->nfa_type) {
+ case CTA_ORIG:
+ orig = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(orig->src.ip),
+ NIPQUAD(orig->dst.ip));
+ h = findproto(proto2str[orig->dst.protonum]);
+ if (h)
+ h->print(orig);
+ break;
+ case CTA_RPLY:
+ reply = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(reply->src.ip),
+ NIPQUAD(reply->dst.ip));
+ h = findproto(proto2str[reply->dst.protonum]);
+ if (h)
+ h->print(reply);
+ break;
+ case CTA_STATUS:
+ status = NFA_DATA(attr);
+ printf("status:%u ", *status);
+ break;
+ case CTA_PROTOINFO:
+ proto = NFA_DATA(attr);
+ if (proto2str[proto->num_proto])
+ printf("%s %d", proto2str[proto->num_proto], proto->num_proto);
+ else
+ printf("unknown %d ", proto->num_proto);
+ break;
+ case CTA_TIMEOUT:
+ timeout = NFA_DATA(attr);
+ printf("timeout:%lu ", *timeout);
+ break;
+/* case CTA_ID:
+ id = NFA_DATA(attr);
+ printf(" id:%lu ", *id);
+ break;*/
+ }
+ DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type);
+ DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len);
+ attr = NFA_NEXT(attr, attrlen);
+ }
+ min_len += nlh->nlmsg_len;
+ nlh = (struct nlmsghdr *) attr;
+ printf("\n");
+ }
+ DEBUGP("exit from handler\n");
+
+ return 0;
+}
+
+/* FIXME: use event messages better */
+static char *typemsg2str[] = {
+ "NEW",
+ "GET",
+ "DESTROY"
+};
+
+static int event_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct nfgenmsg *nfmsg;
+ struct nfattr *nfa;
+ int min_len = 0;
+ struct ctproto_handler *h = NULL;
+ int type = NFNL_MSG_TYPE(nlh->nlmsg_type);
+
+ DEBUGP("netlink header\n");
+ DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n",
+ nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
+ nlh->nlmsg_seq, nlh->nlmsg_pid);
+
+ nfmsg = NLMSG_DATA(nlh);
+ DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family);
+
+ min_len = sizeof(struct nfgenmsg);
+ if (nlh->nlmsg_len < min_len)
+ return -EINVAL;
+
+ DEBUGP("size:%d\n", nlh->nlmsg_len);
+
+ printf("type: [%s] ", typemsg2str[type]);
+
+ while (nlh->nlmsg_len > min_len) {
+ struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+ struct ip_conntrack_tuple *orig, *reply;
+ unsigned long *status, *timeout;
+ struct cta_proto *proto;
+ unsigned long *id;
+
+ while (NFA_OK(attr, attrlen)) {
+ switch(attr->nfa_type) {
+ case CTA_ORIG:
+ orig = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(orig->src.ip),
+ NIPQUAD(orig->dst.ip));
+ h = findproto(proto2str[orig->dst.protonum]);
+ if (h)
+ h->print(orig);
+ break;
+ case CTA_RPLY:
+ reply = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(reply->src.ip),
+ NIPQUAD(reply->dst.ip));
+ h = findproto(proto2str[reply->dst.protonum]);
+ if (h)
+ h->print(reply);
+ break;
+ case CTA_STATUS:
+ status = NFA_DATA(attr);
+ printf("status:%u ", *status);
+ break;
+ case CTA_PROTOINFO:
+ proto = NFA_DATA(attr);
+ if (proto2str[proto->num_proto])
+ printf("%s %d", proto2str[proto->num_proto], proto->num_proto);
+ else
+ printf("unknown %d ", proto->num_proto);
+ break;
+ case CTA_TIMEOUT:
+ timeout = NFA_DATA(attr);
+ printf("timeout:%lu ", *timeout);
+ break;
+/* case CTA_ID:
+ id = NFA_DATA(attr);
+ printf(" id:%lu ", *id);
+ break;*/
+ }
+ DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type);
+ DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len);
+ attr = NFA_NEXT(attr, attrlen);
+ }
+ min_len += nlh->nlmsg_len;
+ nlh = (struct nlmsghdr *) attr;
+ printf("\n");
+ }
+ DEBUGP("exit from handler\n");
+
+ return 0;
+}
+
+static int expect_handler(struct sockaddr_nl *sock, struct nlmsghdr *nlh, void *arg)
+{
+ struct nfgenmsg *nfmsg;
+ struct nfattr *nfa;
+ int min_len = 0;
+ struct ctproto_handler *h = NULL;
+
+ DEBUGP("netlink header\n");
+ DEBUGP("len: %d type: %d flags: %d seq: %d pid: %d\n",
+ nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
+ nlh->nlmsg_seq, nlh->nlmsg_pid);
+
+ nfmsg = NLMSG_DATA(nlh);
+ DEBUGP("nfmsg->nfgen_family: %d\n", nfmsg->nfgen_family);
+
+ min_len = sizeof(struct nfgenmsg);
+ if (nlh->nlmsg_len < min_len)
+ return -EINVAL;
+
+ DEBUGP("size:%d\n", nlh->nlmsg_len);
+
+ while (nlh->nlmsg_len > min_len) {
+ struct nfattr *attr = NFM_NFA(NLMSG_DATA(nlh));
+ int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
+
+ struct ip_conntrack_tuple *exp, *mask;
+ unsigned long *timeout;
+
+ while (NFA_OK(attr, attrlen)) {
+ switch(attr->nfa_type) {
+ case CTA_EXP_TUPLE:
+ exp = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(exp->src.ip),
+ NIPQUAD(exp->dst.ip));
+ h = findproto(proto2str[exp->dst.protonum]);
+ if (h)
+ h->print(exp);
+ break;
+ case CTA_EXP_MASK:
+ mask = NFA_DATA(attr);
+ printf("src=%u.%u.%u.%u dst=%u.%u.%u.%u ",
+ NIPQUAD(mask->src.ip),
+ NIPQUAD(mask->dst.ip));
+ h = findproto(proto2str[mask->dst.protonum]);
+ if (h)
+ h->print(mask);
+ break;
+ case CTA_EXP_TIMEOUT:
+ timeout = NFA_DATA(attr);
+ printf("timeout:%lu ", *timeout);
+ break;
+ }
+ DEBUGP("nfa->nfa_type: %d\n", attr->nfa_type);
+ DEBUGP("nfa->nfa_len: %d\n", attr->nfa_len);
+ attr = NFA_NEXT(attr, attrlen);
+ }
+ min_len += nlh->nlmsg_len;
+ nlh = (struct nlmsghdr *) attr;
+ printf("\n");
+ }
+ DEBUGP("exit from handler\n");
+
+ return 0;
+}
+
+void create_conntrack(struct ip_conntrack_tuple *orig,
+ struct ip_conntrack_tuple *reply,
+ unsigned long timeout,
+ union ip_conntrack_proto *proto,
+ unsigned int status)
+{
+ struct cta_proto cta;
+ struct nfattr *cda[CTA_MAX];
+ struct ctnl_handle cth;
+
+ cta.num_proto = orig->dst.protonum;
+ memcpy(&cta.proto, proto, sizeof(*proto));
+ if (ctnl_open(&cth, 0) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ /* FIXME: please unify returns values... */
+ if (ctnl_new_conntrack(&cth, orig, reply, timeout, proto, status) < 0) {
+ printf("error new conntrack\n");
+ exit(0);
+ }
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2");
+ exit(0);
+ }
+}
+
+void delete_conntrack(struct ip_conntrack_tuple *tuple,
+ enum ctattr_type_t t,
+ unsigned long id)
+{
+ struct nfattr *cda[CTA_MAX];
+ struct ctnl_handle cth;
+
+ if (ctnl_open(&cth, 0) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ /* FIXME: please unify returns values... */
+ if (ctnl_del_conntrack(&cth, tuple, t, id) < 0) {
+ printf("error del conntrack\n");
+ exit(0);
+ }
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2");
+ exit(0);
+ }
+}
+
+/* get_conntrack_handler */
+void get_conntrack(struct ip_conntrack_tuple *tuple,
+ enum ctattr_type_t t,
+ unsigned long id)
+{
+ struct nfattr *cda[CTA_MAX];
+ struct ctnl_handle cth;
+ struct ctnl_msg_handler h = {
+ .type = 0,
+ .handler = handler
+ };
+
+ if (ctnl_open(&cth, 0) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ ctnl_register_handler(&cth, &h);
+
+ /* FIXME!!!! get_conntrack_handler returns -100 */
+ if (ctnl_get_conntrack(&cth, tuple, t, id) != -100) {
+ printf("error get conntrack\n");
+ exit(0);
+ }
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2");
+ exit(0);
+ }
+}
+
+void dump_conntrack_table()
+{
+ struct ctnl_handle cth;
+ struct ctnl_msg_handler h = {
+ .type = 0, /* Hm... really? */
+ .handler = handler
+ };
+
+ if (ctnl_open(&cth, 0) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ ctnl_register_handler(&cth, &h);
+
+ if (ctnl_list_conntrack(&cth, AF_INET) != -100) {
+ printf("error list\n");
+ exit(0);
+ }
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2\n");
+ exit(0);
+ }
+}
+
+void event_conntrack()
+{
+ struct ctnl_handle cth;
+ struct ctnl_msg_handler hnew = {
+ .type = 0, /* new */
+ .handler = event_handler
+ };
+ struct ctnl_msg_handler hdestroy = {
+ .type = 2, /* destroy */
+ .handler = event_handler
+ };
+
+ if (ctnl_open(&cth, NFGRP_IPV4_CT_TCP) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ ctnl_register_handler(&cth, &hnew);
+ ctnl_register_handler(&cth, &hdestroy);
+ ctnl_event_conntrack(&cth, AF_INET);
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2\n");
+ exit(0);
+ }
+}
+
+struct ctproto_handler *findproto(char *name)
+{
+ void *h = NULL;
+ struct list_head *i;
+ struct ctproto_handler *cur = NULL, *handler = NULL;
+
+ list_for_each(i, &proto_list) {
+ cur = (struct ctproto_handler *) i;
+ if (strcmp(cur->name, name) == 0) {
+ handler = cur;
+ break;
+ }
+ }
+
+ if (!handler) {
+ char path[sizeof("extensions/libct_proto_.so")
+ + strlen(name)];
+ sprintf(path, "extensions/libct_proto_%s.so", name);
+ if (dlopen(path, RTLD_NOW))
+ handler = findproto(name);
+/* else
+ fprintf (stderr, "%s\n", dlerror());*/
+ }
+
+ if (!handler)
+ handler = &generic_handler;
+
+ return handler;
+}
+
+void register_proto(struct ctproto_handler *h)
+{
+ list_add(&h->head, &proto_list);
+}
+
+void unregister_proto(struct ctproto_handler *h)
+{
+ list_del(&h->head);
+}
+
+void dump_expect_list()
+{
+ struct ctnl_handle cth;
+ struct ctnl_msg_handler h = {
+ .type = 0, /* Hm... really? */
+ .handler = expect_handler
+ };
+
+ if (ctnl_open(&cth, 0) < 0) {
+ printf("error\n");
+ exit(0);
+ }
+
+ ctnl_register_handler(&cth, &h);
+
+ if (ctnl_list_expect(&cth, AF_INET) != -100) {
+ printf("error list\n");
+ exit(0);
+ }
+
+ if (ctnl_close(&cth) < 0) {
+ printf("error2\n");
+ exit(0);
+ }
+}
+