/* Get the sndbuf space available at the time on the association. */ staticinlineint sctp_wspace(struct sctp_association *asoc)
{ struct sock *sk = asoc->base.sk;
/* Increment the used sndbuf space count of the corresponding association by *thesizeoftheoutgoingdatachunk. *Also,settheskbdestructorforsndbufaccountinglater. * *Sinceitisalways1-1betweenchunkandskb,andalsoanewskbisalways *allocatedforchunkbundlinginsctp_packet_transmit(),wecanusethe *destructorinthedatachunkskbforthepurposeofthesndbufspace *tracking.
*/ staticinlinevoid sctp_set_owner_w(struct sctp_chunk *chunk)
{ struct sctp_association *asoc = chunk->asoc; struct sock *sk = asoc->base.sk;
/* The sndbuf space is tracked per association. */
sctp_association_hold(asoc);
if (chunk->shkey)
sctp_auth_shkey_hold(chunk->shkey);
skb_set_owner_w(chunk->skb, sk);
chunk->skb->destructor = sctp_wfree; /* Save the chunk pointer in skb for sctp_wfree to use later. */
skb_shinfo(chunk->skb)->destructor_arg = chunk;
/* Verify that this is a valid address. */ staticinlineint sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len)
{ struct sctp_af *af;
/* Verify basic sockaddr. */
af = sctp_sockaddr_af(sctp_sk(sk), addr, len); if (!af) return -EINVAL;
/* Is this a valid SCTP address? */ if (!af->addr_valid(addr, sctp_sk(sk), NULL)) return -EINVAL;
if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr))) return -EINVAL;
return0;
}
/* Look up the association by its id. If this is not a UDP-style *socket,theIDfieldisalwaysignored.
*/ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{ struct sctp_association *asoc = NULL;
/* If this is not a UDP-style socket, assoc id should be ignored. */ if (!sctp_style(sk, UDP)) { /* Return NULL if the socket state is not ESTABLISHED. It *couldbeaTCP-stylelisteningsocketorasocketwhich *hasn'tyetcalledconnect()toestablishanassociation.
*/ if (!sctp_sstate(sk, ESTABLISHED) && !sctp_sstate(sk, CLOSING)) return NULL;
/* Get the first and the only association from the list. */ if (!list_empty(&sctp_sk(sk)->ep->asocs))
asoc = list_entry(sctp_sk(sk)->ep->asocs.next, struct sctp_association, asocs); return asoc;
}
/* Otherwise this is a UDP-style socket. */ if (id <= SCTP_ALL_ASSOC) return NULL;
spin_lock_bh(&sctp_assocs_id_lock);
asoc = (struct sctp_association *)idr_find(&sctp_assocs_id, (int)id); if (asoc && (asoc->base.sk != sk || asoc->base.dead))
asoc = NULL;
spin_unlock_bh(&sctp_assocs_id_lock);
return asoc;
}
/* Look up the transport from an address and an assoc id. If both address and *idarespecified,theassociationsmatchingtheaddressandtheidshouldbe *thesame.
*/ staticstruct sctp_transport *sctp_addr_id2transport(struct sock *sk, struct sockaddr_storage *addr,
sctp_assoc_t id)
{ struct sctp_association *addr_asoc = NULL, *id_asoc = NULL; struct sctp_af *af = sctp_get_af_specific(addr->ss_family); union sctp_addr *laddr = (union sctp_addr *)addr; struct sctp_transport *transport;
if (!af || sctp_verify_addr(sk, laddr, af->sockaddr_len)) return NULL;
staticint sctp_get_port_local(struct sock *, union sctp_addr *);
/* Verify this is a valid sockaddr. */ staticstruct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt, union sctp_addr *addr, int len)
{ struct sctp_af *af;
if (!opt->pf->af_supported(addr->sa.sa_family, opt)) return NULL;
if (addr->sa.sa_family == AF_INET6) { if (len < SIN6_LEN_RFC2133) return NULL; /* V4 mapped address are really of AF_INET family */ if (ipv6_addr_v4mapped(&addr->v6.sin6_addr) &&
!opt->pf->af_supported(AF_INET, opt)) return NULL;
}
/* If we get this far, af is valid. */
af = sctp_get_af_specific(addr->sa.sa_family);
if (len < af->sockaddr_len) return NULL;
return af;
}
staticvoid sctp_auto_asconf_init(struct sctp_sock *sp)
{ struct net *net = sock_net(&sp->inet.sk);
/* Bind a local address either to an endpoint or to an association. */ staticint sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
{ struct net *net = sock_net(sk); struct sctp_sock *sp = sctp_sk(sk); struct sctp_endpoint *ep = sp->ep; struct sctp_bind_addr *bp = &ep->base.bind_addr; struct sctp_af *af; unsignedshort snum; int ret = 0;
/* Common sockaddr verification. */
af = sctp_sockaddr_af(sp, addr, len); if (!af) {
pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n",
__func__, sk, addr, len); return -EINVAL;
}
snum = ntohs(addr->v4.sin_port);
pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n",
__func__, sk, &addr->sa, bp->port, snum, len);
/* PF specific bind() address verification. */ if (!sp->pf->bind_verify(sp, addr)) return -EADDRNOTAVAIL;
/* We must either be unbound, or bind to the same port. *It'sOKtoallow0portsifwearealreadybound. *We'lljustinhertanalreadyboundportinthiscase
*/ if (bp->port) { if (!snum)
snum = bp->port; elseif (snum != bp->port) {
pr_debug("%s: new port %d doesn't match existing port " "%d\n", __func__, snum, bp->port); return -EINVAL;
}
}
if (snum && inet_port_requires_bind_service(net, snum) &&
!ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) return -EACCES;
/* See if the address matches any of the addresses we may have *alreadyboundbeforecheckingagainstotherendpoints.
*/ if (sctp_bind_addr_match(bp, addr, sp)) return -EINVAL;
/* Make sure we are allowed to bind here. *Thefunctionsctp_get_port_local()doesduplicateaddress *detection.
*/
addr->v4.sin_port = htons(snum); if (sctp_get_port_local(sk, addr)) return -EADDRINUSE;
/* Add the address to the bind address list. *UseGFP_ATOMICsinceBHswillbedisabled.
*/
ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
SCTP_ADDR_SRC, GFP_ATOMIC);
if (ret) {
sctp_put_port(sk); return ret;
} /* Copy back into socket for getsockname() use. */
inet_sk(sk)->inet_sport = htons(inet_sk(sk)->inet_num);
sp->pf->to_sk_saddr(addr, sk);
return ret;
}
/* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks * *R1)OneandonlyoneASCONFChunkMAYbeintransitandunacknowledged *atanyonetime.Ifasender,aftersendinganASCONFchunk,decides *itneedstotransferanotherASCONFChunk,itMUSTwaituntilthe *ASCONF-ACKChunkreturnsfromthepreviousASCONFChunkbeforesendinga *subsequentASCONF.Notethisrestrictionbindseachside,soatany *timetwoASCONFmaybein-transitonanygivenassociation(onesent *fromeachendpoint).
*/ staticint sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk)
{ int retval = 0;
/* If there is an outstanding ASCONF chunk, queue it for later *transmission.
*/ if (asoc->addip_last_asconf) {
list_add_tail(&chunk->list, &asoc->addip_chunk_list); goto out;
}
/* Hold the chunk until an ASCONF_ACK is received. */
sctp_chunk_hold(chunk);
retval = sctp_primitive_ASCONF(asoc->base.net, asoc, chunk); if (retval)
sctp_chunk_free(chunk); else
asoc->addip_last_asconf = chunk;
out: return retval;
}
/* Add a list of addresses as bind addresses to local endpoint or *association. * *Basicallyrunthrougheachaddressspecifiedintheaddrs/addrcnt *array/lengthpair,determineifitisIPv6orIPv4andcall *sctp_do_bind()onit. * *Ifanyofthemfails,thentheoperationwillbereversedandthe *onesthatwereaddedwillberemoved. * *Onlysctp_setsockopt_bindx()issupposedtocallthisfunction.
*/ staticint sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ int cnt; int retval = 0; void *addr_buf; struct sockaddr *sa_addr; struct sctp_af *af;
addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { /* The list may contain either IPv4 or IPv6 address; *determinetheaddresslengthforwalkingthruthelist.
*/
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa_family); if (!af) {
retval = -EINVAL; goto err_bindx_add;
}
list_for_each_entry(asoc, &ep->asocs, asocs) { if (!asoc->peer.asconf_capable) continue;
if (asoc->peer.addip_disabled_mask & SCTP_PARAM_ADD_IP) continue;
if (!sctp_state(asoc, ESTABLISHED)) continue;
/* Check if any address in the packed array of addresses is *inthebindaddresslistoftheassociation.Ifso, *donotsendtheasconfchunktoitspeer,butcontinuewith *otherassociations.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family); if (!af) {
retval = -EINVAL; goto out;
}
if (sctp_assoc_lookup_laddr(asoc, addr)) break;
addr_buf += af->sockaddr_len;
} if (i < addrcnt) continue;
/* Use the first valid address in bind addr list of *associationasAddressParameterofASCONFCHUNK.
*/
bp = &asoc->base.bind_addr;
p = bp->address_list.next;
laddr = list_entry(p, struct sctp_sockaddr_entry, list);
chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
addrcnt, SCTP_PARAM_ADD_IP); if (!chunk) {
retval = -ENOMEM; goto out;
}
/* Add the new addresses to the bind address list with *use_as_srcsetto0.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
memcpy(&saveaddr, addr, af->sockaddr_len);
retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
} if (asoc->src_out_of_asoc_ok) { struct sctp_transport *trans;
addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { /* If the bind address list is empty or if there is only one *bindaddress,thereisnothingmoretoberemoved(weneed *atleastoneaddresshere).
*/ if (list_empty(&bp->address_list) ||
(sctp_list_single_entry(&bp->address_list))) {
retval = -EBUSY; goto err_bindx_rem;
}
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa.sa_family); if (!af) {
retval = -EINVAL; goto err_bindx_rem;
}
if (!sa_addr->v4.sin_port)
sa_addr->v4.sin_port = htons(bp->port);
/* FIXME - There is probably a need to check if sk->sk_saddr and *sk->sk_rcv_addrarecurrentlysettooneoftheaddressesto *beremoved.Thisissomethingwhichneedstobelookedinto *whenwearefixingtheoutstandingissueswithmulti-homing *socketroutingandfailoverschemes.Refertocommentsin *sctp_do_bind().-daisy
*/
retval = sctp_del_bind_addr(bp, sa_addr);
addr_buf += af->sockaddr_len;
err_bindx_rem: if (retval < 0) { /* Failed. Add the ones that has been removed back */ if (cnt > 0)
sctp_bindx_add(sk, addrs, cnt); return retval;
}
}
return retval;
}
/* Send an ASCONF chunk with Delete IP address parameters to all the peers of *theassociationsthatarepartoftheendpointindicatingthatalistof *localaddressesareremovedfromtheendpoint. * *Ifanyoftheaddressesisalreadyinthebindaddresslistofthe *association,wedonotsendthechunkforthatassociation.Butitwillnot *affectotherassociations. * *Onlysctp_setsockopt_bindx()issupposedtocallthisfunction.
*/ staticint sctp_send_asconf_del_ip(struct sock *sk, struct sockaddr *addrs, int addrcnt)
{ struct sctp_sock *sp; struct sctp_endpoint *ep; struct sctp_association *asoc; struct sctp_transport *transport; struct sctp_bind_addr *bp; struct sctp_chunk *chunk; union sctp_addr *laddr; void *addr_buf; struct sctp_af *af; struct sctp_sockaddr_entry *saddr; int i; int retval = 0; int stored = 0;
if (asoc->peer.addip_disabled_mask & SCTP_PARAM_DEL_IP) continue;
if (!sctp_state(asoc, ESTABLISHED)) continue;
/* Check if any address in the packed array of addresses is *notpresentinthebindaddresslistoftheassociation. *Ifso,donotsendtheasconfchunktoitspeer,but *continuewithotherassociations.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
laddr = addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family); if (!af) {
retval = -EINVAL; goto out;
}
if (!sctp_assoc_lookup_laddr(asoc, laddr)) break;
addr_buf += af->sockaddr_len;
} if (i < addrcnt) continue;
/* Find one address in the association's bind address list *thatisnotinthepackedarrayofaddresses.Thisisto *makesurethatwedonotdeletealltheaddressesinthe *association.
*/
bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp); if ((laddr == NULL) && (addrcnt == 1)) { if (asoc->asconf_addr_del_pending) continue;
asoc->asconf_addr_del_pending =
kzalloc(sizeof(union sctp_addr), GFP_ATOMIC); if (asoc->asconf_addr_del_pending == NULL) {
retval = -ENOMEM; goto out;
}
asoc->asconf_addr_del_pending->sa.sa_family =
addrs->sa_family;
asoc->asconf_addr_del_pending->v4.sin_port =
htons(bp->port); if (addrs->sa_family == AF_INET) { struct sockaddr_in *sin;
/* We do not need RCU protection throughout this loop *becausethisisdoneunderasocketlockfromthe *setsockoptcall.
*/
chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
SCTP_PARAM_DEL_IP); if (!chunk) {
retval = -ENOMEM; goto out;
}
skip_mkasconf: /* Reset use_as_src flag for the addresses in the bind address *listthataretobedeleted.
*/
addr_buf = addrs; for (i = 0; i < addrcnt; i++) {
laddr = addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family);
list_for_each_entry(saddr, &bp->address_list, list) { if (sctp_cmp_addr_exact(&saddr->a, laddr))
saddr->state = SCTP_ADDR_DEL;
}
addr_buf += af->sockaddr_len;
}
/* Update the route and saddr entries for all the transports *assomeoftheaddressesinthebindaddresslistare *abouttobedeletedandcannotbeusedassourceaddresses.
*/
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
sctp_transport_route(transport, NULL,
sctp_sk(asoc->base.sk));
}
if (stored) /* We don't need to transmit ASCONF */ continue;
retval = sctp_send_asconf(asoc, chunk);
}
out: return retval;
}
/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */ int sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
{ struct sock *sk = sctp_opt2sk(sp); union sctp_addr *addr; struct sctp_af *af;
/* It is safe to write port space in caller. */
addr = &addrw->a;
addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
af = sctp_get_af_specific(addr->sa.sa_family); if (!af) return -EINVAL; if (sctp_verify_addr(sk, addr, af->sockaddr_len)) return -EINVAL;
/* Walk through the addrs buffer and count the number of addresses. */ while (walk_size < addrs_size) { if (walk_size + sizeof(sa_family_t) > addrs_size) return -EINVAL;
sa_addr = addr_buf;
af = sctp_get_af_specific(sa_addr->sa_family);
/* If the address family is not supported or if this address *causestheaddressbuffertooverflowreturnEINVAL.
*/ if (!af || (walk_size + af->sockaddr_len) > addrs_size) return -EINVAL;
addrcnt++;
addr_buf += af->sockaddr_len;
walk_size += af->sockaddr_len;
}
/* Do the work. */ switch (op) { case SCTP_BINDX_ADD_ADDR: /* Allow security module to validate bindx addresses. */
err = security_sctp_bind_connect(sk, SCTP_SOCKOPT_BINDX_ADD,
addrs, addrs_size); if (err) return err;
err = sctp_bindx_add(sk, addrs, addrcnt); if (err) return err; return sctp_send_asconf_add_ip(sk, addrs, addrcnt); case SCTP_BINDX_REM_ADDR:
err = sctp_bindx_rem(sk, addrs, addrcnt); if (err) return err; return sctp_send_asconf_del_ip(sk, addrs, addrcnt);
default: return -EINVAL;
}
}
staticint sctp_bind_add(struct sock *sk, struct sockaddr *addrs, int addrlen)
{ int err;
if (init->sinit_num_ostreams) {
__u16 outcnt = init->sinit_num_ostreams;
asoc->c.sinit_num_ostreams = outcnt; /* outcnt has been changed, need to re-init stream */
err = sctp_stream_init(&asoc->stream, outcnt, 0, GFP_KERNEL); if (err) goto free;
}
if (init->sinit_max_instreams)
asoc->c.sinit_max_instreams = init->sinit_max_instreams;
if (init->sinit_max_attempts)
asoc->max_init_attempts = init->sinit_max_attempts;
if (init->sinit_max_init_timeo)
asoc->max_init_timeo =
msecs_to_jiffies(init->sinit_max_init_timeo);
/* In case the user of sctp_connectx() wants an association *idback,assignonenow.
*/ if (assoc_id) {
err = sctp_assoc_set_id(asoc, GFP_KERNEL); if (err < 0) goto out_free;
}
/* in-kernel sockets don't generally have a file allocated to them *ifalltheydoiscallsock_create_kern().
*/ if (sk->sk_socket->file)
flags = sk->sk_socket->file->f_flags;
/* Clean up any skbs sitting on the receive queue. */
data_was_unread = sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
data_was_unread += sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
/* Walk all associations on an endpoint. */
list_for_each_safe(pos, temp, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
if (sctp_style(sk, TCP)) { /* A closed association can still be in the list if *itbelongstoaTCP-stylelisteningsocketthatis *notyetaccepted.Ifso,freeit.Ifnot,sendan *ABORTorSHUTDOWNbasedonthelingeroptions.
*/ if (sctp_state(asoc, CLOSED)) {
sctp_association_free(asoc); continue;
}
}
/* On a TCP-style socket, block for at most linger_time if set. */ if (sctp_style(sk, TCP) && timeout)
sctp_wait_for_close(sk, timeout);
/* This will run the backlog queue. */
release_sock(sk);
/* Supposedly, no process has access to the socket, but *thenetlayersstillmay. *Also,sctp_destroy_sock()needstobecalledwithaddr_wq_lock *heldandthatshouldbegrabbedbeforesocketlock.
*/
spin_lock_bh(&net->sctp.addr_wq_lock);
bh_lock_sock_nested(sk);
/* Hold the sock, since sk_common_release() will put sock_put() *andwehavejustalittlemorecleanup.
*/
sock_hold(sk);
sk_common_release(sk);
if (!cmsgs->prinfo)
sinfo->sinfo_flags = asoc->default_flags;
}
if (!cmsgs->srinfo && !cmsgs->prinfo)
sinfo->sinfo_timetolive = asoc->default_timetolive;
if (cmsgs->authinfo) { /* Reuse sinfo_tsn to indicate that authinfo was set and *sinfo_ssntosavethekeyidontxpath.
*/
sinfo->sinfo_tsn = 1;
sinfo->sinfo_ssn = cmsgs->authinfo->auth_keynumber;
}
}
/* This is an extended version of skb_pull() that removes the data from the *startofaskbevenwhendataisspreadacrossthelistofskb'sinthe *frag_list.lenspecifiesthetotalamountofdatathatneedstoberemoved. *when'len'bytescouldberemovedfromtheskb,itreturns0. *If'len'exceedsthetotalskblength,itreturnstheno.ofbytesthat *couldnotberemoved.
*/ staticint sctp_skb_pull(struct sk_buff *skb, int len)
{ struct sk_buff *list; int skb_len = skb_headlen(skb); int rlen;
if (len <= skb_len) {
__skb_pull(skb, len); return0;
}
len -= skb_len;
__skb_pull(skb, skb_len);
/* Check if we allow SCTP_NXTINFO. */ if (sp->recvnxtinfo)
sctp_ulpevent_read_nxtinfo(event, msg, sk); /* Check if we allow SCTP_RCVINFO. */ if (sp->recvrcvinfo)
sctp_ulpevent_read_rcvinfo(event, msg); /* Check if we allow SCTP_SNDRCVINFO. */ if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_DATA_IO_EVENT))
sctp_ulpevent_read_sndrcvinfo(event, msg);
err = copied;
/* If skb's length exceeds the user's buffer, update the skb and *pushitbacktothereceive_queuesothatthenextcallto *recvmsg()willreturntheremainingdata.Don'tsetMSG_EOR.
*/ if (skb_len > copied) {
msg->msg_flags &= ~MSG_EOR; if (flags & MSG_PEEK) goto out_free;
sctp_skb_pull(skb, copied);
skb_queue_head(&sk->sk_receive_queue, skb);
/* When only partial message is copied to the user, increase *rwndbythatamount.Ifallthedataintheskbisread, *rwndisupdatedwhentheeventisfreed.
*/ if (!sctp_ulpevent_is_notification(event))
sctp_assoc_rwnd_increase(event->asoc, copied); goto out;
} elseif ((event->msg_flags & MSG_NOTIFICATION) ||
(event->msg_flags & MSG_EOR))
msg->msg_flags |= MSG_EOR; else
msg->msg_flags &= ~MSG_EOR;
out_free: if (flags & MSG_PEEK) { /* Release the skb reference acquired after peeking the skb in *sctp_skb_recv_datagram().
*/
kfree_skb(skb);
} else { /* Free the event which includes releasing the reference to *theowneroftheskb,freeingtheskbandupdatingthe *rwnd.
*/
sctp_ulpevent_free(event);
}
out:
release_sock(sk); return err;
}
/* At the time when a user app subscribes to SCTP_SENDER_DRY_EVENT, *ifthereisnodatatobesentorretransmit,thestackwill *immediatelysendupthisnotification.
*/ if (sctp_ulpevent_type_enabled(sp->subscribe, SCTP_SENDER_DRY_EVENT)) { struct sctp_ulpevent *event;
asoc = sctp_id2assoc(sk, 0); if (asoc && sctp_outq_is_empty(&asoc->outqueue)) {
event = sctp_ulpevent_make_sender_dry_event(asoc,
GFP_USER | __GFP_NOWARN); if (!event) return -ENOMEM;
if (params->spp_flags & SPP_HB_DEMAND && trans) {
error = sctp_primitive_REQUESTHEARTBEAT(trans->asoc->base.net,
trans->asoc, trans); if (error) return error;
}
/* Note that unless the spp_flag is set to SPP_HB_ENABLE the value of *thisfieldisignored.Notealsothatavalueofzeroindicates *thecurrentsettingshouldbeleftunchanged.
*/ if (params->spp_flags & SPP_HB_ENABLE) {
/* Re-zero the interval if the SPP_HB_TIME_IS_ZERO is *set.Thisletsususe0valuewhenthisflag *isset.
*/ if (params->spp_flags & SPP_HB_TIME_IS_ZERO)
params->spp_hbinterval = 0;
/* When Path MTU discovery is disabled the value specified here will *bethe"fixed"pathmtu(i.e.thevalueofthespp_flagsfieldmust *includetheflagSPP_PMTUD_DISABLEforthisfieldtohaveany *effect).
*/ if ((params->spp_flags & SPP_PMTUD_DISABLE) && params->spp_pathmtu) { if (trans) {
trans->pathmtu = params->spp_pathmtu;
sctp_assoc_sync_pmtu(asoc);
} elseif (asoc) {
sctp_assoc_set_pmtu(asoc, params->spp_pathmtu);
} else {
sp->pathmtu = params->spp_pathmtu;
}
}
/* Note that a value of zero indicates the current setting should be leftunchanged.
*/ if (params->spp_pathmaxrxt) { if (trans) {
trans->pathmaxrxt = params->spp_pathmaxrxt;
} elseif (asoc) {
asoc->pathmaxrxt = params->spp_pathmaxrxt;
} else {
sp->pathmaxrxt = params->spp_pathmaxrxt;
}
}
if (params->spp_flags & SPP_IPV6_FLOWLABEL) { if (trans) { if (trans->ipaddr.sa.sa_family == AF_INET6) {
trans->flowlabel = params->spp_ipv6_flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
trans->flowlabel |= SCTP_FLOWLABEL_SET_MASK;
}
} elseif (asoc) { struct sctp_transport *t;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/ 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;
}
/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the *socketisaonetomanystylesocket,andanassociation *wasnotfound,thentheidwasinvalid.
*/
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,butnotasocket.
*/ if (params->spp_flags & SPP_HB_DEMAND && !trans && !asoc) return -EINVAL;
/* If changes are for association, also apply parameters to each *transport.
*/ if (!trans && asoc) {
list_for_each_entry(trans, &asoc->peer.transport_addr_list,
transports) {
sctp_apply_peer_addr_params(params, trans, asoc, sp,
hb_change, pmtud_change,
sackdelay_change);
}
}
if (optlen != sizeof(struct sctp_initmsg)) return -EINVAL;
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 (asoc) { if (rtoinfo->srto_initial != 0)
asoc->rto_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 *setthevaluestotheendpoint.
*/ if (rtoinfo->srto_initial != 0)
sp->rtoinfo.srto_initial = rtoinfo->srto_initial;
sp->rtoinfo.srto_max = rto_max;
sp->rtoinfo.srto_min = rto_min;
}
/* Set the values to the specific association */ if (asoc) { if (assocparams->sasoc_asocmaxrxt != 0) {
__u32 path_sum = 0; int paths = 0; struct sctp_transport *peer_addr;
/* Only validate asocmaxrxt if we have more than *onepath/transport.Wedothisbecausepath *retransmissionsareonlycountedwhenwehavemore *thenonepath.
*/ if (paths > 1 &&
assocparams->sasoc_asocmaxrxt > path_sum) return -EINVAL;
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)
sp->assocparams.sasoc_asocmaxrxt =
assocparams->sasoc_asocmaxrxt; if (assocparams->sasoc_cookie_life != 0)
sp->assocparams.sasoc_cookie_life =
assocparams->sasoc_cookie_life;
} return0;
}
/* Note: We double the receive buffer from what the user sets *ittobe,alsoinitialrwndisbasedonrcvbuf/2.
*/ if (*val > (sk->sk_rcvbuf >> 1)) return -EINVAL;
len = v2 ? sizeof(*val) : sizeof(struct sctp_paddrthlds); if (optlen < len) return -EINVAL;
if (v2 && val->spt_pathpfthld > val->spt_pathcpthld) return -EINVAL;
if (!sctp_is_any(sk, (constunion 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 (v2)
trans->ps_retrans = val->spt_pathcpthld;
trans->pf_retrans = val->spt_pathpfthld;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/
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; return0;
}
/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the *socketisaonetomanystylesocket,andanassociation *wasnotfound,thentheidwasinvalid.
*/
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 *eachtransport.
*/ if (asoc) {
list_for_each_entry(t, &asoc->peer.transport_addr_list,
transports)
t->encap_port = encap_port;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/ 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;
/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the *socketisaonetomanystylesocket,andanassociation *wasnotfound,thentheidwasinvalid.
*/
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 *eachtransport.
*/ 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);
}
/* I can hardly begin to describe how wrong this is. This is *sobrokenastobeworsethanuseless.TheAPIdraft *REALLYisNOThelpfulhere...Iamnotconvincedthatthe *semanticsofsetsockopt()withalevelOTHERTHANSOL_SCTP *areatallwell-founded.
*/ if (level != SOL_SCTP) { struct sctp_af *af = sctp_sk(sk)->pf->af;
if (optlen > 0) { /* Trim it to the biggest size sctp sockopt may need if necessary */
optlen = min_t(unsignedint, 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);
switch (optname) { case SCTP_SOCKOPT_BINDX_ADD: /* 'optlen' is the size of the addresses buffer. */
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, 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, kopt, optlen); break;
case SCTP_SOCKOPT_CONNECTX: /* 'optlen' is the size of the addresses buffer. */
retval = sctp_setsockopt_connectx(sk, kopt, optlen); break;
case SCTP_DISABLE_FRAGMENTS:
retval = sctp_setsockopt_disable_fragments(sk, kopt, optlen); break;
case SCTP_EVENTS:
retval = sctp_setsockopt_events(sk, kopt, optlen); break;
case SCTP_AUTOCLOSE:
retval = sctp_setsockopt_autoclose(sk, kopt, optlen); break;
case SCTP_PEER_ADDR_PARAMS:
retval = sctp_setsockopt_peer_addr_params(sk, kopt, optlen); break;
case SCTP_DELAYED_SACK:
retval = sctp_setsockopt_delayed_ack(sk, kopt, optlen); break; case SCTP_PARTIAL_DELIVERY_POINT:
retval = sctp_setsockopt_partial_delivery_point(sk, kopt, optlen); break;
case SCTP_INITMSG:
retval = sctp_setsockopt_initmsg(sk, kopt, optlen); break; case SCTP_DEFAULT_SEND_PARAM:
retval = sctp_setsockopt_default_send_param(sk, kopt, optlen); break; case SCTP_DEFAULT_SNDINFO:
retval = sctp_setsockopt_default_sndinfo(sk, kopt, optlen); break; case SCTP_PRIMARY_ADDR:
retval = sctp_setsockopt_primary_addr(sk, kopt, optlen); break; case SCTP_SET_PEER_PRIMARY_ADDR:
retval = sctp_setsockopt_peer_primary_addr(sk, kopt, optlen); break; case SCTP_NODELAY:
retval = sctp_setsockopt_nodelay(sk, kopt, optlen); break; case SCTP_RTOINFO:
retval = sctp_setsockopt_rtoinfo(sk, kopt, optlen); break; case SCTP_ASSOCINFO:
retval = sctp_setsockopt_associnfo(sk, kopt, optlen); break; case SCTP_I_WANT_MAPPED_V4_ADDR:
retval = sctp_setsockopt_mappedv4(sk, kopt, optlen); break; case SCTP_MAXSEG:
retval = sctp_setsockopt_maxseg(sk, kopt, optlen); break; case SCTP_ADAPTATION_LAYER:
retval = sctp_setsockopt_adaptation_layer(sk, kopt, optlen); break; case SCTP_CONTEXT:
retval = sctp_setsockopt_context(sk, kopt, optlen); break; case SCTP_FRAGMENT_INTERLEAVE:
retval = sctp_setsockopt_fragment_interleave(sk, kopt, optlen); break; case SCTP_MAX_BURST:
retval = sctp_setsockopt_maxburst(sk, kopt, optlen); break; case SCTP_AUTH_CHUNK:
retval = sctp_setsockopt_auth_chunk(sk, kopt, optlen); break; case SCTP_HMAC_IDENT:
retval = sctp_setsockopt_hmac_ident(sk, kopt, optlen); break; case SCTP_AUTH_KEY:
retval = sctp_setsockopt_auth_key(sk, kopt, optlen); break; case SCTP_AUTH_ACTIVE_KEY:
retval = sctp_setsockopt_active_key(sk, kopt, optlen); break; case SCTP_AUTH_DELETE_KEY:
retval = sctp_setsockopt_del_key(sk, kopt, optlen); break; case SCTP_AUTH_DEACTIVATE_KEY:
retval = sctp_setsockopt_deactivate_key(sk, kopt, optlen); break; case SCTP_AUTO_ASCONF:
retval = sctp_setsockopt_auto_asconf(sk, kopt, optlen); break; case SCTP_PEER_ADDR_THLDS:
retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen, false); break; case SCTP_PEER_ADDR_THLDS_V2:
retval = sctp_setsockopt_paddr_thresholds(sk, kopt, optlen, true); break; case SCTP_RECVRCVINFO:
retval = sctp_setsockopt_recvrcvinfo(sk, kopt, optlen); break; case SCTP_RECVNXTINFO:
retval = sctp_setsockopt_recvnxtinfo(sk, kopt, optlen); break; case SCTP_PR_SUPPORTED:
retval = sctp_setsockopt_pr_supported(sk, kopt, optlen); break; case SCTP_DEFAULT_PRINFO:
retval = sctp_setsockopt_default_prinfo(sk, kopt, optlen); break; case SCTP_RECONFIG_SUPPORTED:
retval = sctp_setsockopt_reconfig_supported(sk, kopt, optlen); break; case SCTP_ENABLE_STREAM_RESET:
retval = sctp_setsockopt_enable_strreset(sk, kopt, optlen); break; case SCTP_RESET_STREAMS:
retval = sctp_setsockopt_reset_streams(sk, kopt, optlen); break; case SCTP_RESET_ASSOC:
retval = sctp_setsockopt_reset_assoc(sk, kopt, optlen); break; case SCTP_ADD_STREAMS:
retval = sctp_setsockopt_add_streams(sk, kopt, optlen); break; case SCTP_STREAM_SCHEDULER:
retval = sctp_setsockopt_scheduler(sk, kopt, optlen); break; case SCTP_STREAM_SCHEDULER_VALUE:
retval = sctp_setsockopt_scheduler_value(sk, kopt, optlen); break; case SCTP_INTERLEAVING_SUPPORTED:
retval = sctp_setsockopt_interleaving_supported(sk, kopt,
optlen); break; case SCTP_REUSE_PORT:
retval = sctp_setsockopt_reuse_port(sk, kopt, optlen); break; case SCTP_EVENT:
retval = sctp_setsockopt_event(sk, kopt, optlen); break; case SCTP_ASCONF_SUPPORTED:
retval = sctp_setsockopt_asconf_supported(sk, kopt, optlen); break; case SCTP_AUTH_SUPPORTED:
retval = sctp_setsockopt_auth_supported(sk, kopt, optlen); break; case SCTP_ECN_SUPPORTED:
retval = sctp_setsockopt_ecn_supported(sk, kopt, optlen); break; case SCTP_EXPOSE_POTENTIALLY_FAILED_STATE:
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; break;
}
/* Only called when shutdown a listening SCTP socket. */ staticint sctp_disconnect(struct sock *sk, int flags)
{ if (!sctp_style(sk, TCP)) return -EOPNOTSUPP;
error = sctp_wait_for_accept(sk, timeo); if (error) goto out;
/* We treat the list of associations on the endpoint as the accept *queueandpickthefirstassociationonthelist.
*/
asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
/* Populate the fields of the newsk from the oldsk and migrate the *asoctothenewsk.
*/
error = sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP); if (error) {
sk_common_release(newsk);
newsk = NULL;
}
/* This is the function which gets called during socket creation to *initializedtheSCTP-specificportionofthesock. *Thesockstructureshouldalreadybezero-filledmemory.
*/ staticint sctp_init_sock(struct sock *sk)
{ struct net *net = sock_net(sk); struct sctp_sock *sp;
pr_debug("%s: sk:%p\n", __func__, sk);
sp = sctp_sk(sk);
/* Initialize the SCTP per socket area. */ switch (sk->sk_type) { case SOCK_SEQPACKET:
sp->type = SCTP_SOCKET_UDP; break; case SOCK_STREAM:
sp->type = SCTP_SOCKET_TCP; break; default: return -ESOCKTNOSUPPORT;
}
sk->sk_gso_type = SKB_GSO_SCTP;
/* Initialize default send parameters. These parameters can be *modifiedwiththeSCTP_DEFAULT_SEND_PARAMsocketoption.
*/
sp->default_stream = 0;
sp->default_ppid = 0;
sp->default_flags = 0;
sp->default_context = 0;
sp->default_timetolive = 0;
/* If enabled no SCTP message fragmentation will be performed. *ConfigurethroughSCTP_DISABLE_FRAGMENTSsocketoption.
*/
sp->disable_fragments = 0;
/* Enable Nagle algorithm by default. */
sp->nodelay = 0;
sp->recvrcvinfo = 0;
sp->recvnxtinfo = 0;
/* Enable by default. */
sp->v4mapped = 1;
/* Auto-close idle associations after the configured *numberofseconds.Avalueof0disablesthis *feature.ConfigurethroughtheSCTP_AUTOCLOSEsocketoption, *forUDP-stylesocketsonly.
*/
sp->autoclose = 0;
/* User specified fragmentation limit. */
sp->user_frag = 0;
sp->adaptation_ind = 0;
sp->pf = sctp_get_pf_specific(sk->sk_family);
/* Control variables for partial data delivery. */
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 *changethedatastructurerelationships,thismaystill *beusefulforstoringpre-connectaddressinformation.
*/
sp->ep = sctp_endpoint_new(sk, GFP_KERNEL); if (!sp->ep) return -ENOMEM;
/* Cleanup any SCTP per socket resources. Must be called with *sock_net(sk)->sctp.addr_wq_lockheldifsp->do_auto_asconfistrue
*/ staticvoid sctp_destroy_sock(struct sock *sk)
{ struct sctp_sock *sp;
pr_debug("%s: sk:%p\n", __func__, sk);
/* Release our hold on the endpoint. */
sp = sctp_sk(sk); /* This could happen during socket init, thus we bail out *early,sincetherestofthebelowisnotsetupeither.
*/ if (sp->ep == NULL) return;
/* Triggered when there are no references on the socket anymore */ staticvoid sctp_destruct_common(struct sock *sk)
{ struct sctp_sock *sp = sctp_sk(sk);
/* Free up the HMAC transform. */
crypto_free_shash(sp->hmac);
}
int sctp_transport_lookup_process(sctp_callback_t cb, struct net *net, constunion sctp_addr *laddr, constunion sctp_addr *paddr, void *p, int dif)
{ struct sctp_transport *transport; struct sctp_endpoint *ep; int err = -ENOENT;
rcu_read_lock();
transport = sctp_addrs_lookup_transport(net, laddr, paddr, dif, dif); 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();
/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS) * *Thisoptionisaon/offflag.IfenablednoSCTPmessage *fragmentationwillbeperformed.Insteadifamessagebeingsent *exceedsthecurrentPMTUsize,themessagewillNOTbesentand *insteadaerrorwillbeindicatedtotheuser.
*/ staticint sctp_getsockopt_disable_fragments(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ int val;
if (len < sizeof(int)) return -EINVAL;
len = sizeof(int);
val = (sctp_sk(sk)->disable_fragments == 1); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; return0;
}
/* 7.1.15 Set notification and ancillary events (SCTP_EVENTS) * *Thissocketoptionisusedtospecifyvariousnotificationsand *ancillarydatatheuserwishestoreceive.
*/ staticint sctp_getsockopt_events(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ struct sctp_event_subscribe subscribe;
__u8 *sn_type = (__u8 *)&subscribe; int i;
if (len == 0) return -EINVAL; if (len > sizeof(struct sctp_event_subscribe))
len = sizeof(struct sctp_event_subscribe); if (put_user(len, optlen)) return -EFAULT;
for (i = 0; i < len; i++)
sn_type[i] = sctp_ulpevent_type_enabled(sctp_sk(sk)->subscribe,
SCTP_SN_TYPE_BASE + i);
if (copy_to_user(optval, &subscribe, len)) return -EFAULT;
return0;
}
/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE) * *ThissocketoptionisapplicabletotheUDP-stylesocketonly.When *setitwillcauseassociationsthatareidleformorethanthe *specifiednumberofsecondstoautomaticallyclose.Anassociation *beingidleisdefinedanassociationthathasNOTsentorreceived *userdata.Thespecialvalueof'0'indicatesthatnoautomatic *closeofanyassociationsshouldbeperformed.Theoptionexpectsan *integerdefiningthenumberofsecondsofidletimebeforean *associationisclosed.
*/ staticint sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ /* Applicable to UDP-style socket only */ if (sctp_style(sk, TCP)) return -EOPNOTSUPP; if (len < sizeof(int)) return -EINVAL;
len = sizeof(int); if (put_user(len, optlen)) return -EFAULT; if (put_user(sctp_sk(sk)->autoclose, (int __user *)optval)) return -EFAULT; return0;
}
/* Helper routine to branch off an association to a new socket. */ staticint sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
{ struct sctp_association *asoc = sctp_id2assoc(sk, id); struct sctp_sock *sp = sctp_sk(sk); struct socket *sock; int err = 0;
/* Do not peel off from one netns to another one. */ if (!net_eq(current->nsproxy->net_ns, sock_net(sk))) return -EINVAL;
if (!asoc) return -EINVAL;
/* An association cannot be branched off from an already peeled-off *socket,noristhissupportedfortcpstylesockets.
*/ if (!sctp_style(sk, UDP)) return -EINVAL;
/* Create a new socket. */
err = sock_create(sk->sk_family, SOCK_SEQPACKET, IPPROTO_SCTP, &sock); if (err < 0) return err;
sctp_copy_sock(sock->sk, sk, asoc);
/* Make peeled-off sockets more like 1-1 accepted sockets. *Setthedaddrandinitializeidtosomethingmorerandomandalso *copyoveranyipoptions.
*/
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 *asoctothenewsk.
*/
err = sctp_sock_migrate(sk, sock->sk, asoc,
SCTP_SOCKET_UDP_HIGH_BANDWIDTH); if (err) {
sock_release(sock);
sock = NULL;
}
/* Map the socket to an unused fd that can be returned to the user. */
retval = get_unused_fd_flags(flags & SOCK_CLOEXEC); if (retval < 0) {
sock_release(newsock); goto out;
}
staticint sctp_getsockopt_peeloff_flags(struct sock *sk, int len, char __user *optval, int __user *optlen)
{
sctp_peeloff_flags_arg_t peeloff; struct file *newfile = NULL; int retval = 0;
if (len < sizeof(sctp_peeloff_flags_arg_t)) return -EINVAL;
len = sizeof(sctp_peeloff_flags_arg_t); if (copy_from_user(&peeloff, optval, len)) return -EFAULT;
if (len >= sizeof(params))
len = sizeof(params); elseif (len >= ALIGN(offsetof(struct sctp_paddrparams,
spp_ipv6_flowlabel), 4))
len = ALIGN(offsetof(struct sctp_paddrparams,
spp_ipv6_flowlabel), 4); else return -EINVAL;
if (copy_from_user(¶ms, optval, len)) return -EFAULT;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/ 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) {
pr_debug("%s: failed no transport\n", __func__); return -EINVAL;
}
}
/* Get association, if assoc_id != SCTP_FUTURE_ASSOC and the *socketisaonetomanystylesocket,andanassociation *wasnotfound,thentheidwasinvalid.
*/
asoc = sctp_id2assoc(sk, params.spp_assoc_id); if (!asoc && params.spp_assoc_id != SCTP_FUTURE_ASSOC &&
sctp_style(sk, UDP)) {
pr_debug("%s: failed no association\n", __func__); return -EINVAL;
}
if (trans) { /* Fetch transport values. */
params.spp_hbinterval = jiffies_to_msecs(trans->hbinterval);
params.spp_pathmtu = trans->pathmtu;
params.spp_pathmaxrxt = trans->pathmaxrxt;
params.spp_sackdelay = jiffies_to_msecs(trans->sackdelay);
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = trans->param_flags; if (trans->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = trans->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
} if (trans->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = trans->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
} elseif (asoc) { /* Fetch association values. */
params.spp_hbinterval = jiffies_to_msecs(asoc->hbinterval);
params.spp_pathmtu = asoc->pathmtu;
params.spp_pathmaxrxt = asoc->pathmaxrxt;
params.spp_sackdelay = jiffies_to_msecs(asoc->sackdelay);
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = asoc->param_flags; if (asoc->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = asoc->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
} if (asoc->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = asoc->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
} else { /* Fetch socket values. */
params.spp_hbinterval = sp->hbinterval;
params.spp_pathmtu = sp->pathmtu;
params.spp_sackdelay = sp->sackdelay;
params.spp_pathmaxrxt = sp->pathmaxrxt;
/*draft-11 doesn't say what to return in spp_flags*/
params.spp_flags = sp->param_flags; if (sp->flowlabel & SCTP_FLOWLABEL_SET_MASK) {
params.spp_ipv6_flowlabel = sp->flowlabel &
SCTP_FLOWLABEL_VAL_MASK;
params.spp_flags |= SPP_IPV6_FLOWLABEL;
} if (sp->dscp & SCTP_DSCP_SET_MASK) {
params.spp_dscp = sp->dscp & SCTP_DSCP_VAL_MASK;
params.spp_flags |= SPP_DSCP;
}
}
if (copy_to_user(optval, ¶ms, len)) return -EFAULT;
staticint sctp_getsockopt_local_addrs(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ struct sctp_bind_addr *bp; struct sctp_association *asoc; int cnt = 0; struct sctp_getaddrs getaddrs; struct sctp_sockaddr_entry *addr; void __user *to; union sctp_addr temp; struct sctp_sock *sp = sctp_sk(sk); int addrlen; int err = 0;
size_t space_left; int bytes_copied = 0; void *addrs; void *buf;
if (len < sizeof(struct sctp_getaddrs)) return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) return -EFAULT;
/* *ForUDP-stylesockets,idspecifiestheassociationtoquery. *Iftheidfieldissettothevalue'0'thenthelocallybound *addressesarereturnedwithoutregardtoanyparticular *association.
*/ if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id); if (!asoc) return -EINVAL;
bp = &asoc->base.bind_addr;
}
to = optval + offsetof(struct sctp_getaddrs, addrs);
space_left = len - offsetof(struct sctp_getaddrs, addrs);
addrs = kmalloc(space_left, GFP_USER | __GFP_NOWARN); if (!addrs) return -ENOMEM;
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid *addressesfromthegloballocaladdresslist.
*/ if (sctp_list_single_entry(&bp->address_list)) {
addr = list_entry(bp->address_list.next, struct sctp_sockaddr_entry, list); if (sctp_is_any(sk, &addr->a)) {
cnt = sctp_copy_laddrs(sk, bp->port, addrs,
space_left, &bytes_copied); if (cnt < 0) {
err = cnt; goto out;
} goto copy_getaddrs;
}
}
buf = addrs; /* Protection on the bound address list is not needed since *inthesocketoptioncontextweholdasocketlockand *thustheboundaddresslistcan'tchange.
*/
list_for_each_entry(addr, &bp->address_list, list) {
memcpy(&temp, &addr->a, sizeof(temp));
addrlen = sctp_get_pf_specific(sk->sk_family)
->addr_to_user(sp, &temp); if (space_left < addrlen) {
err = -ENOMEM; /*fixme: right error?*/ goto out;
}
memcpy(buf, &temp, addrlen);
buf += addrlen;
bytes_copied += addrlen;
cnt++;
space_left -= addrlen;
}
copy_getaddrs: if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT; goto out;
} if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
err = -EFAULT; goto out;
} /* XXX: We should have accounted for sizeof(struct sctp_getaddrs) too, *butwecan'tchangeitanymore.
*/ if (put_user(bytes_copied, optlen))
err = -EFAULT;
out:
kfree(addrs); return err;
}
/* Values correspoinding to the specific association */ if (asoc) {
assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
assocparams.sasoc_local_rwnd = asoc->a_rwnd;
assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);
if (put_user(len, optlen)) return -EFAULT; if (put_user(num_idents, &p->shmac_num_idents)) return -EFAULT; for (i = 0; i < num_idents; i++) {
__u16 hmacid = ntohs(hmacs->hmac_ids[i]);
if (copy_to_user(&p->shmac_idents[i], &hmacid, sizeof(__u16))) return -EFAULT;
} return0;
}
if (len < sizeof(struct sctp_authchunks)) return -EINVAL;
if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT;
to = p->gauth_chunks;
asoc = sctp_id2assoc(sk, val.gauth_assoc_id); if (!asoc) return -EINVAL;
if (!asoc->peer.auth_capable) return -EACCES;
ch = asoc->peer.peer_chunks; if (!ch) goto num;
/* See if the user provided enough room for all the data */
num_chunks = ntohs(ch->param_hdr.length) - sizeof(struct sctp_paramhdr); if (len < num_chunks) return -EINVAL;
if (copy_to_user(to, ch->chunks, num_chunks)) return -EFAULT;
num:
len = sizeof(struct sctp_authchunks) + num_chunks; if (put_user(len, optlen)) return -EFAULT; if (put_user(num_chunks, &p->gauth_number_of_chunks)) return -EFAULT; return0;
}
if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT;
return0;
}
/* *8.1.23SCTP_AUTO_ASCONF *Seethecorrespondingsetsockoptentryasdescription
*/ staticint sctp_getsockopt_auto_asconf(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ int val = 0;
if (len < sizeof(int)) return -EINVAL;
len = sizeof(int); if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
val = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; return0;
}
min = v2 ? sizeof(val) : sizeof(struct sctp_paddrthlds); if (len < min) return -EINVAL;
len = min; if (copy_from_user(&val, optval, len)) return -EFAULT;
if (!sctp_is_any(sk, (constunion sctp_addr *)&val.spt_address)) {
trans = sctp_addr_id2transport(sk, &val.spt_address,
val.spt_assoc_id); if (!trans) return -ENOENT;
/* New high max rto observed, will return 0 if not a single *RTOupdatetookplace.obs_rto_ipaddrwillbebogus *insuchacase
*/
sas.sas_maxrto = asoc->stats.max_obs_rto;
memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr, sizeof(struct sockaddr_storage));
/* Mark beginning of a new observation period */
asoc->stats.max_obs_rto = asoc->rto_min;
if (copy_to_user(optval, &sas, len)) return -EFAULT;
return0;
}
staticint sctp_getsockopt_recvrcvinfo(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ int val = 0;
if (len < sizeof(int)) return -EINVAL;
len = sizeof(int); if (sctp_sk(sk)->recvrcvinfo)
val = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT;
return0;
}
staticint sctp_getsockopt_recvnxtinfo(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ int val = 0;
if (len < sizeof(int)) return -EINVAL;
len = sizeof(int); if (sctp_sk(sk)->recvnxtinfo)
val = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT;
return0;
}
staticint sctp_getsockopt_pr_supported(struct sock *sk, int len, char __user *optval, int __user *optlen)
{ struct sctp_assoc_value params; struct sctp_association *asoc; int retval = -EFAULT;
len = sizeof(encap); if (copy_from_user(&encap, optval, len)) return -EFAULT;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/ 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 *socketisaonetomanystylesocket,andanassociation *wasnotfound,thentheidwasinvalid.
*/
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;
}
len = sizeof(params); if (copy_from_user(¶ms, optval, len)) return -EFAULT;
/* If an address other than INADDR_ANY is specified, and *notransportisfound,thentherequestisinvalid.
*/ 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;
}
/* I can hardly begin to describe how wrong this is. This is *sobrokenastobeworsethanuseless.TheAPIdraft *REALLYisNOThelpfulhere...Iamnotconvincedthatthe *semanticsofgetsockopt()withalevelOTHERTHANSOL_SCTP *areatallwell-founded.
*/ if (level != SOL_SCTP) { struct sctp_af *af = sctp_sk(sk)->pf->af;
switch (optname) { case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); break; case SCTP_DISABLE_FRAGMENTS:
retval = sctp_getsockopt_disable_fragments(sk, len, optval,
optlen); break; case SCTP_EVENTS:
retval = sctp_getsockopt_events(sk, len, optval, optlen); break; case SCTP_AUTOCLOSE:
retval = sctp_getsockopt_autoclose(sk, len, optval, optlen); break; case SCTP_SOCKOPT_PEELOFF:
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); break; case SCTP_SOCKOPT_PEELOFF_FLAGS:
retval = sctp_getsockopt_peeloff_flags(sk, len, optval, optlen); break; case SCTP_PEER_ADDR_PARAMS:
retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
optlen); break; case SCTP_DELAYED_SACK:
retval = sctp_getsockopt_delayed_ack(sk, len, optval,
optlen); break; case SCTP_INITMSG:
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen); break; case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_peer_addrs(sk, len, optval,
optlen); break; case SCTP_GET_LOCAL_ADDRS:
retval = sctp_getsockopt_local_addrs(sk, len, optval,
optlen); break; case SCTP_SOCKOPT_CONNECTX3:
retval = sctp_getsockopt_connectx3(sk, len, optval, optlen); break; case SCTP_DEFAULT_SEND_PARAM:
retval = sctp_getsockopt_default_send_param(sk, len,
optval, optlen); break; case SCTP_DEFAULT_SNDINFO:
retval = sctp_getsockopt_default_sndinfo(sk, len,
optval, optlen); break; case SCTP_PRIMARY_ADDR:
retval = sctp_getsockopt_primary_addr(sk, len, optval, optlen); break; case SCTP_NODELAY:
retval = sctp_getsockopt_nodelay(sk, len, optval, optlen); break; case SCTP_RTOINFO:
retval = sctp_getsockopt_rtoinfo(sk, len, optval, optlen); break; case SCTP_ASSOCINFO:
retval = sctp_getsockopt_associnfo(sk, len, optval, optlen); break; case SCTP_I_WANT_MAPPED_V4_ADDR:
retval = sctp_getsockopt_mappedv4(sk, len, optval, optlen); break; case SCTP_MAXSEG:
retval = sctp_getsockopt_maxseg(sk, len, optval, optlen); break; case SCTP_GET_PEER_ADDR_INFO:
retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
optlen); break; case SCTP_ADAPTATION_LAYER:
retval = sctp_getsockopt_adaptation_layer(sk, len, optval,
optlen); break; case SCTP_CONTEXT:
retval = sctp_getsockopt_context(sk, len, optval, optlen); break; case SCTP_FRAGMENT_INTERLEAVE:
retval = sctp_getsockopt_fragment_interleave(sk, len, optval,
optlen); break; case SCTP_PARTIAL_DELIVERY_POINT:
retval = sctp_getsockopt_partial_delivery_point(sk, len, optval,
optlen); break; case SCTP_MAX_BURST:
retval = sctp_getsockopt_maxburst(sk, len, optval, optlen); break; case SCTP_AUTH_KEY: case SCTP_AUTH_CHUNK: case SCTP_AUTH_DELETE_KEY: case SCTP_AUTH_DEACTIVATE_KEY:
retval = -EOPNOTSUPP; break; case SCTP_HMAC_IDENT:
retval = sctp_getsockopt_hmac_ident(sk, len, optval, optlen); break; case SCTP_AUTH_ACTIVE_KEY:
retval = sctp_getsockopt_active_key(sk, len, optval, optlen); break; case SCTP_PEER_AUTH_CHUNKS:
retval = sctp_getsockopt_peer_auth_chunks(sk, len, optval,
optlen); break; case SCTP_LOCAL_AUTH_CHUNKS:
retval = sctp_getsockopt_local_auth_chunks(sk, len, optval,
optlen); break; case SCTP_GET_ASSOC_NUMBER:
retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen); break; case SCTP_GET_ASSOC_ID_LIST:
retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen); break; case SCTP_AUTO_ASCONF:
retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen); break; case SCTP_PEER_ADDR_THLDS:
retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
optlen, false); break; case SCTP_PEER_ADDR_THLDS_V2:
retval = sctp_getsockopt_paddr_thresholds(sk, optval, len,
optlen, true); break; case SCTP_GET_ASSOC_STATS:
retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen); break; case SCTP_RECVRCVINFO:
retval = sctp_getsockopt_recvrcvinfo(sk, len, optval, optlen); break; case SCTP_RECVNXTINFO:
retval = sctp_getsockopt_recvnxtinfo(sk, len, optval, optlen); break; case SCTP_PR_SUPPORTED:
retval = sctp_getsockopt_pr_supported(sk, len, optval, optlen); break; case SCTP_DEFAULT_PRINFO:
retval = sctp_getsockopt_default_prinfo(sk, len, optval,
optlen); break; case SCTP_PR_ASSOC_STATUS:
retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
optlen); break; case SCTP_PR_STREAM_STATUS:
retval = sctp_getsockopt_pr_streamstatus(sk, len, optval,
optlen); break; case SCTP_RECONFIG_SUPPORTED:
retval = sctp_getsockopt_reconfig_supported(sk, len, optval,
optlen); break; case SCTP_ENABLE_STREAM_RESET:
retval = sctp_getsockopt_enable_strreset(sk, len, optval,
optlen); break; case SCTP_STREAM_SCHEDULER:
retval = sctp_getsockopt_scheduler(sk, len, optval,
optlen); break; case SCTP_STREAM_SCHEDULER_VALUE:
retval = sctp_getsockopt_scheduler_value(sk, len, optval,
optlen); break; case SCTP_INTERLEAVING_SUPPORTED:
retval = sctp_getsockopt_interleaving_supported(sk, len, optval,
optlen); break; case SCTP_REUSE_PORT:
retval = sctp_getsockopt_reuse_port(sk, len, optval, optlen); break; case SCTP_EVENT:
retval = sctp_getsockopt_event(sk, len, optval, optlen); break; case SCTP_ASCONF_SUPPORTED:
retval = sctp_getsockopt_asconf_supported(sk, len, optval,
optlen); break; case SCTP_AUTH_SUPPORTED:
retval = sctp_getsockopt_auth_supported(sk, len, optval,
optlen); break; case SCTP_ECN_SUPPORTED:
retval = sctp_getsockopt_ecn_supported(sk, len, optval, optlen); break; 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;
}
release_sock(sk); return retval;
}
staticbool sctp_bpf_bypass_getsockopt(int level, int optname)
{ if (level == SOL_SCTP) { switch (optname) { case SCTP_SOCKOPT_PEELOFF: case SCTP_SOCKOPT_PEELOFF_FLAGS: case SCTP_SOCKOPT_CONNECTX3: returntrue; default: returnfalse;
}
}
do {
rover++; if ((rover < low) || (rover > high))
rover = low; if (inet_is_local_reserved_port(net, rover)) continue;
index = sctp_phashfn(net, rover);
head = &sctp_port_hashtable[index];
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_bh(&head->lock);
cond_resched();
} while (--remaining > 0);
/* Exhausted local port range during search? */
ret = 1; if (remaining <= 0) return ret;
/* OK, here is the one we will use. HEAD (the port *hashtablelistentry)isnon-NULLandweholdit's *mutex.
*/
snum = rover;
} else { /* We are given an specific port number; we verify *thatitisnotbeingused.Ifitisused,wewill *exahustthesearchinthehashlistcorresponding *totheportnumber(snum)-wedetectthatwiththe *portiterator,ppbeingNULL.
*/
head = &sctp_port_hashtable[sctp_phashfn(net, snum)];
spin_lock_bh(&head->lock);
sctp_for_each_hentry(pp, &head->chain) { if ((pp->port == snum) && net_eq(pp->net, net)) goto pp_found;
}
}
pp = NULL; goto pp_not_found;
pp_found: if (!hlist_empty(&pp->owner)) { /* We had a port hash table hit - there is an *availableport(pp!=NULL)anditisbeing *usedbyothersocket(pp->ownernotempty);thatother *socketisgoingtobesk2.
*/ struct sock *sk2;
pr_debug("%s: found a possible match\n", __func__);
/* Run through the list of sockets bound to the port *(pp->port)[viathepointersbind_nextand *bind_pprevinthestructsock*sk2(pp->sk)].Oneachone, *wegettheendpointtheydescribeandrunthrough *theendpoint'slistofIP(v4orv6)addresses, *comparingeachoftheaddresseswiththeaddressof *thesocketsk.Ifwefindamatch,thenthatmeans *thatthisport/socket(sk)combinationarealready *inanendpoint.
*/
sk_for_each_bound(sk2, &pp->owner) { int bound_dev_if2 = READ_ONCE(sk2->sk_bound_dev_if); struct sctp_sock *sp2 = sctp_sk(sk2); struct sctp_endpoint *ep2 = sp2->ep;
pr_debug("%s: found a match\n", __func__);
}
pp_not_found: /* If there was a hash table miss, create a new port. */
ret = 1; if (!pp && !(pp = sctp_bucket_create(head, net, snum))) goto fail_unlock;
/* In either case (hit or miss), make sure fastreuse is 1 only *ifsk->sk_reuseistoo(thatis,ifthecallerrequested *SO_REUSEADDRonthissocket-sk-).
*/ if (hlist_empty(&pp->owner)) { if (reuse && sk->sk_state != SCTP_SS_LISTENING)
pp->fastreuse = 1; else
pp->fastreuse = 0;
/* We are set, so fill up all the data in the hash table *entry,tiethesocketlistinformationwiththerestofthe *socketsFIXME:Blurry,NPI(ipg).
*/
success: if (!sp->bind_hash) {
inet_sk(sk)->inet_num = snum;
sk_add_bind_node(sk, &pp->owner);
sp->bind_hash = pp;
}
ret = 0;
/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral *portisrequested.
*/ staticint sctp_get_port(struct sock *sk, unsignedshort snum)
{ union sctp_addr addr; struct sctp_af *af = sctp_sk(sk)->pf->af;
/* Set up a dummy address struct from the sk. */
af->from_sk(&addr, sk);
addr.v4.sin_port = htons(snum);
/* Note: sk->sk_num gets filled in if ephemeral port request. */ return sctp_get_port_local(sk, &addr);
}
/* If we are already listening, just update the backlog */ if (sctp_sstate(sk, LISTENING))
WRITE_ONCE(sk->sk_max_ack_backlog, backlog); else {
err = sctp_listen_start(sk, backlog); if (err) goto out;
}
/* A TCP-style listening socket becomes readable when the accept queue *isnotempty.
*/ if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)) return (!list_empty(&sp->ep->asocs)) ?
(EPOLLIN | EPOLLRDNORM) : 0;
mask = 0;
/* Is there any exceptional events? */ if (sk->sk_err || !skb_queue_empty_lockless(&sk->sk_error_queue))
mask |= EPOLLERR |
(sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? EPOLLPRI : 0); if (sk->sk_shutdown & RCV_SHUTDOWN)
mask |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK)
mask |= EPOLLHUP;
/* Is it readable? Reconsider this code with TCP-style support. */ if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
mask |= EPOLLIN | EPOLLRDNORM;
/* The association is either gone or not ready. */ if (!sctp_style(sk, UDP) && sctp_sstate(sk, CLOSED)) return mask;
/* Is it writable? */ if (sctp_writeable(sk)) {
mask |= EPOLLOUT | EPOLLWRNORM;
} else {
sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk); /* *Sincethesocketisnotlocked,thebuffer *mightbemadeavailableafterthewriteablecheckand *beforethebitisset.ThiscouldcausealostI/O *signal.tcp_poll()hasaracebreakerforthisrace *condition.Basedontheirimplementation,weput *inthefollowingcodetocoveritaswell.
*/ if (sctp_writeable(sk))
mask |= EPOLLOUT | EPOLLWRNORM;
} return mask;
}
/* Caller must hold hashbucket lock for this tb with local BH disabled */ staticvoid sctp_bucket_destroy(struct sctp_bind_bucket *pp)
{ if (pp && hlist_empty(&pp->owner)) {
__hlist_del(&pp->node);
kmem_cache_free(sctp_bucket_cachep, pp);
SCTP_DBG_OBJCNT_DEC(bind_bucket);
}
}
/* Release this socket's reference to a local port. */ staticinlinevoid __sctp_put_port(struct sock *sk)
{ struct sctp_bind_hashbucket *head =
&sctp_port_hashtable[sctp_phashfn(sock_net(sk),
inet_sk(sk)->inet_num)]; struct sctp_bind_bucket *pp;
/* Sequenced packets can come disconnected. If so we report the *problem.
*/
error = -ENOTCONN;
/* Is there a good reason to think that we may receive some data? */ if (list_empty(&sctp_sk(sk)->ep->asocs) && !sctp_sstate(sk, LISTENING)) goto out;
/* Handle signals. */ if (signal_pending(current)) goto interrupted;
/* Let another process have a go. Since we are going to sleep *anyway.Note:Thismaycauseoddbehaviorsifthemessage *doesnotfitintheuser'sbuffer,butthisseemstobethe *onlywaytohonorMSG_DONTWAITrealistically.
*/
release_sock(sk);
*timeo_p = schedule_timeout(*timeo_p);
lock_sock(sk);
/* Receive a datagram. *Note:Thisisprettymuchthesameroutineasincore/datagram.c *withafewchangestomakelksctpwork.
*/ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int *err)
{ int error; struct sk_buff *skb; long timeo;
do { /* Again only user level code calls this function, *sonothinginterruptlevel *willsuddenlyeatthereceive_queue. * *Lookatcurrentnfsclientbytheway... *However,thisfunctionwascorrectinanycase.8)
*/ if (flags & MSG_PEEK) {
skb = skb_peek(&sk->sk_receive_queue); if (skb)
refcount_inc(&skb->users);
} else {
skb = __skb_dequeue(&sk->sk_receive_queue);
}
if (skb) return skb;
/* Caller is allowed not to check sk->sk_err before calling. */
error = sock_error(sk); if (error) goto no_packet;
if (sk->sk_shutdown & RCV_SHUTDOWN) break;
/* User doesn't want to wait. */
error = -EAGAIN; if (!timeo) goto no_packet;
} while (sctp_wait_for_packet(sk, err, &timeo) == 0);
return NULL;
no_packet:
*err = error; return NULL;
}
/* If sndbuf has changed, wake up per association sndbuf waiters. */ staticvoid __sctp_write_space(struct sctp_association *asoc)
{ struct sock *sk = asoc->base.sk;
if (sctp_wspace(asoc) <= 0) return;
if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
if (sctp_writeable(sk)) { struct socket_wq *wq;
rcu_read_lock();
wq = rcu_dereference(sk->sk_wq); if (wq) { if (waitqueue_active(&wq->wait))
wake_up_interruptible_poll(&wq->wait, EPOLLOUT |
EPOLLWRNORM | EPOLLWRBAND);
/* Note that we try to include the Async I/O support *herebymodelingfromthecurrentTCP/UDPcode. *Wehavenottestedwithityet.
*/ if (!(sk->sk_shutdown & SEND_SHUTDOWN))
sock_wake_async(wq, SOCK_WAKE_SPACE, POLL_OUT);
}
rcu_read_unlock();
}
}
/* We do accounting for the sndbuf space per association, *soweonlyneedtowakeourownassociation.
*/ if (asoc->ep->sndbuf_policy) return __sctp_write_space(asoc);
/* If association goes down and is just flushing its *outq,thenjustnormallynotifyothers.
*/ if (asoc->base.dead) return sctp_write_space(sk);
/* Accounting for the sndbuf space is per socket, so we *needtowakeupothers,trytobefairandincaseof *otherassociations,letthemhaveagofirstinstead *ofjustdoingasctp_write_space()call. * *Notethatwereachsctp_wake_up_waiters()onlywhen *associationsfreeupqueuedchunks,thusweareunder *lockandthelistofassociationsonasocketis *guaranteednottochange.
*/ for (tmp = list_next_entry(tmp, asocs); 1;
tmp = list_next_entry(tmp, asocs)) { /* Manually skip the head element. */ if (&tmp->asocs == &((sctp_sk(sk))->ep->asocs)) continue; /* Wake up association. */
__sctp_write_space(tmp); /* We've reached the end. */ if (tmp == asoc) break;
}
}
/* Do accounting for the sndbuf space. *Decrementtheusedsndbufspaceofthecorrespondingassociationbythe *datasizewhichwasjusttransmitted(freed).
*/ staticvoid sctp_wfree(struct sk_buff *skb)
{ struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg; struct sctp_association *asoc = chunk->asoc; struct sock *sk = asoc->base.sk;
if (chunk->shkey) { struct sctp_shared_key *shkey = chunk->shkey;
/* refcnt == 2 and !list_empty mean after this release, it's *notbeingusedanywhere,andit'stimetonotifyuserland *thatthisshkeycanbefreedifit'sbeendeactivated.
*/ if (shkey->deactivated && !list_empty(&shkey->key_list) &&
refcount_read(&shkey->refcnt) == 2) { struct sctp_ulpevent *ev;
ev = sctp_ulpevent_make_authkey(asoc, shkey->key_id,
SCTP_AUTH_FREE_KEY,
GFP_KERNEL); if (ev)
asoc->stream.si->enqueue_event(&asoc->ulpq, ev);
}
sctp_auth_shkey_release(chunk->shkey);
}
sock_wfree(skb);
sctp_wake_up_waiters(sk, asoc);
sctp_association_put(asoc);
}
/* Do accounting for the receive space on the socket. *Accountingfortheassociationisdoneinulpevent.c *Wesetthisasadestructorforthecloneddataskbssothat *accountingisdoneatthecorrecttime.
*/ void sctp_sock_rfree(struct sk_buff *skb)
{ struct sock *sk = skb->sk; struct sctp_ulpevent *event = sctp_skb2event(skb);
/* Helper function to wait for space in the sndbuf. */ staticint sctp_wait_for_sndbuf(struct sctp_association *asoc, struct sctp_transport *transport, long *timeo_p, size_t msg_len)
{ struct sock *sk = asoc->base.sk; long current_timeo = *timeo_p;
DEFINE_WAIT(wait); int err = 0;
/* Increment the transport and association's refcnt. */ if (transport)
sctp_transport_hold(transport);
sctp_association_hold(asoc);
/* Wait on the association specific sndbuf space. */ for (;;) {
prepare_to_wait_exclusive(&asoc->wait, &wait,
TASK_INTERRUPTIBLE); if (asoc->base.dead) goto do_dead; if ((!*timeo_p) || (transport && transport->dead)) goto do_nonblock; if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING) goto do_error; if (signal_pending(current)) goto do_interrupted; if ((int)msg_len <= sctp_wspace(asoc) &&
sk_wmem_schedule(sk, msg_len)) break;
/* Let another process have a go. Since we are going *tosleepanyway.
*/
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
lock_sock(sk); if (sk != asoc->base.sk) goto do_error;
*timeo_p = current_timeo;
}
out:
finish_wait(&asoc->wait, &wait);
/* Release the transport and association's refcnt. */ if (transport)
sctp_transport_put(transport);
sctp_association_put(asoc);
/* If socket sndbuf has changed, wake up all per association waiters. */ void sctp_write_space(struct sock *sk)
{ struct sctp_association *asoc;
/* Wake up the tasks in each wait queue. */
list_for_each_entry(asoc, &((sctp_sk(sk))->ep->asocs), asocs) {
__sctp_write_space(asoc);
}
}
/* Is there any sndbuf space available on the socket? * *Notethatsk_wmem_allocisthesumofthesendbuffersonallofthe *associationsonthesamesocket.ForaUDP-stylesocketwith *multipleassociations,itispossibleforittobe"unwriteable" *prematurely.Iassumethatthisisacceptablebecause *apremature"unwriteable"isbetterthananaccidental"writeable"which *wouldcauseanunwantedblockundercertaincircumstances.Forthe1-1 *UDP-stylesocketsorTCP-stylesockets,thiscodeshouldwork. *-Daisy
*/ staticbool sctp_writeable(conststruct sock *sk)
{ return READ_ONCE(sk->sk_sndbuf) > READ_ONCE(sk->sk_wmem_queued);
}
/* Wait for an association to go into ESTABLISHED state. If timeout is 0, *returnsimmediatelywithEINPROGRESS.
*/ staticint sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
{ struct sock *sk = asoc->base.sk; int err = 0; long current_timeo = *timeo_p;
DEFINE_WAIT(wait);
/* Increment the association's refcnt. */
sctp_association_hold(asoc);
for (;;) {
prepare_to_wait_exclusive(&asoc->wait, &wait,
TASK_INTERRUPTIBLE); if (!*timeo_p) goto do_nonblock; if (sk->sk_shutdown & RCV_SHUTDOWN) break; if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
asoc->base.dead) goto do_error; if (signal_pending(current)) goto do_interrupted;
if (sctp_state(asoc, ESTABLISHED)) break;
/* Let another process have a go. Since we are going *tosleepanyway.
*/
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
lock_sock(sk);
*timeo_p = current_timeo;
}
out:
finish_wait(&asoc->wait, &wait);
/* Release the association's refcnt. */
sctp_association_put(asoc);
/* Populate the fields of the newsk from the oldsk and migrate the assoc *anditsmessagestothenewsk.
*/ staticint sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, struct sctp_association *assoc, enum sctp_socket_type type)
{ struct sctp_sock *oldsp = sctp_sk(oldsk); struct sctp_sock *newsp = sctp_sk(newsk); struct sctp_bind_bucket *pp; /* hash list port iterator */ struct sctp_endpoint *newep = newsp->ep; struct sk_buff *skb, *tmp; struct sctp_ulpevent *event; struct sctp_bind_hashbucket *head; int err;
/* Migrate socket buffer sizes and all the socket level options to the *newsocket.
*/
newsk->sk_sndbuf = oldsk->sk_sndbuf;
newsk->sk_rcvbuf = oldsk->sk_rcvbuf; /* Brute force copy old sctp opt. */
sctp_copy_descendant(newsk, oldsk);
/* Restore the ep value that was overwritten with the above structure *copy.
*/
newsp->ep = newep;
newsp->hmac = NULL;
/* Hook this new socket in to the bind_hash list. */
head = &sctp_port_hashtable[sctp_phashfn(sock_net(oldsk),
inet_sk(oldsk)->inet_num)];
spin_lock_bh(&head->lock);
pp = sctp_sk(oldsk)->bind_hash;
sk_add_bind_node(newsk, &pp->owner);
sctp_sk(newsk)->bind_hash = pp;
inet_sk(newsk)->inet_num = inet_sk(oldsk)->inet_num;
spin_unlock_bh(&head->lock);
/* Copy the bind_addr list from the original endpoint to the new *endpointsothatwecanhandlerestartsproperly
*/
err = sctp_bind_addr_dup(&newsp->ep->base.bind_addr,
&oldsp->ep->base.bind_addr, GFP_KERNEL); if (err) return err;
/* New ep's auth_hmacs should be set if old ep's is set, in case *thatnet->sctp.auth_enablehasbeenchangedto0byusersand *newep'sauth_hmacscouldn'tbesetinsctp_endpoint_init().
*/ if (oldsp->ep->auth_hmacs) {
err = sctp_auth_init_hmacs(newsp->ep, GFP_KERNEL); if (err) return err;
}
sctp_auto_asconf_init(newsp);
/* Move any messages in the old socket's receive queue that are for the *peeledoffassociationtothenewsocket'sreceivequeue.
*/
sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
event = sctp_skb2event(skb); if (event->asoc == assoc) {
__skb_unlink(skb, &oldsk->sk_receive_queue);
__skb_queue_tail(&newsk->sk_receive_queue, skb);
sctp_skb_set_owner_r_frag(skb, newsk);
}
}
/* Clean up any messages pending delivery due to partial *delivery.Threecases: *1)Nopartialdeliver;nowork. *2)Peelingoffpartialdelivery;keeppd_lobbyinnewpd_lobby. *3)Peelingoffnon-partialdelivery;movepd_lobbytoreceive_queue.
*/
atomic_set(&sctp_sk(newsk)->pd_mode, assoc->ulpq.pd_mode);
if (atomic_read(&sctp_sk(oldsk)->pd_mode)) { struct sk_buff_head *queue;
/* Decide which queue to move pd_lobby skbs to. */ if (assoc->ulpq.pd_mode) {
queue = &newsp->pd_lobby;
} else
queue = &newsk->sk_receive_queue;
/* Walk through the pd_lobby, looking for skbs that *needmovedtothenewsocket.
*/
sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
event = sctp_skb2event(skb); if (event->asoc == assoc) {
__skb_unlink(skb, &oldsp->pd_lobby);
__skb_queue_tail(queue, skb);
sctp_skb_set_owner_r_frag(skb, newsk);
}
}
/* Clear up any skbs waiting for the partial *deliverytofinish.
*/ if (assoc->ulpq.pd_mode)
sctp_clear_pd(oldsk, NULL);
/* Set the type of socket to indicate that it is peeled off from the *originalUDP-stylesocketorcreatedwiththeaccept()callona *TCP-stylesocket..
*/
newsp->type = type;
/* Mark the new socket "in-use" by the user so that any packets *thatmayarriveontheassociationafterwe'vemoveditare *queuedtothebacklog.Thispreventsapotentialracebetween *backlogprocessingontheoldsocketandnew-packetprocessing *onthenewsocket. * *Thecallerhasjustallocatednewsksowecanguaranteethatother *pathswon'ttrytolockitandthenoldsk.
*/
lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
sctp_assoc_migrate(assoc, newsk);
sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
/* If the association on the newsk is already closed before accept() *iscalled,setRCV_SHUTDOWNflag.
*/ if (sctp_state(assoc, CLOSED) && sctp_style(newsk, TCP)) {
inet_sk_set_state(newsk, SCTP_SS_CLOSED);
newsk->sk_shutdown |= RCV_SHUTDOWN;
} else {
inet_sk_set_state(newsk, SCTP_SS_ESTABLISHED);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.753Bemerkung:
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.