Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Kernel module to match connection tracking information. |
2 | * Superset of Rusty's minimalistic state match. | |
3 | * | |
4 | * (C) 2001 Marc Boucher (marc@mbsi.ca). | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/skbuff.h> | |
9fb9cbb1 YK |
13 | |
14 | #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) | |
1da177e4 | 15 | #include <linux/netfilter_ipv4/ip_conntrack.h> |
9fb9cbb1 YK |
16 | #include <linux/netfilter_ipv4/ip_conntrack_tuple.h> |
17 | #else | |
18 | #include <net/netfilter/nf_conntrack.h> | |
19 | #endif | |
20 | ||
2e4e6a17 HW |
21 | #include <linux/netfilter/x_tables.h> |
22 | #include <linux/netfilter/xt_conntrack.h> | |
fe0b9294 | 23 | #include <net/netfilter/nf_conntrack_compat.h> |
1da177e4 LT |
24 | |
25 | MODULE_LICENSE("GPL"); | |
26 | MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); | |
27 | MODULE_DESCRIPTION("iptables connection tracking match module"); | |
2e4e6a17 | 28 | MODULE_ALIAS("ipt_conntrack"); |
1da177e4 | 29 | |
9fb9cbb1 YK |
30 | #if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) |
31 | ||
1da177e4 LT |
32 | static int |
33 | match(const struct sk_buff *skb, | |
34 | const struct net_device *in, | |
35 | const struct net_device *out, | |
c4986734 | 36 | const struct xt_match *match, |
1da177e4 LT |
37 | const void *matchinfo, |
38 | int offset, | |
2e4e6a17 | 39 | unsigned int protoff, |
1da177e4 LT |
40 | int *hotdrop) |
41 | { | |
2e4e6a17 | 42 | const struct xt_conntrack_info *sinfo = matchinfo; |
1da177e4 LT |
43 | struct ip_conntrack *ct; |
44 | enum ip_conntrack_info ctinfo; | |
45 | unsigned int statebit; | |
46 | ||
47 | ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); | |
48 | ||
50b9f1d5 | 49 | #define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg)) |
1da177e4 LT |
50 | |
51 | if (ct == &ip_conntrack_untracked) | |
2e4e6a17 | 52 | statebit = XT_CONNTRACK_STATE_UNTRACKED; |
1da177e4 | 53 | else if (ct) |
601e68e1 YH |
54 | statebit = XT_CONNTRACK_STATE_BIT(ctinfo); |
55 | else | |
56 | statebit = XT_CONNTRACK_STATE_INVALID; | |
57 | ||
50b9f1d5 | 58 | if (sinfo->flags & XT_CONNTRACK_STATE) { |
1da177e4 | 59 | if (ct) { |
50b9f1d5 | 60 | if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) |
2e4e6a17 | 61 | statebit |= XT_CONNTRACK_STATE_SNAT; |
50b9f1d5 | 62 | if (test_bit(IPS_DST_NAT_BIT, &ct->status)) |
2e4e6a17 | 63 | statebit |= XT_CONNTRACK_STATE_DNAT; |
1da177e4 | 64 | } |
50b9f1d5 PM |
65 | if (FWINV((statebit & sinfo->statemask) == 0, |
66 | XT_CONNTRACK_STATE)) | |
1da177e4 LT |
67 | return 0; |
68 | } | |
69 | ||
50b9f1d5 PM |
70 | if (ct == NULL) { |
71 | if (sinfo->flags & ~XT_CONNTRACK_STATE) | |
1da177e4 | 72 | return 0; |
50b9f1d5 | 73 | return 1; |
1da177e4 LT |
74 | } |
75 | ||
50b9f1d5 PM |
76 | if (sinfo->flags & XT_CONNTRACK_PROTO && |
77 | FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != | |
78 | sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, | |
79 | XT_CONNTRACK_PROTO)) | |
601e68e1 | 80 | return 0; |
50b9f1d5 PM |
81 | |
82 | if (sinfo->flags & XT_CONNTRACK_ORIGSRC && | |
83 | FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip & | |
84 | sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != | |
85 | sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, | |
86 | XT_CONNTRACK_ORIGSRC)) | |
87 | return 0; | |
1da177e4 | 88 | |
50b9f1d5 PM |
89 | if (sinfo->flags & XT_CONNTRACK_ORIGDST && |
90 | FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip & | |
91 | sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != | |
92 | sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, | |
93 | XT_CONNTRACK_ORIGDST)) | |
94 | return 0; | |
1da177e4 | 95 | |
50b9f1d5 PM |
96 | if (sinfo->flags & XT_CONNTRACK_REPLSRC && |
97 | FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip & | |
98 | sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != | |
99 | sinfo->tuple[IP_CT_DIR_REPLY].src.ip, | |
100 | XT_CONNTRACK_REPLSRC)) | |
101 | return 0; | |
1da177e4 | 102 | |
50b9f1d5 PM |
103 | if (sinfo->flags & XT_CONNTRACK_REPLDST && |
104 | FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip & | |
105 | sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != | |
106 | sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, | |
107 | XT_CONNTRACK_REPLDST)) | |
108 | return 0; | |
1da177e4 | 109 | |
50b9f1d5 PM |
110 | if (sinfo->flags & XT_CONNTRACK_STATUS && |
111 | FWINV((ct->status & sinfo->statusmask) == 0, | |
112 | XT_CONNTRACK_STATUS)) | |
113 | return 0; | |
1da177e4 | 114 | |
50b9f1d5 PM |
115 | if (sinfo->flags & XT_CONNTRACK_EXPIRES) { |
116 | unsigned long expires = timer_pending(&ct->timeout) ? | |
117 | (ct->timeout.expires - jiffies)/HZ : 0; | |
1da177e4 | 118 | |
50b9f1d5 PM |
119 | if (FWINV(!(expires >= sinfo->expires_min && |
120 | expires <= sinfo->expires_max), | |
121 | XT_CONNTRACK_EXPIRES)) | |
1da177e4 LT |
122 | return 0; |
123 | } | |
1da177e4 LT |
124 | return 1; |
125 | } | |
126 | ||
9fb9cbb1 YK |
127 | #else /* CONFIG_IP_NF_CONNTRACK */ |
128 | static int | |
129 | match(const struct sk_buff *skb, | |
130 | const struct net_device *in, | |
131 | const struct net_device *out, | |
c4986734 | 132 | const struct xt_match *match, |
9fb9cbb1 YK |
133 | const void *matchinfo, |
134 | int offset, | |
2e4e6a17 | 135 | unsigned int protoff, |
9fb9cbb1 YK |
136 | int *hotdrop) |
137 | { | |
2e4e6a17 | 138 | const struct xt_conntrack_info *sinfo = matchinfo; |
9fb9cbb1 YK |
139 | struct nf_conn *ct; |
140 | enum ip_conntrack_info ctinfo; | |
141 | unsigned int statebit; | |
142 | ||
143 | ct = nf_ct_get((struct sk_buff *)skb, &ctinfo); | |
144 | ||
145 | #define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) | |
146 | ||
147 | if (ct == &nf_conntrack_untracked) | |
2e4e6a17 | 148 | statebit = XT_CONNTRACK_STATE_UNTRACKED; |
9fb9cbb1 | 149 | else if (ct) |
601e68e1 YH |
150 | statebit = XT_CONNTRACK_STATE_BIT(ctinfo); |
151 | else | |
152 | statebit = XT_CONNTRACK_STATE_INVALID; | |
153 | ||
50b9f1d5 | 154 | if (sinfo->flags & XT_CONNTRACK_STATE) { |
9fb9cbb1 | 155 | if (ct) { |
50b9f1d5 | 156 | if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) |
2e4e6a17 | 157 | statebit |= XT_CONNTRACK_STATE_SNAT; |
50b9f1d5 | 158 | if (test_bit(IPS_DST_NAT_BIT, &ct->status)) |
2e4e6a17 | 159 | statebit |= XT_CONNTRACK_STATE_DNAT; |
9fb9cbb1 | 160 | } |
50b9f1d5 PM |
161 | if (FWINV((statebit & sinfo->statemask) == 0, |
162 | XT_CONNTRACK_STATE)) | |
9fb9cbb1 YK |
163 | return 0; |
164 | } | |
165 | ||
50b9f1d5 PM |
166 | if (ct == NULL) { |
167 | if (sinfo->flags & ~XT_CONNTRACK_STATE) | |
9fb9cbb1 | 168 | return 0; |
50b9f1d5 | 169 | return 1; |
9fb9cbb1 YK |
170 | } |
171 | ||
50b9f1d5 PM |
172 | if (sinfo->flags & XT_CONNTRACK_PROTO && |
173 | FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != | |
601e68e1 | 174 | sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, |
50b9f1d5 | 175 | XT_CONNTRACK_PROTO)) |
601e68e1 | 176 | return 0; |
50b9f1d5 PM |
177 | |
178 | if (sinfo->flags & XT_CONNTRACK_ORIGSRC && | |
179 | FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & | |
601e68e1 | 180 | sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != |
50b9f1d5 PM |
181 | sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, |
182 | XT_CONNTRACK_ORIGSRC)) | |
183 | return 0; | |
9fb9cbb1 | 184 | |
50b9f1d5 PM |
185 | if (sinfo->flags & XT_CONNTRACK_ORIGDST && |
186 | FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & | |
601e68e1 | 187 | sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != |
50b9f1d5 PM |
188 | sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, |
189 | XT_CONNTRACK_ORIGDST)) | |
190 | return 0; | |
9fb9cbb1 | 191 | |
50b9f1d5 PM |
192 | if (sinfo->flags & XT_CONNTRACK_REPLSRC && |
193 | FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & | |
601e68e1 | 194 | sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != |
50b9f1d5 PM |
195 | sinfo->tuple[IP_CT_DIR_REPLY].src.ip, |
196 | XT_CONNTRACK_REPLSRC)) | |
197 | return 0; | |
9fb9cbb1 | 198 | |
50b9f1d5 PM |
199 | if (sinfo->flags & XT_CONNTRACK_REPLDST && |
200 | FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & | |
601e68e1 | 201 | sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != |
50b9f1d5 PM |
202 | sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, |
203 | XT_CONNTRACK_REPLDST)) | |
204 | return 0; | |
9fb9cbb1 | 205 | |
50b9f1d5 PM |
206 | if (sinfo->flags & XT_CONNTRACK_STATUS && |
207 | FWINV((ct->status & sinfo->statusmask) == 0, | |
601e68e1 | 208 | XT_CONNTRACK_STATUS)) |
50b9f1d5 | 209 | return 0; |
9fb9cbb1 | 210 | |
50b9f1d5 PM |
211 | if(sinfo->flags & XT_CONNTRACK_EXPIRES) { |
212 | unsigned long expires = timer_pending(&ct->timeout) ? | |
213 | (ct->timeout.expires - jiffies)/HZ : 0; | |
9fb9cbb1 | 214 | |
50b9f1d5 PM |
215 | if (FWINV(!(expires >= sinfo->expires_min && |
216 | expires <= sinfo->expires_max), | |
217 | XT_CONNTRACK_EXPIRES)) | |
9fb9cbb1 YK |
218 | return 0; |
219 | } | |
9fb9cbb1 YK |
220 | return 1; |
221 | } | |
222 | ||
223 | #endif /* CONFIG_NF_IP_CONNTRACK */ | |
224 | ||
b9f78f9f PNA |
225 | static int |
226 | checkentry(const char *tablename, | |
227 | const void *ip, | |
228 | const struct xt_match *match, | |
229 | void *matchinfo, | |
b9f78f9f PNA |
230 | unsigned int hook_mask) |
231 | { | |
b9f78f9f | 232 | if (nf_ct_l3proto_try_module_get(match->family) < 0) { |
fe0b9294 | 233 | printk(KERN_WARNING "can't load conntrack support for " |
b9f78f9f PNA |
234 | "proto=%d\n", match->family); |
235 | return 0; | |
236 | } | |
b9f78f9f PNA |
237 | return 1; |
238 | } | |
239 | ||
50b9f1d5 | 240 | static void destroy(const struct xt_match *match, void *matchinfo) |
b9f78f9f | 241 | { |
b9f78f9f | 242 | nf_ct_l3proto_module_put(match->family); |
b9f78f9f PNA |
243 | } |
244 | ||
2e4e6a17 | 245 | static struct xt_match conntrack_match = { |
1da177e4 | 246 | .name = "conntrack", |
5d04bff0 | 247 | .match = match, |
b9f78f9f PNA |
248 | .checkentry = checkentry, |
249 | .destroy = destroy, | |
5d04bff0 | 250 | .matchsize = sizeof(struct xt_conntrack_info), |
a45049c5 | 251 | .family = AF_INET, |
1da177e4 LT |
252 | .me = THIS_MODULE, |
253 | }; | |
254 | ||
65b4b4e8 | 255 | static int __init xt_conntrack_init(void) |
1da177e4 | 256 | { |
4470bbc7 | 257 | return xt_register_match(&conntrack_match); |
1da177e4 LT |
258 | } |
259 | ||
65b4b4e8 | 260 | static void __exit xt_conntrack_fini(void) |
1da177e4 | 261 | { |
a45049c5 | 262 | xt_unregister_match(&conntrack_match); |
1da177e4 LT |
263 | } |
264 | ||
65b4b4e8 AM |
265 | module_init(xt_conntrack_init); |
266 | module_exit(xt_conntrack_fini); |