📄 ip_conntrack_netlink.c
字号:
err = -ENOENT; if (nlh->nlmsg_flags & NLM_F_CREATE) err = ctnetlink_create_conntrack(cda, &otuple, &rtuple); return err; } /* implicit 'else' */ /* we only allow nat config for new conntracks */ if (cda[CTA_NAT-1]) { err = -EINVAL; goto out_unlock; } /* We manipulate the conntrack inside the global conntrack table lock, * so there's no need to increase the refcount */ DEBUGP("conntrack found\n"); err = -EEXIST; if (!(nlh->nlmsg_flags & NLM_F_EXCL)) err = ctnetlink_change_conntrack(tuplehash_to_ctrack(h), cda);out_unlock: write_unlock_bh(&ip_conntrack_lock); return err;}/*********************************************************************** * EXPECT ***********************************************************************/ static inline intctnetlink_exp_dump_tuple(struct sk_buff *skb, const struct ip_conntrack_tuple *tuple, enum ctattr_expect type){ struct nfattr *nest_parms = NFA_NEST(skb, type); if (ctnetlink_dump_tuples(skb, tuple) < 0) goto nfattr_failure; NFA_NEST_END(skb, nest_parms); return 0;nfattr_failure: return -1;} static inline intctnetlink_exp_dump_expect(struct sk_buff *skb, const struct ip_conntrack_expect *exp){ struct ip_conntrack *master = exp->master; u_int32_t timeout = htonl((exp->timeout.expires - jiffies) / HZ); u_int32_t id = htonl(exp->id); if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0) goto nfattr_failure; if (ctnetlink_exp_dump_tuple(skb, &exp->mask, CTA_EXPECT_MASK) < 0) goto nfattr_failure; if (ctnetlink_exp_dump_tuple(skb, &master->tuplehash[IP_CT_DIR_ORIGINAL].tuple, CTA_EXPECT_MASTER) < 0) goto nfattr_failure; NFA_PUT(skb, CTA_EXPECT_TIMEOUT, sizeof(timeout), &timeout); NFA_PUT(skb, CTA_EXPECT_ID, sizeof(u_int32_t), &id); return 0; nfattr_failure: return -1;}static intctnetlink_exp_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait, const struct ip_conntrack_expect *exp){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; unsigned char *b; b = skb->tail; event |= NFNL_SUBSYS_CTNETLINK_EXP << 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; if (ctnetlink_exp_dump_expect(skb, exp) < 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_expect_event(struct notifier_block *this, unsigned long events, void *ptr){ struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; struct ip_conntrack_expect *exp = (struct ip_conntrack_expect *)ptr; struct sk_buff *skb; unsigned int type; unsigned char *b; int flags = 0; u16 proto; if (events & IPEXP_NEW) { type = IPCTNL_MSG_EXP_NEW; flags = NLM_F_CREATE|NLM_F_EXCL; } else 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 = AF_INET; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; if (ctnetlink_exp_dump_expect(skb, exp) < 0) goto nfattr_failure; nlh->nlmsg_len = skb->tail - b; proto = exp->tuple.dst.protonum; nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0); return NOTIFY_DONE;nlmsg_failure:nfattr_failure: kfree_skb(skb); return NOTIFY_DONE;}#endifstatic intctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb){ struct ip_conntrack_expect *exp = NULL; struct list_head *i; u_int32_t *id = (u_int32_t *) &cb->args[0]; DEBUGP("entered %s, last id=%llu\n", __FUNCTION__, *id); read_lock_bh(&ip_conntrack_lock); list_for_each_prev(i, &ip_conntrack_expect_list) { exp = (struct ip_conntrack_expect *) i; if (exp->id <= *id) continue; if (ctnetlink_exp_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, 1, exp) < 0) goto out; *id = exp->id; }out: read_unlock_bh(&ip_conntrack_lock); DEBUGP("leaving, last id=%llu\n", *id); return skb->len;}static const size_t cta_min_exp[CTA_EXPECT_MAX] = { [CTA_EXPECT_TIMEOUT-1] = sizeof(u_int32_t), [CTA_EXPECT_ID-1] = sizeof(u_int32_t)};static intctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *cda[], int *errp){ struct ip_conntrack_tuple tuple; struct ip_conntrack_expect *exp; struct sk_buff *skb2; int err = 0; DEBUGP("entered %s\n", __FUNCTION__); if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) return -EINVAL; if (nlh->nlmsg_flags & NLM_F_DUMP) { struct nfgenmsg *msg = NLMSG_DATA(nlh); u32 rlen; if (msg->nfgen_family != AF_INET) return -EAFNOSUPPORT; if ((*errp = netlink_dump_start(ctnl, skb, nlh, ctnetlink_exp_dump_table, ctnetlink_done)) != 0) return -EINVAL; rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; skb_pull(skb, rlen); return 0; } if (cda[CTA_EXPECT_MASTER-1]) err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_MASTER); else return -EINVAL; if (err < 0) return err; exp = ip_conntrack_expect_find(&tuple); if (!exp) return -ENOENT; if (cda[CTA_EXPECT_ID-1]) { u_int32_t id = *(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_ID-1]); if (exp->id != ntohl(id)) { ip_conntrack_expect_put(exp); return -ENOENT; } } err = -ENOMEM; skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb2) goto out; NETLINK_CB(skb2).dst_pid = NETLINK_CB(skb).pid; err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, 1, exp); if (err <= 0) goto free; ip_conntrack_expect_put(exp); return netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);free: kfree_skb(skb2);out: ip_conntrack_expect_put(exp); return err;}static intctnetlink_del_expect(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *cda[], int *errp){ struct ip_conntrack_expect *exp, *tmp; struct ip_conntrack_tuple tuple; struct ip_conntrack_helper *h; int err; if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) return -EINVAL; if (cda[CTA_EXPECT_TUPLE-1]) { /* delete a single expect by tuple */ err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); if (err < 0) return err; /* bump usage count to 2 */ exp = ip_conntrack_expect_find(&tuple); if (!exp) return -ENOENT; if (cda[CTA_EXPECT_ID-1]) { u_int32_t id = *(u_int32_t *)NFA_DATA(cda[CTA_EXPECT_ID-1]); if (exp->id != ntohl(id)) { ip_conntrack_expect_put(exp); return -ENOENT; } } /* after list removal, usage count == 1 */ ip_conntrack_unexpect_related(exp); /* have to put what we 'get' above. * after this line usage count == 0 */ ip_conntrack_expect_put(exp); } else if (cda[CTA_EXPECT_HELP_NAME-1]) { char *name = NFA_DATA(cda[CTA_EXPECT_HELP_NAME-1]); /* delete all expectations for this helper */ write_lock_bh(&ip_conntrack_lock); h = __ip_conntrack_helper_find_byname(name); if (!h) { write_unlock_bh(&ip_conntrack_lock); return -EINVAL; } list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { if (exp->master->helper == h && del_timer(&exp->timeout)) { ip_ct_unlink_expect(exp); ip_conntrack_expect_put(exp); } } write_unlock_bh(&ip_conntrack_lock); } else { /* This basically means we have to flush everything*/ write_lock_bh(&ip_conntrack_lock); list_for_each_entry_safe(exp, tmp, &ip_conntrack_expect_list, list) { if (del_timer(&exp->timeout)) { ip_ct_unlink_expect(exp); ip_conntrack_expect_put(exp); } } write_unlock_bh(&ip_conntrack_lock); } return 0;}static intctnetlink_change_expect(struct ip_conntrack_expect *x, struct nfattr *cda[]){ return -EOPNOTSUPP;}static intctnetlink_create_expect(struct nfattr *cda[]){ struct ip_conntrack_tuple tuple, mask, master_tuple; struct ip_conntrack_tuple_hash *h = NULL; struct ip_conntrack_expect *exp; struct ip_conntrack *ct; int err = 0; DEBUGP("entered %s\n", __FUNCTION__); /* caller guarantees that those three CTA_EXPECT_* exist */ err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); if (err < 0) return err; err = ctnetlink_parse_tuple(cda, &mask, CTA_EXPECT_MASK); if (err < 0) return err; err = ctnetlink_parse_tuple(cda, &master_tuple, CTA_EXPECT_MASTER); if (err < 0) return err; /* Look for master conntrack of this expectation */ h = ip_conntrack_find_get(&master_tuple, NULL); if (!h) return -ENOENT; ct = tuplehash_to_ctrack(h); if (!ct->helper) { /* such conntrack hasn't got any helper, abort */ err = -EINVAL; goto out; } exp = ip_conntrack_expect_alloc(ct); if (!exp) { err = -ENOMEM; goto out; } exp->expectfn = NULL; exp->flags = 0; exp->master = ct; memcpy(&exp->tuple, &tuple, sizeof(struct ip_conntrack_tuple)); memcpy(&exp->mask, &mask, sizeof(struct ip_conntrack_tuple)); err = ip_conntrack_expect_related(exp); ip_conntrack_expect_put(exp);out: ip_conntrack_put(tuplehash_to_ctrack(h)); return err;}static intctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb, struct nlmsghdr *nlh, struct nfattr *cda[], int *errp){ struct ip_conntrack_tuple tuple; struct ip_conntrack_expect *exp; int err = 0; DEBUGP("entered %s\n", __FUNCTION__); if (nfattr_bad_size(cda, CTA_EXPECT_MAX, cta_min_exp)) return -EINVAL; if (!cda[CTA_EXPECT_TUPLE-1] || !cda[CTA_EXPECT_MASK-1] || !cda[CTA_EXPECT_MASTER-1]) return -EINVAL; err = ctnetlink_parse_tuple(cda, &tuple, CTA_EXPECT_TUPLE); if (err < 0) return err; write_lock_bh(&ip_conntrack_lock); exp = __ip_conntrack_expect_find(&tuple); if (!exp) { write_unlock_bh(&ip_conntrack_lock); err = -ENOENT; if (nlh->nlmsg_flags & NLM_F_CREATE) err = ctnetlink_create_expect(cda); return err; } err = -EEXIST; if (!(nlh->nlmsg_flags & NLM_F_EXCL)) err = ctnetlink_change_expect(exp, cda); write_unlock_bh(&ip_conntrack_lock); DEBUGP("leaving\n"); return err;}#ifdef CONFIG_IP_NF_CONNTRACK_EVENTSstatic struct notifier_block ctnl_notifier = { .notifier_call = ctnetlink_conntrack_event,};static struct notifier_block ctnl_notifier_exp = { .notifier_call = ctnetlink_expect_event,};#endifstatic struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = { [IPCTNL_MSG_CT_NEW] = { .call = ctnetlink_new_conntrack, .attr_count = CTA_MAX, }, [IPCTNL_MSG_CT_GET] = { .call = ctnetlink_get_conntrack, .attr_count = CTA_MAX, }, [IPCTNL_MSG_CT_DELETE] = { .call = ctnetlink_del_conntrack, .attr_count = CTA_MAX, }, [IPCTNL_MSG_CT_GET_CTRZERO] = { .call = ctnetlink_get_conntrack, .attr_count = CTA_MAX, },};static struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = { [IPCTNL_MSG_EXP_GET] = { .call = ctnetlink_get_expect, .attr_count = CTA_EXPECT_MAX, }, [IPCTNL_MSG_EXP_NEW] = { .call = ctnetlink_new_expect, .attr_count = CTA_EXPECT_MAX, }, [IPCTNL_MSG_EXP_DELETE] = { .call = ctnetlink_del_expect, .attr_count = CTA_EXPECT_MAX, },};static struct nfnetlink_subsystem ctnl_subsys = { .name = "conntrack", .subsys_id = NFNL_SUBSYS_CTNETLINK, .cb_count = IPCTNL_MSG_MAX, .cb = ctnl_cb,};static struct nfnetlink_subsystem ctnl_exp_subsys = { .name = "conntrack_expect", .subsys_id = NFNL_SUBSYS_CTNETLINK_EXP, .cb_count = IPCTNL_MSG_EXP_MAX, .cb = ctnl_exp_cb,};MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTNETLINK);static int __init ctnetlink_init(void){ int ret; printk("ctnetlink v%s: registering with nfnetlink.\n", version); ret = nfnetlink_subsys_register(&ctnl_subsys); if (ret < 0) { printk("ctnetlink_init: cannot register with nfnetlink.\n"); goto err_out; } ret = nfnetlink_subsys_register(&ctnl_exp_subsys); if (ret < 0) { printk("ctnetlink_init: cannot register exp with nfnetlink.\n"); goto err_unreg_subsys; }#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS ret = ip_conntrack_register_notifier(&ctnl_notifier); if (ret < 0) { printk("ctnetlink_init: cannot register notifier.\n"); goto err_unreg_exp_subsys; } ret = ip_conntrack_expect_register_notifier(&ctnl_notifier_exp); if (ret < 0) { printk("ctnetlink_init: cannot expect register notifier.\n"); goto err_unreg_notifier; }#endif return 0;#ifdef CONFIG_IP_NF_CONNTRACK_EVENTSerr_unreg_notifier: ip_conntrack_unregister_notifier(&ctnl_notifier);err_unreg_exp_subsys: nfnetlink_subsys_unregister(&ctnl_exp_subsys);#endiferr_unreg_subsys: nfnetlink_subsys_unregister(&ctnl_subsys);err_out: return ret;}static void __exit ctnetlink_exit(void){ printk("ctnetlink: unregistering from nfnetlink.\n");#ifdef CONFIG_IP_NF_CONNTRACK_EVENTS ip_conntrack_unregister_notifier(&ctnl_notifier_exp); ip_conntrack_unregister_notifier(&ctnl_notifier);#endif nfnetlink_subsys_unregister(&ctnl_exp_subsys); nfnetlink_subsys_unregister(&ctnl_subsys); return;}module_init(ctnetlink_init);module_exit(ctnetlink_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -