compat: Wrap IPv4 fragmentation.
[cascardo/ovs.git] / datapath / linux / compat / include / net / ip.h
1 #ifndef __NET_IP_WRAPPER_H
2 #define __NET_IP_WRAPPER_H 1
3
4 #include_next <net/ip.h>
5
6 #include <net/route.h>
7 #include <linux/version.h>
8
9 #ifndef HAVE_IP_IS_FRAGMENT
10 static inline bool ip_is_fragment(const struct iphdr *iph)
11 {
12         return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0;
13 }
14 #endif
15
16 #ifndef HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET
17 static inline void rpl_inet_get_local_port_range(struct net *net, int *low,
18                                              int *high)
19 {
20         inet_get_local_port_range(low, high);
21 }
22 #define inet_get_local_port_range rpl_inet_get_local_port_range
23
24 #endif
25
26 /* IPv4 datagram length is stored into 16bit field (tot_len) */
27 #ifndef IP_MAX_MTU
28 #define IP_MAX_MTU      0xFFFFU
29 #endif
30
31 #ifndef HAVE_IP_SKB_DST_MTU
32 static inline bool rpl_ip_sk_use_pmtu(const struct sock *sk)
33 {
34         return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
35 }
36 #define ip_sk_use_pmtu rpl_ip_sk_use_pmtu
37
38 static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
39                                                     bool forwarding)
40 {
41 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
42         struct net *net = dev_net(dst->dev);
43
44         if (net->ipv4.sysctl_ip_fwd_use_pmtu ||
45             dst_metric_locked(dst, RTAX_MTU) ||
46             !forwarding)
47                 return dst_mtu(dst);
48 #endif
49
50         return min(dst->dev->mtu, IP_MAX_MTU);
51 }
52
53 static inline unsigned int rpl_ip_skb_dst_mtu(const struct sk_buff *skb)
54 {
55         if (!skb->sk || ip_sk_use_pmtu(skb->sk)) {
56                 bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
57                 return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
58         } else {
59                 return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
60         }
61 }
62 #define ip_skb_dst_mtu rpl_ip_skb_dst_mtu
63 #endif /* HAVE_IP_SKB_DST_MTU */
64
65 #ifdef HAVE_IP_FRAGMENT_TAKES_SOCK
66 #define OVS_VPORT_OUTPUT_PARAMS struct sock *sock, struct sk_buff *skb
67 #else
68 #define OVS_VPORT_OUTPUT_PARAMS struct sk_buff *skb
69 #endif
70
71 #ifdef OVS_FRAGMENT_BACKPORT
72
73 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
74 static inline bool ip_defrag_user_in_between(u32 user,
75                                              enum ip_defrag_users lower_bond,
76                                              enum ip_defrag_users upper_bond)
77 {
78         return user >= lower_bond && user <= upper_bond;
79 }
80 #endif
81
82 #ifndef HAVE_IP_DO_FRAGMENT
83 static inline int rpl_ip_do_fragment(struct sock *sk, struct sk_buff *skb,
84                                      int (*output)(OVS_VPORT_OUTPUT_PARAMS))
85 {
86         unsigned int mtu = ip_skb_dst_mtu(skb);
87         struct iphdr *iph = ip_hdr(skb);
88         struct rtable *rt = skb_rtable(skb);
89         struct net_device *dev = rt->dst.dev;
90
91         if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
92                      (IPCB(skb)->frag_max_size &&
93                       IPCB(skb)->frag_max_size > mtu))) {
94
95                 pr_warn("Dropping packet in ip_do_fragment()\n");
96                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
97                 kfree_skb(skb);
98                 return -EMSGSIZE;
99         }
100
101 #ifndef HAVE_IP_FRAGMENT_TAKES_SOCK
102         return ip_fragment(skb, output);
103 #else
104         return ip_fragment(sk, skb, output);
105 #endif
106 }
107 #define ip_do_fragment rpl_ip_do_fragment
108 #endif /* IP_DO_FRAGMENT */
109 #endif /* OVS_FRAGMENT_BACKPORT */
110 #endif