📄 socket.c
字号:
if (likely(res != -ELINKCONG)) {exit: up(&tsock->sem); return res; } if (m->msg_flags & MSG_DONTWAIT) { res = -EWOULDBLOCK; goto exit; } if (wait_event_interruptible(*sock->sk->sk_sleep, !tsock->p->congested)) { res = -ERESTARTSYS; goto exit; } } while (1);}/** * send_stream - send stream-oriented data * @iocb: (unused) * @sock: socket structure * @m: data to send * @total_len: total length of data to be sent * * Used for SOCK_STREAM data. * * Returns the number of bytes sent on success (or partial success), * or errno if no data sent */static int send_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len){ struct tipc_port *tport; struct msghdr my_msg; struct iovec my_iov; struct iovec *curr_iov; int curr_iovlen; char __user *curr_start; u32 hdr_size; int curr_left; int bytes_to_send; int bytes_sent; int res; /* Handle special cases where there is no connection */ if (unlikely(sock->state != SS_CONNECTED)) { if (sock->state == SS_UNCONNECTED) return send_packet(iocb, sock, m, total_len); else if (sock->state == SS_DISCONNECTING) return -EPIPE; else return -ENOTCONN; } if (unlikely(m->msg_name)) return -EISCONN; /* * Send each iovec entry using one or more messages * * Note: This algorithm is good for the most likely case * (i.e. one large iovec entry), but could be improved to pass sets * of small iovec entries into send_packet(). */ curr_iov = m->msg_iov; curr_iovlen = m->msg_iovlen; my_msg.msg_iov = &my_iov; my_msg.msg_iovlen = 1; my_msg.msg_flags = m->msg_flags; my_msg.msg_name = NULL; bytes_sent = 0; tport = tipc_sk(sock->sk)->p; hdr_size = msg_hdr_sz(&tport->phdr); while (curr_iovlen--) { curr_start = curr_iov->iov_base; curr_left = curr_iov->iov_len; while (curr_left) { bytes_to_send = tport->max_pkt - hdr_size; if (bytes_to_send > TIPC_MAX_USER_MSG_SIZE) bytes_to_send = TIPC_MAX_USER_MSG_SIZE; if (curr_left < bytes_to_send) bytes_to_send = curr_left; my_iov.iov_base = curr_start; my_iov.iov_len = bytes_to_send; if ((res = send_packet(iocb, sock, &my_msg, 0)) < 0) { if (bytes_sent != 0) res = bytes_sent; return res; } curr_left -= bytes_to_send; curr_start += bytes_to_send; bytes_sent += bytes_to_send; } curr_iov++; } return bytes_sent;}/** * auto_connect - complete connection setup to a remote port * @sock: socket structure * @tsock: TIPC-specific socket structure * @msg: peer's response message * * Returns 0 on success, errno otherwise */static int auto_connect(struct socket *sock, struct tipc_sock *tsock, struct tipc_msg *msg){ struct tipc_portid peer; if (msg_errcode(msg)) { sock->state = SS_DISCONNECTING; return -ECONNREFUSED; } peer.ref = msg_origport(msg); peer.node = msg_orignode(msg); tipc_connect2port(tsock->p->ref, &peer); tipc_set_portimportance(tsock->p->ref, msg_importance(msg)); sock->state = SS_CONNECTED; return 0;}/** * set_orig_addr - capture sender's address for received message * @m: descriptor for message info * @msg: received message header * * Note: Address is not captured if not requested by receiver. */static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg){ struct sockaddr_tipc *addr = (struct sockaddr_tipc *)m->msg_name; if (addr) { addr->family = AF_TIPC; addr->addrtype = TIPC_ADDR_ID; addr->addr.id.ref = msg_origport(msg); addr->addr.id.node = msg_orignode(msg); addr->addr.name.domain = 0; /* could leave uninitialized */ addr->scope = 0; /* could leave uninitialized */ m->msg_namelen = sizeof(struct sockaddr_tipc); }}/** * anc_data_recv - optionally capture ancillary data for received message * @m: descriptor for message info * @msg: received message header * @tport: TIPC port associated with message * * Note: Ancillary data is not captured if not requested by receiver. * * Returns 0 if successful, otherwise errno */static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg, struct tipc_port *tport){ u32 anc_data[3]; u32 err; u32 dest_type; int has_name; int res; if (likely(m->msg_controllen == 0)) return 0; /* Optionally capture errored message object(s) */ err = msg ? msg_errcode(msg) : 0; if (unlikely(err)) { anc_data[0] = err; anc_data[1] = msg_data_sz(msg); if ((res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data))) return res; if (anc_data[1] && (res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1], msg_data(msg)))) return res; } /* Optionally capture message destination object */ dest_type = msg ? msg_type(msg) : TIPC_DIRECT_MSG; switch (dest_type) { case TIPC_NAMED_MSG: has_name = 1; anc_data[0] = msg_nametype(msg); anc_data[1] = msg_namelower(msg); anc_data[2] = msg_namelower(msg); break; case TIPC_MCAST_MSG: has_name = 1; anc_data[0] = msg_nametype(msg); anc_data[1] = msg_namelower(msg); anc_data[2] = msg_nameupper(msg); break; case TIPC_CONN_MSG: has_name = (tport->conn_type != 0); anc_data[0] = tport->conn_type; anc_data[1] = tport->conn_instance; anc_data[2] = tport->conn_instance; break; default: has_name = 0; } if (has_name && (res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data))) return res; return 0;}/** * recv_msg - receive packet-oriented message * @iocb: (unused) * @m: descriptor for message info * @buf_len: total size of user buffer area * @flags: receive flags * * Used for SOCK_DGRAM, SOCK_RDM, and SOCK_SEQPACKET messages. * If the complete message doesn't fit in user area, truncate it. * * Returns size of returned message data, errno otherwise */static int recv_msg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags){ struct tipc_sock *tsock = tipc_sk(sock->sk); struct sk_buff *buf; struct tipc_msg *msg; unsigned int q_len; unsigned int sz; u32 err; int res; /* Currently doesn't support receiving into multiple iovec entries */ if (m->msg_iovlen != 1) return -EOPNOTSUPP; /* Catch invalid receive attempts */ if (unlikely(!buf_len)) return -EINVAL; if (sock->type == SOCK_SEQPACKET) { if (unlikely(sock->state == SS_UNCONNECTED)) return -ENOTCONN; if (unlikely((sock->state == SS_DISCONNECTING) && (skb_queue_len(&sock->sk->sk_receive_queue) == 0))) return -ENOTCONN; } /* Look for a message in receive queue; wait if necessary */ if (unlikely(down_interruptible(&tsock->sem))) return -ERESTARTSYS;restart: if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & MSG_DONTWAIT))) { res = -EWOULDBLOCK; goto exit; } if ((res = wait_event_interruptible( *sock->sk->sk_sleep, ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) || (sock->state == SS_DISCONNECTING))) )) { goto exit; } /* Catch attempt to receive on an already terminated connection */ /* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */ if (!q_len) { res = -ENOTCONN; goto exit; } /* Get access to first message in receive queue */ buf = skb_peek(&sock->sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Complete connection setup for an implied connect */ if (unlikely(sock->state == SS_CONNECTING)) { if ((res = auto_connect(sock, tsock, msg))) goto exit; } /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_queue(tsock); goto restart; } /* Capture sender's address (optional) */ set_orig_addr(m, msg); /* Capture ancillary data (optional) */ if ((res = anc_data_recv(m, msg, tsock->p))) goto exit; /* Capture message data (if valid) & compute return value (always) */ if (!err) { if (unlikely(buf_len < sz)) { sz = buf_len; m->msg_flags |= MSG_TRUNC; } if (unlikely(copy_to_user(m->msg_iov->iov_base, msg_data(msg), sz))) { res = -EFAULT; goto exit; } res = sz; } else { if ((sock->state == SS_READY) || ((err == TIPC_CONN_SHUTDOWN) || m->msg_control)) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tsock->p->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tsock->p->ref, tsock->p->conn_unacked); advance_queue(tsock); }exit: up(&tsock->sem); return res;}/** * recv_stream - receive stream-oriented data * @iocb: (unused) * @m: descriptor for message info * @buf_len: 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. * * Returns size of returned message data, errno otherwise */static int recv_stream(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags){ struct tipc_sock *tsock = tipc_sk(sock->sk); struct sk_buff *buf; struct tipc_msg *msg; unsigned int q_len; unsigned int sz; int sz_to_copy; int sz_copied = 0; int needed; char __user *crs = m->msg_iov->iov_base; unsigned char *buf_crs; u32 err; int res; /* Currently doesn't support receiving into multiple iovec entries */ if (m->msg_iovlen != 1) return -EOPNOTSUPP; /* Catch invalid receive attempts */ if (unlikely(!buf_len)) return -EINVAL; if (unlikely(sock->state == SS_DISCONNECTING)) { if (skb_queue_len(&sock->sk->sk_receive_queue) == 0) return -ENOTCONN; } else if (unlikely(sock->state != SS_CONNECTED)) return -ENOTCONN; /* Look for a message in receive queue; wait if necessary */ if (unlikely(down_interruptible(&tsock->sem))) return -ERESTARTSYS;restart: if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & MSG_DONTWAIT))) { res = -EWOULDBLOCK; goto exit; } if ((res = wait_event_interruptible( *sock->sk->sk_sleep, ((q_len = skb_queue_len(&sock->sk->sk_receive_queue)) || (sock->state == SS_DISCONNECTING))) )) { goto exit; } /* Catch attempt to receive on an already terminated connection */ /* [THIS CHECK MAY OVERLAP WITH AN EARLIER CHECK] */ if (!q_len) { res = -ENOTCONN; goto exit; } /* Get access to first message in receive queue */ buf = skb_peek(&sock->sk->sk_receive_queue); msg = buf_msg(buf); sz = msg_data_sz(msg); err = msg_errcode(msg); /* Discard an empty non-errored message & try again */ if ((!sz) && (!err)) { advance_queue(tsock); goto restart; } /* Optionally capture sender's address & ancillary data of first msg */ if (sz_copied == 0) { set_orig_addr(m, msg); if ((res = anc_data_recv(m, msg, tsock->p))) goto exit; } /* Capture message data (if valid) & compute return value (always) */ if (!err) { buf_crs = (unsigned char *)(TIPC_SKB_CB(buf)->handle); sz = skb_tail_pointer(buf) - buf_crs; needed = (buf_len - sz_copied); sz_to_copy = (sz <= needed) ? sz : needed; if (unlikely(copy_to_user(crs, buf_crs, sz_to_copy))) { res = -EFAULT; goto exit; } sz_copied += sz_to_copy; if (sz_to_copy < sz) { if (!(flags & MSG_PEEK)) TIPC_SKB_CB(buf)->handle = buf_crs + sz_to_copy; goto exit; } crs += sz_to_copy; } else { if (sz_copied != 0) goto exit; /* can't add error msg to valid data */ if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) res = 0; else res = -ECONNRESET; } /* Consume received message (optional) */ if (likely(!(flags & MSG_PEEK))) { if (unlikely(++tsock->p->conn_unacked >= TIPC_FLOW_CONTROL_WIN)) tipc_acknowledge(tsock->p->ref, tsock->p->conn_unacked); advance_queue(tsock); } /* Loop around if more data is required */ if ((sz_copied < buf_len) /* didn't get all requested data */ && (flags & MSG_WAITALL) /* ... and need to wait for more */ && (!(flags & MSG_PEEK)) /* ... and aren't just peeking at data */ && (!err) /* ... and haven't reached a FIN */ ) goto restart;exit: up(&tsock->sem); return sz_copied ? sz_copied : res;}/** * queue_overloaded - test if queue overload condition exists * @queue_size: current size of queue * @base: nominal maximum size of queue * @msg: message to be added to queue * * Returns 1 if queue is currently overloaded, 0 otherwise */static int queue_overloaded(u32 queue_size, u32 base, struct tipc_msg *msg){ u32 threshold; u32 imp = msg_importance(msg); if (imp == TIPC_LOW_IMPORTANCE) threshold = base; else if (imp == TIPC_MEDIUM_IMPORTANCE) threshold = base * 2; else if (imp == TIPC_HIGH_IMPORTANCE) threshold = base * 100; else return 0; if (msg_connected(msg)) threshold *= 4; return (queue_size > threshold);}/** * async_disconnect - wrapper function used to disconnect port * @portref: TIPC port reference (passed as pointer-sized value) */static void async_disconnect(unsigned long portref){ tipc_disconnect((u32)portref);}/** * dispatch - handle arriving message * @tport: TIPC port that received message * @buf: message * * Called with port locked. Must not take socket lock to avoid deadlock risk. * * Returns TIPC error status code (TIPC_OK if message is not to be rejected) */static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf){ struct tipc_msg *msg = buf_msg(buf); struct tipc_sock *tsock = (struct tipc_sock *)tport->usr_handle; struct socket *sock; u32 recv_q_len; /* Reject message if socket is closing */ if (!tsock) return TIPC_ERR_NO_PORT; /* Reject message if it is wrong sort of message for socket */ /* * WOULD IT BE BETTER TO JUST DISCARD THESE MESSAGES INSTEAD? * "NO PORT" ISN'T REALLY THE RIGHT ERROR CODE, AND THERE MAY * BE SECURITY IMPLICATIONS INHERENT IN REJECTING INVALID TRAFFIC */ sock = tsock->sk.sk_socket; if (sock->state == SS_READY) { if (msg_connected(msg)) { msg_dbg(msg, "dispatch filter 1\n"); return TIPC_ERR_NO_PORT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -