]>
Commit | Line | Data |
---|---|---|
020b4c12 HW |
1 | #include <linux/config.h> |
2 | ||
3 | #ifdef CONFIG_NETFILTER | |
4 | ||
5 | /* IPv4 specific functions of netfilter core */ | |
6 | #include <linux/kernel.h> | |
7 | #include <linux/netfilter.h> | |
8 | ||
9 | #include <linux/tcp.h> | |
10 | #include <linux/udp.h> | |
11 | #include <linux/icmp.h> | |
12 | #include <net/route.h> | |
13 | #include <linux/ip.h> | |
14 | ||
15 | /* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */ | |
16 | int ip_route_me_harder(struct sk_buff **pskb) | |
17 | { | |
18 | struct iphdr *iph = (*pskb)->nh.iph; | |
19 | struct rtable *rt; | |
20 | struct flowi fl = {}; | |
21 | struct dst_entry *odst; | |
22 | unsigned int hh_len; | |
23 | ||
24 | /* some non-standard hacks like ipt_REJECT.c:send_reset() can cause | |
25 | * packets with foreign saddr to appear on the NF_IP_LOCAL_OUT hook. | |
26 | */ | |
27 | if (inet_addr_type(iph->saddr) == RTN_LOCAL) { | |
28 | fl.nl_u.ip4_u.daddr = iph->daddr; | |
29 | fl.nl_u.ip4_u.saddr = iph->saddr; | |
30 | fl.nl_u.ip4_u.tos = RT_TOS(iph->tos); | |
31 | fl.oif = (*pskb)->sk ? (*pskb)->sk->sk_bound_dev_if : 0; | |
32 | #ifdef CONFIG_IP_ROUTE_FWMARK | |
33 | fl.nl_u.ip4_u.fwmark = (*pskb)->nfmark; | |
34 | #endif | |
35 | fl.proto = iph->protocol; | |
36 | if (ip_route_output_key(&rt, &fl) != 0) | |
37 | return -1; | |
38 | ||
39 | /* Drop old route. */ | |
40 | dst_release((*pskb)->dst); | |
41 | (*pskb)->dst = &rt->u.dst; | |
42 | } else { | |
43 | /* non-local src, find valid iif to satisfy | |
44 | * rp-filter when calling ip_route_input. */ | |
45 | fl.nl_u.ip4_u.daddr = iph->saddr; | |
46 | if (ip_route_output_key(&rt, &fl) != 0) | |
47 | return -1; | |
48 | ||
49 | odst = (*pskb)->dst; | |
50 | if (ip_route_input(*pskb, iph->daddr, iph->saddr, | |
51 | RT_TOS(iph->tos), rt->u.dst.dev) != 0) { | |
52 | dst_release(&rt->u.dst); | |
53 | return -1; | |
54 | } | |
55 | dst_release(&rt->u.dst); | |
56 | dst_release(odst); | |
57 | } | |
58 | ||
59 | if ((*pskb)->dst->error) | |
60 | return -1; | |
61 | ||
62 | /* Change in oif may mean change in hh_len. */ | |
63 | hh_len = (*pskb)->dst->dev->hard_header_len; | |
64 | if (skb_headroom(*pskb) < hh_len) { | |
65 | struct sk_buff *nskb; | |
66 | ||
67 | nskb = skb_realloc_headroom(*pskb, hh_len); | |
68 | if (!nskb) | |
69 | return -1; | |
70 | if ((*pskb)->sk) | |
71 | skb_set_owner_w(nskb, (*pskb)->sk); | |
72 | kfree_skb(*pskb); | |
73 | *pskb = nskb; | |
74 | } | |
75 | ||
76 | return 0; | |
77 | } | |
78 | EXPORT_SYMBOL(ip_route_me_harder); | |
79 | #endif /* CONFIG_NETFILTER */ |