*/
        int (*input2)(struct xfrm_state *x, struct sk_buff *skb);
 
-       /*
-        * This is the actual input entry point.
-        *
-        * For transport mode and equivalent this would be identical to
-        * input2 (which does not need to be set).  While tunnel mode
-        * and equivalent would set this to the tunnel encapsulation function
-        * xfrm4_prepare_input that would in turn call input2.
-        */
-       int (*input)(struct xfrm_state *x, struct sk_buff *skb);
-
        /*
         * Add encapsulation header.
         *
 int xfrm_state_mtu(struct xfrm_state *x, int mtu);
 int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload);
 int xfrm_init_state(struct xfrm_state *x);
-int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb);
 int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type);
 int xfrm_input_resume(struct sk_buff *skb, int nexthdr);
 int xfrm_trans_queue(struct sk_buff *skb,
 
 
 static struct xfrm_mode xfrm4_beet_mode = {
        .input2 = xfrm4_beet_input,
-       .input = xfrm_prepare_input,
        .output2 = xfrm4_beet_output,
        .output = xfrm4_prepare_output,
        .owner = THIS_MODULE,
 
        return 0;
 }
 
-/* Remove encapsulation header.
- *
- * The IP header will be moved over the top of the encapsulation header.
- *
- * On entry, skb->h shall point to where the IP header should be and skb->nh
- * shall be set to where the IP header currently is.  skb->data shall point
- * to the start of the payload.
- */
-static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
-{
-       int ihl = skb->data - skb_transport_header(skb);
-
-       if (skb->transport_header != skb->network_header) {
-               memmove(skb_transport_header(skb),
-                       skb_network_header(skb), ihl);
-               skb->network_header = skb->transport_header;
-       }
-       ip_hdr(skb)->tot_len = htons(skb->len + ihl);
-       skb_reset_transport_header(skb);
-       return 0;
-}
-
 static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x,
                                                   struct sk_buff *skb,
                                                   netdev_features_t features)
 }
 
 static struct xfrm_mode xfrm4_transport_mode = {
-       .input = xfrm4_transport_input,
        .output = xfrm4_transport_output,
        .gso_segment = xfrm4_transport_gso_segment,
        .xmit = xfrm4_transport_xmit,
 
 
 static struct xfrm_mode xfrm4_tunnel_mode = {
        .input2 = xfrm4_mode_tunnel_input,
-       .input = xfrm_prepare_input,
        .output2 = xfrm4_mode_tunnel_output,
        .output = xfrm4_prepare_output,
        .gso_segment = xfrm4_mode_tunnel_gso_segment,
 
 
 static struct xfrm_mode xfrm6_beet_mode = {
        .input2 = xfrm6_beet_input,
-       .input = xfrm_prepare_input,
        .output2 = xfrm6_beet_output,
        .output = xfrm6_prepare_output,
        .owner = THIS_MODULE,
 
        return 0;
 }
 
-/* Remove encapsulation header.
- *
- * The IP header will be moved over the top of the encapsulation header.
- *
- * On entry, skb->h shall point to where the IP header should be and skb->nh
- * shall be set to where the IP header currently is.  skb->data shall point
- * to the start of the payload.
- */
-static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
-{
-       int ihl = skb->data - skb_transport_header(skb);
-
-       if (skb->transport_header != skb->network_header) {
-               memmove(skb_transport_header(skb),
-                       skb_network_header(skb), ihl);
-               skb->network_header = skb->transport_header;
-       }
-       ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -
-                                          sizeof(struct ipv6hdr));
-       skb_reset_transport_header(skb);
-       return 0;
-}
-
 static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x,
                                                   struct sk_buff *skb,
                                                   netdev_features_t features)
        }
 }
 
-
 static struct xfrm_mode xfrm6_transport_mode = {
-       .input = xfrm6_transport_input,
        .output = xfrm6_transport_output,
        .gso_segment = xfrm4_transport_gso_segment,
        .xmit = xfrm6_transport_xmit,
 
 
 static struct xfrm_mode xfrm6_tunnel_mode = {
        .input2 = xfrm6_mode_tunnel_input,
-       .input = xfrm_prepare_input,
        .output2 = xfrm6_mode_tunnel_output,
        .output = xfrm6_prepare_output,
        .gso_segment = xfrm6_mode_tunnel_gso_segment,
 
 }
 EXPORT_SYMBOL(xfrm_parse_spi);
 
-int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
+static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct xfrm_mode *inner_mode = x->inner_mode;
        int err;
        skb->protocol = inner_mode->afinfo->eth_proto;
        return inner_mode->input2(x, skb);
 }
-EXPORT_SYMBOL(xfrm_prepare_input);
+
+/* Remove encapsulation header.
+ *
+ * The IP header will be moved over the top of the encapsulation header.
+ *
+ * On entry, skb_transport_header() shall point to where the IP header
+ * should be and skb_network_header() shall be set to where the IP header
+ * currently is.  skb->data shall point to the start of the payload.
+ */
+static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+#if IS_ENABLED(CONFIG_INET_XFRM_MODE_TRANSPORT)
+       int ihl = skb->data - skb_transport_header(skb);
+
+       if (skb->transport_header != skb->network_header) {
+               memmove(skb_transport_header(skb),
+                       skb_network_header(skb), ihl);
+               skb->network_header = skb->transport_header;
+       }
+       ip_hdr(skb)->tot_len = htons(skb->len + ihl);
+       skb_reset_transport_header(skb);
+       return 0;
+#else
+       return -EOPNOTSUPP;
+#endif
+}
+
+static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
+{
+#if IS_ENABLED(CONFIG_INET6_XFRM_MODE_TRANSPORT)
+       int ihl = skb->data - skb_transport_header(skb);
+
+       if (skb->transport_header != skb->network_header) {
+               memmove(skb_transport_header(skb),
+                       skb_network_header(skb), ihl);
+               skb->network_header = skb->transport_header;
+       }
+       ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -
+                                          sizeof(struct ipv6hdr));
+       skb_reset_transport_header(skb);
+       return 0;
+#else
+       return -EOPNOTSUPP;
+#endif
+}
+
+static int xfrm_inner_mode_input(struct xfrm_state *x,
+                                const struct xfrm_mode *inner_mode,
+                                struct sk_buff *skb)
+{
+       switch (inner_mode->encap) {
+       case XFRM_MODE_BEET:
+       case XFRM_MODE_TUNNEL:
+               return xfrm_prepare_input(x, skb);
+       case XFRM_MODE_TRANSPORT:
+               if (inner_mode->family == AF_INET)
+                       return xfrm4_transport_input(x, skb);
+               if (inner_mode->family == AF_INET6)
+                       return xfrm6_transport_input(x, skb);
+               break;
+       case XFRM_MODE_ROUTEOPTIMIZATION:
+               WARN_ON_ONCE(1);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
 
 int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 {
                        }
                }
 
-               if (inner_mode->input(x, skb)) {
+               if (xfrm_inner_mode_input(x, inner_mode, skb)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
                        goto drop;
                }