2 * xt_conntrack - Netfilter module to match connection tracking
3 * information. (Superset of Rusty's minimalistic state match.)
5 * (C) 2001 Marc Boucher (marc@mbsi.ca).
6 * Copyright © CC Computer Consultants GmbH, 2007 - 2008
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_conntrack.h>
18 #include <net/netfilter/nf_conntrack.h>
20 MODULE_LICENSE("GPL");
21 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
22 MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
23 MODULE_DESCRIPTION("Xtables: connection tracking state match");
24 MODULE_ALIAS("ipt_conntrack");
25 MODULE_ALIAS("ip6t_conntrack");
28 conntrack_mt_v0(const struct sk_buff
*skb
, const struct xt_match_param
*par
)
30 const struct xt_conntrack_info
*sinfo
= par
->matchinfo
;
31 const struct nf_conn
*ct
;
32 enum ip_conntrack_info ctinfo
;
33 unsigned int statebit
;
35 ct
= nf_ct_get(skb
, &ctinfo
);
37 #define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg)))
39 if (ct
== &nf_conntrack_untracked
)
40 statebit
= XT_CONNTRACK_STATE_UNTRACKED
;
42 statebit
= XT_CONNTRACK_STATE_BIT(ctinfo
);
44 statebit
= XT_CONNTRACK_STATE_INVALID
;
46 if (sinfo
->flags
& XT_CONNTRACK_STATE
) {
48 if (test_bit(IPS_SRC_NAT_BIT
, &ct
->status
))
49 statebit
|= XT_CONNTRACK_STATE_SNAT
;
50 if (test_bit(IPS_DST_NAT_BIT
, &ct
->status
))
51 statebit
|= XT_CONNTRACK_STATE_DNAT
;
53 if (FWINV((statebit
& sinfo
->statemask
) == 0,
59 if (sinfo
->flags
& ~XT_CONNTRACK_STATE
)
64 if (sinfo
->flags
& XT_CONNTRACK_PROTO
&&
65 FWINV(nf_ct_protonum(ct
) !=
66 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.protonum
,
70 if (sinfo
->flags
& XT_CONNTRACK_ORIGSRC
&&
71 FWINV((ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
.ip
&
72 sinfo
->sipmsk
[IP_CT_DIR_ORIGINAL
].s_addr
) !=
73 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].src
.ip
,
74 XT_CONNTRACK_ORIGSRC
))
77 if (sinfo
->flags
& XT_CONNTRACK_ORIGDST
&&
78 FWINV((ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.u3
.ip
&
79 sinfo
->dipmsk
[IP_CT_DIR_ORIGINAL
].s_addr
) !=
80 sinfo
->tuple
[IP_CT_DIR_ORIGINAL
].dst
.ip
,
81 XT_CONNTRACK_ORIGDST
))
84 if (sinfo
->flags
& XT_CONNTRACK_REPLSRC
&&
85 FWINV((ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.src
.u3
.ip
&
86 sinfo
->sipmsk
[IP_CT_DIR_REPLY
].s_addr
) !=
87 sinfo
->tuple
[IP_CT_DIR_REPLY
].src
.ip
,
88 XT_CONNTRACK_REPLSRC
))
91 if (sinfo
->flags
& XT_CONNTRACK_REPLDST
&&
92 FWINV((ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.dst
.u3
.ip
&
93 sinfo
->dipmsk
[IP_CT_DIR_REPLY
].s_addr
) !=
94 sinfo
->tuple
[IP_CT_DIR_REPLY
].dst
.ip
,
95 XT_CONNTRACK_REPLDST
))
98 if (sinfo
->flags
& XT_CONNTRACK_STATUS
&&
99 FWINV((ct
->status
& sinfo
->statusmask
) == 0,
100 XT_CONNTRACK_STATUS
))
103 if(sinfo
->flags
& XT_CONNTRACK_EXPIRES
) {
104 unsigned long expires
= timer_pending(&ct
->timeout
) ?
105 (ct
->timeout
.expires
- jiffies
)/HZ
: 0;
107 if (FWINV(!(expires
>= sinfo
->expires_min
&&
108 expires
<= sinfo
->expires_max
),
109 XT_CONNTRACK_EXPIRES
))
117 conntrack_addrcmp(const union nf_inet_addr
*kaddr
,
118 const union nf_inet_addr
*uaddr
,
119 const union nf_inet_addr
*umask
, unsigned int l3proto
)
121 if (l3proto
== NFPROTO_IPV4
)
122 return ((kaddr
->ip
^ uaddr
->ip
) & umask
->ip
) == 0;
123 else if (l3proto
== NFPROTO_IPV6
)
124 return ipv6_masked_addr_cmp(&kaddr
->in6
, &umask
->in6
,
131 conntrack_mt_origsrc(const struct nf_conn
*ct
,
132 const struct xt_conntrack_mtinfo2
*info
,
135 return conntrack_addrcmp(&ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
,
136 &info
->origsrc_addr
, &info
->origsrc_mask
, family
);
140 conntrack_mt_origdst(const struct nf_conn
*ct
,
141 const struct xt_conntrack_mtinfo2
*info
,
144 return conntrack_addrcmp(&ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.dst
.u3
,
145 &info
->origdst_addr
, &info
->origdst_mask
, family
);
149 conntrack_mt_replsrc(const struct nf_conn
*ct
,
150 const struct xt_conntrack_mtinfo2
*info
,
153 return conntrack_addrcmp(&ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.src
.u3
,
154 &info
->replsrc_addr
, &info
->replsrc_mask
, family
);
158 conntrack_mt_repldst(const struct nf_conn
*ct
,
159 const struct xt_conntrack_mtinfo2
*info
,
162 return conntrack_addrcmp(&ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
.dst
.u3
,
163 &info
->repldst_addr
, &info
->repldst_mask
, family
);
167 ct_proto_port_check(const struct xt_conntrack_mtinfo2
*info
,
168 const struct nf_conn
*ct
)
170 const struct nf_conntrack_tuple
*tuple
;
172 tuple
= &ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
;
173 if ((info
->match_flags
& XT_CONNTRACK_PROTO
) &&
174 (nf_ct_protonum(ct
) == info
->l4proto
) ^
175 !(info
->invert_flags
& XT_CONNTRACK_PROTO
))
178 /* Shortcut to match all recognized protocols by using ->src.all. */
179 if ((info
->match_flags
& XT_CONNTRACK_ORIGSRC_PORT
) &&
180 (tuple
->src
.u
.all
== info
->origsrc_port
) ^
181 !(info
->invert_flags
& XT_CONNTRACK_ORIGSRC_PORT
))
184 if ((info
->match_flags
& XT_CONNTRACK_ORIGDST_PORT
) &&
185 (tuple
->dst
.u
.all
== info
->origdst_port
) ^
186 !(info
->invert_flags
& XT_CONNTRACK_ORIGDST_PORT
))
189 tuple
= &ct
->tuplehash
[IP_CT_DIR_REPLY
].tuple
;
191 if ((info
->match_flags
& XT_CONNTRACK_REPLSRC_PORT
) &&
192 (tuple
->src
.u
.all
== info
->replsrc_port
) ^
193 !(info
->invert_flags
& XT_CONNTRACK_REPLSRC_PORT
))
196 if ((info
->match_flags
& XT_CONNTRACK_REPLDST_PORT
) &&
197 (tuple
->dst
.u
.all
== info
->repldst_port
) ^
198 !(info
->invert_flags
& XT_CONNTRACK_REPLDST_PORT
))
205 conntrack_mt(const struct sk_buff
*skb
, const struct xt_match_param
*par
)
207 const struct xt_conntrack_mtinfo2
*info
= par
->matchinfo
;
208 enum ip_conntrack_info ctinfo
;
209 const struct nf_conn
*ct
;
210 unsigned int statebit
;
212 ct
= nf_ct_get(skb
, &ctinfo
);
214 if (ct
== &nf_conntrack_untracked
)
215 statebit
= XT_CONNTRACK_STATE_UNTRACKED
;
217 statebit
= XT_CONNTRACK_STATE_BIT(ctinfo
);
219 statebit
= XT_CONNTRACK_STATE_INVALID
;
221 if (info
->match_flags
& XT_CONNTRACK_STATE
) {
223 if (test_bit(IPS_SRC_NAT_BIT
, &ct
->status
))
224 statebit
|= XT_CONNTRACK_STATE_SNAT
;
225 if (test_bit(IPS_DST_NAT_BIT
, &ct
->status
))
226 statebit
|= XT_CONNTRACK_STATE_DNAT
;
228 if (!!(info
->state_mask
& statebit
) ^
229 !(info
->invert_flags
& XT_CONNTRACK_STATE
))
234 return info
->match_flags
& XT_CONNTRACK_STATE
;
235 if ((info
->match_flags
& XT_CONNTRACK_DIRECTION
) &&
236 (CTINFO2DIR(ctinfo
) == IP_CT_DIR_ORIGINAL
) ^
237 !!(info
->invert_flags
& XT_CONNTRACK_DIRECTION
))
240 if (info
->match_flags
& XT_CONNTRACK_ORIGSRC
)
241 if (conntrack_mt_origsrc(ct
, info
, par
->family
) ^
242 !(info
->invert_flags
& XT_CONNTRACK_ORIGSRC
))
245 if (info
->match_flags
& XT_CONNTRACK_ORIGDST
)
246 if (conntrack_mt_origdst(ct
, info
, par
->family
) ^
247 !(info
->invert_flags
& XT_CONNTRACK_ORIGDST
))
250 if (info
->match_flags
& XT_CONNTRACK_REPLSRC
)
251 if (conntrack_mt_replsrc(ct
, info
, par
->family
) ^
252 !(info
->invert_flags
& XT_CONNTRACK_REPLSRC
))
255 if (info
->match_flags
& XT_CONNTRACK_REPLDST
)
256 if (conntrack_mt_repldst(ct
, info
, par
->family
) ^
257 !(info
->invert_flags
& XT_CONNTRACK_REPLDST
))
260 if (!ct_proto_port_check(info
, ct
))
263 if ((info
->match_flags
& XT_CONNTRACK_STATUS
) &&
264 (!!(info
->status_mask
& ct
->status
) ^
265 !(info
->invert_flags
& XT_CONNTRACK_STATUS
)))
268 if (info
->match_flags
& XT_CONNTRACK_EXPIRES
) {
269 unsigned long expires
= 0;
271 if (timer_pending(&ct
->timeout
))
272 expires
= (ct
->timeout
.expires
- jiffies
) / HZ
;
273 if ((expires
>= info
->expires_min
&&
274 expires
<= info
->expires_max
) ^
275 !(info
->invert_flags
& XT_CONNTRACK_EXPIRES
))
282 conntrack_mt_v1(const struct sk_buff
*skb
, const struct xt_match_param
*par
)
284 const struct xt_conntrack_mtinfo2
*const *info
= par
->matchinfo
;
285 struct xt_match_param newpar
= *par
;
287 newpar
.matchinfo
= *info
;
288 return conntrack_mt(skb
, &newpar
);
291 static bool conntrack_mt_check(const struct xt_mtchk_param
*par
)
293 if (nf_ct_l3proto_try_module_get(par
->family
) < 0) {
294 printk(KERN_WARNING
"can't load conntrack support for "
295 "proto=%u\n", par
->family
);
301 static bool conntrack_mt_check_v1(const struct xt_mtchk_param
*par
)
303 struct xt_conntrack_mtinfo1
*info
= par
->matchinfo
;
304 struct xt_conntrack_mtinfo2
*up
;
305 int ret
= conntrack_mt_check(par
);
310 up
= kmalloc(sizeof(*up
), GFP_KERNEL
);
312 nf_ct_l3proto_module_put(par
->family
);
317 * The strategy here is to minimize the overhead of v1 matching,
318 * by prebuilding a v2 struct and putting the pointer into the
321 memcpy(up
, info
, offsetof(typeof(*info
), state_mask
));
322 up
->state_mask
= info
->state_mask
;
323 up
->status_mask
= info
->status_mask
;
328 static void conntrack_mt_destroy(const struct xt_mtdtor_param
*par
)
330 nf_ct_l3proto_module_put(par
->family
);
333 static void conntrack_mt_destroy_v1(const struct xt_mtdtor_param
*par
)
335 struct xt_conntrack_mtinfo2
**info
= par
->matchinfo
;
337 conntrack_mt_destroy(par
);
341 struct compat_xt_conntrack_info
343 compat_uint_t statemask
;
344 compat_uint_t statusmask
;
345 struct ip_conntrack_old_tuple tuple
[IP_CT_DIR_MAX
];
346 struct in_addr sipmsk
[IP_CT_DIR_MAX
];
347 struct in_addr dipmsk
[IP_CT_DIR_MAX
];
348 compat_ulong_t expires_min
;
349 compat_ulong_t expires_max
;
354 static void conntrack_mt_compat_from_user_v0(void *dst
, void *src
)
356 const struct compat_xt_conntrack_info
*cm
= src
;
357 struct xt_conntrack_info m
= {
358 .statemask
= cm
->statemask
,
359 .statusmask
= cm
->statusmask
,
360 .expires_min
= cm
->expires_min
,
361 .expires_max
= cm
->expires_max
,
363 .invflags
= cm
->invflags
,
365 memcpy(m
.tuple
, cm
->tuple
, sizeof(m
.tuple
));
366 memcpy(m
.sipmsk
, cm
->sipmsk
, sizeof(m
.sipmsk
));
367 memcpy(m
.dipmsk
, cm
->dipmsk
, sizeof(m
.dipmsk
));
368 memcpy(dst
, &m
, sizeof(m
));
371 static int conntrack_mt_compat_to_user_v0(void __user
*dst
, void *src
)
373 const struct xt_conntrack_info
*m
= src
;
374 struct compat_xt_conntrack_info cm
= {
375 .statemask
= m
->statemask
,
376 .statusmask
= m
->statusmask
,
377 .expires_min
= m
->expires_min
,
378 .expires_max
= m
->expires_max
,
380 .invflags
= m
->invflags
,
382 memcpy(cm
.tuple
, m
->tuple
, sizeof(cm
.tuple
));
383 memcpy(cm
.sipmsk
, m
->sipmsk
, sizeof(cm
.sipmsk
));
384 memcpy(cm
.dipmsk
, m
->dipmsk
, sizeof(cm
.dipmsk
));
385 return copy_to_user(dst
, &cm
, sizeof(cm
)) ? -EFAULT
: 0;
389 static struct xt_match conntrack_mt_reg
[] __read_mostly
= {
393 .family
= NFPROTO_IPV4
,
394 .match
= conntrack_mt_v0
,
395 .checkentry
= conntrack_mt_check
,
396 .destroy
= conntrack_mt_destroy
,
397 .matchsize
= sizeof(struct xt_conntrack_info
),
400 .compatsize
= sizeof(struct compat_xt_conntrack_info
),
401 .compat_from_user
= conntrack_mt_compat_from_user_v0
,
402 .compat_to_user
= conntrack_mt_compat_to_user_v0
,
408 .family
= NFPROTO_UNSPEC
,
409 .matchsize
= sizeof(struct xt_conntrack_mtinfo1
),
410 .match
= conntrack_mt_v1
,
411 .checkentry
= conntrack_mt_check_v1
,
412 .destroy
= conntrack_mt_destroy_v1
,
418 .family
= NFPROTO_UNSPEC
,
419 .matchsize
= sizeof(struct xt_conntrack_mtinfo2
),
420 .match
= conntrack_mt
,
421 .checkentry
= conntrack_mt_check
,
422 .destroy
= conntrack_mt_destroy
,
427 static int __init
conntrack_mt_init(void)
429 return xt_register_matches(conntrack_mt_reg
,
430 ARRAY_SIZE(conntrack_mt_reg
));
433 static void __exit
conntrack_mt_exit(void)
435 xt_unregister_matches(conntrack_mt_reg
, ARRAY_SIZE(conntrack_mt_reg
));
438 module_init(conntrack_mt_init
);
439 module_exit(conntrack_mt_exit
);