netfilter: nfnetlink_queue: allow to attach expectations to conntracks
This patch adds the capability to attach expectations via nfnetlink_queue. This is required by conntrack helpers that trigger expectations based on the first packet seen like the TFTP and the DHCPv6 user-space helpers. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
					parent
					
						
							
								0ef71ee1a5
							
						
					
				
			
			
				commit
				
					
						bd07793705
					
				
			
		
					 6 changed files with 114 additions and 16 deletions
				
			
		|  | @ -325,6 +325,8 @@ struct nfq_ct_hook { | ||||||
| 	size_t (*build_size)(const struct nf_conn *ct); | 	size_t (*build_size)(const struct nf_conn *ct); | ||||||
| 	int (*build)(struct sk_buff *skb, struct nf_conn *ct); | 	int (*build)(struct sk_buff *skb, struct nf_conn *ct); | ||||||
| 	int (*parse)(const struct nlattr *attr, struct nf_conn *ct); | 	int (*parse)(const struct nlattr *attr, struct nf_conn *ct); | ||||||
|  | 	int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, | ||||||
|  | 			     u32 portid, u32 report); | ||||||
| }; | }; | ||||||
| extern struct nfq_ct_hook __rcu *nfq_ct_hook; | extern struct nfq_ct_hook __rcu *nfq_ct_hook; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,8 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, | ||||||
| 		 enum ip_conntrack_info ctinfo); | 		 enum ip_conntrack_info ctinfo); | ||||||
| void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, | void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, | ||||||
| 			 enum ip_conntrack_info ctinfo, int diff); | 			 enum ip_conntrack_info ctinfo, int diff); | ||||||
|  | int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||||||
|  | 			u32 portid, u32 report); | ||||||
| #else | #else | ||||||
| inline struct nf_conn * | inline struct nf_conn * | ||||||
| nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) | nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) | ||||||
|  | @ -39,5 +41,11 @@ inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, | ||||||
| 				enum ip_conntrack_info ctinfo, int diff) | 				enum ip_conntrack_info ctinfo, int diff) | ||||||
| { | { | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||||||
|  | 			       u32 portid, u32 report) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
| #endif /* NF_CONNTRACK */ | #endif /* NF_CONNTRACK */ | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -46,6 +46,7 @@ enum nfqnl_attr_type { | ||||||
| 	NFQA_CT_INFO,			/* enum ip_conntrack_info */ | 	NFQA_CT_INFO,			/* enum ip_conntrack_info */ | ||||||
| 	NFQA_CAP_LEN,			/* __u32 length of captured packet */ | 	NFQA_CAP_LEN,			/* __u32 length of captured packet */ | ||||||
| 	NFQA_SKB_INFO,			/* __u32 skb meta information */ | 	NFQA_SKB_INFO,			/* __u32 skb meta information */ | ||||||
|  | 	NFQA_EXP,			/* nf_conntrack_netlink.h */ | ||||||
| 
 | 
 | ||||||
| 	__NFQA_MAX | 	__NFQA_MAX | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1987,6 +1987,27 @@ out: | ||||||
| 	return err == -EAGAIN ? -ENOBUFS : err; | 	return err == -EAGAIN ? -ENOBUFS : err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { | ||||||
|  | 	[CTA_EXPECT_MASTER]	= { .type = NLA_NESTED }, | ||||||
|  | 	[CTA_EXPECT_TUPLE]	= { .type = NLA_NESTED }, | ||||||
|  | 	[CTA_EXPECT_MASK]	= { .type = NLA_NESTED }, | ||||||
|  | 	[CTA_EXPECT_TIMEOUT]	= { .type = NLA_U32 }, | ||||||
|  | 	[CTA_EXPECT_ID]		= { .type = NLA_U32 }, | ||||||
|  | 	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_NUL_STRING, | ||||||
|  | 				    .len = NF_CT_HELPER_NAME_LEN - 1 }, | ||||||
|  | 	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 }, | ||||||
|  | 	[CTA_EXPECT_FLAGS]	= { .type = NLA_U32 }, | ||||||
|  | 	[CTA_EXPECT_CLASS]	= { .type = NLA_U32 }, | ||||||
|  | 	[CTA_EXPECT_NAT]	= { .type = NLA_NESTED }, | ||||||
|  | 	[CTA_EXPECT_FN]		= { .type = NLA_NUL_STRING }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct nf_conntrack_expect * | ||||||
|  | ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, | ||||||
|  | 		       struct nf_conntrack_helper *helper, | ||||||
|  | 		       struct nf_conntrack_tuple *tuple, | ||||||
|  | 		       struct nf_conntrack_tuple *mask); | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT | #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT | ||||||
| static size_t | static size_t | ||||||
| ctnetlink_nfqueue_build_size(const struct nf_conn *ct) | ctnetlink_nfqueue_build_size(const struct nf_conn *ct) | ||||||
|  | @ -2127,10 +2148,69 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, | ||||||
|  | 				       const struct nf_conn *ct, | ||||||
|  | 				       struct nf_conntrack_tuple *tuple, | ||||||
|  | 				       struct nf_conntrack_tuple *mask) | ||||||
|  | { | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = ctnetlink_parse_tuple(cda, tuple, CTA_EXPECT_TUPLE, | ||||||
|  | 				    nf_ct_l3num(ct)); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	return ctnetlink_parse_tuple(cda, mask, CTA_EXPECT_MASK, | ||||||
|  | 				     nf_ct_l3num(ct)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int | ||||||
|  | ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, | ||||||
|  | 				u32 portid, u32 report) | ||||||
|  | { | ||||||
|  | 	struct nlattr *cda[CTA_EXPECT_MAX+1]; | ||||||
|  | 	struct nf_conntrack_tuple tuple, mask; | ||||||
|  | 	struct nf_conntrack_helper *helper; | ||||||
|  | 	struct nf_conntrack_expect *exp; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda, | ||||||
|  | 					  ct, &tuple, &mask); | ||||||
|  | 	if (err < 0) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	if (cda[CTA_EXPECT_HELP_NAME]) { | ||||||
|  | 		const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); | ||||||
|  | 
 | ||||||
|  | 		helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), | ||||||
|  | 						    nf_ct_protonum(ct)); | ||||||
|  | 		if (helper == NULL) | ||||||
|  | 			return -EOPNOTSUPP; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, | ||||||
|  | 				     helper, &tuple, &mask); | ||||||
|  | 	if (IS_ERR(exp)) | ||||||
|  | 		return PTR_ERR(exp); | ||||||
|  | 
 | ||||||
