diff --git a/FAQ.md b/FAQ.md
index d11c493..038c28a 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -156,7 +156,7 @@ A: The following table lists the Linux kernel versions against which the
 |    2.1.x     | 2.6.32 to 3.11
 |    2.3.x     | 2.6.32 to 3.14
 |    2.4.x     | 2.6.32 to 4.0
-|    2.5.x     | 2.6.32 to 4.2
+|    2.5.x     | 2.6.32 to 4.3
 
    Open vSwitch userspace should also work with the Linux kernel module
    built into Linux 3.3 and later.
diff --git a/acinclude.m4 b/acinclude.m4
index b246dc7..c5848bc 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -134,10 +134,10 @@ AC_DEFUN([OVS_CHECK_LINUX], [
     AC_MSG_RESULT([$kversion])
 
     if test "$version" -ge 4; then
-       if test "$version" = 4 && test "$patchlevel" -le 2; then
+       if test "$version" = 4 && test "$patchlevel" -le 3; then
           : # Linux 4.x
        else
-          AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version newer than 4.2.x is not supported (please refer to the FAQ for advice)])
+          AC_ERROR([Linux kernel in $KBUILD is version $kversion, but version newer than 4.3.x is not supported (please refer to the FAQ for advice)])
        fi
     elif test "$version" = 3; then
        : # Linux 3.x
@@ -327,6 +327,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/in.h], [proto_ports_offset])
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [__ip_select_ident.*dst_entry],
                   [OVS_DEFINE([HAVE_IP_SELECT_IDENT_USING_DST_ENTRY])])
+  OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [__ip_select_ident.*net],
+                  [OVS_DEFINE([HAVE_IP_SELECT_IDENT_USING_NET])])
+
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [inet_get_local_port_range.*net],
                   [OVS_DEFINE([HAVE_INET_GET_LOCAL_PORT_RANGE_USING_NET])])
   OVS_GREP_IFELSE([$KSRC/include/net/ip.h], [ip_is_fragment])
@@ -344,6 +347,9 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [net_device_extended])
   OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [rx_handler_func_t.*pskb],
                   [OVS_DEFINE([HAVE_RX_HANDLER_PSKB])])
+  OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [netif_needs_gso.*net_device],
+                  [OVS_DEFINE([HAVE_NETIF_NEEDS_GSO_NETDEV])])
+
   OVS_GREP_IFELSE([$KSRC/include/linux/netfilter.h], [nf_hookfn.*nf_hook_ops],
                   [OVS_DEFINE([HAVE_NF_HOOKFN_ARG_OPS])])
 
@@ -420,6 +426,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [
   OVS_GREP_IFELSE([$KSRC/include/net/geneve.h], [geneve_hdr])
 
   OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_cisco_register])
+  OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_handle_offloads])
   OVS_GREP_IFELSE([$KSRC/include/net/ipv6.h], [IP6_FH_F_SKIP_RH])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16])
   OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16])
diff --git a/datapath/linux/compat/geneve.c b/datapath/linux/compat/geneve.c
index 85cf95f..8475419 100644
--- a/datapath/linux/compat/geneve.c
+++ b/datapath/linux/compat/geneve.c
@@ -12,7 +12,8 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) && \
+    defined(HAVE_GRE_HANDLE_OFFLOADS)
 
 #include <linux/kernel.h>
 #include <linux/types.h>
diff --git a/datapath/linux/compat/gre.c b/datapath/linux/compat/gre.c
index fe81380..de1dbaf 100644
--- a/datapath/linux/compat/gre.c
+++ b/datapath/linux/compat/gre.c
@@ -40,7 +40,7 @@
 
 #if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)
+#ifndef HAVE_GRE_HANDLE_OFFLOADS
 
 #ifndef HAVE_GRE_CISCO_REGISTER
 
diff --git a/datapath/linux/compat/gso.c b/datapath/linux/compat/gso.c
index 2c19b58..d11c62e 100644
--- a/datapath/linux/compat/gso.c
+++ b/datapath/linux/compat/gso.c
@@ -130,7 +130,7 @@ int rpl_dev_queue_xmit(struct sk_buff *skb)
 		if (mpls)
 			features &= NETIF_F_SG;
 
