1/* 2 * Kernel module to match cone target. 3 * 4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: ipt_cone.c,v 1.2 2010/07/12 23:55:51 Exp $ 19 */ 20#include <linux/module.h> 21#include <linux/types.h> 22#include <linux/timer.h> 23#include <linux/skbuff.h> 24#include <linux/vmalloc.h> 25#include <net/ip.h> 26#include <linux/udp.h> 27#include <net/netfilter/nf_nat_rule.h> 28#include <linux/netfilter_ipv4.h> 29#include <linux/netfilter/x_tables.h> 30 31#define DEBUGP(format, args...) 32 33static DEFINE_RWLOCK(cone_lock); 34 35/* Calculated at init based on memory size */ 36static unsigned int ipt_cone_htable_size; 37static struct list_head *bycone; 38 39static inline size_t 40hash_by_cone(__be32 ip, __be16 port, u_int8_t protonum) 41{ 42 /* Original src, to ensure we map it consistently if poss. */ 43 return (ip + port + protonum) % ipt_cone_htable_size; 44} 45 46static inline int 47cone_cmp(const struct nf_conn *ct, 48 const struct nf_conntrack_tuple *tuple) 49{ 50 return (ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum 51 == tuple->dst.protonum && 52 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip 53 == tuple->dst.u3.ip && 54 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port 55 == tuple->dst.u.udp.port); 56} 57 58/* Only called for SRC manip */ 59static struct nf_conn * 60find_appropriate_cone(const struct nf_conntrack_tuple *tuple) 61{ 62 unsigned int h = hash_by_cone(tuple->dst.u3.ip, 63 tuple->dst.u.udp.port, tuple->dst.protonum); 64 65 struct nf_conn_nat *nat; 66 struct nf_conn *ct; 67 68 read_lock_bh(&cone_lock); 69 list_for_each_entry(nat, &bycone[h], info.bycone) { 70 ct = (struct nf_conn *)((char *)nat - offsetof(struct nf_conn, data)); 71 if (cone_cmp(ct, tuple)) { 72 read_unlock_bh(&cone_lock); 73 return ct; 74 } 75 } 76 read_unlock_bh(&cone_lock); 77 return 0; 78} 79 80void 81ipt_cone_place_in_hashes(struct nf_conn *ct) 82{ 83 struct nf_conn_nat *nat = nfct_nat(ct); 84 struct nf_conntrack_tuple *tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; 85 86 /* Make sure it's IPV4 */ 87 if (tuple->src.l3num != PF_INET) 88 return; 89 90 if ((nat->info.nat_type & NFC_IP_CONE_NAT)) { 91 if (!find_appropriate_cone(tuple)) { 92 unsigned int conehash = hash_by_cone( 93 tuple->dst.u3.ip, tuple->dst.u.udp.port, tuple->dst.protonum); 94 95 write_lock_bh(&cone_lock); 96 list_add(&nat->info.bycone, &bycone[conehash]); 97 write_unlock_bh(&cone_lock); 98 } 99 } 100} 101 102void 103ipt_cone_cleanup_conntrack(struct nf_conn_nat *nat) 104{ 105 if (nat->info.bycone.next) { 106 write_lock_bh(&cone_lock); 107 list_del(&nat->info.bycone); 108 write_unlock_bh(&cone_lock); 109 } 110} 111 112unsigned int 113ipt_cone_target(struct sk_buff **pskb, 114 unsigned int hooknum, 115 const struct net_device *in, 116 const struct net_device *out, 117 const struct xt_target *target, 118 const void *targinfo) 119{ 120 struct nf_conn *ct; 121 struct nf_conn_nat *nat; 122 enum ip_conntrack_info ctinfo; 123 struct nf_nat_range newrange; 124 const struct nf_nat_multi_range_compat *mr = targinfo; 125 __be32 newdst; 126 __be16 newport; 127 struct nf_conntrack_tuple *tuple; 128 struct nf_conn *cone; 129 130 131 /* Care about only new created one */ 132 ct = nf_ct_get(*pskb, &ctinfo); 133 if (ct == 0 || (ctinfo != IP_CT_NEW && ctinfo != IP_CT_RELATED)) 134 return XT_CONTINUE; 135 136 nat = nfct_nat(ct); 137 138 /* As a matter of fact, CONE NAT should only apply on IPPROTO_UDP */ 139 tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; 140 if (tuple->src.l3num != PF_INET || tuple->dst.protonum != IPPROTO_UDP) 141 return XT_CONTINUE; 142 143 /* Handle forwarding from WAN to LAN */ 144 if (hooknum == NF_IP_FORWARD) { 145 if (nat->info.nat_type & NFC_IP_CONE_NAT_ALTERED) 146 return NF_ACCEPT; 147 else 148 return XT_CONTINUE; 149 } 150 151 /* Make sure it is pre routing */ 152 if (hooknum != NF_IP_PRE_ROUTING) 153 return XT_CONTINUE; 154 155 if (nat->info.nat_type & NFC_IP_CONE_NAT_ALTERED) 156 return NF_ACCEPT; 157 158 /* Get cone dst */ 159 cone = find_appropriate_cone(tuple); 160 if (cone == NULL) 161 return XT_CONTINUE; 162 163 /* Mark it's a CONE_NAT_TYPE, so NF_IP_FORWARD can accept it */ 164 nat->info.nat_type |= NFC_IP_CONE_NAT_ALTERED; 165 166 /* Setup new dst ip and port */ 167 newdst = cone->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; 168 newport = cone->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; 169 170 /* Transfer from original range. */ 171 newrange = ((struct nf_nat_range) 172 { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS, 173 newdst, newdst, 174 {newport}, {newport} }); 175 176 /* Hand modified range to generic setup. */ 177 return nf_nat_setup_info(ct, &newrange, hooknum); 178} 179 180/* Init/cleanup */ 181static int __init ipt_cone_init(void) 182{ 183 size_t i; 184 185 /* Leave them the same for the moment. */ 186 ipt_cone_htable_size = nf_conntrack_htable_size; 187 188 /* vmalloc for hash table */ 189 bycone = vmalloc(sizeof(struct list_head) * ipt_cone_htable_size); 190 if (!bycone) 191 return -ENOMEM; 192 193 for (i = 0; i < ipt_cone_htable_size; i++) { 194 INIT_LIST_HEAD(&bycone[i]); 195 } 196 197 return 0; 198} 199 200static void __exit ipt_cone_cleanup(void) 201{ 202 vfree(bycone); 203} 204 205module_init(ipt_cone_init); 206module_exit(ipt_cone_cleanup); 207MODULE_LICENSE("Proprietary"); 208