xt_conntrack.c revision 93bbce1ad0cd788190dd7d6c17d289f771fe3d0d
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 inline bool 116port_match(u16 min, u16 max, u16 port, bool invert) 117{ 118 return (port >= min && port <= max) ^ invert; 119} 120 121static inline bool 122ct_proto_port_check_v3(const struct xt_conntrack_mtinfo3 *info, 123 const struct nf_conn *ct) 124{ 125 const struct nf_conntrack_tuple *tuple; 126 127 tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 128 if ((info->match_flags & XT_CONNTRACK_PROTO) && 129 (nf_ct_protonum(ct) == info->l4proto) ^ 130 !(info->invert_flags & XT_CONNTRACK_PROTO)) 131 return false; 132 133 /* Shortcut to match all recognized protocols by using ->src.all. */ 134 if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) && 135 !port_match(info->origsrc_port, info->origsrc_port_high, 136 ntohs(tuple->src.u.all), 137 info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT)) 138 return false; 139 140 if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) && 141 !port_match(info->origdst_port, info->origdst_port_high, 142 ntohs(tuple->dst.u.all), 143 info->invert_flags & XT_CONNTRACK_ORIGDST_PORT)) 144 return false; 145 146 tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 147 148 if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) && 149 !port_match(info->replsrc_port, info->replsrc_port_high, 150 ntohs(tuple->src.u.all), 151 info->invert_flags & XT_CONNTRACK_REPLSRC_PORT)) 152 return false; 153 154 if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) && 155 !port_match(info->repldst_port, info->repldst_port_high, 156 ntohs(tuple->dst.u.all), 157 info->invert_flags & XT_CONNTRACK_REPLDST_PORT)) 158 return false; 159 160 return true; 161} 162 163static bool 164conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, 165 u16 state_mask, u16 status_mask) 166{ 167 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 168 enum ip_conntrack_info ctinfo; 169 const struct nf_conn *ct; 170 unsigned int statebit; 171 172 ct = nf_ct_get(skb, &ctinfo); 173 174 if (ct) { 175 if (nf_ct_is_untracked(ct)) 176 statebit = XT_CONNTRACK_STATE_UNTRACKED; 177 else 178 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 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, par->family) ^ 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, par->family) ^ 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, par->family) ^ 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, par->family) ^ 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 = 0; 236 237 if (timer_pending(&ct->timeout)) 238 expires = (ct->timeout.expires - jiffies) / HZ; 239 if ((expires >= info->expires_min && 240 expires <= info->expires_max) ^ 241 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 242 return false; 243 } 244 return true; 245} 246 247static bool 248conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 249{ 250 const struct xt_conntrack_mtinfo1 *info = par->matchinfo; 251 252 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 253} 254 255static bool 256conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) 257{ 258 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 259 260 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 261} 262 263static bool 264conntrack_mt_v3(const struct sk_buff *skb, struct xt_action_param *par) 265{ 266 const struct xt_conntrack_mtinfo3 *info = par->matchinfo; 267 268 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 269} 270 271static int conntrack_mt_check(const struct xt_mtchk_param *par) 272{ 273 int ret; 274 275 ret = nf_ct_l3proto_try_module_get(par->family); 276 if (ret < 0) 277 pr_info("cannot load conntrack support for proto=%u\n", 278 par->family); 279 return ret; 280} 281 282static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 283{ 284 nf_ct_l3proto_module_put(par->family); 285} 286 287static struct xt_match conntrack_mt_reg[] __read_mostly = { 288 { 289 .name = "conntrack", 290 .revision = 1, 291 .family = NFPROTO_UNSPEC, 292 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 293 .match = conntrack_mt_v1, 294 .checkentry = conntrack_mt_check, 295 .destroy = conntrack_mt_destroy, 296 .me = THIS_MODULE, 297 }, 298 { 299 .name = "conntrack", 300 .revision = 2, 301 .family = NFPROTO_UNSPEC, 302 .matchsize = sizeof(struct xt_conntrack_mtinfo2), 303 .match = conntrack_mt_v2, 304 .checkentry = conntrack_mt_check, 305 .destroy = conntrack_mt_destroy, 306 .me = THIS_MODULE, 307 }, 308 { 309 .name = "conntrack", 310 .revision = 3, 311 .family = NFPROTO_UNSPEC, 312 .matchsize = sizeof(struct xt_conntrack_mtinfo3), 313 .match = conntrack_mt_v3, 314 .checkentry = conntrack_mt_check, 315 .destroy = conntrack_mt_destroy, 316 .me = THIS_MODULE, 317 }, 318}; 319 320static int __init conntrack_mt_init(void) 321{ 322 return xt_register_matches(conntrack_mt_reg, 323 ARRAY_SIZE(conntrack_mt_reg)); 324} 325 326static void __exit conntrack_mt_exit(void) 327{ 328 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 329} 330 331module_init(conntrack_mt_init); 332module_exit(conntrack_mt_exit); 333