-		if (netif_needs_gso(skb->dev, skb, features)) {
+		if (netif_needs_gso(skb, features)) {
 			struct sk_buff *nskb;
 
 			nskb = skb_gso_segment(skb, features);
@@ -170,7 +170,8 @@ drop:
 EXPORT_SYMBOL_GPL(rpl_dev_queue_xmit);
 #endif /* OVS_USE_COMPAT_GSO_SEGMENTATION */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 static __be16 __skb_network_protocol(struct sk_buff *skb)
 {
 	__be16 type = skb->protocol;
diff --git a/datapath/linux/compat/gso.h b/datapath/linux/compat/gso.h
index 6fcaff8..bf409cf 100644
--- a/datapath/linux/compat/gso.h
+++ b/datapath/linux/compat/gso.h
@@ -2,7 +2,8 @@
 #define __LINUX_GSO_WRAPPER_H
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -14,13 +15,10 @@ typedef void (*gso_fix_segment_t)(struct sk_buff *);
 struct ovs_gso_cb {
 	struct ovs_skb_cb dp_cb;
 	gso_fix_segment_t fix_segment;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 	__be16		inner_protocol;
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
 	unsigned int	inner_mac_header;
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
 	unsigned int	inner_network_header;
 #endif
 };
@@ -37,12 +35,15 @@ static inline void skb_clear_ovs_gso_cb(struct sk_buff *skb)
 }
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
+#define skb_inner_mac_header rpl_skb_inner_mac_header
 static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb)
 {
 	return skb->head + OVS_GSO_CB(skb)->inner_mac_header;
 }
 
+#define skb_set_inner_mac_header rpl_skb_set_inner_mac_header
 static inline void skb_set_inner_mac_header(const struct sk_buff *skb,
 					    int offset)
 {
@@ -50,12 +51,15 @@ static inline void skb_set_inner_mac_header(const struct sk_buff *skb,
 }
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
+#define skb_inner_network_header rpl_skb_inner_network_header
 static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb)
 {
 	return skb->head + OVS_GSO_CB(skb)->inner_network_header;
 }
 
+#define skb_inner_network_offset rpl_skb_inner_network_offset
 static inline int skb_inner_network_offset(const struct sk_buff *skb)
 {
 	return skb_inner_network_header(skb) - skb->data;
@@ -70,6 +74,7 @@ static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
 	return 0;
 }
 
+#define skb_set_inner_network_header rpl_skb_set_inner_network_header
 static inline void skb_set_inner_network_header(const struct sk_buff *skb,
 						int offset)
 {
@@ -77,6 +82,7 @@ static inline void skb_set_inner_network_header(const struct sk_buff *skb,
 						+ offset;
 }
 
+#define skb_set_inner_transport_header rpl_skb_set_inner_transport_header
 static inline void skb_set_inner_transport_header(const struct sk_buff *skb,
 						  int offset)
 { }
@@ -90,7 +96,8 @@ static inline int ovs_skb_inner_transport_offset(const struct sk_buff *skb)
 
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
 	OVS_GSO_CB(skb)->inner_protocol = htons(0);
 }
@@ -114,7 +121,8 @@ static inline void ovs_skb_init_inner_protocol(struct sk_buff *skb) {
 	 */
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 static inline void ovs_skb_set_inner_protocol(struct sk_buff *skb,
 					      __be16 ethertype)
 {
@@ -134,10 +142,12 @@ static inline __be16 ovs_skb_get_inner_protocol(struct sk_buff *skb)
 }
 #endif /* 3.11 */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) ||\
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 #define ip_local_out rpl_ip_local_out
 int rpl_ip_local_out(struct sk_buff *skb);
 
+#define skb_inner_mac_offset rpl_skb_inner_mac_offset
 static inline int skb_inner_mac_offset(const struct sk_buff *skb)
 {
 	return skb_inner_mac_header(skb) - skb->data;
diff --git a/datapath/linux/compat/include/linux/netdevice.h b/datapath/linux/compat/include/linux/netdevice.h
index 0fb2144..a2f7eb9 100644
--- a/datapath/linux/compat/include/linux/netdevice.h
+++ b/datapath/linux/compat/include/linux/netdevice.h
@@ -88,7 +88,8 @@ static inline struct net_device *dev_get_by_index_rcu(struct net *net, int ifind
 typedef u32 netdev_features_t;
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 #define OVS_USE_COMPAT_GSO_SEGMENTATION
 #endif
 
@@ -112,18 +113,15 @@ struct sk_buff *rpl_skb_gso_segment(struct sk_buff *skb, netdev_features_t featu
 netdev_features_t rpl_netif_skb_features(struct sk_buff *skb);
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
-static inline int rpl_netif_needs_gso(struct net_device *dev,
-				      struct sk_buff *skb, int features)
+#ifdef HAVE_NETIF_NEEDS_GSO_NETDEV
+#define netif_needs_gso rpl_netif_needs_gso
+static inline bool netif_needs_gso(struct sk_buff *skb,
+				   netdev_features_t features)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
 	return skb_is_gso(skb) && (!skb_gso_ok(skb, features) ||
-		unlikely(skb->ip_summed != CHECKSUM_PARTIAL));
-#else
-	return netif_needs_gso(skb, features);
-#endif
+		unlikely((skb->ip_summed != CHECKSUM_PARTIAL) &&
+			 (skb->ip_summed != CHECKSUM_UNNECESSARY)));
 }
-#define netif_needs_gso rpl_netif_needs_gso
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
diff --git a/datapath/linux/compat/include/net/geneve.h b/datapath/linux/compat/include/net/geneve.h
index 4f250c2..116ebc7 100644
--- a/datapath/linux/compat/include/net/geneve.h
+++ b/datapath/linux/compat/include/net/geneve.h
@@ -3,7 +3,8 @@
 
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) && \
+    defined(HAVE_GRE_HANDLE_OFFLOADS)
 #include_next <net/geneve.h>
 #else
 
diff --git a/datapath/linux/compat/include/net/gre.h b/datapath/linux/compat/include/net/gre.h
index 6e0df0f..040fd11 100644
--- a/datapath/linux/compat/include/net/gre.h
+++ b/datapath/linux/compat/include/net/gre.h
@@ -28,13 +28,17 @@ int rpl_gre_cisco_register(struct gre_cisco_protocol *proto);
 #define gre_cisco_unregister rpl_gre_cisco_unregister
 int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
+#ifndef HAVE_GRE_HANDLE_OFFLOADS
+#ifndef GRE_HEADER_SECTION
 struct gre_base_hdr {
 	__be16 flags;
 	__be16 protocol;
 };
 #define GRE_HEADER_SECTION 4
 
+#endif
+
+#define gre_flags_to_tnl_flags rpl_gre_flags_to_tnl_flags
 static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
 {
 	__be16 tflags = 0;
@@ -57,6 +61,7 @@ static inline __be16 gre_flags_to_tnl_flags(__be16 flags)
 	return tflags;
 }
 
+#define tnl_flags_to_gre_flags rpl_tnl_flags_to_gre_flags
 static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
 {
 	__be16 flags = 0;
@@ -84,8 +89,7 @@ static inline __be16 tnl_flags_to_gre_flags(__be16 tflags)
 #define gre_handle_offloads rpl_gre_handle_offloads
 struct sk_buff *rpl_gre_handle_offloads(struct sk_buff *skb, bool gre_csum);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,12,0)
-
+#ifndef HAVE_GRE_HANDLE_OFFLOADS
 #define gre_build_header rpl_gre_build_header
 void rpl_gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
 		          int hdr_len);
diff --git a/datapath/linux/compat/include/net/ip_tunnels.h b/datapath/linux/compat/include/net/ip_tunnels.h
index 3ed6f91..0ca086f 100644
--- a/datapath/linux/compat/include/net/ip_tunnels.h
+++ b/datapath/linux/compat/include/net/ip_tunnels.h
@@ -7,7 +7,8 @@
 #include_next <net/ip_tunnels.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 
 #include <linux/if_tunnel.h>
 #include <linux/netdevice.h>
diff --git a/datapath/linux/compat/include/net/udp_tunnel.h b/datapath/linux/compat/include/net/udp_tunnel.h
index d334746..21d05ff 100644
--- a/datapath/linux/compat/include/net/udp_tunnel.h
+++ b/datapath/linux/compat/include/net/udp_tunnel.h
@@ -4,7 +4,9 @@
 #include <linux/version.h>
 #include <linux/kconfig.h>
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,0,0) && \
+    defined(HAVE_GRE_HANDLE_OFFLOADS)
+
 #include_next <net/udp_tunnel.h>
 
 static inline struct sk_buff *
