| 1 | |
| 2 | |
| 3 | * IPv4 specific functions of netfilter core
|
| 4 | *
|
| 5 | * Rusty Russell (C) 2000
|
| 6 | * Patrick McHardy (C) 2006-2012
|
| 7 |
|
| 8 | #include <linux/kernel.h>
|
| 9 | #include <linux/netfilter.h>
|
| 10 | #include <linux/netfilter_ipv4.h>
|
| 11 | #include <linux/ip.h>
|
| 12 | #include <linux/skbuff.h>
|
| 13 | #include <linux/gfp.h>
|
| 14 | #include <linux/export.h>
|
| 15 | #include <net/flow.h>
|
| 16 | #include <net/route.h>
|
| 17 | #include <net/xfrm.h>
|
| 18 | #include <net/ip.h>
|
| 19 | #include <net/netfilter/nf_queue.h>
|
| 20 |
|
| 21 |
|
| 22 | int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type)
|
| 23 | {
|
| 24 | struct net_device *dev = skb_dst_dev(skb);
|
| 25 | const struct iphdr *iph = ip_hdr(skb);
|
| 26 | struct rtable *rt;
|
| 27 | struct flowi4 fl4 = {};
|
| 28 | __be32 saddr = iph->saddr;
|
| 29 | __u8 flags;
|
| 30 | struct flow_keys flkeys;
|
| 31 | unsigned int hh_len;
|
| 32 |
|
| 33 | sk = sk_to_full_sk(sk);
|
| 34 | flags = sk ? inet_sk_flowi_flags(sk) : 0;
|
| 35 |
|
| 36 | if (addr_type == RTN_UNSPEC)
|
| 37 | addr_type = inet_addr_type_dev_table(net, dev, saddr);
|
| 38 | if (addr_type == RTN_LOCAL || addr_type == RTN_UNICAST)
|
| 39 | flags |= FLOWI_FLAG_ANYSRC;
|
| 40 | else
|
| 41 | saddr = 0;
|
| 42 |
|
| 43 | |
| 44 | * packets with foreign saddr to appear on the NF_INET_LOCAL_OUT hook.
|
| 45 |
|
| 46 | fl4.daddr = iph->daddr;
|
| 47 | fl4.saddr = saddr;
|
| 48 | fl4.flowi4_dscp = ip4h_dscp(iph);
|
| 49 | fl4.flowi4_oif = sk ? sk->sk_bound_dev_if : 0;
|
| 50 | fl4.flowi4_l3mdev = l3mdev_master_ifindex(dev);
|
| 51 | fl4.flowi4_mark = skb->mark;
|
| 52 | fl4.flowi4_flags = flags;
|
| 53 | fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys);
|
| 54 | rt = ip_route_output_key(net, &fl4);
|
| 55 | if (IS_ERR(rt))
|
| 56 | return PTR_ERR(rt);
|
| 57 |
|
| 58 |
|
| 59 | skb_dst_drop(skb);
|
| 60 | skb_dst_set(skb, &rt->dst);
|
| 61 |
|
| 62 | if (skb_dst(skb)->error)
|
| 63 | return skb_dst(skb)->error;
|
| 64 |
|
| 65 | #ifdef CONFIG_XFRM
|
| 66 | if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
|
| 67 | xfrm_decode_session(net, skb, flowi4_to_flowi(&fl4), AF_INET) == 0) {
|
| 68 | struct dst_entry *dst = skb_dst(skb);
|
| 69 | |
| 70 | * care of dropping the refcnt if needed.
|
| 71 |
|
| 72 | skb_dstref_steal(skb);
|
| 73 | dst = xfrm_lookup(net, dst, flowi4_to_flowi(&fl4), sk, 0);
|
| 74 | if (IS_ERR(dst))
|
| 75 | return PTR_ERR(dst);
|
| 76 | skb_dst_set(skb, dst);
|
| 77 | }
|
| 78 | #endif
|
| 79 |
|
| 80 |
|
| 81 | hh_len = skb_dst_dev(skb)->hard_header_len;
|
| 82 | if (skb_headroom(skb) < hh_len &&
|
| 83 | pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
|
| 84 | 0, GFP_ATOMIC))
|
| 85 | return -ENOMEM;
|
| 86 |
|
| 87 | return 0;
|
| 88 | }
|
| 89 | EXPORT_SYMBOL(ip_route_me_harder);
|
| 90 |
|
| 91 | int nf_ip_route(struct net *net, struct dst_entry **dst, struct flowi *fl,
|
| 92 | bool strict __always_unused)
|
| 93 | {
|
| 94 | struct rtable *rt = ip_route_output_key(net, &fl->u.ip4);
|
| 95 | if (IS_ERR(rt))
|
| 96 | return PTR_ERR(rt);
|
| 97 | *dst = &rt->dst;
|
| 98 | return 0;
|
| 99 | }
|
| 100 | EXPORT_SYMBOL_GPL(nf_ip_route);
|