xt_conntrack.c revision 6556874dc3770aefae89907b3cf9be8e23d66137
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 net_device *in, 29 const struct net_device *out, const struct xt_match *match, 30 const void *matchinfo, int offset, unsigned int protoff, 31 bool *hotdrop) 32{ 33 const struct xt_conntrack_info *sinfo = matchinfo; 34 const struct nf_conn *ct; 35 enum ip_conntrack_info ctinfo; 36 unsigned int statebit; 37 38 ct = nf_ct_get(skb, &ctinfo); 39 40#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg))) 41 42 if (ct == &nf_conntrack_untracked) 43 statebit = XT_CONNTRACK_STATE_UNTRACKED; 44 else if (ct) 45 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 46 else 47 statebit = XT_CONNTRACK_STATE_INVALID; 48 49 if (sinfo->flags & XT_CONNTRACK_STATE) { 50 if (ct) { 51 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 52 statebit |= XT_CONNTRACK_STATE_SNAT; 53 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 54 statebit |= XT_CONNTRACK_STATE_DNAT; 55 } 56 if (FWINV((statebit & sinfo->statemask) == 0, 57 XT_CONNTRACK_STATE)) 58 return false; 59 } 60 61 if (ct == NULL) { 62 if (sinfo->flags & ~XT_CONNTRACK_STATE) 63 return false; 64 return true; 65 } 66 67 if (sinfo->flags & XT_CONNTRACK_PROTO && 68 FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != 69 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, 70 XT_CONNTRACK_PROTO)) 71 return false; 72 73 if (sinfo->flags & XT_CONNTRACK_ORIGSRC && 74 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & 75 sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 76 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 77 XT_CONNTRACK_ORIGSRC)) 78 return false; 79 80 if (sinfo->flags & XT_CONNTRACK_ORIGDST && 81 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & 82 sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 83 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 84 XT_CONNTRACK_ORIGDST)) 85 return false; 86 87 if (sinfo->flags & XT_CONNTRACK_REPLSRC && 88 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & 89 sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != 90 sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 91 XT_CONNTRACK_REPLSRC)) 92 return false; 93 94 if (sinfo->flags & XT_CONNTRACK_REPLDST && 95 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & 96 sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != 97 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 98 XT_CONNTRACK_REPLDST)) 99 return false; 100 101 if (sinfo->flags & XT_CONNTRACK_STATUS && 102 FWINV((ct->status & sinfo->statusmask) == 0, 103 XT_CONNTRACK_STATUS)) 104 return false; 105 106 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 107 unsigned long expires = timer_pending(&ct->timeout) ? 108 (ct->timeout.expires - jiffies)/HZ : 0; 109 110 if (FWINV(!(expires >= sinfo->expires_min && 111 expires <= sinfo->expires_max), 112 XT_CONNTRACK_EXPIRES)) 113 return false; 114 } 115 return true; 116#undef FWINV 117} 118 119static bool 120conntrack_addrcmp(const union nf_inet_addr *kaddr, 121 const union nf_inet_addr *uaddr, 122 const union nf_inet_addr *umask, unsigned int l3proto) 123{ 124 if (l3proto == AF_INET) 125 return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0; 126 else if (l3proto == AF_INET6) 127 return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6, 128 &uaddr->in6) == 0; 129 else 130 return false; 131} 132 133static inline bool 134conntrack_mt_origsrc(const struct nf_conn *ct, 135 const struct xt_conntrack_mtinfo1 *info, 136 unsigned int family) 137{ 138 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, 139 &info->origsrc_addr, &info->origsrc_mask, family); 140} 141 142static inline bool 143conntrack_mt_origdst(const struct nf_conn *ct, 144 const struct xt_conntrack_mtinfo1 *info, 145 unsigned int family) 146{ 147 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3, 148 &info->origdst_addr, &info->origdst_mask, family); 149} 150 151static inline bool 152conntrack_mt_replsrc(const struct nf_conn *ct, 153 const struct xt_conntrack_mtinfo1 *info, 154 unsigned int family) 155{ 156 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3, 157 &info->replsrc_addr, &info->replsrc_mask, family); 158} 159 160static inline bool 161conntrack_mt_repldst(const struct nf_conn *ct, 162 const struct xt_conntrack_mtinfo1 *info, 163 unsigned int family) 164{ 165 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3, 166 &info->repldst_addr, &info->repldst_mask, family); 167} 168 169static inline bool 170ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info, 171 const struct nf_conn *ct) 172{ 173 const struct nf_conntrack_tuple *tuple; 174 175 tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 176 if ((info->match_flags & XT_CONNTRACK_PROTO) && 177 (tuple->dst.protonum == info->l4proto) ^ 178 !(info->invert_flags & XT_CONNTRACK_PROTO)) 179 return false; 180 181 /* Shortcut to match all recognized protocols by using ->src.all. */ 182 if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) && 183 (tuple->src.u.all == info->origsrc_port) ^ 184 !(info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)) 185 return false; 186 187 if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) && 188 (tuple->dst.u.all == info->origdst_port) ^ 189 !(info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)) 190 return false; 191 192 tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 193 194 if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) && 195 (tuple->src.u.all == info->replsrc_port) ^ 196 !(info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)) 197 return false; 198 199 if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) && 200 (tuple->dst.u.all == info->repldst_port) ^ 201 !(info->invert_flags & XT_CONNTRACK_REPLDST_PORT)) 202 return false; 203 204 return true; 205} 206 207static bool 208conntrack_mt(const struct sk_buff *skb, const struct net_device *in, 209 const struct net_device *out, const struct xt_match *match, 210 const void *matchinfo, int offset, unsigned int protoff, 211 bool *hotdrop) 212{ 213 const struct xt_conntrack_mtinfo1 *info = matchinfo; 214 enum ip_conntrack_info ctinfo; 215 const struct nf_conn *ct; 216 unsigned int statebit; 217 218 ct = nf_ct_get(skb, &ctinfo); 219 220 if (ct == &nf_conntrack_untracked) 221 statebit = XT_CONNTRACK_STATE_UNTRACKED; 222 else if (ct != NULL) 223 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 224 else 225 statebit = XT_CONNTRACK_STATE_INVALID; 226 227 if (info->match_flags & XT_CONNTRACK_STATE) { 228 if (ct != NULL) { 229 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 230 statebit |= XT_CONNTRACK_STATE_SNAT; 231 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 232 statebit |= XT_CONNTRACK_STATE_DNAT; 233 } 234 if (!!(info->state_mask & statebit) ^ 235 !(info->invert_flags & XT_CONNTRACK_STATE)) 236 return false; 237 } 238 239 if (ct == NULL) 240 return info->match_flags & XT_CONNTRACK_STATE; 241 if ((info->match_flags & XT_CONNTRACK_DIRECTION) && 242 (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ 243 !!(info->invert_flags & XT_CONNTRACK_DIRECTION)) 244 return false; 245 246 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 247 if (conntrack_mt_origsrc(ct, info, match->family) ^ 248 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 249 return false; 250 251 if (info->match_flags & XT_CONNTRACK_ORIGDST) 252 if (conntrack_mt_origdst(ct, info, match->family) ^ 253 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 254 return false; 255 256 if (info->match_flags & XT_CONNTRACK_REPLSRC) 257 if (conntrack_mt_replsrc(ct, info, match->family) ^ 258 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 259 return false; 260 261 if (info->match_flags & XT_CONNTRACK_REPLDST) 262 if (conntrack_mt_repldst(ct, info, match->family) ^ 263 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 264 return false; 265 266 if (!ct_proto_port_check(info, ct)) 267 return false; 268 269 if ((info->match_flags & XT_CONNTRACK_STATUS) && 270 (!!(info->status_mask & ct->status) ^ 271 !(info->invert_flags & XT_CONNTRACK_STATUS))) 272 return false; 273 274 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 275 unsigned long expires = 0; 276 277 if (timer_pending(&ct->timeout)) 278 expires = (ct->timeout.expires - jiffies) / HZ; 279 if ((expires >= info->expires_min && 280 expires <= info->expires_max) ^ 281 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 282 return false; 283 } 284 return true; 285} 286 287static bool 288conntrack_mt_check(const char *tablename, const void *ip, 289 const struct xt_match *match, void *matchinfo, 290 unsigned int hook_mask) 291{ 292 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 293 printk(KERN_WARNING "can't load conntrack support for " 294 "proto=%u\n", match->family); 295 return false; 296 } 297 return true; 298} 299 300static void 301conntrack_mt_destroy(const struct xt_match *match, void *matchinfo) 302{ 303 nf_ct_l3proto_module_put(match->family); 304} 305 306#ifdef CONFIG_COMPAT 307struct compat_xt_conntrack_info 308{ 309 compat_uint_t statemask; 310 compat_uint_t statusmask; 311 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; 312 struct in_addr sipmsk[IP_CT_DIR_MAX]; 313 struct in_addr dipmsk[IP_CT_DIR_MAX]; 314 compat_ulong_t expires_min; 315 compat_ulong_t expires_max; 316 u_int8_t flags; 317 u_int8_t invflags; 318}; 319 320static void conntrack_mt_compat_from_user_v0(void *dst, void *src) 321{ 322 const struct compat_xt_conntrack_info *cm = src; 323 struct xt_conntrack_info m = { 324 .statemask = cm->statemask, 325 .statusmask = cm->statusmask, 326 .expires_min = cm->expires_min, 327 .expires_max = cm->expires_max, 328 .flags = cm->flags, 329 .invflags = cm->invflags, 330 }; 331 memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); 332 memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); 333 memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); 334 memcpy(dst, &m, sizeof(m)); 335} 336 337static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) 338{ 339 const struct xt_conntrack_info *m = src; 340 struct compat_xt_conntrack_info cm = { 341 .statemask = m->statemask, 342 .statusmask = m->statusmask, 343 .expires_min = m->expires_min, 344 .expires_max = m->expires_max, 345 .flags = m->flags, 346 .invflags = m->invflags, 347 }; 348 memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); 349 memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); 350 memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); 351 return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; 352} 353#endif 354 355static struct xt_match conntrack_mt_reg[] __read_mostly = { 356 { 357 .name = "conntrack", 358 .revision = 0, 359 .family = AF_INET, 360 .match = conntrack_mt_v0, 361 .checkentry = conntrack_mt_check, 362 .destroy = conntrack_mt_destroy, 363 .matchsize = sizeof(struct xt_conntrack_info), 364 .me = THIS_MODULE, 365#ifdef CONFIG_COMPAT 366 .compatsize = sizeof(struct compat_xt_conntrack_info), 367 .compat_from_user = conntrack_mt_compat_from_user_v0, 368 .compat_to_user = conntrack_mt_compat_to_user_v0, 369#endif 370 }, 371 { 372 .name = "conntrack", 373 .revision = 1, 374 .family = AF_INET, 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 .name = "conntrack", 383 .revision = 1, 384 .family = AF_INET6, 385 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 386 .match = conntrack_mt, 387 .checkentry = conntrack_mt_check, 388 .destroy = conntrack_mt_destroy, 389 .me = THIS_MODULE, 390 }, 391}; 392 393static int __init conntrack_mt_init(void) 394{ 395 return xt_register_matches(conntrack_mt_reg, 396 ARRAY_SIZE(conntrack_mt_reg)); 397} 398 399static void __exit conntrack_mt_exit(void) 400{ 401 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 402} 403 404module_init(conntrack_mt_init); 405module_exit(conntrack_mt_exit); 406