|  | 	err = nf_ct_expect_related_report(exp, portid, report); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		nf_ct_expect_put(exp); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static struct nfq_ct_hook ctnetlink_nfqueue_hook = { | static struct nfq_ct_hook ctnetlink_nfqueue_hook = { | ||||||
| 	.build_size	= ctnetlink_nfqueue_build_size, | 	.build_size	= ctnetlink_nfqueue_build_size, | ||||||
| 	.build		= ctnetlink_nfqueue_build, | 	.build		= ctnetlink_nfqueue_build, | ||||||
| 	.parse		= ctnetlink_nfqueue_parse, | 	.parse		= ctnetlink_nfqueue_parse, | ||||||
|  | 	.attach_expect	= ctnetlink_nfqueue_attach_expect, | ||||||
| }; | }; | ||||||
| #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ | #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ | ||||||
| 
 | 
 | ||||||
|  | @ -2498,21 +2578,6 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb, | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { |  | ||||||
| 	[CTA_EXPECT_MASTER]	= { .type = NLA_NESTED }, |  | ||||||
| 	[CTA_EXPECT_TUPLE]	= { .type = NLA_NESTED }, |  | ||||||
| 	[CTA_EXPECT_MASK]	= { .type = NLA_NESTED }, |  | ||||||
| 	[CTA_EXPECT_TIMEOUT]	= { .type = NLA_U32 }, |  | ||||||
| 	[CTA_EXPECT_ID]		= { .type = NLA_U32 }, |  | ||||||
| 	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_NUL_STRING, |  | ||||||
| 				    .len = NF_CT_HELPER_NAME_LEN - 1 }, |  | ||||||
| 	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 }, |  | ||||||
| 	[CTA_EXPECT_FLAGS]	= { .type = NLA_U32 }, |  | ||||||
| 	[CTA_EXPECT_CLASS]	= { .type = NLA_U32 }, |  | ||||||
| 	[CTA_EXPECT_NAT]	= { .type = NLA_NESTED }, |  | ||||||
| 	[CTA_EXPECT_FN]		= { .type = NLA_NUL_STRING }, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int | static int | ||||||
| ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, | ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, | ||||||
| 		     const struct nlmsghdr *nlh, | 		     const struct nlmsghdr *nlh, | ||||||
|  |  | ||||||
|  | @ -859,6 +859,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { | ||||||
| 	[NFQA_MARK]		= { .type = NLA_U32 }, | 	[NFQA_MARK]		= { .type = NLA_U32 }, | ||||||
| 	[NFQA_PAYLOAD]		= { .type = NLA_UNSPEC }, | 	[NFQA_PAYLOAD]		= { .type = NLA_UNSPEC }, | ||||||
| 	[NFQA_CT]		= { .type = NLA_UNSPEC }, | 	[NFQA_CT]		= { .type = NLA_UNSPEC }, | ||||||
|  | 	[NFQA_EXP]		= { .type = NLA_UNSPEC }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { | static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { | ||||||
|  | @ -987,8 +988,14 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, | ||||||
| 	if (entry == NULL) | 	if (entry == NULL) | ||||||
| 		return -ENOENT; | 		return -ENOENT; | ||||||
| 
 | 
 | ||||||
| 	if (nfqa[NFQA_CT]) | 	if (nfqa[NFQA_CT]) { | ||||||
| 		ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); | 		ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); | ||||||
|  | 		if (ct && nfqa[NFQA_EXP]) { | ||||||
|  | 			nfqnl_attach_expect(ct, nfqa[NFQA_EXP], | ||||||
|  | 					    NETLINK_CB(skb).portid, | ||||||
|  | 					    nlmsg_report(nlh)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if (nfqa[NFQA_PAYLOAD]) { | 	if (nfqa[NFQA_PAYLOAD]) { | ||||||
| 		u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); | 		u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); | ||||||
|  |  | ||||||
|  | @ -96,3 +96,18 @@ void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, | ||||||
| 	if ((ct->status & IPS_NAT_MASK) && diff) | 	if ((ct->status & IPS_NAT_MASK) && diff) | ||||||
| 		nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); | 		nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, | ||||||
|  | 			u32 portid, u32 report) | ||||||
|  | { | ||||||
|  | 	struct nfq_ct_hook *nfq_ct; | ||||||
|  | 
 | ||||||
|  | 	if (nf_ct_is_untracked(ct)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	nfq_ct = rcu_dereference(nfq_ct_hook); | ||||||
|  | 	if (nfq_ct == NULL) | ||||||
|  | 		return -EOPNOTSUPP; | ||||||
|  | 
 | ||||||
|  | 	return nfq_ct->attach_expect(attr, ct, portid, report); | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Neira Ayuso
				Pablo Neira Ayuso