sctp: Inherit all socket options from parent correctly.
authorVlad Yasevich <vladislav.yasevich@hp.com>
Fri, 13 Feb 2009 08:33:44 +0000 (08:33 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Feb 2009 08:03:11 +0000 (00:03 -0800)
During peeloff/accept() sctp needs to save the parent socket state
into the new socket so that any options set on the parent are
inherited by the child socket.  This was found when the
parent/listener socket issues SO_BINDTODEVICE, but the
data was misrouted after a route cache flush.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sctp.h
net/sctp/ipv6.c
net/sctp/protocol.c
net/sctp/socket.c

index bbb7742195b0df31e4498b19e909e7a97a7b47e7..9e226be3be69d45c25a2cc83a86033645ff537c4 100644 (file)
@@ -138,6 +138,8 @@ void sctp_write_space(struct sock *sk);
 unsigned int sctp_poll(struct file *file, struct socket *sock,
                poll_table *wait);
 void sctp_sock_rfree(struct sk_buff *skb);
+void sctp_copy_sock(struct sock *newsk, struct sock *sk,
+                   struct sctp_association *asoc);
 extern struct percpu_counter sctp_sockets_allocated;
 
 /*
index 786227566696354bce2ef9e6d902b96875fe2ea6..a63de3f7f1859ccf839893f69b4b7db4ee84b431 100644 (file)
@@ -627,9 +627,7 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
 static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
                                             struct sctp_association *asoc)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct sock *newsk;
-       struct inet_sock *newinet;
        struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
        struct sctp6_sock *newsctp6sk;
 
@@ -639,17 +637,7 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
 
        sock_init_data(NULL, newsk);
 
-       newsk->sk_type = SOCK_STREAM;
-
-       newsk->sk_prot = sk->sk_prot;
-       newsk->sk_no_check = sk->sk_no_check;
-       newsk->sk_reuse = sk->sk_reuse;
-
-       newsk->sk_destruct = inet_sock_destruct;
-       newsk->sk_family = PF_INET6;
-       newsk->sk_protocol = IPPROTO_SCTP;
-       newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
-       newsk->sk_shutdown = sk->sk_shutdown;
+       sctp_copy_sock(newsk, sk, asoc);
        sock_reset_flag(sk, SOCK_ZAPPED);
 
        newsctp6sk = (struct sctp6_sock *)newsk;
@@ -657,7 +645,6 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
 
        sctp_sk(newsk)->v4mapped = sctp_sk(sk)->v4mapped;
 
-       newinet = inet_sk(newsk);
        newnp = inet6_sk(newsk);
 
        memcpy(newnp, np, sizeof(struct ipv6_pinfo));
@@ -665,26 +652,8 @@ static struct sock *sctp_v6_create_accept_sk(struct sock *sk,
        /* Initialize sk's sport, dport, rcv_saddr and daddr for getsockname()
         * and getpeername().
         */
-       newinet->sport = inet->sport;
-       newnp->saddr = np->saddr;
-       newnp->rcv_saddr = np->rcv_saddr;
-       newinet->dport = htons(asoc->peer.port);
        sctp_v6_to_sk_daddr(&asoc->peer.primary_addr, newsk);
 
-       /* Init the ipv4 part of the socket since we can have sockets
-        * using v6 API for ipv4.
-        */
-       newinet->uc_ttl = -1;
-       newinet->mc_loop = 1;
-       newinet->mc_ttl = 1;
-       newinet->mc_index = 0;
-       newinet->mc_list = NULL;
-
-       if (ipv4_config.no_pmtu_disc)
-               newinet->pmtudisc = IP_PMTUDISC_DONT;
-       else
-               newinet->pmtudisc = IP_PMTUDISC_WANT;
-
        sk_refcnt_debug_inc(newsk);
 
        if (newsk->sk_prot->init(newsk)) {
index cc0b592698f968702e065648e42ba37f937e7618..c1e316ee7155d52abad10d8ec40b926a2d3c6e42 100644 (file)
@@ -589,46 +589,21 @@ static int sctp_v4_is_ce(const struct sk_buff *skb)
 static struct sock *sctp_v4_create_accept_sk(struct sock *sk,
                                             struct sctp_association *asoc)
 {
-       struct inet_sock *inet = inet_sk(sk);
-       struct inet_sock *newinet;
        struct sock *newsk = sk_alloc(sock_net(sk), PF_INET, GFP_KERNEL,
                        sk->sk_prot);
+       struct inet_sock *newinet;
 
        if (!newsk)
                goto out;
 
        sock_init_data(NULL, newsk);
 
-       newsk->sk_type = SOCK_STREAM;
-
-       newsk->sk_no_check = sk->sk_no_check;
-       newsk->sk_reuse = sk->sk_reuse;
-       newsk->sk_shutdown = sk->sk_shutdown;
-
-       newsk->sk_destruct = inet_sock_destruct;
-       newsk->sk_family = PF_INET;
-       newsk->sk_protocol = IPPROTO_SCTP;
-       newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+       sctp_copy_sock(newsk, sk, asoc);
        sock_reset_flag(newsk, SOCK_ZAPPED);
 
        newinet = inet_sk(newsk);
 
-       /* Initialize sk's sport, dport, rcv_saddr and daddr for
-        * getsockname() and getpeername()
-        */
-       newinet->sport = inet->sport;
-       newinet->saddr = inet->saddr;
-       newinet->rcv_saddr = inet->rcv_saddr;
-       newinet->dport = htons(asoc->peer.port);
        newinet->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
-       newinet->pmtudisc = inet->pmtudisc;
-       newinet->id = asoc->next_tsn ^ jiffies;
-
-       newinet->uc_ttl = -1;
-       newinet->mc_loop = 1;
-       newinet->mc_ttl = 1;
-       newinet->mc_index = 0;
-       newinet->mc_list = NULL;
 
        sk_refcnt_debug_inc(newsk);
 
index ff0a8f88de04bd275f3b16b921ed88d846407ca4..dea864f5de54cd1fab32036ad4e89350f6e32a12 100644 (file)
@@ -3939,7 +3939,6 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
 {
        struct sock *sk = asoc->base.sk;
        struct socket *sock;
-       struct inet_sock *inetsk;
        struct sctp_af *af;
        int err = 0;
 
@@ -3954,18 +3953,18 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
        if (err < 0)
                return err;
 
-       /* Populate the fields of the newsk from the oldsk and migrate the
-        * asoc to the newsk.
-        */
-       sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
+       sctp_copy_sock(sock->sk, sk, asoc);
 
        /* Make peeled-off sockets more like 1-1 accepted sockets.
         * Set the daddr and initialize id to something more random
         */
        af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
        af->to_sk_daddr(&asoc->peer.primary_addr, sk);
-       inetsk = inet_sk(sock->sk);
-       inetsk->id = asoc->next_tsn ^ jiffies;
+
+       /* Populate the fields of the newsk from the oldsk and migrate the
+        * asoc to the newsk.
+        */
+       sctp_sock_migrate(sk, sock->sk, asoc, SCTP_SOCKET_UDP_HIGH_BANDWIDTH);
 
        *sockp = sock;
 
@@ -6700,6 +6699,48 @@ done:
        sctp_skb_set_owner_r(skb, sk);
 }
 
+void sctp_copy_sock(struct sock *newsk, struct sock *sk,
+                   struct sctp_association *asoc)
+{
+       struct inet_sock *inet = inet_sk(sk);
+       struct inet_sock *newinet = inet_sk(newsk);
+
+       newsk->sk_type = sk->sk_type;
+       newsk->sk_bound_dev_if = sk->sk_bound_dev_if;
+       newsk->sk_flags = sk->sk_flags;
+       newsk->sk_no_check = sk->sk_no_check;
+       newsk->sk_reuse = sk->sk_reuse;
+
+       newsk->sk_shutdown = sk->sk_shutdown;
+       newsk->sk_destruct = inet_sock_destruct;
+       newsk->sk_family = sk->sk_family;
+       newsk->sk_protocol = IPPROTO_SCTP;
+       newsk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+       newsk->sk_sndbuf = sk->sk_sndbuf;
+       newsk->sk_rcvbuf = sk->sk_rcvbuf;
+       newsk->sk_lingertime = sk->sk_lingertime;
+       newsk->sk_rcvtimeo = sk->sk_rcvtimeo;
+       newsk->sk_sndtimeo = sk->sk_sndtimeo;
+
+       newinet = inet_sk(newsk);
+
+       /* Initialize sk's sport, dport, rcv_saddr and daddr for
+        * getsockname() and getpeername()
+        */
+       newinet->sport = inet->sport;
+       newinet->saddr = inet->saddr;
+       newinet->rcv_saddr = inet->rcv_saddr;
+       newinet->dport = htons(asoc->peer.port);
+       newinet->pmtudisc = inet->pmtudisc;
+       newinet->id = asoc->next_tsn ^ jiffies;
+
+       newinet->uc_ttl = inet->uc_ttl;
+       newinet->mc_loop = 1;
+       newinet->mc_ttl = 1;
+       newinet->mc_index = 0;
+       newinet->mc_list = NULL;
+}
+
 /* Populate the fields of the newsk from the oldsk and migrate the assoc
  * and its messages to the newsk.
  */