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