diff --git a/datapath/linux/compat/include/net/vxlan.h b/datapath/linux/compat/include/net/vxlan.h
index cafff79..1a85a17 100644
--- a/datapath/linux/compat/include/net/vxlan.h
+++ b/datapath/linux/compat/include/net/vxlan.h
@@ -8,7 +8,9 @@
 
 #include <linux/version.h>
 
-#ifdef HAVE_VXLAN_METADATA
+/* gre_handle_offload is not defined on kernel with netdev-vport support
+ * Use this as way to switch between compatibility implementations. */
+#if defined(HAVE_VXLAN_METADATA) && defined(HAVE_GRE_HANDLE_OFFLOADS)
 #define USE_UPSTREAM_VXLAN
 
 #include_next <net/vxlan.h>
diff --git a/datapath/linux/compat/ip_tunnels_core.c b/datapath/linux/compat/ip_tunnels_core.c
index 8ff7cd7..05c8564 100644
--- a/datapath/linux/compat/ip_tunnels_core.c
+++ b/datapath/linux/compat/ip_tunnels_core.c
@@ -35,7 +35,8 @@
 #include "compat.h"
 #include "gso.h"
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
                       __be32 src, __be32 dst, __u8 proto, __u8 tos, __u8 ttl,
                       __be16 df, bool xnet)
