xt_conntrack.c revision 4a5a5c73b7cfee46a0b1411903cfa0dea532deec
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 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13#include <linux/module.h> 14#include <linux/skbuff.h> 15#include <net/ipv6.h> 16#include <linux/netfilter/x_tables.h> 17#include <linux/netfilter/xt_conntrack.h> 18#include <net/netfilter/nf_conntrack.h> 19 20MODULE_LICENSE("GPL"); 21MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 22MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); 23MODULE_DESCRIPTION("Xtables: connection tracking state match"); 24MODULE_ALIAS("ipt_conntrack"); 25MODULE_ALIAS("ip6t_conntrack"); 26 27static bool 28conntrack_addrcmp(const union nf_inet_addr *kaddr, 29 const union nf_inet_addr *uaddr, 30 const union nf_inet_addr *umask, unsigned int l3proto) 31{ 32 if (l3proto == NFPROTO_IPV4) 33 return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0; 34 else if (l3proto == NFPROTO_IPV6) 35 return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6, 36 &uaddr->in6) == 0; 37 else 38 return false; 39} 40 41static inline bool 42conntrack_mt_origsrc(const struct nf_conn *ct, 43 const struct xt_conntrack_mtinfo2 *info, 44 u_int8_t family) 45{ 46 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, 47 &info->origsrc_addr, &info->origsrc_mask, family); 48} 49 50static inline bool 51conntrack_mt_origdst(const struct nf_conn *ct, 52 const struct xt_conntrack_mtinfo2 *info, 53 u_int8_t family) 54{ 55 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3, 56 &info->origdst_addr, &info->origdst_mask, family); 57} 58 59static inline bool 60conntrack_mt_replsrc(const struct nf_conn *ct, 61 const struct xt_conntrack_mtinfo2 *info, 62 u_int8_t family) 63{ 64 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3, 65 &info->replsrc_addr, &info->replsrc_mask, family); 66} 67 68static inline bool 69conntrack_mt_repldst(const struct nf_conn *ct, 70 const struct xt_conntrack_mtinfo2 *info, 71 u_int8_t family) 72{ 73 return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3, 74 &info->repldst_addr, &info->repldst_mask, family); 75} 76 77static inline bool 78ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info, 79 const struct nf_conn *ct) 80{ 81 const struct nf_conntrack_tuple *tuple; 82 83 tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 84 if ((info->match_flags & XT_CONNTRACK_PROTO) && 85 (nf_ct_protonum(ct) == info->l4proto) ^ 86 !(info->invert_flags & XT_CONNTRACK_PROTO)) 87 return false; 88 89 /* Shortcut to match all recognized protocols by using ->src.all. */ 90 if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) && 91 (tuple->src.u.all == info->origsrc_port) ^ 92 !(info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)) 93 return false; 94 95 if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) && 96 (tuple->dst.u.all == info->origdst_port) ^ 97 !(info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)) 98 return false; 99 100 tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 101 102 if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) && 103 (tuple->src.u.all == info->replsrc_port) ^ 104 !(info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)) 105 return false; 106 107 if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) && 108 (tuple->dst.u.all == info->repldst_port) ^ 109 !(info->invert_flags & XT_CONNTRACK_REPLDST_PORT)) 110 return false; 111 112 return true; 113} 114 115static bool 116conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par, 117 u16 state_mask, u16 status_mask) 118{ 119 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 120 enum ip_conntrack_info ctinfo; 121 const struct nf_conn *ct; 122 unsigned int statebit; 123 124 ct = nf_ct_get(skb, &ctinfo); 125 126 if (ct == &nf_conntrack_untracked) 127 statebit = XT_CONNTRACK_STATE_UNTRACKED; 128 else if (ct != NULL) 129 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 130 else 131 statebit = XT_CONNTRACK_STATE_INVALID; 132 133 if (info->match_flags & XT_CONNTRACK_STATE) { 134 if (ct != NULL) { 135 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 136 statebit |= XT_CONNTRACK_STATE_SNAT; 137 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 138 statebit |= XT_CONNTRACK_STATE_DNAT; 139 } 140 if (!!(state_mask & statebit) ^ 141 !(info->invert_flags & XT_CONNTRACK_STATE)) 142 return false; 143 } 144 145 if (ct == NULL) 146 return info->match_flags & XT_CONNTRACK_STATE; 147 if ((info->match_flags & XT_CONNTRACK_DIRECTION) && 148 (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ 149 !!(info->invert_flags & XT_CONNTRACK_DIRECTION)) 150 return false; 151 152 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 153 if (conntrack_mt_origsrc(ct, info, par->family) ^ 154 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 155 return false; 156 157 if (info->match_flags & XT_CONNTRACK_ORIGDST) 158 if (conntrack_mt_origdst(ct, info, par->family) ^ 159 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 160 return false; 161 162 if (info->match_flags & XT_CONNTRACK_REPLSRC) 163 if (conntrack_mt_replsrc(ct, info, par->family) ^ 164 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 165 return false; 166 167 if (info->match_flags & XT_CONNTRACK_REPLDST) 168 if (conntrack_mt_repldst(ct, info, par->family) ^ 169 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 170 return false; 171 172 if (!ct_proto_port_check(info, ct)) 173 return false; 174 175 if ((info->match_flags & XT_CONNTRACK_STATUS) && 176 (!!(status_mask & ct->status) ^ 177 !(info->invert_flags & XT_CONNTRACK_STATUS))) 178 return false; 179 180 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 181 unsigned long expires = 0; 182 183 if (timer_pending(&ct->timeout)) 184 expires = (ct->timeout.expires - jiffies) / HZ; 185 if ((expires >= info->expires_min && 186 expires <= info->expires_max) ^ 187 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 188 return false; 189 } 190 return true; 191} 192 193static bool 194conntrack_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par) 195{ 196 const struct xt_conntrack_mtinfo1 *info = par->matchinfo; 197 198 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 199} 200 201static bool 202conntrack_mt_v2(const struct sk_buff *skb, const struct xt_match_param *par) 203{ 204 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 205 206 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 207} 208 209static int conntrack_mt_check(const struct xt_mtchk_param *par) 210{ 211 int ret; 212 213 ret = nf_ct_l3proto_try_module_get(par->family); 214 if (ret < 0) { 215 pr_info("cannot load conntrack support for proto=%u\n", 216 par->family); 217 return ret; 218 } 219 return 0; 220} 221 222static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 223{ 224 nf_ct_l3proto_module_put(par->family); 225} 226 227static struct xt_match conntrack_mt_reg[] __read_mostly = { 228 { 229 .name = "conntrack", 230 .revision = 1, 231 .family = NFPROTO_UNSPEC, 232 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 233 .match = conntrack_mt_v1, 234 .checkentry = conntrack_mt_check, 235 .destroy = conntrack_mt_destroy, 236 .me = THIS_MODULE, 237 }, 238 { 239 .name = "conntrack", 240 .revision = 2, 241 .family = NFPROTO_UNSPEC, 242 .matchsize = sizeof(struct xt_conntrack_mtinfo2), 243 .match = conntrack_mt_v2, 244 .checkentry = conntrack_mt_check, 245 .destroy = conntrack_mt_destroy, 246 .me = THIS_MODULE, 247 }, 248}; 249 250static int __init conntrack_mt_init(void) 251{ 252 return xt_register_matches(conntrack_mt_reg, 253 ARRAY_SIZE(conntrack_mt_reg)); 254} 255 256static void __exit conntrack_mt_exit(void) 257{ 258 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 259} 260 261module_init(conntrack_mt_init); 262module_exit(conntrack_mt_exit); 263