133eb2f0741613858a0929d32455ac0e13a5366d
[linux-2.6.git] / net / netfilter / xt_MARK.c
1 /* This is a module which is used for setting the NFMARK field of an skb. */
2
3 /* (C) 1999-2001 Marc Boucher <marc@mbsi.ca>
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  */
10
11 #include <linux/module.h>
12 #include <linux/version.h>
13 #include <linux/skbuff.h>
14 #include <linux/ip.h>
15 #include <net/checksum.h>
16 #include <net/route.h>
17 #include <net/inet_hashtables.h>
18
19 #include <linux/netfilter_ipv4/ip_conntrack.h>
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter/xt_MARK.h>
22
23 MODULE_LICENSE("GPL");
24 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
25 MODULE_DESCRIPTION("ip[6]tables MARK modification module");
26 MODULE_ALIAS("ipt_MARK");
27 MODULE_ALIAS("ip6t_MARK");
28
29 static inline u_int16_t
30 get_dst_port(struct ip_conntrack_tuple *tuple)
31 {
32         switch (tuple->dst.protonum) {
33         case IPPROTO_GRE:
34                 /* XXX Truncate 32-bit GRE key to 16 bits */
35 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
36                 return tuple->dst.u.gre.key;
37 #else
38                 return htons(ntohl(tuple->dst.u.gre.key));
39 #endif  
40         case IPPROTO_ICMP:
41                 /* Bind on ICMP echo ID */
42                 return tuple->src.u.icmp.id;
43         case IPPROTO_TCP:
44                 return tuple->dst.u.tcp.port;
45         case IPPROTO_UDP:
46                 return tuple->dst.u.udp.port;
47         default:
48                 return tuple->dst.u.all;
49         }
50 }
51
52 static inline u_int16_t
53 get_src_port(struct ip_conntrack_tuple *tuple)
54 {
55         switch (tuple->dst.protonum) {
56         case IPPROTO_GRE:
57                 /* XXX Truncate 32-bit GRE key to 16 bits */
58                 return htons(ntohl(tuple->src.u.gre.key));
59         case IPPROTO_ICMP:
60                 /* Bind on ICMP echo ID */
61                 return tuple->src.u.icmp.id;
62         case IPPROTO_TCP:
63                 return tuple->src.u.tcp.port;
64         case IPPROTO_UDP:
65                 return tuple->src.u.udp.port;
66         default:
67                 return tuple->src.u.all;
68         }
69 }
70
71 static unsigned int
72 target_v0(struct sk_buff **pskb,
73           const struct net_device *in,
74           const struct net_device *out,
75           unsigned int hooknum,
76           const struct xt_target *target,
77           const void *targinfo)
78 {
79         const struct xt_mark_target_info *markinfo = targinfo;
80
81         if((*pskb)->mark != markinfo->mark)
82                 (*pskb)->mark = markinfo->mark;
83
84         return XT_CONTINUE;
85 }
86
87 static unsigned int
88 target_v1(struct sk_buff **pskb,
89           const struct net_device *in,
90           const struct net_device *out,
91           unsigned int hooknum,
92           const struct xt_target *target,
93           const void *targinfo)
94 {
95         const struct xt_mark_target_info_v1 *markinfo = targinfo;
96         int mark = -1;
97
98         switch (markinfo->mode) {
99         case XT_MARK_SET:
100                 mark = markinfo->mark;
101                 break;
102                 
103         case XT_MARK_AND:
104                 mark = (*pskb)->mark & markinfo->mark;
105                 break;
106                 
107         case XT_MARK_OR:
108                 mark = (*pskb)->mark | markinfo->mark;
109                 break;
110
111         case XT_MARK_COPYXID: 
112                 {
113                 enum ip_conntrack_info ctinfo;
114                 struct sock *connection_sk;
115                 int dif;
116
117                 struct ip_conntrack *ct = ip_conntrack_get((*pskb), &ctinfo);
118                 extern struct inet_hashinfo tcp_hashinfo;
119                 enum ip_conntrack_dir dir;
120
121                 if (!ct) 
122                         break;
123                 dir= CTINFO2DIR(ctinfo);
124                 u_int32_t src_ip = ct->tuplehash[dir].tuple.src.ip;
125                 u_int16_t src_port = get_src_port(&ct->tuplehash[dir].tuple);
126
127                 u_int32_t ip;
128                 u_int16_t port;
129
130                 dif = ((struct rtable *)(*pskb)->dst)->rt_iif;
131                 ip = ct->tuplehash[dir].tuple.dst.ip;
132                 port = get_dst_port(&ct->tuplehash[dir].tuple);
133
134                 if ((*pskb)->sk) 
135                         connection_sk = (*pskb)->sk;
136                 else {
137                         connection_sk = inet_lookup(&tcp_hashinfo, src_ip, src_port, ip, port, dif);
138                 }
139
140                 if (connection_sk) {
141                         /* XXX:
142                         connection_sk->sk_peercred.gid = connection_sk->sk_peercred.uid = ct->xid[dir];
143                         ct->xid[!dir]=connection_sk->sk_xid;
144                         */
145                         connection_sk->sk_peercred.gid = connection_sk->sk_peercred.uid = connection_sk->sk_xid;
146                         if (connection_sk->sk_xid != 0) 
147                                 mark = connection_sk->sk_xid;
148                         if (connection_sk != (*pskb)->sk)
149                                 sock_put(connection_sk);
150                 }
151                 }
152                 break;
153         }
154
155         if((*pskb)->mark != mark && mark != -1)
156                 (*pskb)->mark = mark;
157
158         return XT_CONTINUE;
159 }
160
161
162 static int
163 checkentry_v0(const char *tablename,
164               const void *entry,
165               const struct xt_target *target,
166               void *targinfo,
167               unsigned int hook_mask)
168 {
169         struct xt_mark_target_info *markinfo = targinfo;
170
171         if (markinfo->mark > 0xffffffff) {
172                 printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
173                 return 0;
174         }
175         return 1;
176 }
177
178 static int
179 checkentry_v1(const char *tablename,
180               const void *entry,
181               const struct xt_target *target,
182               void *targinfo,
183               unsigned int hook_mask)
184 {
185         struct xt_mark_target_info_v1 *markinfo = targinfo;
186
187         if (markinfo->mode != XT_MARK_SET
188             && markinfo->mode != XT_MARK_AND
189             && markinfo->mode != XT_MARK_OR
190             && markinfo->mode != XT_MARK_COPYXID) {
191                 printk(KERN_WARNING "MARK: unknown mode %u\n",
192                        markinfo->mode);
193                 return 0;
194         }
195         if (markinfo->mark > 0xffffffff) {
196                 printk(KERN_WARNING "MARK: Only supports 32bit wide mark\n");
197                 return 0;
198         }
199         return 1;
200 }
201
202 #ifdef CONFIG_COMPAT
203 struct compat_xt_mark_target_info_v1 {
204         compat_ulong_t  mark;
205         u_int8_t        mode;
206         u_int8_t        __pad1;
207         u_int16_t       __pad2;
208 };
209
210 static void compat_from_user_v1(void *dst, void *src)
211 {
212         struct compat_xt_mark_target_info_v1 *cm = src;
213         struct xt_mark_target_info_v1 m = {
214                 .mark   = cm->mark,
215                 .mode   = cm->mode,
216         };
217         memcpy(dst, &m, sizeof(m));
218 }
219
220 static int compat_to_user_v1(void __user *dst, void *src)
221 {
222         struct xt_mark_target_info_v1 *m = src;
223         struct compat_xt_mark_target_info_v1 cm = {
224                 .mark   = m->mark,
225                 .mode   = m->mode,
226         };
227         return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
228 }
229 #endif /* CONFIG_COMPAT */
230
231 static struct xt_target xt_mark_target[] = {
232         {
233                 .name           = "MARK",
234                 .family         = AF_INET,
235                 .revision       = 0,
236                 .checkentry     = checkentry_v0,
237                 .target         = target_v0,
238                 .targetsize     = sizeof(struct xt_mark_target_info),
239                 .table          = "mangle",
240                 .me             = THIS_MODULE,
241         },
242         {
243                 .name           = "MARK",
244                 .family         = AF_INET,
245                 .revision       = 1,
246                 .checkentry     = checkentry_v1,
247                 .target         = target_v1,
248                 .targetsize     = sizeof(struct xt_mark_target_info_v1),
249 #ifdef CONFIG_COMPAT
250                 .compatsize     = sizeof(struct compat_xt_mark_target_info_v1),
251                 .compat_from_user = compat_from_user_v1,
252                 .compat_to_user = compat_to_user_v1,
253 #endif
254                 .table          = "mangle",
255                 .me             = THIS_MODULE,
256         },
257         {
258                 .name           = "MARK",
259                 .family         = AF_INET6,
260                 .revision       = 0,
261                 .checkentry     = checkentry_v0,
262                 .target         = target_v0,
263                 .targetsize     = sizeof(struct xt_mark_target_info),
264                 .table          = "mangle",
265                 .me             = THIS_MODULE,
266         },
267 };
268
269 static int __init xt_mark_init(void)
270 {
271         return xt_register_targets(xt_mark_target, ARRAY_SIZE(xt_mark_target));
272 }
273
274 static void __exit xt_mark_fini(void)
275 {
276         xt_unregister_targets(xt_mark_target, ARRAY_SIZE(xt_mark_target));
277 }
278
279 module_init(xt_mark_init);
280 module_exit(xt_mark_fini);