diff mbox series

[net-next,13/15] sctp: support for sending packet over udp4 sock

Message ID 82b358f40c81cfdecbfc394113be40fd1f682043.1601387231.git.lucien.xin@gmail.com
State Superseded
Headers show
Series sctp: Implement RFC6951: UDP Encapsulation of SCTP | expand

Commit Message

Xin Long Sept. 29, 2020, 1:49 p.m. UTC
This patch does what the rfc6951#section-5.3 says for ipv4:

  "Within the UDP header, the source port MUST be the local UDP
   encapsulation port number of the SCTP stack, and the destination port
   MUST be the remote UDP encapsulation port number maintained for the
   association and the destination address to which the packet is sent
   (see Section 5.1).

   Because the SCTP packet is the UDP payload, the length of the UDP
   packet MUST be the length of the SCTP packet plus the size of the UDP
   header.

   The SCTP checksum MUST be computed for IPv4 and IPv6, and the UDP
   checksum SHOULD be computed for IPv4 and IPv6."

Some places need to be adjusted in sctp_packet_transmit():

  1. For non-gso packets, when transport's encap_port is set, sctp
     checksum has to be done in sctp_packet_pack(), as the outer
     udp will use ip_summed = CHECKSUM_PARTIAL to do the offload
     setting for checksum.

  2. Delay calling dst_clone() and skb_dst_set() for non-udp packets
     until sctp_v4_xmit(), as for udp packets, skb_dst_set() is not
     needed before calling udp_tunnel_xmit_skb().

then in sctp_v4_xmit():

  1. Go to udp_tunnel_xmit_skb() only when transport->encap_port and
     net->sctp.udp_port both are set, as these are one for dst port
     and another for src port.

  2. For gso packet, SKB_GSO_UDP_TUNNEL_CSUM is set for gso_type, and
     with this udp checksum can be done in __skb_udp_tunnel_segment()
     for each segments after the sctp gso.

  3. inner_mac_header and inner_transport_header are set, as these
     will be needed in __skb_udp_tunnel_segment() to find the right
     headers.

  4. df and ttl are calculated, as these are the required params by
     udp_tunnel_xmit_skb().

  5. nocheck param has to be false, as "the UDP checksum SHOULD be
     computed for IPv4 and IPv6", says in rfc6951#section-5.3.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
 net/sctp/output.c   |  9 +++------
 net/sctp/protocol.c | 44 +++++++++++++++++++++++++++++++++-----------
 2 files changed, 36 insertions(+), 17 deletions(-)

Comments

kernel test robot Sept. 29, 2020, 4:25 p.m. UTC | #1
Hi Xin,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 280095713ce244e8dbdfb059cdca695baa72230a
config: ia64-randconfig-r014-20200929 (attached as .config)
compiler: ia64-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/a1016fd4a55f176fcc2eae05052a61ad7d5a142b
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
        git checkout a1016fd4a55f176fcc2eae05052a61ad7d5a142b
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=ia64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   net/sctp/protocol.c: In function 'sctp_udp_sock_start':
>> net/sctp/protocol.c:894:11: error: 'struct udp_port_cfg' has no member named 'local_ip6'; did you mean 'local_ip'?
     894 |  udp_conf.local_ip6 = in6addr_any;
         |           ^~~~~~~~~
         |           local_ip

vim +894 net/sctp/protocol.c

