📄 ip_conntrack_netlink.c
字号:
/* Connection tracking via netlink socket. Allows for user space * protocol helpers and general trouble making from userspace. * * (C) 2001 by Jay Schulist <jschlst@samba.org> * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org> * (C) 2003 by Patrick Mchardy <kaber@trash.net> * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net> * * I've reworked this stuff to use attributes instead of conntrack * structures. 5.44 am. I need more tea. --pablo 05/07/11. * * Initial connection tracking via netlink development funded and * generally made possible by Network Robots, Inc. (www.networkrobots.com) * * Further development of this code funded by Astaro AG (http://www.astaro.com) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. */#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/timer.h>#include <linux/skbuff.h>#include <linux/errno.h>#include <linux/netlink.h>#include <linux/spinlock.h>#include <linux/interrupt.h>#include <linux/notifier.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4/ip_conntrack.h>#include <linux/netfilter_ipv4/ip_conntrack_core.h>#include <linux/netfilter_ipv4/ip_conntrack_helper.h>#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>#include <linux/netfilter_ipv4/ip_nat_protocol.h>#include <linux/netfilter/nfnetlink.h>#include <linux/netfilter/nfnetlink_conntrack.h>MODULE_LICENSE("GPL");static char __initdata version[] = "0.90";#if 0#define DEBUGP printk#else#define DEBUGP(format, args...)#endifstatic inline intctnetlink_dump_tuples_proto(struct sk_buff *skb, const struct ip_conntrack_tuple *tuple){ struct ip_conntrack_protocol *proto; int ret = 0; NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); /* If no protocol helper is found, this function will return the * generic protocol helper, so proto won't *ever* be NULL */ proto = ip_conntrack_proto_find_get(tuple->dst.protonum); if (likely(proto->tuple_to_nfattr)) ret = proto->tuple_to_nfattr(skb, tuple); ip_conntrack_proto_put(proto); return ret;nfattr_failure: return -1;}static inline intctnetlink_dump_tuples(struct sk_buff *skb, const struct ip_conntrack_tuple *tuple){ struct nfattr *nest_parms; nest_parms = NFA_NEST(skb, CTA_TUPLE_IP); NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(u_int32_t), &tuple->src.ip); NFA_PUT(skb, CTA_IP_V4_DST, sizeof(u_int32_t), &tuple->dst.ip); NFA_NEST_END(skb, nest_parms); nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO); ctnetlink_dump_tuples_proto(skb, tuple); NFA_NEST_END(skb, nest_parms); return 0;nfattr_failure: return -1;}static inline intctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct){ u_int32_t status = htonl((u_int32_t) ct->status); NFA_PUT(skb, CTA_STATUS, sizeof(status), &status); return 0;nfattr_failure: return -1;}static inline intctnetlink_dump_timeout(struct sk_buff *skb, const struct ip_conntrack *ct){ long timeout_l = ct->timeout.expires - jiffies; u_int32_t timeout; if (timeout_l < 0) timeout = 0; else timeout = htonl(timeout_l / HZ); NFA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout); return 0;nfattr_failure: return -1;}static inline intctnetlink_dump_protoinfo(struct sk_buff *skb, const struct ip_conntrack *ct){ struct ip_conntrack_protocol *proto = ip_conntrack_proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); struct nfattr *nest_proto; int ret; if (!proto->to_nfattr) { ip_conntrack_proto_put(proto); return 0; } nest_proto = NFA_NEST(skb, CTA_PROTOINFO); ret = proto->to_nfattr(skb, nest_proto, ct); ip_conntrack_proto_put(proto); NFA_NEST_END(skb, nest_proto); return ret;nfattr_failure: return -1;}static inline intctnetlink_dump_helpinfo(struct sk_buff *skb, const struct ip_conntrack *ct){ struct nfattr *nest_helper; if (!ct->helper) return 0; nest_helper = NFA_NEST(skb, CTA_HELP); NFA_PUT(skb, CTA_HELP_NAME, CTA_HELP_MAXNAMESIZE, &ct->helper->name); if (ct->helper->to_nfattr) ct->helper->to_nfattr(skb, ct); NFA_NEST_END(skb, nest_helper); return 0;nfattr_failure: return -1;}#ifdef CONFIG_IP_NF_CT_ACCTstatic inline intctnetlink_dump_counters(struct sk_buff *skb, const struct ip_conntrack *ct, enum ip_conntrack_dir dir){ enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nfattr *nest_count = NFA_NEST(skb, type); u_int32_t tmp; tmp = htonl(ct->counters[dir].packets); NFA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp); tmp = htonl(ct->counters[dir].bytes); NFA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(u_int32_t), &tmp); NFA_NEST_END(skb, nest_count); return 0;nfattr_failure: return -1;}#else#define ctnetlink_dump_counters(a, b, c) (0)#endif#ifdef CONFIG_IP_NF_CONNTRACK_MARKstatic inline intctnetlink_dump_mark(struct sk_buff *skb, const struct ip_conntrack *ct){ u_int32_t mark = htonl(ct->mark); NFA_PUT(skb, CTA_MARK, sizeof(u_int32_t), &mark); return 0;nfattr_failure: return -1;}#else#define ctnetlink_dump_mark(a, b) (0)#endifstatic inline intctnetlink_dump_id(struct sk_buff *skb, const struct ip_conntrack *ct){ u_int32_t id = htonl(ct->id); NFA_PUT(skb, CTA_ID, sizeof(u_int32_t), &id); return 0;nfattr_failure: return -1;}static inline intctnetlink_dump_use(struct sk_buff *skb, const struct ip_conntrack *ct){ unsigned int use = htonl(atomic_read(&ct->ct_general.use)); NFA_PUT(skb, CTA_USE, sizeof(u_int32_t), &use); return 0;nfattr_failure: return -1;}#define tuple(ct, dir) (&(ct)->tuplehash[dir].tuple)static intctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait, const struct ip_conntrack *ct){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nfattr *nest_parms; unsigned char *b; b = skb->tail; event |= NFNL_SUBSYS_CTNETLINK << 8; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(struct nfgenmsg)); nfmsg = NLMSG_DATA(nlh); nlh->nlmsg_flags = (nowait && pid) ? NLM_F_MULTI : 0; nfmsg->nfgen_family = AF_INET; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) goto nfattr_failure; NFA_NEST_END(skb, nest_parms); nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) goto nfattr_failure; NFA_NEST_END(skb, nest_parms); if (ctnetlink_dump_status(skb, ct) < 0 || ctnetlink_dump_timeout(skb, ct) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || ctnetlink_dump_protoinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0) goto nfattr_failure; nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:nfattr_failure: skb_trim(skb, b - skb->data); return -1;}#ifdef CONFIG_IP_NF_CONNTRACK_EVENTSstatic int ctnetlink_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nfattr *nest_parms; struct ip_conntrack *ct = (struct ip_conntrack *)ptr; struct sk_buff *skb; unsigned int type; unsigned char *b; unsigned int flags = 0, group; /* ignore our fake conntrack entry */ if (ct == &ip_conntrack_untracked) return NOTIFY_DONE; if (events & IPCT_DESTROY) { type = IPCTNL_MSG_CT_DELETE; group = NFNLGRP_CONNTRACK_DESTROY; goto alloc_skb; } if (events & (IPCT_NEW | IPCT_RELATED)) { type = IPCTNL_MSG_CT_NEW; flags = NLM_F_CREATE|NLM_F_EXCL; /* dump everything */ events = ~0UL; group = NFNLGRP_CONNTRACK_NEW; goto alloc_skb; } if (events & (IPCT_STATUS | IPCT_PROTOINFO | IPCT_HELPER | IPCT_HELPINFO | IPCT_NATINFO)) { type = IPCTNL_MSG_CT_NEW; group = NFNLGRP_CONNTRACK_UPDATE; goto alloc_skb; } return NOTIFY_DONE;alloc_skb: /* FIXME: Check if there are any listeners before, don't hurt performance */ skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); if (!skb) return NOTIFY_DONE; b = skb->tail; type |= NFNL_SUBSYS_CTNETLINK << 8; nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg)); nfmsg = NLMSG_DATA(nlh); nlh->nlmsg_flags = flags; nfmsg->nfgen_family = AF_INET; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; nest_parms = NFA_NEST(skb, CTA_TUPLE_ORIG); if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) goto nfattr_failure; NFA_NEST_END(skb, nest_parms); nest_parms = NFA_NEST(skb, CTA_TUPLE_REPLY); if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) goto nfattr_failure; NFA_NEST_END(skb, nest_parms); /* NAT stuff is now a status flag */ if ((events & IPCT_STATUS || events & IPCT_NATINFO) && ctnetlink_dump_status(skb, ct) < 0) goto nfattr_failure; if (events & IPCT_REFRESH && ctnetlink_dump_timeout(skb, ct) < 0) goto nfattr_failure; if (events & IPCT_PROTOINFO && ctnetlink_dump_protoinfo(skb, ct) < 0) goto nfattr_failure; if (events & IPCT_HELPINFO && ctnetlink_dump_helpinfo(skb, ct) < 0) goto nfattr_failure; if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) goto nfattr_failure; nlh->nlmsg_len = skb->tail - b; nfnetlink_send(skb, 0, group, 0); return NOTIFY_DONE;nlmsg_failure:nfattr_failure: kfree_skb(skb); return NOTIFY_DONE;}#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */static int ctnetlink_done(struct netlink_callback *cb){ DEBUGP("entered %s\n", __FUNCTION__); return 0;}static intctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb){ struct ip_conntrack *ct = NULL; struct ip_conntrack_tuple_hash *h; struct list_head *i; u_int32_t *id = (u_int32_t *) &cb->args[1]; DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__, cb->args[0], *id); read_lock_bh(&ip_conntrack_lock); for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++, *id = 0) { list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { h = (struct ip_conntrack_tuple_hash *) i; if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) continue; ct = tuplehash_to_ctrack(h); if (ct->id <= *id) continue; if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, 1, ct) < 0) goto out; *id = ct->id; } }out: read_unlock_bh(&ip_conntrack_lock); DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id); return skb->len;}#ifdef CONFIG_IP_NF_CT_ACCTstatic intctnetlink_dump_table_w(struct sk_buff *skb, struct netlink_callback *cb){ struct ip_conntrack *ct = NULL; struct ip_conntrack_tuple_hash *h; struct list_head *i; u_int32_t *id = (u_int32_t *) &cb->args[1]; DEBUGP("entered %s, last bucket=%u id=%u\n", __FUNCTION__, cb->args[0], *id); write_lock_bh(&ip_conntrack_lock); for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++, *id = 0) { list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { h = (struct ip_conntrack_tuple_hash *) i; if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) continue; ct = tuplehash_to_ctrack(h); if (ct->id <= *id) continue; if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, 1, ct) < 0) goto out; *id = ct->id; memset(&ct->counters, 0, sizeof(ct->counters)); } }out: write_unlock_bh(&ip_conntrack_lock); DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id); return skb->len;}#endifstatic const size_t cta_min_ip[CTA_IP_MAX] = { [CTA_IP_V4_SRC-1] = sizeof(u_int32_t), [CTA_IP_V4_DST-1] = sizeof(u_int32_t),};static inline intctnetlink_parse_tuple_ip(struct nfattr *attr, struct ip_conntrack_tuple *tuple){ struct nfattr *tb[CTA_IP_MAX]; DEBUGP("entered %s\n", __FUNCTION__); nfattr_parse_nested(tb, CTA_IP_MAX, attr); if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip)) return -EINVAL; if (!tb[CTA_IP_V4_SRC-1]) return -EINVAL; tuple->src.ip = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_SRC-1]); if (!tb[CTA_IP_V4_DST-1]) return -EINVAL; tuple->dst.ip = *(u_int32_t *)NFA_DATA(tb[CTA_IP_V4_DST-1]); DEBUGP("leaving\n"); return 0;}static const size_t cta_min_proto[CTA_PROTO_MAX] = { [CTA_PROTO_NUM-1] = sizeof(u_int8_t), [CTA_PROTO_SRC_PORT-1] = sizeof(u_int16_t), [CTA_PROTO_DST_PORT-1] = sizeof(u_int16_t), [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t),};static inline intctnetlink_parse_tuple_proto(struct nfattr *attr, struct ip_conntrack_tuple *tuple){ struct nfattr *tb[CTA_PROTO_MAX]; struct ip_conntrack_protocol *proto; int ret = 0; DEBUGP("entered %s\n", __FUNCTION__); nfattr_parse_nested(tb, CTA_PROTO_MAX, attr); if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) return -EINVAL; if (!tb[CTA_PROTO_NUM-1]) return -EINVAL; tuple->dst.protonum = *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_NUM-1]); proto = ip_conntrack_proto_find_get(tuple->dst.protonum); if (likely(proto->nfattr_to_tuple)) ret = proto->nfattr_to_tuple(tb, tuple); ip_conntrack_proto_put(proto); return ret;}static inline intctnetlink_parse_tuple(struct nfattr *cda[], struct ip_conntrack_tuple *tuple, enum ctattr_tuple type){ struct nfattr *tb[CTA_TUPLE_MAX];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -