xt_conntrack.c revision 64eb12f9972d45f3b9b0f0a33a966e311c3d5275
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 * Jan Engelhardt <jengelh@computergmbh.de> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#include <linux/module.h> 15#include <linux/skbuff.h> 16#include <net/ipv6.h> 17#include <linux/netfilter/x_tables.h> 18#include <linux/netfilter/xt_conntrack.h> 19#include <net/netfilter/nf_conntrack.h> 20 21MODULE_LICENSE("GPL"); 22MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 23MODULE_DESCRIPTION("iptables connection tracking match module"); 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 & umask->ip) == uaddr->ip; 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 bool 170conntrack_mt(const struct sk_buff *skb, const struct net_device *in, 171 const struct net_device *out, const struct xt_match *match, 172 const void *matchinfo, int offset, unsigned int protoff, 173 bool *hotdrop) 174{ 175 const struct xt_conntrack_mtinfo1 *info = matchinfo; 176 enum ip_conntrack_info ctinfo; 177 const struct nf_conn *ct; 178 unsigned int statebit; 179 180 ct = nf_ct_get(skb, &ctinfo); 181 182 if (ct == &nf_conntrack_untracked) 183 statebit = XT_CONNTRACK_STATE_UNTRACKED; 184 else if (ct != NULL) 185 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 186 else 187 statebit = XT_CONNTRACK_STATE_INVALID; 188 189 if (info->match_flags & XT_CONNTRACK_STATE) { 190 if (ct != NULL) { 191 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 192 statebit |= XT_CONNTRACK_STATE_SNAT; 193 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 194 statebit |= XT_CONNTRACK_STATE_DNAT; 195 } 196 if ((info->state_mask & statebit) ^ 197 !(info->invert_flags & XT_CONNTRACK_STATE)) 198 return false; 199 } 200 201 if (ct == NULL) 202 return info->match_flags & XT_CONNTRACK_STATE; 203 204 if ((info->match_flags & XT_CONNTRACK_PROTO) && 205 ((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == 206 info->l4proto) ^ !(info->invert_flags & XT_CONNTRACK_PROTO))) 207 return false; 208 209 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 210 if (conntrack_mt_origsrc(ct, info, match->family) ^ 211 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 212 return false; 213 214 if (info->match_flags & XT_CONNTRACK_ORIGDST) 215 if (conntrack_mt_origdst(ct, info, match->family) ^ 216 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 217 return false; 218 219 if (info->match_flags & XT_CONNTRACK_REPLSRC) 220 if (conntrack_mt_replsrc(ct, info, match->family) ^ 221 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 222 return false; 223 224 if (info->match_flags & XT_CONNTRACK_REPLDST) 225 if (conntrack_mt_repldst(ct, info, match->family) ^ 226 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 227 return false; 228 229 if ((info->match_flags & XT_CONNTRACK_STATUS) && 230 (!!(info->status_mask & ct->status) ^ 231 !(info->invert_flags & XT_CONNTRACK_STATUS))) 232 return false; 233 234 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 235 unsigned long expires = 0; 236 237 if (timer_pending(&ct->timeout)) 238 expires = (ct->timeout.expires - jiffies) / HZ; 239 if ((expires >= info->expires_min && 240 expires <= info->expires_max) ^ 241 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 242 return false; 243 } 244 return true; 245} 246 247static bool 248conntrack_mt_check(const char *tablename, const void *ip, 249 const struct xt_match *match, void *matchinfo, 250 unsigned int hook_mask) 251{ 252 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 253 printk(KERN_WARNING "can't load conntrack support for " 254 "proto=%u\n", match->family); 255 return false; 256 } 257 return true; 258} 259 260static void 261conntrack_mt_destroy(const struct xt_match *match, void *matchinfo) 262{ 263 nf_ct_l3proto_module_put(match->family); 264} 265 266#ifdef CONFIG_COMPAT 267struct compat_xt_conntrack_info 268{ 269 compat_uint_t statemask; 270 compat_uint_t statusmask; 271 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; 272 struct in_addr sipmsk[IP_CT_DIR_MAX]; 273 struct in_addr dipmsk[IP_CT_DIR_MAX]; 274 compat_ulong_t expires_min; 275 compat_ulong_t expires_max; 276 u_int8_t flags; 277 u_int8_t invflags; 278}; 279 280static void conntrack_mt_compat_from_user_v0(void *dst, void *src) 281{ 282 const struct compat_xt_conntrack_info *cm = src; 283 struct xt_conntrack_info m = { 284 .statemask = cm->statemask, 285 .statusmask = cm->statusmask, 286 .expires_min = cm->expires_min, 287 .expires_max = cm->expires_max, 288 .flags = cm->flags, 289 .invflags = cm->invflags, 290 }; 291 memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); 292 memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); 293 memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); 294 memcpy(dst, &m, sizeof(m)); 295} 296 297static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src) 298{ 299 const struct xt_conntrack_info *m = src; 300 struct compat_xt_conntrack_info cm = { 301 .statemask = m->statemask, 302 .statusmask = m->statusmask, 303 .expires_min = m->expires_min, 304 .expires_max = m->expires_max, 305 .flags = m->flags, 306 .invflags = m->invflags, 307 }; 308 memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); 309 memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); 310 memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); 311 return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; 312} 313#endif 314 315static struct xt_match conntrack_mt_reg[] __read_mostly = { 316 { 317 .name = "conntrack", 318 .revision = 0, 319 .family = AF_INET, 320 .match = conntrack_mt_v0, 321 .checkentry = conntrack_mt_check, 322 .destroy = conntrack_mt_destroy, 323 .matchsize = sizeof(struct xt_conntrack_info), 324 .me = THIS_MODULE, 325#ifdef CONFIG_COMPAT 326 .compatsize = sizeof(struct compat_xt_conntrack_info), 327 .compat_from_user = conntrack_mt_compat_from_user_v0, 328 .compat_to_user = conntrack_mt_compat_to_user_v0, 329#endif 330 }, 331 { 332 .name = "conntrack", 333 .revision = 1, 334 .family = AF_INET, 335 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 336 .match = conntrack_mt, 337 .checkentry = conntrack_mt_check, 338 .destroy = conntrack_mt_destroy, 339 .me = THIS_MODULE, 340 }, 341 { 342 .name = "conntrack", 343 .revision = 1, 344 .family = AF_INET6, 345 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 346 .match = conntrack_mt, 347 .checkentry = conntrack_mt_check, 348 .destroy = conntrack_mt_destroy, 349 .me = THIS_MODULE, 350 }, 351}; 352 353static int __init conntrack_mt_init(void) 354{ 355 return xt_register_matches(conntrack_mt_reg, 356 ARRAY_SIZE(conntrack_mt_reg)); 357} 358 359static void __exit conntrack_mt_exit(void) 360{ 361 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 362} 363 364module_init(conntrack_mt_init); 365module_exit(conntrack_mt_exit); 366