a330bee1c278f8 Xin Long 2020-09-29  870  
140bb5309cf409 Xin Long 2020-09-29  871  int sctp_udp_sock_start(struct net *net)
140bb5309cf409 Xin Long 2020-09-29  872  {
140bb5309cf409 Xin Long 2020-09-29  873  	struct udp_tunnel_sock_cfg tuncfg = {NULL};
140bb5309cf409 Xin Long 2020-09-29  874  	struct udp_port_cfg udp_conf = {0};
140bb5309cf409 Xin Long 2020-09-29  875  	struct socket *sock;
140bb5309cf409 Xin Long 2020-09-29  876  	int err;
140bb5309cf409 Xin Long 2020-09-29  877  
140bb5309cf409 Xin Long 2020-09-29  878  	udp_conf.family = AF_INET;
140bb5309cf409 Xin Long 2020-09-29  879  	udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
140bb5309cf409 Xin Long 2020-09-29  880  	udp_conf.local_udp_port = htons(net->sctp.udp_port);
140bb5309cf409 Xin Long 2020-09-29  881  	err = udp_sock_create(net, &udp_conf, &sock);
140bb5309cf409 Xin Long 2020-09-29  882  	if (err)
140bb5309cf409 Xin Long 2020-09-29  883  		return err;
140bb5309cf409 Xin Long 2020-09-29  884  
140bb5309cf409 Xin Long 2020-09-29  885  	tuncfg.encap_type = 1;
140bb5309cf409 Xin Long 2020-09-29  886  	tuncfg.encap_rcv = sctp_udp_rcv;
a330bee1c278f8 Xin Long 2020-09-29  887  	tuncfg.encap_err_lookup = sctp_udp_err_lookup;
140bb5309cf409 Xin Long 2020-09-29  888  	setup_udp_tunnel_sock(net, sock, &tuncfg);
140bb5309cf409 Xin Long 2020-09-29  889  	net->sctp.udp4_sock = sock->sk;
140bb5309cf409 Xin Long 2020-09-29  890  
cff8956126170d Xin Long 2020-09-29  891  	memset(&udp_conf, 0, sizeof(udp_conf));
cff8956126170d Xin Long 2020-09-29  892  
cff8956126170d Xin Long 2020-09-29  893  	udp_conf.family = AF_INET6;
cff8956126170d Xin Long 2020-09-29 @894  	udp_conf.local_ip6 = in6addr_any;
cff8956126170d Xin Long 2020-09-29  895  	udp_conf.local_udp_port = htons(net->sctp.udp_port);
cff8956126170d Xin Long 2020-09-29  896  	udp_conf.use_udp6_rx_checksums = true;
cff8956126170d Xin Long 2020-09-29  897  	udp_conf.ipv6_v6only = true;
cff8956126170d Xin Long 2020-09-29  898  	err = udp_sock_create(net, &udp_conf, &sock);
cff8956126170d Xin Long 2020-09-29  899  	if (err) {
cff8956126170d Xin Long 2020-09-29  900  		udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket);
cff8956126170d Xin Long 2020-09-29  901  		net->sctp.udp4_sock = NULL;
cff8956126170d Xin Long 2020-09-29  902  		return err;
cff8956126170d Xin Long 2020-09-29  903  	}
cff8956126170d Xin Long 2020-09-29  904  
cff8956126170d Xin Long 2020-09-29  905  	tuncfg.encap_type = 1;
cff8956126170d Xin Long 2020-09-29  906  	tuncfg.encap_rcv = sctp_udp_rcv;
a330bee1c278f8 Xin Long 2020-09-29  907  	tuncfg.encap_err_lookup = sctp_udp_err_lookup;
cff8956126170d Xin Long 2020-09-29  908  	setup_udp_tunnel_sock(net, sock, &tuncfg);
cff8956126170d Xin Long 2020-09-29  909  	net->sctp.udp6_sock = sock->sk;
cff8956126170d Xin Long 2020-09-29  910  
140bb5309cf409 Xin Long 2020-09-29  911  	return 0;
140bb5309cf409 Xin Long 2020-09-29  912  }
140bb5309cf409 Xin Long 2020-09-29  913  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot Sept. 29, 2020, 7:19 p.m. UTC | #2
Hi Xin,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 280095713ce244e8dbdfb059cdca695baa72230a
config: x86_64-randconfig-a002-20200929 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project de55ebe3bbc77882901ae2b9654503b7611b28f3)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/a1016fd4a55f176fcc2eae05052a61ad7d5a142b
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
        git checkout a1016fd4a55f176fcc2eae05052a61ad7d5a142b
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> net/sctp/protocol.c:894:11: error: no member named 'local_ip6' in 'struct udp_port_cfg'; did you mean 'local_ip'?
           udp_conf.local_ip6 = in6addr_any;
                    ^~~~~~~~~
                    local_ip
   include/net/udp_tunnel.h:18:19: note: 'local_ip' declared here
                   struct in_addr          local_ip;
                                           ^
