📄 nf_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-2006 by Harald Welte <laforge@gnumonks.org> * (C) 2003 by Patrick Mchardy <kaber@trash.net> * (C) 2005-2007 by Pablo Neira Ayuso <pablo@netfilter.org> * * 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 <net/netlink.h>#include <net/netfilter/nf_conntrack.h>#include <net/netfilter/nf_conntrack_core.h>#include <net/netfilter/nf_conntrack_expect.h>#include <net/netfilter/nf_conntrack_helper.h>#include <net/netfilter/nf_conntrack_l3proto.h>#include <net/netfilter/nf_conntrack_l4proto.h>#include <net/netfilter/nf_conntrack_tuple.h>#ifdef CONFIG_NF_NAT_NEEDED#include <net/netfilter/nf_nat_core.h>#include <net/netfilter/nf_nat_protocol.h>#endif#include <linux/netfilter/nfnetlink.h>#include <linux/netfilter/nfnetlink_conntrack.h>MODULE_LICENSE("GPL");static char __initdata version[] = "0.93";static inline intctnetlink_dump_tuples_proto(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, struct nf_conntrack_l4proto *l4proto){ int ret = 0; struct nlattr *nest_parms; nest_parms = nla_nest_start(skb, CTA_TUPLE_PROTO | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; NLA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum); if (likely(l4proto->tuple_to_nlattr)) ret = l4proto->tuple_to_nlattr(skb, tuple); nla_nest_end(skb, nest_parms); return ret;nla_put_failure: return -1;}static inline intctnetlink_dump_tuples_ip(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, struct nf_conntrack_l3proto *l3proto){ int ret = 0; struct nlattr *nest_parms; nest_parms = nla_nest_start(skb, CTA_TUPLE_IP | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; if (likely(l3proto->tuple_to_nlattr)) ret = l3proto->tuple_to_nlattr(skb, tuple); nla_nest_end(skb, nest_parms); return ret;nla_put_failure: return -1;}static inline intctnetlink_dump_tuples(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple){ int ret; struct nf_conntrack_l3proto *l3proto; struct nf_conntrack_l4proto *l4proto; l3proto = nf_ct_l3proto_find_get(tuple->src.l3num); ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto); nf_ct_l3proto_put(l3proto); if (unlikely(ret < 0)) return ret; l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum); ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto); nf_ct_l4proto_put(l4proto); return ret;}static inline intctnetlink_dump_status(struct sk_buff *skb, const struct nf_conn *ct){ __be32 status = htonl((u_int32_t) ct->status); NLA_PUT(skb, CTA_STATUS, sizeof(status), &status); return 0;nla_put_failure: return -1;}static inline intctnetlink_dump_timeout(struct sk_buff *skb, const struct nf_conn *ct){ long timeout_l = ct->timeout.expires - jiffies; __be32 timeout; if (timeout_l < 0) timeout = 0; else timeout = htonl(timeout_l / HZ); NLA_PUT(skb, CTA_TIMEOUT, sizeof(timeout), &timeout); return 0;nla_put_failure: return -1;}static inline intctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct){ struct nf_conntrack_l4proto *l4proto = nf_ct_l4proto_find_get(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum); struct nlattr *nest_proto; int ret; if (!l4proto->to_nlattr) { nf_ct_l4proto_put(l4proto); return 0; } nest_proto = nla_nest_start(skb, CTA_PROTOINFO | NLA_F_NESTED); if (!nest_proto) goto nla_put_failure; ret = l4proto->to_nlattr(skb, nest_proto, ct); nf_ct_l4proto_put(l4proto); nla_nest_end(skb, nest_proto); return ret;nla_put_failure: nf_ct_l4proto_put(l4proto); return -1;}static inline intctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct){ struct nlattr *nest_helper; const struct nf_conn_help *help = nfct_help(ct); struct nf_conntrack_helper *helper; if (!help) return 0; rcu_read_lock(); helper = rcu_dereference(help->helper); if (!helper) goto out; nest_helper = nla_nest_start(skb, CTA_HELP | NLA_F_NESTED); if (!nest_helper) goto nla_put_failure; NLA_PUT(skb, CTA_HELP_NAME, strlen(helper->name), helper->name); if (helper->to_nlattr) helper->to_nlattr(skb, ct); nla_nest_end(skb, nest_helper);out: rcu_read_unlock(); return 0;nla_put_failure: rcu_read_unlock(); return -1;}#ifdef CONFIG_NF_CT_ACCTstatic inline intctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, enum ip_conntrack_dir dir){ enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nlattr *nest_count; __be32 tmp; nest_count = nla_nest_start(skb, type | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; tmp = htonl(ct->counters[dir].packets); NLA_PUT(skb, CTA_COUNTERS32_PACKETS, sizeof(u_int32_t), &tmp); tmp = htonl(ct->counters[dir].bytes); NLA_PUT(skb, CTA_COUNTERS32_BYTES, sizeof(u_int32_t), &tmp); nla_nest_end(skb, nest_count); return 0;nla_put_failure: return -1;}#else#define ctnetlink_dump_counters(a, b, c) (0)#endif#ifdef CONFIG_NF_CONNTRACK_MARKstatic inline intctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct){ __be32 mark = htonl(ct->mark); NLA_PUT(skb, CTA_MARK, sizeof(u_int32_t), &mark); return 0;nla_put_failure: return -1;}#else#define ctnetlink_dump_mark(a, b) (0)#endifstatic inline intctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct){ __be32 id = htonl((unsigned long)ct); NLA_PUT(skb, CTA_ID, sizeof(u_int32_t), &id); return 0;nla_put_failure: return -1;}static inline intctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct){ __be32 use = htonl(atomic_read(&ct->ct_general.use)); NLA_PUT(skb, CTA_USE, sizeof(u_int32_t), &use); return 0;nla_put_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 nf_conn *ct){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nlattr *nest_parms; unsigned char *b = skb_tail_pointer(skb); 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 = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) goto nla_put_failure; nla_nest_end(skb, nest_parms); nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) goto nla_put_failure; nla_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 nla_put_failure; nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len;nlmsg_failure:nla_put_failure: nlmsg_trim(skb, b); return -1;}#ifdef CONFIG_NF_CONNTRACK_EVENTSstatic int ctnetlink_conntrack_event(struct notifier_block *this, unsigned long events, void *ptr){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct nlattr *nest_parms; struct nf_conn *ct = (struct nf_conn *)ptr; struct sk_buff *skb; unsigned int type; sk_buff_data_t b; unsigned int flags = 0, group; /* ignore our fake conntrack entry */ if (ct == &nf_conntrack_untracked) return NOTIFY_DONE; if (events & IPCT_DESTROY) { type = IPCTNL_MSG_CT_DELETE; group = NFNLGRP_CONNTRACK_DESTROY; } else if (events & (IPCT_NEW | IPCT_RELATED)) { type = IPCTNL_MSG_CT_NEW; flags = NLM_F_CREATE|NLM_F_EXCL; group = NFNLGRP_CONNTRACK_NEW; } else if (events & (IPCT_STATUS | IPCT_PROTOINFO)) { type = IPCTNL_MSG_CT_NEW; group = NFNLGRP_CONNTRACK_UPDATE; } else return NOTIFY_DONE; if (!nfnetlink_has_listeners(group)) return NOTIFY_DONE; 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 = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_ORIGINAL)) < 0) goto nla_put_failure; nla_nest_end(skb, nest_parms); nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED); if (!nest_parms) goto nla_put_failure; if (ctnetlink_dump_tuples(skb, tuple(ct, IP_CT_DIR_REPLY)) < 0) goto nla_put_failure; nla_nest_end(skb, nest_parms); if (events & IPCT_DESTROY) { if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) goto nla_put_failure; } else { if (ctnetlink_dump_status(skb, ct) < 0) goto nla_put_failure; if (ctnetlink_dump_timeout(skb, ct) < 0) goto nla_put_failure; if (events & IPCT_PROTOINFO && ctnetlink_dump_protoinfo(skb, ct) < 0) goto nla_put_failure; if ((events & IPCT_HELPER || nfct_help(ct)) && ctnetlink_dump_helpinfo(skb, ct) < 0) goto nla_put_failure;#ifdef CONFIG_NF_CONNTRACK_MARK if ((events & IPCT_MARK || ct->mark) && ctnetlink_dump_mark(skb, ct) < 0) goto nla_put_failure;#endif if (events & IPCT_COUNTER_FILLING && (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) goto nla_put_failure; } nlh->nlmsg_len = skb->tail - b; nfnetlink_send(skb, 0, group, 0); return NOTIFY_DONE;nlmsg_failure:nla_put_failure: kfree_skb(skb); return NOTIFY_DONE;}#endif /* CONFIG_NF_CONNTRACK_EVENTS */static int ctnetlink_done(struct netlink_callback *cb){ if (cb->args[1]) nf_ct_put((struct nf_conn *)cb->args[1]); return 0;}#define L3PROTO(ct) ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3numstatic intctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb){ struct nf_conn *ct, *last; struct nf_conntrack_tuple_hash *h; struct hlist_node *n; struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; read_lock_bh(&nf_conntrack_lock); last = (struct nf_conn *)cb->args[1]; for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {restart: hlist_for_each_entry(h, n, &nf_conntrack_hash[cb->args[0]], hnode) { if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL) continue; ct = nf_ct_tuplehash_to_ctrack(h); /* Dump entries of a given L3 protocol number. * If it is not specified, ie. l3proto == 0, * then dump everything. */ if (l3proto && L3PROTO(ct) != l3proto) continue; if (cb->args[1]) { if (ct != last) continue; cb->args[1] = 0; } if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, 1, ct) < 0) { nf_conntrack_get(&ct->ct_general); cb->args[1] = (unsigned long)ct; goto out; }#ifdef CONFIG_NF_CT_ACCT if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) memset(&ct->counters, 0, sizeof(ct->counters));#endif } if (cb->args[1]) { cb->args[1] = 0; goto restart; } }out: read_unlock_bh(&nf_conntrack_lock); if (last) nf_ct_put(last); return skb->len;}static inline intctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple){ struct nlattr *tb[CTA_IP_MAX+1]; struct nf_conntrack_l3proto *l3proto; int ret = 0; nla_parse_nested(tb, CTA_IP_MAX, attr, NULL); l3proto = nf_ct_l3proto_find_get(tuple->src.l3num); if (likely(l3proto->nlattr_to_tuple)) { ret = nla_validate_nested(attr, CTA_IP_MAX, l3proto->nla_policy); if (ret == 0) ret = l3proto->nlattr_to_tuple(tb, tuple); } nf_ct_l3proto_put(l3proto); return ret;}static const struct nla_policy proto_nla_policy[CTA_PROTO_MAX+1] = { [CTA_PROTO_NUM] = { .type = NLA_U8 },};static inline intctnetlink_parse_tuple_proto(struct nlattr *attr, struct nf_conntrack_tuple *tuple){ struct nlattr *tb[CTA_PROTO_MAX+1]; struct nf_conntrack_l4proto *l4proto; int ret = 0; ret = nla_parse_nested(tb, CTA_PROTO_MAX, attr, proto_nla_policy); if (ret < 0) return ret; if (!tb[CTA_PROTO_NUM]) return -EINVAL; tuple->dst.protonum = *(u_int8_t *)nla_data(tb[CTA_PROTO_NUM]); l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum); if (likely(l4proto->nlattr_to_tuple)) { ret = nla_validate_nested(attr, CTA_PROTO_MAX, l4proto->nla_policy); if (ret == 0) ret = l4proto->nlattr_to_tuple(tb, tuple); } nf_ct_l4proto_put(l4proto); return ret;}static inline intctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple, enum ctattr_tuple type, u_int8_t l3num){ struct nlattr *tb[CTA_TUPLE_MAX+1]; int err; memset(tuple, 0, sizeof(*tuple)); nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -