blob: aebdb337fd7ed2f3ad9ba5b8d30e6d59ff3aa8fc [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2004 Netfilter Core Team <coreteam@netfilter.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <linux/module.h>
12#include <linux/netfilter_ipv4/ip_tables.h>
13#include <linux/netdevice.h>
14#include <linux/skbuff.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <net/sock.h>
17#include <net/route.h>
18#include <linux/ip.h>
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -030019#include <net/ip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21MODULE_LICENSE("GPL");
22MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
23MODULE_DESCRIPTION("iptables mangle table");
24
Patrick McHardy6e23ae22007-11-19 18:53:30 -080025#define MANGLE_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
26 (1 << NF_INET_LOCAL_IN) | \
27 (1 << NF_INET_FORWARD) | \
28 (1 << NF_INET_LOCAL_OUT) | \
29 (1 << NF_INET_POST_ROUTING))
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Florian Westphalb9e69e12016-02-25 10:08:36 +010031static int __net_init iptable_mangle_table_init(struct net *net);
32
Jan Engelhardt35aad0f2009-08-24 14:56:30 +020033static const struct xt_table packet_mangler = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 .name = "mangle",
35 .valid_hooks = MANGLE_VALID_HOOKS,
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 .me = THIS_MODULE,
Jan Engelhardtf88e6a82009-06-13 06:25:44 +020037 .af = NFPROTO_IPV4,
Jan Engelhardt2b95efe2009-06-17 13:57:48 +020038 .priority = NF_IP_PRI_MANGLE,
Florian Westphalb9e69e12016-02-25 10:08:36 +010039 .table_init = iptable_mangle_table_init,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040};
41
Linus Torvalds1da177e2005-04-16 15:20:36 -070042static unsigned int
David S. Miller1c491ba2015-04-03 20:56:08 -040043ipt_mangle_out(struct sk_buff *skb, const struct nf_hook_state *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
45 unsigned int ret;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -070046 const struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 u_int8_t tos;
Al Viro6a19d612006-09-28 14:22:24 -070048 __be32 saddr, daddr;
Thomas Graf82e91ff2006-11-09 15:19:14 -080049 u_int32_t mark;
Patrick McHardyc9e16732013-04-05 06:41:10 +000050 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 /* root is playing with raw sockets. */
Joe Perches3666ed12009-11-23 23:17:06 +010053 if (skb->len < sizeof(struct iphdr) ||
54 ip_hdrlen(skb) < sizeof(struct iphdr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 return NF_ACCEPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57 /* Save things which could affect route */
Herbert Xu3db05fe2007-10-15 00:53:15 -070058 mark = skb->mark;
59 iph = ip_hdr(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -070060 saddr = iph->saddr;
61 daddr = iph->daddr;
62 tos = iph->tos;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Eric W. Biederman6cb8ff3f12015-09-18 14:32:55 -050064 ret = ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 /* Reroute for ANY change. */
Florian Westphal28a51ba2011-01-20 10:23:26 +010066 if (ret != NF_DROP && ret != NF_STOLEN) {
Herbert Xu3db05fe2007-10-15 00:53:15 -070067 iph = ip_hdr(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -070068
69 if (iph->saddr != saddr ||
70 iph->daddr != daddr ||
Herbert Xu3db05fe2007-10-15 00:53:15 -070071 skb->mark != mark ||
Patrick McHardyc9e16732013-04-05 06:41:10 +000072 iph->tos != tos) {
Eric W. Biedermane45f5062015-09-25 15:07:30 -050073 err = ip_route_me_harder(state->net, skb, RTN_UNSPEC);
Patrick McHardyc9e16732013-04-05 06:41:10 +000074 if (err < 0)
75 ret = NF_DROP_ERR(err);
76 }
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -070077 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
79 return ret;
80}
81
Jan Engelhardt737535c2009-06-13 06:46:36 +020082/* The work comes in here from netfilter.c. */
83static unsigned int
Eric W. Biederman06198b32015-09-18 14:33:06 -050084iptable_mangle_hook(void *priv,
Jan Engelhardt737535c2009-06-13 06:46:36 +020085 struct sk_buff *skb,
David S. Miller238e54c2015-04-03 20:32:56 -040086 const struct nf_hook_state *state)
Jan Engelhardt737535c2009-06-13 06:46:36 +020087{
Eric W. Biederman6cb8ff3f12015-09-18 14:32:55 -050088 if (state->hook == NF_INET_LOCAL_OUT)
David S. Miller1c491ba2015-04-03 20:56:08 -040089 return ipt_mangle_out(skb, state);
Eric W. Biederman6cb8ff3f12015-09-18 14:32:55 -050090 return ipt_do_table(skb, state, state->net->ipv4.iptable_mangle);
Jan Engelhardt737535c2009-06-13 06:46:36 +020091}
92
Jan Engelhardt2b95efe2009-06-17 13:57:48 +020093static struct nf_hook_ops *mangle_ops __read_mostly;
Florian Westphalb9e69e12016-02-25 10:08:36 +010094static int __net_init iptable_mangle_table_init(struct net *net)
Alexey Dobriyan9335f042008-01-31 04:03:23 -080095{
Jan Engelhardte3eaa992009-06-17 22:14:54 +020096 struct ipt_replace *repl;
Florian Westphala67dd262016-02-25 10:08:35 +010097 int ret;
Jan Engelhardte3eaa992009-06-17 22:14:54 +020098
Florian Westphalb9e69e12016-02-25 10:08:36 +010099 if (net->ipv4.iptable_mangle)
100 return 0;
101
Jan Engelhardte3eaa992009-06-17 22:14:54 +0200102 repl = ipt_alloc_initial_table(&packet_mangler);
103 if (repl == NULL)
104 return -ENOMEM;
Florian Westphala67dd262016-02-25 10:08:35 +0100105 ret = ipt_register_table(net, &packet_mangler, repl, mangle_ops,
106 &net->ipv4.iptable_mangle);
Jan Engelhardte3eaa992009-06-17 22:14:54 +0200107 kfree(repl);
Florian Westphala67dd262016-02-25 10:08:35 +0100108 return ret;
Alexey Dobriyan9335f042008-01-31 04:03:23 -0800109}
110
111static void __net_exit iptable_mangle_net_exit(struct net *net)
112{
Florian Westphalb9e69e12016-02-25 10:08:36 +0100113 if (!net->ipv4.iptable_mangle)
114 return;
Florian Westphala67dd262016-02-25 10:08:35 +0100115 ipt_unregister_table(net, net->ipv4.iptable_mangle, mangle_ops);
Florian Westphalb9e69e12016-02-25 10:08:36 +0100116 net->ipv4.iptable_mangle = NULL;
Alexey Dobriyan9335f042008-01-31 04:03:23 -0800117}
118
119static struct pernet_operations iptable_mangle_net_ops = {
Alexey Dobriyan9335f042008-01-31 04:03:23 -0800120 .exit = iptable_mangle_net_exit,
121};
122
Andrew Morton65b4b4e2006-03-28 16:37:06 -0800123static int __init iptable_mangle_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124{
125 int ret;
126
Florian Westphalb9e69e12016-02-25 10:08:36 +0100127 mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook);
Jan Engelhardt2b95efe2009-06-17 13:57:48 +0200128 if (IS_ERR(mangle_ops)) {
129 ret = PTR_ERR(mangle_ops);
Florian Westphalb9e69e12016-02-25 10:08:36 +0100130 return ret;
131 }
132
133 ret = register_pernet_subsys(&iptable_mangle_net_ops);
134 if (ret < 0) {
135 kfree(mangle_ops);
136 return ret;
137 }
138
139 ret = iptable_mangle_table_init(&init_net);
140 if (ret) {
Jean Sacren90efbed2012-08-19 15:11:32 +0000141 unregister_pernet_subsys(&iptable_mangle_net_ops);
Florian Westphalb9e69e12016-02-25 10:08:36 +0100142 kfree(mangle_ops);
Jan Engelhardt2b95efe2009-06-17 13:57:48 +0200143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
Andrew Morton65b4b4e2006-03-28 16:37:06 -0800148static void __exit iptable_mangle_fini(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149{
Alexey Dobriyan9335f042008-01-31 04:03:23 -0800150 unregister_pernet_subsys(&iptable_mangle_net_ops);
Florian Westphalb9e69e12016-02-25 10:08:36 +0100151 kfree(mangle_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152}
153
Andrew Morton65b4b4e2006-03-28 16:37:06 -0800154module_init(iptable_mangle_init);
155module_exit(iptable_mangle_fini);