[SK_BUFF]: Introduce ip_hdr(), remove skb->nh.iph
[safe/jmp/linux-2.6] / net / ipv4 / netfilter / ipt_ECN.c
1 /* iptables module for the IPv4 and TCP ECN bits, Version 1.5
2  *
3  * (C) 2002 by Harald Welte <laforge@netfilter.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * ipt_ECN.c,v 1.5 2002/08/18 19:36:51 laforge Exp
10 */
11
12 #include <linux/in.h>
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/ip.h>
16 #include <net/ip.h>
17 #include <linux/tcp.h>
18 #include <net/checksum.h>
19
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter_ipv4/ip_tables.h>
22 #include <linux/netfilter_ipv4/ipt_ECN.h>
23
24 MODULE_LICENSE("GPL");
25 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
26 MODULE_DESCRIPTION("iptables ECN modification module");
27
28 /* set ECT codepoint from IP header.
29  *      return 0 if there was an error. */
30 static inline int
31 set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
32 {
33         struct iphdr *iph = ip_hdr(*pskb);
34
35         if ((iph->tos & IPT_ECN_IP_MASK) != (einfo->ip_ect & IPT_ECN_IP_MASK)) {
36                 __u8 oldtos;
37                 if (!skb_make_writable(pskb, sizeof(struct iphdr)))
38                         return 0;
39                 iph = ip_hdr(*pskb);
40                 oldtos = iph->tos;
41                 iph->tos &= ~IPT_ECN_IP_MASK;
42                 iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK);
43                 nf_csum_replace2(&iph->check, htons(oldtos), htons(iph->tos));
44         }
45         return 1;
46 }
47
48 /* Return 0 if there was an error. */
49 static inline int
50 set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
51 {
52         struct tcphdr _tcph, *tcph;
53         __be16 oldval;
54
55         /* Not enought header? */
56         tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb),
57                                   sizeof(_tcph), &_tcph);
58         if (!tcph)
59                 return 0;
60
61         if ((!(einfo->operation & IPT_ECN_OP_SET_ECE) ||
62              tcph->ece == einfo->proto.tcp.ece) &&
63             ((!(einfo->operation & IPT_ECN_OP_SET_CWR) ||
64              tcph->cwr == einfo->proto.tcp.cwr)))
65                 return 1;
66
67         if (!skb_make_writable(pskb, ip_hdrlen(*pskb) + sizeof(*tcph)))
68                 return 0;
69         tcph = (void *)ip_hdr(*pskb) + ip_hdrlen(*pskb);
70
71         oldval = ((__be16 *)tcph)[6];
72         if (einfo->operation & IPT_ECN_OP_SET_ECE)
73                 tcph->ece = einfo->proto.tcp.ece;
74         if (einfo->operation & IPT_ECN_OP_SET_CWR)
75                 tcph->cwr = einfo->proto.tcp.cwr;
76
77         nf_proto_csum_replace2(&tcph->check, *pskb,
78                                 oldval, ((__be16 *)tcph)[6], 0);
79         return 1;
80 }
81
82 static unsigned int
83 target(struct sk_buff **pskb,
84        const struct net_device *in,
85        const struct net_device *out,
86        unsigned int hooknum,
87        const struct xt_target *target,
88        const void *targinfo)
89 {
90         const struct ipt_ECN_info *einfo = targinfo;
91
92         if (einfo->operation & IPT_ECN_OP_SET_IP)
93                 if (!set_ect_ip(pskb, einfo))
94                         return NF_DROP;
95
96         if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
97             && ip_hdr(*pskb)->protocol == IPPROTO_TCP)
98                 if (!set_ect_tcp(pskb, einfo))
99                         return NF_DROP;
100
101         return XT_CONTINUE;
102 }
103
104 static int
105 checkentry(const char *tablename,
106            const void *e_void,
107            const struct xt_target *target,
108            void *targinfo,
109            unsigned int hook_mask)
110 {
111         const struct ipt_ECN_info *einfo = (struct ipt_ECN_info *)targinfo;
112         const struct ipt_entry *e = e_void;
113
114         if (einfo->operation & IPT_ECN_OP_MASK) {
115                 printk(KERN_WARNING "ECN: unsupported ECN operation %x\n",
116                         einfo->operation);
117                 return 0;
118         }
119         if (einfo->ip_ect & ~IPT_ECN_IP_MASK) {
120                 printk(KERN_WARNING "ECN: new ECT codepoint %x out of mask\n",
121                         einfo->ip_ect);
122                 return 0;
123         }
124         if ((einfo->operation & (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR))
125             && (e->ip.proto != IPPROTO_TCP || (e->ip.invflags & XT_INV_PROTO))) {
126                 printk(KERN_WARNING "ECN: cannot use TCP operations on a "
127                        "non-tcp rule\n");
128                 return 0;
129         }
130         return 1;
131 }
132
133 static struct xt_target ipt_ecn_reg = {
134         .name           = "ECN",
135         .family         = AF_INET,
136         .target         = target,
137         .targetsize     = sizeof(struct ipt_ECN_info),
138         .table          = "mangle",
139         .checkentry     = checkentry,
140         .me             = THIS_MODULE,
141 };
142
143 static int __init ipt_ecn_init(void)
144 {
145         return xt_register_target(&ipt_ecn_reg);
146 }
147
148 static void __exit ipt_ecn_fini(void)
149 {
150         xt_unregister_target(&ipt_ecn_reg);
151 }
152
153 module_init(ipt_ecn_init);
154 module_exit(ipt_ecn_fini);