xt_conntrack.c revision f7108a20dee44e5bb037f9e48f6a207b42e6ae1c
1/* 2 * xt_conntrack - Netfilter module to match connection tracking 3 * information. (Superset of Rusty's minimalistic state match.) 4 * 5 * (C) 2001 Marc Boucher (marc@mbsi.ca). 6 * Copyright © CC Computer Consultants GmbH, 2007 - 2008 7 * 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. 11 */ 12 13#include <linux/module.h> 14#include <linux/skbuff.h> 15#include <net/ipv6.h> 16#include <linux/netfilter/x_tables.h> 17#include <linux/netfilter/xt_conntrack.h> 18#include <net/netfilter/nf_conntrack.h> 19 20MODULE_LICENSE("GPL"); 21MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 22MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>"); 23MODULE_DESCRIPTION("Xtables: connection tracking state match"); 24MODULE_ALIAS("ipt_conntrack"); 25MODULE_ALIAS("ip6t_conntrack"); 26 27static bool 28conntrack_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) 29{ 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; 34 35 ct = nf_ct_get(skb, &ctinfo); 36 37#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg))) 38 39 if (ct == &nf_conntrack_untracked) 40 statebit = XT_CONNTRACK_STATE_UNTRACKED; 41 else if (ct) 42 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 43 else 44 statebit = XT_CONNTRACK_STATE_INVALID; 45 46 if (sinfo->flags & XT_CONNTRACK_STATE) { 47 if (ct) { 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; 52 } 53 if (FWINV((statebit & sinfo->statemask) == 0, 54 XT_CONNTRACK_STATE)) 55 return false; 56 } 57 58 if (ct == NULL) { 59 if (sinfo->flags & ~XT_CONNTRACK_STATE) 60 return false; 61 return true; 62 } 63 64 if (sinfo->flags & XT_CONNTRACK_PROTO && 65 FWINV(nf_ct_protonum(ct) != 66 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, 67 XT_CONNTRACK_PROTO)) 68 return false; 69 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)) 75 return false; 76 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)) 82 return false; 83 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)) 89 return false; 90 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)) 96 return false; 97 98 if (sinfo->flags & XT_CONNTRACK_STATUS && 99 FWINV((ct->status & sinfo->statusmask) == 0, 100 XT_CONNTRACK_STATUS)) 101 return false; 102 103 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 104 unsigned long expires = timer_pending(&ct->timeout) ? 105 (ct->timeout.expires - jiffies)/HZ : 0; 106 107 if (FWINV(!(expires >= sinfo->expires_min && 108 expires <= sinfo->expires_max), 109 XT_CONNTRACK_EXPIRES)) 110 return false; 111 } 112 return true; 113#undef FWINV 114} 115 116static bool 117conntrack_addrcmp(const union nf_inet_addr *kaddr, 118 const union nf_inet_addr *uaddr, 119 const union nf_inet_addr *umask, unsigned int l3proto) 120{ 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, 125 &uaddr->in6) == 0; 126 else 127 return false; 128} 129 130static inline bool 131conntrack_mt_origsrc(const struct nf_conn *ct, 132 const struct xt_conntrack_mtinfo1 *info, 133 u_int8_t family) 134{ 135 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, 136 &info->origsrc_addr, &info->origsrc_mask, family); 137} 138 139static inline bool 140conntrack_mt_origdst(const struct nf_conn *ct, 141 const struct xt_conntrack_mtinfo1 *info, 142 u_int8_t family) 143{ 144 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3, 145 &info->origdst_addr, &info->origdst_mask, family); 146} 147 148static inline bool 149conntrack_mt_replsrc(const struct nf_conn *ct, 150 const struct xt_conntrack_mtinfo1 *info, 151 u_int8_t family) 152{ 153 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3, 154 &info->replsrc_addr, &info->replsrc_mask, family); 155} 156 157static inline bool 158conntrack_mt_repldst(const struct nf_conn *ct, 159 const struct xt_conntrack_mtinfo1 *info, 160 u_int8_t family) 161{ 162 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3, 163 &info->repldst_addr, &info->repldst_mask, family); 164} 165 166static inline bool 167ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info, 168 const struct nf_conn *ct) 169{ 170 const struct nf_conntrack_tuple *tuple; 171 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)) 176 return false; 177 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)) 182 return false; 183 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)) 187 return false; 188 189 tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 190 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)) 194 return false; 195 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)) 199 return false; 200 201 return true; 202} 203 204static bool 205conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par) 206{ 207 const struct xt_conntrack_mtinfo1 *info = par->matchinfo; 208 enum ip_conntrack_info ctinfo; 209 const struct nf_conn *ct; 210 unsigned int statebit; 211 212 ct = nf_ct_get(skb, &ctinfo); 213 214 if (ct == &nf_conntrack_untracked) 215 statebit = XT_CONNTRACK_STATE_UNTRACKED; 216 else if (ct != NULL) 217 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 218 else 219 statebit = XT_CONNTRACK_STATE_INVALID; 220 221 if (info->match_flags & XT_CONNTRACK_STATE) { 222 if (ct != NULL) { 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; 227 } 228 if (!!(info->state_mask & statebit) ^ 229 !(info->invert_flags & XT_CONNTRACK_STATE)) 230 return false; 231 } 232 233 if (ct == NULL) 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)) 238 return false; 239 240 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 241 if (conntrack_mt_origsrc(ct, info, par->match->family) ^ 242 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 243 return false; 244 245 if (info->match_flags & XT_CONNTRACK_ORIGDST) 246 if (conntrack_mt_origdst(ct, info, par->match->family) ^ 247 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 248 return false; 249 250 if (info->match_flags & XT_CONNTRACK_REPLSRC) 251 if (conntrack_mt_replsrc(ct, info, par->match->family) ^ 252 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 253 return false; 254 255 if (info->match_flags & XT_CONNTRACK_REPLDST) 256 if (conntrack_mt_repldst(ct, info, par->match->family) ^ 257 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 258 return false; 259 260 if (!ct_proto_port_check(info, ct)) 261 return false; 262 263 if ((info->match_flags & XT_CONNTRACK_STATUS) && 264 (!!(info->status_mask & ct->status) ^ 265 !(info->invert_flags & XT_CONNTRACK_STATUS))) 266 return false; 267 268 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 269 unsigned long expires = 0; 270 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)) 276 return false; 277 } 278 return true; 279} 280 281static bool 282conntrack_mt_check(const char *tablename, const void *ip, 283 const struct xt_match *match, void *matchinfo, 284 unsigned int hook_mask) 285{ 286 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 287 printk(KERN_WARNING "can't load conntrack support for " 288 "proto=%u\n", match->family); 289 return false; 290 } 291 return true; 292} 293 294static void 295conntrack_mt_destroy(const struct xt_match *match, void *matchinfo) 296{ 297 nf_ct_l3proto_module_put(match->family); 298} 299 300#ifdef CONFIG_COMPAT 301struct compat_xt_conntrack_info 302{ 303 compat_uint_t statemask; 304 compat_uint_t statusmask; 305 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; 306 struct in_addr sipmsk[IP_CT_DIR_MAX]; 307 struct in_addr dipmsk[IP_CT_DIR_MAX]; 308 compat_ulong_t expires_min; 309 compat_ulong_t expires_max; 310 u_int8_t flags; 311 u_int8_t invflags; 312}; 313 314static void conntrack_mt_compat_from_user_v0(void *dst, void *src) 315{ 316 const struct compat_xt_conntrack_info *cm = src; 317 struct xt_conntrack_info m = { 318 .statemask = cm->statemask, 319 .statusmask = cm->statusmask, 320 .expires_min = cm->expires_min, 321 .expires_max = cm->expires_max, 322 .flags = cm->flags, 323 .invflags = cm->invflags, 324 }; 325 memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); 326 memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); 327 memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); 328 memcpy(dst, &m, sizeof(m)); 329} 330 331static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) 332{ 333 const struct xt_conntrack_info *m = src; 334 struct compat_xt_conntrack_info cm = { 335 .statemask = m->statemask, 336 .statusmask = m->statusmask, 337 .expires_min = m->expires_min, 338 .expires_max = m->expires_max, 339 .flags = m->flags, 340 .invflags = m->invflags, 341 }; 342 memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); 343 memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); 344 memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); 345 return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; 346} 347#endif 348 349static struct xt_match conntrack_mt_reg[] __read_mostly = { 350 { 351 .name = "conntrack", 352 .revision = 0, 353 .family = NFPROTO_IPV4, 354 .match = conntrack_mt_v0, 355 .checkentry = conntrack_mt_check, 356 .destroy = conntrack_mt_destroy, 357 .matchsize = sizeof(struct xt_conntrack_info), 358 .me = THIS_MODULE, 359#ifdef CONFIG_COMPAT 360 .compatsize = sizeof(struct compat_xt_conntrack_info), 361 .compat_from_user = conntrack_mt_compat_from_user_v0, 362 .compat_to_user = conntrack_mt_compat_to_user_v0, 363#endif 364 }, 365 { 366 .name = "conntrack", 367 .revision = 1, 368 .family = NFPROTO_IPV4, 369 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 370 .match = conntrack_mt, 371 .checkentry = conntrack_mt_check, 372 .destroy = conntrack_mt_destroy, 373 .me = THIS_MODULE, 374 }, 375 { 376 .name = "conntrack", 377 .revision = 1, 378 .family = NFPROTO_IPV6, 379 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 380 .match = conntrack_mt, 381 .checkentry = conntrack_mt_check, 382 .destroy = conntrack_mt_destroy, 383 .me = THIS_MODULE, 384 }, 385}; 386 387static int __init conntrack_mt_init(void) 388{ 389 return xt_register_matches(conntrack_mt_reg, 390 ARRAY_SIZE(conntrack_mt_reg)); 391} 392 393static void __exit conntrack_mt_exit(void) 394{ 395 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 396} 397 398module_init(conntrack_mt_init); 399module_exit(conntrack_mt_exit); 400