📄 socket.c
字号:
default_sinfo.sinfo_ppid = asoc->default_ppid; default_sinfo.sinfo_context = asoc->default_context; default_sinfo.sinfo_timetolive = asoc->default_timetolive; default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc); sinfo = &default_sinfo; } /* API 7.1.7, the sndbuf size per association bounds the * maximum size of data that can be sent in a single send call. */ if (msg_len > sk->sk_sndbuf) { err = -EMSGSIZE; goto out_free; } /* If fragmentation is disabled and the message length exceeds the * association fragmentation point, return EMSGSIZE. The I-D * does not specify what this error is, but this looks like * a great fit. */ if (sctp_sk(sk)->disable_fragments && (msg_len > asoc->frag_point)) { err = -EMSGSIZE; goto out_free; } if (sinfo) { /* Check for invalid stream. */ if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { err = -EINVAL; goto out_free; } } timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); if (!sctp_wspace(asoc)) { err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); if (err) goto out_free; } /* If an address is passed with the sendto/sendmsg call, it is used * to override the primary destination address in the TCP model, or * when MSG_ADDR_OVER flag is set in the UDP model. */ if ((sctp_style(sk, TCP) && msg_name) || (sinfo_flags & MSG_ADDR_OVER)) { chunk_tp = sctp_assoc_lookup_paddr(asoc, &to); if (!chunk_tp) { err = -EINVAL; goto out_free; } } else chunk_tp = NULL; /* Auto-connect, if we aren't connected already. */ if (sctp_state(asoc, CLOSED)) { err = sctp_primitive_ASSOCIATE(asoc, NULL); if (err < 0) goto out_free; SCTP_DEBUG_PRINTK("We associated primitively.\n"); } /* Break the message into multiple chunks of maximum size. */ datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len); if (!datamsg) { err = -ENOMEM; goto out_free; } /* Now send the (possibly) fragmented message. */ list_for_each(pos, &datamsg->chunks) { chunk = list_entry(pos, struct sctp_chunk, frag_list); sctp_datamsg_track(chunk); /* Do accounting for the write space. */ sctp_set_owner_w(chunk); chunk->transport = chunk_tp; /* Send it to the lower layers. Note: all chunks * must either fail or succeed. The lower layer * works that way today. Keep it that way or this * breaks. */ err = sctp_primitive_SEND(asoc, chunk); /* Did the lower layer accept the chunk? */ if (err) sctp_chunk_free(chunk); SCTP_DEBUG_PRINTK("We sent primitively.\n"); } sctp_datamsg_free(datamsg); if (err) goto out_free; else err = msg_len; /* If we are already past ASSOCIATE, the lower * layers are responsible for association cleanup. */ goto out_unlock;out_free: if (new_asoc) sctp_association_free(asoc);out_unlock: sctp_release_sock(sk);out_nounlock: return sctp_error(sk, msg_flags, err);#if 0do_sock_err: if (msg_len) err = msg_len; else err = sock_error(sk); goto out;do_interrupted: if (msg_len) err = msg_len; goto out;#endif /* 0 */}/* This is an extended version of skb_pull() that removes the data from the * start of a skb even when data is spread across the list of skb's in the * frag_list. len specifies the total amount of data that needs to be removed. * when 'len' bytes could be removed from the skb, it returns 0. * If 'len' exceeds the total skb length, it returns the no. of bytes that * could not be removed. */static int 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); return 0; } len -= skb_len; __skb_pull(skb, skb_len); for (list = skb_shinfo(skb)->frag_list; list; list = list->next) { rlen = sctp_skb_pull(list, len); skb->len -= (len-rlen); skb->data_len -= (len-rlen); if (!rlen) return 0; len = rlen; } return len;}/* API 3.1.3 recvmsg() - UDP Style Syntax * * ssize_t recvmsg(int socket, struct msghdr *message, * int flags); * * socket - the socket descriptor of the endpoint. * message - pointer to the msghdr structure which contains a single * user message and possibly some ancillary data. * * See Section 5 for complete description of the data * structures. * * flags - flags sent or received with the user message, see Section * 5 for complete description of the flags. */static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len){ struct sctp_ulpevent *event = NULL; struct sctp_sock *sp = sctp_sk(sk); struct sk_buff *skb; int copied; int err = 0; int skb_len; SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %zd, %s: %d, %s: " "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg, "len", len, "knoblauch", noblock, "flags", flags, "addr_len", addr_len); sctp_lock_sock(sk); if (sctp_style(sk, TCP) && !sctp_sstate(sk, ESTABLISHED)) { err = -ENOTCONN; goto out; } skb = sctp_skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; /* Get the total length of the skb including any skb's in the * frag_list. */ skb_len = skb->len; copied = skb_len; if (copied > len) copied = len; err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); event = sctp_skb2event(skb); if (err) goto out_free; sock_recv_timestamp(msg, sk, skb); if (sctp_ulpevent_is_notification(event)) { msg->msg_flags |= MSG_NOTIFICATION; sp->pf->event_msgname(event, msg->msg_name, addr_len); } else { sp->pf->skb_msgname(skb, msg->msg_name, addr_len); } /* Check if we allow SCTP_SNDRCVINFO. */ if (sp->subscribe.sctp_data_io_event) sctp_ulpevent_read_sndrcvinfo(event, msg);#if 0 /* FIXME: we should be calling IP/IPv6 layers. */ if (sk->sk_protinfo.af_inet.cmsg_flags) ip_cmsg_recv(msg, skb);#endif err = copied; /* If skb's length exceeds the user's buffer, update the skb and * push it back to the receive_queue so that the next call to * recvmsg() will return the remaining data. Don't set MSG_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 * rwnd by that amount. If all the data in the skb is read, * rwnd is updated when the event is freed. */ sctp_assoc_rwnd_increase(event->asoc, copied); goto out; } else if ((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 * the owner of the skb, freeing the skb and updating the * rwnd. */ sctp_ulpevent_free(event); }out: sctp_release_sock(sk); return err;}/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS) * * This option is a on/off flag. If enabled no SCTP message * fragmentation will be performed. Instead if a message being sent * 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, 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; return 0;}static int sctp_setsockopt_events(struct sock *sk, char __user *optval, int optlen){ if (optlen != sizeof(struct sctp_event_subscribe)) return -EINVAL; if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen)) return -EFAULT; return 0;}/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE) * * This socket option is applicable to the UDP-style socket only. When * set it will cause associations that are idle for more than the * specified number of seconds to automatically close. An association * being idle is defined an association that has NOT sent or received * user data. The special value of '0' indicates that no automatic * close of any associations should be performed. The option expects an * 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, int optlen){ struct sctp_sock *sp = sctp_sk(sk); /* Applicable to UDP-style socket only */ if (sctp_style(sk, TCP)) return -EOPNOTSUPP; if (optlen != sizeof(int)) return -EINVAL; if (copy_from_user(&sp->autoclose, optval, optlen)) return -EFAULT; sp->ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ; return 0;}/* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) * * Applications can enable or disable heartbeats for any peer address of * an association, modify an address's heartbeat interval, force a * heartbeat to be sent immediately, and adjust the address's maximum * number of retransmissions sent before an address is considered * unreachable. The following structure is used to access and modify an * address's parameters: * * struct sctp_paddrparams { * sctp_assoc_t spp_assoc_id; * struct sockaddr_storage spp_address; * uint32_t spp_hbinterval; * uint16_t spp_pathmaxrxt; * }; * * spp_assoc_id - (UDP style socket) This is filled in the application, * and identifies the association for this query. * spp_address - This specifies which address is of interest. * spp_hbinterval - This contains the value of the heartbeat interval, * in milliseconds. A value of 0, when modifying the * parameter, specifies that the heartbeat on this * address should be disabled. A value of UINT32_MAX * (4294967295), when modifying the parameter, * specifies that a heartbeat should be sent * immediately to the peer address, and the current * interval should remain unchanged. * spp_pathmaxrxt - This contains the maximum number of * retransmissions before this address shall be * considered unreachable. */static int sctp_setsockopt_peer_addr_params(struct sock *sk, char __user *optval, int optlen){ struct sctp_paddrparams params; struct sctp_transport *trans; int error; if (optlen != sizeof(struct sctp_paddrparams)) return -EINVAL; if (copy_from_user(¶ms, optval, optlen)) return -EFAULT; /* * API 7. Socket Options (setting the default value for the endpoint) * All options that support specific settings on an association by * filling in either an association id variable or a sockaddr_storage * SHOULD also support setting of the same value for the entire endpoint * (i.e. future associations). To accomplish this the following logic is * used when setting one of these options: * c) If neither the sockaddr_storage or association identification is * set i.e. the sockaddr_storage is set to all 0's (INADDR_ANY) and * the association identification is 0, the settings are a default * and to be applied to the endpoint (all future associations). */ /* update default value for endpoint (all future associations) */ if (!params.spp_assoc_id && sctp_is_any(( union sctp_addr *)¶ms.spp_address)) { /* Manual heartbeat on an endpoint is invalid. */ if (0xffffffff == params.spp_hbinterval) return -EINVAL; else if (params.spp_hbinterval) sctp_sk(sk)->paddrparam.spp_hbinterval = params.spp_hbinterval; if (params.spp_pathmaxrxt) sctp_sk(sk)->paddrparam.spp_pathmaxrxt = params.spp_pathmaxrxt; return 0; } trans = sctp_addr_id2transport(sk, ¶ms.spp_address, params.spp_assoc_id); if (!trans) return -EINVAL; /* Applications can enable or disable heartbeats for any peer address * of an association, modify an address's heartbeat interval, force a * heartbeat to be sent immediately, and adjust the address's maximum * number of retransmissions sent before an address is considered * unreachable. * * The value of the heartbeat interval, in milliseconds. A value of * UINT32_MAX (4294967295), when modifying the parameter, specifies * that a heartbeat should be sent immediately to the peer address, * and the current interval should remain unchanged. */ if (0xffffffff == params.spp_hbinterval) { error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans); if (error) return error; } else { /* The value of the heartbeat interval, in milliseconds. A value of 0, * when modifying the parameter, specifies that the heartbeat on this * address should be disabled. */ if (params.spp_hbinterval) { trans->hb_allowed = 1; trans->hb_interval = msecs_to_jiffies(params.spp_hbinterval); } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -