[NETFILTER]: Introduce NF_INET_ hook values
[safe/jmp/linux-2.6] / 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 <net/route.h>
8 #include <net/xfrm.h>
9 #include <net/ip.h>
10
11 /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
12 int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type)
13 {
14         const struct iphdr *iph = ip_hdr(skb);
15         struct rtable *rt;
16         struct flowi fl = {};
17         struct dst_entry *odst;
18         unsigned int hh_len;
19         unsigned int type;
20
21         type = inet_addr_type(iph->saddr);
22         if (addr_type == RTN_UNSPEC)
23                 addr_type = type;
24
25         /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause
26          * packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
27          */
28         if (addr_type == RTN_LOCAL) {
29                 fl.nl_u.ip4_u.daddr = iph->daddr;
30                 if (type == RTN_LOCAL)
31                         fl.nl_u.ip4_u.saddr = iph->saddr;
32                 fl.nl_u.ip4_u.tos = RT_TOS(iph->tos);
33                 fl.oif = skb->sk ? skb->sk->sk_bound_dev_if : 0;
34                 fl.mark = skb->mark;
35                 if (ip_route_output_key(&rt, &fl) != 0)
36                         return -1;
37
38                 /* Drop old route. */
39                 dst_release(skb->dst);
40                 skb->dst = &rt->u.dst;
41         } else {
42                 /* non-local src, find valid iif to satisfy
43                  * rp-filter when calling ip_route_input. */
44                 fl.nl_u.ip4_u.daddr = iph->saddr;
45                 if (ip_route_output_key(&rt, &fl) != 0)
46                         return -1;
47
48                 odst = skb->dst;
49                 if (ip_route_input(skb, iph->daddr, iph->saddr,
50                                    RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
51                         dst_release(&rt->u.dst);
52                         return -1;
53                 }
54                 dst_release(&rt->u.dst);
55                 dst_release(odst);
56         }
57
58         if (skb->dst->error)
59                 return -1;
60
61 #ifdef CONFIG_XFRM
62         if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
63             xfrm_decode_session(skb, &fl, AF_INET) == 0)
64                 if (xfrm_lookup(&skb->dst, &fl, skb->sk, 0))
65                         return -1;
66 #endif
67
68         /* Change in oif may mean change in hh_len. */
69         hh_len = skb->dst->dev->hard_header_len;
70         if (skb_headroom(skb) < hh_len &&
71             pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
72                 return -1;
73
74         return 0;
75 }
76 EXPORT_SYMBOL(ip_route_me_harder);
77
78 #ifdef CONFIG_XFRM
79 int ip_xfrm_me_harder(struct sk_buff *skb)
80 {
81         struct flowi fl;
82         unsigned int hh_len;
83         struct dst_entry *dst;
84
85         if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
86                 return 0;
87         if (xfrm_decode_session(skb, &fl, AF_INET) < 0)
88                 return -1;
89
90         dst = skb->dst;
91         if (dst->xfrm)
92                 dst = ((struct xfrm_dst *)dst)->route;
93         dst_hold(dst);
94
95         if (xfrm_lookup(&dst, &fl, skb->sk, 0) < 0)
96                 return -1;
97
98         dst_release(skb->dst);
99         skb->dst = dst;
100
101         /* Change in oif may mean change in hh_len. */
102         hh_len = skb->dst->dev->hard_header_len;
103         if (skb_headroom(skb) < hh_len &&
104             pskb_expand_head(skb, hh_len - skb_headroom(skb), 0, GFP_ATOMIC))
105                 return -1;
106         return 0;
107 }
108 EXPORT_SYMBOL(ip_xfrm_me_harder);
109 #endif
110
111 void (*ip_nat_decode_session)(struct sk_buff *, struct flowi *);
112 EXPORT_SYMBOL(ip_nat_decode_session);
113
114 /*
115  * Extra routing may needed on local out, as the QUEUE target never
116  * returns control to the table.
117  */
118
119 struct ip_rt_info {
120         __be32 daddr;
121         __be32 saddr;
122         u_int8_t tos;
123 };
124
125 static void nf_ip_saveroute(const struct sk_buff *skb, struct nf_info *info)
126 {
127         struct ip_rt_info *rt_info = nf_info_reroute(info);
128
129         if (info->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         }
136 }
137
138 static int nf_ip_reroute(struct sk_buff *skb, const struct nf_info *info)
139 {
140         const struct ip_rt_info *rt_info = nf_info_reroute(info);
141
142         if (info->hook == NF_INET_LOCAL_OUT) {
143                 const struct iphdr *iph = ip_hdr(skb);
144
145                 if (!(iph->tos == rt_info->tos
146                       && iph->daddr == rt_info->daddr
147                       && iph->saddr == rt_info->saddr))
148                         return ip_route_me_harder(skb, RTN_UNSPEC);
149         }
150         return 0;
151 }
152
153 __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook,
154                             unsigned int dataoff, u_int8_t protocol)
155 {
156         const struct iphdr *iph = ip_hdr(skb);
157         __sum16 csum = 0;
158
159         switch (skb->ip_summed) {
160         case CHECKSUM_COMPLETE:
161                 if (hook != NF_INET_PRE_ROUTING && hook != NF_INET_LOCAL_IN)
162                         break;
163                 if ((protocol == 0 && !csum_fold(skb->csum)) ||
164                     !csum_tcpudp_magic(iph->saddr, iph->daddr,
165                                        skb->len - dataoff, protocol,
166                                        skb->csum)) {
167                         skb->ip_summed = CHECKSUM_UNNECESSARY;
168                         break;
169                 }
170                 /* fall through */
171         case CHECKSUM_NONE:
172                 if (protocol == 0)
173                         skb->csum = 0;
174                 else
175                         skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr,
176                                                        skb->len - dataoff,
177                                                        protocol, 0);
178                 csum = __skb_checksum_complete(skb);
179         }
180         return csum;
181 }
182
183 EXPORT_SYMBOL(nf_ip_checksum);
184
185 static struct nf_afinfo nf_ip_afinfo = {
186         .family         = AF_INET,
187         .checksum       = nf_ip_checksum,
188         .saveroute      = nf_ip_saveroute,
189         .reroute        = nf_ip_reroute,
190         .route_key_size = sizeof(struct ip_rt_info),
191 };
192
193 static int ipv4_netfilter_init(void)
194 {
195         return nf_register_afinfo(&nf_ip_afinfo);
196 }
197
198 static void ipv4_netfilter_fini(void)
199 {
200         nf_unregister_afinfo(&nf_ip_afinfo);
201 }
202
203 module_init(ipv4_netfilter_init);
204 module_exit(ipv4_netfilter_fini);