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