>> net/sctp/protocol.c:894:21: error: assigning to 'struct in_addr' from incompatible type 'const struct in6_addr'
           udp_conf.local_ip6 = in6addr_any;
                              ^ ~~~~~~~~~~~
   2 errors generated.

vim +894 net/sctp/protocol.c

a330bee1c278f86 Xin Long 2020-09-29  870  
140bb5309cf4095 Xin Long 2020-09-29  871  int sctp_udp_sock_start(struct net *net)
140bb5309cf4095 Xin Long 2020-09-29  872  {
140bb5309cf4095 Xin Long 2020-09-29  873  	struct udp_tunnel_sock_cfg tuncfg = {NULL};
140bb5309cf4095 Xin Long 2020-09-29  874  	struct udp_port_cfg udp_conf = {0};
140bb5309cf4095 Xin Long 2020-09-29  875  	struct socket *sock;
140bb5309cf4095 Xin Long 2020-09-29  876  	int err;
140bb5309cf4095 Xin Long 2020-09-29  877  
140bb5309cf4095 Xin Long 2020-09-29  878  	udp_conf.family = AF_INET;
140bb5309cf4095 Xin Long 2020-09-29  879  	udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
140bb5309cf4095 Xin Long 2020-09-29  880  	udp_conf.local_udp_port = htons(net->sctp.udp_port);
140bb5309cf4095 Xin Long 2020-09-29  881  	err = udp_sock_create(net, &udp_conf, &sock);
140bb5309cf4095 Xin Long 2020-09-29  882  	if (err)
140bb5309cf4095 Xin Long 2020-09-29  883  		return err;
140bb5309cf4095 Xin Long 2020-09-29  884  
140bb5309cf4095 Xin Long 2020-09-29  885  	tuncfg.encap_type = 1;
140bb5309cf4095 Xin Long 2020-09-29  886  	tuncfg.encap_rcv = sctp_udp_rcv;
a330bee1c278f86 Xin Long 2020-09-29  887  	tuncfg.encap_err_lookup = sctp_udp_err_lookup;
140bb5309cf4095 Xin Long 2020-09-29  888  	setup_udp_tunnel_sock(net, sock, &tuncfg);
140bb5309cf4095 Xin Long 2020-09-29  889  	net->sctp.udp4_sock = sock->sk;
140bb5309cf4095 Xin Long 2020-09-29  890  
cff8956126170d6 Xin Long 2020-09-29  891  	memset(&udp_conf, 0, sizeof(udp_conf));
cff8956126170d6 Xin Long 2020-09-29  892  
cff8956126170d6 Xin Long 2020-09-29  893  	udp_conf.family = AF_INET6;
cff8956126170d6 Xin Long 2020-09-29 @894  	udp_conf.local_ip6 = in6addr_any;
cff8956126170d6 Xin Long 2020-09-29  895  	udp_conf.local_udp_port = htons(net->sctp.udp_port);
cff8956126170d6 Xin Long 2020-09-29  896  	udp_conf.use_udp6_rx_checksums = true;
cff8956126170d6 Xin Long 2020-09-29  897  	udp_conf.ipv6_v6only = true;
cff8956126170d6 Xin Long 2020-09-29  898  	err = udp_sock_create(net, &udp_conf, &sock);
cff8956126170d6 Xin Long 2020-09-29  899  	if (err) {
cff8956126170d6 Xin Long 2020-09-29  900  		udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket);
cff8956126170d6 Xin Long 2020-09-29  901  		net->sctp.udp4_sock = NULL;
cff8956126170d6 Xin Long 2020-09-29  902  		return err;
cff8956126170d6 Xin Long 2020-09-29  903  	}
cff8956126170d6 Xin Long 2020-09-29  904  
cff8956126170d6 Xin Long 2020-09-29  905  	tuncfg.encap_type = 1;
cff8956126170d6 Xin Long 2020-09-29  906  	tuncfg.encap_rcv = sctp_udp_rcv;
a330bee1c278f86 Xin Long 2020-09-29  907  	tuncfg.encap_err_lookup = sctp_udp_err_lookup;
cff8956126170d6 Xin Long 2020-09-29  908  	setup_udp_tunnel_sock(net, sock, &tuncfg);
cff8956126170d6 Xin Long 2020-09-29  909  	net->sctp.udp6_sock = sock->sk;
cff8956126170d6 Xin Long 2020-09-29  910  
140bb5309cf4095 Xin Long 2020-09-29  911  	return 0;
140bb5309cf4095 Xin Long 2020-09-29  912  }
140bb5309cf4095 Xin Long 2020-09-29  913  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Xin Long Sept. 30, 2020, 6:26 a.m. UTC | #3
On Wed, Sep 30, 2020 at 12:26 AM kernel test robot <lkp@intel.com> wrote:
>
> Hi Xin,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on net-next/master]
>
> url:    https://github.com/0day-ci/linux/commits/Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 280095713ce244e8dbdfb059cdca695baa72230a
> config: ia64-randconfig-r014-20200929 (attached as .config)
> compiler: ia64-linux-gcc (GCC) 9.3.0
> reproduce (this is a W=1 build):
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # https://github.com/0day-ci/linux/commit/a1016fd4a55f176fcc2eae05052a61ad7d5a142b
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Xin-Long/sctp-Implement-RFC6951-UDP-Encapsulation-of-SCTP/20200929-215159
>         git checkout a1016fd4a55f176fcc2eae05052a61ad7d5a142b
>         # save the attached .config to linux build tree
>         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=ia64
>
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
>
> All errors (new ones prefixed by >>):
>
>    net/sctp/protocol.c: In function 'sctp_udp_sock_start':
> >> net/sctp/protocol.c:894:11: error: 'struct udp_port_cfg' has no member named 'local_ip6'; did you mean 'local_ip'?
>      894 |  udp_conf.local_ip6 = in6addr_any;
>          |           ^~~~~~~~~
>          |           local_ip
>
> vim +894 net/sctp/protocol.c
>
> a330bee1c278f8 Xin Long 2020-09-29  870
> 140bb5309cf409 Xin Long 2020-09-29  871  int sctp_udp_sock_start(struct net *net)
> 140bb5309cf409 Xin Long 2020-09-29  872  {
> 140bb5309cf409 Xin Long 2020-09-29  873         struct udp_tunnel_sock_cfg tuncfg = {NULL};
> 140bb5309cf409 Xin Long 2020-09-29  874         struct udp_port_cfg udp_conf = {0};
> 140bb5309cf409 Xin Long 2020-09-29  875         struct socket *sock;
> 140bb5309cf409 Xin Long 2020-09-29  876         int err;
> 140bb5309cf409 Xin Long 2020-09-29  877
> 140bb5309cf409 Xin Long 2020-09-29  878         udp_conf.family = AF_INET;
> 140bb5309cf409 Xin Long 2020-09-29  879         udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
> 140bb5309cf409 Xin Long 2020-09-29  880         udp_conf.local_udp_port = htons(net->sctp.udp_port);
> 140bb5309cf409 Xin Long 2020-09-29  881         err = udp_sock_create(net, &udp_conf, &sock);
> 140bb5309cf409 Xin Long 2020-09-29  882         if (err)
> 140bb5309cf409 Xin Long 2020-09-29  883                 return err;
> 140bb5309cf409 Xin Long 2020-09-29  884
> 140bb5309cf409 Xin Long 2020-09-29  885         tuncfg.encap_type = 1;
> 140bb5309cf409 Xin Long 2020-09-29  886         tuncfg.encap_rcv = sctp_udp_rcv;
> a330bee1c278f8 Xin Long 2020-09-29  887         tuncfg.encap_err_lookup = sctp_udp_err_lookup;
> 140bb5309cf409 Xin Long 2020-09-29  888         setup_udp_tunnel_sock(net, sock, &tuncfg);
> 140bb5309cf409 Xin Long 2020-09-29  889         net->sctp.udp4_sock = sock->sk;
> 140bb5309cf409 Xin Long 2020-09-29  890
> cff8956126170d Xin Long 2020-09-29  891         memset(&udp_conf, 0, sizeof(udp_conf));
> cff8956126170d Xin Long 2020-09-29  892
> cff8956126170d Xin Long 2020-09-29  893         udp_conf.family = AF_INET6;
> cff8956126170d Xin Long 2020-09-29 @894         udp_conf.local_ip6 = in6addr_any;
> cff8956126170d Xin Long 2020-09-29  895         udp_conf.local_udp_port = htons(net->sctp.udp_port);
> cff8956126170d Xin Long 2020-09-29  896         udp_conf.use_udp6_rx_checksums = true;
> cff8956126170d Xin Long 2020-09-29  897         udp_conf.ipv6_v6only = true;
> cff8956126170d Xin Long 2020-09-29  898         err = udp_sock_create(net, &udp_conf, &sock);
> cff8956126170d Xin Long 2020-09-29  899         if (err) {
> cff8956126170d Xin Long 2020-09-29  900                 udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket);
> cff8956126170d Xin Long 2020-09-29  901                 net->sctp.udp4_sock = NULL;
> cff8956126170d Xin Long 2020-09-29  902                 return err;
> cff8956126170d Xin Long 2020-09-29  903         }
> cff8956126170d Xin Long 2020-09-29  904
> cff8956126170d Xin Long 2020-09-29  905         tuncfg.encap_type = 1;
> cff8956126170d Xin Long 2020-09-29  906         tuncfg.encap_rcv = sctp_udp_rcv;
> a330bee1c278f8 Xin Long 2020-09-29  907         tuncfg.encap_err_lookup = sctp_udp_err_lookup;
> cff8956126170d Xin Long 2020-09-29  908         setup_udp_tunnel_sock(net, sock, &tuncfg);
> cff8956126170d Xin Long 2020-09-29  909         net->sctp.udp6_sock = sock->sk;
#if IS_ENABLED(CONFIG_IPV6) is needed for this part.

