Merge tag 'selinux-pr-20180403' of git://git.kernel.org/pub/scm/linux/kernel/git...
[muen/linux.git] / net / sctp / protocol.c
index a24cde236330dec9c551c43452ccb435ad21580e..d685f84567624aedfb043c9ba56d3977849f16e5 100644 (file)
@@ -187,6 +187,45 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp,
        return error;
 }
 
        return error;
 }
 
+/* Copy over any ip options */
+static void sctp_v4_copy_ip_options(struct sock *sk, struct sock *newsk)
+{
+       struct inet_sock *newinet, *inet = inet_sk(sk);
+       struct ip_options_rcu *inet_opt, *newopt = NULL;
+
+       newinet = inet_sk(newsk);
+
+       rcu_read_lock();
+       inet_opt = rcu_dereference(inet->inet_opt);
+       if (inet_opt) {
+               newopt = sock_kmalloc(newsk, sizeof(*inet_opt) +
+                                     inet_opt->opt.optlen, GFP_ATOMIC);
+               if (newopt)
+                       memcpy(newopt, inet_opt, sizeof(*inet_opt) +
+                              inet_opt->opt.optlen);
+               else
+                       pr_err("%s: Failed to copy ip options\n", __func__);
+       }
+       RCU_INIT_POINTER(newinet->inet_opt, newopt);
+       rcu_read_unlock();
+}
+
+/* Account for the IP options */
+static int sctp_v4_ip_options_len(struct sock *sk)
+{
+       struct inet_sock *inet = inet_sk(sk);
+       struct ip_options_rcu *inet_opt;
+       int len = 0;
+
+       rcu_read_lock();
+       inet_opt = rcu_dereference(inet->inet_opt);
+       if (inet_opt)
+               len = inet_opt->opt.optlen;
+
+       rcu_read_unlock();
+       return len;
+}
+
 /* Initialize a sctp_addr from in incoming skb.  */
 static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
                             int is_saddr)
 /* Initialize a sctp_addr from in incoming skb.  */
 static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
                             int is_saddr)
@@ -538,6 +577,8 @@ static struct sock *sctp_v4_create_accept_sk(struct sock *sk,
        sctp_copy_sock(newsk, sk, asoc);
        sock_reset_flag(newsk, SOCK_ZAPPED);
 
        sctp_copy_sock(newsk, sk, asoc);
        sock_reset_flag(newsk, SOCK_ZAPPED);
 
+       sctp_v4_copy_ip_options(sk, newsk);
+
        newinet = inet_sk(newsk);
 
        newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
        newinet = inet_sk(newsk);
 
        newinet->inet_daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
@@ -956,6 +997,7 @@ static struct sctp_pf sctp_pf_inet = {
        .addr_to_user  = sctp_v4_addr_to_user,
        .to_sk_saddr   = sctp_v4_to_sk_saddr,
        .to_sk_daddr   = sctp_v4_to_sk_daddr,
        .addr_to_user  = sctp_v4_addr_to_user,
        .to_sk_saddr   = sctp_v4_to_sk_saddr,
        .to_sk_daddr   = sctp_v4_to_sk_daddr,
+       .copy_ip_options = sctp_v4_copy_ip_options,
        .af            = &sctp_af_inet
 };
 
        .af            = &sctp_af_inet
 };
 
@@ -1040,6 +1082,7 @@ static struct sctp_af sctp_af_inet = {
        .ecn_capable       = sctp_v4_ecn_capable,
        .net_header_len    = sizeof(struct iphdr),
        .sockaddr_len      = sizeof(struct sockaddr_in),
        .ecn_capable       = sctp_v4_ecn_capable,
        .net_header_len    = sizeof(struct iphdr),
        .sockaddr_len      = sizeof(struct sockaddr_in),
+       .ip_options_len    = sctp_v4_ip_options_len,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_ip_setsockopt,
        .compat_getsockopt = compat_ip_getsockopt,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_ip_setsockopt,
        .compat_getsockopt = compat_ip_getsockopt,