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