From c86e48dc7d688d97a6ee4697ce01e594fa70d7db Mon Sep 17 00:00:00 2001 From: "/C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=laforge/emailAddress=laforge@netfilter.org" Date: Sat, 16 Apr 2005 13:30:56 +0000 Subject: add pablo's conntrack tool --- EXAMPLES | 23 ++ Makefile | 14 + TODO | 27 ++ extensions/Makefile | 12 + extensions/libct_proto_tcp.c | 67 ++++ extensions/libct_proto_udp.c | 67 ++++ include/libct_proto.h | 33 ++ include/libctnetlink.h | 72 +++++ include/libnfnetlink.h | 50 +++ include/linux_list.h | 725 +++++++++++++++++++++++++++++++++++++++++++ src/conntrack.c | 472 ++++++++++++++++++++++++++++ src/libct.c | 486 +++++++++++++++++++++++++++++ 12 files changed, 2048 insertions(+) create mode 100644 EXAMPLES create mode 100644 Makefile create mode 100644 TODO create mode 100644 extensions/Makefile create mode 100644 extensions/libct_proto_tcp.c create mode 100644 extensions/libct_proto_udp.c create mode 100644 include/libct_proto.h create mode 100644 include/libctnetlink.h create mode 100644 include/libnfnetlink.h create mode 100644 include/linux_list.h create mode 100644 src/conntrack.c create mode 100644 src/libct.c 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 +#include +#include +#include /* For htons */ +#include +#include +#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 +#include +#include +#include /* For htons */ +#include +#include +#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 + +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 , Copyright (c) 2001. + * (C) 2002 by Harald Welte + * + * 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 +#include +#include +#include +#include +#include +#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 + */ + +#ifndef __LIBNFNETLINK_H +#define __LIBNFNETLINK_H + +#include +#include +#include + +#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 + * + * 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 + * for introducing me to advanced firewalling stuff. + * + * --pablo 13/04/2005 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#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<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 +#include +#include +#include +#include +#include +#include +#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); + } +} + -- cgit v1.2.3