~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to net/ipv4/netfilter.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

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