Thanks.
> cff8956126170d Xin Long 2020-09-29  910
> 140bb5309cf409 Xin Long 2020-09-29  911         return 0;
> 140bb5309cf409 Xin Long 2020-09-29  912  }
> 140bb5309cf409 Xin Long 2020-09-29  913
>
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/net/sctp/output.c b/net/sctp/output.c
index fb16500..6614c9f 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -514,8 +514,8 @@  static int sctp_packet_pack(struct sctp_packet *packet,
 	if (sctp_checksum_disable)
 		return 1;
 
-	if (!(skb_dst(head)->dev->features & NETIF_F_SCTP_CRC) ||
-	    dst_xfrm(skb_dst(head)) || packet->ipfragok) {
+	if (!(tp->dst->dev->features & NETIF_F_SCTP_CRC) ||
+	    dst_xfrm(tp->dst) || packet->ipfragok || tp->encap_port) {
 		struct sctphdr *sh =
 			(struct sctphdr *)skb_transport_header(head);
 
@@ -542,7 +542,6 @@  int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
 	struct sctp_association *asoc = tp->asoc;
 	struct sctp_chunk *chunk, *tmp;
 	int pkt_count, gso = 0;
-	struct dst_entry *dst;
 	struct sk_buff *head;
 	struct sctphdr *sh;
 	struct sock *sk;
@@ -579,13 +578,11 @@  int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
 	sh->checksum = 0;
 
 	/* drop packet if no dst */
-	dst = dst_clone(tp->dst);
-	if (!dst) {
+	if (!tp->dst) {
 		IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
 		kfree_skb(head);
 		goto out;
 	}
-	skb_dst_set(head, dst);
 
 	rcu_read_lock();
 	if (__sk_dst_get(sk) != tp->dst) {
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index c73fd5f..6606a63 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -44,6 +44,7 @@ 
 #include <net/addrconf.h>
 #include <net/inet_common.h>
 #include <net/inet_ecn.h>
+#include <net/udp_tunnel.h>
 
 #define MAX_SCTP_PORT_HASH_ENTRIES (64 * 1024)
 
@@ -1053,25 +1054,46 @@  static int sctp_inet_supported_addrs(const struct sctp_sock *opt,
 }
 
 /* Wrapper routine that calls the ip transmit routine. */
-static inline int sctp_v4_xmit(struct sk_buff *skb,
-			       struct sctp_transport *transport)
+static inline int sctp_v4_xmit(struct sk_buff *skb, struct sctp_transport *t)
 {
-	struct inet_sock *inet = inet_sk(skb->sk);
+	struct dst_entry *dst = dst_clone(t->dst);
+	struct flowi4 *fl4 = &t->fl.u.ip4;
+	struct sock *sk = skb->sk;
+	struct inet_sock *inet = inet_sk(sk);
+	struct net *net = sock_net(sk);
 	__u8 dscp = inet->tos;
+	__be16 df = 0;
 
 	pr_debug("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", __func__, skb,
-		 skb->len, &transport->fl.u.ip4.saddr,
-		 &transport->fl.u.ip4.daddr);
+		 skb->len, &fl4->saddr, &fl4->daddr);
+
+	if (t->dscp & SCTP_DSCP_SET_MASK)
+		dscp = t->dscp & SCTP_DSCP_VAL_MASK;
 
-	if (transport->dscp & SCTP_DSCP_SET_MASK)
-		dscp = transport->dscp & SCTP_DSCP_VAL_MASK;
+	inet->pmtudisc = t->param_flags & SPP_PMTUD_ENABLE ? IP_PMTUDISC_DO
+							   : IP_PMTUDISC_DONT;
+	SCTP_INC_STATS(net, SCTP_MIB_OUTSCTPPACKS);
 
-	inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ?
-			 IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
+	if (!t->encap_port || !net->sctp.udp_port) {
+		skb_dst_set(skb, dst);
+		return __ip_queue_xmit(sk, skb, &t->fl, dscp);
+	}
+
+	if (skb_is_gso(skb))
+		skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
 
-	SCTP_INC_STATS(sock_net(&inet->sk), SCTP_MIB_OUTSCTPPACKS);
+	if (ip_dont_fragment(sk, dst) && !skb->ignore_df)
+		df = htons(IP_DF);
 
-	return __ip_queue_xmit(&inet->sk, skb, &transport->fl, dscp);
+	skb->encapsulation = 1;
+	skb_reset_inner_mac_header(skb);
+	skb_reset_inner_transport_header(skb);
+	skb_set_inner_ipproto(skb, IPPROTO_SCTP);
+	udp_tunnel_xmit_skb((struct rtable *)dst, sk, skb, fl4->saddr,
+			    fl4->daddr, dscp, ip4_dst_hoplimit(dst), df,
+			    htons(net->sctp.udp_port), htons(t->encap_port),
+			    false, false);
+	return 0;
 }
 
 static struct sctp_af sctp_af_inet;