diff options
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r-- | net/sctp/socket.c | 1706 |
1 files changed, 871 insertions, 835 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 1b56fc440606..83628c347744 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -93,6 +93,7 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, static unsigned long sctp_memory_pressure; static atomic_long_t sctp_memory_allocated; +static DEFINE_PER_CPU(int, sctp_memory_per_cpu_fw_alloc); struct percpu_counter sctp_sockets_allocated; static void sctp_enter_memory_pressure(struct sock *sk) @@ -147,29 +148,44 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk) skb_orphan(chunk->skb); } +#define traverse_and_process() \ +do { \ + msg = chunk->msg; \ + if (msg == prev_msg) \ + continue; \ + list_for_each_entry(c, &msg->chunks, frag_list) { \ + if ((clear && asoc->base.sk == c->skb->sk) || \ + (!clear && asoc->base.sk != c->skb->sk)) \ + cb(c); \ + } \ + prev_msg = msg; \ +} while (0) + static void sctp_for_each_tx_datachunk(struct sctp_association *asoc, + bool clear, void (*cb)(struct sctp_chunk *)) { + struct sctp_datamsg *msg, *prev_msg = NULL; struct sctp_outq *q = &asoc->outqueue; + struct sctp_chunk *chunk, *c; struct sctp_transport *t; - struct sctp_chunk *chunk; list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) list_for_each_entry(chunk, &t->transmitted, transmitted_list) - cb(chunk); + traverse_and_process(); list_for_each_entry(chunk, &q->retransmit, transmitted_list) - cb(chunk); + traverse_and_process(); list_for_each_entry(chunk, &q->sacked, transmitted_list) - cb(chunk); + traverse_and_process(); list_for_each_entry(chunk, &q->abandoned, transmitted_list) - cb(chunk); + traverse_and_process(); list_for_each_entry(chunk, &q->out_chunk_list, list) - cb(chunk); + traverse_and_process(); } static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk, @@ -342,6 +358,18 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, return af; } +static void sctp_auto_asconf_init(struct sctp_sock *sp) +{ + struct net *net = sock_net(&sp->inet.sk); + + if (net->sctp.default_auto_asconf) { + spin_lock(&net->sctp.addr_wq_lock); + list_add_tail(&sp->auto_asconf_list, &net->sctp.auto_asconf_splist); + spin_unlock(&net->sctp.addr_wq_lock); + sp->do_auto_asconf = 1; + } +} + /* Bind a local address either to an endpoint or to an association. */ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) { @@ -403,8 +431,10 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) return -EADDRINUSE; /* Refresh ephemeral port. */ - if (!bp->port) + if (!bp->port) { bp->port = inet_sk(sk)->inet_num; + sctp_auto_asconf_init(sp); + } /* Add the address to the bind address list. * Use GFP_ATOMIC since BHs will be disabled. @@ -957,42 +987,33 @@ int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw) * it. * * sk The sk of the socket - * addrs The pointer to the addresses in user land + * addrs The pointer to the addresses * addrssize Size of the addrs buffer * op Operation to perform (add or remove, see the flags of * sctp_bindx) * * Returns 0 if ok, <0 errno code on error. */ -static int sctp_setsockopt_bindx(struct sock *sk, - struct sockaddr __user *addrs, +static int sctp_setsockopt_bindx(struct sock *sk, struct sockaddr *addrs, int addrs_size, int op) { - struct sockaddr *kaddrs; int err; int addrcnt = 0; int walk_size = 0; struct sockaddr *sa_addr; - void *addr_buf; + void *addr_buf = addrs; struct sctp_af *af; pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n", - __func__, sk, addrs, addrs_size, op); + __func__, sk, addr_buf, addrs_size, op); if (unlikely(addrs_size <= 0)) return -EINVAL; - kaddrs = memdup_user(addrs, addrs_size); - if (IS_ERR(kaddrs)) - return PTR_ERR(kaddrs); - /* Walk through the addrs buffer and count the number of addresses. */ - addr_buf = kaddrs; while (walk_size < addrs_size) { - if (walk_size + sizeof(sa_family_t) > addrs_size) { - kfree(kaddrs); + if (walk_size + sizeof(sa_family_t) > addrs_size) return -EINVAL; - } sa_addr = addr_buf; af = sctp_get_af_specific(sa_addr->sa_family); @@ -1000,10 +1021,8 @@ static int sctp_setsockopt_bindx(struct sock *sk, /* If the address family is not supported or if this address * causes the address buffer to overflow return EINVAL. */ - if (!af || (walk_size + af->sockaddr_len) > addrs_size) { - kfree(kaddrs); + if (!af || (walk_size + af->sockaddr_len) > addrs_size) return -EINVAL; - } addrcnt++; addr_buf += af->sockaddr_len; walk_size += af->sockaddr_len; @@ -1014,31 +1033,32 @@ static int sctp_setsockopt_bindx(struct sock *sk, case SCTP_BINDX_ADD_ADDR: /* Allow security module to validate bindx addresses. */ err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD, - (struct sockaddr *)kaddrs, - addrs_size); + addrs, addrs_size); if (err) - goto out; - err = sctp_bindx_add(sk, kaddrs, addrcnt); + return err; + err = sctp_bindx_add(sk, addrs, addrcnt); if (err) - goto out; - err = sctp_send_asconf_add_ip(sk, kaddrs, addrcnt); - break; - + return err; + return sctp_send_asconf_add_ip(sk, addrs, addrcnt); case SCTP_BINDX_REM_ADDR: - err = sctp_bindx_rem(sk, kaddrs, addrcnt); + err = sctp_bindx_rem(sk, addrs, addrcnt); if (err) - goto out; - err = sctp_send_asconf_del_ip(sk, kaddrs, addrcnt); - break; + return err; + return sctp_send_asconf_del_ip(sk, addrs, addrcnt); default: - err = -EINVAL; - break; + return -EINVAL; } +} -out: - kfree(kaddrs); +static int sctp_bind_add(struct sock *sk, struct sockaddr *addrs, + int addrlen) +{ + int err; + lock_sock(sk); + err = sctp_setsockopt_bindx(sk, addrs, addrlen, SCTP_BINDX_ADD_ADDR); + release_sock(sk); return err; } @@ -1281,36 +1301,29 @@ out_free: * it. * * sk The sk of the socket - * addrs The pointer to the addresses in user land + * addrs The pointer to the addresses * addrssize Size of the addrs buffer * * Returns >=0 if ok, <0 errno code on error. */ -static int __sctp_setsockopt_connectx(struct sock *sk, - struct sockaddr __user *addrs, - int addrs_size, - sctp_assoc_t *assoc_id) +static int __sctp_setsockopt_connectx(struct sock *sk, struct sockaddr *kaddrs, + int addrs_size, sctp_assoc_t *assoc_id) { - struct sockaddr *kaddrs; int err = 0, flags = 0; pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n", - __func__, sk, addrs, addrs_size); + __func__, sk, kaddrs, addrs_size); /* make sure the 1st addr's sa_family is accessible later */ if (unlikely(addrs_size < sizeof(sa_family_t))) return -EINVAL; - kaddrs = memdup_user(addrs, addrs_size); - if (IS_ERR(kaddrs)) - return PTR_ERR(kaddrs); - /* Allow security module to validate connectx addresses. */ err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_CONNECTX, (struct sockaddr *)kaddrs, addrs_size); if (err) - goto out_free; + return err; /* in-kernel sockets don't generally have a file allocated to them * if all they do is call sock_create_kern(). @@ -1318,12 +1331,7 @@ static int __sctp_setsockopt_connectx(struct sock *sk, if (sk->sk_socket->file) flags = sk->sk_socket->file->f_flags; - err = __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id); - -out_free: - kfree(kaddrs); - - return err; + return __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id); } /* @@ -1331,10 +1339,10 @@ out_free: * to the option that doesn't provide association id. */ static int sctp_setsockopt_connectx_old(struct sock *sk, - struct sockaddr __user *addrs, + struct sockaddr *kaddrs, int addrs_size) { - return __sctp_setsockopt_connectx(sk, addrs, addrs_size, NULL); + return __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, NULL); } /* @@ -1344,13 +1352,13 @@ static int sctp_setsockopt_connectx_old(struct sock *sk, * always positive. */ static int sctp_setsockopt_connectx(struct sock *sk, - struct sockaddr __user *addrs, + struct sockaddr *kaddrs, int addrs_size) { sctp_assoc_t assoc_id = 0; int err = 0; - err = __sctp_setsockopt_connectx(sk, addrs, addrs_size, &assoc_id); + err = __sctp_setsockopt_connectx(sk, kaddrs, addrs_size, &assoc_id); if (err) return err; @@ -1380,6 +1388,7 @@ static int sctp_getsockopt_connectx3(struct sock *sk, int len, { struct sctp_getaddrs_old param; sctp_assoc_t assoc_id = 0; + struct sockaddr *kaddrs; int err = 0; #ifdef CONFIG_COMPAT @@ -1403,9 +1412,12 @@ static int sctp_getsockopt_connectx3(struct sock *sk, int len, return -EFAULT; } - err = __sctp_setsockopt_connectx(sk, (struct sockaddr __user *) - param.addrs, param.addr_num, - &assoc_id); + kaddrs = memdup_user(param.addrs, param.addr_num); + if (IS_ERR(kaddrs)) + return PTR_ERR(kaddrs); + + err = __sctp_setsockopt_connectx(sk, kaddrs, param.addr_num, &assoc_id); + kfree(kaddrs); if (err == 0 || err == -EINPROGRESS) { if (copy_to_user(optval, &assoc_id, sizeof(assoc_id))) return -EFAULT; @@ -1812,9 +1824,6 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc, if (sctp_wspace(asoc) < (int)msg_len) sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc)); - if (sk_under_memory_pressure(sk)) - sk_mem_reclaim(sk); - if (sctp_wspace(asoc) <= 0 || !sk_wmem_schedule(sk, msg_len)) { timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); @@ -2073,7 +2082,7 @@ static int sctp_skb_pull(struct sk_buff *skb, int len) * 5 for complete description of the flags. */ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - int noblock, int flags, int *addr_len) + int flags, int *addr_len) { struct sctp_ulpevent *event = NULL; struct sctp_sock *sp = sctp_sk(sk); @@ -2082,9 +2091,8 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int err = 0; int skb_len; - pr_debug("%s: sk:%p, msghdr:%p, len:%zd, noblock:%d, flags:0x%x, " - "addr_len:%p)\n", __func__, sk, msg, len, noblock, flags, - addr_len); + pr_debug("%s: sk:%p, msghdr:%p, len:%zd, flags:0x%x, addr_len:%p)\n", + __func__, sk, msg, len, flags, addr_len); lock_sock(sk); @@ -2094,7 +2102,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto out; } - skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); + skb = sctp_skb_recv_datagram(sk, flags, &err); if (!skb) goto out; @@ -2118,7 +2126,7 @@ static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, head_skb = event->chunk->head_skb; else head_skb = skb; - sock_recv_ts_and_drops(msg, sk, head_skb); + sock_recv_cmsgs(msg, sk, head_skb); if (sctp_ulpevent_is_notification(event)) { msg->msg_flags |= MSG_NOTIFICATION; sp->pf->event_msgname(event, msg->msg_name, addr_len); @@ -2187,28 +2195,18 @@ out: * exceeds the current PMTU size, the message will NOT be sent and * instead a error will be indicated to the user. */ -static int sctp_setsockopt_disable_fragments(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_disable_fragments(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (optlen < sizeof(int)) return -EINVAL; - - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1; - + sctp_sk(sk)->disable_fragments = (*val == 0) ? 0 : 1; return 0; } -static int sctp_setsockopt_events(struct sock *sk, char __user *optval, +static int sctp_setsockopt_events(struct sock *sk, __u8 *sn_type, unsigned int optlen) { - struct sctp_event_subscribe subscribe; - __u8 *sn_type = (__u8 *)&subscribe; struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; int i; @@ -2216,9 +2214,6 @@ static int sctp_setsockopt_events(struct sock *sk, char __user *optval, if (optlen > sizeof(struct sctp_event_subscribe)) return -EINVAL; - if (copy_from_user(&subscribe, optval, optlen)) - return -EFAULT; - for (i = 0; i < optlen; i++) sctp_ulpevent_type_set(&sp->subscribe, SCTP_SN_TYPE_BASE + i, sn_type[i]); @@ -2258,7 +2253,7 @@ static int sctp_setsockopt_events(struct sock *sk, char __user *optval, * integer defining the number of seconds of idle time before an * association is closed. */ -static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval, +static int sctp_setsockopt_autoclose(struct sock *sk, u32 *optval, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); @@ -2269,9 +2264,8 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char __user *optval, return -EOPNOTSUPP; if (optlen != sizeof(int)) return -EINVAL; - if (copy_from_user(&sp->autoclose, optval, optlen)) - return -EFAULT; + sp->autoclose = *optval; if (sp->autoclose > net->sctp.max_autoclose) sp->autoclose = net->sctp.max_autoclose; @@ -2499,6 +2493,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, sctp_transport_pmtu(trans, sctp_opt2sk(sp)); sctp_assoc_sync_pmtu(asoc); } + sctp_transport_pl_reset(trans); } else if (asoc) { asoc->param_flags = (asoc->param_flags & ~SPP_PMTUD) | pmtud_change; @@ -2606,48 +2601,42 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, } static int sctp_setsockopt_peer_addr_params(struct sock *sk, - char __user *optval, + struct sctp_paddrparams *params, unsigned int optlen) { - struct sctp_paddrparams params; struct sctp_transport *trans = NULL; struct sctp_association *asoc = NULL; struct sctp_sock *sp = sctp_sk(sk); int error; int hb_change, pmtud_change, sackdelay_change; - if (optlen == sizeof(params)) { - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - } else if (optlen == ALIGN(offsetof(struct sctp_paddrparams, + if (optlen == ALIGN(offsetof(struct sctp_paddrparams, spp_ipv6_flowlabel), 4)) { - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - if (params.spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL)) + if (params->spp_flags & (SPP_DSCP | SPP_IPV6_FLOWLABEL)) return -EINVAL; - } else { + } else if (optlen != sizeof(*params)) { return -EINVAL; } /* Validate flags and value parameters. */ - hb_change = params.spp_flags & SPP_HB; - pmtud_change = params.spp_flags & SPP_PMTUD; - sackdelay_change = params.spp_flags & SPP_SACKDELAY; + hb_change = params->spp_flags & SPP_HB; + pmtud_change = params->spp_flags & SPP_PMTUD; + sackdelay_change = params->spp_flags & SPP_SACKDELAY; if (hb_change == SPP_HB || pmtud_change == SPP_PMTUD || sackdelay_change == SPP_SACKDELAY || - params.spp_sackdelay > 500 || - (params.spp_pathmtu && - params.spp_pathmtu < SCTP_DEFAULT_MINSEGMENT)) + params->spp_sackdelay > 500 || + (params->spp_pathmtu && + params->spp_pathmtu < SCTP_DEFAULT_MINSEGMENT)) return -EINVAL; /* If an address other than INADDR_ANY is specified, and * no transport is found, then the request is invalid. */ - if (!sctp_is_any(sk, (union sctp_addr *)¶ms.spp_address)) { - trans = sctp_addr_id2transport(sk, ¶ms.spp_address, - params.spp_assoc_id); + if (!sctp_is_any(sk, (union sctp_addr *)¶ms->spp_address)) { + trans = sctp_addr_id2transport(sk, ¶ms->spp_address, + params->spp_assoc_id); if (!trans) return -EINVAL; } @@ -2656,19 +2645,19 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk, * socket is a one to many style socket, and an association * was not found, then the id was invalid. */ - asoc = sctp_id2assoc(sk, params.spp_assoc_id); - if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->spp_assoc_id); + if (!asoc && params->spp_assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; /* Heartbeat demand can only be sent on a transport or * association, but not a socket. */ - if (params.spp_flags & SPP_HB_DEMAND && !trans && !asoc) + if (params->spp_flags & SPP_HB_DEMAND && !trans && !asoc) return -EINVAL; /* Process parameters. */ - error = sctp_apply_peer_addr_params(¶ms, trans, asoc, sp, + error = sctp_apply_peer_addr_params(params, trans, asoc, sp, hb_change, pmtud_change, sackdelay_change); @@ -2681,7 +2670,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk, if (!trans && asoc) { list_for_each_entry(trans, &asoc->peer.transport_addr_list, transports) { - sctp_apply_peer_addr_params(¶ms, trans, asoc, sp, + sctp_apply_peer_addr_params(params, trans, asoc, sp, hb_change, pmtud_change, sackdelay_change); } @@ -2772,83 +2761,86 @@ static void sctp_apply_asoc_delayed_ack(struct sctp_sack_info *params, * timer to expire. The default value for this is 2, setting this * value to 1 will disable the delayed sack algorithm. */ - -static int sctp_setsockopt_delayed_ack(struct sock *sk, - char __user *optval, unsigned int optlen) +static int __sctp_setsockopt_delayed_ack(struct sock *sk, + struct sctp_sack_info *params) { struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; - struct sctp_sack_info params; - - if (optlen == sizeof(struct sctp_sack_info)) { - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - - if (params.sack_delay == 0 && params.sack_freq == 0) - return 0; - } else if (optlen == sizeof(struct sctp_assoc_value)) { - pr_warn_ratelimited(DEPRECATED - "%s (pid %d) " - "Use of struct sctp_assoc_value in delayed_ack socket option.\n" - "Use struct sctp_sack_info instead\n", - current->comm, task_pid_nr(current)); - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - - if (params.sack_delay == 0) - params.sack_freq = 1; - else - params.sack_freq = 0; - } else - return -EINVAL; /* Validate value parameter. */ - if (params.sack_delay > 500) + if (params->sack_delay > 500) return -EINVAL; /* Get association, if sack_assoc_id != SCTP_FUTURE_ASSOC and the * socket is a one to many style socket, and an association * was not found, then the id was invalid. */ - asoc = sctp_id2assoc(sk, params.sack_assoc_id); - if (!asoc && params.sack_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, params->sack_assoc_id); + if (!asoc && params->sack_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { - sctp_apply_asoc_delayed_ack(¶ms, asoc); + sctp_apply_asoc_delayed_ack(params, asoc); return 0; } if (sctp_style(sk, TCP)) - params.sack_assoc_id = SCTP_FUTURE_ASSOC; + params->sack_assoc_id = SCTP_FUTURE_ASSOC; - if (params.sack_assoc_id == SCTP_FUTURE_ASSOC || - params.sack_assoc_id == SCTP_ALL_ASSOC) { - if (params.sack_delay) { - sp->sackdelay = params.sack_delay; + if (params->sack_assoc_id == SCTP_FUTURE_ASSOC || + params->sack_assoc_id == SCTP_ALL_ASSOC) { + if (params->sack_delay) { + sp->sackdelay = params->sack_delay; sp->param_flags = sctp_spp_sackdelay_enable(sp->param_flags); } - if (params.sack_freq == 1) { + if (params->sack_freq == 1) { sp->param_flags = sctp_spp_sackdelay_disable(sp->param_flags); - } else if (params.sack_freq > 1) { - sp->sackfreq = params.sack_freq; + } else if (params->sack_freq > 1) { + sp->sackfreq = params->sack_freq; sp->param_flags = sctp_spp_sackdelay_enable(sp->param_flags); } } - if (params.sack_assoc_id == SCTP_CURRENT_ASSOC || - params.sack_assoc_id == SCTP_ALL_ASSOC) + if (params->sack_assoc_id == SCTP_CURRENT_ASSOC || + params->sack_assoc_id == SCTP_ALL_ASSOC) list_for_each_entry(asoc, &sp->ep->asocs, asocs) - sctp_apply_asoc_delayed_ack(¶ms, asoc); + sctp_apply_asoc_delayed_ack(params, asoc); return 0; } +static int sctp_setsockopt_delayed_ack(struct sock *sk, + struct sctp_sack_info *params, + unsigned int optlen) +{ + if (optlen == sizeof(struct sctp_assoc_value)) { + struct sctp_assoc_value *v = (struct sctp_assoc_value *)params; + struct sctp_sack_info p; + + pr_warn_ratelimited(DEPRECATED + "%s (pid %d) " + "Use of struct sctp_assoc_value in delayed_ack socket option.\n" + "Use struct sctp_sack_info instead\n", + current->comm, task_pid_nr(current)); + + p.sack_assoc_id = v->assoc_id; + p.sack_delay = v->assoc_value; + p.sack_freq = v->assoc_value ? 0 : 1; + return __sctp_setsockopt_delayed_ack(sk, &p); + } + + if (optlen != sizeof(struct sctp_sack_info)) + return -EINVAL; + if (params->sack_delay == 0 && params->sack_freq == 0) + return 0; + return __sctp_setsockopt_delayed_ack(sk, params); +} + /* 7.1.3 Initialization Parameters (SCTP_INITMSG) * * Applications can specify protocol parameters for the default association @@ -2860,24 +2852,22 @@ static int sctp_setsockopt_delayed_ack(struct sock *sk, * by the change). With TCP-style sockets, this option is inherited by * sockets derived from a listener socket. */ -static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, unsigned int optlen) +static int sctp_setsockopt_initmsg(struct sock *sk, struct sctp_initmsg *sinit, + unsigned int optlen) { - struct sctp_initmsg sinit; struct sctp_sock *sp = sctp_sk(sk); if (optlen != sizeof(struct sctp_initmsg)) return -EINVAL; - if (copy_from_user(&sinit, optval, optlen)) - return -EFAULT; - if (sinit.sinit_num_ostreams) - sp->initmsg.sinit_num_ostreams = sinit.sinit_num_ostreams; - if (sinit.sinit_max_instreams) - sp->initmsg.sinit_max_instreams = sinit.sinit_max_instreams; - if (sinit.sinit_max_attempts) - sp->initmsg.sinit_max_attempts = sinit.sinit_max_attempts; - if (sinit.sinit_max_init_timeo) - sp->initmsg.sinit_max_init_timeo = sinit.sinit_max_init_timeo; + if (sinit->sinit_num_ostreams) + sp->initmsg.sinit_num_ostreams = sinit->sinit_num_ostreams; + if (sinit->sinit_max_instreams) + sp->initmsg.sinit_max_instreams = sinit->sinit_max_instreams; + if (sinit->sinit_max_attempts) + sp->initmsg.sinit_max_attempts = sinit->sinit_max_attempts; + if (sinit->sinit_max_init_timeo) + sp->initmsg.sinit_max_init_timeo = sinit->sinit_max_init_timeo; return 0; } @@ -2897,57 +2887,54 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char __user *optval, unsigne * to this call if the caller is using the UDP model. */ static int sctp_setsockopt_default_send_param(struct sock *sk, - char __user *optval, + struct sctp_sndrcvinfo *info, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; - struct sctp_sndrcvinfo info; - if (optlen != sizeof(info)) + if (optlen != sizeof(*info)) return -EINVAL; - if (copy_from_user(&info, optval, optlen)) - return -EFAULT; - if (info.sinfo_flags & + if (info->sinfo_flags & ~(SCTP_UNORDERED | SCTP_ADDR_OVER | SCTP_ABORT | SCTP_EOF)) return -EINVAL; - asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); - if (!asoc && info.sinfo_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, info->sinfo_assoc_id); + if (!asoc && info->sinfo_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { - asoc->default_stream = info.sinfo_stream; - asoc->default_flags = info.sinfo_flags; - asoc->default_ppid = info.sinfo_ppid; - asoc->default_context = info.sinfo_context; - asoc->default_timetolive = info.sinfo_timetolive; + asoc->default_stream = info->sinfo_stream; + asoc->default_flags = info->sinfo_flags; + asoc->default_ppid = info->sinfo_ppid; + asoc->default_context = info->sinfo_context; + asoc->default_timetolive = info->sinfo_timetolive; return 0; } if (sctp_style(sk, TCP)) - info.sinfo_assoc_id = SCTP_FUTURE_ASSOC; + info->sinfo_assoc_id = SCTP_FUTURE_ASSOC; - if (info.sinfo_assoc_id == SCTP_FUTURE_ASSOC || - info.sinfo_assoc_id == SCTP_ALL_ASSOC) { - sp->default_stream = info.sinfo_stream; - sp->default_flags = info.sinfo_flags; - sp->default_ppid = info.sinfo_ppid; - sp->default_context = info.sinfo_context; - sp->default_timetolive = info.sinfo_timetolive; + if (info->sinfo_assoc_id == SCTP_FUTURE_ASSOC || + info->sinfo_assoc_id == SCTP_ALL_ASSOC) { + sp->default_stream = info->sinfo_stream; + sp->default_flags = info->sinfo_flags; + sp->default_ppid = info->sinfo_ppid; + sp->default_context = info->sinfo_context; + sp->default_timetolive = info->sinfo_timetolive; } - if (info.sinfo_assoc_id == SCTP_CURRENT_ASSOC || - info.sinfo_assoc_id == SCTP_ALL_ASSOC) { + if (info->sinfo_assoc_id == SCTP_CURRENT_ASSOC || + info->sinfo_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &sp->ep->asocs, asocs) { - asoc->default_stream = info.sinfo_stream; - asoc->default_flags = info.sinfo_flags; - asoc->default_ppid = info.sinfo_ppid; - asoc->default_context = info.sinfo_context; - asoc->default_timetolive = info.sinfo_timetolive; + asoc->default_stream = info->sinfo_stream; + asoc->default_flags = info->sinfo_flags; + asoc->default_ppid = info->sinfo_ppid; + asoc->default_context = info->sinfo_context; + asoc->default_timetolive = info->sinfo_timetolive; } } @@ -2958,54 +2945,51 @@ static int sctp_setsockopt_default_send_param(struct sock *sk, * (SCTP_DEFAULT_SNDINFO) */ static int sctp_setsockopt_default_sndinfo(struct sock *sk, - char __user *optval, + struct sctp_sndinfo *info, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; - struct sctp_sndinfo info; - if (optlen != sizeof(info)) + if (optlen != sizeof(*info)) return -EINVAL; - if (copy_from_user(&info, optval, optlen)) - return -EFAULT; - if (info.snd_flags & + if (info->snd_flags & ~(SCTP_UNORDERED | SCTP_ADDR_OVER | SCTP_ABORT | SCTP_EOF)) return -EINVAL; - asoc = sctp_id2assoc(sk, info.snd_assoc_id); - if (!asoc && info.snd_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, info->snd_assoc_id); + if (!asoc && info->snd_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { - asoc->default_stream = info.snd_sid; - asoc->default_flags = info.snd_flags; - asoc->default_ppid = info.snd_ppid; - asoc->default_context = info.snd_context; + asoc->default_stream = info->snd_sid; + asoc->default_flags = info->snd_flags; + asoc->default_ppid = info->snd_ppid; + asoc->default_context = info->snd_context; return 0; } if (sctp_style(sk, TCP)) - info.snd_assoc_id = SCTP_FUTURE_ASSOC; + info->snd_assoc_id = SCTP_FUTURE_ASSOC; - if (info.snd_assoc_id == SCTP_FUTURE_ASSOC || - info.snd_assoc_id == SCTP_ALL_ASSOC) { - sp->default_stream = info.snd_sid; - sp->default_flags = info.snd_flags; - sp->default_ppid = info.snd_ppid; - sp->default_context = info.snd_context; + if (info->snd_assoc_id == SCTP_FUTURE_ASSOC || + info->snd_assoc_id == SCTP_ALL_ASSOC) { + sp->default_stream = info->snd_sid; + sp->default_flags = info->snd_flags; + sp->default_ppid = info->snd_ppid; + sp->default_context = info->snd_context; } - if (info.snd_assoc_id == SCTP_CURRENT_ASSOC || - info.snd_assoc_id == SCTP_ALL_ASSOC) { + if (info->snd_assoc_id == SCTP_CURRENT_ASSOC || + info->snd_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &sp->ep->asocs, asocs) { - asoc->default_stream = info.snd_sid; - asoc->default_flags = info.snd_flags; - asoc->default_ppid = info.snd_ppid; - asoc->default_context = info.snd_context; + asoc->default_stream = info->snd_sid; + asoc->default_flags = info->snd_flags; + asoc->default_ppid = info->snd_ppid; + asoc->default_context = info->snd_context; } } @@ -3018,10 +3002,9 @@ static int sctp_setsockopt_default_sndinfo(struct sock *sk, * the association primary. The enclosed address must be one of the * association peer's addresses. */ -static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, +static int sctp_setsockopt_primary_addr(struct sock *sk, struct sctp_prim *prim, unsigned int optlen) { - struct sctp_prim prim; struct sctp_transport *trans; struct sctp_af *af; int err; @@ -3029,21 +3012,18 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, if (optlen != sizeof(struct sctp_prim)) return -EINVAL; - if (copy_from_user(&prim, optval, sizeof(struct sctp_prim))) - return -EFAULT; - /* Allow security module to validate address but need address len. */ - af = sctp_get_af_specific(prim.ssp_addr.ss_family); + af = sctp_get_af_specific(prim->ssp_addr.ss_family); if (!af) return -EINVAL; err = security_sctp_bind_connect(sk, SCTP_PRIMARY_ADDR, - (struct sockaddr *)&prim.ssp_addr, + (struct sockaddr *)&prim->ssp_addr, af->sockaddr_len); if (err) return err; - trans = sctp_addr_id2transport(sk, &prim.ssp_addr, prim.ssp_assoc_id); + trans = sctp_addr_id2transport(sk, &prim->ssp_addr, prim->ssp_assoc_id); if (!trans) return -EINVAL; @@ -3060,17 +3040,12 @@ static int sctp_setsockopt_primary_addr(struct sock *sk, char __user *optval, * introduced, at the cost of more packets in the network. Expects an * integer boolean flag. */ -static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval, +static int sctp_setsockopt_nodelay(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1; + sctp_sk(sk)->nodelay = (*val == 0) ? 0 : 1; return 0; } @@ -3086,9 +3061,10 @@ static int sctp_setsockopt_nodelay(struct sock *sk, char __user *optval, * be changed. * */ -static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigned int optlen) +static int sctp_setsockopt_rtoinfo(struct sock *sk, + struct sctp_rtoinfo *rtoinfo, + unsigned int optlen) { - struct sctp_rtoinfo rtoinfo; struct sctp_association *asoc; unsigned long rto_min, rto_max; struct sctp_sock *sp = sctp_sk(sk); @@ -3096,18 +3072,15 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigne if (optlen != sizeof (struct sctp_rtoinfo)) return -EINVAL; - if (copy_from_user(&rtoinfo, optval, optlen)) - return -EFAULT; - - asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id); + asoc = sctp_id2assoc(sk, rtoinfo->srto_assoc_id); /* Set the values to the specific association */ - if (!asoc && rtoinfo.srto_assoc_id != SCTP_FUTURE_ASSOC && + if (!asoc && rtoinfo->srto_assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; - rto_max = rtoinfo.srto_max; - rto_min = rtoinfo.srto_min; + rto_max = rtoinfo->srto_max; + rto_min = rtoinfo->srto_min; if (rto_max) rto_max = asoc ? msecs_to_jiffies(rto_max) : rto_max; @@ -3123,17 +3096,17 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigne return -EINVAL; if (asoc) { - if (rtoinfo.srto_initial != 0) + if (rtoinfo->srto_initial != 0) asoc->rto_initial = - msecs_to_jiffies(rtoinfo.srto_initial); + msecs_to_jiffies(rtoinfo->srto_initial); asoc->rto_max = rto_max; asoc->rto_min = rto_min; } else { /* If there is no association or the association-id = 0 * set the values to the endpoint. */ - if (rtoinfo.srto_initial != 0) - sp->rtoinfo.srto_initial = rtoinfo.srto_initial; + if (rtoinfo->srto_initial != 0) + sp->rtoinfo.srto_initial = rtoinfo->srto_initial; sp->rtoinfo.srto_max = rto_max; sp->rtoinfo.srto_min = rto_min; } @@ -3152,26 +3125,25 @@ static int sctp_setsockopt_rtoinfo(struct sock *sk, char __user *optval, unsigne * See [SCTP] for more information. * */ -static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsigned int optlen) +static int sctp_setsockopt_associnfo(struct sock *sk, + struct sctp_assocparams *assocparams, + unsigned int optlen) { - struct sctp_assocparams assocparams; struct sctp_association *asoc; if (optlen != sizeof(struct sctp_assocparams)) return -EINVAL; - if (copy_from_user(&assocparams, optval, optlen)) - return -EFAULT; - asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id); + asoc = sctp_id2assoc(sk, assocparams->sasoc_assoc_id); - if (!asoc && assocparams.sasoc_assoc_id != SCTP_FUTURE_ASSOC && + if (!asoc && assocparams->sasoc_assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; /* Set the values to the specific association */ if (asoc) { - if (assocparams.sasoc_asocmaxrxt != 0) { + if (assocparams->sasoc_asocmaxrxt != 0) { __u32 path_sum = 0; int paths = 0; struct sctp_transport *peer_addr; @@ -3188,24 +3160,25 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsig * then one path. */ if (paths > 1 && - assocparams.sasoc_asocmaxrxt > path_sum) + assocparams->sasoc_asocmaxrxt > path_sum) return -EINVAL; - asoc->max_retrans = assocparams.sasoc_asocmaxrxt; + asoc->max_retrans = assocparams->sasoc_asocmaxrxt; } - if (assocparams.sasoc_cookie_life != 0) - asoc->cookie_life = ms_to_ktime(assocparams.sasoc_cookie_life); + if (assocparams->sasoc_cookie_life != 0) + asoc->cookie_life = + ms_to_ktime(assocparams->sasoc_cookie_life); } else { /* Set the values to the endpoint */ struct sctp_sock *sp = sctp_sk(sk); - if (assocparams.sasoc_asocmaxrxt != 0) + if (assocparams->sasoc_asocmaxrxt != 0) sp->assocparams.sasoc_asocmaxrxt = - assocparams.sasoc_asocmaxrxt; - if (assocparams.sasoc_cookie_life != 0) + assocparams->sasoc_asocmaxrxt; + if (assocparams->sasoc_cookie_life != 0) sp->assocparams.sasoc_cookie_life = - assocparams.sasoc_cookie_life; + assocparams->sasoc_cookie_life; } return 0; } @@ -3220,16 +3193,14 @@ static int sctp_setsockopt_associnfo(struct sock *sk, char __user *optval, unsig * addresses and a user will receive both PF_INET6 and PF_INET type * addresses on the socket. */ -static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsigned int optlen) +static int sctp_setsockopt_mappedv4(struct sock *sk, int *val, + unsigned int optlen) { - int val; struct sctp_sock *sp = sctp_sk(sk); if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; - if (val) + if (*val) sp->v4mapped = 1; else sp->v4mapped = 0; @@ -3264,11 +3235,13 @@ static int sctp_setsockopt_mappedv4(struct sock *sk, char __user *optval, unsign * changed (effecting future associations only). * assoc_value: This parameter specifies the maximum size in bytes. */ -static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen) +static int sctp_setsockopt_maxseg(struct sock *sk, + struct sctp_assoc_value *params, + unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); - struct sctp_assoc_value params; struct sctp_association *asoc; + sctp_assoc_t assoc_id; int val; if (optlen == sizeof(int)) { @@ -3277,19 +3250,17 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned "Use of int in maxseg socket option.\n" "Use struct sctp_assoc_value instead\n", current->comm, task_pid_nr(current)); - if (copy_from_user(&val, optval, optlen)) - return -EFAULT; - params.assoc_id = SCTP_FUTURE_ASSOC; + assoc_id = SCTP_FUTURE_ASSOC; + val = *(int *)params; } else if (optlen == sizeof(struct sctp_assoc_value)) { - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - val = params.assoc_value; + assoc_id = params->assoc_id; + val = params->assoc_value; } else { return -EINVAL; } - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, assoc_id); + if (!asoc && assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; @@ -3324,12 +3295,12 @@ static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned * locally bound addresses. The following structure is used to make a * set primary request: */ -static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval, +static int sctp_setsockopt_peer_primary_addr(struct sock *sk, + struct sctp_setpeerprim *prim, unsigned int optlen) { struct sctp_sock *sp; struct sctp_association *asoc = NULL; - struct sctp_setpeerprim prim; struct sctp_chunk *chunk; struct sctp_af *af; int err; @@ -3342,10 +3313,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva if (optlen != sizeof(struct sctp_setpeerprim)) return -EINVAL; - if (copy_from_user(&prim, optval, optlen)) - return -EFAULT; - - asoc = sctp_id2assoc(sk, prim.sspp_assoc_id); + asoc = sctp_id2assoc(sk, prim->sspp_assoc_id); if (!asoc) return -EINVAL; @@ -3358,26 +3326,26 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva if (!sctp_state(asoc, ESTABLISHED)) return -ENOTCONN; - af = sctp_get_af_specific(prim.sspp_addr.ss_family); + af = sctp_get_af_specific(prim->sspp_addr.ss_family); if (!af) return -EINVAL; - if (!af->addr_valid((union sctp_addr *)&prim.sspp_addr, sp, NULL)) + if (!af->addr_valid((union sctp_addr *)&prim->sspp_addr, sp, NULL)) return -EADDRNOTAVAIL; - if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr)) + if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim->sspp_addr)) return -EADDRNOTAVAIL; /* Allow security module to validate address. */ err = security_sctp_bind_connect(sk, SCTP_SET_PEER_PRIMARY_ADDR, - (struct sockaddr *)&prim.sspp_addr, + (struct sockaddr *)&prim->sspp_addr, af->sockaddr_len); if (err) return err; /* Create an ASCONF chunk with SET_PRIMARY parameter */ chunk = sctp_make_asconf_set_prim(asoc, - (union sctp_addr *)&prim.sspp_addr); + (union sctp_addr *)&prim->sspp_addr); if (!chunk) return -ENOMEM; @@ -3388,17 +3356,14 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva return err; } -static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval, +static int sctp_setsockopt_adaptation_layer(struct sock *sk, + struct sctp_setadaptation *adapt, unsigned int optlen) { - struct sctp_setadaptation adaptation; - if (optlen != sizeof(struct sctp_setadaptation)) return -EINVAL; - if (copy_from_user(&adaptation, optval, optlen)) - return -EFAULT; - sctp_sk(sk)->adaptation_ind = adaptation.ssb_adaptation_ind; + sctp_sk(sk)->adaptation_ind = adapt->ssb_adaptation_ind; return 0; } @@ -3417,40 +3382,38 @@ static int sctp_setsockopt_adaptation_layer(struct sock *sk, char __user *optval * received messages from the peer and does not effect the value that is * saved with outbound messages. */ -static int sctp_setsockopt_context(struct sock *sk, char __user *optval, +static int sctp_setsockopt_context(struct sock *sk, + struct sctp_assoc_value *params, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); - struct sctp_assoc_value params; struct sctp_association *asoc; if (optlen != sizeof(struct sctp_assoc_value)) return -EINVAL; - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { - asoc->default_rcv_context = params.assoc_value; + asoc->default_rcv_context = params->assoc_value; return 0; } if (sctp_style(sk, TCP)) - params.assoc_id = SCTP_FUTURE_ASSOC; + params->assoc_id = SCTP_FUTURE_ASSOC; - if (params.assoc_id == SCTP_FUTURE_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) - sp->default_rcv_context = params.assoc_value; + if (params->assoc_id == SCTP_FUTURE_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) + sp->default_rcv_context = params->assoc_value; - if (params.assoc_id == SCTP_CURRENT_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) + if (params->assoc_id == SCTP_CURRENT_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) list_for_each_entry(asoc, &sp->ep->asocs, asocs) - asoc->default_rcv_context = params.assoc_value; + asoc->default_rcv_context = params->assoc_value; return 0; } @@ -3479,18 +3442,13 @@ static int sctp_setsockopt_context(struct sock *sk, char __user *optval, * application using the one to many model may become confused and act * incorrectly. */ -static int sctp_setsockopt_fragment_interleave(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_fragment_interleave(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (optlen != sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; - sctp_sk(sk)->frag_interleave = !!val; + sctp_sk(sk)->frag_interleave = !!*val; if (!sctp_sk(sk)->frag_interleave) sctp_sk(sk)->ep->intl_enable = 0; @@ -3515,24 +3473,19 @@ static int sctp_setsockopt_fragment_interleave(struct sock *sk, * call as long as the user provided buffer is large enough to hold the * message. */ -static int sctp_setsockopt_partial_delivery_point(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_partial_delivery_point(struct sock *sk, u32 *val, unsigned int optlen) { - u32 val; - if (optlen != sizeof(u32)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; /* Note: We double the receive buffer from what the user sets * it to be, also initial rwnd is based on rcvbuf/2. */ - if (val > (sk->sk_rcvbuf >> 1)) + if (*val > (sk->sk_rcvbuf >> 1)) return -EINVAL; - sctp_sk(sk)->pd_point = val; + sctp_sk(sk)->pd_point = *val; return 0; /* is this the right error code? */ } @@ -3549,12 +3502,13 @@ static int sctp_setsockopt_partial_delivery_point(struct sock *sk, * future associations inheriting the socket value. */ static int sctp_setsockopt_maxburst(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); - struct sctp_assoc_value params; struct sctp_association *asoc; + sctp_assoc_t assoc_id; + u32 assoc_value; if (optlen == sizeof(int)) { pr_warn_ratelimited(DEPRECATED @@ -3562,37 +3516,33 @@ static int sctp_setsockopt_maxburst(struct sock *sk, "Use of int in max_burst socket option deprecated.\n" "Use struct sctp_assoc_value instead\n", current->comm, task_pid_nr(current)); - if (copy_from_user(¶ms.assoc_value, optval, optlen)) - return -EFAULT; - params.assoc_id = SCTP_FUTURE_ASSOC; + assoc_id = SCTP_FUTURE_ASSOC; + assoc_value = *((int *)params); } else if (optlen == sizeof(struct sctp_assoc_value)) { - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; + assoc_id = params->assoc_id; + assoc_value = params->assoc_value; } else return -EINVAL; - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id > SCTP_ALL_ASSOC && - sctp_style(sk, UDP)) + asoc = sctp_id2assoc(sk, assoc_id); + if (!asoc && assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { - asoc->max_burst = params.assoc_value; + asoc->max_burst = assoc_value; return 0; } if (sctp_style(sk, TCP)) - params.assoc_id = SCTP_FUTURE_ASSOC; + assoc_id = SCTP_FUTURE_ASSOC; - if (params.assoc_id == SCTP_FUTURE_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) - sp->max_burst = params.assoc_value; + if (assoc_id == SCTP_FUTURE_ASSOC || assoc_id == SCTP_ALL_ASSOC) + sp->max_burst = assoc_value; - if (params.assoc_id == SCTP_CURRENT_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) + if (assoc_id == SCTP_CURRENT_ASSOC || assoc_id == SCTP_ALL_ASSOC) list_for_each_entry(asoc, &sp->ep->asocs, asocs) - asoc->max_burst = params.assoc_value; + asoc->max_burst = assoc_value; return 0; } @@ -3605,21 +3555,18 @@ static int sctp_setsockopt_maxburst(struct sock *sk, * will only effect future associations on the socket. */ static int sctp_setsockopt_auth_chunk(struct sock *sk, - char __user *optval, + struct sctp_authchunk *val, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; - struct sctp_authchunk val; if (!ep->auth_enable) return -EACCES; if (optlen != sizeof(struct sctp_authchunk)) return -EINVAL; - if (copy_from_user(&val, optval, optlen)) - return -EFAULT; - switch (val.sauth_chunk) { + switch (val->sauth_chunk) { case SCTP_CID_INIT: case SCTP_CID_INIT_ACK: case SCTP_CID_SHUTDOWN_COMPLETE: @@ -3628,7 +3575,7 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk, } /* add this chunk id to the endpoint */ - return sctp_auth_ep_add_chunkid(ep, val.sauth_chunk); + return sctp_auth_ep_add_chunkid(ep, val->sauth_chunk); } /* @@ -3638,13 +3585,11 @@ static int sctp_setsockopt_auth_chunk(struct sock *sk, * endpoint requires the peer to use. */ static int sctp_setsockopt_hmac_ident(struct sock *sk, - char __user *optval, + struct sctp_hmacalgo *hmacs, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; - struct sctp_hmacalgo *hmacs; u32 idents; - int err; if (!ep->auth_enable) return -EACCES; @@ -3654,21 +3599,12 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk, optlen = min_t(unsigned int, optlen, sizeof(struct sctp_hmacalgo) + SCTP_AUTH_NUM_HMACS * sizeof(u16)); - hmacs = memdup_user(optval, optlen); - if (IS_ERR(hmacs)) - return PTR_ERR(hmacs); - idents = hmacs->shmac_num_idents; if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS || - (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo))) { - err = -EINVAL; - goto out; - } + (idents * sizeof(u16)) > (optlen - sizeof(struct sctp_hmacalgo))) + return -EINVAL; - err = sctp_auth_ep_set_hmacs(ep, hmacs); -out: - kfree(hmacs); - return err; + return sctp_auth_ep_set_hmacs(ep, hmacs); } /* @@ -3678,11 +3614,10 @@ out: * association shared key. */ static int sctp_setsockopt_auth_key(struct sock *sk, - char __user *optval, + struct sctp_authkey *authkey, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; - struct sctp_authkey *authkey; struct sctp_association *asoc; int ret = -EINVAL; @@ -3693,10 +3628,6 @@ static int sctp_setsockopt_auth_key(struct sock *sk, */ optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(*authkey)); - authkey = memdup_user(optval, optlen); - if (IS_ERR(authkey)) - return PTR_ERR(authkey); - if (authkey->sca_keylength > optlen - sizeof(*authkey)) goto out; @@ -3733,7 +3664,7 @@ static int sctp_setsockopt_auth_key(struct sock *sk, } out: - kzfree(authkey); + memzero_explicit(authkey, optlen); return ret; } @@ -3744,42 +3675,39 @@ out: * the association shared key. */ static int sctp_setsockopt_active_key(struct sock *sk, - char __user *optval, + struct sctp_authkeyid *val, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; struct sctp_association *asoc; - struct sctp_authkeyid val; int ret = 0; if (optlen != sizeof(struct sctp_authkeyid)) return -EINVAL; - if (copy_from_user(&val, optval, optlen)) - return -EFAULT; - asoc = sctp_id2assoc(sk, val.scact_assoc_id); - if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, val->scact_assoc_id); + if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) - return sctp_auth_set_active_key(ep, asoc, val.scact_keynumber); + return sctp_auth_set_active_key(ep, asoc, val->scact_keynumber); if (sctp_style(sk, TCP)) - val.scact_assoc_id = SCTP_FUTURE_ASSOC; + val->scact_assoc_id = SCTP_FUTURE_ASSOC; - if (val.scact_assoc_id == SCTP_FUTURE_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { - ret = sctp_auth_set_active_key(ep, asoc, val.scact_keynumber); + if (val->scact_assoc_id == SCTP_FUTURE_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { + ret = sctp_auth_set_active_key(ep, asoc, val->scact_keynumber); if (ret) return ret; } - if (val.scact_assoc_id == SCTP_CURRENT_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { + if (val->scact_assoc_id == SCTP_CURRENT_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &ep->asocs, asocs) { int res = sctp_auth_set_active_key(ep, asoc, - val.scact_keynumber); + val->scact_keynumber); if (res && !ret) ret = res; @@ -3795,42 +3723,39 @@ static int sctp_setsockopt_active_key(struct sock *sk, * This set option will delete a shared secret key from use. */ static int sctp_setsockopt_del_key(struct sock *sk, - char __user *optval, + struct sctp_authkeyid *val, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; struct sctp_association *asoc; - struct sctp_authkeyid val; int ret = 0; if (optlen != sizeof(struct sctp_authkeyid)) return -EINVAL; - if (copy_from_user(&val, optval, optlen)) - return -EFAULT; - asoc = sctp_id2assoc(sk, val.scact_assoc_id); - if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, val->scact_assoc_id); + if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) - return sctp_auth_del_key_id(ep, asoc, val.scact_keynumber); + return sctp_auth_del_key_id(ep, asoc, val->scact_keynumber); if (sctp_style(sk, TCP)) - val.scact_assoc_id = SCTP_FUTURE_ASSOC; + val->scact_assoc_id = SCTP_FUTURE_ASSOC; - if (val.scact_assoc_id == SCTP_FUTURE_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { - ret = sctp_auth_del_key_id(ep, asoc, val.scact_keynumber); + if (val->scact_assoc_id == SCTP_FUTURE_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { + ret = sctp_auth_del_key_id(ep, asoc, val->scact_keynumber); if (ret) return ret; } - if (val.scact_assoc_id == SCTP_CURRENT_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { + if (val->scact_assoc_id == SCTP_CURRENT_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &ep->asocs, asocs) { int res = sctp_auth_del_key_id(ep, asoc, - val.scact_keynumber); + val->scact_keynumber); if (res && !ret) ret = res; @@ -3845,42 +3770,40 @@ static int sctp_setsockopt_del_key(struct sock *sk, * * This set option will deactivate a shared secret key. */ -static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval, +static int sctp_setsockopt_deactivate_key(struct sock *sk, + struct sctp_authkeyid *val, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; struct sctp_association *asoc; - struct sctp_authkeyid val; int ret = 0; if (optlen != sizeof(struct sctp_authkeyid)) return -EINVAL; - if (copy_from_user(&val, optval, optlen)) - return -EFAULT; - asoc = sctp_id2assoc(sk, val.scact_assoc_id); - if (!asoc && val.scact_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, val->scact_assoc_id); + if (!asoc && val->scact_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) - return sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber); + return sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber); if (sctp_style(sk, TCP)) - val.scact_assoc_id = SCTP_FUTURE_ASSOC; + val->scact_assoc_id = SCTP_FUTURE_ASSOC; - if (val.scact_assoc_id == SCTP_FUTURE_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { - ret = sctp_auth_deact_key_id(ep, asoc, val.scact_keynumber); + if (val->scact_assoc_id == SCTP_FUTURE_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { + ret = sctp_auth_deact_key_id(ep, asoc, val->scact_keynumber); if (ret) return ret; } - if (val.scact_assoc_id == SCTP_CURRENT_ASSOC || - val.scact_assoc_id == SCTP_ALL_ASSOC) { + if (val->scact_assoc_id == SCTP_CURRENT_ASSOC || + val->scact_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &ep->asocs, asocs) { int res = sctp_auth_deact_key_id(ep, asoc, - val.scact_keynumber); + val->scact_keynumber); if (res && !ret) ret = res; @@ -3904,26 +3827,23 @@ static int sctp_setsockopt_deactivate_key(struct sock *sk, char __user *optval, * Note. In this implementation, socket operation overrides default parameter * being set by sysctl as well as FreeBSD implementation */ -static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, +static int sctp_setsockopt_auto_asconf(struct sock *sk, int *val, unsigned int optlen) { - int val; struct sctp_sock *sp = sctp_sk(sk); if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; - if (!sctp_is_ep_boundall(sk) && val) + if (!sctp_is_ep_boundall(sk) && *val) return -EINVAL; - if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf)) + if ((*val && sp->do_auto_asconf) || (!*val && !sp->do_auto_asconf)) return 0; spin_lock_bh(&sock_net(sk)->sctp.addr_wq_lock); - if (val == 0 && sp->do_auto_asconf) { + if (*val == 0 && sp->do_auto_asconf) { list_del(&sp->auto_asconf_list); sp->do_auto_asconf = 0; - } else if (val && !sp->do_auto_asconf) { + } else if (*val && !sp->do_auto_asconf) { list_add_tail(&sp->auto_asconf_list, &sock_net(sk)->sctp.auto_asconf_splist); sp->do_auto_asconf = 1; @@ -3940,176 +3860,154 @@ static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval, * http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt */ static int sctp_setsockopt_paddr_thresholds(struct sock *sk, - char __user *optval, + struct sctp_paddrthlds_v2 *val, unsigned int optlen, bool v2) { - struct sctp_paddrthlds_v2 val; struct sctp_transport *trans; struct sctp_association *asoc; int len; - len = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds); + len = v2 ? sizeof(*val) : sizeof(struct sctp_paddrthlds); if (optlen < len) return -EINVAL; - if (copy_from_user(&val, optval, len)) - return -EFAULT; - if (v2 && val.spt_pathpfthld > val.spt_pathcpthld) + if (v2 && val->spt_pathpfthld > val->spt_pathcpthld) return -EINVAL; - if (!sctp_is_any(sk, (const union sctp_addr *)&val.spt_address)) { - trans = sctp_addr_id2transport(sk, &val.spt_address, - val.spt_assoc_id); + if (!sctp_is_any(sk, (const union sctp_addr *)&val->spt_address)) { + trans = sctp_addr_id2transport(sk, &val->spt_address, + val->spt_assoc_id); if (!trans) return -ENOENT; - if (val.spt_pathmaxrxt) - trans->pathmaxrxt = val.spt_pathmaxrxt; + if (val->spt_pathmaxrxt) + trans->pathmaxrxt = val->spt_pathmaxrxt; if (v2) - trans->ps_retrans = val.spt_pathcpthld; - trans->pf_retrans = val.spt_pathpfthld; + trans->ps_retrans = val->spt_pathcpthld; + trans->pf_retrans = val->spt_pathpfthld; return 0; } - asoc = sctp_id2assoc(sk, val.spt_assoc_id); - if (!asoc && val.spt_assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, val->spt_assoc_id); + if (!asoc && val->spt_assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) { list_for_each_entry(trans, &asoc->peer.transport_addr_list, transports) { - if (val.spt_pathmaxrxt) - trans->pathmaxrxt = val.spt_pathmaxrxt; + if (val->spt_pathmaxrxt) + trans->pathmaxrxt = val->spt_pathmaxrxt; if (v2) - trans->ps_retrans = val.spt_pathcpthld; - trans->pf_retrans = val.spt_pathpfthld; + trans->ps_retrans = val->spt_pathcpthld; + trans->pf_retrans = val->spt_pathpfthld; } - if (val.spt_pathmaxrxt) - asoc->pathmaxrxt = val.spt_pathmaxrxt; + if (val->spt_pathmaxrxt) + asoc->pathmaxrxt = val->spt_pathmaxrxt; if (v2) - asoc->ps_retrans = val.spt_pathcpthld; - asoc->pf_retrans = val.spt_pathpfthld; + asoc->ps_retrans = val->spt_pathcpthld; + asoc->pf_retrans = val->spt_pathpfthld; } else { struct sctp_sock *sp = sctp_sk(sk); - if (val.spt_pathmaxrxt) - sp->pathmaxrxt = val.spt_pathmaxrxt; + if (val->spt_pathmaxrxt) + sp->pathmaxrxt = val->spt_pathmaxrxt; if (v2) - sp->ps_retrans = val.spt_pathcpthld; - sp->pf_retrans = val.spt_pathpfthld; + sp->ps_retrans = val->spt_pathcpthld; + sp->pf_retrans = val->spt_pathpfthld; } return 0; } -static int sctp_setsockopt_recvrcvinfo(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_recvrcvinfo(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *) optval)) - return -EFAULT; - sctp_sk(sk)->recvrcvinfo = (val == 0) ? 0 : 1; + sctp_sk(sk)->recvrcvinfo = (*val == 0) ? 0 : 1; return 0; } -static int sctp_setsockopt_recvnxtinfo(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_recvnxtinfo(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *) optval)) - return -EFAULT; - sctp_sk(sk)->recvnxtinfo = (val == 0) ? 0 : 1; + sctp_sk(sk)->recvnxtinfo = (*val == 0) ? 0 : 1; return 0; } static int sctp_setsockopt_pr_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) return -EINVAL; - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) return -EINVAL; - sctp_sk(sk)->ep->prsctp_enable = !!params.assoc_value; + sctp_sk(sk)->ep->prsctp_enable = !!params->assoc_value; return 0; } static int sctp_setsockopt_default_prinfo(struct sock *sk, - char __user *optval, + struct sctp_default_prinfo *info, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); - struct sctp_default_prinfo info; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen != sizeof(info)) + if (optlen != sizeof(*info)) goto out; - if (copy_from_user(&info, optval, sizeof(info))) { - retval = -EFAULT; + if (info->pr_policy & ~SCTP_PR_SCTP_MASK) goto out; - } - if (info.pr_policy & ~SCTP_PR_SCTP_MASK) - goto out; + if (info->pr_policy == SCTP_PR_SCTP_NONE) + info->pr_value = 0; - if (info.pr_policy == SCTP_PR_SCTP_NONE) - info.pr_value = 0; - - asoc = sctp_id2assoc(sk, info.pr_assoc_id); - if (!asoc && info.pr_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, info->pr_assoc_id); + if (!asoc && info->pr_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) goto out; retval = 0; if (asoc) { - SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy); - asoc->default_timetolive = info.pr_value; + SCTP_PR_SET_POLICY(asoc->default_flags, info->pr_policy); + asoc->default_timetolive = info->pr_value; goto out; } if (sctp_style(sk, TCP)) - info.pr_assoc_id = SCTP_FUTURE_ASSOC; + info->pr_assoc_id = SCTP_FUTURE_ASSOC; - if (info.pr_assoc_id == SCTP_FUTURE_ASSOC || - info.pr_assoc_id == SCTP_ALL_ASSOC) { - SCTP_PR_SET_POLICY(sp->default_flags, info.pr_policy); - sp->default_timetolive = info.pr_value; + if (info->pr_assoc_id == SCTP_FUTURE_ASSOC || + info->pr_assoc_id == SCTP_ALL_ASSOC) { + SCTP_PR_SET_POLICY(sp->default_flags, info->pr_policy); + sp->default_timetolive = info->pr_value; } - if (info.pr_assoc_id == SCTP_CURRENT_ASSOC || - info.pr_assoc_id == SCTP_ALL_ASSOC) { + if (info->pr_assoc_id == SCTP_CURRENT_ASSOC || + info->pr_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &sp->ep->asocs, asocs) { - SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy); - asoc->default_timetolive = info.pr_value; + SCTP_PR_SET_POLICY(asoc->default_flags, + info->pr_policy); + asoc->default_timetolive = info->pr_value; } } @@ -4118,27 +4016,21 @@ out: } static int sctp_setsockopt_reconfig_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) goto out; - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) goto out; - sctp_sk(sk)->ep->reconf_enable = !!params.assoc_value; + sctp_sk(sk)->ep->reconf_enable = !!params->assoc_value; retval = 0; @@ -4147,60 +4039,52 @@ out: } static int sctp_setsockopt_enable_strreset(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { struct sctp_endpoint *ep = sctp_sk(sk)->ep; - struct sctp_assoc_value params; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen != sizeof(params)) - goto out; - - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; + if (optlen != sizeof(*params)) goto out; - } - if (params.assoc_value & (~SCTP_ENABLE_STRRESET_MASK)) + if (params->assoc_value & (~SCTP_ENABLE_STRRESET_MASK)) goto out; - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) goto out; retval = 0; if (asoc) { - asoc->strreset_enable = params.assoc_value; + asoc->strreset_enable = params->assoc_value; goto out; } if (sctp_style(sk, TCP)) - params.assoc_id = SCTP_FUTURE_ASSOC; + params->assoc_id = SCTP_FUTURE_ASSOC; - if (params.assoc_id == SCTP_FUTURE_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) - ep->strreset_enable = params.assoc_value; + if (params->assoc_id == SCTP_FUTURE_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) + ep->strreset_enable = params->assoc_value; - if (params.assoc_id == SCTP_CURRENT_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) + if (params->assoc_id == SCTP_CURRENT_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) list_for_each_entry(asoc, &ep->asocs, asocs) - asoc->strreset_enable = params.assoc_value; + asoc->strreset_enable = params->assoc_value; out: return retval; } static int sctp_setsockopt_reset_streams(struct sock *sk, - char __user *optval, + struct sctp_reset_streams *params, unsigned int optlen) { - struct sctp_reset_streams *params; struct sctp_association *asoc; - int retval = -EINVAL; if (optlen < sizeof(*params)) return -EINVAL; @@ -4208,116 +4092,82 @@ static int sctp_setsockopt_reset_streams(struct sock *sk, optlen = min_t(unsigned int, optlen, USHRT_MAX + sizeof(__u16) * sizeof(*params)); - params = memdup_user(optval, optlen); - if (IS_ERR(params)) - return PTR_ERR(params); - if (params->srs_number_streams * sizeof(__u16) > optlen - sizeof(*params)) - goto out; + return -EINVAL; asoc = sctp_id2assoc(sk, params->srs_assoc_id); if (!asoc) - goto out; - - retval = sctp_send_reset_streams(asoc, params); + return -EINVAL; -out: - kfree(params); - return retval; + return sctp_send_reset_streams(asoc, params); } -static int sctp_setsockopt_reset_assoc(struct sock *sk, - char __user *optval, +static int sctp_setsockopt_reset_assoc(struct sock *sk, sctp_assoc_t *associd, unsigned int optlen) { struct sctp_association *asoc; - sctp_assoc_t associd; - int retval = -EINVAL; - if (optlen != sizeof(associd)) - goto out; - - if (copy_from_user(&associd, optval, optlen)) { - retval = -EFAULT; - goto out; - } + if (optlen != sizeof(*associd)) + return -EINVAL; - asoc = sctp_id2assoc(sk, associd); + asoc = sctp_id2assoc(sk, *associd); if (!asoc) - goto out; - - retval = sctp_send_reset_assoc(asoc); + return -EINVAL; -out: - return retval; + return sctp_send_reset_assoc(asoc); } static int sctp_setsockopt_add_streams(struct sock *sk, - char __user *optval, + struct sctp_add_streams *params, unsigned int optlen) { struct sctp_association *asoc; - struct sctp_add_streams params; - int retval = -EINVAL; - if (optlen != sizeof(params)) - goto out; - - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } + if (optlen != sizeof(*params)) + return -EINVAL; - asoc = sctp_id2assoc(sk, params.sas_assoc_id); + asoc = sctp_id2assoc(sk, params->sas_assoc_id); if (!asoc) - goto out; - - retval = sctp_send_add_streams(asoc, ¶ms); + return -EINVAL; -out: - return retval; + return sctp_send_add_streams(asoc, params); } static int sctp_setsockopt_scheduler(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; - struct sctp_assoc_value params; int retval = 0; - if (optlen < sizeof(params)) + if (optlen < sizeof(*params)) return -EINVAL; - optlen = sizeof(params); - if (copy_from_user(¶ms, optval, optlen)) - return -EFAULT; - - if (params.assoc_value > SCTP_SS_MAX) + if (params->assoc_value > SCTP_SS_MAX) return -EINVAL; - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) - return sctp_sched_set_sched(asoc, params.assoc_value); + return sctp_sched_set_sched(asoc, params->assoc_value); if (sctp_style(sk, TCP)) - params.assoc_id = SCTP_FUTURE_ASSOC; + params->assoc_id = SCTP_FUTURE_ASSOC; - if (params.assoc_id == SCTP_FUTURE_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) - sp->default_ss = params.assoc_value; + if (params->assoc_id == SCTP_FUTURE_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) + sp->default_ss = params->assoc_value; - if (params.assoc_id == SCTP_CURRENT_ASSOC || - params.assoc_id == SCTP_ALL_ASSOC) { + if (params->assoc_id == SCTP_CURRENT_ASSOC || + params->assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &sp->ep->asocs, asocs) { int ret = sctp_sched_set_sched(asoc, - params.assoc_value); + params->assoc_value); if (ret && !retval) retval = ret; @@ -4328,38 +4178,32 @@ static int sctp_setsockopt_scheduler(struct sock *sk, } static int sctp_setsockopt_scheduler_value(struct sock *sk, - char __user *optval, + struct sctp_stream_value *params, unsigned int optlen) { - struct sctp_stream_value params; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen < sizeof(params)) - goto out; - - optlen = sizeof(params); - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; + if (optlen < sizeof(*params)) goto out; - } - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_CURRENT_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_CURRENT_ASSOC && sctp_style(sk, UDP)) goto out; if (asoc) { - retval = sctp_sched_set_value(asoc, params.stream_id, - params.stream_value, GFP_KERNEL); + retval = sctp_sched_set_value(asoc, params->stream_id, + params->stream_value, GFP_KERNEL); goto out; } retval = 0; list_for_each_entry(asoc, &sctp_sk(sk)->ep->asocs, asocs) { - int ret = sctp_sched_set_value(asoc, params.stream_id, - params.stream_value, GFP_KERNEL); + int ret = sctp_sched_set_value(asoc, params->stream_id, + params->stream_value, + GFP_KERNEL); if (ret && !retval) /* try to return the 1st error. */ retval = ret; } @@ -4369,46 +4213,30 @@ out: } static int sctp_setsockopt_interleaving_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *p, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); - struct sctp_assoc_value params; struct sctp_association *asoc; - int retval = -EINVAL; - - if (optlen < sizeof(params)) - goto out; - optlen = sizeof(params); - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } + if (optlen < sizeof(*p)) + return -EINVAL; - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && - sctp_style(sk, UDP)) - goto out; + asoc = sctp_id2assoc(sk, p->assoc_id); + if (!asoc && p->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) + return -EINVAL; if (!sock_net(sk)->sctp.intl_enable || !sp->frag_interleave) { - retval = -EPERM; - goto out; + return -EPERM; } - sp->ep->intl_enable = !!params.assoc_value; - - retval = 0; - -out: - return retval; + sp->ep->intl_enable = !!p->assoc_value; + return 0; } -static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval, +static int sctp_setsockopt_reuse_port(struct sock *sk, int *val, unsigned int optlen) { - int val; - if (!sctp_style(sk, TCP)) return -EOPNOTSUPP; @@ -4418,10 +4246,7 @@ static int sctp_setsockopt_reuse_port(struct sock *sk, char __user *optval, if (optlen < sizeof(int)) return -EINVAL; - if (get_user(val, (int __user *)optval)) - return -EFAULT; - - sctp_sk(sk)->reuse = !!val; + sctp_sk(sk)->reuse = !!*val; return 0; } @@ -4447,45 +4272,40 @@ static int sctp_assoc_ulpevent_type_set(struct sctp_event *param, return 0; } -static int sctp_setsockopt_event(struct sock *sk, char __user *optval, +static int sctp_setsockopt_event(struct sock *sk, struct sctp_event *param, unsigned int optlen) { struct sctp_sock *sp = sctp_sk(sk); struct sctp_association *asoc; - struct sctp_event param; int retval = 0; - if (optlen < sizeof(param)) + if (optlen < sizeof(*param)) return -EINVAL; - optlen = sizeof(param); - if (copy_from_user(¶m, optval, optlen)) - return -EFAULT; - - if (param.se_type < SCTP_SN_TYPE_BASE || - param.se_type > SCTP_SN_TYPE_MAX) + if (param->se_type < SCTP_SN_TYPE_BASE || + param->se_type > SCTP_SN_TYPE_MAX) return -EINVAL; - asoc = sctp_id2assoc(sk, param.se_assoc_id); - if (!asoc && param.se_assoc_id > SCTP_ALL_ASSOC && + asoc = sctp_id2assoc(sk, param->se_assoc_id); + if (!asoc && param->se_assoc_id > SCTP_ALL_ASSOC && sctp_style(sk, UDP)) return -EINVAL; if (asoc) - return sctp_assoc_ulpevent_type_set(¶m, asoc); + return sctp_assoc_ulpevent_type_set(param, asoc); if (sctp_style(sk, TCP)) - param.se_assoc_id = SCTP_FUTURE_ASSOC; + param->se_assoc_id = SCTP_FUTURE_ASSOC; - if (param.se_assoc_id == SCTP_FUTURE_ASSOC || - param.se_assoc_id == SCTP_ALL_ASSOC) + if (param->se_assoc_id == SCTP_FUTURE_ASSOC || + param->se_assoc_id == SCTP_ALL_ASSOC) sctp_ulpevent_type_set(&sp->subscribe, - param.se_type, param.se_on); + param->se_type, param->se_on); - if (param.se_assoc_id == SCTP_CURRENT_ASSOC || - param.se_assoc_id == SCTP_ALL_ASSOC) { + if (param->se_assoc_id == SCTP_CURRENT_ASSOC || + param->se_assoc_id == SCTP_ALL_ASSOC) { list_for_each_entry(asoc, &sp->ep->asocs, asocs) { - int ret = sctp_assoc_ulpevent_type_set(¶m, asoc); + int ret = sctp_assoc_ulpevent_type_set(param, asoc); if (ret && !retval) retval = ret; @@ -4496,29 +4316,23 @@ static int sctp_setsockopt_event(struct sock *sk, char __user *optval, } static int sctp_setsockopt_asconf_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; struct sctp_endpoint *ep; int retval = -EINVAL; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) goto out; - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) goto out; ep = sctp_sk(sk)->ep; - ep->asconf_enable = !!params.assoc_value; + ep->asconf_enable = !!params->assoc_value; if (ep->asconf_enable && ep->auth_enable) { sctp_auth_ep_add_chunkid(ep, SCTP_CID_ASCONF); @@ -4532,29 +4346,23 @@ out: } static int sctp_setsockopt_auth_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; struct sctp_endpoint *ep; int retval = -EINVAL; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) goto out; - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) goto out; ep = sctp_sk(sk)->ep; - if (params.assoc_value) { + if (params->assoc_value) { retval = sctp_auth_init(ep, GFP_KERNEL); if (retval) goto out; @@ -4564,7 +4372,7 @@ static int sctp_setsockopt_auth_supported(struct sock *sk, } } - ep->auth_enable = !!params.assoc_value; + ep->auth_enable = !!params->assoc_value; retval = 0; out: @@ -4572,27 +4380,21 @@ out: } static int sctp_setsockopt_ecn_supported(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) goto out; - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; - goto out; - } - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) goto out; - sctp_sk(sk)->ep->ecn_enable = !!params.assoc_value; + sctp_sk(sk)->ep->ecn_enable = !!params->assoc_value; retval = 0; out: @@ -4600,39 +4402,138 @@ out: } static int sctp_setsockopt_pf_expose(struct sock *sk, - char __user *optval, + struct sctp_assoc_value *params, unsigned int optlen) { - struct sctp_assoc_value params; struct sctp_association *asoc; int retval = -EINVAL; - if (optlen != sizeof(params)) + if (optlen != sizeof(*params)) goto out; - if (copy_from_user(¶ms, optval, optlen)) { - retval = -EFAULT; + if (params->assoc_value > SCTP_PF_EXPOSE_MAX) goto out; - } - if (params.assoc_value > SCTP_PF_EXPOSE_MAX) - goto out; - - asoc = sctp_id2assoc(sk, params.assoc_id); - if (!asoc && params.assoc_id != SCTP_FUTURE_ASSOC && + asoc = sctp_id2assoc(sk, params->assoc_id); + if (!asoc && params->assoc_id != SCTP_FUTURE_ASSOC && sctp_style(sk, UDP)) goto out; if (asoc) - asoc->pf_expose = params.assoc_value; + asoc->pf_expose = params->assoc_value; else - sctp_sk(sk)->pf_expose = params.assoc_value; + sctp_sk(sk)->pf_expose = params->assoc_value; retval = 0; out: return retval; } +static int sctp_setsockopt_encap_port(struct sock *sk, + struct sctp_udpencaps *encap, + unsigned int optlen) +{ + struct sctp_association *asoc; + struct sctp_transport *t; + __be16 encap_port; + + if (optlen != sizeof(*encap)) + return -EINVAL; + + /* If an address other than INADDR_ANY is specified, and + * no transport is found, then the request is invalid. + */ + encap_port = (__force __be16)encap->sue_port; + if (!sctp_is_any(sk, (union sctp_addr *)&encap->sue_address)) { + t = sctp_addr_id2transport(sk, &encap->sue_address, + encap->sue_assoc_id); + if (!t) + return -EINVAL; + + t->encap_port = encap_port; + return 0; + } + + /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the + * socket is a one to many style socket, and an association + * was not found, then the id was invalid. + */ + asoc = sctp_id2assoc(sk, encap->sue_assoc_id); + if (!asoc && encap->sue_assoc_id != SCTP_FUTURE_ASSOC && + sctp_style(sk, UDP)) + return -EINVAL; + + /* If changes are for association, also apply encap_port to + * each transport. + */ + if (asoc) { + list_for_each_entry(t, &asoc->peer.transport_addr_list, + transports) + t->encap_port = encap_port; + + asoc->encap_port = encap_port; + return 0; + } + + sctp_sk(sk)->encap_port = encap_port; + return 0; +} + +static int sctp_setsockopt_probe_interval(struct sock *sk, + struct sctp_probeinterval *params, + unsigned int optlen) +{ + struct sctp_association *asoc; + struct sctp_transport *t; + __u32 probe_interval; + + if (optlen != sizeof(*params)) + return -EINVAL; + + probe_interval = params->spi_interval; + if (probe_interval && probe_interval < SCTP_PROBE_TIMER_MIN) + return -EINVAL; + + /* If an address other than INADDR_ANY is specified, and + * no transport is found, then the request is invalid. + */ + if (!sctp_is_any(sk, (union sctp_addr *)¶ms->spi_address)) { + t = sctp_addr_id2transport(sk, ¶ms->spi_address, + params->spi_assoc_id); + if (!t) + return -EINVAL; + + t->probe_interval = msecs_to_jiffies(probe_interval); + sctp_transport_pl_reset(t); + return 0; + } + + /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the + * socket is a one to many style socket, and an association + * was not found, then the id was invalid. + */ + asoc = sctp_id2assoc(sk, params->spi_assoc_id); + if (!asoc && params->spi_assoc_id != SCTP_FUTURE_ASSOC && + sctp_style(sk, UDP)) + return -EINVAL; + + /* If changes are for association, also apply probe_interval to + * each transport. + */ + if (asoc) { + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) { + t->probe_interval = msecs_to_jiffies(probe_interval); + sctp_transport_pl_reset(t); + } + + asoc->probe_interval = msecs_to_jiffies(probe_interval); + return 0; + } + + sctp_sk(sk)->probe_interval = probe_interval; + return 0; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -4653,8 +4554,9 @@ out: * optlen - the size of the buffer. */ static int sctp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen) + sockptr_t optval, unsigned int optlen) { + void *kopt = NULL; int retval = 0; pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname); @@ -4667,8 +4569,18 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, */ if (level != SOL_SCTP) { struct sctp_af *af = sctp_sk(sk)->pf->af; - retval = af->setsockopt(sk, level, optname, optval, optlen); - goto out_nounlock; + + return af->setsockopt(sk, level, optname, optval, optlen); + } + + if (optlen > 0) { + /* Trim it to the biggest size sctp sockopt may need if necessary */ + optlen = min_t(unsigned int, optlen, + PAGE_ALIGN(USHRT_MAX + + sizeof(__u16) * sizeof(struct sctp_reset_streams))); + kopt = memdup_sockptr(optval, optlen); + if (IS_ERR(kopt)) + return PTR_ERR(kopt); } lock_sock(sk); @@ -4676,179 +4588,180 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, switch (optname) { case SCTP_SOCKOPT_BINDX_ADD: /* 'optlen' is the size of the addresses buffer. */ - retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval, - optlen, SCTP_BINDX_ADD_ADDR); + retval = sctp_setsockopt_bindx(sk, kopt, optlen, + SCTP_BINDX_ADD_ADDR); break; case SCTP_SOCKOPT_BINDX_REM: /* 'optlen' is the size of the addresses buffer. */ - retval = sctp_setsockopt_bindx(sk, (struct sockaddr __user *)optval, - optlen, SCTP_BINDX_REM_ADDR); + retval = sctp_setsockopt_bindx(sk, kopt, optlen, + SCTP_BINDX_REM_ADDR); break; case SCTP_SOCKOPT_CONNECTX_OLD: /* 'optlen' is the size of the addresses buffer. */ - retval = sctp_setsockopt_connectx_old(sk, - (struct sockaddr __user *)optval, - optlen); + retval = sctp_setsockopt_connectx_old(sk, kopt, optlen); break; case SCTP_SOCKOPT_CONNECTX: /* 'optlen' is the size of the addresses buffer. */ - retval = sctp_setsockopt_connectx(sk, - (struct sockaddr __user *)optval, - optlen); + retval = sctp_setsockopt_connectx(sk, kopt, optlen); break; case SCTP_DISABLE_FRAGMENTS: - retval = sctp_setsockopt_disable_fragments(sk, optval, optlen); + retval = sctp_setsockopt_disable_fragments(sk, kopt, optlen); break; case SCTP_EVENTS: - retval = sctp_setsockopt_events(sk, optval, optlen); + retval = sctp_setsockopt_events(sk, kopt, optlen); break; case SCTP_AUTOCLOSE: - retval = sctp_setsockopt_autoclose(sk, optval, optlen); + retval = sctp_setsockopt_autoclose(sk, kopt, optlen); break; case SCTP_PEER_ADDR_PARAMS: - retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); + retval = sctp_setsockopt_peer_addr_params(sk, kopt, optlen); break; case SCTP_DELAYED_SACK: - retval = sctp_setsockopt_delayed_ack(sk, optval, optlen); + retval = sctp_setsockopt_delayed_ack(sk, kopt, optlen); break; case SCTP_PARTIAL_DELIVERY_POINT: - retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen); + retval = sctp_setsockopt_partial_delivery_point(sk, kopt, optlen); break; case SCTP_INITMSG: - retval = sctp_setsockopt_initmsg(sk, optval, optlen); + retval = sctp_setsockopt_initmsg(sk, kopt, optlen); break; case SCTP_DEFAULT_SEND_PARAM: - retval = sctp_setsockopt_default_send_param(sk, optval, - optlen); + retval = sctp_setsockopt_default_send_param(sk, kopt, optlen); break; case SCTP_DEFAULT_SNDINFO: - retval = sctp_setsockopt_default_sndinfo(sk, optval, optlen); + retval = sctp_setsockopt_default_sndinfo(sk, kopt, optlen); break; case SCTP_PRIMARY_ADDR: - retval = sctp_setsockopt_primary_addr(sk, optval, optlen); + retval = sctp_setsockopt_primary_addr(sk, kopt, optlen); break; case SCTP_SET_PEER_PRIMARY_ADDR: - retval = sctp_setsockopt_peer_primary_addr(sk, optval, optlen); + retval = sctp_setsockopt_peer_primary_addr(sk, kopt, optlen); break; case SCTP_NODELAY: - retval = sctp_setsockopt_nodelay(sk, optval, optlen); + retval = sctp_setsockopt_nodelay(sk, kopt, optlen); break; case SCTP_RTOINFO: - retval = sctp_setsockopt_rtoinfo(sk, optval, optlen); + retval = sctp_setsockopt_rtoinfo(sk, kopt, optlen); break; case SCTP_ASSOCINFO: - retval = sctp_setsockopt_associnfo(sk, optval, optlen); + retval = sctp_setsockopt_associnfo(sk, kopt, optlen); break; case SCTP_I_WANT_MAPPED_V4_ADDR: - retval = sctp_setsockopt_mappedv4(sk, optval, optlen); + retval = sctp_setsockopt_mappedv4(sk, kopt, optlen); break; case SCTP_MAXSEG: - retval = sctp_setsockopt_maxseg(sk, optval, optlen); + retval = sctp_setsockopt_maxseg(sk, kopt, optlen); break; case SCTP_ADAPTATION_LAYER: - retval = sctp_setsockopt_adaptation_layer(sk, optval, optlen); + retval = sctp_setsockopt_adaptation_layer(sk, kopt, optlen); break; case SCTP_CONTEXT: - retval = sctp_setsockopt_context(sk, optval, optlen); + retval = sctp_setsockopt_context(sk, kopt, optlen); break; case SCTP_FRAGMENT_INTERLEAVE: - retval = sctp_setsockopt_fragment_interleave(sk, optval, optlen); + retval = sctp_setsockopt_fragment_interleave(sk, kopt, optlen); break; case SCTP_MAX_BURST: - retval = sctp_setsockopt_maxburst(sk, optval, optlen); + retval = sctp_setsockopt_maxburst(sk, kopt, optlen); break; case SCTP_AUTH_CHUNK: - retval = sctp_setsockopt_auth_chunk(sk, optval, optlen); + retval = sctp_setsockopt_auth_chunk(sk, kopt, optlen); break; case SCTP_HMAC_IDENT: - retval = sctp_setsockopt_hmac_ident(sk, optval, optlen); + retval = sctp_setsockopt_hmac_ident(sk, kopt, optlen); break; case SCTP_AUTH_KEY: - retval = sctp_setsockopt_auth_key(sk, optval, optlen); + retval = sctp_setsockopt_auth_key(sk, kopt, optlen); break; case SCTP_AUTH_ACTIVE_KEY: - retval = sctp_setsockopt_active_key(sk, optval, optlen); + retval = sctp_setsockopt_active_key(sk, kopt, optlen); break; case SCTP_AUTH_DELETE_KEY: - retval = sctp_setsockopt_del_key(sk, optval, optlen); + retval = sctp_setsockopt_del_key(sk, kopt, optlen); break; case SCTP_AUTH_DEACTIVATE_KEY: - retval = sctp_setsockopt_deactivate_key(sk, optval, optlen); + retval = sctp_setsockopt_deactivate_key(sk, kopt, optlen); break; case SCTP_AUTO_ASCONF: - retval = sctp_setsockopt_auto_asconf(sk, optval, optlen); + retval = sctp_setsockopt_auto_asconf(sk, kopt, optlen); break; case SCTP_PEER_ADDR_THLDS: - retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen, + retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen, false); break; case SCTP_PEER_ADDR_THLDS_V2: - retval = sctp_setsockopt_paddr_thresholds(sk, optval, optlen, + retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen, true); break; case SCTP_RECVRCVINFO: - retval = sctp_setsockopt_recvrcvinfo(sk, optval, optlen); + retval = sctp_setsockopt_recvrcvinfo(sk, kopt, optlen); break; case SCTP_RECVNXTINFO: - retval = sctp_setsockopt_recvnxtinfo(sk, optval, optlen); + retval = sctp_setsockopt_recvnxtinfo(sk, kopt, optlen); break; case SCTP_PR_SUPPORTED: - retval = sctp_setsockopt_pr_supported(sk, optval, optlen); + retval = sctp_setsockopt_pr_supported(sk, kopt, optlen); break; case SCTP_DEFAULT_PRINFO: - retval = sctp_setsockopt_default_prinfo(sk, optval, optlen); + retval = sctp_setsockopt_default_prinfo(sk, kopt, optlen); break; case SCTP_RECONFIG_SUPPORTED: - retval = sctp_setsockopt_reconfig_supported(sk, optval, optlen); + retval = sctp_setsockopt_reconfig_supported(sk, kopt, optlen); break; case SCTP_ENABLE_STREAM_RESET: - retval = sctp_setsockopt_enable_strreset(sk, optval, optlen); + retval = sctp_setsockopt_enable_strreset(sk, kopt, optlen); break; case SCTP_RESET_STREAMS: - retval = sctp_setsockopt_reset_streams(sk, optval, optlen); + retval = sctp_setsockopt_reset_streams(sk, kopt, optlen); break; case SCTP_RESET_ASSOC: - retval = sctp_setsockopt_reset_assoc(sk, optval, optlen); + retval = sctp_setsockopt_reset_assoc(sk, kopt, optlen); break; case SCTP_ADD_STREAMS: - retval = sctp_setsockopt_add_streams(sk, optval, optlen); + retval = sctp_setsockopt_add_streams(sk, kopt, optlen); break; case SCTP_STREAM_SCHEDULER: - retval = sctp_setsockopt_scheduler(sk, optval, optlen); + retval = sctp_setsockopt_scheduler(sk, kopt, optlen); break; case SCTP_STREAM_SCHEDULER_VALUE: - retval = sctp_setsockopt_scheduler_value(sk, optval, optlen); + retval = sctp_setsockopt_scheduler_value(sk, kopt, optlen); break; case SCTP_INTERLEAVING_SUPPORTED: - retval = sctp_setsockopt_interleaving_supported(sk, optval, + retval = sctp_setsockopt_interleaving_supported(sk, kopt, optlen); break; case SCTP_REUSE_PORT: - retval = sctp_setsockopt_reuse_port(sk, optval, optlen); + retval = sctp_setsockopt_reuse_port(sk, kopt, optlen); break; case SCTP_EVENT: - retval = sctp_setsockopt_event(sk, optval, optlen); + retval = sctp_setsockopt_event(sk, kopt, optlen); break; case SCTP_ASCONF_SUPPORTED: - retval = sctp_setsockopt_asconf_supported(sk, optval, optlen); + retval = sctp_setsockopt_asconf_supported(sk, kopt, optlen); break; case SCTP_AUTH_SUPPORTED: - retval = sctp_setsockopt_auth_supported(sk, optval, optlen); + retval = sctp_setsockopt_auth_supported(sk, kopt, optlen); break; case SCTP_ECN_SUPPORTED: - retval = sctp_setsockopt_ecn_supported(sk, optval, optlen); + retval = sctp_setsockopt_ecn_supported(sk, kopt, optlen); break; case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE: - retval = sctp_setsockopt_pf_expose(sk, optval, optlen); + retval = sctp_setsockopt_pf_expose(sk, kopt, optlen); + break; + case SCTP_REMOTE_UDP_ENCAPS_PORT: + retval = sctp_setsockopt_encap_port(sk, kopt, optlen); + break; + case SCTP_PLPMTUD_PROBE_INTERVAL: + retval = sctp_setsockopt_probe_interval(sk, kopt, optlen); break; default: retval = -ENOPROTOOPT; @@ -4856,8 +4769,7 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, } release_sock(sk); - -out_nounlock: + kfree(kopt); return retval; } @@ -5091,6 +5003,8 @@ static int sctp_init_sock(struct sock *sk) * be modified via SCTP_PEER_ADDR_PARAMS */ sp->hbinterval = net->sctp.hb_interval; + sp->udp_port = htons(net->sctp.udp_port); + sp->encap_port = htons(net->sctp.encap_port); sp->pathmaxrxt = net->sctp.max_retrans_path; sp->pf_retrans = net->sctp.pf_retrans; sp->ps_retrans = net->sctp.ps_retrans; @@ -5135,6 +5049,7 @@ static int sctp_init_sock(struct sock *sk) atomic_set(&sp->pd_mode, 0); skb_queue_head_init(&sp->pd_lobby); sp->frag_interleave = 0; + sp->probe_interval = net->sctp.probe_interval; /* Create a per socket endpoint structure. Even if we * change the data structure relationships, this may still @@ -5150,25 +5065,9 @@ static int sctp_init_sock(struct sock *sk) SCTP_DBG_OBJCNT_INC(sock); - local_bh_disable(); sk_sockets_allocated_inc(sk); sock_prot_inuse_add(net, sk->sk_prot, 1); - /* Nothing can fail after this block, otherwise - * sctp_destroy_sock() will be called without addr_wq_lock held - */ - if (net->sctp.default_auto_asconf) { - spin_lock(&sock_net(sk)->sctp.addr_wq_lock); - list_add_tail(&sp->auto_asconf_list, - &net->sctp.auto_asconf_splist); - sp->do_auto_asconf = 1; - spin_unlock(&sock_net(sk)->sctp.addr_wq_lock); - } else { - sp->do_auto_asconf = 0; - } - - local_bh_enable(); - return 0; } @@ -5194,10 +5093,8 @@ static void sctp_destroy_sock(struct sock *sk) list_del(&sp->auto_asconf_list); } sctp_endpoint_free(sp->ep); - local_bh_disable(); sk_sockets_allocated_dec(sk); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - local_bh_enable(); } /* Triggered when there are no references on the socket anymore */ @@ -5333,14 +5230,14 @@ int sctp_get_sctp_info(struct sock *sk, struct sctp_association *asoc, EXPORT_SYMBOL_GPL(sctp_get_sctp_info); /* use callback to avoid exporting the core structure */ -void sctp_transport_walk_start(struct rhashtable_iter *iter) +void sctp_transport_walk_start(struct rhashtable_iter *iter) __acquires(RCU) { rhltable_walk_enter(&sctp_transport_hashtable, iter); rhashtable_walk_start(iter); } -void sctp_transport_walk_stop(struct rhashtable_iter *iter) +void sctp_transport_walk_stop(struct rhashtable_iter *iter) __releases(RCU) { rhashtable_walk_stop(iter); rhashtable_walk_exit(iter); @@ -5394,14 +5291,14 @@ int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), void *p) { int err = 0; int hash = 0; - struct sctp_ep_common *epb; + struct sctp_endpoint *ep; struct sctp_hashbucket *head; for (head = sctp_ep_hashtable; hash < sctp_ep_hashsize; hash++, head++) { read_lock_bh(&head->lock); - sctp_for_each_hentry(epb, &head->chain) { - err = cb(sctp_ep(epb), p); + sctp_for_each_hentry(ep, &head->chain) { + err = cb(ep, p); if (err) break; } @@ -5412,32 +5309,41 @@ int sctp_for_each_endpoint(int (*cb)(struct sctp_endpoint *, void *), } EXPORT_SYMBOL_GPL(sctp_for_each_endpoint); -int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *), - struct net *net, +int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, const union sctp_addr *laddr, const union sctp_addr *paddr, void *p) { struct sctp_transport *transport; - int err; + struct sctp_endpoint *ep; + int err = -ENOENT; rcu_read_lock(); transport = sctp_addrs_lookup_transport(net, laddr, paddr); + if (!transport) { + rcu_read_unlock(); + return err; + } + ep = transport->asoc->ep; + if (!sctp_endpoint_hold(ep)) { /* asoc can be peeled off */ + sctp_transport_put(transport); + rcu_read_unlock(); + return err; + } rcu_read_unlock(); - if (!transport) - return -ENOENT; - err = cb(transport, p); + err = cb(ep, transport, p); + sctp_endpoint_put(ep); sctp_transport_put(transport); - return err; } EXPORT_SYMBOL_GPL(sctp_transport_lookup_process); -int sctp_for_each_transport(int (*cb)(struct sctp_transport *, void *), - int (*cb_done)(struct sctp_transport *, void *), - struct net *net, int *pos, void *p) { +int sctp_transport_traverse_process(sctp_callback_t cb, sctp_callback_t cb_done, + struct net *net, int *pos, void *p) +{ struct rhashtable_iter hti; struct sctp_transport *tsp; + struct sctp_endpoint *ep; int ret; again: @@ -5446,26 +5352,32 @@ again: tsp = sctp_transport_get_idx(net, &hti, *pos + 1); for (; !IS_ERR_OR_NULL(tsp); tsp = sctp_transport_get_next(net, &hti)) { - ret = cb(tsp, p); - if (ret) - break; + ep = tsp->asoc->ep; + if (sctp_endpoint_hold(ep)) { /* asoc can be peeled off */ + ret = cb(ep, tsp, p); + if (ret) + break; + sctp_endpoint_put(ep); + } (*pos)++; sctp_transport_put(tsp); } sctp_transport_walk_stop(&hti); if (ret) { - if (cb_done && !cb_done(tsp, p)) { + if (cb_done && !cb_done(ep, tsp, p)) { (*pos)++; + sctp_endpoint_put(ep); sctp_transport_put(tsp); goto again; } + sctp_endpoint_put(ep); sctp_transport_put(tsp); } return ret; } -EXPORT_SYMBOL_GPL(sctp_for_each_transport); +EXPORT_SYMBOL_GPL(sctp_transport_traverse_process); /* 7.2.1 Association Status (SCTP_STATUS) @@ -5721,7 +5633,7 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) * Set the daddr and initialize id to something more random and also * copy over any ip options. */ - sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sk); + sp->pf->to_sk_daddr(&asoc->peer.primary_addr, sock->sk); sp->pf->copy_ip_options(sk, sock->sk); /* Populate the fields of the newsk from the oldsk and migrate the @@ -8005,6 +7917,125 @@ out: return retval; } +static int sctp_getsockopt_encap_port(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + struct sctp_association *asoc; + struct sctp_udpencaps encap; + struct sctp_transport *t; + __be16 encap_port; + + if (len < sizeof(encap)) + return -EINVAL; + + len = sizeof(encap); + if (copy_from_user(&encap, optval, len)) + return -EFAULT; + + /* If an address other than INADDR_ANY is specified, and + * no transport is found, then the request is invalid. + */ + if (!sctp_is_any(sk, (union sctp_addr *)&encap.sue_address)) { + t = sctp_addr_id2transport(sk, &encap.sue_address, + encap.sue_assoc_id); + if (!t) { + pr_debug("%s: failed no transport\n", __func__); + return -EINVAL; + } + + encap_port = t->encap_port; + goto out; + } + + /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the + * socket is a one to many style socket, and an association + * was not found, then the id was invalid. + */ + asoc = sctp_id2assoc(sk, encap.sue_assoc_id); + if (!asoc && encap.sue_assoc_id != SCTP_FUTURE_ASSOC && + sctp_style(sk, UDP)) { + pr_debug("%s: failed no association\n", __func__); + return -EINVAL; + } + + if (asoc) { + encap_port = asoc->encap_port; + goto out; + } + + encap_port = sctp_sk(sk)->encap_port; + +out: + encap.sue_port = (__force uint16_t)encap_port; + if (copy_to_user(optval, &encap, len)) + return -EFAULT; + + if (put_user(len, optlen)) + return -EFAULT; + + return 0; +} + +static int sctp_getsockopt_probe_interval(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_probeinterval params; + struct sctp_association *asoc; + struct sctp_transport *t; + __u32 probe_interval; + + if (len < sizeof(params)) + return -EINVAL; + + len = sizeof(params); + if (copy_from_user(¶ms, optval, len)) + return -EFAULT; + + /* If an address other than INADDR_ANY is specified, and + * no transport is found, then the request is invalid. + */ + if (!sctp_is_any(sk, (union sctp_addr *)¶ms.spi_address)) { + t = sctp_addr_id2transport(sk, ¶ms.spi_address, + params.spi_assoc_id); + if (!t) { + pr_debug("%s: failed no transport\n", __func__); + return -EINVAL; + } + + probe_interval = jiffies_to_msecs(t->probe_interval); + goto out; + } + + /* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the + * socket is a one to many style socket, and an association + * was not found, then the id was invalid. + */ + asoc = sctp_id2assoc(sk, params.spi_assoc_id); + if (!asoc && params.spi_assoc_id != SCTP_FUTURE_ASSOC && + sctp_style(sk, UDP)) { + pr_debug("%s: failed no association\n", __func__); + return -EINVAL; + } + + if (asoc) { + probe_interval = jiffies_to_msecs(asoc->probe_interval); + goto out; + } + + probe_interval = sctp_sk(sk)->probe_interval; + +out: + params.spi_interval = probe_interval; + if (copy_to_user(optval, ¶ms, len)) + return -EFAULT; + + if (put_user(len, optlen)) + return -EFAULT; + + return 0; +} + static int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -8225,6 +8256,12 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE: retval = sctp_getsockopt_pf_expose(sk, len, optval, optlen); break; + case SCTP_REMOTE_UDP_ENCAPS_PORT: + retval = sctp_getsockopt_encap_port(sk, len, optval, optlen); + break; + case SCTP_PLPMTUD_PROBE_INTERVAL: + retval = sctp_getsockopt_probe_interval(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -8275,8 +8312,6 @@ static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr) pr_debug("%s: begins, snum:%d\n", __func__, snum); - local_bh_disable(); - if (snum == 0) { /* Search for an available port. */ int low, high, remaining, index; @@ -8284,7 +8319,7 @@ static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr) inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; - rover = prandom_u32() % remaining + low; + rover = prandom_u32_max(remaining) + low; do { rover++; @@ -8294,20 +8329,21 @@ static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr) continue; index = sctp_phashfn(net, rover); head = &sctp_port_hashtable[index]; - spin_lock(&head->lock); + spin_lock_bh(&head->lock); sctp_for_each_hentry(pp, &head->chain) if ((pp->port == rover) && net_eq(net, pp->net)) goto next; break; next: - spin_unlock(&head->lock); + spin_unlock_bh(&head->lock); + cond_resched(); } while (--remaining > 0); /* Exhausted local port range during search? */ ret = 1; if (remaining <= 0) - goto fail; + return ret; /* OK, here is the one we will use. HEAD (the port * hash table list entry) is non-NULL and we hold it's @@ -8322,7 +8358,7 @@ static int sctp_get_port_local(struct sock *sk, union sctp_addr *addr) * port iterator, pp being NULL. */ head = &sctp_port_hashtable[sctp_phashfn(net, snum)]; - spin_lock(&head->lock); + spin_lock_bh(&head->lock); sctp_for_each_hentry(pp, &head->chain) { if ((pp->port == snum) && net_eq(pp->net, net)) goto pp_found; @@ -8422,10 +8458,7 @@ success: ret = 0; fail_unlock: - spin_unlock(&head->lock); - -fail: - local_bh_enable(); + spin_unlock_bh(&head->lock); return ret; } @@ -8942,14 +8975,13 @@ out: * Note: This is pretty much the same routine as in core/datagram.c * with a few changes to make lksctp work. */ -struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, - int noblock, int *err) +struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err) { int error; struct sk_buff *skb; long timeo; - timeo = sock_rcvtimeo(sk, noblock); + timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); pr_debug("%s: timeo:%ld, max:%ld\n", __func__, timeo, MAX_SCHEDULE_TIMEOUT); @@ -8982,7 +9014,7 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, break; if (sk_can_busy_loop(sk)) { - sk_busy_loop(sk, noblock); + sk_busy_loop(sk, flags & MSG_DONTWAIT); if (!skb_queue_empty_lockless(&sk->sk_receive_queue)) continue; @@ -9160,8 +9192,6 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, goto do_error; if (signal_pending(current)) goto do_interrupted; - if (sk_under_memory_pressure(sk)) - sk_mem_reclaim(sk); if ((int)msg_len <= sctp_wspace(asoc) && sk_wmem_schedule(sk, msg_len)) break; @@ -9386,7 +9416,6 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, struct inet_sock *inet = inet_sk(sk); struct inet_sock *newinet; struct sctp_sock *sp = sctp_sk(sk); - struct sctp_endpoint *ep = sp->ep; newsk->sk_type = sk->sk_type; newsk->sk_bound_dev_if = sk->sk_bound_dev_if; @@ -9419,7 +9448,7 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, newinet->inet_rcv_saddr = inet->inet_rcv_saddr; newinet->inet_dport = htons(asoc->peer.port); newinet->pmtudisc = inet->pmtudisc; - newinet->inet_id = prandom_u32(); + newinet->inet_id = get_random_u16(); newinet->uc_ttl = inet->uc_ttl; newinet->mc_loop = 1; @@ -9430,22 +9459,19 @@ void sctp_copy_sock(struct sock *newsk, struct sock *sk, if (newsk->sk_flags & SK_FLAGS_TIMESTAMP) net_enable_timestamp(); - /* Set newsk security attributes from orginal sk and connection - * security attribute from ep. + /* Set newsk security attributes from original sk and connection + * security attribute from asoc. */ - security_sctp_sk_clone(ep, sk, newsk); + security_sctp_sk_clone(asoc, sk, newsk); } static inline void sctp_copy_descendant(struct sock *sk_to, const struct sock *sk_from) { - int ancestor_size = sizeof(struct inet_sock) + - sizeof(struct sctp_sock) - - offsetof(struct sctp_sock, pd_lobby); - - if (sk_from->sk_family == PF_INET6) - ancestor_size += sizeof(struct ipv6_pinfo); + size_t ancestor_size = sizeof(struct inet_sock); + ancestor_size += sk_from->sk_prot->obj_size; + ancestor_size -= offsetof(struct sctp_sock, pd_lobby); __inet_sk_copy_descendant(sk_to, sk_from, ancestor_size); } @@ -9507,6 +9533,8 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, return err; } + sctp_auto_asconf_init(newsp); + /* Move any messages in the old socket's receive queue that are for the * peeled off association to the new socket's receive queue. */ @@ -9574,9 +9602,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, * paths won't try to lock it and then oldsk. */ lock_sock_nested(newsk, SINGLE_DEPTH_NESTING); - sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w); + sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w); sctp_assoc_migrate(assoc, newsk); - sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w); + sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w); /* If the association on the newsk is already closed before accept() * is called, set RCV_SHUTDOWN flag. @@ -9610,6 +9638,7 @@ struct proto sctp_prot = { .sendmsg = sctp_sendmsg, .recvmsg = sctp_recvmsg, .bind = sctp_bind, + .bind_add = sctp_bind_add, .backlog_rcv = sctp_backlog_rcv, .hash = sctp_hash, .unhash = sctp_unhash, @@ -9624,7 +9653,10 @@ struct proto sctp_prot = { .sysctl_wmem = sysctl_sctp_wmem, .memory_pressure = &sctp_memory_pressure, .enter_memory_pressure = sctp_enter_memory_pressure, + .memory_allocated = &sctp_memory_allocated, + .per_cpu_fw_alloc = &sctp_memory_per_cpu_fw_alloc, + .sockets_allocated = &sctp_sockets_allocated, }; @@ -9652,6 +9684,7 @@ struct proto sctpv6_prot = { .sendmsg = sctp_sendmsg, .recvmsg = sctp_recvmsg, .bind = sctp_bind, + .bind_add = sctp_bind_add, .backlog_rcv = sctp_backlog_rcv, .hash = sctp_hash, .unhash = sctp_unhash, @@ -9666,7 +9699,10 @@ struct proto sctpv6_prot = { .sysctl_wmem = sysctl_sctp_wmem, .memory_pressure = &sctp_memory_pressure, .enter_memory_pressure = sctp_enter_memory_pressure, + .memory_allocated = &sctp_memory_allocated, + .per_cpu_fw_alloc = &sctp_memory_per_cpu_fw_alloc, + .sockets_allocated = &sctp_sockets_allocated, }; #endif /* IS_ENABLED(CONFIG_IPV6) */ |