1b4219952SHerbert Xu /* 2b4219952SHerbert Xu * Stateless NAT actions 3b4219952SHerbert Xu * 4b4219952SHerbert Xu * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au> 5b4219952SHerbert Xu * 6b4219952SHerbert Xu * This program is free software; you can redistribute it and/or modify it 7b4219952SHerbert Xu * under the terms of the GNU General Public License as published by the Free 8b4219952SHerbert Xu * Software Foundation; either version 2 of the License, or (at your option) 9b4219952SHerbert Xu * any later version. 10b4219952SHerbert Xu */ 11b4219952SHerbert Xu 12b4219952SHerbert Xu #include <linux/errno.h> 13b4219952SHerbert Xu #include <linux/init.h> 14b4219952SHerbert Xu #include <linux/kernel.h> 15b4219952SHerbert Xu #include <linux/module.h> 16b4219952SHerbert Xu #include <linux/netfilter.h> 17b4219952SHerbert Xu #include <linux/rtnetlink.h> 18b4219952SHerbert Xu #include <linux/skbuff.h> 19b4219952SHerbert Xu #include <linux/slab.h> 20b4219952SHerbert Xu #include <linux/spinlock.h> 21b4219952SHerbert Xu #include <linux/string.h> 22b4219952SHerbert Xu #include <linux/tc_act/tc_nat.h> 23b4219952SHerbert Xu #include <net/act_api.h> 24b4219952SHerbert Xu #include <net/icmp.h> 25b4219952SHerbert Xu #include <net/ip.h> 26b4219952SHerbert Xu #include <net/netlink.h> 27b4219952SHerbert Xu #include <net/tc_act/tc_nat.h> 28b4219952SHerbert Xu #include <net/tcp.h> 29b4219952SHerbert Xu #include <net/udp.h> 30b4219952SHerbert Xu 31b4219952SHerbert Xu 32b4219952SHerbert Xu #define NAT_TAB_MASK 15 33b4219952SHerbert Xu 34*ddf97ccdSWANG Cong static int nat_net_id; 35*ddf97ccdSWANG Cong 3653b2bf3fSPatrick McHardy static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { 3753b2bf3fSPatrick McHardy [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) }, 3853b2bf3fSPatrick McHardy }; 3953b2bf3fSPatrick McHardy 40c1b52739SBenjamin LaHaise static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, 41b4219952SHerbert Xu struct tc_action *a, int ovr, int bind) 42b4219952SHerbert Xu { 43*ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, nat_net_id); 447ba699c6SPatrick McHardy struct nlattr *tb[TCA_NAT_MAX + 1]; 45b4219952SHerbert Xu struct tc_nat *parm; 46cee63723SPatrick McHardy int ret = 0, err; 47b4219952SHerbert Xu struct tcf_nat *p; 48b4219952SHerbert Xu 49cee63723SPatrick McHardy if (nla == NULL) 50b4219952SHerbert Xu return -EINVAL; 51b4219952SHerbert Xu 5253b2bf3fSPatrick McHardy err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy); 53cee63723SPatrick McHardy if (err < 0) 54cee63723SPatrick McHardy return err; 55cee63723SPatrick McHardy 5653b2bf3fSPatrick McHardy if (tb[TCA_NAT_PARMS] == NULL) 57b4219952SHerbert Xu return -EINVAL; 587ba699c6SPatrick McHardy parm = nla_data(tb[TCA_NAT_PARMS]); 59b4219952SHerbert Xu 60*ddf97ccdSWANG Cong if (!tcf_hash_check(tn, parm->index, a, bind)) { 61*ddf97ccdSWANG Cong ret = tcf_hash_create(tn, parm->index, est, a, 62*ddf97ccdSWANG Cong sizeof(*p), bind, false); 6386062033SWANG Cong if (ret) 6486062033SWANG Cong return ret; 65b4219952SHerbert Xu ret = ACT_P_CREATED; 66b4219952SHerbert Xu } else { 671a29321eSJamal Hadi Salim if (bind) 681a29321eSJamal Hadi Salim return 0; 6986062033SWANG Cong tcf_hash_release(a, bind); 701a29321eSJamal Hadi Salim if (!ovr) 71b4219952SHerbert Xu return -EEXIST; 72b4219952SHerbert Xu } 7386062033SWANG Cong p = to_tcf_nat(a); 74b4219952SHerbert Xu 75b4219952SHerbert Xu spin_lock_bh(&p->tcf_lock); 76b4219952SHerbert Xu p->old_addr = parm->old_addr; 77b4219952SHerbert Xu p->new_addr = parm->new_addr; 78b4219952SHerbert Xu p->mask = parm->mask; 79b4219952SHerbert Xu p->flags = parm->flags; 80b4219952SHerbert Xu 81b4219952SHerbert Xu p->tcf_action = parm->action; 82b4219952SHerbert Xu spin_unlock_bh(&p->tcf_lock); 83b4219952SHerbert Xu 84b4219952SHerbert Xu if (ret == ACT_P_CREATED) 85*ddf97ccdSWANG Cong tcf_hash_insert(tn, a); 86b4219952SHerbert Xu 87b4219952SHerbert Xu return ret; 88b4219952SHerbert Xu } 89b4219952SHerbert Xu 90dc7f9f6eSEric Dumazet static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, 91b4219952SHerbert Xu struct tcf_result *res) 92b4219952SHerbert Xu { 93b4219952SHerbert Xu struct tcf_nat *p = a->priv; 94b4219952SHerbert Xu struct iphdr *iph; 95b4219952SHerbert Xu __be32 old_addr; 96b4219952SHerbert Xu __be32 new_addr; 97b4219952SHerbert Xu __be32 mask; 98b4219952SHerbert Xu __be32 addr; 99b4219952SHerbert Xu int egress; 100b4219952SHerbert Xu int action; 101b4219952SHerbert Xu int ihl; 10236d12690SChangli Gao int noff; 103b4219952SHerbert Xu 104b4219952SHerbert Xu spin_lock(&p->tcf_lock); 105b4219952SHerbert Xu 106b4219952SHerbert Xu p->tcf_tm.lastuse = jiffies; 107b4219952SHerbert Xu old_addr = p->old_addr; 108b4219952SHerbert Xu new_addr = p->new_addr; 109b4219952SHerbert Xu mask = p->mask; 110b4219952SHerbert Xu egress = p->flags & TCA_NAT_FLAG_EGRESS; 111b4219952SHerbert Xu action = p->tcf_action; 112b4219952SHerbert Xu 113bfe0d029SEric Dumazet bstats_update(&p->tcf_bstats, skb); 114b4219952SHerbert Xu 115b4219952SHerbert Xu spin_unlock(&p->tcf_lock); 116b4219952SHerbert Xu 117b4219952SHerbert Xu if (unlikely(action == TC_ACT_SHOT)) 118b4219952SHerbert Xu goto drop; 119b4219952SHerbert Xu 12036d12690SChangli Gao noff = skb_network_offset(skb); 12136d12690SChangli Gao if (!pskb_may_pull(skb, sizeof(*iph) + noff)) 122b4219952SHerbert Xu goto drop; 123b4219952SHerbert Xu 124b4219952SHerbert Xu iph = ip_hdr(skb); 125b4219952SHerbert Xu 126b4219952SHerbert Xu if (egress) 127b4219952SHerbert Xu addr = iph->saddr; 128b4219952SHerbert Xu else 129b4219952SHerbert Xu addr = iph->daddr; 130b4219952SHerbert Xu 131b4219952SHerbert Xu if (!((old_addr ^ addr) & mask)) { 1323697649fSDaniel Borkmann if (skb_try_make_writable(skb, sizeof(*iph) + noff)) 133b4219952SHerbert Xu goto drop; 134b4219952SHerbert Xu 135b4219952SHerbert Xu new_addr &= mask; 136b4219952SHerbert Xu new_addr |= addr & ~mask; 137b4219952SHerbert Xu 138b4219952SHerbert Xu /* Rewrite IP header */ 139b4219952SHerbert Xu iph = ip_hdr(skb); 140b4219952SHerbert Xu if (egress) 141b4219952SHerbert Xu iph->saddr = new_addr; 142b4219952SHerbert Xu else 143b4219952SHerbert Xu iph->daddr = new_addr; 144b4219952SHerbert Xu 145be0ea7d5SPatrick McHardy csum_replace4(&iph->check, addr, new_addr); 14633c29ddeSChangli Gao } else if ((iph->frag_off & htons(IP_OFFSET)) || 14733c29ddeSChangli Gao iph->protocol != IPPROTO_ICMP) { 14833c29ddeSChangli Gao goto out; 149b4219952SHerbert Xu } 150b4219952SHerbert Xu 151b4219952SHerbert Xu ihl = iph->ihl * 4; 152b4219952SHerbert Xu 153b4219952SHerbert Xu /* It would be nice to share code with stateful NAT. */ 154b4219952SHerbert Xu switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) { 155b4219952SHerbert Xu case IPPROTO_TCP: 156b4219952SHerbert Xu { 157b4219952SHerbert Xu struct tcphdr *tcph; 158b4219952SHerbert Xu 15936d12690SChangli Gao if (!pskb_may_pull(skb, ihl + sizeof(*tcph) + noff) || 1603697649fSDaniel Borkmann skb_try_make_writable(skb, ihl + sizeof(*tcph) + noff)) 161b4219952SHerbert Xu goto drop; 162b4219952SHerbert Xu 163b4219952SHerbert Xu tcph = (void *)(skb_network_header(skb) + ihl); 1644b048d6dSTom Herbert inet_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1654b048d6dSTom Herbert true); 166b4219952SHerbert Xu break; 167b4219952SHerbert Xu } 168b4219952SHerbert Xu case IPPROTO_UDP: 169b4219952SHerbert Xu { 170b4219952SHerbert Xu struct udphdr *udph; 171b4219952SHerbert Xu 17236d12690SChangli Gao if (!pskb_may_pull(skb, ihl + sizeof(*udph) + noff) || 1733697649fSDaniel Borkmann skb_try_make_writable(skb, ihl + sizeof(*udph) + noff)) 174b4219952SHerbert Xu goto drop; 175b4219952SHerbert Xu 176b4219952SHerbert Xu udph = (void *)(skb_network_header(skb) + ihl); 177b4219952SHerbert Xu if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) { 178be0ea7d5SPatrick McHardy inet_proto_csum_replace4(&udph->check, skb, addr, 1794b048d6dSTom Herbert new_addr, true); 180b4219952SHerbert Xu if (!udph->check) 181b4219952SHerbert Xu udph->check = CSUM_MANGLED_0; 182b4219952SHerbert Xu } 183b4219952SHerbert Xu break; 184b4219952SHerbert Xu } 185b4219952SHerbert Xu case IPPROTO_ICMP: 186b4219952SHerbert Xu { 187b4219952SHerbert Xu struct icmphdr *icmph; 188b4219952SHerbert Xu 18936d12690SChangli Gao if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + noff)) 190b4219952SHerbert Xu goto drop; 191b4219952SHerbert Xu 192b4219952SHerbert Xu icmph = (void *)(skb_network_header(skb) + ihl); 193b4219952SHerbert Xu 194b4219952SHerbert Xu if ((icmph->type != ICMP_DEST_UNREACH) && 195b4219952SHerbert Xu (icmph->type != ICMP_TIME_EXCEEDED) && 196b4219952SHerbert Xu (icmph->type != ICMP_PARAMETERPROB)) 197b4219952SHerbert Xu break; 198b4219952SHerbert Xu 19936d12690SChangli Gao if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph) + 20036d12690SChangli Gao noff)) 20170c2efa5SChangli Gao goto drop; 20270c2efa5SChangli Gao 203072d79a3SChangli Gao icmph = (void *)(skb_network_header(skb) + ihl); 204b4219952SHerbert Xu iph = (void *)(icmph + 1); 205b4219952SHerbert Xu if (egress) 206b4219952SHerbert Xu addr = iph->daddr; 207b4219952SHerbert Xu else 208b4219952SHerbert Xu addr = iph->saddr; 209b4219952SHerbert Xu 210b4219952SHerbert Xu if ((old_addr ^ addr) & mask) 211b4219952SHerbert Xu break; 212b4219952SHerbert Xu 2133697649fSDaniel Borkmann if (skb_try_make_writable(skb, ihl + sizeof(*icmph) + 2143697649fSDaniel Borkmann sizeof(*iph) + noff)) 215b4219952SHerbert Xu goto drop; 216b4219952SHerbert Xu 217b4219952SHerbert Xu icmph = (void *)(skb_network_header(skb) + ihl); 218b4219952SHerbert Xu iph = (void *)(icmph + 1); 219b4219952SHerbert Xu 220b4219952SHerbert Xu new_addr &= mask; 221b4219952SHerbert Xu new_addr |= addr & ~mask; 222b4219952SHerbert Xu 223b4219952SHerbert Xu /* XXX Fix up the inner checksums. */ 224b4219952SHerbert Xu if (egress) 225b4219952SHerbert Xu iph->daddr = new_addr; 226b4219952SHerbert Xu else 227b4219952SHerbert Xu iph->saddr = new_addr; 228b4219952SHerbert Xu 229be0ea7d5SPatrick McHardy inet_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr, 2304b048d6dSTom Herbert false); 231b4219952SHerbert Xu break; 232b4219952SHerbert Xu } 233b4219952SHerbert Xu default: 234b4219952SHerbert Xu break; 235b4219952SHerbert Xu } 236b4219952SHerbert Xu 23733c29ddeSChangli Gao out: 238b4219952SHerbert Xu return action; 239b4219952SHerbert Xu 240b4219952SHerbert Xu drop: 241b4219952SHerbert Xu spin_lock(&p->tcf_lock); 242b4219952SHerbert Xu p->tcf_qstats.drops++; 243b4219952SHerbert Xu spin_unlock(&p->tcf_lock); 244b4219952SHerbert Xu return TC_ACT_SHOT; 245b4219952SHerbert Xu } 246b4219952SHerbert Xu 247b4219952SHerbert Xu static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a, 248b4219952SHerbert Xu int bind, int ref) 249b4219952SHerbert Xu { 250b4219952SHerbert Xu unsigned char *b = skb_tail_pointer(skb); 251b4219952SHerbert Xu struct tcf_nat *p = a->priv; 2521c40be12SEric Dumazet struct tc_nat opt = { 2531c40be12SEric Dumazet .old_addr = p->old_addr, 2541c40be12SEric Dumazet .new_addr = p->new_addr, 2551c40be12SEric Dumazet .mask = p->mask, 2561c40be12SEric Dumazet .flags = p->flags, 2571c40be12SEric Dumazet 2581c40be12SEric Dumazet .index = p->tcf_index, 2591c40be12SEric Dumazet .action = p->tcf_action, 2601c40be12SEric Dumazet .refcnt = p->tcf_refcnt - ref, 2611c40be12SEric Dumazet .bindcnt = p->tcf_bindcnt - bind, 2621c40be12SEric Dumazet }; 263b4219952SHerbert Xu struct tcf_t t; 264b4219952SHerbert Xu 2651b34ec43SDavid S. Miller if (nla_put(skb, TCA_NAT_PARMS, sizeof(opt), &opt)) 2661b34ec43SDavid S. Miller goto nla_put_failure; 267b4219952SHerbert Xu t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); 268b4219952SHerbert Xu t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); 269b4219952SHerbert Xu t.expires = jiffies_to_clock_t(p->tcf_tm.expires); 2701b34ec43SDavid S. Miller if (nla_put(skb, TCA_NAT_TM, sizeof(t), &t)) 2711b34ec43SDavid S. Miller goto nla_put_failure; 272b4219952SHerbert Xu 273b4219952SHerbert Xu return skb->len; 274b4219952SHerbert Xu 2757ba699c6SPatrick McHardy nla_put_failure: 276b4219952SHerbert Xu nlmsg_trim(skb, b); 277b4219952SHerbert Xu return -1; 278b4219952SHerbert Xu } 279b4219952SHerbert Xu 280*ddf97ccdSWANG Cong static int tcf_nat_walker(struct net *net, struct sk_buff *skb, 281*ddf97ccdSWANG Cong struct netlink_callback *cb, int type, 282*ddf97ccdSWANG Cong struct tc_action *a) 283*ddf97ccdSWANG Cong { 284*ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, nat_net_id); 285*ddf97ccdSWANG Cong 286*ddf97ccdSWANG Cong return tcf_generic_walker(tn, skb, cb, type, a); 287*ddf97ccdSWANG Cong } 288*ddf97ccdSWANG Cong 289*ddf97ccdSWANG Cong static int tcf_nat_search(struct net *net, struct tc_action *a, u32 index) 290*ddf97ccdSWANG Cong { 291*ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, nat_net_id); 292*ddf97ccdSWANG Cong 293*ddf97ccdSWANG Cong return tcf_hash_search(tn, a, index); 294*ddf97ccdSWANG Cong } 295*ddf97ccdSWANG Cong 296b4219952SHerbert Xu static struct tc_action_ops act_nat_ops = { 297b4219952SHerbert Xu .kind = "nat", 298b4219952SHerbert Xu .type = TCA_ACT_NAT, 299b4219952SHerbert Xu .owner = THIS_MODULE, 300b4219952SHerbert Xu .act = tcf_nat, 301b4219952SHerbert Xu .dump = tcf_nat_dump, 302b4219952SHerbert Xu .init = tcf_nat_init, 303*ddf97ccdSWANG Cong .walk = tcf_nat_walker, 304*ddf97ccdSWANG Cong .lookup = tcf_nat_search, 305*ddf97ccdSWANG Cong }; 306*ddf97ccdSWANG Cong 307*ddf97ccdSWANG Cong static __net_init int nat_init_net(struct net *net) 308*ddf97ccdSWANG Cong { 309*ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, nat_net_id); 310*ddf97ccdSWANG Cong 311*ddf97ccdSWANG Cong return tc_action_net_init(tn, &act_nat_ops, NAT_TAB_MASK); 312*ddf97ccdSWANG Cong } 313*ddf97ccdSWANG Cong 314*ddf97ccdSWANG Cong static void __net_exit nat_exit_net(struct net *net) 315*ddf97ccdSWANG Cong { 316*ddf97ccdSWANG Cong struct tc_action_net *tn = net_generic(net, nat_net_id); 317*ddf97ccdSWANG Cong 318*ddf97ccdSWANG Cong tc_action_net_exit(tn); 319*ddf97ccdSWANG Cong } 320*ddf97ccdSWANG Cong 321*ddf97ccdSWANG Cong static struct pernet_operations nat_net_ops = { 322*ddf97ccdSWANG Cong .init = nat_init_net, 323*ddf97ccdSWANG Cong .exit = nat_exit_net, 324*ddf97ccdSWANG Cong .id = &nat_net_id, 325*ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net), 326b4219952SHerbert Xu }; 327b4219952SHerbert Xu 328b4219952SHerbert Xu MODULE_DESCRIPTION("Stateless NAT actions"); 329b4219952SHerbert Xu MODULE_LICENSE("GPL"); 330b4219952SHerbert Xu 331b4219952SHerbert Xu static int __init nat_init_module(void) 332b4219952SHerbert Xu { 333*ddf97ccdSWANG Cong return tcf_register_action(&act_nat_ops, &nat_net_ops); 334b4219952SHerbert Xu } 335b4219952SHerbert Xu 336b4219952SHerbert Xu static void __exit nat_cleanup_module(void) 337b4219952SHerbert Xu { 338*ddf97ccdSWANG Cong tcf_unregister_action(&act_nat_ops, &nat_net_ops); 339b4219952SHerbert Xu } 340b4219952SHerbert Xu 341b4219952SHerbert Xu module_init(nat_init_module); 342b4219952SHerbert Xu module_exit(nat_cleanup_module); 343