xt_conntrack.c revision 6be3d8598e883fb632edf059ba2f8d1b9f4da138
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 conntrack_mt_check(const struct xt_mtchk_param *par) 282{ 283 if (nf_ct_l3proto_try_module_get(par->match->family) < 0) { 284 printk(KERN_WARNING "can't load conntrack support for " 285 "proto=%u\n", par->match->family); 286 return false; 287 } 288 return true; 289} 290 291static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 292{ 293 nf_ct_l3proto_module_put(par->match->family); 294} 295 296#ifdef CONFIG_COMPAT 297struct compat_xt_conntrack_info 298{ 299 compat_uint_t statemask; 300 compat_uint_t statusmask; 301 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; 302 struct in_addr sipmsk[IP_CT_DIR_MAX]; 303 struct in_addr dipmsk[IP_CT_DIR_MAX]; 304 compat_ulong_t expires_min; 305 compat_ulong_t expires_max; 306 u_int8_t flags; 307 u_int8_t invflags; 308}; 309 310static void conntrack_mt_compat_from_user_v0(void *dst, void *src) 311{ 312 const struct compat_xt_conntrack_info *cm = src; 313 struct xt_conntrack_info m = { 314 .statemask = cm->statemask, 315 .statusmask = cm->statusmask, 316 .expires_min = cm->expires_min, 317 .expires_max = cm->expires_max, 318 .flags = cm->flags, 319 .invflags = cm->invflags, 320 }; 321 memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); 322 memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); 323 memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); 324 memcpy(dst, &m, sizeof(m)); 325} 326 327static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) 328{ 329 const struct xt_conntrack_info *m = src; 330 struct compat_xt_conntrack_info cm = { 331 .statemask = m->statemask, 332 .statusmask = m->statusmask, 333 .expires_min = m->expires_min, 334 .expires_max = m->expires_max, 335 .flags = m->flags, 336 .invflags = m->invflags, 337 }; 338 memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); 339 memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); 340 memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); 341 return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; 342} 343#endif 344 345static struct xt_match conntrack_mt_reg[] __read_mostly = { 346 { 347 .name = "conntrack", 348 .revision = 0, 349 .family = NFPROTO_IPV4, 350 .match = conntrack_mt_v0, 351 .checkentry = conntrack_mt_check, 352 .destroy = conntrack_mt_destroy, 353 .matchsize = sizeof(struct xt_conntrack_info), 354 .me = THIS_MODULE, 355#ifdef CONFIG_COMPAT 356 .compatsize = sizeof(struct compat_xt_conntrack_info), 357 .compat_from_user = conntrack_mt_compat_from_user_v0, 358 .compat_to_user = conntrack_mt_compat_to_user_v0, 359#endif 360 }, 361 { 362 .name = "conntrack", 363 .revision = 1, 364 .family = NFPROTO_IPV4, 365 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 366 .match = conntrack_mt, 367 .checkentry = conntrack_mt_check, 368 .destroy = conntrack_mt_destroy, 369 .me = THIS_MODULE, 370 }, 371 { 372 .name = "conntrack", 373 .revision = 1, 374 .family = NFPROTO_IPV6, 375 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 376 .match = conntrack_mt, 377 .checkentry = conntrack_mt_check, 378 .destroy = conntrack_mt_destroy, 379 .me = THIS_MODULE, 380 }, 381}; 382 383static int __init conntrack_mt_init(void) 384{ 385 return xt_register_matches(conntrack_mt_reg, 386 ARRAY_SIZE(conntrack_mt_reg)); 387} 388 389static void __exit conntrack_mt_exit(void) 390{ 391 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 392} 393 394module_init(conntrack_mt_init); 395module_exit(conntrack_mt_exit); 396