1b3d54b3eSJan Engelhardt /* 2b3d54b3eSJan Engelhardt * (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk> 3b3d54b3eSJan Engelhardt * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> 4b3d54b3eSJan Engelhardt * 5b3d54b3eSJan Engelhardt * This program is free software; you can redistribute it and/or modify 6b3d54b3eSJan Engelhardt * it under the terms of the GNU General Public License version 2 as 7b3d54b3eSJan Engelhardt * published by the Free Software Foundation. 8b3d54b3eSJan Engelhardt */ 9b3d54b3eSJan Engelhardt 10b3d54b3eSJan Engelhardt #include <linux/ip.h> 11b3d54b3eSJan Engelhardt #include <linux/kernel.h> 12b3d54b3eSJan Engelhardt #include <linux/module.h> 13b3d54b3eSJan Engelhardt #include <linux/netdevice.h> 14b3d54b3eSJan Engelhardt #include <linux/ipv6.h> 15b3d54b3eSJan Engelhardt #include <linux/netfilter.h> 16b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv4.h> 17b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv6.h> 18b3d54b3eSJan Engelhardt #include <linux/netfilter/x_tables.h> 19b3d54b3eSJan Engelhardt #include <net/netfilter/nf_nat.h> 20b3d54b3eSJan Engelhardt 21b3d54b3eSJan Engelhardt static unsigned int 22b3d54b3eSJan Engelhardt netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par) 23b3d54b3eSJan Engelhardt { 24b3d54b3eSJan Engelhardt const struct nf_nat_range *range = par->targinfo; 25b3d54b3eSJan Engelhardt struct nf_nat_range newrange; 26b3d54b3eSJan Engelhardt struct nf_conn *ct; 27b3d54b3eSJan Engelhardt enum ip_conntrack_info ctinfo; 28b3d54b3eSJan Engelhardt union nf_inet_addr new_addr, netmask; 29b3d54b3eSJan Engelhardt unsigned int i; 30b3d54b3eSJan Engelhardt 31b3d54b3eSJan Engelhardt ct = nf_ct_get(skb, &ctinfo); 32b3d54b3eSJan Engelhardt for (i = 0; i < ARRAY_SIZE(range->min_addr.ip6); i++) 33b3d54b3eSJan Engelhardt netmask.ip6[i] = ~(range->min_addr.ip6[i] ^ 34b3d54b3eSJan Engelhardt range->max_addr.ip6[i]); 35b3d54b3eSJan Engelhardt 36613dbd95SPablo Neira Ayuso if (xt_hooknum(par) == NF_INET_PRE_ROUTING || 37613dbd95SPablo Neira Ayuso xt_hooknum(par) == NF_INET_LOCAL_OUT) 38b3d54b3eSJan Engelhardt new_addr.in6 = ipv6_hdr(skb)->daddr; 39b3d54b3eSJan Engelhardt else 40b3d54b3eSJan Engelhardt new_addr.in6 = ipv6_hdr(skb)->saddr; 41b3d54b3eSJan Engelhardt 42b3d54b3eSJan Engelhardt for (i = 0; i < ARRAY_SIZE(new_addr.ip6); i++) { 43b3d54b3eSJan Engelhardt new_addr.ip6[i] &= ~netmask.ip6[i]; 44b3d54b3eSJan Engelhardt new_addr.ip6[i] |= range->min_addr.ip6[i] & 45b3d54b3eSJan Engelhardt netmask.ip6[i]; 46b3d54b3eSJan Engelhardt } 47b3d54b3eSJan Engelhardt 48b3d54b3eSJan Engelhardt newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 49b3d54b3eSJan Engelhardt newrange.min_addr = new_addr; 50b3d54b3eSJan Engelhardt newrange.max_addr = new_addr; 51b3d54b3eSJan Engelhardt newrange.min_proto = range->min_proto; 52b3d54b3eSJan Engelhardt newrange.max_proto = range->max_proto; 53b3d54b3eSJan Engelhardt 54613dbd95SPablo Neira Ayuso return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); 55b3d54b3eSJan Engelhardt } 56b3d54b3eSJan Engelhardt 57b3d54b3eSJan Engelhardt static int netmap_tg6_checkentry(const struct xt_tgchk_param *par) 58b3d54b3eSJan Engelhardt { 59b3d54b3eSJan Engelhardt const struct nf_nat_range *range = par->targinfo; 60b3d54b3eSJan Engelhardt 61b3d54b3eSJan Engelhardt if (!(range->flags & NF_NAT_RANGE_MAP_IPS)) 62b3d54b3eSJan Engelhardt return -EINVAL; 63a357b3f8SFlorian Westphal return nf_ct_netns_get(par->net, par->family); 64a357b3f8SFlorian Westphal } 65a357b3f8SFlorian Westphal 66a357b3f8SFlorian Westphal static void netmap_tg_destroy(const struct xt_tgdtor_param *par) 67a357b3f8SFlorian Westphal { 68a357b3f8SFlorian Westphal nf_ct_netns_put(par->net, par->family); 69b3d54b3eSJan Engelhardt } 70b3d54b3eSJan Engelhardt 71b3d54b3eSJan Engelhardt static unsigned int 72b3d54b3eSJan Engelhardt netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) 73b3d54b3eSJan Engelhardt { 74b3d54b3eSJan Engelhardt struct nf_conn *ct; 75b3d54b3eSJan Engelhardt enum ip_conntrack_info ctinfo; 76b3d54b3eSJan Engelhardt __be32 new_ip, netmask; 77b3d54b3eSJan Engelhardt const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; 78b3d54b3eSJan Engelhardt struct nf_nat_range newrange; 79b3d54b3eSJan Engelhardt 8044d6e2f2SVarsha Rao WARN_ON(xt_hooknum(par) != NF_INET_PRE_ROUTING && 8144d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_POST_ROUTING && 8244d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_LOCAL_OUT && 8344d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_LOCAL_IN); 84b3d54b3eSJan Engelhardt ct = nf_ct_get(skb, &ctinfo); 85b3d54b3eSJan Engelhardt 86b3d54b3eSJan Engelhardt netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); 87b3d54b3eSJan Engelhardt 88613dbd95SPablo Neira Ayuso if (xt_hooknum(par) == NF_INET_PRE_ROUTING || 89613dbd95SPablo Neira Ayuso xt_hooknum(par) == NF_INET_LOCAL_OUT) 90b3d54b3eSJan Engelhardt new_ip = ip_hdr(skb)->daddr & ~netmask; 91b3d54b3eSJan Engelhardt else 92b3d54b3eSJan Engelhardt new_ip = ip_hdr(skb)->saddr & ~netmask; 93b3d54b3eSJan Engelhardt new_ip |= mr->range[0].min_ip & netmask; 94b3d54b3eSJan Engelhardt 95b3d54b3eSJan Engelhardt memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 96b3d54b3eSJan Engelhardt memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 97b3d54b3eSJan Engelhardt newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; 98b3d54b3eSJan Engelhardt newrange.min_addr.ip = new_ip; 99b3d54b3eSJan Engelhardt newrange.max_addr.ip = new_ip; 100b3d54b3eSJan Engelhardt newrange.min_proto = mr->range[0].min; 101b3d54b3eSJan Engelhardt newrange.max_proto = mr->range[0].max; 102b3d54b3eSJan Engelhardt 103b3d54b3eSJan Engelhardt /* Hand modified range to generic setup. */ 104613dbd95SPablo Neira Ayuso return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); 105b3d54b3eSJan Engelhardt } 106b3d54b3eSJan Engelhardt 107b3d54b3eSJan Engelhardt static int netmap_tg4_check(const struct xt_tgchk_param *par) 108b3d54b3eSJan Engelhardt { 109b3d54b3eSJan Engelhardt const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; 110b3d54b3eSJan Engelhardt 111b3d54b3eSJan Engelhardt if (!(mr->range[0].flags & NF_NAT_RANGE_MAP_IPS)) { 112b3d54b3eSJan Engelhardt pr_debug("bad MAP_IPS.\n"); 113b3d54b3eSJan Engelhardt return -EINVAL; 114b3d54b3eSJan Engelhardt } 115b3d54b3eSJan Engelhardt if (mr->rangesize != 1) { 116b3d54b3eSJan Engelhardt pr_debug("bad rangesize %u.\n", mr->rangesize); 117b3d54b3eSJan Engelhardt return -EINVAL; 118b3d54b3eSJan Engelhardt } 119a357b3f8SFlorian Westphal return nf_ct_netns_get(par->net, par->family); 120b3d54b3eSJan Engelhardt } 121b3d54b3eSJan Engelhardt 122b3d54b3eSJan Engelhardt static struct xt_target netmap_tg_reg[] __read_mostly = { 123b3d54b3eSJan Engelhardt { 124b3d54b3eSJan Engelhardt .name = "NETMAP", 125b3d54b3eSJan Engelhardt .family = NFPROTO_IPV6, 126b3d54b3eSJan Engelhardt .revision = 0, 127b3d54b3eSJan Engelhardt .target = netmap_tg6, 128b3d54b3eSJan Engelhardt .targetsize = sizeof(struct nf_nat_range), 129b3d54b3eSJan Engelhardt .table = "nat", 130b3d54b3eSJan Engelhardt .hooks = (1 << NF_INET_PRE_ROUTING) | 131b3d54b3eSJan Engelhardt (1 << NF_INET_POST_ROUTING) | 132b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_OUT) | 133b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_IN), 134b3d54b3eSJan Engelhardt .checkentry = netmap_tg6_checkentry, 135a357b3f8SFlorian Westphal .destroy = netmap_tg_destroy, 136b3d54b3eSJan Engelhardt .me = THIS_MODULE, 137b3d54b3eSJan Engelhardt }, 138b3d54b3eSJan Engelhardt { 139b3d54b3eSJan Engelhardt .name = "NETMAP", 140b3d54b3eSJan Engelhardt .family = NFPROTO_IPV4, 141b3d54b3eSJan Engelhardt .revision = 0, 142b3d54b3eSJan Engelhardt .target = netmap_tg4, 143b3d54b3eSJan Engelhardt .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), 144b3d54b3eSJan Engelhardt .table = "nat", 145b3d54b3eSJan Engelhardt .hooks = (1 << NF_INET_PRE_ROUTING) | 146b3d54b3eSJan Engelhardt (1 << NF_INET_POST_ROUTING) | 147b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_OUT) | 148b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_IN), 149b3d54b3eSJan Engelhardt .checkentry = netmap_tg4_check, 150a357b3f8SFlorian Westphal .destroy = netmap_tg_destroy, 151b3d54b3eSJan Engelhardt .me = THIS_MODULE, 152b3d54b3eSJan Engelhardt }, 153b3d54b3eSJan Engelhardt }; 154b3d54b3eSJan Engelhardt 155b3d54b3eSJan Engelhardt static int __init netmap_tg_init(void) 156b3d54b3eSJan Engelhardt { 157b3d54b3eSJan Engelhardt return xt_register_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg)); 158b3d54b3eSJan Engelhardt } 159b3d54b3eSJan Engelhardt 160b3d54b3eSJan Engelhardt static void netmap_tg_exit(void) 161b3d54b3eSJan Engelhardt { 162b3d54b3eSJan Engelhardt xt_unregister_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg)); 163b3d54b3eSJan Engelhardt } 164b3d54b3eSJan Engelhardt 165b3d54b3eSJan Engelhardt module_init(netmap_tg_init); 166b3d54b3eSJan Engelhardt module_exit(netmap_tg_exit); 167b3d54b3eSJan Engelhardt 168b3d54b3eSJan Engelhardt MODULE_LICENSE("GPL"); 169b3d54b3eSJan Engelhardt MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of subnets"); 170b3d54b3eSJan Engelhardt MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 171b3d54b3eSJan Engelhardt MODULE_ALIAS("ip6t_NETMAP"); 172b3d54b3eSJan Engelhardt MODULE_ALIAS("ipt_NETMAP"); 173