/** * filter_connect - Handle all incoming messages for a connection-based socket * @tsock: TIPC socket * @msg: message * * Returns TIPC error status code and socket error status code * once it encounters some errors */ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) { struct socket *sock = tsock->sk.sk_socket; struct tipc_msg *msg = buf_msg(*buf); struct sock *sk = &tsock->sk; u32 retval = TIPC_ERR_NO_PORT; int res; if (msg_mcast(msg)) return retval; switch ((int)sock->state) { case SS_CONNECTED: /* Accept only connection-based messages sent by peer */ if (msg_connected(msg) && tipc_port_peer_msg(tsock->p, msg)) { if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; __tipc_disconnect(tsock->p); } retval = TIPC_OK; } break; case SS_CONNECTING: /* Accept only ACK or NACK message */ if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; sk->sk_err = ECONNREFUSED; retval = TIPC_OK; break; } if (unlikely(!msg_connected(msg))) break; res = auto_connect(sock, msg); if (res) { sock->state = SS_DISCONNECTING; sk->sk_err = -res; retval = TIPC_OK; break; } /* If an incoming message is an 'ACK-', it should be * discarded here because it doesn't contain useful * data. In addition, we should try to wake up * connect() routine if sleeping. */ if (msg_data_sz(msg) == 0) { kfree_skb(*buf); *buf = NULL; if (waitqueue_active(sk_sleep(sk))) wake_up_interruptible(sk_sleep(sk)); } retval = TIPC_OK; break; case SS_LISTENING: case SS_UNCONNECTED: /* Accept only SYN message */ if (!msg_connected(msg) && !(msg_errcode(msg))) retval = TIPC_OK; break; case SS_DISCONNECTING: break; default: pr_err("Unknown socket state %u\n", sock->state); } return retval; }
/** * filter_rcv - validate incoming message * @sk: socket * @buf: message * * Enqueues message on receive queue if acceptable; optionally handles * disconnect indication for a connected socket. * * Called with socket lock already taken; port lock may also be taken. * * Returns TIPC error status code (TIPC_OK if message is not to be rejected) */ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) { struct socket *sock = sk->sk_socket; struct tipc_msg *msg = buf_msg(buf); u32 recv_q_len; /* Reject message if it is wrong sort of message for socket */ if (msg_type(msg) > TIPC_DIRECT_MSG) return TIPC_ERR_NO_PORT; if (sock->state == SS_READY) { if (msg_connected(msg)) return TIPC_ERR_NO_PORT; } else { if (msg_mcast(msg)) return TIPC_ERR_NO_PORT; if (sock->state == SS_CONNECTED) { if (!msg_connected(msg) || !tipc_port_peer_msg(tipc_sk_port(sk), msg)) return TIPC_ERR_NO_PORT; } else if (sock->state == SS_CONNECTING) { if (!msg_connected(msg) && (msg_errcode(msg) == 0)) return TIPC_ERR_NO_PORT; } else if (sock->state == SS_LISTENING) { if (msg_connected(msg) || msg_errcode(msg)) return TIPC_ERR_NO_PORT; } else if (sock->state == SS_DISCONNECTING) { return TIPC_ERR_NO_PORT; } else /* (sock->state == SS_UNCONNECTED) */ { if (msg_connected(msg) || msg_errcode(msg)) return TIPC_ERR_NO_PORT; } } /* Reject message if there isn't room to queue it */ recv_q_len = (u32)atomic_read(&tipc_queue_size); if (unlikely(recv_q_len >= OVERLOAD_LIMIT_BASE)) { if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE)) return TIPC_ERR_OVERLOAD; } recv_q_len = skb_queue_len(&sk->sk_receive_queue); if (unlikely(recv_q_len >= (OVERLOAD_LIMIT_BASE / 2))) { if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE / 2)) return TIPC_ERR_OVERLOAD; } /* Enqueue message (finally!) */ TIPC_SKB_CB(buf)->handle = 0; atomic_inc(&tipc_queue_size); __skb_queue_tail(&sk->sk_receive_queue, buf); /* Initiate connection termination for an incoming 'FIN' */ if (unlikely(msg_errcode(msg) && (sock->state == SS_CONNECTED))) { sock->state = SS_DISCONNECTING; tipc_disconnect_port(tipc_sk_port(sk)); } if (waitqueue_active(sk_sleep(sk))) wake_up_interruptible(sk_sleep(sk)); return TIPC_OK; }