/* * net/tipc/socket.c: TIPC socket API * * Copyright (c) 2001-2007, 2012-2019, Ericsson AB * Copyright (c) 2004-2008, 2010-2013, Wind River Systems * Copyright (c) 2020-2021, Red Hat Inc * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the names of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE.
*/
/** * struct tipc_sock - TIPC socket structure * @sk: socket - interacts with 'port' and with user via the socket API * @max_pkt: maximum packet size "hint" used when building messages sent by port * @maxnagle: maximum size of msg which can be subject to nagle * @portid: unique port identity in TIPC socket hash table * @phdr: preformatted message header used when sending messages * @cong_links: list of congested links * @publications: list of publications for port * @pub_count: total # of publications port has made during its lifetime * @conn_timeout: the time we can wait for an unresponded setup request * @probe_unacked: probe has not received ack yet * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue * @cong_link_cnt: number of congested links * @snt_unacked: # messages sent by socket, and not yet acked by peer * @snd_win: send window size * @peer_caps: peer capabilities mask * @rcv_unacked: # messages read by user, but not yet acked back to peer * @rcv_win: receive window size * @peer: 'connected' peer for dgram/rdm * @node: hash table node * @mc_method: cookie for use between socket and broadcast layer * @rcu: rcu struct for tipc_sock * @group: TIPC communications group * @oneway: message count in one direction (FIXME) * @nagle_start: current nagle value * @snd_backlog: send backlog count * @msg_acc: messages accepted; used in managing backlog and nagle * @pkt_cnt: TIPC socket packet count * @expect_ack: whether this TIPC socket is expecting an ack * @nodelay: setsockopt() TIPC_NODELAY setting * @group_is_open: TIPC socket group is fully open (FIXME) * @published: true if port has one or more associated names * @conn_addrtype: address type used when establishing connection
*/ struct tipc_sock { struct sock sk;
u32 max_pkt;
u32 maxnagle;
u32 portid; struct tipc_msg phdr; struct list_head cong_links; struct list_head publications;
u32 pub_count;
atomic_t dupl_rcvcnt;
u16 conn_timeout; bool probe_unacked;
u16 cong_link_cnt;
u16 snt_unacked;
u16 snd_win;
u16 peer_caps;
u16 rcv_unacked;
u16 rcv_win; struct sockaddr_tipc peer; struct rhash_head node; struct tipc_mc_method mc_method; struct rcu_head rcu; struct tipc_group *group;
u32 oneway;
u32 nagle_start;
u16 snd_backlog;
u16 msg_acc;
u16 pkt_cnt; bool expect_ack; bool nodelay; bool group_is_open; bool published;
u8 conn_addrtype;
};
/* tsk_blocks(): translate a buffer size in bytes to number of * advertisable blocks, taking into account the ratio truesize(len)/len * We can trust that this ratio is always < 4 for len >= FLOWCTL_BLK_SZ
*/ static u16 tsk_adv_blocks(int len)
{ return len / FLOWCTL_BLK_SZ / 4;
}
/* tsk_inc(): increment counter for sent or received data * - If block based flow control is not supported by peer we * fall back to message based ditto, incrementing the counter
*/ static u16 tsk_inc(struct tipc_sock *tsk, int msglen)
{ if (likely(tsk->peer_caps & TIPC_BLOCK_FLOWCTL)) return ((msglen / FLOWCTL_BLK_SZ) + 1); return 1;
}
/* tipc_sk_type_connectionless - check if the socket is datagram socket * @sk: socket * * Returns true if connection less, false otherwise
*/ staticbool tipc_sk_type_connectionless(struct sock *sk)
{ return sk->sk_type == SOCK_RDM || sk->sk_type == SOCK_DGRAM;
}
/* tsk_peer_msg - verify if message was sent by connected port's peer * * Handles cases where the node's network address has changed from * the default of <0.0.0> to its configured setting.
*/ staticbool tsk_peer_msg(struct tipc_sock *tsk, struct tipc_msg *msg)
{ struct sock *sk = &tsk->sk;
u32 self = tipc_own_addr(sock_net(sk));
u32 peer_port = tsk_peer_port(tsk);
u32 orig_node, peer_node;
if (unlikely(!tipc_sk_connected(sk))) returnfalse;
if (unlikely(msg_origport(msg) != peer_port)) returnfalse;
/* tipc_set_sk_state - set the sk_state of the socket * @sk: socket * * Caller must hold socket lock * * Returns 0 on success, errno otherwise
*/ staticint tipc_set_sk_state(struct sock *sk, int state)
{ int oldsk_state = sk->sk_state; int res = -EINVAL;
switch (state) { case TIPC_OPEN:
res = 0; break; case TIPC_LISTEN: case TIPC_CONNECTING: if (oldsk_state == TIPC_OPEN)
res = 0; break; case TIPC_ESTABLISHED: if (oldsk_state == TIPC_CONNECTING ||
oldsk_state == TIPC_OPEN)
res = 0; break; case TIPC_DISCONNECTING: if (oldsk_state == TIPC_CONNECTING ||
oldsk_state == TIPC_ESTABLISHED)
res = 0; break;
}
if (!res)
sk->sk_state = state;
return res;
}
staticint tipc_sk_sock_err(struct socket *sock, long *timeout)
{ struct sock *sk = sock->sk; int err = sock_error(sk); int typ = sock->type;
if (err) return err; if (typ == SOCK_STREAM || typ == SOCK_SEQPACKET) { if (sk->sk_state == TIPC_DISCONNECTING) return -EPIPE; elseif (!tipc_sk_connected(sk)) return -ENOTCONN;
} if (!*timeout) return -EAGAIN; if (signal_pending(current)) return sock_intr_errno(*timeout);
/* Caller should hold socket lock for the socket. */ staticvoid __tipc_shutdown(struct socket *sock, int error)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct net *net = sock_net(sk); long timeout = msecs_to_jiffies(CONN_TIMEOUT_DEFAULT);
u32 dnode = tsk_peer_node(tsk); struct sk_buff *skb;
/* Avoid that hi-prio shutdown msgs bypass msgs in link wakeup queue */
tipc_wait_for_cond(sock, &timeout, (!tsk->cong_link_cnt &&
!tsk_conn_cong(tsk)));
/* Push out delayed messages if in Nagle mode */
tipc_sk_push_backlog(tsk, false); /* Remove pending SYN */
__skb_queue_purge(&sk->sk_write_queue);
/* Remove partially received buffer if any */
skb = skb_peek(&sk->sk_receive_queue); if (skb && TIPC_SKB_CB(skb)->bytes_read) {
__skb_unlink(skb, &sk->sk_receive_queue);
kfree_skb(skb);
}
/* Reject all unreceived messages if connectionless */ if (tipc_sk_type_connectionless(sk)) {
tsk_rej_rx_queue(sk, error); return;
}
switch (sk->sk_state) { case TIPC_CONNECTING: case TIPC_ESTABLISHED:
tipc_set_sk_state(sk, TIPC_DISCONNECTING);
tipc_node_remove_conn(net, dnode, tsk->portid); /* Send a FIN+/- to its peer */
skb = __skb_dequeue(&sk->sk_receive_queue); if (skb) {
__skb_queue_purge(&sk->sk_receive_queue);
tipc_sk_respond(sk, skb, error); break;
}
skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
TIPC_CONN_MSG, SHORT_H_SIZE, 0, dnode,
tsk_own_node(tsk), tsk_peer_port(tsk),
tsk->portid, error); if (skb)
tipc_node_xmit_skb(net, skb, dnode, tsk->portid); break; case TIPC_LISTEN: /* Reject all SYN messages */
tsk_rej_rx_queue(sk, error); break; default:
__skb_queue_purge(&sk->sk_receive_queue); break;
}
}
/** * tipc_release - destroy a TIPC socket * @sock: socket to destroy * * This routine cleans up any messages that are still queued on the socket. * For DGRAM and RDM socket types, all queued messages are rejected. * For SEQPACKET and STREAM socket types, the first message is rejected * and any others are discarded. (If the first message on a STREAM socket * is partially-read, it is discarded and the next one is rejected instead.) * * NOTE: Rejected messages are not necessarily returned to the sender! They * are returned or discarded according to the "destination droppable" setting * specified for the message by the sender. * * Return: 0 on success, errno otherwise
*/ staticint tipc_release(struct socket *sock)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk;
/* * Exit if socket isn't fully initialized (occurs when a failed accept() * releases a pre-allocated child socket that was never used)
*/ if (sk == NULL) return 0;
sock_orphan(sk); /* Reject any messages that accumulated in backlog queue */
release_sock(sk);
tipc_dest_list_purge(&tsk->cong_links);
tsk->cong_link_cnt = 0;
call_rcu(&tsk->rcu, tipc_sk_callback);
sock->sk = NULL;
return 0;
}
/** * __tipc_bind - associate or disassociate TIPC name(s) with a socket * @sock: socket structure * @skaddr: socket address describing name(s) and desired operation * @alen: size of socket address data structure * * Name and name sequence binding are indicated using a positive scope value; * a negative scope value unbinds the specified name. Specifying no name * (i.e. a socket address length of 0) unbinds all names from the socket. * * Return: 0 on success, errno otherwise * * NOTE: This routine doesn't need to take the socket lock since it doesn't * access any non-constant socket information.
*/ staticint __tipc_bind(struct socket *sock, struct sockaddr *skaddr, int alen)
{ struct tipc_uaddr *ua = (struct tipc_uaddr *)skaddr; struct tipc_sock *tsk = tipc_sk(sock->sk); bool unbind = false;
if (unlikely(!alen)) return tipc_sk_withdraw(tsk, NULL);
if (ua->addrtype == TIPC_SERVICE_ADDR) {
ua->addrtype = TIPC_SERVICE_RANGE;
ua->sr.upper = ua->sr.lower;
} if (ua->scope < 0) {
unbind = true;
ua->scope = -ua->scope;
} /* Users may still use deprecated TIPC_ZONE_SCOPE */ if (ua->scope != TIPC_NODE_SCOPE)
ua->scope = TIPC_CLUSTER_SCOPE;
if (tsk->group) return -EACCES;
if (unbind) return tipc_sk_withdraw(tsk, ua); return tipc_sk_publish(tsk, ua);
}
int tipc_sk_bind(struct socket *sock, struct sockaddr *skaddr, int alen)
{ int res;
lock_sock(sock->sk);
res = __tipc_bind(sock, skaddr, alen);
release_sock(sock->sk); return res;
}
if (alen) { if (!tipc_uaddr_valid(ua, alen)) return -EINVAL; if (atype == TIPC_SOCKET_ADDR) return -EAFNOSUPPORT; if (ua->sr.type < TIPC_RESERVED_TYPES) {
pr_warn_once("Can't bind to reserved service type %u\n",
ua->sr.type); return -EACCES;
}
} return tipc_sk_bind(sock, skaddr, alen);
}
/** * tipc_getname - get port ID of socket or peer socket * @sock: socket structure * @uaddr: area for returned socket address * @peer: 0 = own ID, 1 = current peer ID, 2 = current/former peer ID * * Return: 0 on success, errno otherwise * * NOTE: This routine doesn't need to take the socket lock since it only * accesses socket information that is unchanging (or which changes in * a completely predictable manner).
*/ staticint tipc_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
{ struct sockaddr_tipc *addr = (struct sockaddr_tipc *)uaddr; struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk);
/** * tipc_poll - read and possibly block on pollmask * @file: file structure associated with the socket * @sock: socket for which to calculate the poll bits * @wait: ??? * * Return: pollmask value * * COMMENTARY: * It appears that the usual socket locking mechanisms are not useful here * since the pollmask info is potentially out-of-date the moment this routine * exits. TCP and other protocols seem to rely on higher level poll routines * to handle any preventable race conditions, so TIPC will do the same ... * * IMPORTANT: The fact that a read or write operation is indicated does NOT * imply that the operation will succeed, merely that it should be performed * and will not block.
*/ static __poll_t tipc_poll(struct file *file, struct socket *sock,
poll_table *wait)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk);
__poll_t revents = 0;
if (sk->sk_shutdown & RCV_SHUTDOWN)
revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK)
revents |= EPOLLHUP;
switch (sk->sk_state) { case TIPC_ESTABLISHED: if (!tsk->cong_link_cnt && !tsk_conn_cong(tsk))
revents |= EPOLLOUT;
fallthrough; case TIPC_LISTEN: case TIPC_CONNECTING: if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
revents |= EPOLLIN | EPOLLRDNORM; break; case TIPC_OPEN: if (tsk->group_is_open && !tsk->cong_link_cnt)
revents |= EPOLLOUT; if (!tipc_sk_type_connectionless(sk)) break; if (skb_queue_empty_lockless(&sk->sk_receive_queue)) break;
revents |= EPOLLIN | EPOLLRDNORM; break; case TIPC_DISCONNECTING:
revents = EPOLLIN | EPOLLRDNORM | EPOLLHUP; break;
} return revents;
}
/** * tipc_sendmcast - send multicast message * @sock: socket structure * @ua: destination address struct * @msg: message to send * @dlen: length of data to send * @timeout: timeout to wait for wakeup * * Called from function tipc_sendmsg(), which has done all sanity checks * Return: the number of bytes sent on success, or errno
*/ staticint tipc_sendmcast(struct socket *sock, struct tipc_uaddr *ua, struct msghdr *msg, size_t dlen, long timeout)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *hdr = &tsk->phdr; struct net *net = sock_net(sk); int mtu = tipc_bcast_get_mtu(net); struct sk_buff_head pkts; struct tipc_nlist dsts; int rc;
if (tsk->group) return -EACCES;
/* Block or return if any destination link is congested */
rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); if (unlikely(rc)) return rc;
/* A broadcast sent within next EXPIRE period must follow same path */
method->rcast = true;
method->mandatory = true; return dlen;
}
/** * tipc_send_group_unicast - send message to a member in the group * @sock: socket structure * @m: message to send * @dlen: total length of message data * @timeout: timeout to wait for wakeup * * Called from function tipc_sendmsg(), which has done all sanity checks * Return: the number of bytes sent on success, or errno
*/ staticint tipc_send_group_unicast(struct socket *sock, struct msghdr *m, int dlen, long timeout)
{ struct sock *sk = sock->sk; struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name; int blks = tsk_blocks(GROUP_H_SIZE + dlen); struct tipc_sock *tsk = tipc_sk(sk); struct net *net = sock_net(sk); struct tipc_member *mb = NULL;
u32 node, port; int rc;
node = ua->sk.node;
port = ua->sk.ref; if (!port && !node) return -EHOSTUNREACH;
/* Block or return if destination link or member is congested */
rc = tipc_wait_for_cond(sock, &timeout,
!tipc_dest_find(&tsk->cong_links, node, 0) &&
tsk->group &&
!tipc_group_cong(tsk->group, node, port, blks,
&mb)); if (unlikely(rc)) return rc;
if (unlikely(!mb)) return -EHOSTUNREACH;
rc = tipc_send_group_msg(net, tsk, m, mb, node, port, dlen);
return rc ? rc : dlen;
}
/** * tipc_send_group_anycast - send message to any member with given identity * @sock: socket structure * @m: message to send * @dlen: total length of message data * @timeout: timeout to wait for wakeup * * Called from function tipc_sendmsg(), which has done all sanity checks * Return: the number of bytes sent on success, or errno
*/ staticint tipc_send_group_anycast(struct socket *sock, struct msghdr *m, int dlen, long timeout)
{ struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name; struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct list_head *cong_links = &tsk->cong_links; int blks = tsk_blocks(GROUP_H_SIZE + dlen); struct tipc_msg *hdr = &tsk->phdr; struct tipc_member *first = NULL; struct tipc_member *mbr = NULL; struct net *net = sock_net(sk);
u32 node, port, exclude;
LIST_HEAD(dsts); int lookups = 0; int dstcnt, rc; bool cong;
while (++lookups < 4) {
exclude = tipc_group_exclude(tsk->group);
first = NULL;
/* Look for a non-congested destination member, if any */ while (1) { if (!tipc_nametbl_lookup_group(net, ua, &dsts, &dstcnt,
exclude, false)) return -EHOSTUNREACH;
tipc_dest_pop(&dsts, &node, &port);
cong = tipc_group_cong(tsk->group, node, port, blks,
&mbr); if (!cong) break; if (mbr == first) break; if (!first)
first = mbr;
}
/* Start over if destination was not in member list */ if (unlikely(!mbr)) continue;
if (likely(!cong && !tipc_dest_find(cong_links, node, 0))) break;
/* Block or return if destination link or member is congested */
rc = tipc_wait_for_cond(sock, &timeout,
!tipc_dest_find(cong_links, node, 0) &&
tsk->group &&
!tipc_group_cong(tsk->group, node, port,
blks, &mbr)); if (unlikely(rc)) return rc;
/* Send, unless destination disappeared while waiting */ if (likely(mbr)) break;
}
if (unlikely(lookups >= 4)) return -EHOSTUNREACH;
rc = tipc_send_group_msg(net, tsk, m, mbr, node, port, dlen);
return rc ? rc : dlen;
}
/** * tipc_send_group_bcast - send message to all members in communication group * @sock: socket structure * @m: message to send * @dlen: total length of message data * @timeout: timeout to wait for wakeup * * Called from function tipc_sendmsg(), which has done all sanity checks * Return: the number of bytes sent on success, or errno
*/ staticint tipc_send_group_bcast(struct socket *sock, struct msghdr *m, int dlen, long timeout)
{ struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name; struct sock *sk = sock->sk; struct net *net = sock_net(sk); struct tipc_sock *tsk = tipc_sk(sk); struct tipc_nlist *dsts; struct tipc_mc_method *method = &tsk->mc_method; bool ack = method->mandatory && method->rcast; int blks = tsk_blocks(MCAST_H_SIZE + dlen); struct tipc_msg *hdr = &tsk->phdr; int mtu = tipc_bcast_get_mtu(net); struct sk_buff_head pkts; int rc = -EHOSTUNREACH;
/* Block or return if any destination link or member is congested */
rc = tipc_wait_for_cond(sock, &timeout,
!tsk->cong_link_cnt && tsk->group &&
!tipc_group_bc_cong(tsk->group, blks)); if (unlikely(rc)) return rc;
dsts = tipc_group_dests(tsk->group); if (!dsts->local && !dsts->remote) return -EHOSTUNREACH;
/* Update broadcast sequence number and send windows */
tipc_group_update_bc_members(tsk->group, blks, ack);
/* Broadcast link is now free to choose method for next broadcast */
method->mandatory = false;
method->expires = jiffies;
return dlen;
}
/** * tipc_send_group_mcast - send message to all members with given identity * @sock: socket structure * @m: message to send * @dlen: total length of message data * @timeout: timeout to wait for wakeup * * Called from function tipc_sendmsg(), which has done all sanity checks * Return: the number of bytes sent on success, or errno
*/ staticint tipc_send_group_mcast(struct socket *sock, struct msghdr *m, int dlen, long timeout)
{ struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name; struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_group *grp = tsk->group; struct tipc_msg *hdr = &tsk->phdr; struct net *net = sock_net(sk);
u32 dstcnt, exclude;
LIST_HEAD(dsts);
/** * tipc_sendmsg - send message in connectionless manner * @sock: socket structure * @m: message to send * @dsz: amount of user data to be sent * * Message must have an destination specified explicitly. * Used for SOCK_RDM and SOCK_DGRAM messages, * and for 'SYN' messages on SOCK_SEQPACKET and SOCK_STREAM connections. * (Note: 'SYN+' is prohibited on SOCK_STREAM.) * * Return: the number of bytes sent on success, or errno otherwise
*/ staticint tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dsz)
{ struct sock *sk = sock->sk; int ret;
lock_sock(sk);
ret = __tipc_sendmsg(sock, m, dsz);
release_sock(sk);
if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) return -EMSGSIZE;
if (ua) { if (!tipc_uaddr_valid(ua, m->msg_namelen)) return -EINVAL;
atype = ua->addrtype;
}
/* If socket belongs to a communication group follow other paths */ if (grp) { if (!ua) return tipc_send_group_bcast(sock, m, dlen, timeout); if (atype == TIPC_SERVICE_ADDR) return tipc_send_group_anycast(sock, m, dlen, timeout); if (atype == TIPC_SOCKET_ADDR) return tipc_send_group_unicast(sock, m, dlen, timeout); if (atype == TIPC_SERVICE_RANGE) return tipc_send_group_mcast(sock, m, dlen, timeout); return -EINVAL;
}
if (!ua) {
ua = (struct tipc_uaddr *)&tsk->peer; if (!syn && ua->family != AF_TIPC) return -EDESTADDRREQ;
atype = ua->addrtype;
}
if (unlikely(syn)) { if (sk->sk_state == TIPC_LISTEN) return -EPIPE; if (sk->sk_state != TIPC_OPEN) return -EISCONN; if (tsk->published) return -EOPNOTSUPP; if (atype == TIPC_SERVICE_ADDR)
tsk->conn_addrtype = atype;
msg_set_syn(hdr, 1);
}
/* Block or return if destination link is congested */
rc = tipc_wait_for_cond(sock, &timeout,
!tipc_dest_find(clinks, skaddr.node, 0)); if (unlikely(rc)) return rc;
if (unlikely(syn && !rc)) {
tipc_set_sk_state(sk, TIPC_CONNECTING); if (dlen && timeout) {
timeout = msecs_to_jiffies(timeout);
tipc_wait_for_connect(sock, &timeout);
}
}
return rc ? rc : dlen;
}
/** * tipc_sendstream - send stream-oriented data * @sock: socket structure * @m: data to send * @dsz: total length of data to be transmitted * * Used for SOCK_STREAM data. * * Return: the number of bytes sent on success (or partial success), * or errno if no data sent
*/ staticint tipc_sendstream(struct socket *sock, struct msghdr *m, size_t dsz)
{ struct sock *sk = sock->sk; int ret;
lock_sock(sk);
ret = __tipc_sendstream(sock, m, dsz);
release_sock(sk);
/** * tipc_send_packet - send a connection-oriented message * @sock: socket structure * @m: message to send * @dsz: length of data to be transmitted * * Used for SOCK_SEQPACKET messages. * * Return: the number of bytes sent on success, or errno otherwise
*/ staticint tipc_send_packet(struct socket *sock, struct msghdr *m, size_t dsz)
{ if (dsz > TIPC_MAX_USER_MSG_SIZE) return -EMSGSIZE;
return tipc_sendstream(sock, m, dsz);
}
/* tipc_sk_finish_conn - complete the setup of a connection
*/ staticvoid tipc_sk_finish_conn(struct tipc_sock *tsk, u32 peer_port,
u32 peer_node)
{ struct sock *sk = &tsk->sk; struct net *net = sock_net(sk); struct tipc_msg *msg = &tsk->phdr;
/* Group message users may also want to know sending member's id */
srcaddr->member.family = AF_TIPC;
srcaddr->member.addrtype = TIPC_SERVICE_ADDR;
srcaddr->member.scope = 0;
srcaddr->member.addr.name.name.type = msg_nametype(hdr);
srcaddr->member.addr.name.name.instance = TIPC_SKB_CB(skb)->orig_member;
srcaddr->member.addr.name.domain = 0;
m->msg_namelen = sizeof(*srcaddr);
}
/** * tipc_sk_anc_data_recv - optionally capture ancillary data for received message * @m: descriptor for message info * @skb: received message buffer * @tsk: TIPC port associated with message * * Note: Ancillary data is not captured if not requested by receiver. * * Return: 0 if successful, otherwise errno
*/ staticint tipc_sk_anc_data_recv(struct msghdr *m, struct sk_buff *skb, struct tipc_sock *tsk)
{ struct tipc_msg *hdr;
u32 data[3] = {0,}; bool has_addr; int dlen, rc;
if (likely(m->msg_controllen == 0)) return 0;
hdr = buf_msg(skb);
dlen = msg_data_sz(hdr);
/* Capture errored message object, if any */ if (msg_errcode(hdr)) { if (skb_linearize(skb)) return -ENOMEM;
hdr = buf_msg(skb);
data[0] = msg_errcode(hdr);
data[1] = dlen;
rc = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, data); if (rc || !dlen) return rc;
rc = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, dlen, msg_data(hdr)); if (rc) return rc;
}
/* Step rcv queue to first msg with data or error; wait if necessary */ do {
rc = tipc_wait_for_rcvmsg(sock, &timeout); if (unlikely(rc)) gotoexit;
skb = skb_peek(&sk->sk_receive_queue);
skb_cb = TIPC_SKB_CB(skb);
hdr = buf_msg(skb);
dlen = msg_data_sz(hdr);
hlen = msg_hdr_sz(hdr);
err = msg_errcode(hdr);
grp_evt = msg_is_grp_evt(hdr); if (likely(dlen || err)) break;
tsk_advance_rx_queue(sk);
} while (1);
/* Collect msg meta data, including error code and rejected data */
tipc_sk_set_orig_addr(m, skb);
rc = tipc_sk_anc_data_recv(m, skb, tsk); if (unlikely(rc)) gotoexit;
hdr = buf_msg(skb);
/* Capture data if non-error msg, otherwise just set return value */ if (likely(!err)) { int offset = skb_cb->bytes_read;
/* Mark message as group event if applicable */ if (unlikely(grp_evt)) { if (msg_grp_evt(hdr) == TIPC_WITHDRAWN)
m->msg_flags |= MSG_EOR;
m->msg_flags |= MSG_OOB;
copy = 0;
}
/* Caption of data or error code/rejected data was successful */ if (unlikely(flags & MSG_PEEK)) gotoexit;
/* Send group flow control advertisement when applicable */ if (tsk->group && msg_in_group(hdr) && !grp_evt) {
__skb_queue_head_init(&xmitq);
tipc_group_update_rcv_win(tsk->group, tsk_blocks(hlen + dlen),
msg_orignode(hdr), msg_origport(hdr),
&xmitq);
tipc_node_distr_xmit(sock_net(sk), &xmitq);
}
/** * tipc_recvstream - receive stream-oriented data * @sock: network socket * @m: descriptor for message info * @buflen: total size of user buffer area * @flags: receive flags * * Used for SOCK_STREAM messages only. If not enough data is available * will optionally wait for more; never truncates data. * * Return: size of returned message data, errno otherwise
*/ staticint tipc_recvstream(struct socket *sock, struct msghdr *m,
size_t buflen, int flags)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct sk_buff *skb; struct tipc_msg *hdr; struct tipc_skb_cb *skb_cb; bool peek = flags & MSG_PEEK; int offset, required, copy, copied = 0; int hlen, dlen, err, rc; long timeout;
/* Catch invalid receive attempts */ if (unlikely(!buflen)) return -EINVAL;
/** * tipc_write_space - wake up thread if port congestion is released * @sk: socket
*/ staticvoid tipc_write_space(struct sock *sk)
{ struct socket_wq *wq;
/** * tipc_data_ready - wake up threads to indicate messages have been received * @sk: socket
*/ staticvoid tipc_data_ready(struct sock *sk)
{ struct socket_wq *wq;
switch (msg_user(hdr)) { case CONN_MANAGER:
tipc_sk_conn_proto_rcv(tsk, skb, inputq, xmitq); return; case SOCK_WAKEUP:
tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0); /* coupled with smp_rmb() in tipc_wait_for_cond() */
smp_wmb();
tsk->cong_link_cnt--;
wakeup = true;
tipc_sk_push_backlog(tsk, false); break; case GROUP_PROTOCOL:
tipc_group_proto_rcv(grp, &wakeup, hdr, inputq, xmitq); break; case TOP_SRV:
tipc_group_member_evt(tsk->group, &wakeup, &sk->sk_rcvbuf,
hdr, inputq, xmitq); break; default: break;
}
if (wakeup)
sk->sk_write_space(sk);
kfree_skb(skb);
}
/** * tipc_sk_filter_connect - check incoming message for a connection-based socket * @tsk: TIPC socket * @skb: pointer to message buffer. * @xmitq: for Nagle ACK if any * Return: true if message should be added to receive queue, false otherwise
*/ staticbool tipc_sk_filter_connect(struct tipc_sock *tsk, struct sk_buff *skb, struct sk_buff_head *xmitq)
{ struct sock *sk = &tsk->sk; struct net *net = sock_net(sk); struct tipc_msg *hdr = buf_msg(skb); bool con_msg = msg_connected(hdr);
u32 pport = tsk_peer_port(tsk);
u32 pnode = tsk_peer_node(tsk);
u32 oport = msg_origport(hdr);
u32 onode = msg_orignode(hdr); int err = msg_errcode(hdr); unsignedlong delay;
if (unlikely(msg_mcast(hdr))) returnfalse;
tsk->oneway = 0;
switch (sk->sk_state) { case TIPC_CONNECTING: /* Setup ACK */ if (likely(con_msg)) { if (err) break;
tipc_sk_finish_conn(tsk, oport, onode);
msg_set_importance(&tsk->phdr, msg_importance(hdr)); /* ACK+ message with data is added to receive queue */ if (msg_data_sz(hdr)) returntrue; /* Empty ACK-, - wake up sleeping connect() and drop */
sk->sk_state_change(sk);
msg_set_dest_droppable(hdr, 1); returnfalse;
} /* Ignore connectionless message if not from listening socket */ if (oport != pport || onode != pnode) returnfalse;
/* Rejected SYN */ if (err != TIPC_ERR_OVERLOAD) break;
/* Prepare for new setup attempt if we have a SYN clone */ if (skb_queue_empty(&sk->sk_write_queue)) break;
get_random_bytes(&delay, 2);
delay %= (tsk->conn_timeout / 4);
delay = msecs_to_jiffies(delay + 100);
sk_reset_timer(sk, &sk->sk_timer, jiffies + delay); returnfalse; case TIPC_OPEN: case TIPC_DISCONNECTING: returnfalse; case TIPC_LISTEN: /* Accept only SYN message */ if (!msg_is_syn(hdr) &&
tipc_node_get_capabilities(net, onode) & TIPC_SYN_BIT) returnfalse; if (!con_msg && !err) returntrue; returnfalse; case TIPC_ESTABLISHED: if (!skb_queue_empty(&sk->sk_write_queue))
tipc_sk_push_backlog(tsk, false); /* Accept only connection-based messages sent by peer */ if (likely(con_msg && !err && pport == oport &&
pnode == onode)) { if (msg_ack_required(hdr)) { struct sk_buff *skb;
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.