xref: /linux-6.15/net/netfilter/xt_connbytes.c (revision 643d1f7f)
1 /* Kernel module to match connection tracking byte counter.
2  * GPL (C) 2002 Martin Devera ([email protected]).
3  */
4 #include <linux/module.h>
5 #include <linux/bitops.h>
6 #include <linux/skbuff.h>
7 #include <linux/netfilter/x_tables.h>
8 #include <linux/netfilter/xt_connbytes.h>
9 #include <net/netfilter/nf_conntrack.h>
10 
11 #include <asm/div64.h>
12 
13 MODULE_LICENSE("GPL");
14 MODULE_AUTHOR("Harald Welte <[email protected]>");
15 MODULE_DESCRIPTION("Xtables: Number of packets/bytes per connection matching");
16 MODULE_ALIAS("ipt_connbytes");
17 MODULE_ALIAS("ip6t_connbytes");
18 
19 static bool
20 connbytes_mt(const struct sk_buff *skb, const struct net_device *in,
21              const struct net_device *out, const struct xt_match *match,
22              const void *matchinfo, int offset, unsigned int protoff,
23              bool *hotdrop)
24 {
25 	const struct xt_connbytes_info *sinfo = matchinfo;
26 	const struct nf_conn *ct;
27 	enum ip_conntrack_info ctinfo;
28 	u_int64_t what = 0;	/* initialize to make gcc happy */
29 	u_int64_t bytes = 0;
30 	u_int64_t pkts = 0;
31 	const struct ip_conntrack_counter *counters;
32 
33 	ct = nf_ct_get(skb, &ctinfo);
34 	if (!ct)
35 		return false;
36 	counters = ct->counters;
37 
38 	switch (sinfo->what) {
39 	case XT_CONNBYTES_PKTS:
40 		switch (sinfo->direction) {
41 		case XT_CONNBYTES_DIR_ORIGINAL:
42 			what = counters[IP_CT_DIR_ORIGINAL].packets;
43 			break;
44 		case XT_CONNBYTES_DIR_REPLY:
45 			what = counters[IP_CT_DIR_REPLY].packets;
46 			break;
47 		case XT_CONNBYTES_DIR_BOTH:
48 			what = counters[IP_CT_DIR_ORIGINAL].packets;
49 			what += counters[IP_CT_DIR_REPLY].packets;
50 			break;
51 		}
52 		break;
53 	case XT_CONNBYTES_BYTES:
54 		switch (sinfo->direction) {
55 		case XT_CONNBYTES_DIR_ORIGINAL:
56 			what = counters[IP_CT_DIR_ORIGINAL].bytes;
57 			break;
58 		case XT_CONNBYTES_DIR_REPLY:
59 			what = counters[IP_CT_DIR_REPLY].bytes;
60 			break;
61 		case XT_CONNBYTES_DIR_BOTH:
62 			what = counters[IP_CT_DIR_ORIGINAL].bytes;
63 			what += counters[IP_CT_DIR_REPLY].bytes;
64 			break;
65 		}
66 		break;
67 	case XT_CONNBYTES_AVGPKT:
68 		switch (sinfo->direction) {
69 		case XT_CONNBYTES_DIR_ORIGINAL:
70 			bytes = counters[IP_CT_DIR_ORIGINAL].bytes;
71 			pkts  = counters[IP_CT_DIR_ORIGINAL].packets;
72 			break;
73 		case XT_CONNBYTES_DIR_REPLY:
74 			bytes = counters[IP_CT_DIR_REPLY].bytes;
75 			pkts  = counters[IP_CT_DIR_REPLY].packets;
76 			break;
77 		case XT_CONNBYTES_DIR_BOTH:
78 			bytes = counters[IP_CT_DIR_ORIGINAL].bytes +
79 				counters[IP_CT_DIR_REPLY].bytes;
80 			pkts  = counters[IP_CT_DIR_ORIGINAL].packets +
81 				counters[IP_CT_DIR_REPLY].packets;
82 			break;
83 		}
84 		if (pkts != 0)
85 			what = div64_64(bytes, pkts);
86 		break;
87 	}
88 
89 	if (sinfo->count.to)
90 		return what <= sinfo->count.to && what >= sinfo->count.from;
91 	else
92 		return what >= sinfo->count.from;
93 }
94 
95 static bool
96 connbytes_mt_check(const char *tablename, const void *ip,
97                    const struct xt_match *match, void *matchinfo,
98                    unsigned int hook_mask)
99 {
100 	const struct xt_connbytes_info *sinfo = matchinfo;
101 
102 	if (sinfo->what != XT_CONNBYTES_PKTS &&
103 	    sinfo->what != XT_CONNBYTES_BYTES &&
104 	    sinfo->what != XT_CONNBYTES_AVGPKT)
105 		return false;
106 
107 	if (sinfo->direction != XT_CONNBYTES_DIR_ORIGINAL &&
108 	    sinfo->direction != XT_CONNBYTES_DIR_REPLY &&
109 	    sinfo->direction != XT_CONNBYTES_DIR_BOTH)
110 		return false;
111 
112 	if (nf_ct_l3proto_try_module_get(match->family) < 0) {
113 		printk(KERN_WARNING "can't load conntrack support for "
114 				    "proto=%u\n", match->family);
115 		return false;
116 	}
117 
118 	return true;
119 }
120 
121 static void
122 connbytes_mt_destroy(const struct xt_match *match, void *matchinfo)
123 {
124 	nf_ct_l3proto_module_put(match->family);
125 }
126 
127 static struct xt_match connbytes_mt_reg[] __read_mostly = {
128 	{
129 		.name		= "connbytes",
130 		.family		= AF_INET,
131 		.checkentry	= connbytes_mt_check,
132 		.match		= connbytes_mt,
133 		.destroy	= connbytes_mt_destroy,
134 		.matchsize	= sizeof(struct xt_connbytes_info),
135 		.me		= THIS_MODULE
136 	},
137 	{
138 		.name		= "connbytes",
139 		.family		= AF_INET6,
140 		.checkentry	= connbytes_mt_check,
141 		.match		= connbytes_mt,
142 		.destroy	= connbytes_mt_destroy,
143 		.matchsize	= sizeof(struct xt_connbytes_info),
144 		.me		= THIS_MODULE
145 	},
146 };
147 
148 static int __init connbytes_mt_init(void)
149 {
150 	return xt_register_matches(connbytes_mt_reg,
151 	       ARRAY_SIZE(connbytes_mt_reg));
152 }
153 
154 static void __exit connbytes_mt_exit(void)
155 {
156 	xt_unregister_matches(connbytes_mt_reg, ARRAY_SIZE(connbytes_mt_reg));
157 }
158 
159 module_init(connbytes_mt_init);
160 module_exit(connbytes_mt_exit);
161