File Index
Files and directories
Sign in
FILE

nf_conntrack_expect.c

File preview with syntax highlighting.

linux-6.19.14/net/netfilter/nf_conntrack_expect.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Expectation handling for nf_conntrack. */
  3
  4/* (C) 1999-2001 Paul `Rusty' Russell
  5 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
  6 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
  7 * (c) 2005-2012 Patrick McHardy <kaber@trash.net>
  8 */
  9
 10#include <linux/types.h>
 11#include <linux/netfilter.h>
 12#include <linux/skbuff.h>
 13#include <linux/proc_fs.h>
 14#include <linux/seq_file.h>
 15#include <linux/stddef.h>
 16#include <linux/slab.h>
 17#include <linux/err.h>
 18#include <linux/percpu.h>
 19#include <linux/kernel.h>
 20#include <linux/siphash.h>
 21#include <linux/moduleparam.h>
 22#include <linux/export.h>
 23#include <net/net_namespace.h>
 24#include <net/netns/hash.h>
 25
 26#include <net/netfilter/nf_conntrack.h>
 27#include <net/netfilter/nf_conntrack_core.h>
 28#include <net/netfilter/nf_conntrack_ecache.h>
 29#include <net/netfilter/nf_conntrack_expect.h>
 30#include <net/netfilter/nf_conntrack_helper.h>
 31#include <net/netfilter/nf_conntrack_l4proto.h>
 32#include <net/netfilter/nf_conntrack_tuple.h>
 33#include <net/netfilter/nf_conntrack_zones.h>
 34
 35unsigned int nf_ct_expect_hsize __read_mostly;
 36EXPORT_SYMBOL_GPL(nf_ct_expect_hsize);
 37
 38struct hlist_head *nf_ct_expect_hash __read_mostly;
 39EXPORT_SYMBOL_GPL(nf_ct_expect_hash);
 40
 41unsigned int nf_ct_expect_max __read_mostly;
 42
 43static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
 44static siphash_aligned_key_t nf_ct_expect_hashrnd;
 45
 46/* nf_conntrack_expect helper functions */
 47void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
 48				u32 portid, int report)
 49{
 50	struct nf_conn_help *master_help = nfct_help(exp->master);
 51	struct net *net = nf_ct_exp_net(exp);
 52	struct nf_conntrack_net *cnet;
 53
 54	lockdep_nfct_expect_lock_held();
 55	WARN_ON(!master_help);
 56	WARN_ON(timer_pending(&exp->timeout));
 57
 58	hlist_del_rcu(&exp->hnode);
 59
 60	cnet = nf_ct_pernet(net);
 61	cnet->expect_count--;
 62
 63	hlist_del_rcu(&exp->lnode);
 64	master_help->expecting[exp->class]--;
 65
 66	nf_ct_expect_event_report(IPEXP_DESTROY, exp, portid, report);
 67	nf_ct_expect_put(exp);
 68
 69	NF_CT_STAT_INC(net, expect_delete);
 70}
 71EXPORT_SYMBOL_GPL(nf_ct_unlink_expect_report);
 72
 73static void nf_ct_expectation_timed_out(struct timer_list *t)
 74{
 75	struct nf_conntrack_expect *exp = timer_container_of(exp, t, timeout);
 76
 77	spin_lock_bh(&nf_conntrack_expect_lock);
 78	nf_ct_unlink_expect(exp);
 79	spin_unlock_bh(&nf_conntrack_expect_lock);
 80	nf_ct_expect_put(exp);
 81}
 82
 83static unsigned int nf_ct_expect_dst_hash(const struct net *n, const struct nf_conntrack_tuple *tuple)
 84{
 85	struct {
 86		union nf_inet_addr dst_addr;
 87		u32 net_mix;
 88		u16 dport;
 89		u8 l3num;
 90		u8 protonum;
 91	} __aligned(SIPHASH_ALIGNMENT) combined;
 92	u32 hash;
 93
 94	get_random_once(&nf_ct_expect_hashrnd, sizeof(nf_ct_expect_hashrnd));
 95
 96	memset(&combined, 0, sizeof(combined));
 97
 98	combined.dst_addr = tuple->dst.u3;
 99	combined.net_mix = net_hash_mix(n);
100	combined.dport = (__force __u16)tuple->dst.u.all;
101	combined.l3num = tuple->src.l3num;
102	combined.protonum = tuple->dst.protonum;
103
104	hash = siphash(&combined, sizeof(combined), &nf_ct_expect_hashrnd);
105
106	return reciprocal_scale(hash, nf_ct_expect_hsize);
107}
108
109static bool
110nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
111		const struct nf_conntrack_expect *i,
112		const struct nf_conntrack_zone *zone,
113		const struct net *net)
114{
115	return nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask) &&
116	       net_eq(net, read_pnet(&i->net)) &&
117	       nf_ct_exp_zone_equal_any(i, zone);
118}
119
120bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
121{
122	lockdep_nfct_expect_lock_held();
123
124	if (timer_delete(&exp->timeout)) {
125		nf_ct_unlink_expect(exp);
126		nf_ct_expect_put(exp);
127		return true;
128	}
129	return false;
130}
131EXPORT_SYMBOL_GPL(nf_ct_remove_expect);
132
133struct nf_conntrack_expect *
134__nf_ct_expect_find(struct net *net,
135		    const struct nf_conntrack_zone *zone,
136		    const struct nf_conntrack_tuple *tuple)
137{
138	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
139	struct nf_conntrack_expect *i;
140	unsigned int h;
141
142	if (!cnet->expect_count)
143		return NULL;
144
145	h = nf_ct_expect_dst_hash(net, tuple);
146	hlist_for_each_entry_rcu(i, &nf_ct_expect_hash[h], hnode) {
147		if (nf_ct_exp_equal(tuple, i, zone, net))
148			return i;
149	}
150	return NULL;
151}
152EXPORT_SYMBOL_GPL(__nf_ct_expect_find);
153
154/* Just find a expectation corresponding to a tuple. */
155struct nf_conntrack_expect *
156nf_ct_expect_find_get(struct net *net,
157		      const struct nf_conntrack_zone *zone,
158		      const struct nf_conntrack_tuple *tuple)
159{
160	struct nf_conntrack_expect *i;
161
162	rcu_read_lock();
163	i = __nf_ct_expect_find(net, zone, tuple);
164	if (i && !refcount_inc_not_zero(&i->use))
165		i = NULL;
166	rcu_read_unlock();
167
168	return i;
169}
170EXPORT_SYMBOL_GPL(nf_ct_expect_find_get);
171
172/* If an expectation for this connection is found, it gets delete from
173 * global list then returned. */
174struct nf_conntrack_expect *
175nf_ct_find_expectation(struct net *net,
176		       const struct nf_conntrack_zone *zone,
177		       const struct nf_conntrack_tuple *tuple, bool unlink)
178{
179	struct nf_conntrack_net *cnet = nf_ct_pernet(net);
180	struct nf_conntrack_expect *i, *exp = NULL;
181	unsigned int h;
182
183	lockdep_nfct_expect_lock_held();
184
185	if (!cnet->expect_count)
186		return NULL;
187
188	h = nf_ct_expect_dst_hash(net, tuple);
189	hlist_for_each_entry(i, &nf_ct_expect_hash[h], hnode) {
190		if (!(i->flags & NF_CT_EXPECT_INACTIVE) &&
191		    nf_ct_exp_equal(tuple, i, zone, net)) {
192			exp = i;
193			break;
194		}
195	}
196	if (!exp)
197		return NULL;
198
199	/* If master is not in hash table yet (ie. packet hasn't left
200	   this machine yet), how can other end know about expected?
201	   Hence these are not the droids you are looking for (if
202	   master ct never got confirmed, we'd hold a reference to it
203	   and weird things would happen to future packets). */
204	if (!nf_ct_is_confirmed(exp->master))
205		return NULL;
206
207	/* Avoid race with other CPUs, that for exp->master ct, is
208	 * about to invoke ->destroy(), or nf_ct_delete() via timeout
209	 * or early_drop().
210	 *
211	 * The refcount_inc_not_zero() check tells:  If that fails, we
212	 * know that the ct is being destroyed.  If it succeeds, we
213	 * can be sure the ct cannot disappear underneath.
214	 */
215	if (unlikely(nf_ct_is_dying(exp->master) ||
216		     !refcount_inc_not_zero(&exp->master->ct_general.use)))
217		return NULL;
218
219	if (exp->flags & NF_CT_EXPECT_PERMANENT || !unlink) {
220		refcount_inc(&exp->use);
221		return exp;
222	} else if (timer_delete(&exp->timeout)) {
223		nf_ct_unlink_expect(exp);
224		return exp;
225	}
226	/* Undo exp->master refcnt increase, if timer_delete() failed */
227	nf_ct_put(exp->master);
228
229	return NULL;
230}
231
232/* delete all expectations for this conntrack */
233void nf_ct_remove_expectations(struct nf_conn *ct)
234{
235	struct nf_conn_help *help = nfct_help(ct);
236	struct nf_conntrack_expect *exp;
237	struct hlist_node *next;
238
239	/* Optimization: most connection never expect any others. */
240	if (!help)
241		return;
242
243	spin_lock_bh(&nf_conntrack_expect_lock);
244	hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) {
245		nf_ct_remove_expect(exp);
246	}
247	spin_unlock_bh(&nf_conntrack_expect_lock);
248}
249EXPORT_SYMBOL_GPL(nf_ct_remove_expectations);
250
251/* Would two expected things clash? */
252static inline int expect_clash(const struct nf_conntrack_expect *a,
253			       const struct nf_conntrack_expect *b)
254{
255	/* Part covered by intersection of masks must be unequal,
256	   otherwise they clash */
257	struct nf_conntrack_tuple_mask intersect_mask;
258	int count;
259
260	intersect_mask.src.u.all = a->mask.src.u.all & b->mask.src.u.all;
261
262	for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++){
263		intersect_mask.src.u3.all[count] =
264			a->mask.src.u3.all[count] & b->mask.src.u3.all[count];
265	}
266
267	return nf_ct_tuple_mask_cmp(&a->tuple, &b->tuple, &intersect_mask) &&
268	       net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) &&
269	       nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master));
270}
271
272static inline int expect_matches(const struct nf_conntrack_expect *a,
273				 const struct nf_conntrack_expect *b)
274{
275	return nf_ct_tuple_equal(&a->tuple, &b->tuple) &&
276	       nf_ct_tuple_mask_equal(&a->mask, &b->mask) &&
277	       net_eq(nf_ct_net(a->master), nf_ct_net(b->master)) &&
278	       nf_ct_zone_equal_any(a->master, nf_ct_zone(b->master));
279}
280
281static bool master_matches(const struct nf_conntrack_expect *a,
282			   const struct nf_conntrack_expect *b,
283			   unsigned int flags)
284{
285	if (flags & NF_CT_EXP_F_SKIP_MASTER)
286		return true;
287
288	return a->master == b->master;
289}
290
291/* Generally a bad idea to call this: could have matched already. */
292void nf_ct_unexpect_related(struct nf_conntrack_expect *exp)
293{
294	spin_lock_bh(&nf_conntrack_expect_lock);
295	nf_ct_remove_expect(exp);
296	spin_unlock_bh(&nf_conntrack_expect_lock);
297}
298EXPORT_SYMBOL_GPL(nf_ct_unexpect_related);
299
300/* We don't increase the master conntrack refcount for non-fulfilled
301 * conntracks. During the conntrack destruction, the expectations are
302 * always killed before the conntrack itself */
303struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
304{
305	struct nf_conntrack_expect *new;
306
307	new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC);
308	if (!new)
309		return NULL;
310
311	new->master = me;
312	refcount_set(&new->use, 1);
313	return new;
314}
315EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);
316
317/* This function can only be used from packet path, where accessing
318 * master's helper is safe, because the packet holds a reference on
319 * the conntrack object. Never use it from control plane.
320 */
321void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
322		       u_int8_t family,
323		       const union nf_inet_addr *saddr,
324		       const union nf_inet_addr *daddr,
325		       u_int8_t proto, const __be16 *src, const __be16 *dst)
326{
327	struct nf_conntrack_helper *helper = NULL;
328	struct nf_conn *ct = exp->master;
329	struct net *net = read_pnet(&ct->ct_net);
330	struct nf_conn_help *help;
331	int len;
332
333	if (family == AF_INET)
334		len = 4;
335	else
336		len = 16;
337
338	exp->flags = 0;
339	exp->class = class;
340	exp->expectfn = NULL;
341
342	help = nfct_help(ct);
343	if (help)
344		helper = rcu_dereference(help->helper);
345
346	rcu_assign_pointer(exp->helper, helper);
347	write_pnet(&exp->net, net);
348#ifdef CONFIG_NF_CONNTRACK_ZONES
349	exp->zone = ct->zone;
350#endif
351	exp->tuple.src.l3num = family;
352	exp->tuple.dst.protonum = proto;
353
354	if (saddr) {
355		memcpy(&exp->tuple.src.u3, saddr, len);
356		if (sizeof(exp->tuple.src.u3) > len)
357			/* address needs to be cleared for nf_ct_tuple_equal */
358			memset((void *)&exp->tuple.src.u3 + len, 0x00,
359			       sizeof(exp->tuple.src.u3) - len);
360		memset(&exp->mask.src.u3, 0xFF, len);
361		if (sizeof(exp->mask.src.u3) > len)
362			memset((void *)&exp->mask.src.u3 + len, 0x00,
363			       sizeof(exp->mask.src.u3) - len);
364	} else {
365		memset(&exp->tuple.src.u3, 0x00, sizeof(exp->tuple.src.u3));
366		memset(&exp->mask.src.u3, 0x00, sizeof(exp->mask.src.u3));
367	}
368
369	if (src) {
370		exp->tuple.src.u.all = *src;
371		exp->mask.src.u.all = htons(0xFFFF);
372	} else {
373		exp->tuple.src.u.all = 0;
374		exp->mask.src.u.all = 0;
375	}
376
377	memcpy(&exp->tuple.dst.u3, daddr, len);
378	if (sizeof(exp->tuple.dst.u3) > len)
379		/* address needs to be cleared for nf_ct_tuple_equal */
380		memset((void *)&exp->tuple.dst.u3 + len, 0x00,
381		       sizeof(exp->tuple.dst.u3) - len);
382
383	exp->tuple.dst.u.all = *dst;
384
385#if IS_ENABLED(CONFIG_NF_NAT)
386	memset(&exp->saved_addr, 0, sizeof(exp->saved_addr));
387	memset(&exp->saved_proto, 0, sizeof(exp->saved_proto));
388#endif
389}
390EXPORT_SYMBOL_GPL(nf_ct_expect_init);
391
392static void nf_ct_expect_free_rcu(struct rcu_head *head)
393{
394	struct nf_conntrack_expect *exp;
395
396	exp = container_of(head, struct nf_conntrack_expect, rcu);
397	kmem_cache_free(nf_ct_expect_cachep, exp);
398}
399
400void nf_ct_expect_put(struct nf_conntrack_expect *exp)
401{
402	if (refcount_dec_and_test(&exp->use))
403		call_rcu(&exp->rcu, nf_ct_expect_free_rcu);
404}
405EXPORT_SYMBOL_GPL(nf_ct_expect_put);
406
407static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
408{
409	struct nf_conntrack_net *cnet;
410	struct nf_conn_help *master_help = nfct_help(exp->master);
411	struct nf_conntrack_helper *helper;
412	struct net *net = nf_ct_exp_net(exp);
413	unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple);
414
415	/* two references : one for hash insert, one for the timer */
416	refcount_add(2, &exp->use);
417
418	timer_setup(&exp->timeout, nf_ct_expectation_timed_out, 0);
419	helper = rcu_dereference_protected(master_help->helper,
420					   lockdep_is_held(&nf_conntrack_expect_lock));
421	if (helper) {
422		exp->timeout.expires = jiffies +
423			helper->expect_policy[exp->class].timeout * HZ;
424	}
425	add_timer(&exp->timeout);
426
427	hlist_add_head_rcu(&exp->lnode, &master_help->expectations);
428	master_help->expecting[exp->class]++;
429
430	hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
431	cnet = nf_ct_pernet(net);
432	cnet->expect_count++;
433
434	NF_CT_STAT_INC(net, expect_create);
435}
436
437/* Race with expectations being used means we could have none to find; OK. */
438static void evict_oldest_expect(struct nf_conn *master,
439				struct nf_conntrack_expect *new)
440{
441	struct nf_conn_help *master_help = nfct_help(master);
442	struct nf_conntrack_expect *exp, *last = NULL;
443
444	hlist_for_each_entry(exp, &master_help->expectations, lnode) {
445		if (exp->class == new->class)
446			last = exp;
447	}
448
449	if (last)
450		nf_ct_remove_expect(last);
451}
452
453static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
454				       unsigned int flags)
455{
456	const struct nf_conntrack_expect_policy *p;
457	struct nf_conntrack_expect *i;
458	struct nf_conntrack_net *cnet;
459	struct nf_conn *master = expect->master;
460	struct nf_conn_help *master_help = nfct_help(master);
461	struct nf_conntrack_helper *helper;
462	struct net *net = nf_ct_exp_net(expect);
463	struct hlist_node *next;
464	unsigned int h;
465	int ret = 0;
466
467	lockdep_nfct_expect_lock_held();
468
469	if (!master_help) {
470		ret = -ESHUTDOWN;
471		goto out;
472	}
473	h = nf_ct_expect_dst_hash(net, &expect->tuple);
474	hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) {
475		if (master_matches(i, expect, flags) &&
476		    expect_matches(i, expect)) {
477			if (i->class != expect->class ||
478			    i->master != expect->master)
479				return -EALREADY;
480
481			if (nf_ct_remove_expect(i))
482				break;
483		} else if (expect_clash(i, expect)) {
484			ret = -EBUSY;
485			goto out;
486		}
487	}
488	/* Will be over limit? */
489	helper = rcu_dereference_protected(master_help->helper,
490					   lockdep_is_held(&nf_conntrack_expect_lock));
491	if (helper) {
492		p = &helper->expect_policy[expect->class];
493		if (p->max_expected &&
494		    master_help->expecting[expect->class] >= p->max_expected) {
495			evict_oldest_expect(master, expect);
496			if (master_help->expecting[expect->class]
497						>= p->max_expected) {
498				ret = -EMFILE;
499				goto out;
500			}
501		}
502	}
503
504	cnet = nf_ct_pernet(net);
505	if (cnet->expect_count >= nf_ct_expect_max) {
506		net_warn_ratelimited("nf_conntrack: expectation table full\n");
507		ret = -EMFILE;
508	}
509out:
510	return ret;
511}
512
513int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
514				u32 portid, int report, unsigned int flags)
515{
516	int ret;
517
518	spin_lock_bh(&nf_conntrack_expect_lock);
519	ret = __nf_ct_expect_check(expect, flags);
520	if (ret < 0)
521		goto out;
522
523	nf_ct_expect_insert(expect);
524
525	nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
526	spin_unlock_bh(&nf_conntrack_expect_lock);
527
528	return 0;
529out:
530	spin_unlock_bh(&nf_conntrack_expect_lock);
531	return ret;
532}
533EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
534
535void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data),
536				  void *data)
537{
538	struct nf_conntrack_expect *exp;
539	const struct hlist_node *next;
540	unsigned int i;
541
542	spin_lock_bh(&nf_conntrack_expect_lock);
543
544	for (i = 0; i < nf_ct_expect_hsize; i++) {
545		hlist_for_each_entry_safe(exp, next,
546					  &nf_ct_expect_hash[i],
547					  hnode) {
548			if (iter(exp, data) && timer_delete(&exp->timeout)) {
549				nf_ct_unlink_expect(exp);
550				nf_ct_expect_put(exp);
551			}
552		}
553	}
554
555	spin_unlock_bh(&nf_conntrack_expect_lock);
556}
557EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_destroy);
558
559void nf_ct_expect_iterate_net(struct net *net,
560			      bool (*iter)(struct nf_conntrack_expect *e, void *data),
561			      void *data,
562			      u32 portid, int report)
563{
564	struct nf_conntrack_expect *exp;
565	const struct hlist_node *next;
566	unsigned int i;
567
568	spin_lock_bh(&nf_conntrack_expect_lock);
569
570	for (i = 0; i < nf_ct_expect_hsize; i++) {
571		hlist_for_each_entry_safe(exp, next,
572					  &nf_ct_expect_hash[i],
573					  hnode) {
574
575			if (!net_eq(nf_ct_exp_net(exp), net))
576				continue;
577
578			if (iter(exp, data) && timer_delete(&exp->timeout)) {
579				nf_ct_unlink_expect_report(exp, portid, report);
580				nf_ct_expect_put(exp);
581			}
582		}
583	}
584
585	spin_unlock_bh(&nf_conntrack_expect_lock);
586}
587EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_net);
588
589#ifdef CONFIG_NF_CONNTRACK_PROCFS
590struct ct_expect_iter_state {
591	struct seq_net_private p;
592	unsigned int bucket;
593};
594
595static struct hlist_node *ct_expect_get_first(struct seq_file *seq)
596{
597	struct ct_expect_iter_state *st = seq->private;
598	struct hlist_node *n;
599
600	for (st->bucket = 0; st->bucket < nf_ct_expect_hsize; st->bucket++) {
601		n = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket]));
602		if (n)
603			return n;
604	}
605	return NULL;
606}
607
608static struct hlist_node *ct_expect_get_next(struct seq_file *seq,
609					     struct hlist_node *head)
610{
611	struct ct_expect_iter_state *st = seq->private;
612
613	head = rcu_dereference(hlist_next_rcu(head));
614	while (head == NULL) {
615		if (++st->bucket >= nf_ct_expect_hsize)
616			return NULL;
617		head = rcu_dereference(hlist_first_rcu(&nf_ct_expect_hash[st->bucket]));
618	}
619	return head;
620}
621
622static struct hlist_node *ct_expect_get_idx(struct seq_file *seq, loff_t pos)
623{
624	struct hlist_node *head = ct_expect_get_first(seq);
625
626	if (head)
627		while (pos && (head = ct_expect_get_next(seq, head)))
628			pos--;
629	return pos ? NULL : head;
630}
631
632static void *exp_seq_start(struct seq_file *seq, loff_t *pos)
633	__acquires(RCU)
634{
635	rcu_read_lock();
636	return ct_expect_get_idx(seq, *pos);
637}
638
639static void *exp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
640{
641	(*pos)++;
642	return ct_expect_get_next(seq, v);
643}
644
645static void exp_seq_stop(struct seq_file *seq, void *v)
646	__releases(RCU)
647{
648	rcu_read_unlock();
649}
650
651static int exp_seq_show(struct seq_file *s, void *v)
652{
653	struct nf_conntrack_expect *expect;
654	struct nf_conntrack_helper *helper;
655	struct net *net = seq_file_net(s);
656	struct hlist_node *n = v;
657	char *delim = "";
658
659	expect = hlist_entry(n, struct nf_conntrack_expect, hnode);
660
661	if (!net_eq(nf_ct_exp_net(expect), net))
662		return 0;
663
664	if (expect->timeout.function)
665		seq_printf(s, "%ld ", timer_pending(&expect->timeout)
666			   ? (long)(expect->timeout.expires - jiffies)/HZ : 0);
667	else
668		seq_puts(s, "- ");
669	seq_printf(s, "l3proto = %u proto=%u ",
670		   expect->tuple.src.l3num,
671		   expect->tuple.dst.protonum);
672	print_tuple(s, &expect->tuple,
673		    nf_ct_l4proto_find(expect->tuple.dst.protonum));
674
675	if (expect->flags & NF_CT_EXPECT_PERMANENT) {
676		seq_puts(s, "PERMANENT");
677		delim = ",";
678	}
679	if (expect->flags & NF_CT_EXPECT_INACTIVE) {
680		seq_printf(s, "%sINACTIVE", delim);
681		delim = ",";
682	}
683	if (expect->flags & NF_CT_EXPECT_USERSPACE)
684		seq_printf(s, "%sUSERSPACE", delim);
685
686	helper = rcu_dereference(expect->helper);
687	if (helper) {
688		seq_printf(s, "%s%s", expect->flags ? " " : "", helper->name);
689		if (helper->expect_policy[expect->class].name[0])
690			seq_printf(s, "/%s",
691				   helper->expect_policy[expect->class].name);
692	}
693
694	seq_putc(s, '\n');
695
696	return 0;
697}
698
699static const struct seq_operations exp_seq_ops = {
700	.start = exp_seq_start,
701	.next = exp_seq_next,
702	.stop = exp_seq_stop,
703	.show = exp_seq_show
704};
705#endif /* CONFIG_NF_CONNTRACK_PROCFS */
706
707static int exp_proc_init(struct net *net)
708{
709#ifdef CONFIG_NF_CONNTRACK_PROCFS
710	struct proc_dir_entry *proc;
711	kuid_t root_uid;
712	kgid_t root_gid;
713
714	proc = proc_create_net("nf_conntrack_expect", 0440, net->proc_net,
715			&exp_seq_ops, sizeof(struct ct_expect_iter_state));
716	if (!proc)
717		return -ENOMEM;
718
719	root_uid = make_kuid(net->user_ns, 0);
720	root_gid = make_kgid(net->user_ns, 0);
721	if (uid_valid(root_uid) && gid_valid(root_gid))
722		proc_set_user(proc, root_uid, root_gid);
723#endif /* CONFIG_NF_CONNTRACK_PROCFS */
724	return 0;
725}
726
727static void exp_proc_remove(struct net *net)
728{
729#ifdef CONFIG_NF_CONNTRACK_PROCFS
730	remove_proc_entry("nf_conntrack_expect", net->proc_net);
731#endif /* CONFIG_NF_CONNTRACK_PROCFS */
732}
733
734module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
735
736int nf_conntrack_expect_pernet_init(struct net *net)
737{
738	return exp_proc_init(net);
739}
740
741void nf_conntrack_expect_pernet_fini(struct net *net)
742{
743	exp_proc_remove(net);
744}
745
746int nf_conntrack_expect_init(void)
747{
748	if (!nf_ct_expect_hsize) {
749		nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
750		if (!nf_ct_expect_hsize)
751			nf_ct_expect_hsize = 1;
752	}
753	nf_ct_expect_max = nf_ct_expect_hsize * 4;
754	nf_ct_expect_cachep = KMEM_CACHE(nf_conntrack_expect, 0);
755	if (!nf_ct_expect_cachep)
756		return -ENOMEM;
757
758	nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
759	if (!nf_ct_expect_hash) {
760		kmem_cache_destroy(nf_ct_expect_cachep);
761		return -ENOMEM;
762	}
763
764	return 0;
765}
766
767void nf_conntrack_expect_fini(void)
768{
769	rcu_barrier(); /* Wait for call_rcu() before destroy */
770	kmem_cache_destroy(nf_ct_expect_cachep);
771	kvfree(nf_ct_expect_hash);
772}
~/ Read-only file preview.