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, struct xt_action_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) { 127 if (nf_ct_is_untracked(ct)) 128 statebit = XT_CONNTRACK_STATE_UNTRACKED; 129 else 130 statebit = XT_CONNTRACK_STATE_BIT(ctinfo); 131 } else 132 statebit = XT_CONNTRACK_STATE_INVALID; 133 134 if (info->match_flags & XT_CONNTRACK_STATE) { 135 if (ct != NULL) { 136 if (test_bit(IPS_SRC_NAT_BIT, &ct->status)) 137 statebit |= XT_CONNTRACK_STATE_SNAT; 138 if (test_bit(IPS_DST_NAT_BIT, &ct->status)) 139 statebit |= XT_CONNTRACK_STATE_DNAT; 140 } 141 if (!!(state_mask & statebit) ^ 142 !(info->invert_flags & XT_CONNTRACK_STATE)) 143 return false; 144 } 145 146 if (ct == NULL) 147 return info->match_flags & XT_CONNTRACK_STATE; 148 if ((info->match_flags & XT_CONNTRACK_DIRECTION) && 149 (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^ 150 !!(info->invert_flags & XT_CONNTRACK_DIRECTION)) 151 return false; 152 153 if (info->match_flags & XT_CONNTRACK_ORIGSRC) 154 if (conntrack_mt_origsrc(ct, info, par->family) ^ 155 !(info->invert_flags & XT_CONNTRACK_ORIGSRC)) 156 return false; 157 158 if (info->match_flags & XT_CONNTRACK_ORIGDST) 159 if (conntrack_mt_origdst(ct, info, par->family) ^ 160 !(info->invert_flags & XT_CONNTRACK_ORIGDST)) 161 return false; 162 163 if (info->match_flags & XT_CONNTRACK_REPLSRC) 164 if (conntrack_mt_replsrc(ct, info, par->family) ^ 165 !(info->invert_flags & XT_CONNTRACK_REPLSRC)) 166 return false; 167 168 if (info->match_flags & XT_CONNTRACK_REPLDST) 169 if (conntrack_mt_repldst(ct, info, par->family) ^ 170 !(info->invert_flags & XT_CONNTRACK_REPLDST)) 171 return false; 172 173 if (!ct_proto_port_check(info, ct)) 174 return false; 175 176 if ((info->match_flags & XT_CONNTRACK_STATUS) && 177 (!!(status_mask & ct->status) ^ 178 !(info->invert_flags & XT_CONNTRACK_STATUS))) 179 return false; 180 181 if (info->match_flags & XT_CONNTRACK_EXPIRES) { 182 unsigned long expires = 0; 183 184 if (timer_pending(&ct->timeout)) 185 expires = (ct->timeout.expires - jiffies) / HZ; 186 if ((expires >= info->expires_min && 187 expires <= info->expires_max) ^ 188 !(info->invert_flags & XT_CONNTRACK_EXPIRES)) 189 return false; 190 } 191 return true; 192} 193 194static bool 195conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) 196{ 197 const struct xt_conntrack_mtinfo1 *info = par->matchinfo; 198 199 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 200} 201 202static bool 203conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) 204{ 205 const struct xt_conntrack_mtinfo2 *info = par->matchinfo; 206 207 return conntrack_mt(skb, par, info->state_mask, info->status_mask); 208} 209 210static int conntrack_mt_check(const struct xt_mtchk_param *par) 211{ 212 int ret; 213 214 ret = nf_ct_l3proto_try_module_get(par->family); 215 if (ret < 0) 216 pr_info("cannot load conntrack support for proto=%u\n", 217 par->family); 218 return ret; 219} 220 221static void conntrack_mt_destroy(const struct xt_mtdtor_param *par) 222{ 223 nf_ct_l3proto_module_put(par->family); 224} 225 226static struct xt_match conntrack_mt_reg[] __read_mostly = { 227 { 228 .name = "conntrack", 229 .revision = 1, 230 .family = NFPROTO_UNSPEC, 231 .matchsize = sizeof(struct xt_conntrack_mtinfo1), 232 .match = conntrack_mt_v1, 233 .checkentry = conntrack_mt_check, 234 .destroy = conntrack_mt_destroy, 235 .me = THIS_MODULE, 236 }, 237 { 238 .name = "conntrack", 239 .revision = 2, 240 .family = NFPROTO_UNSPEC, 241 .matchsize = sizeof(struct xt_conntrack_mtinfo2), 242 .match = conntrack_mt_v2, 243 .checkentry = conntrack_mt_check, 244 .destroy = conntrack_mt_destroy, 245 .me = THIS_MODULE, 246 }, 247}; 248 249static int __init conntrack_mt_init(void) 250{ 251 return xt_register_matches(conntrack_mt_reg, 252 ARRAY_SIZE(conntrack_mt_reg)); 253} 254 255static void __exit conntrack_mt_exit(void) 256{ 257 xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); 258} 259 260module_init(conntrack_mt_init); 261module_exit(conntrack_mt_exit); 262