/* * 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;
/* Send pending response/rejected messages, if any */
tipc_node_distr_xmit(sock_net(sk), &xmitq); return 0;
}
/** * tipc_sk_enqueue - extract all buffers with destination 'dport' from * inputq and try adding them to socket or backlog queue * @inputq: list of incoming buffers with potentially different destinations * @sk: socket where the buffers should be enqueued * @dport: port number for the socket * @xmitq: output queue * * Caller must hold socket lock
*/ staticvoid tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
u32 dport, struct sk_buff_head *xmitq)
{ unsignedlong time_limit = jiffies + usecs_to_jiffies(20000); struct sk_buff *skb; unsignedint lim;
atomic_t *dcnt;
u32 onode;
while (skb_queue_len(inputq)) { if (unlikely(time_after_eq(jiffies, time_limit))) return;
skb = tipc_skb_dequeue(inputq, dport); if (unlikely(!skb)) return;
/* Add message directly to receive queue if possible */ if (!sock_owned_by_user(sk)) {
tipc_sk_filter_rcv(sk, skb, xmitq); continue;
}
/** * tipc_sk_rcv - handle a chain of incoming buffers * @net: the associated network namespace * @inputq: buffer list containing the buffers * Consumes all buffers in list until inputq is empty * Note: may be called in multiple threads referring to the same queue
*/ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
{ struct sk_buff_head xmitq;
u32 dnode, dport = 0; int err; struct tipc_sock *tsk; struct sock *sk; struct sk_buff *skb;
if (likely(tsk)) {
sk = &tsk->sk; if (likely(spin_trylock_bh(&sk->sk_lock.slock))) {
tipc_sk_enqueue(inputq, sk, dport, &xmitq);
spin_unlock_bh(&sk->sk_lock.slock);
} /* Send pending response/rejected messages, if any */
tipc_node_distr_xmit(sock_net(sk), &xmitq);
sock_put(sk); continue;
} /* No destination socket => dequeue skb if still there */
skb = tipc_skb_dequeue(inputq, dport); if (!skb) return;
/* Try secondary lookup if unresolved named message */
err = TIPC_ERR_NO_PORT; if (tipc_msg_lookup_dest(net, skb, &err)) goto xmit;
/* Prepare for message rejection */ if (!tipc_msg_reverse(tipc_own_addr(net), &skb, err)) continue;
staticint tipc_wait_for_connect(struct socket *sock, long *timeo_p)
{
DEFINE_WAIT_FUNC(wait, woken_wake_function); struct sock *sk = sock->sk; int done;
do { int err = sock_error(sk); if (err) return err; if (!*timeo_p) return -ETIMEDOUT; if (signal_pending(current)) return sock_intr_errno(*timeo_p); if (sk->sk_state == TIPC_DISCONNECTING) break;
/** * tipc_connect - establish a connection to another TIPC port * @sock: socket structure * @dest: socket address for destination port * @destlen: size of socket address data structure * @flags: file-related flags associated with socket * * Return: 0 on success, errno otherwise
*/ staticint tipc_connect(struct socket *sock, struct sockaddr *dest, int destlen, int flags)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest; struct msghdr m = {NULL,}; long timeout = (flags & O_NONBLOCK) ? 0 : tsk->conn_timeout; int previous; int res = 0;
if (destlen != sizeof(struct sockaddr_tipc)) return -EINVAL;
lock_sock(sk);
if (tsk->group) {
res = -EINVAL; gotoexit;
}
if (dst->family == AF_UNSPEC) {
memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); if (!tipc_sk_type_connectionless(sk))
res = -EINVAL; gotoexit;
} if (!tipc_sockaddr_is_sane(dst)) {
res = -EINVAL; gotoexit;
} /* DGRAM/RDM connect(), just save the destaddr */ if (tipc_sk_type_connectionless(sk)) {
memcpy(&tsk->peer, dest, destlen); gotoexit;
} elseif (dst->addrtype == TIPC_SERVICE_RANGE) {
res = -EINVAL; gotoexit;
}
previous = sk->sk_state;
switch (sk->sk_state) { case TIPC_OPEN: /* Send a 'SYN-' to destination */
m.msg_name = dest;
m.msg_namelen = destlen;
iov_iter_kvec(&m.msg_iter, ITER_SOURCE, NULL, 0, 0);
/* If connect is in non-blocking case, set MSG_DONTWAIT to * indicate send_msg() is never blocked.
*/ if (!timeout)
m.msg_flags = MSG_DONTWAIT;
res = __tipc_sendmsg(sock, &m, 0); if ((res < 0) && (res != -EWOULDBLOCK)) gotoexit;
/* Just entered TIPC_CONNECTING state; the only * difference is that return value in non-blocking * case is EINPROGRESS, rather than EALREADY.
*/
res = -EINPROGRESS;
fallthrough; case TIPC_CONNECTING: if (!timeout) { if (previous == TIPC_CONNECTING)
res = -EALREADY; gotoexit;
}
timeout = msecs_to_jiffies(timeout); /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
res = tipc_wait_for_connect(sock, &timeout); break; case TIPC_ESTABLISHED:
res = -EISCONN; break; default:
res = -EINVAL;
}
exit:
release_sock(sk); return res;
}
/** * tipc_listen - allow socket to listen for incoming connections * @sock: socket structure * @len: (unused) * * Return: 0 on success, errno otherwise
*/ staticint tipc_listen(struct socket *sock, int len)
{ struct sock *sk = sock->sk; int res;
lock_sock(sk);
res = tipc_set_sk_state(sk, TIPC_LISTEN);
release_sock(sk);
return res;
}
staticint tipc_wait_for_accept(struct socket *sock, long timeo)
{ struct sock *sk = sock->sk;
DEFINE_WAIT_FUNC(wait, woken_wake_function); int err;
/* True wake-one mechanism for incoming connections: only * one process gets woken up, not the 'whole herd'. * Since we do not 'race & poll' for established sockets * anymore, the common case will execute the loop only once.
*/ for (;;) { if (timeo && skb_queue_empty(&sk->sk_receive_queue)) {
add_wait_queue(sk_sleep(sk), &wait);
release_sock(sk);
timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo);
lock_sock(sk);
remove_wait_queue(sk_sleep(sk), &wait);
}
err = 0; if (!skb_queue_empty(&sk->sk_receive_queue)) break;
err = -EAGAIN; if (!timeo) break;
err = sock_intr_errno(timeo); if (signal_pending(current)) break;
} return err;
}
/** * tipc_accept - wait for connection request * @sock: listening socket * @new_sock: new socket that is to be connected * @arg: arguments for accept * * Return: 0 on success, errno otherwise
*/ staticint tipc_accept(struct socket *sock, struct socket *new_sock, struct proto_accept_arg *arg)
{ struct sock *new_sk, *sk = sock->sk; struct tipc_sock *new_tsock; struct msghdr m = {NULL,}; struct tipc_msg *msg; struct sk_buff *buf; long timeo; int res;
lock_sock(sk);
if (sk->sk_state != TIPC_LISTEN) {
res = -EINVAL; gotoexit;
}
timeo = sock_rcvtimeo(sk, arg->flags & O_NONBLOCK);
res = tipc_wait_for_accept(sock, timeo); if (res) gotoexit;
buf = skb_peek(&sk->sk_receive_queue);
res = tipc_sk_create(sock_net(sock->sk), new_sock, 0, arg->kern); if (res) gotoexit;
security_sk_clone(sock->sk, new_sock->sk);
/* we lock on new_sk; but lockdep sees the lock on sk */
lock_sock_nested(new_sk, SINGLE_DEPTH_NESTING);
/* * Reject any stray messages received by new socket * before the socket lock was taken (very, very unlikely)
*/
tsk_rej_rx_queue(new_sk, TIPC_ERR_NO_PORT);
/* Connect new socket to it's peer */
tipc_sk_finish_conn(new_tsock, msg_origport(msg), msg_orignode(msg));
/* Try again later if dest link is congested */ if (tsk->cong_link_cnt) {
sk_reset_timer(sk, &sk->sk_timer,
jiffies + msecs_to_jiffies(100)); return;
} /* Prepare SYN for retransmit */
tipc_msg_skb_clone(&sk->sk_write_queue, list);
}
/* Try again later if socket is busy */ if (sock_owned_by_user(sk)) {
sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ / 20);
bh_unlock_sock(sk);
sock_put(sk); return;
}
if (!skb_queue_empty(&list))
rc = tipc_node_xmit(sock_net(sk), &list, pnode, tsk->portid);
/* SYN messages may cause link congestion */ if (rc == -ELINKCONG) {
tipc_dest_push(&tsk->cong_links, pnode, 0);
tsk->cong_link_cnt = 1;
}
sock_put(sk);
}
list_for_each_entry_safe(p, safe, &tsk->publications, binding_sock) { if (!ua) {
tipc_uaddr(&_ua, TIPC_SERVICE_RANGE, p->scope,
p->sr.type, p->sr.lower, p->sr.upper);
tipc_nametbl_withdraw(net, &_ua, &p->sk, p->key); continue;
} /* Unbind specific publication */ if (p->scope != ua->scope) continue; if (p->sr.type != ua->sr.type) continue; if (p->sr.lower != ua->sr.lower) continue; if (p->sr.upper != ua->sr.upper) break;
tipc_nametbl_withdraw(net, ua, &p->sk, p->key);
rc = 0; break;
} if (list_empty(&tsk->publications)) {
tsk->published = 0;
rc = 0;
} return rc;
}
/* tipc_sk_reinit: set non-zero address in all existing sockets * when we go from standalone to network mode.
*/ void tipc_sk_reinit(struct net *net)
{ struct tipc_net *tn = net_generic(net, tipc_net_id); struct rhashtable_iter iter; struct tipc_sock *tsk; struct tipc_msg *msg;
/** * tipc_setsockopt - set socket option * @sock: socket structure * @lvl: option level * @opt: option identifier * @ov: pointer to new option value * @ol: length of option value * * For stream sockets only, accepts and ignores all IPPROTO_TCP options * (to ease compatibility). * * Return: 0 on success, errno otherwise
*/ staticint tipc_setsockopt(struct socket *sock, int lvl, int opt,
sockptr_t ov, unsignedint ol)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_group_req mreq;
u32 value = 0; int res = 0;
if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) return 0; if (lvl != SOL_TIPC) return -ENOPROTOOPT;
switch (opt) { case TIPC_IMPORTANCE: case TIPC_SRC_DROPPABLE: case TIPC_DEST_DROPPABLE: case TIPC_CONN_TIMEOUT: case TIPC_NODELAY: if (ol < sizeof(value)) return -EINVAL; if (copy_from_sockptr(&value, ov, sizeof(u32))) return -EFAULT; break; case TIPC_GROUP_JOIN: if (ol < sizeof(mreq)) return -EINVAL; if (copy_from_sockptr(&mreq, ov, sizeof(mreq))) return -EFAULT; break; default: if (!sockptr_is_null(ov) || ol) return -EINVAL;
}
lock_sock(sk);
switch (opt) { case TIPC_IMPORTANCE:
res = tsk_set_importance(sk, value); break; case TIPC_SRC_DROPPABLE: if (sock->type != SOCK_STREAM)
tsk_set_unreliable(tsk, value); else
res = -ENOPROTOOPT; break; case TIPC_DEST_DROPPABLE:
tsk_set_unreturnable(tsk, value); break; case TIPC_CONN_TIMEOUT:
tipc_sk(sk)->conn_timeout = value; break; case TIPC_MCAST_BROADCAST:
tsk->mc_method.rcast = false;
tsk->mc_method.mandatory = true; break; case TIPC_MCAST_REPLICAST:
tsk->mc_method.rcast = true;
tsk->mc_method.mandatory = true; break; case TIPC_GROUP_JOIN:
res = tipc_sk_join(tsk, &mreq); break; case TIPC_GROUP_LEAVE:
res = tipc_sk_leave(tsk); break; case TIPC_NODELAY:
tsk->nodelay = !!value;
tsk_set_nagle(tsk); break; default:
res = -EINVAL;
}
release_sock(sk);
return res;
}
/** * tipc_getsockopt - get socket option * @sock: socket structure * @lvl: option level * @opt: option identifier * @ov: receptacle for option value * @ol: receptacle for length of option value * * For stream sockets only, returns 0 length result for all IPPROTO_TCP options * (to ease compatibility). * * Return: 0 on success, errno otherwise
*/ staticint tipc_getsockopt(struct socket *sock, int lvl, int opt, char __user *ov, int __user *ol)
{ struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_service_range seq; int len, scope;
u32 value; int res;
if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) return put_user(0, ol); if (lvl != SOL_TIPC) return -ENOPROTOOPT;
res = get_user(len, ol); if (res) return res;
lock_sock(sk);
switch (opt) { case TIPC_IMPORTANCE:
value = tsk_importance(tsk); break; case TIPC_SRC_DROPPABLE:
value = tsk_unreliable(tsk); break; case TIPC_DEST_DROPPABLE:
value = tsk_unreturnable(tsk); break; case TIPC_CONN_TIMEOUT:
value = tsk->conn_timeout; /* no need to set "res", since already 0 at this point */ break; case TIPC_NODE_RECVQ_DEPTH:
value = 0; /* was tipc_queue_size, now obsolete */ break; case TIPC_SOCK_RECVQ_DEPTH:
value = skb_queue_len(&sk->sk_receive_queue); break; case TIPC_SOCK_RECVQ_USED:
value = sk_rmem_alloc_get(sk); break; case TIPC_GROUP_JOIN:
seq.type = 0; if (tsk->group)
tipc_group_self(tsk->group, &seq, &scope);
value = seq.type; break; default:
res = -EINVAL;
}
release_sock(sk);
if (res) return res; /* "get" failed */
if (len < sizeof(value)) return -EINVAL;
if (copy_to_user(ov, &value, sizeof(value))) return -EFAULT;
/* Caller should hold socket lock for the passed tipc socket. */ staticint __tipc_nl_list_sk_publ(struct sk_buff *skb, struct netlink_callback *cb, struct tipc_sock *tsk, u32 *last_publ)
{ int err; struct publication *p;
if (*last_publ) {
list_for_each_entry(p, &tsk->publications, binding_sock) { if (p->key == *last_publ) break;
} if (list_entry_is_head(p, &tsk->publications, binding_sock)) { /* We never set seq or call nl_dump_check_consistent() * this means that setting prev_seq here will cause the * consistence check to fail in the netlink callback * handler. Resulting in the last NLMSG_DONE message * having the NLM_F_DUMP_INTR flag set.
*/
cb->prev_seq = 1;
*last_publ = 0; return -EPIPE;
}
} else {
p = list_first_entry(&tsk->publications, struct publication,
binding_sock);
}
/** * tipc_sk_filtering - check if a socket should be traced * @sk: the socket to be examined * * @sysctl_tipc_sk_filter is used as the socket tuple for filtering: * (portid, sock type, name type, name lower, name upper) * * Return: true if the socket meets the socket tuple data * (value 0 = 'any') or when there is no tuple set (all = 0), * otherwise false
*/ bool tipc_sk_filtering(struct sock *sk)
{ struct tipc_sock *tsk; struct publication *p;
u32 _port, _sktype, _type, _lower, _upper;
u32 type = 0, lower = 0, upper = 0;
/** * tipc_sk_overlimit1 - check if socket rx queue is about to be overloaded, * both the rcv and backlog queues are considered * @sk: tipc sk to be checked * @skb: tipc msg to be checked * * Return: true if the socket rx queue allocation is > 90%, otherwise false
*/
/** * tipc_sk_overlimit2 - check if socket rx queue is about to be overloaded, * only the rcv queue is considered * @sk: tipc sk to be checked * @skb: tipc msg to be checked * * Return: true if the socket rx queue allocation is > 90%, otherwise false
*/
i += scnprintf(buf, sz, "sk data: %u", sk->sk_type);
i += scnprintf(buf + i, sz - i, " %d", sk->sk_state);
i += scnprintf(buf + i, sz - i, " %x", tsk_own_node(tsk));
i += scnprintf(buf + i, sz - i, " %u", tsk->portid);
i += scnprintf(buf + i, sz - i, " | %u", tsk_connected); if (tsk_connected) {
i += scnprintf(buf + i, sz - i, " %x", tsk_peer_node(tsk));
i += scnprintf(buf + i, sz - i, " %u", tsk_peer_port(tsk));
conn_type = msg_nametype(&tsk->phdr);
conn_instance = msg_nameinst(&tsk->phdr);
i += scnprintf(buf + i, sz - i, " %u", conn_type);
i += scnprintf(buf + i, sz - i, " %u", conn_instance);
}
i += scnprintf(buf + i, sz - i, " | %u", tsk->published); if (tsk->published) {
p = list_first_entry_or_null(&tsk->publications, struct publication, binding_sock);
i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.type : 0);
i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.lower : 0);
i += scnprintf(buf + i, sz - i, " %u", (p) ? p->sr.upper : 0);
}
i += scnprintf(buf + i, sz - i, " | %u", tsk->snd_win);
i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_win);
i += scnprintf(buf + i, sz - i, " %u", tsk->max_pkt);
i += scnprintf(buf + i, sz - i, " %x", tsk->peer_caps);
i += scnprintf(buf + i, sz - i, " %u", tsk->cong_link_cnt);
i += scnprintf(buf + i, sz - i, " %u", tsk->snt_unacked);
i += scnprintf(buf + i, sz - i, " %u", tsk->rcv_unacked);
i += scnprintf(buf + i, sz - i, " %u", atomic_read(&tsk->dupl_rcvcnt));
i += scnprintf(buf + i, sz - i, " %u", sk->sk_shutdown);
i += scnprintf(buf + i, sz - i, " | %d", sk_wmem_alloc_get(sk));
i += scnprintf(buf + i, sz - i, " %d", sk->sk_sndbuf);
i += scnprintf(buf + i, sz - i, " | %d", sk_rmem_alloc_get(sk));
i += scnprintf(buf + i, sz - i, " %d", sk->sk_rcvbuf);
i += scnprintf(buf + i, sz - i, " | %d\n", READ_ONCE(sk->sk_backlog.len));
if (dqueues & TIPC_DUMP_SK_SNDQ) {
i += scnprintf(buf + i, sz - i, "sk_write_queue: ");
i += tipc_list_dump(&sk->sk_write_queue, false, buf + i);
}
if (dqueues & TIPC_DUMP_SK_RCVQ) {
i += scnprintf(buf + i, sz - i, "sk_receive_queue: ");
i += tipc_list_dump(&sk->sk_receive_queue, false, buf + i);
}
if (dqueues & TIPC_DUMP_SK_BKLGQ) {
i += scnprintf(buf + i, sz - i, "sk_backlog:\n head ");
i += tipc_skb_dump(sk->sk_backlog.head, false, buf + i); if (sk->sk_backlog.tail != sk->sk_backlog.head) {
i += scnprintf(buf + i, sz - i, " tail ");
i += tipc_skb_dump(sk->sk_backlog.tail, false,
buf + i);
}
}
return i;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-04-26)
¤
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.