// SPDX-License-Identifier: GPL-2.0
/*
* Management Component Transport Protocol (MCTP) - routing
* implementation.
*
* This is currently based on a simple routing table, with no dst cache. The
* number of routes should stay fairly small, so the lookup cost is small.
*
* Copyright (c) 2021 Code Construct
* Copyright (c) 2021 Google
*/
#include <linux/idr.h>
#include <linux/kconfig.h>
#include <linux/mctp.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <uapi/linux/if_arp.h>
#include <net/mctp.h>
#include <net/mctpdevice.h>
#include <net/netlink.h>
#include <net/sock.h>
#include <trace/events/mctp.h>
static const unsigned int mctp_message_maxlen = 64 * 1024;
static const unsigned long mctp_key_lifetime = 6 * CONFIG_HZ;
static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev);
/* route output callbacks */
static int mctp_route_discard(struct mctp_route *route, struct sk_buff *skb)
{
kfree_skb(skb);
return 0;
}
static struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb)
{
struct mctp_skb_cb *cb = mctp_cb(skb);
struct mctp_hdr *mh;
struct sock *sk;
u8 type;
WARN_ON(!rcu_read_lock_held());
/* TODO: look up in skb->cb? */
mh = mctp_hdr(skb);
if (!skb_headlen(skb))
return NULL;
type = (*(u8 *)skb->data) & 0x7f;
sk_for_each_rcu(sk, &net->mctp.binds) {
struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
if (msk->bind_net != MCTP_NET_ANY && msk->bind_net != cb->net)
continue;
if (msk->bind_type != type)
continue;
if (!mctp_address_matches(msk->bind_addr, mh->dest))
continue;
return msk;
}
return NULL;
}
/* A note on the key allocations.
*
* struct net->mctp.keys contains our set of currently-allocated keys for
* MCTP tag management. The lookup tuple for these is the peer EID,
* local EID and MCTP tag.
*
* In some cases, the peer EID may be MCTP_EID_ANY: for example, when a
* broadcast message is sent, we may receive responses from any peer EID.
* Because the broadcast dest address is equivalent to ANY, we create
* a key with (local = local-eid, peer = ANY). This allows a match on the
* incoming broadcast responses from any peer.
*
* We perform lookups when packets are received, and when tags are allocated
* in two scenarios:
*
* - when a packet is sent, with a locally-owned tag: we need to find an
* unused tag value for the (local, peer) EID pair.
*
* - when a tag is manually allocated: we need to find an unused tag value
* for the peer EID, but don't have a specific local EID at that stage.
*
* in the latter case, on successful allocation, we end up with a tag with
* (local = ANY, peer = peer-eid).
*
* So, the key set allows both a local EID of ANY, as well as a peer EID of
* ANY in the lookup tuple. Both may be ANY if we prealloc for a broadcast.
* The matching (in mctp_key_match()) during lookup allows the match value to
* be ANY in either the dest or source addresses.
*
* When allocating (+ inserting) a tag, we need to check for conflicts amongst
* the existing tag set. This requires macthing either exactly on the local
* and peer addresses, or either being ANY.
*/
static bool mctp_key_match(struct mctp_sk_key *key, unsigned int net,
mctp_eid_t local, mctp_eid_t peer, u8 tag)
{
if (key->net != net)
return false;
if (!mctp_address_matches(key->local_addr, local))
return false;
if (!mctp_address_matches(key->peer_addr, peer))
return false;
if (key->tag != tag)
return false;
return true;
}
/* returns a key (with key->lock held, and refcounted), or NULL if no such
* key exists.
*/
static struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb,
unsigned int netid, mctp_eid_t peer,
unsigned long *irqflags)
__acquires(&key->lock)
{
struct mctp_sk_key *key, *ret;
unsigned long flags;
struct mctp_hdr *mh;
u8 tag;
mh = mctp_hdr(skb);
tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
ret = NULL;
spin_lock_irqsave(&net->mctp.keys_lock, flags);
hlist_for_each_entry(key, &net->mctp.keys, hlist) {
if (!mctp_key_match(key, netid, mh->dest, peer, tag))
continue;
spin_lock(&key->lock);
if (key->valid) {
refcount_inc(&key->refs);
ret = key;
break;
}
spin_unlock(&key->lock);
}
if (ret) {
spin_unlock(&net->mctp.keys_lock);
*irqflags = flags;
} else {
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
}
return ret;
}
static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
unsigned int net,
mctp_eid_t local, mctp_eid_t peer,
u8 tag, gfp_t gfp)
{
struct mctp_sk_key *key;
key = kzalloc(sizeof(*key), gfp);
if (!key)
return NULL;
key->net = net;
key->peer_addr = peer;
key->local_addr = local;
key->tag = tag;
key->sk = &msk->sk;
key->valid = true;
spin_lock_init(&key->lock);
refcount_set(&key->refs, 1);
sock_hold(key->sk);
return key;
}
void mctp_key_unref(struct mctp_sk_key *key)
{
unsigned long flags;
if (!refcount_dec_and_test(&key->refs))
return;
/* even though no refs exist here, the lock allows us to stay
* consistent with the locking requirement of mctp_dev_release_key
*/
spin_lock_irqsave(&key->lock, flags);
mctp_dev_release_key(key->dev, key);
spin_unlock_irqrestore(&key->lock, flags);
sock_put(key->sk);
kfree(key);
}
static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
{
struct net *net = sock_net(&msk->sk);
struct mctp_sk_key *tmp;
unsigned long flags;
int rc = 0;
spin_lock_irqsave(&net->mctp.keys_lock, flags);
if (sock_flag(&msk->sk, SOCK_DEAD)) {
rc = -EINVAL;
goto out_unlock;
}
hlist_for_each_entry(tmp, &net->mctp.keys, hlist) {
if (mctp_key_match(tmp, key->net, key->local_addr,
key->peer_addr, key->tag)) {
spin_lock(&tmp->lock);
if (tmp->valid)
rc = -EEXIST;
spin_unlock(&tmp->lock);
if (rc)
break;
}
}
if (!rc) {
refcount_inc(&key->refs);
key->expiry = jiffies + mctp_key_lifetime;
timer_reduce(&msk->key_expiry, key->expiry);
hlist_add_head(&key->hlist, &net->mctp.keys);
hlist_add_head(&key->sklist, &msk->keys);
}
out_unlock:
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
return rc;
}
/* Helper for mctp_route_input().
* We're done with the key; unlock and unref the key.
* For the usual case of automatic expiry we remove the key from lists.
* In the case that manual allocation is set on a key we release the lock
* and local ref, reset reassembly, but don't remove from lists.
*/
static void __mctp_key_done_in(struct mctp_sk_key *key, struct net *net,
unsigned long flags, unsigned long reason)
__releases(&key->lock)
{
struct sk_buff *skb;
trace_mctp_key_release(key, reason);
skb = key->reasm_head;
key->reasm_head = NULL;
if (!key->manual_alloc) {
key->reasm_dead = true;
key->valid = false;
mctp_dev_release_key(key->dev, key);
}
spin_unlock_irqrestore(&key->lock, flags);
if (!key->manual_alloc) {
spin_lock_irqsave(&net->mctp.keys_lock, flags);
if (!hlist_unhashed(&key->hlist)) {
hlist_del_init(&key->hlist);
hlist_del_init(&key->sklist);
mctp_key_unref(key);
}
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
}
/* and one for the local reference */
mctp_key_unref(key);
kfree_skb(skb);
}
#ifdef CONFIG_MCTP_FLOWS
static void mct
|