929b27bdeb79829801b4be02f9371d6538a161e8
[pandora-kernel.git] / net / ipv4 / netfilter.c
1 /* IPv4 specific functions of netfilter core */
2 #include <linux/kernel.h>
3 #include <linux/netfilter.h>
4 #include <linux/netfilter_ipv4.h>
5 #include <linux/ip.h>
6 #include <linux/skbuff.h>
7 #include <linux/gfp.h>
8 #include <net/route.h>
9 #include <net/xfrm.h>
10 #include <net/ip.h>
11 #include <net/netfilter/nf_queue.h>
12
13 /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
14 int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
15 {
16         struct net *net = dev_net(skb_dst(skb)->dev);
17         const struct iphdr *iph = ip_hdr(skb);
18         struct rtable *rt;
19         struct flowi4 fl4 = {};
20         __be32 saddr = iph->saddr;
21         __u8 flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0;
22         unsigned int hh_len;
23
24         if (addr_type == RTN_UNSPEC)
25                 addr_type = inet_addr_type(net, saddr);
26         if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
27                 flags |= FLOWI_FLAG_ANYSRC;
28         else
29                 saddr = 0;
30
31         /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
32          * packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
33          */
34         fl4.daddr = iph->daddr;
35         fl4.saddr = saddr;
36         fl4.flowi4_tos = RT_TOS(iph->tos);
37         fl4.flowi4_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0;
38         fl4.flowi4_mark = skb->mark;
39         fl4.flowi4_flags = flags;
40         rt = ip_route_output_key(net, &fl4);
41         if (IS_ERR(rt))
42                 return -1;
43
44         /* Drop old route. */
45         skb_dst_drop(skb);
46         skb_dst_set(skb, &rt->dst);
47
48         if (skb_dst(skb)->error)
49                 return -1;
50
51 #ifdef CONFIG_XFRM
52         if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
53             xfrm_decode_session(skb, flowi4_to_flowi(&fl4), AF_INET) == 0) {
54                 struct dst_entry *dst = skb_dst(skb);
55                 skb_dst_set(skb, NULL);
56                 dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), skb->sk, 0);
57                 if (IS_ERR(dst))
58                         return -1;
59                 skb_dst_set(skb, dst);
60         }
61 #endif
62
63         /* Change in oif may mean change in hh_len. */
64         hh_len = skb_dst(skb)->dev->hard_header_len;
65         if (skb_headroom(skb) < hh_len &&
66             pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
67                 return -1;
68
69         return 0;
70 }
71 EXPORT_SYMBOL(ip_route_me_harder);
72
73 #ifdef CONFIG_XFRM
74 int ip_xfrm_me_harder(struct sk_buff *skb)
75 {
76         struct flowi fl;
77         unsigned int hh_len;
78         struct dst_entry *dst;
79
80         if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
81                 return 0;
82         if (xfrm_decode_session(skb, &fl, AF_INET) < 0)
83                 return -1;
84
85         dst = skb_dst(skb);
86         if (dst->xfrm)
87                 dst = ((struct xfrm_dst *)dst)->route;
88         dst_hold(dst);
89
90         dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0);
91         if (IS_ERR(dst))
92                 return -1;
93
94         skb_dst_drop(skb);
95         skb_dst_set(skb, dst);
96
97         /* Change in oif may mean change in hh_len. */
98         hh_len = skb_dst(skb)->dev->hard_header_len;
99         if (skb_headroom(skb) < hh_len &&
100             pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
101                 return -1;
102         return 0;
103 }
104 EXPORT_SYMBOL(ip_xfrm_me_harder);
105 #endif
106
107 void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
108 EXPORT_SYMBOL(ip_nat_decode_session);
109
110 /*
111  * Extra routing may needed on local out, as the QUEUE target never
112  * returns control to the table.
113  */
114
115 struct ip_rt_info {
116         __be32 daddr;
117         __be32 saddr;
118         u_int8_t tos;
119         u_int32_t mark;
120 };
121
122 static void nf_ip_saveroute(const struct sk_buff *skb,
123                             struct nf_queue_entry *entry)
124 {
125         struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
126
127         if (entry->hook == NF_INET_LOCAL_OUT) {
128                 const struct iphdr *iph = ip_hdr(skb);
129
130                 rt_info->tos = iph->tos;
131                 rt_info->daddr = iph->daddr;
132                 rt_info->saddr = iph->saddr;
133                 rt_info->mark = skb->mark;
134         }
135 }
136
137 static int nf_ip_reroute(struct sk_buff *skb,
138                          const struct nf_queue_entry *entry)
139 {
140         const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
141
142         if (entry->hook == NF_INET_LOCAL_OUT) {
143                 const struct iphdr *iph = ip_hdr(skb);
144
145                 if (!(iph->tos == rt_info->tos &&
146                       skb->mark == rt_info->mark &&
147                       iph->daddr == rt_info->daddr &&
148                       iph->saddr == rt_info->saddr))
149                         return ip_route_me_harder(skb, RTN_UNSPEC);
150         }
151         return 0;
152 }
153
154 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
155                             unsigned int dataoff, u_int8_t protocol)
156 {
157         const struct iphdr *iph = ip_hdr(skb);
158         __sum16 csum = 0;
159
160         switch (skb->ip_summed) {
161         case CHECKSUM_COMPLETE:
162                 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
163                         break;
164                 if ((protocol == 0 && !csum_fold(skb->csum)) ||
165                     !csum_tcpudp_magic(iph->saddr, iph->daddr,
166                                        skb->len - dataoff, protocol,
167                                        skb->csum)) {
168                         skb->ip_summed = CHECKSUM_UNNECESSARY;
169                         break;
170                 }
171                 /* fall through */
172         case CHECKSUM_NONE:
173                 if (protocol == 0)
174                         skb->csum = 0;
175                 else
176                         skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
177                                                        skb->len - dataoff,
178                                                        protocol, 0);
179                 csum = __skb_checksum_complete(skb);
180         }
181         return csum;
182 }
183 EXPORT_SYMBOL(nf_ip_checksum);
184
185 static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook,
186                                       unsigned int dataoff, unsigned int len,
187                                       u_int8_t protocol)
188 {
189         const struct iphdr *iph = ip_hdr(skb);
190         __sum16 csum = 0;
191
192         switch (skb->ip_summed) {
193         case CHECKSUM_COMPLETE:
194                 if (len == skb->len - dataoff)
195                         return nf_ip_checksum(skb, hook, dataoff, protocol);
196                 /* fall through */
197         case CHECKSUM_NONE:
198                 skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, protocol,
199                                                skb->len - dataoff, 0);
200                 skb->ip_summed = CHECKSUM_NONE;
201                 return __skb_checksum_complete_head(skb, dataoff + len);
202         }
203         return csum;
204 }
205
206 static int nf_ip_route(struct net *net, struct dst_entry **dst,
207                        struct flowi *fl, bool strict __always_unused)
208 {
209         struct rtable *rt = ip_route_output_key(net, &fl->u.ip4);
210         if (IS_ERR(rt))
211                 return PTR_ERR(rt);
212         *dst = &rt->dst;
213         return 0;
214 }
215
216 static const struct nf_afinfo nf_ip_afinfo = {
217         .family                 = AF_INET,
218         .checksum               = nf_ip_checksum,
219         .checksum_partial       = nf_ip_checksum_partial,
220         .route                  = nf_ip_route,
221         .saveroute              = nf_ip_saveroute,
222         .reroute                = nf_ip_reroute,
223         .route_key_size         = sizeof(struct ip_rt_info),
224 };
225
226 static int ipv4_netfilter_init(void)
227 {
228         return nf_register_afinfo(&nf_ip_afinfo);
229 }
230
231 static void ipv4_netfilter_fini(void)
232 {
233         nf_unregister_afinfo(&nf_ip_afinfo);
234 }
235
236 module_init(ipv4_netfilter_init);
237 module_exit(ipv4_netfilter_fini);
238
239 #ifdef CONFIG_SYSCTL
240 struct ctl_path nf_net_ipv4_netfilter_sysctl_path[] = {
241         { .procname = "net", },
242         { .procname = "ipv4", },
243         { .procname = "netfilter", },
244         { }
245 };
246 EXPORT_SYMBOL_GPL(nf_net_ipv4_netfilter_sysctl_path);
247 #endif /* CONFIG_SYSCTL */