diff options
Diffstat (limited to 'net/tipc')
| -rw-r--r-- | net/tipc/bcast.c | 22 | ||||
| -rw-r--r-- | net/tipc/bcast.h | 2 | ||||
| -rw-r--r-- | net/tipc/bearer.c | 1 | ||||
| -rw-r--r-- | net/tipc/core.c | 9 | ||||
| -rw-r--r-- | net/tipc/crypto.c | 9 | ||||
| -rw-r--r-- | net/tipc/discover.c | 14 | ||||
| -rw-r--r-- | net/tipc/group.c | 4 | ||||
| -rw-r--r-- | net/tipc/link.c | 6 | ||||
| -rw-r--r-- | net/tipc/monitor.c | 3 | ||||
| -rw-r--r-- | net/tipc/name_distr.c | 13 | ||||
| -rw-r--r-- | net/tipc/name_table.c | 30 | ||||
| -rw-r--r-- | net/tipc/netlink.c | 18 | ||||
| -rw-r--r-- | net/tipc/netlink_compat.c | 4 | ||||
| -rw-r--r-- | net/tipc/node.c | 32 | ||||
| -rw-r--r-- | net/tipc/socket.c | 49 | ||||
| -rw-r--r-- | net/tipc/udp_media.c | 107 |
16 files changed, 219 insertions, 104 deletions
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 76a1585d3f6b..10d1ec593084 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, */ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq) + struct sk_buff_head *retrq, bool *valid) { struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct tipc_gap_ack_blks *ga; struct sk_buff_head xmitq; int rc = 0; + u16 glen; __skb_queue_head_init(&xmitq); @@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, if (msg_type(hdr) != STATE_MSG) { tipc_link_bc_init_rcv(l, hdr); } else if (!msg_bc_ack_invalid(hdr)) { - tipc_get_gap_ack_blks(&ga, l, hdr, false); - if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), - msg_bc_gap(hdr), ga, &xmitq, - retrq); - rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + glen = tipc_get_gap_ack_blks(&ga, l, hdr, false); + if (glen > msg_data_sz(hdr)) { + /* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + } else { + if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), + msg_bc_gap(hdr), ga, &xmitq, + retrq); + rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + } } tipc_bcast_unlock(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 2d9352dc7b0e..55d17b5413e1 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq); + struct sk_buff_head *retrq, bool *valid); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *bcl); int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index a3bd1ef17558..05dcd2f9e887 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -482,6 +482,7 @@ void tipc_disable_l2_media(struct tipc_bearer *b) dev = (struct net_device *)rtnl_dereference(b->media_ptr); dev_remove_pack(&b->pt); RCU_INIT_POINTER(dev->tipc_ptr, NULL); + RCU_INIT_POINTER(b->media_ptr, NULL); synchronize_net(); dev_put(dev); } diff --git a/net/tipc/core.c b/net/tipc/core.c index 434e70eabe08..315975c3be81 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -45,6 +45,7 @@ #include "crypto.h" #include <linux/module.h> +#include <linux/wait_bit.h> /* configurable TIPC parameters */ unsigned int tipc_net_id __read_mostly; @@ -118,8 +119,7 @@ static void __net_exit tipc_exit_net(struct net *net) #ifdef CONFIG_TIPC_CRYPTO tipc_crypto_stop(&tipc_net(net)->crypto_tx); #endif - while (atomic_read(&tn->wq_count)) - cond_resched(); + wait_var_event(&tn->wq_count, atomic_read(&tn->wq_count) == 0); } static void __net_exit tipc_pernet_pre_exit(struct net *net) @@ -218,6 +218,11 @@ static void __exit tipc_exit(void) unregister_pernet_device(&tipc_net_ops); tipc_unregister_sysctl(); + /* TODO: Wait for all timers that called call_rcu() to finish before + * calling rcu_barrier(). + */ + rcu_barrier(); + pr_info("Deactivated\n"); } diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 6d3b6b89b1d1..16f1ed1f6b1b 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -941,12 +941,20 @@ static int tipc_aead_decrypt(struct net *net, struct tipc_aead *aead, goto exit; } + /* Get net to avoid freed tipc_crypto when delete namespace */ + if (!maybe_get_net(net)) { + tipc_bearer_put(b); + rc = -ENODEV; + goto exit; + } + /* Now, do decrypt */ rc = crypto_aead_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) return rc; tipc_bearer_put(b); + put_net(net); exit: kfree(ctx); @@ -984,6 +992,7 @@ static void tipc_aead_decrypt_done(void *data, int err) } tipc_bearer_put(b); + put_net(net); } static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr) diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 3e54d2df5683..b9d06595b067 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -58,6 +58,7 @@ * @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) + * @rcu: RCU head for deferred freeing */ struct tipc_discoverer { u32 bearer_id; @@ -69,6 +70,7 @@ struct tipc_discoverer { struct sk_buff *skb; struct timer_list timer; unsigned long timer_intv; + struct rcu_head rcu; }; /** @@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, return 0; } +static void tipc_disc_free_rcu(struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + + kfree_skb(d->skb); + kfree(d); +} + /** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure @@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b, void tipc_disc_delete(struct tipc_discoverer *d) { timer_shutdown_sync(&d->timer); - kfree_skb(d->skb); - kfree(d); + call_rcu(&d->rcu, tipc_disc_free_rcu); } /** diff --git a/net/tipc/group.c b/net/tipc/group.c index 14e6732624e2..74f6d3dac078 100644 --- a/net/tipc/group.c +++ b/net/tipc/group.c @@ -797,10 +797,10 @@ void tipc_group_proto_rcv(struct tipc_group *grp, bool *usr_wakeup, tipc_group_open(m, usr_wakeup); return; case GRP_ACK_MSG: - if (!m) + if (!m || !grp->bc_ackers) return; acked = msg_grp_bc_acked(hdr); - if (less_eq(acked, m->bc_acked)) + if (acked != grp->bc_snd_nxt || m->bc_acked == acked) return; m->bc_acked = acked; if (--grp->bc_ackers) diff --git a/net/tipc/link.c b/net/tipc/link.c index 49dfc098d89b..6427c69f8929 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id, snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown", self_str, if_name, peer_str); - strcpy(l->if_name, if_name); + strscpy(l->if_name, if_name); l->addr = peer; l->peer_caps = peer_caps; l->net = net; @@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id, snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name, peer_str); } else { - strcpy(l->name, tipc_bclink_name); + strscpy(l->name, tipc_bclink_name); } trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!"); tipc_link_reset(l); @@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe, msg_set_dest_session(hdr, l->peer_session); } msg_set_max_pkt(hdr, l->advertised_mtu); - strcpy(data, l->if_name); + memcpy(data, l->if_name, TIPC_MAX_IF_NAME); msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME); skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME); } diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c index a94b9b36a700..1a438e312d66 100644 --- a/net/tipc/monitor.c +++ b/net/tipc/monitor.c @@ -632,9 +632,10 @@ static void mon_timeout(struct timer_list *t) { struct tipc_monitor *mon = timer_container_of(mon, t, timer); struct tipc_peer *self; - int best_member_cnt = dom_size(mon->peer_cnt) - 1; + int best_member_cnt; write_lock_bh(&mon->lock); + best_member_cnt = dom_size(mon->peer_cnt) - 1; self = mon->self; if (self && (best_member_cnt != self->applied)) { mon_update_local_domain(mon); diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index 190b49c5cbc3..ba4f4906e13b 100644 --- a/net/tipc/name_distr.c +++ b/net/tipc/name_distr.c @@ -280,12 +280,21 @@ static bool tipc_update_nametbl(struct net *net, struct distr_item *i, u32 node, u32 dtype) { struct publication *p = NULL; + u32 lower = ntohl(i->lower); + u32 upper = ntohl(i->upper); struct tipc_socket_addr sk; - struct tipc_uaddr ua; u32 key = ntohl(i->key); + struct tipc_uaddr ua; + + /* A peer-advertised binding with lower > upper can never be matched + * or withdrawn and would leak the publication; the local bind path + * rejects such ranges, so reject ranges learned from the network too. + */ + if (lower > upper) + return false; tipc_uaddr(&ua, TIPC_SERVICE_RANGE, TIPC_CLUSTER_SCOPE, - ntohl(i->type), ntohl(i->lower), ntohl(i->upper)); + ntohl(i->type), lower, upper); sk.ref = ntohl(i->port); sk.node = node; diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 253c72d1366e..6fda36ab1766 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua, struct tipc_socket_addr *sk, u32 key) { struct name_table *nt = tipc_name_table(net); + u32 max_user_pub = TIPC_MAX_PUBL - 1; struct tipc_net *tn = tipc_net(net); struct publication *p = NULL; struct sk_buff *skb = NULL; + bool protocol_type = false; u32 rc_dests; + if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE || + ua->sr.type == TIPC_TOP_SRV) + protocol_type = true; + spin_lock_bh(&tn->nametbl_lock); + if (protocol_type) + goto insert; - if (nt->local_publ_count >= TIPC_MAX_PUBL) { - pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL); + /* Reserve one entry for node state service type because it has cluster + * scope and it is distributed in bulk. So, the maximum number of user's + * publications is (TIPC_MAX_PUBL - 1). + */ + if (nt->local_publ_count >= max_user_pub) { + pr_warn("Bind failed, max limit %u reached\n", max_user_pub); goto exit; } +insert: p = tipc_nametbl_insert_publ(net, ua, sk, key); if (p) { - nt->local_publ_count++; + /* Not count node state, link state and topology server types + * so that maximum nt->local_publ_count does not prevent + * protocol service types from being inserted into the name + * table. + */ + if (!protocol_type) + nt->local_publ_count++; skb = tipc_named_publish(net, p); } rc_dests = nt->rc_dests; @@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua, p = tipc_nametbl_remove_publ(net, ua, sk, key); if (p) { - nt->local_publ_count--; + if (p->sr.type != TIPC_NODE_STATE && + p->sr.type != TIPC_LINK_STATE && + p->sr.type != TIPC_TOP_SRV) + nt->local_publ_count--; skb = tipc_named_withdraw(net, p); list_del_init(&p->binding_sock); kfree_rcu(p, rcu); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 1a9a5bdaccf4..1307dd1a9613 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = { }; /* Properties valid for media, bearer and link */ +static const struct netlink_range_validation tipc_nl_mtu_range = { + .max = U16_MAX, +}; + const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = { [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC }, [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 }, [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 }, [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 }, - [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 }, + [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range), [TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 }, [TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 } }; @@ -152,11 +156,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_DISABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_disable, }, { .cmd = TIPC_NL_BEARER_ENABLE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_enable, }, { @@ -168,11 +174,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_BEARER_ADD, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_add, }, { .cmd = TIPC_NL_BEARER_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_bearer_set, }, { @@ -197,11 +205,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_LINK_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_link, }, { .cmd = TIPC_NL_LINK_RESET_STATS, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_reset_link_stats, }, { @@ -213,6 +223,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MEDIA_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_media_set, }, { @@ -228,6 +239,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_NET_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_net_set, }, { @@ -238,6 +250,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_MON_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_monitor, }, { @@ -255,6 +268,7 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_PEER_REMOVE, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_peer_rm, }, #ifdef CONFIG_TIPC_MEDIA_UDP @@ -269,11 +283,13 @@ static const struct genl_ops tipc_genl_v2_ops[] = { { .cmd = TIPC_NL_KEY_SET, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_set_key, }, { .cmd = TIPC_NL_KEY_FLUSH, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, + .flags = GENL_UNS_ADMIN_PERM, .doit = tipc_nl_node_flush_key, }, #endif diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 2a786c56c8c5..d9a4f94ea2d4 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -221,6 +221,10 @@ static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, int rem; len = (*cmd->dumpit)(buf, &cb); + if (len < 0) { + err = len; + goto err_out; + } nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { err = nlmsg_parse_deprecated(nlmsg, GENL_HDRLEN, diff --git a/net/tipc/node.c b/net/tipc/node.c index 97aa970a0d83..bd91378b7540 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1061,18 +1061,23 @@ static void __tipc_node_link_down(struct tipc_node *n, int *bearer_id, static void tipc_node_link_down(struct tipc_node *n, int bearer_id, bool delete) { - struct tipc_link_entry *le = &n->links[bearer_id]; struct tipc_media_addr *maddr = NULL; - struct tipc_link *l = le->link; int old_bearer_id = bearer_id; + struct tipc_link_entry *le; struct sk_buff_head xmitq; - - if (!l) - return; + struct tipc_link *l; __skb_queue_head_init(&xmitq); + /* Synchronize the link lookup with bearer teardown. */ tipc_node_write_lock(n); + le = &n->links[bearer_id]; + l = le->link; + if (!l) { + tipc_node_write_unlock_fast(n); + return; + } + if (!tipc_link_is_establishing(l)) { __tipc_node_link_down(n, &bearer_id, &xmitq, &maddr); } else { @@ -1328,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n) pr_warn("Resetting all links to %x\n", n->addr); + tipc_node_write_lock(n); trace_tipc_node_reset_links(n, true, " "); + tipc_node_write_unlock_fast(n); for (i = 0; i < MAX_BEARERS; i++) { tipc_node_link_down(n, i, false); } @@ -1831,12 +1838,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n) } static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, - int bearer_id, struct sk_buff_head *xmitq) + int bearer_id, struct sk_buff_head *xmitq, + bool *valid) { struct tipc_link *ucl; int rc; - rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq); + rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid); + if (!*valid) + return; if (rc & TIPC_LINK_DOWN_EVT) { tipc_node_reset_links(n); @@ -2140,12 +2150,18 @@ rcv: /* Ensure broadcast reception is in synch with peer's send state */ if (unlikely(usr == LINK_PROTOCOL)) { + bool valid = true; + if (unlikely(skb_linearize(skb))) { tipc_node_put(n); goto discard; } hdr = buf_msg(skb); - tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); + tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid); + if (!valid) { + tipc_node_put(n); + goto discard; + } } else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) { tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr); } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 9329919fb07f..d5d70eb230b5 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -37,6 +37,7 @@ #include <linux/rhashtable.h> #include <linux/sched/signal.h> +#include <linux/uio.h> #include <trace/events/sock.h> #include "core.h" @@ -501,6 +502,7 @@ static int tipc_sk_create(struct net *net, struct socket *sock, tipc_set_sk_state(sk, TIPC_OPEN); if (tipc_sk_insert(tsk)) { sk_free(sk); + sock->sk = NULL; pr_warn("Socket create failed; port number exhausted\n"); return -EINVAL; } @@ -794,7 +796,7 @@ static __poll_t tipc_poll(struct file *file, struct socket *sock, __poll_t revents = 0; sock_poll_wait(file, sock, wait); - trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_ALL, " "); + trace_tipc_sk_poll(sk, NULL, TIPC_DUMP_NONE, " "); if (sk->sk_shutdown & RCV_SHUTDOWN) revents |= EPOLLRDHUP | EPOLLIN | EPOLLRDNORM; @@ -1362,6 +1364,9 @@ static void tipc_sk_conn_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb, __skb_queue_tail(xmitq, skb); return; } else if (mtyp == CONN_ACK) { + if (tsk->snt_unacked < msg_conn_ack(hdr)) + goto exit; + was_cong = tsk_conn_cong(tsk); tipc_sk_push_backlog(tsk, msg_nagle_ack(hdr)); tsk->snt_unacked -= msg_conn_ack(hdr); @@ -1931,7 +1936,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, if (likely(!err)) { int offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen); + copy = min_t(size_t, dlen - offset, buflen); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) goto exit; @@ -2063,7 +2068,7 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m, /* Copy data if msg ok, otherwise return error/partial data */ if (likely(!err)) { offset = skb_cb->bytes_read; - copy = min_t(int, dlen - offset, buflen - copied); + copy = min_t(size_t, dlen - offset, buflen - copied); rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); if (unlikely(rc)) break; @@ -2452,17 +2457,17 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk, atomic_set(dcnt, 0); lim = rcvbuf_limit(sk, skb) + atomic_read(dcnt); if (likely(!sk_add_backlog(sk, skb, lim))) { - trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_overlimit1(sk, skb, TIPC_DUMP_SK_BKLGQ, "bklg & rcvq >90% allocated!"); continue; } - trace_tipc_sk_dump(sk, skb, TIPC_DUMP_ALL, "err_overload!"); + trace_tipc_sk_dump(sk, skb, TIPC_DUMP_SK_BKLGQ, "err_overload!"); /* Overload => reject message back to sender */ onode = tipc_own_addr(sock_net(sk)); sk_drops_inc(sk); if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD)) { - trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_ALL, + trace_tipc_sk_rej_msg(sk, skb, TIPC_DUMP_SK_BKLGQ, "@sk_enqueue!"); __skb_queue_tail(xmitq, skb); } @@ -3220,8 +3225,7 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, * @sock: socket structure * @lvl: option level * @opt: option identifier - * @ov: receptacle for option value - * @ol: receptacle for length of option value + * @sopt: socket option container (input buffer length, output value/length) * * For stream sockets only, returns 0 length result for all IPPROTO_TCP options * (to ease compatibility). @@ -3229,22 +3233,22 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, * Return: 0 on success, errno otherwise */ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, - char __user *ov, int __user *ol) + sockopt_t *sopt) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); struct tipc_service_range seq; int len, scope; + int res = 0; u32 value; - int res; - if ((lvl == IPPROTO_TCP) && (sock->type == SOCK_STREAM)) - return put_user(0, ol); + if (lvl == IPPROTO_TCP && sock->type == SOCK_STREAM) { + sopt->optlen = 0; + return 0; + } if (lvl != SOL_TIPC) return -ENOPROTOOPT; - res = get_user(len, ol); - if (res) - return res; + len = sopt->optlen; lock_sock(sk); @@ -3260,7 +3264,6 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, 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 */ @@ -3284,15 +3287,17 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, release_sock(sk); if (res) - return res; /* "get" failed */ + return res; if (len < sizeof(value)) return -EINVAL; - if (copy_to_user(ov, &value, sizeof(value))) + if (copy_to_iter(&value, sizeof(value), &sopt->iter_out) != + sizeof(value)) return -EFAULT; + sopt->optlen = sizeof(value); - return put_user(sizeof(value), ol); + return 0; } static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) @@ -3365,7 +3370,7 @@ static const struct proto_ops msg_ops = { .listen = sock_no_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_sendmsg, .recvmsg = tipc_recvmsg, .mmap = sock_no_mmap, @@ -3385,7 +3390,7 @@ static const struct proto_ops packet_ops = { .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_send_packet, .recvmsg = tipc_recvmsg, .mmap = sock_no_mmap, @@ -3405,7 +3410,7 @@ static const struct proto_ops stream_ops = { .listen = tipc_listen, .shutdown = tipc_shutdown, .setsockopt = tipc_setsockopt, - .getsockopt = tipc_getsockopt, + .getsockopt_iter = tipc_getsockopt, .sendmsg = tipc_sendstream, .recvmsg = tipc_recvstream, .mmap = sock_no_mmap, diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 2c66b356025a..230645cc01c9 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -40,6 +40,7 @@ #include <linux/igmp.h> #include <linux/kernel.h> #include <linux/workqueue.h> +#include <linux/wait_bit.h> #include <linux/list.h> #include <net/sock.h> #include <net/ip.h> @@ -89,17 +90,21 @@ struct udp_replicast { /** * struct udp_bearer - ip/udp bearer data structure * @bearer: associated generic tipc bearer - * @ubsock: bearer associated socket + * @sk: bearer associated socket * @ifindex: local address scope * @work: used to schedule deferred work on a bearer * @rcast: associated udp_replicast container + * @rcast_lock: serialize updates to @rcast.list against concurrent updaters + * @disabled: bearer is being torn down; reject further @rcast.list additions */ struct udp_bearer { struct tipc_bearer __rcu *bearer; - struct socket *ubsock; + struct sock *sk; u32 ifindex; struct work_struct work; struct udp_replicast rcast; + spinlock_t rcast_lock; + bool disabled; }; static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr) @@ -194,7 +199,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, } ttl = ip4_dst_hoplimit(&rt->dst); - udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr, + udp_tunnel_xmit_skb(rt, ub->sk, skb, src->ipv4.s_addr, dst->ipv4.s_addr, 0, ttl, 0, src->port, dst->port, false, true, 0); #if IS_ENABLED(CONFIG_IPV6) @@ -206,7 +211,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, .saddr = src->ipv6, .flowi6_proto = IPPROTO_UDP }; - ndst = ip6_dst_lookup_flow(net, ub->ubsock->sk, + ndst = ip6_dst_lookup_flow(net, ub->sk, &fl6, NULL); if (IS_ERR(ndst)) { err = PTR_ERR(ndst); @@ -215,7 +220,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb, dst_cache_set_ip6(cache, ndst, &fl6.saddr); } ttl = ip6_dst_hoplimit(ndst); - udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL, + udp_tunnel6_xmit_skb(ndst, ub->sk, skb, NULL, &src->ipv6, &dst->ipv6, 0, ttl, 0, src->port, dst->port, false, 0); #endif @@ -277,26 +282,6 @@ out: return err; } -static bool tipc_udp_is_known_peer(struct tipc_bearer *b, - struct udp_media_addr *addr) -{ - struct udp_replicast *rcast, *tmp; - struct udp_bearer *ub; - - ub = rcu_dereference_rtnl(b->media_ptr); - if (!ub) { - pr_err_ratelimited("UDP bearer instance not found\n"); - return false; - } - - list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { - if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr))) - return true; - } - - return false; -} - static int tipc_udp_rcast_add(struct tipc_bearer *b, struct udp_media_addr *addr) { @@ -307,16 +292,34 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, if (!ub) return -ENODEV; + spin_lock_bh(&ub->rcast_lock); + if (ub->disabled) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + list_for_each_entry(rcast, &ub->rcast.list, list) { + if (!memcmp(&rcast->addr, addr, sizeof(*addr))) { + spin_unlock_bh(&ub->rcast_lock); + return 0; + } + } + rcast = kmalloc_obj(*rcast, GFP_ATOMIC); - if (!rcast) + if (!rcast) { + spin_unlock_bh(&ub->rcast_lock); return -ENOMEM; + } if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) { + spin_unlock_bh(&ub->rcast_lock); kfree(rcast); return -ENOMEM; } memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr)); + list_add_rcu(&rcast->list, &ub->rcast.list); + b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; + spin_unlock_bh(&ub->rcast_lock); if (ntohs(addr->proto) == ETH_P_IP) pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4); @@ -324,8 +327,6 @@ static int tipc_udp_rcast_add(struct tipc_bearer *b, else if (ntohs(addr->proto) == ETH_P_IPV6) pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6); #endif - b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT; - list_add_rcu(&rcast->list, &ub->rcast.list); return 0; } @@ -360,9 +361,6 @@ static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb) return 0; } - if (likely(tipc_udp_is_known_peer(b, &src))) - return 0; - return tipc_udp_rcast_add(b, &src); } @@ -405,9 +403,9 @@ out: static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) { - int err = 0; + struct sock *sk = ub->sk; struct ip_mreqn mreqn; - struct sock *sk = ub->ubsock->sk; + int err = 0; if (ntohs(remote->proto) == ETH_P_IP) { mreqn.imr_multiaddr = remote->ipv4; @@ -643,9 +641,6 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr) return -EINVAL; } - if (tipc_udp_is_known_peer(b, &addr)) - return 0; - return tipc_udp_rcast_add(b, &addr); } @@ -670,6 +665,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; u8 node_id[NODE_ID_LEN] = {0,}; struct net_device *dev; + struct socket *sock; int rmcast = 0; ub = kzalloc_obj(*ub, GFP_ATOMIC); @@ -677,6 +673,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, return -ENOMEM; INIT_LIST_HEAD(&ub->rcast.list); + spin_lock_init(&ub->rcast_lock); if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) goto err; @@ -764,14 +761,16 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, goto err; } udp_conf.local_udp_port = local.port; - err = udp_sock_create(net, &udp_conf, &ub->ubsock); + err = udp_sock_create(net, &udp_conf, &sock); if (err) goto err; + + ub->sk = sock->sk; tuncfg.sk_user_data = ub; tuncfg.encap_type = 1; tuncfg.encap_rcv = tipc_udp_recv; tuncfg.encap_destroy = NULL; - setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg); + setup_udp_tunnel_sock(net, ub->sk, &tuncfg); err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC); if (err) @@ -793,12 +792,21 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, free: dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->sk); + synchronize_rcu(); err: kfree(ub); return err; } +static void rcast_free_rcu(struct rcu_head *rcu) +{ + struct udp_replicast *rcast = container_of(rcu, struct udp_replicast, rcu); + + dst_cache_destroy(&rcast->dst_cache); + kfree(rcast); +} + /* cleanup_bearer - break the socket/bearer association */ static void cleanup_bearer(struct work_struct *work) { @@ -806,20 +814,23 @@ static void cleanup_bearer(struct work_struct *work) struct udp_replicast *rcast, *tmp; struct tipc_net *tn; + spin_lock_bh(&ub->rcast_lock); list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) { - dst_cache_destroy(&rcast->dst_cache); list_del_rcu(&rcast->list); - kfree_rcu(rcast, rcu); + call_rcu_hurry(&rcast->rcu, rcast_free_rcu); } + ub->disabled = true; + spin_unlock_bh(&ub->rcast_lock); - tn = tipc_net(sock_net(ub->ubsock->sk)); + tn = tipc_net(sock_net(ub->sk)); - dst_cache_destroy(&ub->rcast.dst_cache); - udp_tunnel_sock_release(ub->ubsock); + udp_tunnel_sock_release(ub->sk); - /* Note: could use a call_rcu() to avoid another synchronize_net() */ synchronize_net(); - atomic_dec(&tn->wq_count); + + dst_cache_destroy(&ub->rcast.dst_cache); + if (atomic_dec_and_test(&tn->wq_count)) + wake_up_var(&tn->wq_count); kfree(ub); } @@ -833,11 +844,11 @@ static void tipc_udp_disable(struct tipc_bearer *b) pr_err("UDP bearer instance not found\n"); return; } - sock_set_flag(ub->ubsock->sk, SOCK_DEAD); + sock_set_flag(ub->sk, SOCK_DEAD); RCU_INIT_POINTER(ub->bearer, NULL); /* sock_release need to be done outside of rtnl lock */ - atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count); + atomic_inc(&tipc_net(sock_net(ub->sk))->wq_count); INIT_WORK(&ub->work, cleanup_bearer); schedule_work(&ub->work); } |
