Merge branch 'x86-fpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / net / netfilter / xt_HMARK.c
1 /*
2  * xt_HMARK - Netfilter module to set mark by means of hashing
3  *
4  * (C) 2012 by Hans Schillstrom <hans.schillstrom@ericsson.com>
5  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  */
11
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 #include <linux/icmp.h>
15
16 #include <linux/netfilter/x_tables.h>
17 #include <linux/netfilter/xt_HMARK.h>
18
19 #include <net/ip.h>
20 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
21 #include <net/netfilter/nf_conntrack.h>
22 #endif
23 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
24 #include <net/ipv6.h>
25 #include <linux/netfilter_ipv6/ip6_tables.h>
26 #endif
27
28 MODULE_LICENSE("GPL");
29 MODULE_AUTHOR("Hans Schillstrom <hans.schillstrom@ericsson.com>");
30 MODULE_DESCRIPTION("Xtables: packet marking using hash calculation");
31 MODULE_ALIAS("ipt_HMARK");
32 MODULE_ALIAS("ip6t_HMARK");
33
34 struct hmark_tuple {
35         u32                     src;
36         u32                     dst;
37         union hmark_ports       uports;
38         uint8_t                 proto;
39 };
40
41 static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask)
42 {
43         return (addr32[0] & mask[0]) ^
44                (addr32[1] & mask[1]) ^
45                (addr32[2] & mask[2]) ^
46                (addr32[3] & mask[3]);
47 }
48
49 static inline u32
50 hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask)
51 {
52         switch (l3num) {
53         case AF_INET:
54                 return *addr32 & *mask;
55         case AF_INET6:
56                 return hmark_addr6_mask(addr32, mask);
57         }
58         return 0;
59 }
60
61 static int
62 hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
63                     const struct xt_hmark_info *info)
64 {
65 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
66         enum ip_conntrack_info ctinfo;
67         struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
68         struct nf_conntrack_tuple *otuple;
69         struct nf_conntrack_tuple *rtuple;
70
71         if (ct == NULL || nf_ct_is_untracked(ct))
72                 return -1;
73
74         otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
75         rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
76
77         t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all,
78                                  info->src_mask.all);
79         t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all,
80                                  info->dst_mask.all);
81
82         if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
83                 return 0;
84
85         t->proto = nf_ct_protonum(ct);
86         if (t->proto != IPPROTO_ICMP) {
87                 t->uports.p16.src = otuple->src.u.all;
88                 t->uports.p16.dst = rtuple->src.u.all;
89                 t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
90                                 info->port_set.v32;
91                 if (t->uports.p16.dst < t->uports.p16.src)
92                         swap(t->uports.p16.dst, t->uports.p16.src);
93         }
94
95         return 0;
96 #else
97         return -1;
98 #endif
99 }
100
101 static inline u32
102 hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)
103 {
104         u32 hash;
105
106         if (t->dst < t->src)
107                 swap(t->src, t->dst);
108
109         hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd);
110         hash = hash ^ (t->proto & info->proto_mask);
111
112         return (((u64)hash * info->hmodulus) >> 32) + info->hoffset;
113 }
114
115 static void
116 hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,
117                       struct hmark_tuple *t, const struct xt_hmark_info *info)
118 {
119         int protoff;
120
121         protoff = proto_ports_offset(t->proto);
122         if (protoff < 0)
123                 return;
124
125         nhoff += protoff;
126         if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)
127                 return;
128
129         t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
130                         info->port_set.v32;
131
132         if (t->uports.p16.dst < t->uports.p16.src)
133                 swap(t->uports.p16.dst, t->uports.p16.src);
134 }
135
136 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
137 static int get_inner6_hdr(const struct sk_buff *skb, int *offset)
138 {
139         struct icmp6hdr *icmp6h, _ih6;
140
141         icmp6h = skb_header_pointer(skb, *offset, sizeof(_ih6), &_ih6);
142         if (icmp6h == NULL)
143                 return 0;
144
145         if (icmp6h->icmp6_type && icmp6h->icmp6_type < 128) {
146                 *offset += sizeof(struct icmp6hdr);
147                 return 1;
148         }
149         return 0;
150 }
151
152 static int
153 hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
154                           const struct xt_hmark_info *info)
155 {
156         struct ipv6hdr *ip6, _ip6;
157         int flag = IP6T_FH_F_AUTH;
158         unsigned int nhoff = 0;
159         u16 fragoff = 0;
160         int nexthdr;
161
162         ip6 = (struct ipv6hdr *) (skb->data + skb_network_offset(skb));
163         nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
164         if (nexthdr < 0)
165                 return 0;
166         /* No need to check for icmp errors on fragments */
167         if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
168                 goto noicmp;
169         /* Use inner header in case of ICMP errors */
170         if (get_inner6_hdr(skb, &nhoff)) {
171                 ip6 = skb_header_pointer(skb, nhoff, sizeof(_ip6), &_ip6);
172                 if (ip6 == NULL)
173                         return -1;
174                 /* If AH present, use SPI like in ESP. */
175                 flag = IP6T_FH_F_AUTH;
176                 nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
177                 if (nexthdr < 0)
178                         return -1;
179         }
180 noicmp:
181         t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.all);
182         t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.all);
183
184         if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
185                 return 0;
186
187         t->proto = nexthdr;
188         if (t->proto == IPPROTO_ICMPV6)
189                 return 0;
190
191         if (flag & IP6T_FH_F_FRAG)
192                 return 0;
193
194         hmark_set_tuple_ports(skb, nhoff, t, info);
195         return 0;
196 }
197
198 static unsigned int
199 hmark_tg_v6(struct sk_buff *skb, const struct xt_action_param *par)
200 {
201         const struct xt_hmark_info *info = par->targinfo;
202         struct hmark_tuple t;
203
204         memset(&t, 0, sizeof(struct hmark_tuple));
205
206         if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
207                 if (hmark_ct_set_htuple(skb, &t, info) < 0)
208                         return XT_CONTINUE;
209         } else {
210                 if (hmark_pkt_set_htuple_ipv6(skb, &t, info) < 0)
211                         return XT_CONTINUE;
212         }
213
214         skb->mark = hmark_hash(&t, info);
215         return XT_CONTINUE;
216 }
217 #endif
218
219 static int get_inner_hdr(const struct sk_buff *skb, int iphsz, int *nhoff)
220 {
221         const struct icmphdr *icmph;
222         struct icmphdr _ih;
223
224         /* Not enough header? */
225         icmph = skb_header_pointer(skb, *nhoff + iphsz, sizeof(_ih), &_ih);
226         if (icmph == NULL || icmph->type > NR_ICMP_TYPES)
227                 return 0;
228
229         /* Error message? */
230         if (icmph->type != ICMP_DEST_UNREACH &&
231             icmph->type != ICMP_SOURCE_QUENCH &&
232             icmph->type != ICMP_TIME_EXCEEDED &&
233             icmph->type != ICMP_PARAMETERPROB &&
234             icmph->type != ICMP_REDIRECT)
235                 return 0;
236
237         *nhoff += iphsz + sizeof(_ih);
238         return 1;
239 }
240
241 static int
242 hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,
243                           const struct xt_hmark_info *info)
244 {
245         struct iphdr *ip, _ip;
246         int nhoff = skb_network_offset(skb);
247
248         ip = (struct iphdr *) (skb->data + nhoff);
249         if (ip->protocol == IPPROTO_ICMP) {
250                 /* Use inner header in case of ICMP errors */
251                 if (get_inner_hdr(skb, ip->ihl * 4, &nhoff)) {
252                         ip = skb_header_pointer(skb, nhoff, sizeof(_ip), &_ip);
253                         if (ip == NULL)
254                                 return -1;
255                 }
256         }
257
258         t->src = (__force u32) ip->saddr;
259         t->dst = (__force u32) ip->daddr;
260
261         t->src &= info->src_mask.ip;
262         t->dst &= info->dst_mask.ip;
263
264         if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
265                 return 0;
266
267         t->proto = ip->protocol;
268
269         /* ICMP has no ports, skip */
270         if (t->proto == IPPROTO_ICMP)
271                 return 0;
272
273         /* follow-up fragments don't contain ports, skip all fragments */
274         if (ip->frag_off & htons(IP_MF | IP_OFFSET))
275                 return 0;
276
277         hmark_set_tuple_ports(skb, (ip->ihl * 4) + nhoff, t, info);
278
279         return 0;
280 }
281
282 static unsigned int
283 hmark_tg_v4(struct sk_buff *skb, const struct xt_action_param *par)
284 {
285         const struct xt_hmark_info *info = par->targinfo;
286         struct hmark_tuple t;
287
288         memset(&t, 0, sizeof(struct hmark_tuple));
289
290         if (info->flags & XT_HMARK_FLAG(XT_HMARK_CT)) {
291                 if (hmark_ct_set_htuple(skb, &t, info) < 0)
292                         return XT_CONTINUE;
293         } else {
294                 if (hmark_pkt_set_htuple_ipv4(skb, &t, info) < 0)
295                         return XT_CONTINUE;
296         }
297
298         skb->mark = hmark_hash(&t, info);
299         return XT_CONTINUE;
300 }
301
302 static int hmark_tg_check(const struct xt_tgchk_param *par)
303 {
304         const struct xt_hmark_info *info = par->targinfo;
305
306         if (!info->hmodulus) {
307                 pr_info("xt_HMARK: hash modulus can't be zero\n");
308                 return -EINVAL;
309         }
310         if (info->proto_mask &&
311             (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))) {
312                 pr_info("xt_HMARK: proto mask must be zero with L3 mode\n");
313                 return -EINVAL;
314         }
315         if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI_MASK) &&
316             (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT_MASK) |
317                              XT_HMARK_FLAG(XT_HMARK_DPORT_MASK)))) {
318                 pr_info("xt_HMARK: spi-mask and port-mask can't be combined\n");
319                 return -EINVAL;
320         }
321         if (info->flags & XT_HMARK_FLAG(XT_HMARK_SPI) &&
322             (info->flags & (XT_HMARK_FLAG(XT_HMARK_SPORT) |
323                              XT_HMARK_FLAG(XT_HMARK_DPORT)))) {
324                 pr_info("xt_HMARK: spi-set and port-set can't be combined\n");
325                 return -EINVAL;
326         }
327         return 0;
328 }
329
330 static struct xt_target hmark_tg_reg[] __read_mostly = {
331         {
332                 .name           = "HMARK",
333                 .family         = NFPROTO_IPV4,
334                 .target         = hmark_tg_v4,
335                 .targetsize     = sizeof(struct xt_hmark_info),
336                 .checkentry     = hmark_tg_check,
337                 .me             = THIS_MODULE,
338         },
339 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
340         {
341                 .name           = "HMARK",
342                 .family         = NFPROTO_IPV6,
343                 .target         = hmark_tg_v6,
344                 .targetsize     = sizeof(struct xt_hmark_info),
345                 .checkentry     = hmark_tg_check,
346                 .me             = THIS_MODULE,
347         },
348 #endif
349 };
350
351 static int __init hmark_tg_init(void)
352 {
353         return xt_register_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
354 }
355
356 static void __exit hmark_tg_exit(void)
357 {
358         xt_unregister_targets(hmark_tg_reg, ARRAY_SIZE(hmark_tg_reg));
359 }
360
361 module_init(hmark_tg_init);
362 module_exit(hmark_tg_exit);