xt_conntrack.c revision cff533ac12494fa002e2c46acc94d670e5f636a2
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#include <linux/netfilter/x_tables.h> 14#include <linux/netfilter/xt_conntrack.h> 15#include <net/netfilter/nf_conntrack.h> 16 17MODULE_LICENSE("GPL"); 18MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 19MODULE_DESCRIPTION("iptables connection tracking match module"); 20MODULE_ALIAS("ipt_conntrack"); 21 22static int 23match(const struct sk_buff *skb, 24 const struct net_device *in, 25 const struct net_device *out, 26 const struct xt_match *match, 27 const void *matchinfo, 28 int offset, 29 unsigned int protoff, 30 bool *hotdrop) 31{ 32 const struct xt_conntrack_info *sinfo = matchinfo; 33 struct nf_conn *ct; 34 enum ip_conntrack_info ctinfo; 35 unsigned int statebit; 36 37 ct = nf_ct_get((struct sk_buff *)skb, &ctinfo); 38 39#define FWINV(bool,invflg) ((bool) ^ !!(sinfo->invflags & invflg)) 40 41 if (ct == &nf_conntrack_untracked) 42 statebit = XT_CONNTRACK_STATE_UNTRACKED; 43 else if (ct) 44 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 45 else 46 statebit = XT_CONNTRACK_STATE_INVALID; 47 48 if (sinfo->flags & XT_CONNTRACK_STATE) { 49 if (ct) { 50 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 51 statebit |= XT_CONNTRACK_STATE_SNAT; 52 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 53 statebit |= XT_CONNTRACK_STATE_DNAT; 54 } 55 if (FWINV((statebit & sinfo->statemask) == 0, 56 XT_CONNTRACK_STATE)) 57 return 0; 58 } 59 60 if (ct == NULL) { 61 if (sinfo->flags & ~XT_CONNTRACK_STATE) 62 return 0; 63 return 1; 64 } 65 66 if (sinfo->flags & XT_CONNTRACK_PROTO && 67 FWINV(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum != 68 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum, 69 XT_CONNTRACK_PROTO)) 70 return 0; 71 72 if (sinfo->flags & XT_CONNTRACK_ORIGSRC && 73 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip & 74 sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 75 sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, 76 XT_CONNTRACK_ORIGSRC)) 77 return 0; 78 79 if (sinfo->flags & XT_CONNTRACK_ORIGDST && 80 FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip & 81 sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) != 82 sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, 83 XT_CONNTRACK_ORIGDST)) 84 return 0; 85 86 if (sinfo->flags & XT_CONNTRACK_REPLSRC && 87 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip & 88 sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) != 89 sinfo->tuple[IP_CT_DIR_REPLY].src.ip, 90 XT_CONNTRACK_REPLSRC)) 91 return 0; 92 93 if (sinfo->flags & XT_CONNTRACK_REPLDST && 94 FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip & 95 sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) != 96 sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, 97 XT_CONNTRACK_REPLDST)) 98 return 0; 99 100 if (sinfo->flags & XT_CONNTRACK_STATUS && 101 FWINV((ct->status & sinfo->statusmask) == 0, 102 XT_CONNTRACK_STATUS)) 103 return 0; 104 105 if(sinfo->flags & XT_CONNTRACK_EXPIRES) { 106 unsigned long expires = timer_pending(&ct->timeout) ? 107 (ct->timeout.expires - jiffies)/HZ : 0; 108 109 if (FWINV(!(expires >= sinfo->expires_min && 110 expires <= sinfo->expires_max), 111 XT_CONNTRACK_EXPIRES)) 112 return 0; 113 } 114 return 1; 115} 116 117static int 118checkentry(const char *tablename, 119 const void *ip, 120 const struct xt_match *match, 121 void *matchinfo, 122 unsigned int hook_mask) 123{ 124 if (nf_ct_l3proto_try_module_get(match->family) < 0) { 125 printk(KERN_WARNING "can't load conntrack support for " 126 "proto=%d\n", match->family); 127 return 0; 128 } 129 return 1; 130} 131 132static void destroy(const struct xt_match *match, void *matchinfo) 133{ 134 nf_ct_l3proto_module_put(match->family); 135} 136 137#ifdef CONFIG_COMPAT 138struct compat_xt_conntrack_info 139{ 140 compat_uint_t statemask; 141 compat_uint_t statusmask; 142 struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; 143 struct in_addr sipmsk[IP_CT_DIR_MAX]; 144 struct in_addr dipmsk[IP_CT_DIR_MAX]; 145 compat_ulong_t expires_min; 146 compat_ulong_t expires_max; 147 u_int8_t flags; 148 u_int8_t invflags; 149}; 150 151static void compat_from_user(void *dst, void *src) 152{ 153 struct compat_xt_conntrack_info *cm = src; 154 struct xt_conntrack_info m = { 155 .statemask = cm->statemask, 156 .statusmask = cm->statusmask, 157 .expires_min = cm->expires_min, 158 .expires_max = cm->expires_max, 159 .flags = cm->flags, 160 .invflags = cm->invflags, 161 }; 162 memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); 163 memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); 164 memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); 165 memcpy(dst, &m, sizeof(m)); 166} 167 168static int compat_to_user(void __user *dst, void *src) 169{ 170 struct xt_conntrack_info *m = src; 171 struct compat_xt_conntrack_info cm = { 172 .statemask = m->statemask, 173 .statusmask = m->statusmask, 174 .expires_min = m->expires_min, 175 .expires_max = m->expires_max, 176 .flags = m->flags, 177 .invflags = m->invflags, 178 }; 179 memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); 180 memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); 181 memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); 182 return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; 183} 184#endif 185 186static struct xt_match conntrack_match = { 187 .name = "conntrack", 188 .match = match, 189 .checkentry = checkentry, 190 .destroy = destroy, 191 .matchsize = sizeof(struct xt_conntrack_info), 192#ifdef CONFIG_COMPAT 193 .compatsize = sizeof(struct compat_xt_conntrack_info), 194 .compat_from_user = compat_from_user, 195 .compat_to_user = compat_to_user, 196#endif 197 .family = AF_INET, 198 .me = THIS_MODULE, 199}; 200 201static int __init xt_conntrack_init(void) 202{ 203 return xt_register_match(&conntrack_match); 204} 205 206static void __exit xt_conntrack_fini(void) 207{ 208 xt_unregister_match(&conntrack_match); 209} 210 211module_init(xt_conntrack_init); 212module_exit(xt_conntrack_fini); 213