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