162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Management Component Transport Protocol (MCTP) - routing 462306a36Sopenharmony_ci * implementation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This is currently based on a simple routing table, with no dst cache. The 762306a36Sopenharmony_ci * number of routes should stay fairly small, so the lookup cost is small. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2021 Code Construct 1062306a36Sopenharmony_ci * Copyright (c) 2021 Google 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/idr.h> 1462306a36Sopenharmony_ci#include <linux/kconfig.h> 1562306a36Sopenharmony_ci#include <linux/mctp.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <uapi/linux/if_arp.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <net/mctp.h> 2362306a36Sopenharmony_ci#include <net/mctpdevice.h> 2462306a36Sopenharmony_ci#include <net/netlink.h> 2562306a36Sopenharmony_ci#include <net/sock.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <trace/events/mctp.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const unsigned int mctp_message_maxlen = 64 * 1024; 3062306a36Sopenharmony_cistatic const unsigned long mctp_key_lifetime = 6 * CONFIG_HZ; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* route output callbacks */ 3562306a36Sopenharmony_cistatic int mctp_route_discard(struct mctp_route *route, struct sk_buff *skb) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci kfree_skb(skb); 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct mctp_skb_cb *cb = mctp_cb(skb); 4462306a36Sopenharmony_ci struct mctp_hdr *mh; 4562306a36Sopenharmony_ci struct sock *sk; 4662306a36Sopenharmony_ci u8 type; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci WARN_ON(!rcu_read_lock_held()); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* TODO: look up in skb->cb? */ 5162306a36Sopenharmony_ci mh = mctp_hdr(skb); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!skb_headlen(skb)) 5462306a36Sopenharmony_ci return NULL; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci type = (*(u8 *)skb->data) & 0x7f; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci sk_for_each_rcu(sk, &net->mctp.binds) { 5962306a36Sopenharmony_ci struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (msk->bind_net != MCTP_NET_ANY && msk->bind_net != cb->net) 6262306a36Sopenharmony_ci continue; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (msk->bind_type != type) 6562306a36Sopenharmony_ci continue; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!mctp_address_matches(msk->bind_addr, mh->dest)) 6862306a36Sopenharmony_ci continue; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return msk; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return NULL; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic bool mctp_key_match(struct mctp_sk_key *key, mctp_eid_t local, 7762306a36Sopenharmony_ci mctp_eid_t peer, u8 tag) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci if (!mctp_address_matches(key->local_addr, local)) 8062306a36Sopenharmony_ci return false; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (key->peer_addr != peer) 8362306a36Sopenharmony_ci return false; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (key->tag != tag) 8662306a36Sopenharmony_ci return false; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return true; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* returns a key (with key->lock held, and refcounted), or NULL if no such 9262306a36Sopenharmony_ci * key exists. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb, 9562306a36Sopenharmony_ci mctp_eid_t peer, 9662306a36Sopenharmony_ci unsigned long *irqflags) 9762306a36Sopenharmony_ci __acquires(&key->lock) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct mctp_sk_key *key, *ret; 10062306a36Sopenharmony_ci unsigned long flags; 10162306a36Sopenharmony_ci struct mctp_hdr *mh; 10262306a36Sopenharmony_ci u8 tag; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci mh = mctp_hdr(skb); 10562306a36Sopenharmony_ci tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci ret = NULL; 10862306a36Sopenharmony_ci spin_lock_irqsave(&net->mctp.keys_lock, flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci hlist_for_each_entry(key, &net->mctp.keys, hlist) { 11162306a36Sopenharmony_ci if (!mctp_key_match(key, mh->dest, peer, tag)) 11262306a36Sopenharmony_ci continue; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci spin_lock(&key->lock); 11562306a36Sopenharmony_ci if (key->valid) { 11662306a36Sopenharmony_ci refcount_inc(&key->refs); 11762306a36Sopenharmony_ci ret = key; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci spin_unlock(&key->lock); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (ret) { 12462306a36Sopenharmony_ci spin_unlock(&net->mctp.keys_lock); 12562306a36Sopenharmony_ci *irqflags = flags; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci spin_unlock_irqrestore(&net->mctp.keys_lock, flags); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk, 13462306a36Sopenharmony_ci mctp_eid_t local, mctp_eid_t peer, 13562306a36Sopenharmony_ci u8 tag, gfp_t gfp) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct mctp_sk_key *key; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci key = kzalloc(sizeof(*key), gfp); 14062306a36Sopenharmony_ci if (!key) 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci key->peer_addr = peer; 14462306a36Sopenharmony_ci key->local_addr = local; 14562306a36Sopenharmony_ci key->tag = tag; 14662306a36Sopenharmony_ci key->sk = &msk->sk; 14762306a36Sopenharmony_ci key->valid = true; 14862306a36Sopenharmony_ci spin_lock_init(&key->lock); 14962306a36Sopenharmony_ci refcount_set(&key->refs, 1); 15062306a36Sopenharmony_ci sock_hold(key->sk); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return key; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_civoid mctp_key_unref(struct mctp_sk_key *key) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci unsigned long flags; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!refcount_dec_and_test(&key->refs)) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* even though no refs exist here, the lock allows us to stay 16362306a36Sopenharmony_ci * consistent with the locking requirement of mctp_dev_release_key 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci spin_lock_irqsave(&key->lock, flags); 16662306a36Sopenharmony_ci mctp_dev_release_key(key->dev, key); 16762306a36Sopenharmony_ci spin_unlock_irqrestore(&key->lock, flags); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci sock_put(key->sk); 17062306a36Sopenharmony_ci kfree(key); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct net *net = sock_net(&msk->sk); 17662306a36Sopenharmony_ci struct mctp_sk_key *tmp; 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci int rc = 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_lock_irqsave(&net->mctp.keys_lock, flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (sock_flag(&msk->sk, SOCK_DEAD)) { 18362306a36Sopenharmony_ci rc = -EINVAL; 18462306a36Sopenharmony_ci goto out_unlock; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci hlist_for_each_entry(tmp, &net->mctp.keys, hlist) { 18862306a36Sopenharmony_ci if (mctp_key_match(tmp, key->local_addr, key->peer_addr, 18962306a36Sopenharmony_ci key->tag)) { 19062306a36Sopenharmony_ci spin_lock(&tmp->lock); 19162306a36Sopenharmony_ci if (tmp->valid) 19262306a36Sopenharmony_ci rc = -EEXIST; 19362306a36Sopenharmony_ci spin_unlock(&tmp->lock); 19462306a36Sopenharmony_ci if (rc) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!rc) { 20062306a36Sopenharmony_ci refcount_inc(&key->refs); 20162306a36Sopenharmony_ci key->expiry = jiffies + mctp_key_lifetime; 20262306a36Sopenharmony_ci timer_reduce(&msk->key_expiry, key->expiry); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci hlist_add_head(&key->hlist, &net->mctp.keys); 20562306a36Sopenharmony_ci hlist_add_head(&key->sklist, &msk->keys); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout_unlock: 20962306a36Sopenharmony_ci spin_unlock_irqrestore(&net->mctp.keys_lock, flags); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return rc; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* Helper for mctp_route_input(). 21562306a36Sopenharmony_ci * We're done with the key; unlock and unref the key. 21662306a36Sopenharmony_ci * For the usual case of automatic expiry we remove the key from lists. 21762306a36Sopenharmony_ci * In the case that manual allocation is set on a key we release the lock 21862306a36Sopenharmony_ci * and local ref, reset reassembly, but don't remove from lists. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic void __mctp_key_done_in(struct mctp_sk_key *key, struct net *net, 22162306a36Sopenharmony_ci unsigned long flags, unsigned long reason) 22262306a36Sopenharmony_ci__releases(&key->lock) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct sk_buff *skb; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci trace_mctp_key_release(key, reason); 22762306a36Sopenharmony_ci skb = key->reasm_head; 22862306a36Sopenharmony_ci key->reasm_head = NULL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (!key->manual_alloc) { 23162306a36Sopenharmony_ci key->reasm_dead = true; 23262306a36Sopenharmony_ci key->valid = false; 23362306a36Sopenharmony_ci mctp_dev_release_key(key->dev, key); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci spin_unlock_irqrestore(&key->lock, flags); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!key->manual_alloc) { 23862306a36Sopenharmony_ci spin_lock_irqsave(&net->mctp.keys_lock, flags); 23962306a36Sopenharmony_ci if (!hlist_unhashed(&key->hlist)) { 24062306a36Sopenharmony_ci hlist_del_init(&key->hlist); 24162306a36Sopenharmony_ci hlist_del_init(&key->sklist); 24262306a36Sopenharmony_ci mctp_key_unref(key); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci spin_unlock_irqrestore(&net->mctp.keys_lock, flags); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* and one for the local reference */ 24862306a36Sopenharmony_ci mctp_key_unref(key); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci kfree_skb(skb); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#ifdef CONFIG_MCTP_FLOWS 25462306a36Sopenharmony_cistatic void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct mctp_flow *flow; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci flow = skb_ext_add(skb, SKB_EXT_MCTP); 25962306a36Sopenharmony_ci if (!flow) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci refcount_inc(&key->refs); 26362306a36Sopenharmony_ci flow->key = key; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct mctp_sk_key *key; 26962306a36Sopenharmony_ci struct mctp_flow *flow; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci flow = skb_ext_find(skb, SKB_EXT_MCTP); 27262306a36Sopenharmony_ci if (!flow) 27362306a36Sopenharmony_ci return; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci key = flow->key; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (WARN_ON(key->dev && key->dev != dev)) 27862306a36Sopenharmony_ci return; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mctp_dev_set_key(dev, key); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci#else 28362306a36Sopenharmony_cistatic void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key) {} 28462306a36Sopenharmony_cistatic void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev) {} 28562306a36Sopenharmony_ci#endif 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct mctp_hdr *hdr = mctp_hdr(skb); 29062306a36Sopenharmony_ci u8 exp_seq, this_seq; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci this_seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) 29362306a36Sopenharmony_ci & MCTP_HDR_SEQ_MASK; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!key->reasm_head) { 29662306a36Sopenharmony_ci key->reasm_head = skb; 29762306a36Sopenharmony_ci key->reasm_tailp = &(skb_shinfo(skb)->frag_list); 29862306a36Sopenharmony_ci key->last_seq = this_seq; 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci exp_seq = (key->last_seq + 1) & MCTP_HDR_SEQ_MASK; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (this_seq != exp_seq) 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (key->reasm_head->len + skb->len > mctp_message_maxlen) 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci skb->next = NULL; 31162306a36Sopenharmony_ci skb->sk = NULL; 31262306a36Sopenharmony_ci *key->reasm_tailp = skb; 31362306a36Sopenharmony_ci key->reasm_tailp = &skb->next; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci key->last_seq = this_seq; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci key->reasm_head->data_len += skb->len; 31862306a36Sopenharmony_ci key->reasm_head->len += skb->len; 31962306a36Sopenharmony_ci key->reasm_head->truesize += skb->truesize; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int mctp_route_input(struct mctp_route *route, struct sk_buff *skb) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct mctp_sk_key *key, *any_key = NULL; 32762306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 32862306a36Sopenharmony_ci struct mctp_sock *msk; 32962306a36Sopenharmony_ci struct mctp_hdr *mh; 33062306a36Sopenharmony_ci unsigned long f; 33162306a36Sopenharmony_ci u8 tag, flags; 33262306a36Sopenharmony_ci int rc; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci msk = NULL; 33562306a36Sopenharmony_ci rc = -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* we may be receiving a locally-routed packet; drop source sk 33862306a36Sopenharmony_ci * accounting 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci skb_orphan(skb); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* ensure we have enough data for a header and a type */ 34362306a36Sopenharmony_ci if (skb->len < sizeof(struct mctp_hdr) + 1) 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* grab header, advance data ptr */ 34762306a36Sopenharmony_ci mh = mctp_hdr(skb); 34862306a36Sopenharmony_ci skb_pull(skb, sizeof(struct mctp_hdr)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (mh->ver != 1) 35162306a36Sopenharmony_ci goto out; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM); 35462306a36Sopenharmony_ci tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci rcu_read_lock(); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* lookup socket / reasm context, exactly matching (src,dest,tag). 35962306a36Sopenharmony_ci * we hold a ref on the key, and key->lock held. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci key = mctp_lookup_key(net, skb, mh->src, &f); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (flags & MCTP_HDR_FLAG_SOM) { 36462306a36Sopenharmony_ci if (key) { 36562306a36Sopenharmony_ci msk = container_of(key->sk, struct mctp_sock, sk); 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci /* first response to a broadcast? do a more general 36862306a36Sopenharmony_ci * key lookup to find the socket, but don't use this 36962306a36Sopenharmony_ci * key for reassembly - we'll create a more specific 37062306a36Sopenharmony_ci * one for future packets if required (ie, !EOM). 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci any_key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f); 37362306a36Sopenharmony_ci if (any_key) { 37462306a36Sopenharmony_ci msk = container_of(any_key->sk, 37562306a36Sopenharmony_ci struct mctp_sock, sk); 37662306a36Sopenharmony_ci spin_unlock_irqrestore(&any_key->lock, f); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!key && !msk && (tag & MCTP_HDR_FLAG_TO)) 38162306a36Sopenharmony_ci msk = mctp_lookup_bind(net, skb); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!msk) { 38462306a36Sopenharmony_ci rc = -ENOENT; 38562306a36Sopenharmony_ci goto out_unlock; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* single-packet message? deliver to socket, clean up any 38962306a36Sopenharmony_ci * pending key. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci if (flags & MCTP_HDR_FLAG_EOM) { 39262306a36Sopenharmony_ci sock_queue_rcv_skb(&msk->sk, skb); 39362306a36Sopenharmony_ci if (key) { 39462306a36Sopenharmony_ci /* we've hit a pending reassembly; not much we 39562306a36Sopenharmony_ci * can do but drop it 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ci __mctp_key_done_in(key, net, f, 39862306a36Sopenharmony_ci MCTP_TRACE_KEY_REPLIED); 39962306a36Sopenharmony_ci key = NULL; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci rc = 0; 40262306a36Sopenharmony_ci goto out_unlock; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* broadcast response or a bind() - create a key for further 40662306a36Sopenharmony_ci * packets for this message 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci if (!key) { 40962306a36Sopenharmony_ci key = mctp_key_alloc(msk, mh->dest, mh->src, 41062306a36Sopenharmony_ci tag, GFP_ATOMIC); 41162306a36Sopenharmony_ci if (!key) { 41262306a36Sopenharmony_ci rc = -ENOMEM; 41362306a36Sopenharmony_ci goto out_unlock; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* we can queue without the key lock here, as the 41762306a36Sopenharmony_ci * key isn't observable yet 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci mctp_frag_queue(key, skb); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* if the key_add fails, we've raced with another 42262306a36Sopenharmony_ci * SOM packet with the same src, dest and tag. There's 42362306a36Sopenharmony_ci * no way to distinguish future packets, so all we 42462306a36Sopenharmony_ci * can do is drop; we'll free the skb on exit from 42562306a36Sopenharmony_ci * this function. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci rc = mctp_key_add(key, msk); 42862306a36Sopenharmony_ci if (!rc) 42962306a36Sopenharmony_ci trace_mctp_key_acquire(key); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* we don't need to release key->lock on exit, so 43262306a36Sopenharmony_ci * clean up here and suppress the unlock via 43362306a36Sopenharmony_ci * setting to NULL 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci mctp_key_unref(key); 43662306a36Sopenharmony_ci key = NULL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci if (key->reasm_head || key->reasm_dead) { 44062306a36Sopenharmony_ci /* duplicate start? drop everything */ 44162306a36Sopenharmony_ci __mctp_key_done_in(key, net, f, 44262306a36Sopenharmony_ci MCTP_TRACE_KEY_INVALIDATED); 44362306a36Sopenharmony_ci rc = -EEXIST; 44462306a36Sopenharmony_ci key = NULL; 44562306a36Sopenharmony_ci } else { 44662306a36Sopenharmony_ci rc = mctp_frag_queue(key, skb); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci } else if (key) { 45162306a36Sopenharmony_ci /* this packet continues a previous message; reassemble 45262306a36Sopenharmony_ci * using the message-specific key 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* we need to be continuing an existing reassembly... */ 45662306a36Sopenharmony_ci if (!key->reasm_head) 45762306a36Sopenharmony_ci rc = -EINVAL; 45862306a36Sopenharmony_ci else 45962306a36Sopenharmony_ci rc = mctp_frag_queue(key, skb); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* end of message? deliver to socket, and we're done with 46262306a36Sopenharmony_ci * the reassembly/response key 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci if (!rc && flags & MCTP_HDR_FLAG_EOM) { 46562306a36Sopenharmony_ci sock_queue_rcv_skb(key->sk, key->reasm_head); 46662306a36Sopenharmony_ci key->reasm_head = NULL; 46762306a36Sopenharmony_ci __mctp_key_done_in(key, net, f, MCTP_TRACE_KEY_REPLIED); 46862306a36Sopenharmony_ci key = NULL; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci } else { 47262306a36Sopenharmony_ci /* not a start, no matching key */ 47362306a36Sopenharmony_ci rc = -ENOENT; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ciout_unlock: 47762306a36Sopenharmony_ci rcu_read_unlock(); 47862306a36Sopenharmony_ci if (key) { 47962306a36Sopenharmony_ci spin_unlock_irqrestore(&key->lock, f); 48062306a36Sopenharmony_ci mctp_key_unref(key); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci if (any_key) 48362306a36Sopenharmony_ci mctp_key_unref(any_key); 48462306a36Sopenharmony_ciout: 48562306a36Sopenharmony_ci if (rc) 48662306a36Sopenharmony_ci kfree_skb(skb); 48762306a36Sopenharmony_ci return rc; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic unsigned int mctp_route_mtu(struct mctp_route *rt) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci return rt->mtu ?: READ_ONCE(rt->dev->dev->mtu); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int mctp_route_output(struct mctp_route *route, struct sk_buff *skb) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct mctp_skb_cb *cb = mctp_cb(skb); 49862306a36Sopenharmony_ci struct mctp_hdr *hdr = mctp_hdr(skb); 49962306a36Sopenharmony_ci char daddr_buf[MAX_ADDR_LEN]; 50062306a36Sopenharmony_ci char *daddr = NULL; 50162306a36Sopenharmony_ci unsigned int mtu; 50262306a36Sopenharmony_ci int rc; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MCTP); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci mtu = READ_ONCE(skb->dev->mtu); 50762306a36Sopenharmony_ci if (skb->len > mtu) { 50862306a36Sopenharmony_ci kfree_skb(skb); 50962306a36Sopenharmony_ci return -EMSGSIZE; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (cb->ifindex) { 51362306a36Sopenharmony_ci /* direct route; use the hwaddr we stashed in sendmsg */ 51462306a36Sopenharmony_ci if (cb->halen != skb->dev->addr_len) { 51562306a36Sopenharmony_ci /* sanity check, sendmsg should have already caught this */ 51662306a36Sopenharmony_ci kfree_skb(skb); 51762306a36Sopenharmony_ci return -EMSGSIZE; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci daddr = cb->haddr; 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci /* If lookup fails let the device handle daddr==NULL */ 52262306a36Sopenharmony_ci if (mctp_neigh_lookup(route->dev, hdr->dest, daddr_buf) == 0) 52362306a36Sopenharmony_ci daddr = daddr_buf; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci rc = dev_hard_header(skb, skb->dev, ntohs(skb->protocol), 52762306a36Sopenharmony_ci daddr, skb->dev->dev_addr, skb->len); 52862306a36Sopenharmony_ci if (rc < 0) { 52962306a36Sopenharmony_ci kfree_skb(skb); 53062306a36Sopenharmony_ci return -EHOSTUNREACH; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mctp_flow_prepare_output(skb, route->dev); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci rc = dev_queue_xmit(skb); 53662306a36Sopenharmony_ci if (rc) 53762306a36Sopenharmony_ci rc = net_xmit_errno(rc); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return rc; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci/* route alloc/release */ 54362306a36Sopenharmony_cistatic void mctp_route_release(struct mctp_route *rt) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci if (refcount_dec_and_test(&rt->refs)) { 54662306a36Sopenharmony_ci mctp_dev_put(rt->dev); 54762306a36Sopenharmony_ci kfree_rcu(rt, rcu); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* returns a route with the refcount at 1 */ 55262306a36Sopenharmony_cistatic struct mctp_route *mctp_route_alloc(void) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct mctp_route *rt; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci rt = kzalloc(sizeof(*rt), GFP_KERNEL); 55762306a36Sopenharmony_ci if (!rt) 55862306a36Sopenharmony_ci return NULL; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci INIT_LIST_HEAD(&rt->list); 56162306a36Sopenharmony_ci refcount_set(&rt->refs, 1); 56262306a36Sopenharmony_ci rt->output = mctp_route_discard; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return rt; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciunsigned int mctp_default_net(struct net *net) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci return READ_ONCE(net->mctp.default_net); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ciint mctp_default_net_set(struct net *net, unsigned int index) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci if (index == 0) 57562306a36Sopenharmony_ci return -EINVAL; 57662306a36Sopenharmony_ci WRITE_ONCE(net->mctp.default_net, index); 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci/* tag management */ 58162306a36Sopenharmony_cistatic void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key, 58262306a36Sopenharmony_ci struct mctp_sock *msk) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct netns_mctp *mns = &net->mctp; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci lockdep_assert_held(&mns->keys_lock); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci key->expiry = jiffies + mctp_key_lifetime; 58962306a36Sopenharmony_ci timer_reduce(&msk->key_expiry, key->expiry); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* we hold the net->key_lock here, allowing updates to both 59262306a36Sopenharmony_ci * then net and sk 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci hlist_add_head_rcu(&key->hlist, &mns->keys); 59562306a36Sopenharmony_ci hlist_add_head_rcu(&key->sklist, &msk->keys); 59662306a36Sopenharmony_ci refcount_inc(&key->refs); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* Allocate a locally-owned tag value for (saddr, daddr), and reserve 60062306a36Sopenharmony_ci * it for the socket msk 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_cistruct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk, 60362306a36Sopenharmony_ci mctp_eid_t daddr, mctp_eid_t saddr, 60462306a36Sopenharmony_ci bool manual, u8 *tagp) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct net *net = sock_net(&msk->sk); 60762306a36Sopenharmony_ci struct netns_mctp *mns = &net->mctp; 60862306a36Sopenharmony_ci struct mctp_sk_key *key, *tmp; 60962306a36Sopenharmony_ci unsigned long flags; 61062306a36Sopenharmony_ci u8 tagbits; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* for NULL destination EIDs, we may get a response from any peer */ 61362306a36Sopenharmony_ci if (daddr == MCTP_ADDR_NULL) 61462306a36Sopenharmony_ci daddr = MCTP_ADDR_ANY; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* be optimistic, alloc now */ 61762306a36Sopenharmony_ci key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL); 61862306a36Sopenharmony_ci if (!key) 61962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 8 possible tag values */ 62262306a36Sopenharmony_ci tagbits = 0xff; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci spin_lock_irqsave(&mns->keys_lock, flags); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Walk through the existing keys, looking for potential conflicting 62762306a36Sopenharmony_ci * tags. If we find a conflict, clear that bit from tagbits 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci hlist_for_each_entry(tmp, &mns->keys, hlist) { 63062306a36Sopenharmony_ci /* We can check the lookup fields (*_addr, tag) without the 63162306a36Sopenharmony_ci * lock held, they don't change over the lifetime of the key. 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* if we don't own the tag, it can't conflict */ 63562306a36Sopenharmony_ci if (tmp->tag & MCTP_HDR_FLAG_TO) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (!(mctp_address_matches(tmp->peer_addr, daddr) && 63962306a36Sopenharmony_ci mctp_address_matches(tmp->local_addr, saddr))) 64062306a36Sopenharmony_ci continue; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci spin_lock(&tmp->lock); 64362306a36Sopenharmony_ci /* key must still be valid. If we find a match, clear the 64462306a36Sopenharmony_ci * potential tag value 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ci if (tmp->valid) 64762306a36Sopenharmony_ci tagbits &= ~(1 << tmp->tag); 64862306a36Sopenharmony_ci spin_unlock(&tmp->lock); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!tagbits) 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (tagbits) { 65562306a36Sopenharmony_ci key->tag = __ffs(tagbits); 65662306a36Sopenharmony_ci mctp_reserve_tag(net, key, msk); 65762306a36Sopenharmony_ci trace_mctp_key_acquire(key); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci key->manual_alloc = manual; 66062306a36Sopenharmony_ci *tagp = key->tag; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci spin_unlock_irqrestore(&mns->keys_lock, flags); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (!tagbits) { 66662306a36Sopenharmony_ci mctp_key_unref(key); 66762306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return key; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk, 67462306a36Sopenharmony_ci mctp_eid_t daddr, 67562306a36Sopenharmony_ci u8 req_tag, u8 *tagp) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct net *net = sock_net(&msk->sk); 67862306a36Sopenharmony_ci struct netns_mctp *mns = &net->mctp; 67962306a36Sopenharmony_ci struct mctp_sk_key *key, *tmp; 68062306a36Sopenharmony_ci unsigned long flags; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci req_tag &= ~(MCTP_TAG_PREALLOC | MCTP_TAG_OWNER); 68362306a36Sopenharmony_ci key = NULL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci spin_lock_irqsave(&mns->keys_lock, flags); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci hlist_for_each_entry(tmp, &mns->keys, hlist) { 68862306a36Sopenharmony_ci if (tmp->tag != req_tag) 68962306a36Sopenharmony_ci continue; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!mctp_address_matches(tmp->peer_addr, daddr)) 69262306a36Sopenharmony_ci continue; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (!tmp->manual_alloc) 69562306a36Sopenharmony_ci continue; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_lock(&tmp->lock); 69862306a36Sopenharmony_ci if (tmp->valid) { 69962306a36Sopenharmony_ci key = tmp; 70062306a36Sopenharmony_ci refcount_inc(&key->refs); 70162306a36Sopenharmony_ci spin_unlock(&tmp->lock); 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci spin_unlock(&tmp->lock); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci spin_unlock_irqrestore(&mns->keys_lock, flags); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (!key) 70962306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (tagp) 71262306a36Sopenharmony_ci *tagp = key->tag; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return key; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* routing lookups */ 71862306a36Sopenharmony_cistatic bool mctp_rt_match_eid(struct mctp_route *rt, 71962306a36Sopenharmony_ci unsigned int net, mctp_eid_t eid) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci return READ_ONCE(rt->dev->net) == net && 72262306a36Sopenharmony_ci rt->min <= eid && rt->max >= eid; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* compares match, used for duplicate prevention */ 72662306a36Sopenharmony_cistatic bool mctp_rt_compare_exact(struct mctp_route *rt1, 72762306a36Sopenharmony_ci struct mctp_route *rt2) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci ASSERT_RTNL(); 73062306a36Sopenharmony_ci return rt1->dev->net == rt2->dev->net && 73162306a36Sopenharmony_ci rt1->min == rt2->min && 73262306a36Sopenharmony_ci rt1->max == rt2->max; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistruct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet, 73662306a36Sopenharmony_ci mctp_eid_t daddr) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct mctp_route *tmp, *rt = NULL; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci rcu_read_lock(); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci list_for_each_entry_rcu(tmp, &net->mctp.routes, list) { 74362306a36Sopenharmony_ci /* TODO: add metrics */ 74462306a36Sopenharmony_ci if (mctp_rt_match_eid(tmp, dnet, daddr)) { 74562306a36Sopenharmony_ci if (refcount_inc_not_zero(&tmp->refs)) { 74662306a36Sopenharmony_ci rt = tmp; 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci rcu_read_unlock(); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return rt; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic struct mctp_route *mctp_route_lookup_null(struct net *net, 75862306a36Sopenharmony_ci struct net_device *dev) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci struct mctp_route *tmp, *rt = NULL; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci rcu_read_lock(); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci list_for_each_entry_rcu(tmp, &net->mctp.routes, list) { 76562306a36Sopenharmony_ci if (tmp->dev->dev == dev && tmp->type == RTN_LOCAL && 76662306a36Sopenharmony_ci refcount_inc_not_zero(&tmp->refs)) { 76762306a36Sopenharmony_ci rt = tmp; 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci rcu_read_unlock(); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return rt; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb, 77862306a36Sopenharmony_ci unsigned int mtu, u8 tag) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci const unsigned int hlen = sizeof(struct mctp_hdr); 78162306a36Sopenharmony_ci struct mctp_hdr *hdr, *hdr2; 78262306a36Sopenharmony_ci unsigned int pos, size, headroom; 78362306a36Sopenharmony_ci struct sk_buff *skb2; 78462306a36Sopenharmony_ci int rc; 78562306a36Sopenharmony_ci u8 seq; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci hdr = mctp_hdr(skb); 78862306a36Sopenharmony_ci seq = 0; 78962306a36Sopenharmony_ci rc = 0; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (mtu < hlen + 1) { 79262306a36Sopenharmony_ci kfree_skb(skb); 79362306a36Sopenharmony_ci return -EMSGSIZE; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* keep same headroom as the original skb */ 79762306a36Sopenharmony_ci headroom = skb_headroom(skb); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* we've got the header */ 80062306a36Sopenharmony_ci skb_pull(skb, hlen); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci for (pos = 0; pos < skb->len;) { 80362306a36Sopenharmony_ci /* size of message payload */ 80462306a36Sopenharmony_ci size = min(mtu - hlen, skb->len - pos); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci skb2 = alloc_skb(headroom + hlen + size, GFP_KERNEL); 80762306a36Sopenharmony_ci if (!skb2) { 80862306a36Sopenharmony_ci rc = -ENOMEM; 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* generic skb copy */ 81362306a36Sopenharmony_ci skb2->protocol = skb->protocol; 81462306a36Sopenharmony_ci skb2->priority = skb->priority; 81562306a36Sopenharmony_ci skb2->dev = skb->dev; 81662306a36Sopenharmony_ci memcpy(skb2->cb, skb->cb, sizeof(skb2->cb)); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (skb->sk) 81962306a36Sopenharmony_ci skb_set_owner_w(skb2, skb->sk); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* establish packet */ 82262306a36Sopenharmony_ci skb_reserve(skb2, headroom); 82362306a36Sopenharmony_ci skb_reset_network_header(skb2); 82462306a36Sopenharmony_ci skb_put(skb2, hlen + size); 82562306a36Sopenharmony_ci skb2->transport_header = skb2->network_header + hlen; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* copy header fields, calculate SOM/EOM flags & seq */ 82862306a36Sopenharmony_ci hdr2 = mctp_hdr(skb2); 82962306a36Sopenharmony_ci hdr2->ver = hdr->ver; 83062306a36Sopenharmony_ci hdr2->dest = hdr->dest; 83162306a36Sopenharmony_ci hdr2->src = hdr->src; 83262306a36Sopenharmony_ci hdr2->flags_seq_tag = tag & 83362306a36Sopenharmony_ci (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (pos == 0) 83662306a36Sopenharmony_ci hdr2->flags_seq_tag |= MCTP_HDR_FLAG_SOM; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (pos + size == skb->len) 83962306a36Sopenharmony_ci hdr2->flags_seq_tag |= MCTP_HDR_FLAG_EOM; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci hdr2->flags_seq_tag |= seq << MCTP_HDR_SEQ_SHIFT; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* copy message payload */ 84462306a36Sopenharmony_ci skb_copy_bits(skb, pos, skb_transport_header(skb2), size); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* we need to copy the extensions, for MCTP flow data */ 84762306a36Sopenharmony_ci skb_ext_copy(skb2, skb); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* do route */ 85062306a36Sopenharmony_ci rc = rt->output(rt, skb2); 85162306a36Sopenharmony_ci if (rc) 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci seq = (seq + 1) & MCTP_HDR_SEQ_MASK; 85562306a36Sopenharmony_ci pos += size; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci consume_skb(skb); 85962306a36Sopenharmony_ci return rc; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ciint mctp_local_output(struct sock *sk, struct mctp_route *rt, 86362306a36Sopenharmony_ci struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); 86662306a36Sopenharmony_ci struct mctp_skb_cb *cb = mctp_cb(skb); 86762306a36Sopenharmony_ci struct mctp_route tmp_rt = {0}; 86862306a36Sopenharmony_ci struct mctp_sk_key *key; 86962306a36Sopenharmony_ci struct mctp_hdr *hdr; 87062306a36Sopenharmony_ci unsigned long flags; 87162306a36Sopenharmony_ci unsigned int mtu; 87262306a36Sopenharmony_ci mctp_eid_t saddr; 87362306a36Sopenharmony_ci bool ext_rt; 87462306a36Sopenharmony_ci int rc; 87562306a36Sopenharmony_ci u8 tag; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci rc = -ENODEV; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (rt) { 88062306a36Sopenharmony_ci ext_rt = false; 88162306a36Sopenharmony_ci if (WARN_ON(!rt->dev)) 88262306a36Sopenharmony_ci goto out_release; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci } else if (cb->ifindex) { 88562306a36Sopenharmony_ci struct net_device *dev; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci ext_rt = true; 88862306a36Sopenharmony_ci rt = &tmp_rt; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci rcu_read_lock(); 89162306a36Sopenharmony_ci dev = dev_get_by_index_rcu(sock_net(sk), cb->ifindex); 89262306a36Sopenharmony_ci if (!dev) { 89362306a36Sopenharmony_ci rcu_read_unlock(); 89462306a36Sopenharmony_ci goto out_free; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci rt->dev = __mctp_dev_get(dev); 89762306a36Sopenharmony_ci rcu_read_unlock(); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (!rt->dev) 90062306a36Sopenharmony_ci goto out_release; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* establish temporary route - we set up enough to keep 90362306a36Sopenharmony_ci * mctp_route_output happy 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci rt->output = mctp_route_output; 90662306a36Sopenharmony_ci rt->mtu = 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci } else { 90962306a36Sopenharmony_ci rc = -EINVAL; 91062306a36Sopenharmony_ci goto out_free; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci spin_lock_irqsave(&rt->dev->addrs_lock, flags); 91462306a36Sopenharmony_ci if (rt->dev->num_addrs == 0) { 91562306a36Sopenharmony_ci rc = -EHOSTUNREACH; 91662306a36Sopenharmony_ci } else { 91762306a36Sopenharmony_ci /* use the outbound interface's first address as our source */ 91862306a36Sopenharmony_ci saddr = rt->dev->addrs[0]; 91962306a36Sopenharmony_ci rc = 0; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci spin_unlock_irqrestore(&rt->dev->addrs_lock, flags); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (rc) 92462306a36Sopenharmony_ci goto out_release; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci if (req_tag & MCTP_TAG_OWNER) { 92762306a36Sopenharmony_ci if (req_tag & MCTP_TAG_PREALLOC) 92862306a36Sopenharmony_ci key = mctp_lookup_prealloc_tag(msk, daddr, 92962306a36Sopenharmony_ci req_tag, &tag); 93062306a36Sopenharmony_ci else 93162306a36Sopenharmony_ci key = mctp_alloc_local_tag(msk, daddr, saddr, 93262306a36Sopenharmony_ci false, &tag); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (IS_ERR(key)) { 93562306a36Sopenharmony_ci rc = PTR_ERR(key); 93662306a36Sopenharmony_ci goto out_release; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci mctp_skb_set_flow(skb, key); 93962306a36Sopenharmony_ci /* done with the key in this scope */ 94062306a36Sopenharmony_ci mctp_key_unref(key); 94162306a36Sopenharmony_ci tag |= MCTP_HDR_FLAG_TO; 94262306a36Sopenharmony_ci } else { 94362306a36Sopenharmony_ci key = NULL; 94462306a36Sopenharmony_ci tag = req_tag & MCTP_TAG_MASK; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MCTP); 94862306a36Sopenharmony_ci skb->priority = 0; 94962306a36Sopenharmony_ci skb_reset_transport_header(skb); 95062306a36Sopenharmony_ci skb_push(skb, sizeof(struct mctp_hdr)); 95162306a36Sopenharmony_ci skb_reset_network_header(skb); 95262306a36Sopenharmony_ci skb->dev = rt->dev->dev; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* cb->net will have been set on initial ingress */ 95562306a36Sopenharmony_ci cb->src = saddr; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* set up common header fields */ 95862306a36Sopenharmony_ci hdr = mctp_hdr(skb); 95962306a36Sopenharmony_ci hdr->ver = 1; 96062306a36Sopenharmony_ci hdr->dest = daddr; 96162306a36Sopenharmony_ci hdr->src = saddr; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci mtu = mctp_route_mtu(rt); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (skb->len + sizeof(struct mctp_hdr) <= mtu) { 96662306a36Sopenharmony_ci hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | 96762306a36Sopenharmony_ci MCTP_HDR_FLAG_EOM | tag; 96862306a36Sopenharmony_ci rc = rt->output(rt, skb); 96962306a36Sopenharmony_ci } else { 97062306a36Sopenharmony_ci rc = mctp_do_fragment_route(rt, skb, mtu, tag); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* route output functions consume the skb, even on error */ 97462306a36Sopenharmony_ci skb = NULL; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciout_release: 97762306a36Sopenharmony_ci if (!ext_rt) 97862306a36Sopenharmony_ci mctp_route_release(rt); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci mctp_dev_put(tmp_rt.dev); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ciout_free: 98362306a36Sopenharmony_ci kfree_skb(skb); 98462306a36Sopenharmony_ci return rc; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci/* route management */ 98862306a36Sopenharmony_cistatic int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start, 98962306a36Sopenharmony_ci unsigned int daddr_extent, unsigned int mtu, 99062306a36Sopenharmony_ci unsigned char type) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci int (*rtfn)(struct mctp_route *rt, struct sk_buff *skb); 99362306a36Sopenharmony_ci struct net *net = dev_net(mdev->dev); 99462306a36Sopenharmony_ci struct mctp_route *rt, *ert; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (!mctp_address_unicast(daddr_start)) 99762306a36Sopenharmony_ci return -EINVAL; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255) 100062306a36Sopenharmony_ci return -EINVAL; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci switch (type) { 100362306a36Sopenharmony_ci case RTN_LOCAL: 100462306a36Sopenharmony_ci rtfn = mctp_route_input; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci case RTN_UNICAST: 100762306a36Sopenharmony_ci rtfn = mctp_route_output; 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci default: 101062306a36Sopenharmony_ci return -EINVAL; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci rt = mctp_route_alloc(); 101462306a36Sopenharmony_ci if (!rt) 101562306a36Sopenharmony_ci return -ENOMEM; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci rt->min = daddr_start; 101862306a36Sopenharmony_ci rt->max = daddr_start + daddr_extent; 101962306a36Sopenharmony_ci rt->mtu = mtu; 102062306a36Sopenharmony_ci rt->dev = mdev; 102162306a36Sopenharmony_ci mctp_dev_hold(rt->dev); 102262306a36Sopenharmony_ci rt->type = type; 102362306a36Sopenharmony_ci rt->output = rtfn; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci ASSERT_RTNL(); 102662306a36Sopenharmony_ci /* Prevent duplicate identical routes. */ 102762306a36Sopenharmony_ci list_for_each_entry(ert, &net->mctp.routes, list) { 102862306a36Sopenharmony_ci if (mctp_rt_compare_exact(rt, ert)) { 102962306a36Sopenharmony_ci mctp_route_release(rt); 103062306a36Sopenharmony_ci return -EEXIST; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci list_add_rcu(&rt->list, &net->mctp.routes); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return 0; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start, 104062306a36Sopenharmony_ci unsigned int daddr_extent, unsigned char type) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct net *net = dev_net(mdev->dev); 104362306a36Sopenharmony_ci struct mctp_route *rt, *tmp; 104462306a36Sopenharmony_ci mctp_eid_t daddr_end; 104562306a36Sopenharmony_ci bool dropped; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255) 104862306a36Sopenharmony_ci return -EINVAL; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci daddr_end = daddr_start + daddr_extent; 105162306a36Sopenharmony_ci dropped = false; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ASSERT_RTNL(); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) { 105662306a36Sopenharmony_ci if (rt->dev == mdev && 105762306a36Sopenharmony_ci rt->min == daddr_start && rt->max == daddr_end && 105862306a36Sopenharmony_ci rt->type == type) { 105962306a36Sopenharmony_ci list_del_rcu(&rt->list); 106062306a36Sopenharmony_ci /* TODO: immediate RTM_DELROUTE */ 106162306a36Sopenharmony_ci mctp_route_release(rt); 106262306a36Sopenharmony_ci dropped = true; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return dropped ? 0 : -ENOENT; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ciint mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci return mctp_route_add(mdev, addr, 0, 0, RTN_LOCAL); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ciint mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci return mctp_route_remove(mdev, addr, 0, RTN_LOCAL); 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci/* removes all entries for a given device */ 108062306a36Sopenharmony_civoid mctp_route_remove_dev(struct mctp_dev *mdev) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct net *net = dev_net(mdev->dev); 108362306a36Sopenharmony_ci struct mctp_route *rt, *tmp; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ASSERT_RTNL(); 108662306a36Sopenharmony_ci list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) { 108762306a36Sopenharmony_ci if (rt->dev == mdev) { 108862306a36Sopenharmony_ci list_del_rcu(&rt->list); 108962306a36Sopenharmony_ci /* TODO: immediate RTM_DELROUTE */ 109062306a36Sopenharmony_ci mctp_route_release(rt); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* Incoming packet-handling */ 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_cistatic int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, 109862306a36Sopenharmony_ci struct packet_type *pt, 109962306a36Sopenharmony_ci struct net_device *orig_dev) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci struct net *net = dev_net(dev); 110262306a36Sopenharmony_ci struct mctp_dev *mdev; 110362306a36Sopenharmony_ci struct mctp_skb_cb *cb; 110462306a36Sopenharmony_ci struct mctp_route *rt; 110562306a36Sopenharmony_ci struct mctp_hdr *mh; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci rcu_read_lock(); 110862306a36Sopenharmony_ci mdev = __mctp_dev_get(dev); 110962306a36Sopenharmony_ci rcu_read_unlock(); 111062306a36Sopenharmony_ci if (!mdev) { 111162306a36Sopenharmony_ci /* basic non-data sanity checks */ 111262306a36Sopenharmony_ci goto err_drop; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(struct mctp_hdr))) 111662306a36Sopenharmony_ci goto err_drop; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci skb_reset_transport_header(skb); 111962306a36Sopenharmony_ci skb_reset_network_header(skb); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* We have enough for a header; decode and route */ 112262306a36Sopenharmony_ci mh = mctp_hdr(skb); 112362306a36Sopenharmony_ci if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX) 112462306a36Sopenharmony_ci goto err_drop; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* source must be valid unicast or null; drop reserved ranges and 112762306a36Sopenharmony_ci * broadcast 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci if (!(mctp_address_unicast(mh->src) || mctp_address_null(mh->src))) 113062306a36Sopenharmony_ci goto err_drop; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* dest address: as above, but allow broadcast */ 113362306a36Sopenharmony_ci if (!(mctp_address_unicast(mh->dest) || mctp_address_null(mh->dest) || 113462306a36Sopenharmony_ci mctp_address_broadcast(mh->dest))) 113562306a36Sopenharmony_ci goto err_drop; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* MCTP drivers must populate halen/haddr */ 113862306a36Sopenharmony_ci if (dev->type == ARPHRD_MCTP) { 113962306a36Sopenharmony_ci cb = mctp_cb(skb); 114062306a36Sopenharmony_ci } else { 114162306a36Sopenharmony_ci cb = __mctp_cb(skb); 114262306a36Sopenharmony_ci cb->halen = 0; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci cb->net = READ_ONCE(mdev->net); 114562306a36Sopenharmony_ci cb->ifindex = dev->ifindex; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci rt = mctp_route_lookup(net, cb->net, mh->dest); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci /* NULL EID, but addressed to our physical address */ 115062306a36Sopenharmony_ci if (!rt && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST) 115162306a36Sopenharmony_ci rt = mctp_route_lookup_null(net, dev); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (!rt) 115462306a36Sopenharmony_ci goto err_drop; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci rt->output(rt, skb); 115762306a36Sopenharmony_ci mctp_route_release(rt); 115862306a36Sopenharmony_ci mctp_dev_put(mdev); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return NET_RX_SUCCESS; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cierr_drop: 116362306a36Sopenharmony_ci kfree_skb(skb); 116462306a36Sopenharmony_ci mctp_dev_put(mdev); 116562306a36Sopenharmony_ci return NET_RX_DROP; 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic struct packet_type mctp_packet_type = { 116962306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_MCTP), 117062306a36Sopenharmony_ci .func = mctp_pkttype_receive, 117162306a36Sopenharmony_ci}; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci/* netlink interface */ 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = { 117662306a36Sopenharmony_ci [RTA_DST] = { .type = NLA_U8 }, 117762306a36Sopenharmony_ci [RTA_METRICS] = { .type = NLA_NESTED }, 117862306a36Sopenharmony_ci [RTA_OIF] = { .type = NLA_U32 }, 117962306a36Sopenharmony_ci}; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing. 118262306a36Sopenharmony_ci * tb must hold RTA_MAX+1 elements. 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_cistatic int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh, 118562306a36Sopenharmony_ci struct netlink_ext_ack *extack, 118662306a36Sopenharmony_ci struct nlattr **tb, struct rtmsg **rtm, 118762306a36Sopenharmony_ci struct mctp_dev **mdev, mctp_eid_t *daddr_start) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 119062306a36Sopenharmony_ci struct net_device *dev; 119162306a36Sopenharmony_ci unsigned int ifindex; 119262306a36Sopenharmony_ci int rc; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci rc = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, 119562306a36Sopenharmony_ci rta_mctp_policy, extack); 119662306a36Sopenharmony_ci if (rc < 0) { 119762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "incorrect format"); 119862306a36Sopenharmony_ci return rc; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (!tb[RTA_DST]) { 120262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "dst EID missing"); 120362306a36Sopenharmony_ci return -EINVAL; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci *daddr_start = nla_get_u8(tb[RTA_DST]); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (!tb[RTA_OIF]) { 120862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ifindex missing"); 120962306a36Sopenharmony_ci return -EINVAL; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci ifindex = nla_get_u32(tb[RTA_OIF]); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci *rtm = nlmsg_data(nlh); 121462306a36Sopenharmony_ci if ((*rtm)->rtm_family != AF_MCTP) { 121562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "route family must be AF_MCTP"); 121662306a36Sopenharmony_ci return -EINVAL; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 122062306a36Sopenharmony_ci if (!dev) { 122162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "bad ifindex"); 122262306a36Sopenharmony_ci return -ENODEV; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci *mdev = mctp_dev_get_rtnl(dev); 122562306a36Sopenharmony_ci if (!*mdev) 122662306a36Sopenharmony_ci return -ENODEV; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci if (dev->flags & IFF_LOOPBACK) { 122962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "no routes to loopback"); 123062306a36Sopenharmony_ci return -EINVAL; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return 0; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = { 123762306a36Sopenharmony_ci [RTAX_MTU] = { .type = NLA_U32 }, 123862306a36Sopenharmony_ci}; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, 124162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX + 1]; 124462306a36Sopenharmony_ci struct nlattr *tbx[RTAX_MAX + 1]; 124562306a36Sopenharmony_ci mctp_eid_t daddr_start; 124662306a36Sopenharmony_ci struct mctp_dev *mdev; 124762306a36Sopenharmony_ci struct rtmsg *rtm; 124862306a36Sopenharmony_ci unsigned int mtu; 124962306a36Sopenharmony_ci int rc; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci rc = mctp_route_nlparse(skb, nlh, extack, tb, 125262306a36Sopenharmony_ci &rtm, &mdev, &daddr_start); 125362306a36Sopenharmony_ci if (rc < 0) 125462306a36Sopenharmony_ci return rc; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (rtm->rtm_type != RTN_UNICAST) { 125762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST"); 125862306a36Sopenharmony_ci return -EINVAL; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci mtu = 0; 126262306a36Sopenharmony_ci if (tb[RTA_METRICS]) { 126362306a36Sopenharmony_ci rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS], 126462306a36Sopenharmony_ci rta_metrics_policy, NULL); 126562306a36Sopenharmony_ci if (rc < 0) 126662306a36Sopenharmony_ci return rc; 126762306a36Sopenharmony_ci if (tbx[RTAX_MTU]) 126862306a36Sopenharmony_ci mtu = nla_get_u32(tbx[RTAX_MTU]); 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci rc = mctp_route_add(mdev, daddr_start, rtm->rtm_dst_len, mtu, 127262306a36Sopenharmony_ci rtm->rtm_type); 127362306a36Sopenharmony_ci return rc; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, 127762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX + 1]; 128062306a36Sopenharmony_ci mctp_eid_t daddr_start; 128162306a36Sopenharmony_ci struct mctp_dev *mdev; 128262306a36Sopenharmony_ci struct rtmsg *rtm; 128362306a36Sopenharmony_ci int rc; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci rc = mctp_route_nlparse(skb, nlh, extack, tb, 128662306a36Sopenharmony_ci &rtm, &mdev, &daddr_start); 128762306a36Sopenharmony_ci if (rc < 0) 128862306a36Sopenharmony_ci return rc; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* we only have unicast routes */ 129162306a36Sopenharmony_ci if (rtm->rtm_type != RTN_UNICAST) 129262306a36Sopenharmony_ci return -EINVAL; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci rc = mctp_route_remove(mdev, daddr_start, rtm->rtm_dst_len, RTN_UNICAST); 129562306a36Sopenharmony_ci return rc; 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt, 129962306a36Sopenharmony_ci u32 portid, u32 seq, int event, unsigned int flags) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct nlmsghdr *nlh; 130262306a36Sopenharmony_ci struct rtmsg *hdr; 130362306a36Sopenharmony_ci void *metrics; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags); 130662306a36Sopenharmony_ci if (!nlh) 130762306a36Sopenharmony_ci return -EMSGSIZE; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci hdr = nlmsg_data(nlh); 131062306a36Sopenharmony_ci hdr->rtm_family = AF_MCTP; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* we use the _len fields as a number of EIDs, rather than 131362306a36Sopenharmony_ci * a number of bits in the address 131462306a36Sopenharmony_ci */ 131562306a36Sopenharmony_ci hdr->rtm_dst_len = rt->max - rt->min; 131662306a36Sopenharmony_ci hdr->rtm_src_len = 0; 131762306a36Sopenharmony_ci hdr->rtm_tos = 0; 131862306a36Sopenharmony_ci hdr->rtm_table = RT_TABLE_DEFAULT; 131962306a36Sopenharmony_ci hdr->rtm_protocol = RTPROT_STATIC; /* everything is user-defined */ 132062306a36Sopenharmony_ci hdr->rtm_scope = RT_SCOPE_LINK; /* TODO: scope in mctp_route? */ 132162306a36Sopenharmony_ci hdr->rtm_type = rt->type; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci if (nla_put_u8(skb, RTA_DST, rt->min)) 132462306a36Sopenharmony_ci goto cancel; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci metrics = nla_nest_start_noflag(skb, RTA_METRICS); 132762306a36Sopenharmony_ci if (!metrics) 132862306a36Sopenharmony_ci goto cancel; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if (rt->mtu) { 133162306a36Sopenharmony_ci if (nla_put_u32(skb, RTAX_MTU, rt->mtu)) 133262306a36Sopenharmony_ci goto cancel; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci nla_nest_end(skb, metrics); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (rt->dev) { 133862306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_OIF, rt->dev->dev->ifindex)) 133962306a36Sopenharmony_ci goto cancel; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* TODO: conditional neighbour physaddr? */ 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci nlmsg_end(skb, nlh); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci return 0; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cicancel: 134962306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 135062306a36Sopenharmony_ci return -EMSGSIZE; 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic int mctp_dump_rtinfo(struct sk_buff *skb, struct netlink_callback *cb) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 135662306a36Sopenharmony_ci struct mctp_route *rt; 135762306a36Sopenharmony_ci int s_idx, idx; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* TODO: allow filtering on route data, possibly under 136062306a36Sopenharmony_ci * cb->strict_check 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci /* TODO: change to struct overlay */ 136462306a36Sopenharmony_ci s_idx = cb->args[0]; 136562306a36Sopenharmony_ci idx = 0; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci rcu_read_lock(); 136862306a36Sopenharmony_ci list_for_each_entry_rcu(rt, &net->mctp.routes, list) { 136962306a36Sopenharmony_ci if (idx++ < s_idx) 137062306a36Sopenharmony_ci continue; 137162306a36Sopenharmony_ci if (mctp_fill_rtinfo(skb, rt, 137262306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 137362306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 137462306a36Sopenharmony_ci RTM_NEWROUTE, NLM_F_MULTI) < 0) 137562306a36Sopenharmony_ci break; 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci rcu_read_unlock(); 137962306a36Sopenharmony_ci cb->args[0] = idx; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return skb->len; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci/* net namespace implementation */ 138562306a36Sopenharmony_cistatic int __net_init mctp_routes_net_init(struct net *net) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci struct netns_mctp *ns = &net->mctp; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci INIT_LIST_HEAD(&ns->routes); 139062306a36Sopenharmony_ci INIT_HLIST_HEAD(&ns->binds); 139162306a36Sopenharmony_ci mutex_init(&ns->bind_lock); 139262306a36Sopenharmony_ci INIT_HLIST_HEAD(&ns->keys); 139362306a36Sopenharmony_ci spin_lock_init(&ns->keys_lock); 139462306a36Sopenharmony_ci WARN_ON(mctp_default_net_set(net, MCTP_INITIAL_DEFAULT_NET)); 139562306a36Sopenharmony_ci return 0; 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic void __net_exit mctp_routes_net_exit(struct net *net) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci struct mctp_route *rt; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci rcu_read_lock(); 140362306a36Sopenharmony_ci list_for_each_entry_rcu(rt, &net->mctp.routes, list) 140462306a36Sopenharmony_ci mctp_route_release(rt); 140562306a36Sopenharmony_ci rcu_read_unlock(); 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_cistatic struct pernet_operations mctp_net_ops = { 140962306a36Sopenharmony_ci .init = mctp_routes_net_init, 141062306a36Sopenharmony_ci .exit = mctp_routes_net_exit, 141162306a36Sopenharmony_ci}; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ciint __init mctp_routes_init(void) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci dev_add_pack(&mctp_packet_type); 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_GETROUTE, 141862306a36Sopenharmony_ci NULL, mctp_dump_rtinfo, 0); 141962306a36Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_NEWROUTE, 142062306a36Sopenharmony_ci mctp_newroute, NULL, 0); 142162306a36Sopenharmony_ci rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_DELROUTE, 142262306a36Sopenharmony_ci mctp_delroute, NULL, 0); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci return register_pernet_subsys(&mctp_net_ops); 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_civoid mctp_routes_exit(void) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci unregister_pernet_subsys(&mctp_net_ops); 143062306a36Sopenharmony_ci rtnl_unregister(PF_MCTP, RTM_DELROUTE); 143162306a36Sopenharmony_ci rtnl_unregister(PF_MCTP, RTM_NEWROUTE); 143262306a36Sopenharmony_ci rtnl_unregister(PF_MCTP, RTM_GETROUTE); 143362306a36Sopenharmony_ci dev_remove_pack(&mctp_packet_type); 143462306a36Sopenharmony_ci} 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_MCTP_TEST) 143762306a36Sopenharmony_ci#include "test/route-test.c" 143862306a36Sopenharmony_ci#endif 1439