xt_conntrack.c revision 601e68e100b6bf8ba13a32db8faf92d43acaa997
1/* Kernel module to match connection tracking information. 2 * Superset of Rusty's minimalistic state match. 3 * 4 * (C) 2001 Marc Boucher (marc@mbsi.ca). 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/module.h> 12#include <linux/skbuff.h> 13 14#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) 15#include <linux/netfilter_ipv4/ip_conntrack.h> 16#include <linux/netfilter_ipv4/ip_conntrack_tuple.h> 17#else 18#include <net/netfilter/nf_conntrack.h> 19#endif 20 21#include <linux/netfilter/x_tables.h> 22#include <linux/netfilter/xt_conntrack.h> 23#include <net/netfilter/nf_conntrack_compat.h> 24 25MODULE_LICENSE("GPL"); 26MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 27MODULE_DESCRIPTION("iptables connection tracking match module"); 28MODULE_ALIAS("ipt_conntrack"); 29 30#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE) 31 32static int 33match(const struct sk_buff *skb, 34 const struct net_device *in, 35 const struct net_device *out, 36 const struct xt_match *match, 37 const void *matchinfo, 38 int offset, 39 unsigned int protoff, 40 int *hotdrop) 41{ 42 const struct xt_conntrack_info *sinfo = matchinfo; 43 struct ip_conntrack *ct; 44 enum ip_conntrack_info ctinfo; 45 unsigned int statebit; 46 47 ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo); 48 49#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & invflg)) 50 51 if (ct == &ip_conntrack_untracked) 52 statebit = XT_CONNTRACK_STATE_UNTRACKED; 53 else if (ct) 54 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 55 else 56 statebit = XT_CONNTRACK_STATE_INVALID; 57 58 if (sinfo->flags & XT_CONNTRACK_STATE) { 59 if (ct) { 60 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 61 statebit |= XT_CONNTRACK_STATE_SNAT; 62 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 63 statebit |= XT_CONNTRACK_STATE_DNAT; 64 } 65 if (FWINV((statebit & sinfo->statemask) == 0, 66 XT_CONNTRACK_STATE)) 67 return 0; 68 } 69 70 if (ct == NULL) { 71 if (sinfo->flags & ~XT_CONNTRACK_STATE) 72 return 0; 73 return 1; 74 } 75 76 if (sinfo->flags & XT_CONNTRACK_PROTO && 77 FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != 78 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, 79 XT_CONNTRACK_PROTO)) 80 return 0; 81 82 if (sinfo->flags & XT_CONNTRACK_ORIGSRC && 83 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip & 84 sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 85 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 86 XT_CONNTRACK_ORIGSRC)) 87 return 0; 88 89 if (sinfo->flags & XT_CONNTRACK_ORIGDST && 90 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip & 91 sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 92 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 93 XT_CONNTRACK_ORIGDST)) 94 return 0; 95 96 if (sinfo->flags & XT_CONNTRACK_REPLSRC && 97 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip & 98 sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != 99 sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 100 XT_CONNTRACK_REPLSRC)) 101 return 0; 102 103 if (sinfo->flags & XT_CONNTRACK_REPLDST && 104 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip & 105 sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != 106 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 107 XT_CONNTRACK_REPLDST)) 108 return 0; 109 110 if (sinfo->flags & XT_CONNTRACK_STATUS && 111 FWINV((ct->status & sinfo->statusmask) == 0, 112 XT_CONNTRACK_STATUS)) 113 return 0; 114 115 if (sinfo->flags & XT_CONNTRACK_EXPIRES) { 116 unsigned long expires = timer_pending(&ct->timeout) ? 117 (ct->timeout.expires - jiffies)/HZ : 0; 118 119 if (FWINV(!(expires >= sinfo->expires_min && 120 expires <= sinfo->expires_max), 121 XT_CONNTRACK_EXPIRES)) 122 return 0; 123 } 124 return 1; 125} 126 127#else /* CONFIG_IP_NF_CONNTRACK */ 128static int 129match(const struct sk_buff *skb, 130 const struct net_device *in, 131 const struct net_device *out, 132 const struct xt_match *match, 133 const void *matchinfo, 134 int offset, 135 unsigned int protoff, 136 int *hotdrop) 137{ 138 const struct xt_conntrack_info *sinfo = matchinfo; 139 struct nf_conn *ct; 140 enum ip_conntrack_info ctinfo; 141 unsigned int statebit; 142 143 ct = nf_ct_get((struct sk_buff *)skb, &ctinfo); 144 145#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) 146 147 if (ct == &nf_conntrack_untracked) 148 statebit = XT_CONNTRACK_STATE_UNTRACKED; 149 else if (ct) 150 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 151 else 152 statebit = XT_CONNTRACK_STATE_INVALID; 153 154 if (sinfo->flags & XT_CONNTRACK_STATE) { 155 if (ct) { 156 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 157 statebit |= XT_CONNTRACK_STATE_SNAT; 158 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 159 statebit |= XT_CONNTRACK_STATE_DNAT; 160 } 161 if (FWINV((statebit & sinfo->statemask) == 0, 162 XT_CONNTRACK_STATE)) 163 return 0; 164 } 165 166 if (ct == NULL) { 167 if (sinfo->flags & ~XT_CONNTRACK_STATE) 168 return 0; 169 return 1; 170 } 171 172 if (sinfo->flags & XT_CONNTRACK_PROTO && 173 FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != 174 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, 175 XT_CONNTRACK_PROTO)) 176 return 0; 177 178 if (sinfo->flags & XT_CONNTRACK_ORIGSRC && 179 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & 180 sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 181 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 182 XT_CONNTRACK_ORIGSRC)) 183 return 0; 184 185 if (sinfo->flags & XT_CONNTRACK_ORIGDST && 186 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & 187 sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 188 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 189 XT_CONNTRACK_ORIGDST)) 190 return 0; 191 192 if (sinfo->flags & XT_CONNTRACK_REPLSRC && 193 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & 194 sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != 195 sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 196 XT_CONNTRACK_REPLSRC)) 197 return 0; 198 199 if (sinfo->flags & XT_CONNTRACK_REPLDST && 200 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & 201 sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != 202 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 203 XT_CONNTRACK_REPLDST)) 204 return 0; 205 206 if (sinfo->flags & XT_CONNTRACK_STATUS && 207 FWINV((ct->status & sinfo->statusmask) == 0, 208 XT_CONNTRACK_STATUS)) 209 return 0; 210 211 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 212 unsigned long expires = timer_pending(&ct->timeout) ? 213 (ct->timeout.expires - jiffies)/HZ : 0; 214 215 if (FWINV(!(expires >= sinfo->expires_min && 216 expires <= sinfo->expires_max), 217 XT_CONNTRACK_EXPIRES)) 218 return 0; 219 } 220 return 1; 221} 222 223#endif /* CONFIG_NF_IP_CONNTRACK */ 224 225static int 226checkentry(const char *tablename, 227 const void *ip, 228 const struct xt_match *match, 229 void *matchinfo, 230 unsigned int hook_mask) 231{ 232 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 233 printk(KERN_WARNING "can't load conntrack support for " 234 "proto=%d\n", match->family); 235 return 0; 236 } 237 return 1; 238} 239 240static void destroy(const struct xt_match *match, void *matchinfo) 241{ 242 nf_ct_l3proto_module_put(match->family); 243} 244 245static struct xt_match conntrack_match = { 246 .name = "conntrack", 247 .match = match, 248 .checkentry = checkentry, 249 .destroy = destroy, 250 .matchsize = sizeof(struct xt_conntrack_info), 251 .family = AF_INET, 252 .me = THIS_MODULE, 253}; 254 255static int __init xt_conntrack_init(void) 256{ 257 return xt_register_match(&conntrack_match); 258} 259 260static void __exit xt_conntrack_fini(void) 261{ 262 xt_unregister_match(&conntrack_match); 263} 264 265module_init(xt_conntrack_init); 266module_exit(xt_conntrack_fini); 267