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 21 MODULE_LICENSE("GPL"); 22 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 23 MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>"); 24 MODULE_DESCRIPTION("Xtables: connection tracking state match"); 25 MODULE_ALIAS("ipt_conntrack"); 26 MODULE_ALIAS("ip6t_conntrack"); 27 28 static bool 29 conntrack_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 42 static inline bool 43 conntrack_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 51 static inline bool 52 conntrack_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 60 static inline bool 61 conntrack_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 69 static inline bool 70 conntrack_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 78 static inline bool 79 ct_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 116 static inline bool 117 port_match(u16 min, u16 max, u16 port, bool invert) 118 { 119 return (port >= min && port <= max) ^ invert; 120 } 121 122 static inline bool 123 ct_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 164 static bool 165 conntrack_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 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 177 else if (ctinfo == IP_CT_UNTRACKED) 178 statebit = XT_CONNTRACK_STATE_UNTRACKED; 179 else 180 statebit = XT_CONNTRACK_STATE_INVALID; 181 182 if (info->match_flags & XT_CONNTRACK_STATE) { 183 if (ct != NULL) { 184 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 185 statebit |= XT_CONNTRACK_STATE_SNAT; 186 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 187 statebit |= XT_CONNTRACK_STATE_DNAT; 188 } 189 if (!!(state_mask & statebit) ^ 190 !(info->invert_flags & XT_CONNTRACK_STATE)) 191 return false; 192 } 193 194 if (ct == NULL) 195 return info->match_flags & XT_CONNTRACK_STATE; 196 if ((info->match_flags & XT_CONNTRACK_DIRECTION) && 197 (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ 198 !(info->invert_flags & XT_CONNTRACK_DIRECTION)) 199 return false; 200 201 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 202 if (conntrack_mt_origsrc(ct, info, xt_family(par)) ^ 203 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 204 return false; 205 206 if (info->match_flags & XT_CONNTRACK_ORIGDST) 207 if (conntrack_mt_origdst(ct, info, xt_family(par)) ^ 208 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 209 return false; 210 211 if (info->match_flags & XT_CONNTRACK_REPLSRC) 212 if (conntrack_mt_replsrc(ct, info, xt_family(par)) ^ 213 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 214 return false; 215 216 if (info->match_flags & XT_CONNTRACK_REPLDST) 217 if (conntrack_mt_repldst(ct, info, xt_family(par)) ^ 218 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 219 return false; 220 221 if (par->match->revision != 3) { 222 if (!ct_proto_port_check(info, ct)) 223 return false; 224 } else { 225 if (!ct_proto_port_check_v3(par->matchinfo, ct)) 226 return false; 227 } 228 229 if ((info->match_flags & XT_CONNTRACK_STATUS) && 230 (!!(status_mask & ct->status) ^ 231 !(info->invert_flags & XT_CONNTRACK_STATUS))) 232 return false; 233 234 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 235 unsigned long expires = nf_ct_expires(ct) / HZ; 236 237 if ((expires >= info->expires_min && 238 expires <= info->expires_max) ^ 239 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 240 return false; 241 } 242 return true; 243 } 244 245 static bool 246 conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 247 { 248 const struct xt_conntrack_mtinfo1 *info = par->matchinfo; 249 250 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 251 } 252 253 static bool 254 conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) 255 { 256 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 257 258 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 259 } 260 261 static bool 262 conntrack_mt_v3(const struct sk_buff *skb, struct xt_action_param *par) 263 { 264 const struct xt_conntrack_mtinfo3 *info = par->matchinfo; 265 266 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 267 } 268 269 static int conntrack_mt_check(const struct xt_mtchk_param *par) 270 { 271 int ret; 272 273 ret = nf_ct_netns_get(par->net, par->family); 274 if (ret < 0) 275 pr_info_ratelimited("cannot load conntrack support for proto=%u\n", 276 par->family); 277 return ret; 278 } 279 280 static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 281 { 282 nf_ct_netns_put(par->net, par->family); 283 } 284 285 static struct xt_match conntrack_mt_reg[] __read_mostly = { 286 { 287 .name = "conntrack", 288 .revision = 1, 289 .family = NFPROTO_UNSPEC, 290 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 291 .match = conntrack_mt_v1, 292 .checkentry = conntrack_mt_check, 293 .destroy = conntrack_mt_destroy, 294 .me = THIS_MODULE, 295 }, 296 { 297 .name = "conntrack", 298 .revision = 2, 299 .family = NFPROTO_UNSPEC, 300 .matchsize = sizeof(struct xt_conntrack_mtinfo2), 301 .match = conntrack_mt_v2, 302 .checkentry = conntrack_mt_check, 303 .destroy = conntrack_mt_destroy, 304 .me = THIS_MODULE, 305 }, 306 { 307 .name = "conntrack", 308 .revision = 3, 309 .family = NFPROTO_UNSPEC, 310 .matchsize = sizeof(struct xt_conntrack_mtinfo3), 311 .match = conntrack_mt_v3, 312 .checkentry = conntrack_mt_check, 313 .destroy = conntrack_mt_destroy, 314 .me = THIS_MODULE, 315 }, 316 }; 317 318 static int __init conntrack_mt_init(void) 319 { 320 return xt_register_matches(conntrack_mt_reg, 321 ARRAY_SIZE(conntrack_mt_reg)); 322 } 323 324 static void __exit conntrack_mt_exit(void) 325 { 326 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 327 } 328 329 module_init(conntrack_mt_init); 330 module_exit(conntrack_mt_exit); 331