xt_conntrack.c revision d6d3f08b0fd998b647a05540cedd11a067b72867
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_mtinfo2 *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_mtinfo2 *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_mtinfo2 *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_mtinfo2 *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_mtinfo2 *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_mtinfo2 *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->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->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->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->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_v1(const struct sk_buff *skb, const struct xt_match_param *par) 283{ 284 const struct xt_conntrack_mtinfo2 *const *info = par->matchinfo; 285 struct xt_match_param newpar = *par; 286 287 newpar.matchinfo = *info; 288 return conntrack_mt(skb, &newpar); 289} 290 291static bool conntrack_mt_check(const struct xt_mtchk_param *par) 292{ 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); 296 return false; 297 } 298 return true; 299} 300 301static bool conntrack_mt_check_v1(const struct xt_mtchk_param *par) 302{ 303 struct xt_conntrack_mtinfo1 *info = par->matchinfo; 304 struct xt_conntrack_mtinfo2 *up; 305 int ret = conntrack_mt_check(par); 306 307 if (ret < 0) 308 return ret; 309 310 up = kmalloc(sizeof(*up), GFP_KERNEL); 311 if (up == NULL) { 312 nf_ct_l3proto_module_put(par->family); 313 return -ENOMEM; 314 } 315 316 /* 317 * The strategy here is to minimize the overhead of v1 matching, 318 * by prebuilding a v2 struct and putting the pointer into the 319 * v1 dataspace. 320 */ 321 memcpy(up, info, offsetof(typeof(*info), state_mask)); 322 up->state_mask = info->state_mask; 323 up->status_mask = info->status_mask; 324 *(void **)info = up; 325 return true; 326} 327 328static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 329{ 330 nf_ct_l3proto_module_put(par->family); 331} 332 333static void conntrack_mt_destroy_v1(const struct xt_mtdtor_param *par) 334{ 335 struct xt_conntrack_mtinfo2 **info = par->matchinfo; 336 kfree(*info); 337 conntrack_mt_destroy(par); 338} 339 340#ifdef CONFIG_COMPAT 341struct compat_xt_conntrack_info 342{ 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; 350 u_int8_t flags; 351 u_int8_t invflags; 352}; 353 354static void conntrack_mt_compat_from_user_v0(void *dst, void *src) 355{ 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, 362 .flags = cm->flags, 363 .invflags = cm->invflags, 364 }; 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)); 369} 370 371static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) 372{ 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, 379 .flags = m->flags, 380 .invflags = m->invflags, 381 }; 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; 386} 387#endif 388 389static struct xt_match conntrack_mt_reg[] __read_mostly = { 390 { 391 .name = "conntrack", 392 .revision = 0, 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), 398 .me = THIS_MODULE, 399#ifdef CONFIG_COMPAT 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, 403#endif 404 }, 405 { 406 .name = "conntrack", 407 .revision = 1, 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, 413 .me = THIS_MODULE, 414 }, 415 { 416 .name = "conntrack", 417 .revision = 2, 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, 423 .me = THIS_MODULE, 424 }, 425}; 426 427static int __init conntrack_mt_init(void) 428{ 429 return xt_register_matches(conntrack_mt_reg, 430 ARRAY_SIZE(conntrack_mt_reg)); 431} 432 433static void __exit conntrack_mt_exit(void) 434{ 435 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 436} 437 438module_init(conntrack_mt_init); 439module_exit(conntrack_mt_exit); 440