sctp: add encap_err_lookup for udp encap socks
[linux-2.6-microblaze.git] / net / sctp / protocol.c
index 2583323..4d12a0c 100644 (file)
@@ -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)
 
@@ -840,6 +841,90 @@ static int sctp_ctl_sock_init(struct net *net)
        return 0;
 }
 
+static int sctp_udp_rcv(struct sock *sk, struct sk_buff *skb)
+{
+       skb_set_transport_header(skb, sizeof(struct udphdr));
+       sctp_rcv(skb);
+       return 0;
+}
+
+static int sctp_udp_err_lookup(struct sock *sk, struct sk_buff *skb)
+{
+       struct sctp_association *asoc;
+       struct sctp_transport *t;
+       int family;
+
+       skb->transport_header += sizeof(struct udphdr);
+       family = (ip_hdr(skb)->version == 4) ? AF_INET : AF_INET6;
+       sk = sctp_err_lookup(dev_net(skb->dev), family, skb, sctp_hdr(skb),
+                            &asoc, &t);
+       if (!sk)
+               return -ENOENT;
+
+       sctp_err_finish(sk, t);
+       return 0;
+}
+
+int sctp_udp_sock_start(struct net *net)
+{
+       struct udp_tunnel_sock_cfg tuncfg = {NULL};
+       struct udp_port_cfg udp_conf = {0};
+       struct socket *sock;
+       int err;
+
+       udp_conf.family = AF_INET;
+       udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
+       udp_conf.local_udp_port = htons(net->sctp.udp_port);
+       err = udp_sock_create(net, &udp_conf, &sock);
+       if (err) {
+               pr_err("Failed to create the SCTP UDP tunneling v4 sock\n");
+               return err;
+       }
+
+       tuncfg.encap_type = 1;
+       tuncfg.encap_rcv = sctp_udp_rcv;
+       tuncfg.encap_err_lookup = sctp_udp_err_lookup;
+       setup_udp_tunnel_sock(net, sock, &tuncfg);
+       net->sctp.udp4_sock = sock->sk;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       memset(&udp_conf, 0, sizeof(udp_conf));
+
+       udp_conf.family = AF_INET6;
+       udp_conf.local_ip6 = in6addr_any;
+       udp_conf.local_udp_port = htons(net->sctp.udp_port);
+       udp_conf.use_udp6_rx_checksums = true;
+       udp_conf.ipv6_v6only = true;
+       err = udp_sock_create(net, &udp_conf, &sock);
+       if (err) {
+               pr_err("Failed to create the SCTP UDP tunneling v6 sock\n");
+               udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket);
+               net->sctp.udp4_sock = NULL;
+               return err;
+       }
+
+       tuncfg.encap_type = 1;
+       tuncfg.encap_rcv = sctp_udp_rcv;
+       tuncfg.encap_err_lookup = sctp_udp_err_lookup;
+       setup_udp_tunnel_sock(net, sock, &tuncfg);
+       net->sctp.udp6_sock = sock->sk;
+#endif
+
+       return 0;
+}
+
+void sctp_udp_sock_stop(struct net *net)
+{
+       if (net->sctp.udp4_sock) {
+               udp_tunnel_sock_release(net->sctp.udp4_sock->sk_socket);
+               net->sctp.udp4_sock = NULL;
+       }
+       if (net->sctp.udp6_sock) {
+               udp_tunnel_sock_release(net->sctp.udp6_sock->sk_socket);
+               net->sctp.udp6_sock = NULL;
+       }
+}
+
 /* Register address family specific functions. */
 int sctp_register_af(struct sctp_af *af)
 {
@@ -1271,6 +1356,9 @@ static int __net_init sctp_defaults_init(struct net *net)
        /* Enable ECN by default. */
        net->sctp.ecn_enable = 1;
 
+       /* Set UDP tunneling listening port to 0 by default */
+       net->sctp.udp_port = 0;
+
        /* Set SCOPE policy to enabled */
        net->sctp.scope_policy = SCTP_SCOPE_POLICY_ENABLE;