void tipc_net_route_msg(struct sk_buff *buf) { struct tipc_msg *msg; u32 dnode; if (!buf) return; msg = buf_msg(buf); msg_incr_reroute_cnt(msg); if (msg_reroute_cnt(msg) > 6) { if (msg_errcode(msg)) { msg_dbg(msg, "NET>DISC>:"); buf_discard(buf); } else { msg_dbg(msg, "NET>REJ>:"); tipc_reject_msg(buf, msg_destport(msg) ? TIPC_ERR_NO_PORT : TIPC_ERR_NO_NAME); } return; } msg_dbg(msg, "tipc_net->rout: "); /* Handle message for this node */ dnode = msg_short(msg) ? tipc_own_addr : msg_destnode(msg); if (tipc_in_scope(dnode, tipc_own_addr)) { if (msg_isdata(msg)) { if (msg_mcast(msg)) tipc_port_recv_mcast(buf, NULL); else if (msg_destport(msg)) tipc_port_recv_msg(buf); else net_route_named_msg(buf); return; } switch (msg_user(msg)) { case ROUTE_DISTRIBUTOR: tipc_cltr_recv_routing_table(buf); break; case NAME_DISTRIBUTOR: tipc_named_recv(buf); break; case CONN_MANAGER: tipc_port_recv_proto_msg(buf); break; default: msg_dbg(msg,"DROP/NET/<REC<"); buf_discard(buf); } return; } /* Handle message for another node */ msg_dbg(msg, "NET>SEND>: "); tipc_link_send(buf, dnode, msg_link_selector(msg)); }
void tipc_net_route_msg(struct sk_buff *buf) { struct tipc_msg *msg; u32 dnode; if (!buf) return; msg = buf_msg(buf); /* Handle message for this node */ dnode = msg_short(msg) ? tipc_own_addr : msg_destnode(msg); if (tipc_in_scope(dnode, tipc_own_addr)) { if (msg_isdata(msg)) { if (msg_mcast(msg)) tipc_port_mcast_rcv(buf, NULL); else if (msg_destport(msg)) tipc_port_rcv(buf); else net_route_named_msg(buf); return; } switch (msg_user(msg)) { case NAME_DISTRIBUTOR: tipc_named_rcv(buf); break; case CONN_MANAGER: tipc_port_proto_rcv(buf); break; default: kfree_skb(buf); } return; } /* Handle message for another node */ skb_trim(buf, msg_size(msg)); tipc_link_xmit(buf, dnode, msg_link_selector(msg)); }
/** * tipc_bclink_recv_pkt - receive a broadcast packet, and deliver upwards * * tipc_net_lock is read_locked, no other locks set */ void tipc_bclink_recv_pkt(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; u32 seqno; int deferred; /* Screen out unwanted broadcast messages */ if (msg_mc_netid(msg) != tipc_net_id) goto exit; node = tipc_node_find(msg_prevnode(msg)); if (unlikely(!node)) goto exit; tipc_node_lock(node); if (unlikely(!node->bclink.recv_permitted)) goto unlock; /* Handle broadcast protocol message */ if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { if (msg_type(msg) != STATE_MSG) goto unlock; if (msg_destnode(msg) == tipc_own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; bclink->retransmit_to = node; bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); spin_unlock_bh(&bc_lock); } else { tipc_node_unlock(node); bclink_peek_nack(msg); } goto exit; } /* Handle in-sequence broadcast message */ seqno = msg_seqno(msg); next_in = mod(node->bclink.last_in + 1); if (likely(seqno == next_in)) { receive: /* Deliver message to destination */ if (likely(msg_isdata(msg))) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); if (likely(msg_mcast(msg))) tipc_port_recv_mcast(buf, NULL); else kfree_skb(buf); } else if (msg_user(msg) == MSG_BUNDLER) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { int ret = tipc_link_recv_fragment(&node->bclink.defragm, &buf, &msg); if (ret < 0) goto unlock; spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); bcl->stats.recv_fragments++; if (ret > 0) bcl->stats.recv_fragmented++; spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_net_route_msg(buf); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); tipc_named_recv(buf); } else { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); spin_unlock_bh(&bc_lock); tipc_node_unlock(node); kfree_skb(buf); } buf = NULL; /* Determine new synchronization state */ tipc_node_lock(node); if (unlikely(!tipc_node_is_up(node))) goto unlock; if (node->bclink.last_in == node->bclink.last_sent) goto unlock; if (!node->bclink.deferred_head) { node->bclink.oos_state = 1; goto unlock; } msg = buf_msg(node->bclink.deferred_head); seqno = msg_seqno(msg); next_in = mod(next_in + 1); if (seqno != next_in) goto unlock; /* Take in-sequence message from deferred queue & deliver it */ buf = node->bclink.deferred_head; node->bclink.deferred_head = buf->next; buf->next = NULL; node->bclink.deferred_size--; goto receive; } /* Handle out-of-sequence broadcast message */ if (less(next_in, seqno)) { deferred = tipc_link_defer_pkt(&node->bclink.deferred_head, &node->bclink.deferred_tail, buf); node->bclink.deferred_size += deferred; bclink_update_last_sent(node, seqno); buf = NULL; } else deferred = 0; spin_lock_bh(&bc_lock); if (deferred) bcl->stats.deferred_recv++; else bcl->stats.duplicates++; spin_unlock_bh(&bc_lock); unlock: tipc_node_unlock(node); exit: kfree_skb(buf); }
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 */ /* * 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 */ 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)) 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; }
/** * 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; }
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; } } else { if (msg_mcast(msg)) { msg_dbg(msg, "dispatch filter 2\n"); return TIPC_ERR_NO_PORT; } if (sock->state == SS_CONNECTED) { if (!msg_connected(msg)) { msg_dbg(msg, "dispatch filter 3\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_CONNECTING) { if (!msg_connected(msg) && (msg_errcode(msg) == 0)) { msg_dbg(msg, "dispatch filter 4\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_LISTENING) { if (msg_connected(msg) || msg_errcode(msg)) { msg_dbg(msg, "dispatch filter 5\n"); return TIPC_ERR_NO_PORT; } } else if (sock->state == SS_DISCONNECTING) { msg_dbg(msg, "dispatch filter 6\n"); return TIPC_ERR_NO_PORT; } else /* (sock->state == SS_UNCONNECTED) */ { if (msg_connected(msg) || msg_errcode(msg)) { msg_dbg(msg, "dispatch filter 7\n"); return TIPC_ERR_NO_PORT; } } } /* Reject message if there isn't room to queue it */ if (unlikely((u32)atomic_read(&tipc_queue_size) > OVERLOAD_LIMIT_BASE)) { if (queue_overloaded(atomic_read(&tipc_queue_size), OVERLOAD_LIMIT_BASE, msg)) return TIPC_ERR_OVERLOAD; } recv_q_len = skb_queue_len(&tsock->sk.sk_receive_queue); if (unlikely(recv_q_len > (OVERLOAD_LIMIT_BASE / 2))) { if (queue_overloaded(recv_q_len, OVERLOAD_LIMIT_BASE / 2, msg)) return TIPC_ERR_OVERLOAD; } /* Initiate connection termination for an incoming 'FIN' */ if (unlikely(msg_errcode(msg) && (sock->state == SS_CONNECTED))) { sock->state = SS_DISCONNECTING; /* Note: Use signal since port lock is already taken! */ tipc_k_signal((Handler)async_disconnect, tport->ref); } /* Enqueue message (finally!) */ msg_dbg(msg,"<DISP<: "); TIPC_SKB_CB(buf)->handle = msg_data(msg); atomic_inc(&tipc_queue_size); skb_queue_tail(&sock->sk->sk_receive_queue, buf); if (waitqueue_active(sock->sk->sk_sleep)) wake_up_interruptible(sock->sk->sk_sleep); return TIPC_OK; }
/** * 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; }
void tipc_bclink_recv_pkt(struct sk_buff *buf) { struct tipc_msg *msg = buf_msg(buf); struct tipc_node *node; u32 next_in; u32 seqno; struct sk_buff *deferred; /* Screen out unwanted broadcast messages */ if (msg_mc_netid(msg) != tipc_net_id) goto exit; node = tipc_node_find(msg_prevnode(msg)); if (unlikely(!node)) goto exit; tipc_node_lock(node); if (unlikely(!node->bclink.supported)) goto unlock; if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) { if (msg_type(msg) != STATE_MSG) goto unlock; if (msg_destnode(msg) == tipc_own_addr) { tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; bclink->retransmit_to = node; bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); spin_unlock_bh(&bc_lock); } else { tipc_node_unlock(node); tipc_bclink_peek_nack(msg_destnode(msg), msg_bcast_tag(msg), msg_bcgap_after(msg), msg_bcgap_to(msg)); } goto exit; } /* Handle in-sequence broadcast message */ receive: next_in = mod(node->bclink.last_in + 1); seqno = msg_seqno(msg); if (likely(seqno == next_in)) { bcl->stats.recv_info++; node->bclink.last_in++; bclink_set_gap(node); if (unlikely(bclink_ack_allowed(seqno))) { bclink_send_ack(node); bcl->stats.sent_acks++; } if (likely(msg_isdata(msg))) { tipc_node_unlock(node); if (likely(msg_mcast(msg))) tipc_port_recv_mcast(buf, NULL); else buf_discard(buf); } else if (msg_user(msg) == MSG_BUNDLER) { bcl->stats.recv_bundles++; bcl->stats.recv_bundled += msg_msgcnt(msg); tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { bcl->stats.recv_fragments++; if (tipc_link_recv_fragment(&node->bclink.defragm, &buf, &msg)) bcl->stats.recv_fragmented++; tipc_node_unlock(node); tipc_net_route_msg(buf); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { tipc_node_unlock(node); tipc_named_recv(buf); } else { tipc_node_unlock(node); buf_discard(buf); } buf = NULL; tipc_node_lock(node); deferred = node->bclink.deferred_head; if (deferred && (buf_seqno(deferred) == mod(next_in + 1))) { buf = deferred; msg = buf_msg(buf); node->bclink.deferred_head = deferred->next; goto receive; } } else if (less(next_in, seqno)) { u32 gap_after = node->bclink.gap_after; u32 gap_to = node->bclink.gap_to; if (tipc_link_defer_pkt(&node->bclink.deferred_head, &node->bclink.deferred_tail, buf)) { node->bclink.nack_sync++; bcl->stats.deferred_recv++; if (seqno == mod(gap_after + 1)) node->bclink.gap_after = seqno; else if (less(gap_after, seqno) && less(seqno, gap_to)) node->bclink.gap_to = seqno; } buf = NULL; if (bclink_ack_allowed(node->bclink.nack_sync)) { if (gap_to != gap_after) bclink_send_nack(node); bclink_set_gap(node); } } else { bcl->stats.duplicates++; } unlock: tipc_node_unlock(node); exit: buf_discard(buf); }