@@ -71,6 +72,9 @@ int rpl_iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
 
 #ifdef HAVE_IP_SELECT_IDENT_USING_DST_ENTRY
 	__ip_select_ident(iph, &rt_dst(rt), (skb_shinfo(skb)->gso_segs ?: 1) - 1);
+#elif defined(HAVE_IP_SELECT_IDENT_USING_NET)
+	__ip_select_ident(dev_net(rt->dst.dev), iph,
+			  skb_shinfo(skb)->gso_segs ?: 1);
 #else
 	__ip_select_ident(iph, skb_shinfo(skb)->gso_segs ?: 1);
 #endif
@@ -84,7 +88,7 @@ EXPORT_SYMBOL_GPL(rpl_iptunnel_xmit);
 
 struct sk_buff *ovs_iptunnel_handle_offloads(struct sk_buff *skb,
 					     bool csum_help, int gso_type_mask,
-				             void (*fix_segment)(struct sk_buff *))
+					     void (*fix_segment)(struct sk_buff *))
 {
 	int err;
 
diff --git a/datapath/linux/compat/udp_tunnel.c b/datapath/linux/compat/udp_tunnel.c
index b6c5bad..11cb22a 100644
--- a/datapath/linux/compat/udp_tunnel.c
+++ b/datapath/linux/compat/udp_tunnel.c
@@ -1,6 +1,7 @@
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) || \
+    !defined(HAVE_GRE_HANDLE_OFFLOADS)
 
 #include <linux/module.h>
 #include <linux/errno.h>
@@ -23,12 +24,10 @@ int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
 	if (cfg->family == AF_INET6) {
 		struct sockaddr_in6 udp6_addr;
 
-		err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
+		err = ovs_sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock);
 		if (err < 0)
 			goto error;
 
-		sk_change_net(sock->sk, net);
-
 		udp6_addr.sin6_family = AF_INET6;
 		memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
 		       sizeof(udp6_addr.sin6_addr));
@@ -54,12 +53,10 @@ int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
 	if (cfg->family == AF_INET) {
 		struct sockaddr_in udp_addr;
 
-		err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
+		err = ovs_sock_create_kern(net, AF_INET, SOCK_DGRAM, 0, &sock);
 		if (err < 0)
 			goto error;
 
-		sk_change_net(sock->sk, net);
-
 		udp_addr.sin_family = AF_INET;
 		udp_addr.sin_addr = cfg->local_ip;
 		udp_addr.sin_port = cfg->local_udp_port;
