compat: Detect and use upstream ip_fragment().
[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 #ifndef IPSKB_FRAG_PMTU
27 #define IPSKB_FRAG_PMTU                BIT(6)
28 #endif
29
30 /* IPv4 datagram length is stored into 16bit field (tot_len) */
31 #ifndef IP_MAX_MTU
32 #define IP_MAX_MTU      0xFFFFU
33 #endif
34
35 #ifndef HAVE_IP_SKB_DST_MTU
36 static inline bool rpl_ip_sk_use_pmtu(const struct sock *sk)
37 {
38         return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE;
39 }
40 #define ip_sk_use_pmtu rpl_ip_sk_use_pmtu
41
42 static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
43                                                     bool forwarding)
44 {
45 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
46         struct net *net = dev_net(dst->dev);
47
48         if (net->ipv4.sysctl_ip_fwd_use_pmtu ||
49             dst_metric_locked(dst, RTAX_MTU) ||
50             !forwarding)
51                 return dst_mtu(dst);
52 #endif
53
54         return min(dst->dev->mtu, IP_MAX_MTU);
55 }
56
57 static inline unsigned int rpl_ip_skb_dst_mtu(const struct sk_buff *skb)
58 {
59         if (!skb->sk || ip_sk_use_pmtu(skb->sk)) {
60                 bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
61                 return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
62         } else {
63                 return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
64         }
65 }
66 #define ip_skb_dst_mtu rpl_ip_skb_dst_mtu
67 #endif /* HAVE_IP_SKB_DST_MTU */
68
69 #ifdef HAVE_IP_FRAGMENT_TAKES_SOCK
70 #define OVS_VPORT_OUTPUT_PARAMS struct sock *sock, struct sk_buff *skb
71 #else
72 #define OVS_VPORT_OUTPUT_PARAMS struct sk_buff *skb
73 #endif
74
75 /* Prior to upstream commit d6b915e29f4a ("ip_fragment: don't forward
76  * defragmented DF packet"), IPCB(skb)->frag_max_size was not always populated
77  * correctly, which would lead to reassembled packets not being refragmented.
78  * So, we backport all of ip_defrag() in these cases.
79  */
80 #if !defined(HAVE_CORRECT_MRU_HANDLING) && defined(OVS_FRAGMENT_BACKPORT)
81
82 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
83 static inline bool ip_defrag_user_in_between(u32 user,
84                                              enum ip_defrag_users lower_bond,
85                                              enum ip_defrag_users upper_bond)
86 {
87         return user >= lower_bond && user <= upper_bond;
88 }
89 #endif /* < v4.2 */
90
91 #ifndef HAVE_IP_DO_FRAGMENT
92 static inline int rpl_ip_do_fragment(struct sock *sk, struct sk_buff *skb,
93                                      int (*output)(OVS_VPORT_OUTPUT_PARAMS))
94 {
95         unsigned int mtu = ip_skb_dst_mtu(skb);
96         struct iphdr *iph = ip_hdr(skb);
97         struct rtable *rt = skb_rtable(skb);
98         struct net_device *dev = rt->dst.dev;
99
100         if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
101                      (IPCB(skb)->frag_max_size &&
102                       IPCB(skb)->frag_max_size > mtu))) {
103
104                 pr_warn("Dropping packet in ip_do_fragment()\n");
105                 IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
106                 kfree_skb(skb);
107                 return -EMSGSIZE;
108         }
109
110 #ifndef HAVE_IP_FRAGMENT_TAKES_SOCK
111         return ip_fragment(skb, output);
112 #else
113         return ip_fragment(sk, skb, output);
114 #endif
115 }
116 #define ip_do_fragment rpl_ip_do_fragment
117 #endif /* IP_DO_FRAGMENT */
118
119 int rpl_ip_defrag(struct sk_buff *skb, u32 user);
120 #define ip_defrag rpl_ip_defrag
121
122 int __init rpl_ipfrag_init(void);
123 void rpl_ipfrag_fini(void);
124 #else /* HAVE_CORRECT_MRU_HANDLING || !OVS_FRAGMENT_BACKPORT */
125
126 /* We have no good way to detect the presence of upstream commit 8282f27449bf
127  * ("inet: frag: Always orphan skbs inside ip_defrag()"), but it should be
128  * always included in kernels 4.5+. */
129 #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
130 static inline int rpl_ip_defrag(struct sk_buff *skb, u32 user)
131 {
132         skb_orphan(skb);
133         return ip_defrag(skb, user);
134 }
135 #define ip_defrag rpl_ip_defrag
136 #endif
137
138 static inline int rpl_ipfrag_init(void) { return 0; }
139 static inline void rpl_ipfrag_fini(void) { }
140 #endif /* HAVE_CORRECT_MRU_HANDLING && OVS_FRAGMENT_BACKPORT */
141 #define ipfrag_init rpl_ipfrag_init
142 #define ipfrag_fini rpl_ipfrag_fini
143
144 #endif