xref: /openbmc/linux/net/mctp/route.c (revision e297db3eadd7809170aea627ed3d9f714fa3da2d)
1889b7da2SJeremy Kerr // SPDX-License-Identifier: GPL-2.0
2889b7da2SJeremy Kerr /*
3889b7da2SJeremy Kerr  * Management Component Transport Protocol (MCTP) - routing
4889b7da2SJeremy Kerr  * implementation.
5889b7da2SJeremy Kerr  *
6889b7da2SJeremy Kerr  * This is currently based on a simple routing table, with no dst cache. The
7889b7da2SJeremy Kerr  * number of routes should stay fairly small, so the lookup cost is small.
8889b7da2SJeremy Kerr  *
9889b7da2SJeremy Kerr  * Copyright (c) 2021 Code Construct
10889b7da2SJeremy Kerr  * Copyright (c) 2021 Google
11889b7da2SJeremy Kerr  */
12889b7da2SJeremy Kerr 
13889b7da2SJeremy Kerr #include <linux/idr.h>
14161eba50SJeremy Kerr #include <linux/kconfig.h>
15889b7da2SJeremy Kerr #include <linux/mctp.h>
16889b7da2SJeremy Kerr #include <linux/netdevice.h>
17889b7da2SJeremy Kerr #include <linux/rtnetlink.h>
18889b7da2SJeremy Kerr #include <linux/skbuff.h>
19889b7da2SJeremy Kerr 
20889b7da2SJeremy Kerr #include <uapi/linux/if_arp.h>
21889b7da2SJeremy Kerr 
22889b7da2SJeremy Kerr #include <net/mctp.h>
23889b7da2SJeremy Kerr #include <net/mctpdevice.h>
2406d2f4c5SMatt Johnston #include <net/netlink.h>
2506d2f4c5SMatt Johnston #include <net/sock.h>
26889b7da2SJeremy Kerr 
274f9e1ba6SJeremy Kerr #include <trace/events/mctp.h>
284f9e1ba6SJeremy Kerr 
294a992bbdSJeremy Kerr static const unsigned int mctp_message_maxlen = 64 * 1024;
307b14e15aSJeremy Kerr static const unsigned long mctp_key_lifetime = 6 * CONFIG_HZ;
317b14e15aSJeremy Kerr 
3267737c45SJeremy Kerr static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev);
3367737c45SJeremy Kerr 
34889b7da2SJeremy Kerr /* route output callbacks */
35889b7da2SJeremy Kerr static int mctp_route_discard(struct mctp_route *route, struct sk_buff *skb)
36889b7da2SJeremy Kerr {
37889b7da2SJeremy Kerr 	kfree_skb(skb);
38889b7da2SJeremy Kerr 	return 0;
39889b7da2SJeremy Kerr }
40889b7da2SJeremy Kerr 
41833ef3b9SJeremy Kerr static struct mctp_sock *mctp_lookup_bind(struct net *net, struct sk_buff *skb)
42833ef3b9SJeremy Kerr {
43833ef3b9SJeremy Kerr 	struct mctp_skb_cb *cb = mctp_cb(skb);
44833ef3b9SJeremy Kerr 	struct mctp_hdr *mh;
45833ef3b9SJeremy Kerr 	struct sock *sk;
46833ef3b9SJeremy Kerr 	u8 type;
47833ef3b9SJeremy Kerr 
48833ef3b9SJeremy Kerr 	WARN_ON(!rcu_read_lock_held());
49833ef3b9SJeremy Kerr 
50833ef3b9SJeremy Kerr 	/* TODO: look up in skb->cb? */
51833ef3b9SJeremy Kerr 	mh = mctp_hdr(skb);
52833ef3b9SJeremy Kerr 
53833ef3b9SJeremy Kerr 	if (!skb_headlen(skb))
54833ef3b9SJeremy Kerr 		return NULL;
55833ef3b9SJeremy Kerr 
56833ef3b9SJeremy Kerr 	type = (*(u8 *)skb->data) & 0x7f;
57833ef3b9SJeremy Kerr 
58833ef3b9SJeremy Kerr 	sk_for_each_rcu(sk, &net->mctp.binds) {
59833ef3b9SJeremy Kerr 		struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
60833ef3b9SJeremy Kerr 
61833ef3b9SJeremy Kerr 		if (msk->bind_net != MCTP_NET_ANY && msk->bind_net != cb->net)
62833ef3b9SJeremy Kerr 			continue;
63833ef3b9SJeremy Kerr 
64833ef3b9SJeremy Kerr 		if (msk->bind_type != type)
65833ef3b9SJeremy Kerr 			continue;
66833ef3b9SJeremy Kerr 
678069b22dSJeremy Kerr 		if (!mctp_address_matches(msk->bind_addr, mh->dest))
68833ef3b9SJeremy Kerr 			continue;
69833ef3b9SJeremy Kerr 
70833ef3b9SJeremy Kerr 		return msk;
71833ef3b9SJeremy Kerr 	}
72833ef3b9SJeremy Kerr 
73833ef3b9SJeremy Kerr 	return NULL;
74833ef3b9SJeremy Kerr }
75833ef3b9SJeremy Kerr 
76833ef3b9SJeremy Kerr static bool mctp_key_match(struct mctp_sk_key *key, mctp_eid_t local,
77833ef3b9SJeremy Kerr 			   mctp_eid_t peer, u8 tag)
78833ef3b9SJeremy Kerr {
790de55a7dSJeremy Kerr 	if (!mctp_address_matches(key->local_addr, local))
80833ef3b9SJeremy Kerr 		return false;
81833ef3b9SJeremy Kerr 
82833ef3b9SJeremy Kerr 	if (key->peer_addr != peer)
83833ef3b9SJeremy Kerr 		return false;
84833ef3b9SJeremy Kerr 
85833ef3b9SJeremy Kerr 	if (key->tag != tag)
86833ef3b9SJeremy Kerr 		return false;
87833ef3b9SJeremy Kerr 
88833ef3b9SJeremy Kerr 	return true;
89833ef3b9SJeremy Kerr }
90833ef3b9SJeremy Kerr 
9173c61845SJeremy Kerr /* returns a key (with key->lock held, and refcounted), or NULL if no such
9273c61845SJeremy Kerr  * key exists.
9373c61845SJeremy Kerr  */
94833ef3b9SJeremy Kerr static struct mctp_sk_key *mctp_lookup_key(struct net *net, struct sk_buff *skb,
9573c61845SJeremy Kerr 					   mctp_eid_t peer,
9673c61845SJeremy Kerr 					   unsigned long *irqflags)
9773c61845SJeremy Kerr 	__acquires(&key->lock)
98833ef3b9SJeremy Kerr {
99833ef3b9SJeremy Kerr 	struct mctp_sk_key *key, *ret;
10073c61845SJeremy Kerr 	unsigned long flags;
101833ef3b9SJeremy Kerr 	struct mctp_hdr *mh;
102833ef3b9SJeremy Kerr 	u8 tag;
103833ef3b9SJeremy Kerr 
104833ef3b9SJeremy Kerr 	mh = mctp_hdr(skb);
105833ef3b9SJeremy Kerr 	tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
106833ef3b9SJeremy Kerr 
107833ef3b9SJeremy Kerr 	ret = NULL;
10873c61845SJeremy Kerr 	spin_lock_irqsave(&net->mctp.keys_lock, flags);
109833ef3b9SJeremy Kerr 
11073c61845SJeremy Kerr 	hlist_for_each_entry(key, &net->mctp.keys, hlist) {
11173c61845SJeremy Kerr 		if (!mctp_key_match(key, mh->dest, peer, tag))
11273c61845SJeremy Kerr 			continue;
11373c61845SJeremy Kerr 
11473c61845SJeremy Kerr 		spin_lock(&key->lock);
11573c61845SJeremy Kerr 		if (key->valid) {
11673c61845SJeremy Kerr 			refcount_inc(&key->refs);
117833ef3b9SJeremy Kerr 			ret = key;
118833ef3b9SJeremy Kerr 			break;
119833ef3b9SJeremy Kerr 		}
12073c61845SJeremy Kerr 		spin_unlock(&key->lock);
12173c61845SJeremy Kerr 	}
12273c61845SJeremy Kerr 
12373c61845SJeremy Kerr 	if (ret) {
12473c61845SJeremy Kerr 		spin_unlock(&net->mctp.keys_lock);
12573c61845SJeremy Kerr 		*irqflags = flags;
12673c61845SJeremy Kerr 	} else {
12773c61845SJeremy Kerr 		spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
128833ef3b9SJeremy Kerr 	}
129833ef3b9SJeremy Kerr 
130833ef3b9SJeremy Kerr 	return ret;
131833ef3b9SJeremy Kerr }
132833ef3b9SJeremy Kerr 
1334a992bbdSJeremy Kerr static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
1344a992bbdSJeremy Kerr 					  mctp_eid_t local, mctp_eid_t peer,
1354a992bbdSJeremy Kerr 					  u8 tag, gfp_t gfp)
1364a992bbdSJeremy Kerr {
1374a992bbdSJeremy Kerr 	struct mctp_sk_key *key;
1384a992bbdSJeremy Kerr 
1394a992bbdSJeremy Kerr 	key = kzalloc(sizeof(*key), gfp);
1404a992bbdSJeremy Kerr 	if (!key)
1414a992bbdSJeremy Kerr 		return NULL;
1424a992bbdSJeremy Kerr 
1434a992bbdSJeremy Kerr 	key->peer_addr = peer;
1444a992bbdSJeremy Kerr 	key->local_addr = local;
1454a992bbdSJeremy Kerr 	key->tag = tag;
1464a992bbdSJeremy Kerr 	key->sk = &msk->sk;
14773c61845SJeremy Kerr 	key->valid = true;
14873c61845SJeremy Kerr 	spin_lock_init(&key->lock);
14973c61845SJeremy Kerr 	refcount_set(&key->refs, 1);
1504a992bbdSJeremy Kerr 
1514a992bbdSJeremy Kerr 	return key;
1524a992bbdSJeremy Kerr }
1534a992bbdSJeremy Kerr 
15473c61845SJeremy Kerr void mctp_key_unref(struct mctp_sk_key *key)
15573c61845SJeremy Kerr {
15667737c45SJeremy Kerr 	unsigned long flags;
15767737c45SJeremy Kerr 
15867737c45SJeremy Kerr 	if (!refcount_dec_and_test(&key->refs))
15967737c45SJeremy Kerr 		return;
16067737c45SJeremy Kerr 
16167737c45SJeremy Kerr 	/* even though no refs exist here, the lock allows us to stay
16267737c45SJeremy Kerr 	 * consistent with the locking requirement of mctp_dev_release_key
16367737c45SJeremy Kerr 	 */
16467737c45SJeremy Kerr 	spin_lock_irqsave(&key->lock, flags);
16567737c45SJeremy Kerr 	mctp_dev_release_key(key->dev, key);
16667737c45SJeremy Kerr 	spin_unlock_irqrestore(&key->lock, flags);
16767737c45SJeremy Kerr 
16873c61845SJeremy Kerr 	kfree(key);
16973c61845SJeremy Kerr }
17073c61845SJeremy Kerr 
1714a992bbdSJeremy Kerr static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
1724a992bbdSJeremy Kerr {
1734a992bbdSJeremy Kerr 	struct net *net = sock_net(&msk->sk);
1744a992bbdSJeremy Kerr 	struct mctp_sk_key *tmp;
1754a992bbdSJeremy Kerr 	unsigned long flags;
1764a992bbdSJeremy Kerr 	int rc = 0;
1774a992bbdSJeremy Kerr 
1784a992bbdSJeremy Kerr 	spin_lock_irqsave(&net->mctp.keys_lock, flags);
1794a992bbdSJeremy Kerr 
1804a992bbdSJeremy Kerr 	hlist_for_each_entry(tmp, &net->mctp.keys, hlist) {
1814a992bbdSJeremy Kerr 		if (mctp_key_match(tmp, key->local_addr, key->peer_addr,
1824a992bbdSJeremy Kerr 				   key->tag)) {
18373c61845SJeremy Kerr 			spin_lock(&tmp->lock);
18473c61845SJeremy Kerr 			if (tmp->valid)
1854a992bbdSJeremy Kerr 				rc = -EEXIST;
18673c61845SJeremy Kerr 			spin_unlock(&tmp->lock);
18773c61845SJeremy Kerr 			if (rc)
1884a992bbdSJeremy Kerr 				break;
1894a992bbdSJeremy Kerr 		}
1904a992bbdSJeremy Kerr 	}
1914a992bbdSJeremy Kerr 
1924a992bbdSJeremy Kerr 	if (!rc) {
19373c61845SJeremy Kerr 		refcount_inc(&key->refs);
1947b14e15aSJeremy Kerr 		key->expiry = jiffies + mctp_key_lifetime;
1957b14e15aSJeremy Kerr 		timer_reduce(&msk->key_expiry, key->expiry);
1967b14e15aSJeremy Kerr 
1974a992bbdSJeremy Kerr 		hlist_add_head(&key->hlist, &net->mctp.keys);
1984a992bbdSJeremy Kerr 		hlist_add_head(&key->sklist, &msk->keys);
1994a992bbdSJeremy Kerr 	}
2004a992bbdSJeremy Kerr 
2014a992bbdSJeremy Kerr 	spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
2024a992bbdSJeremy Kerr 
2034a992bbdSJeremy Kerr 	return rc;
2044a992bbdSJeremy Kerr }
2054a992bbdSJeremy Kerr 
20663ed1aabSMatt Johnston /* Helper for mctp_route_input().
20763ed1aabSMatt Johnston  * We're done with the key; unlock and unref the key.
20863ed1aabSMatt Johnston  * For the usual case of automatic expiry we remove the key from lists.
20963ed1aabSMatt Johnston  * In the case that manual allocation is set on a key we release the lock
21063ed1aabSMatt Johnston  * and local ref, reset reassembly, but don't remove from lists.
2114a992bbdSJeremy Kerr  */
21263ed1aabSMatt Johnston static void __mctp_key_done_in(struct mctp_sk_key *key, struct net *net,
21363ed1aabSMatt Johnston 			       unsigned long flags, unsigned long reason)
21473c61845SJeremy Kerr __releases(&key->lock)
2154a992bbdSJeremy Kerr {
2164a992bbdSJeremy Kerr 	struct sk_buff *skb;
2174a992bbdSJeremy Kerr 
21863ed1aabSMatt Johnston 	trace_mctp_key_release(key, reason);
2194a992bbdSJeremy Kerr 	skb = key->reasm_head;
2204a992bbdSJeremy Kerr 	key->reasm_head = NULL;
22163ed1aabSMatt Johnston 
22263ed1aabSMatt Johnston 	if (!key->manual_alloc) {
2234a992bbdSJeremy Kerr 		key->reasm_dead = true;
22473c61845SJeremy Kerr 		key->valid = false;
22567737c45SJeremy Kerr 		mctp_dev_release_key(key->dev, key);
22663ed1aabSMatt Johnston 	}
22773c61845SJeremy Kerr 	spin_unlock_irqrestore(&key->lock, flags);
2284a992bbdSJeremy Kerr 
22963ed1aabSMatt Johnston 	if (!key->manual_alloc) {
2304a992bbdSJeremy Kerr 		spin_lock_irqsave(&net->mctp.keys_lock, flags);
23173c61845SJeremy Kerr 		hlist_del(&key->hlist);
23273c61845SJeremy Kerr 		hlist_del(&key->sklist);
2334a992bbdSJeremy Kerr 		spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
23473c61845SJeremy Kerr 
23563ed1aabSMatt Johnston 		/* unref for the lists */
23673c61845SJeremy Kerr 		mctp_key_unref(key);
23763ed1aabSMatt Johnston 	}
23873c61845SJeremy Kerr 
23973c61845SJeremy Kerr 	/* and one for the local reference */
24073c61845SJeremy Kerr 	mctp_key_unref(key);
2414a992bbdSJeremy Kerr 
2424a992bbdSJeremy Kerr 	kfree_skb(skb);
2434a992bbdSJeremy Kerr }
2444a992bbdSJeremy Kerr 
24567737c45SJeremy Kerr #ifdef CONFIG_MCTP_FLOWS
24667737c45SJeremy Kerr static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key)
24767737c45SJeremy Kerr {
24867737c45SJeremy Kerr 	struct mctp_flow *flow;
24967737c45SJeremy Kerr 
25067737c45SJeremy Kerr 	flow = skb_ext_add(skb, SKB_EXT_MCTP);
25167737c45SJeremy Kerr 	if (!flow)
25267737c45SJeremy Kerr 		return;
25367737c45SJeremy Kerr 
25467737c45SJeremy Kerr 	refcount_inc(&key->refs);
25567737c45SJeremy Kerr 	flow->key = key;
25667737c45SJeremy Kerr }
25767737c45SJeremy Kerr 
25867737c45SJeremy Kerr static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev)
25967737c45SJeremy Kerr {
26067737c45SJeremy Kerr 	struct mctp_sk_key *key;
26167737c45SJeremy Kerr 	struct mctp_flow *flow;
26267737c45SJeremy Kerr 
26367737c45SJeremy Kerr 	flow = skb_ext_find(skb, SKB_EXT_MCTP);
26467737c45SJeremy Kerr 	if (!flow)
26567737c45SJeremy Kerr 		return;
26667737c45SJeremy Kerr 
26767737c45SJeremy Kerr 	key = flow->key;
26867737c45SJeremy Kerr 
26967737c45SJeremy Kerr 	if (WARN_ON(key->dev && key->dev != dev))
27067737c45SJeremy Kerr 		return;
27167737c45SJeremy Kerr 
27267737c45SJeremy Kerr 	mctp_dev_set_key(dev, key);
27367737c45SJeremy Kerr }
27467737c45SJeremy Kerr #else
27567737c45SJeremy Kerr static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key) {}
27667737c45SJeremy Kerr static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev) {}
27767737c45SJeremy Kerr #endif
27867737c45SJeremy Kerr 
2794a992bbdSJeremy Kerr static int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb)
2804a992bbdSJeremy Kerr {
2814a992bbdSJeremy Kerr 	struct mctp_hdr *hdr = mctp_hdr(skb);
2824a992bbdSJeremy Kerr 	u8 exp_seq, this_seq;
2834a992bbdSJeremy Kerr 
2844a992bbdSJeremy Kerr 	this_seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT)
2854a992bbdSJeremy Kerr 		& MCTP_HDR_SEQ_MASK;
2864a992bbdSJeremy Kerr 
2874a992bbdSJeremy Kerr 	if (!key->reasm_head) {
2884a992bbdSJeremy Kerr 		key->reasm_head = skb;
2894a992bbdSJeremy Kerr 		key->reasm_tailp = &(skb_shinfo(skb)->frag_list);
2904a992bbdSJeremy Kerr 		key->last_seq = this_seq;
2914a992bbdSJeremy Kerr 		return 0;
2924a992bbdSJeremy Kerr 	}
2934a992bbdSJeremy Kerr 
2944a992bbdSJeremy Kerr 	exp_seq = (key->last_seq + 1) & MCTP_HDR_SEQ_MASK;
2954a992bbdSJeremy Kerr 
2964a992bbdSJeremy Kerr 	if (this_seq != exp_seq)
2974a992bbdSJeremy Kerr 		return -EINVAL;
2984a992bbdSJeremy Kerr 
2994a992bbdSJeremy Kerr 	if (key->reasm_head->len + skb->len > mctp_message_maxlen)
3004a992bbdSJeremy Kerr 		return -EINVAL;
3014a992bbdSJeremy Kerr 
3024a992bbdSJeremy Kerr 	skb->next = NULL;
3034a992bbdSJeremy Kerr 	skb->sk = NULL;
3044a992bbdSJeremy Kerr 	*key->reasm_tailp = skb;
3054a992bbdSJeremy Kerr 	key->reasm_tailp = &skb->next;
3064a992bbdSJeremy Kerr 
3074a992bbdSJeremy Kerr 	key->last_seq = this_seq;
3084a992bbdSJeremy Kerr 
3094a992bbdSJeremy Kerr 	key->reasm_head->data_len += skb->len;
3104a992bbdSJeremy Kerr 	key->reasm_head->len += skb->len;
3114a992bbdSJeremy Kerr 	key->reasm_head->truesize += skb->truesize;
3124a992bbdSJeremy Kerr 
3134a992bbdSJeremy Kerr 	return 0;
3144a992bbdSJeremy Kerr }
3154a992bbdSJeremy Kerr 
316889b7da2SJeremy Kerr static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
317889b7da2SJeremy Kerr {
318833ef3b9SJeremy Kerr 	struct net *net = dev_net(skb->dev);
319833ef3b9SJeremy Kerr 	struct mctp_sk_key *key;
320833ef3b9SJeremy Kerr 	struct mctp_sock *msk;
321833ef3b9SJeremy Kerr 	struct mctp_hdr *mh;
3224a992bbdSJeremy Kerr 	unsigned long f;
3234a992bbdSJeremy Kerr 	u8 tag, flags;
3244a992bbdSJeremy Kerr 	int rc;
325833ef3b9SJeremy Kerr 
326833ef3b9SJeremy Kerr 	msk = NULL;
3274a992bbdSJeremy Kerr 	rc = -EINVAL;
328833ef3b9SJeremy Kerr 
329833ef3b9SJeremy Kerr 	/* we may be receiving a locally-routed packet; drop source sk
330833ef3b9SJeremy Kerr 	 * accounting
331833ef3b9SJeremy Kerr 	 */
332833ef3b9SJeremy Kerr 	skb_orphan(skb);
333833ef3b9SJeremy Kerr 
334833ef3b9SJeremy Kerr 	/* ensure we have enough data for a header and a type */
335833ef3b9SJeremy Kerr 	if (skb->len < sizeof(struct mctp_hdr) + 1)
3364a992bbdSJeremy Kerr 		goto out;
337833ef3b9SJeremy Kerr 
338833ef3b9SJeremy Kerr 	/* grab header, advance data ptr */
339833ef3b9SJeremy Kerr 	mh = mctp_hdr(skb);
340833ef3b9SJeremy Kerr 	skb_pull(skb, sizeof(struct mctp_hdr));
341833ef3b9SJeremy Kerr 
342833ef3b9SJeremy Kerr 	if (mh->ver != 1)
3434a992bbdSJeremy Kerr 		goto out;
344833ef3b9SJeremy Kerr 
3454a992bbdSJeremy Kerr 	flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
3464a992bbdSJeremy Kerr 	tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
347833ef3b9SJeremy Kerr 
348833ef3b9SJeremy Kerr 	rcu_read_lock();
3494a992bbdSJeremy Kerr 
35073c61845SJeremy Kerr 	/* lookup socket / reasm context, exactly matching (src,dest,tag).
35173c61845SJeremy Kerr 	 * we hold a ref on the key, and key->lock held.
35273c61845SJeremy Kerr 	 */
35373c61845SJeremy Kerr 	key = mctp_lookup_key(net, skb, mh->src, &f);
354833ef3b9SJeremy Kerr 
3554a992bbdSJeremy Kerr 	if (flags & MCTP_HDR_FLAG_SOM) {
3564a992bbdSJeremy Kerr 		if (key) {
357833ef3b9SJeremy Kerr 			msk = container_of(key->sk, struct mctp_sock, sk);
3584a992bbdSJeremy Kerr 		} else {
3594a992bbdSJeremy Kerr 			/* first response to a broadcast? do a more general
3604a992bbdSJeremy Kerr 			 * key lookup to find the socket, but don't use this
3614a992bbdSJeremy Kerr 			 * key for reassembly - we'll create a more specific
3624a992bbdSJeremy Kerr 			 * one for future packets if required (ie, !EOM).
3634a992bbdSJeremy Kerr 			 */
36473c61845SJeremy Kerr 			key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
3654a992bbdSJeremy Kerr 			if (key) {
3664a992bbdSJeremy Kerr 				msk = container_of(key->sk,
3674a992bbdSJeremy Kerr 						   struct mctp_sock, sk);
36873c61845SJeremy Kerr 				spin_unlock_irqrestore(&key->lock, f);
36973c61845SJeremy Kerr 				mctp_key_unref(key);
3704a992bbdSJeremy Kerr 				key = NULL;
3714a992bbdSJeremy Kerr 			}
3724a992bbdSJeremy Kerr 		}
3734a992bbdSJeremy Kerr 
3744a992bbdSJeremy Kerr 		if (!key && !msk && (tag & MCTP_HDR_FLAG_TO))
375833ef3b9SJeremy Kerr 			msk = mctp_lookup_bind(net, skb);
376833ef3b9SJeremy Kerr 
3774a992bbdSJeremy Kerr 		if (!msk) {
3784a992bbdSJeremy Kerr 			rc = -ENOENT;
3794a992bbdSJeremy Kerr 			goto out_unlock;
3804a992bbdSJeremy Kerr 		}
381833ef3b9SJeremy Kerr 
3824a992bbdSJeremy Kerr 		/* single-packet message? deliver to socket, clean up any
3834a992bbdSJeremy Kerr 		 * pending key.
3844a992bbdSJeremy Kerr 		 */
3854a992bbdSJeremy Kerr 		if (flags & MCTP_HDR_FLAG_EOM) {
386833ef3b9SJeremy Kerr 			sock_queue_rcv_skb(&msk->sk, skb);
3874a992bbdSJeremy Kerr 			if (key) {
3884a992bbdSJeremy Kerr 				/* we've hit a pending reassembly; not much we
3894a992bbdSJeremy Kerr 				 * can do but drop it
3904a992bbdSJeremy Kerr 				 */
39163ed1aabSMatt Johnston 				__mctp_key_done_in(key, net, f,
3924f9e1ba6SJeremy Kerr 						   MCTP_TRACE_KEY_REPLIED);
39373c61845SJeremy Kerr 				key = NULL;
3944a992bbdSJeremy Kerr 			}
3954a992bbdSJeremy Kerr 			rc = 0;
3964a992bbdSJeremy Kerr 			goto out_unlock;
3974a992bbdSJeremy Kerr 		}
398833ef3b9SJeremy Kerr 
3994a992bbdSJeremy Kerr 		/* broadcast response or a bind() - create a key for further
4004a992bbdSJeremy Kerr 		 * packets for this message
4014a992bbdSJeremy Kerr 		 */
4024a992bbdSJeremy Kerr 		if (!key) {
4034a992bbdSJeremy Kerr 			key = mctp_key_alloc(msk, mh->dest, mh->src,
4044a992bbdSJeremy Kerr 					     tag, GFP_ATOMIC);
4054a992bbdSJeremy Kerr 			if (!key) {
4064a992bbdSJeremy Kerr 				rc = -ENOMEM;
4074a992bbdSJeremy Kerr 				goto out_unlock;
4084a992bbdSJeremy Kerr 			}
4094a992bbdSJeremy Kerr 
41073c61845SJeremy Kerr 			/* we can queue without the key lock here, as the
4114a992bbdSJeremy Kerr 			 * key isn't observable yet
4124a992bbdSJeremy Kerr 			 */
4134a992bbdSJeremy Kerr 			mctp_frag_queue(key, skb);
4144a992bbdSJeremy Kerr 
4154a992bbdSJeremy Kerr 			/* if the key_add fails, we've raced with another
4164a992bbdSJeremy Kerr 			 * SOM packet with the same src, dest and tag. There's
4174a992bbdSJeremy Kerr 			 * no way to distinguish future packets, so all we
4184a992bbdSJeremy Kerr 			 * can do is drop; we'll free the skb on exit from
4194a992bbdSJeremy Kerr 			 * this function.
4204a992bbdSJeremy Kerr 			 */
4214a992bbdSJeremy Kerr 			rc = mctp_key_add(key, msk);
4227e5b6a5cSTom Rix 			if (rc) {
4234a992bbdSJeremy Kerr 				kfree(key);
4247e5b6a5cSTom Rix 			} else {
4254f9e1ba6SJeremy Kerr 				trace_mctp_key_acquire(key);
4264f9e1ba6SJeremy Kerr 
42773c61845SJeremy Kerr 				/* we don't need to release key->lock on exit */
4280b93aed2SMatt Johnston 				mctp_key_unref(key);
4297e5b6a5cSTom Rix 			}
43073c61845SJeremy Kerr 			key = NULL;
4314a992bbdSJeremy Kerr 
43273c61845SJeremy Kerr 		} else {
4334a992bbdSJeremy Kerr 			if (key->reasm_head || key->reasm_dead) {
4344a992bbdSJeremy Kerr 				/* duplicate start? drop everything */
43563ed1aabSMatt Johnston 				__mctp_key_done_in(key, net, f,
4364f9e1ba6SJeremy Kerr 						   MCTP_TRACE_KEY_INVALIDATED);
4374a992bbdSJeremy Kerr 				rc = -EEXIST;
43873c61845SJeremy Kerr 				key = NULL;
4394a992bbdSJeremy Kerr 			} else {
4404a992bbdSJeremy Kerr 				rc = mctp_frag_queue(key, skb);
4414a992bbdSJeremy Kerr 			}
4424a992bbdSJeremy Kerr 		}
4434a992bbdSJeremy Kerr 
4444a992bbdSJeremy Kerr 	} else if (key) {
4454a992bbdSJeremy Kerr 		/* this packet continues a previous message; reassemble
4464a992bbdSJeremy Kerr 		 * using the message-specific key
4474a992bbdSJeremy Kerr 		 */
4484a992bbdSJeremy Kerr 
4494a992bbdSJeremy Kerr 		/* we need to be continuing an existing reassembly... */
4504a992bbdSJeremy Kerr 		if (!key->reasm_head)
4514a992bbdSJeremy Kerr 			rc = -EINVAL;
4524a992bbdSJeremy Kerr 		else
4534a992bbdSJeremy Kerr 			rc = mctp_frag_queue(key, skb);
4544a992bbdSJeremy Kerr 
4554a992bbdSJeremy Kerr 		/* end of message? deliver to socket, and we're done with
4564a992bbdSJeremy Kerr 		 * the reassembly/response key
4574a992bbdSJeremy Kerr 		 */
4584a992bbdSJeremy Kerr 		if (!rc && flags & MCTP_HDR_FLAG_EOM) {
45963ed1aabSMatt Johnston 			msk = container_of(key->sk, struct mctp_sock, sk);
4604a992bbdSJeremy Kerr 			sock_queue_rcv_skb(key->sk, key->reasm_head);
4614a992bbdSJeremy Kerr 			key->reasm_head = NULL;
46263ed1aabSMatt Johnston 			__mctp_key_done_in(key, net, f, MCTP_TRACE_KEY_REPLIED);
46373c61845SJeremy Kerr 			key = NULL;
4644a992bbdSJeremy Kerr 		}
4654a992bbdSJeremy Kerr 
4664a992bbdSJeremy Kerr 	} else {
4674a992bbdSJeremy Kerr 		/* not a start, no matching key */
4684a992bbdSJeremy Kerr 		rc = -ENOENT;
4694a992bbdSJeremy Kerr 	}
4704a992bbdSJeremy Kerr 
4714a992bbdSJeremy Kerr out_unlock:
472833ef3b9SJeremy Kerr 	rcu_read_unlock();
47373c61845SJeremy Kerr 	if (key) {
47473c61845SJeremy Kerr 		spin_unlock_irqrestore(&key->lock, f);
47573c61845SJeremy Kerr 		mctp_key_unref(key);
47673c61845SJeremy Kerr 	}
4774a992bbdSJeremy Kerr out:
4784a992bbdSJeremy Kerr 	if (rc)
479889b7da2SJeremy Kerr 		kfree_skb(skb);
4804a992bbdSJeremy Kerr 	return rc;
4814a992bbdSJeremy Kerr }
4824a992bbdSJeremy Kerr 
4834a992bbdSJeremy Kerr static unsigned int mctp_route_mtu(struct mctp_route *rt)
4844a992bbdSJeremy Kerr {
4854a992bbdSJeremy Kerr 	return rt->mtu ?: READ_ONCE(rt->dev->dev->mtu);
486889b7da2SJeremy Kerr }
487889b7da2SJeremy Kerr 
48806d2f4c5SMatt Johnston static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
489889b7da2SJeremy Kerr {
49099ce45d5SJeremy Kerr 	struct mctp_skb_cb *cb = mctp_cb(skb);
49126ab3fcaSMatt Johnston 	struct mctp_hdr *hdr = mctp_hdr(skb);
49226ab3fcaSMatt Johnston 	char daddr_buf[MAX_ADDR_LEN];
49326ab3fcaSMatt Johnston 	char *daddr = NULL;
494889b7da2SJeremy Kerr 	unsigned int mtu;
495889b7da2SJeremy Kerr 	int rc;
496889b7da2SJeremy Kerr 
497889b7da2SJeremy Kerr 	skb->protocol = htons(ETH_P_MCTP);
498889b7da2SJeremy Kerr 
499889b7da2SJeremy Kerr 	mtu = READ_ONCE(skb->dev->mtu);
500889b7da2SJeremy Kerr 	if (skb->len > mtu) {
501889b7da2SJeremy Kerr 		kfree_skb(skb);
502889b7da2SJeremy Kerr 		return -EMSGSIZE;
503889b7da2SJeremy Kerr 	}
504889b7da2SJeremy Kerr 
50599ce45d5SJeremy Kerr 	if (cb->ifindex) {
50699ce45d5SJeremy Kerr 		/* direct route; use the hwaddr we stashed in sendmsg */
50799ce45d5SJeremy Kerr 		daddr = cb->haddr;
50899ce45d5SJeremy Kerr 	} else {
50926ab3fcaSMatt Johnston 		/* If lookup fails let the device handle daddr==NULL */
51026ab3fcaSMatt Johnston 		if (mctp_neigh_lookup(route->dev, hdr->dest, daddr_buf) == 0)
51126ab3fcaSMatt Johnston 			daddr = daddr_buf;
51299ce45d5SJeremy Kerr 	}
51326ab3fcaSMatt Johnston 
514889b7da2SJeremy Kerr 	rc = dev_hard_header(skb, skb->dev, ntohs(skb->protocol),
51526ab3fcaSMatt Johnston 			     daddr, skb->dev->dev_addr, skb->len);
516889b7da2SJeremy Kerr 	if (rc) {
517889b7da2SJeremy Kerr 		kfree_skb(skb);
518889b7da2SJeremy Kerr 		return -EHOSTUNREACH;
519889b7da2SJeremy Kerr 	}
520889b7da2SJeremy Kerr 
52167737c45SJeremy Kerr 	mctp_flow_prepare_output(skb, route->dev);
52267737c45SJeremy Kerr 
523889b7da2SJeremy Kerr 	rc = dev_queue_xmit(skb);
524889b7da2SJeremy Kerr 	if (rc)
525889b7da2SJeremy Kerr 		rc = net_xmit_errno(rc);
526889b7da2SJeremy Kerr 
527889b7da2SJeremy Kerr 	return rc;
528889b7da2SJeremy Kerr }
529889b7da2SJeremy Kerr 
530889b7da2SJeremy Kerr /* route alloc/release */
531889b7da2SJeremy Kerr static void mctp_route_release(struct mctp_route *rt)
532889b7da2SJeremy Kerr {
533889b7da2SJeremy Kerr 	if (refcount_dec_and_test(&rt->refs)) {
53443f55f23SJeremy Kerr 		mctp_dev_put(rt->dev);
535889b7da2SJeremy Kerr 		kfree_rcu(rt, rcu);
536889b7da2SJeremy Kerr 	}
537889b7da2SJeremy Kerr }
538889b7da2SJeremy Kerr 
539889b7da2SJeremy Kerr /* returns a route with the refcount at 1 */
540889b7da2SJeremy Kerr static struct mctp_route *mctp_route_alloc(void)
541889b7da2SJeremy Kerr {
542889b7da2SJeremy Kerr 	struct mctp_route *rt;
543889b7da2SJeremy Kerr 
544889b7da2SJeremy Kerr 	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
545889b7da2SJeremy Kerr 	if (!rt)
546889b7da2SJeremy Kerr 		return NULL;
547889b7da2SJeremy Kerr 
548889b7da2SJeremy Kerr 	INIT_LIST_HEAD(&rt->list);
549889b7da2SJeremy Kerr 	refcount_set(&rt->refs, 1);
550889b7da2SJeremy Kerr 	rt->output = mctp_route_discard;
551889b7da2SJeremy Kerr 
552889b7da2SJeremy Kerr 	return rt;
553889b7da2SJeremy Kerr }
554889b7da2SJeremy Kerr 
55503f2bbc4SMatt Johnston unsigned int mctp_default_net(struct net *net)
55603f2bbc4SMatt Johnston {
55703f2bbc4SMatt Johnston 	return READ_ONCE(net->mctp.default_net);
55803f2bbc4SMatt Johnston }
55903f2bbc4SMatt Johnston 
56003f2bbc4SMatt Johnston int mctp_default_net_set(struct net *net, unsigned int index)
56103f2bbc4SMatt Johnston {
56203f2bbc4SMatt Johnston 	if (index == 0)
56303f2bbc4SMatt Johnston 		return -EINVAL;
56403f2bbc4SMatt Johnston 	WRITE_ONCE(net->mctp.default_net, index);
56503f2bbc4SMatt Johnston 	return 0;
56603f2bbc4SMatt Johnston }
56703f2bbc4SMatt Johnston 
568833ef3b9SJeremy Kerr /* tag management */
569833ef3b9SJeremy Kerr static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key,
570833ef3b9SJeremy Kerr 			     struct mctp_sock *msk)
571833ef3b9SJeremy Kerr {
572833ef3b9SJeremy Kerr 	struct netns_mctp *mns = &net->mctp;
573833ef3b9SJeremy Kerr 
574833ef3b9SJeremy Kerr 	lockdep_assert_held(&mns->keys_lock);
575833ef3b9SJeremy Kerr 
5767b14e15aSJeremy Kerr 	key->expiry = jiffies + mctp_key_lifetime;
5777b14e15aSJeremy Kerr 	timer_reduce(&msk->key_expiry, key->expiry);
5787b14e15aSJeremy Kerr 
579833ef3b9SJeremy Kerr 	/* we hold the net->key_lock here, allowing updates to both
580833ef3b9SJeremy Kerr 	 * then net and sk
581833ef3b9SJeremy Kerr 	 */
582833ef3b9SJeremy Kerr 	hlist_add_head_rcu(&key->hlist, &mns->keys);
583833ef3b9SJeremy Kerr 	hlist_add_head_rcu(&key->sklist, &msk->keys);
58473c61845SJeremy Kerr 	refcount_inc(&key->refs);
585833ef3b9SJeremy Kerr }
586833ef3b9SJeremy Kerr 
587833ef3b9SJeremy Kerr /* Allocate a locally-owned tag value for (saddr, daddr), and reserve
588833ef3b9SJeremy Kerr  * it for the socket msk
589833ef3b9SJeremy Kerr  */
59063ed1aabSMatt Johnston struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
59163ed1aabSMatt Johnston 					 mctp_eid_t daddr, mctp_eid_t saddr,
59263ed1aabSMatt Johnston 					 bool manual, u8 *tagp)
593833ef3b9SJeremy Kerr {
594833ef3b9SJeremy Kerr 	struct net *net = sock_net(&msk->sk);
595833ef3b9SJeremy Kerr 	struct netns_mctp *mns = &net->mctp;
596833ef3b9SJeremy Kerr 	struct mctp_sk_key *key, *tmp;
597833ef3b9SJeremy Kerr 	unsigned long flags;
598833ef3b9SJeremy Kerr 	u8 tagbits;
599833ef3b9SJeremy Kerr 
6001f6c77acSJeremy Kerr 	/* for NULL destination EIDs, we may get a response from any peer */
6011f6c77acSJeremy Kerr 	if (daddr == MCTP_ADDR_NULL)
6021f6c77acSJeremy Kerr 		daddr = MCTP_ADDR_ANY;
6031f6c77acSJeremy Kerr 
604833ef3b9SJeremy Kerr 	/* be optimistic, alloc now */
6054a992bbdSJeremy Kerr 	key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL);
606833ef3b9SJeremy Kerr 	if (!key)
607212c10c3SJeremy Kerr 		return ERR_PTR(-ENOMEM);
608833ef3b9SJeremy Kerr 
609833ef3b9SJeremy Kerr 	/* 8 possible tag values */
610833ef3b9SJeremy Kerr 	tagbits = 0xff;
611833ef3b9SJeremy Kerr 
612833ef3b9SJeremy Kerr 	spin_lock_irqsave(&mns->keys_lock, flags);
613833ef3b9SJeremy Kerr 
614833ef3b9SJeremy Kerr 	/* Walk through the existing keys, looking for potential conflicting
615833ef3b9SJeremy Kerr 	 * tags. If we find a conflict, clear that bit from tagbits
616833ef3b9SJeremy Kerr 	 */
617833ef3b9SJeremy Kerr 	hlist_for_each_entry(tmp, &mns->keys, hlist) {
61873c61845SJeremy Kerr 		/* We can check the lookup fields (*_addr, tag) without the
61973c61845SJeremy Kerr 		 * lock held, they don't change over the lifetime of the key.
62073c61845SJeremy Kerr 		 */
62173c61845SJeremy Kerr 
622833ef3b9SJeremy Kerr 		/* if we don't own the tag, it can't conflict */
623833ef3b9SJeremy Kerr 		if (tmp->tag & MCTP_HDR_FLAG_TO)
624833ef3b9SJeremy Kerr 			continue;
625833ef3b9SJeremy Kerr 
6268069b22dSJeremy Kerr 		if (!(mctp_address_matches(tmp->peer_addr, daddr) &&
6270de55a7dSJeremy Kerr 		      mctp_address_matches(tmp->local_addr, saddr)))
62873c61845SJeremy Kerr 			continue;
62973c61845SJeremy Kerr 
63073c61845SJeremy Kerr 		spin_lock(&tmp->lock);
63173c61845SJeremy Kerr 		/* key must still be valid. If we find a match, clear the
63273c61845SJeremy Kerr 		 * potential tag value
63373c61845SJeremy Kerr 		 */
63473c61845SJeremy Kerr 		if (tmp->valid)
635833ef3b9SJeremy Kerr 			tagbits &= ~(1 << tmp->tag);
63673c61845SJeremy Kerr 		spin_unlock(&tmp->lock);
637833ef3b9SJeremy Kerr 
638833ef3b9SJeremy Kerr 		if (!tagbits)
639833ef3b9SJeremy Kerr 			break;
640833ef3b9SJeremy Kerr 	}
641833ef3b9SJeremy Kerr 
642833ef3b9SJeremy Kerr 	if (tagbits) {
643833ef3b9SJeremy Kerr 		key->tag = __ffs(tagbits);
644833ef3b9SJeremy Kerr 		mctp_reserve_tag(net, key, msk);
6454f9e1ba6SJeremy Kerr 		trace_mctp_key_acquire(key);
6464f9e1ba6SJeremy Kerr 
64763ed1aabSMatt Johnston 		key->manual_alloc = manual;
648833ef3b9SJeremy Kerr 		*tagp = key->tag;
649833ef3b9SJeremy Kerr 	}
650833ef3b9SJeremy Kerr 
651833ef3b9SJeremy Kerr 	spin_unlock_irqrestore(&mns->keys_lock, flags);
652833ef3b9SJeremy Kerr 
653212c10c3SJeremy Kerr 	if (!tagbits) {
654833ef3b9SJeremy Kerr 		kfree(key);
655212c10c3SJeremy Kerr 		return ERR_PTR(-EBUSY);
656212c10c3SJeremy Kerr 	}
657833ef3b9SJeremy Kerr 
658212c10c3SJeremy Kerr 	return key;
659833ef3b9SJeremy Kerr }
660833ef3b9SJeremy Kerr 
66163ed1aabSMatt Johnston static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
66263ed1aabSMatt Johnston 						    mctp_eid_t daddr,
66363ed1aabSMatt Johnston 						    u8 req_tag, u8 *tagp)
66463ed1aabSMatt Johnston {
66563ed1aabSMatt Johnston 	struct net *net = sock_net(&msk->sk);
66663ed1aabSMatt Johnston 	struct netns_mctp *mns = &net->mctp;
66763ed1aabSMatt Johnston 	struct mctp_sk_key *key, *tmp;
66863ed1aabSMatt Johnston 	unsigned long flags;
66963ed1aabSMatt Johnston 
67063ed1aabSMatt Johnston 	req_tag &= ~(MCTP_TAG_PREALLOC | MCTP_TAG_OWNER);
67163ed1aabSMatt Johnston 	key = NULL;
67263ed1aabSMatt Johnston 
67363ed1aabSMatt Johnston 	spin_lock_irqsave(&mns->keys_lock, flags);
67463ed1aabSMatt Johnston 
67563ed1aabSMatt Johnston 	hlist_for_each_entry(tmp, &mns->keys, hlist) {
67663ed1aabSMatt Johnston 		if (tmp->tag != req_tag)
67763ed1aabSMatt Johnston 			continue;
67863ed1aabSMatt Johnston 
67963ed1aabSMatt Johnston 		if (!mctp_address_matches(tmp->peer_addr, daddr))
68063ed1aabSMatt Johnston 			continue;
68163ed1aabSMatt Johnston 
68263ed1aabSMatt Johnston 		if (!tmp->manual_alloc)
68363ed1aabSMatt Johnston 			continue;
68463ed1aabSMatt Johnston 
68563ed1aabSMatt Johnston 		spin_lock(&tmp->lock);
68663ed1aabSMatt Johnston 		if (tmp->valid) {
68763ed1aabSMatt Johnston 			key = tmp;
68863ed1aabSMatt Johnston 			refcount_inc(&key->refs);
68963ed1aabSMatt Johnston 			spin_unlock(&tmp->lock);
69063ed1aabSMatt Johnston 			break;
69163ed1aabSMatt Johnston 		}
69263ed1aabSMatt Johnston 		spin_unlock(&tmp->lock);
69363ed1aabSMatt Johnston 	}
69463ed1aabSMatt Johnston 	spin_unlock_irqrestore(&mns->keys_lock, flags);
69563ed1aabSMatt Johnston 
69663ed1aabSMatt Johnston 	if (!key)
69763ed1aabSMatt Johnston 		return ERR_PTR(-ENOENT);
69863ed1aabSMatt Johnston 
69963ed1aabSMatt Johnston 	if (tagp)
70063ed1aabSMatt Johnston 		*tagp = key->tag;
70163ed1aabSMatt Johnston 
70263ed1aabSMatt Johnston 	return key;
70363ed1aabSMatt Johnston }
70463ed1aabSMatt Johnston 
705889b7da2SJeremy Kerr /* routing lookups */
706889b7da2SJeremy Kerr static bool mctp_rt_match_eid(struct mctp_route *rt,
707889b7da2SJeremy Kerr 			      unsigned int net, mctp_eid_t eid)
708889b7da2SJeremy Kerr {
709889b7da2SJeremy Kerr 	return READ_ONCE(rt->dev->net) == net &&
710889b7da2SJeremy Kerr 		rt->min <= eid && rt->max >= eid;
711889b7da2SJeremy Kerr }
712889b7da2SJeremy Kerr 
713889b7da2SJeremy Kerr /* compares match, used for duplicate prevention */
714889b7da2SJeremy Kerr static bool mctp_rt_compare_exact(struct mctp_route *rt1,
715889b7da2SJeremy Kerr 				  struct mctp_route *rt2)
716889b7da2SJeremy Kerr {
717889b7da2SJeremy Kerr 	ASSERT_RTNL();
718889b7da2SJeremy Kerr 	return rt1->dev->net == rt2->dev->net &&
719889b7da2SJeremy Kerr 		rt1->min == rt2->min &&
720889b7da2SJeremy Kerr 		rt1->max == rt2->max;
721889b7da2SJeremy Kerr }
722889b7da2SJeremy Kerr 
723889b7da2SJeremy Kerr struct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet,
724889b7da2SJeremy Kerr 				     mctp_eid_t daddr)
725889b7da2SJeremy Kerr {
726889b7da2SJeremy Kerr 	struct mctp_route *tmp, *rt = NULL;
727889b7da2SJeremy Kerr 
728889b7da2SJeremy Kerr 	list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
729889b7da2SJeremy Kerr 		/* TODO: add metrics */
730889b7da2SJeremy Kerr 		if (mctp_rt_match_eid(tmp, dnet, daddr)) {
731889b7da2SJeremy Kerr 			if (refcount_inc_not_zero(&tmp->refs)) {
732889b7da2SJeremy Kerr 				rt = tmp;
733889b7da2SJeremy Kerr 				break;
734889b7da2SJeremy Kerr 			}
735889b7da2SJeremy Kerr 		}
736889b7da2SJeremy Kerr 	}
737889b7da2SJeremy Kerr 
738889b7da2SJeremy Kerr 	return rt;
739889b7da2SJeremy Kerr }
740889b7da2SJeremy Kerr 
7411f6c77acSJeremy Kerr static struct mctp_route *mctp_route_lookup_null(struct net *net,
7421f6c77acSJeremy Kerr 						 struct net_device *dev)
7431f6c77acSJeremy Kerr {
7441f6c77acSJeremy Kerr 	struct mctp_route *rt;
7451f6c77acSJeremy Kerr 
7461f6c77acSJeremy Kerr 	list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
7471f6c77acSJeremy Kerr 		if (rt->dev->dev == dev && rt->type == RTN_LOCAL &&
7481f6c77acSJeremy Kerr 		    refcount_inc_not_zero(&rt->refs))
7491f6c77acSJeremy Kerr 			return rt;
7501f6c77acSJeremy Kerr 	}
7511f6c77acSJeremy Kerr 
7521f6c77acSJeremy Kerr 	return NULL;
7531f6c77acSJeremy Kerr }
7541f6c77acSJeremy Kerr 
7554a992bbdSJeremy Kerr static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
7564a992bbdSJeremy Kerr 				  unsigned int mtu, u8 tag)
7574a992bbdSJeremy Kerr {
7584a992bbdSJeremy Kerr 	const unsigned int hlen = sizeof(struct mctp_hdr);
7594a992bbdSJeremy Kerr 	struct mctp_hdr *hdr, *hdr2;
7604a992bbdSJeremy Kerr 	unsigned int pos, size;
7614a992bbdSJeremy Kerr 	struct sk_buff *skb2;
7624a992bbdSJeremy Kerr 	int rc;
7634a992bbdSJeremy Kerr 	u8 seq;
7644a992bbdSJeremy Kerr 
7654a992bbdSJeremy Kerr 	hdr = mctp_hdr(skb);
7664a992bbdSJeremy Kerr 	seq = 0;
7674a992bbdSJeremy Kerr 	rc = 0;
7684a992bbdSJeremy Kerr 
7694a992bbdSJeremy Kerr 	if (mtu < hlen + 1) {
7704a992bbdSJeremy Kerr 		kfree_skb(skb);
7714a992bbdSJeremy Kerr 		return -EMSGSIZE;
7724a992bbdSJeremy Kerr 	}
7734a992bbdSJeremy Kerr 
7744a992bbdSJeremy Kerr 	/* we've got the header */
7754a992bbdSJeremy Kerr 	skb_pull(skb, hlen);
7764a992bbdSJeremy Kerr 
7774a992bbdSJeremy Kerr 	for (pos = 0; pos < skb->len;) {
7784a992bbdSJeremy Kerr 		/* size of message payload */
7794a992bbdSJeremy Kerr 		size = min(mtu - hlen, skb->len - pos);
7804a992bbdSJeremy Kerr 
7814a992bbdSJeremy Kerr 		skb2 = alloc_skb(MCTP_HEADER_MAXLEN + hlen + size, GFP_KERNEL);
7824a992bbdSJeremy Kerr 		if (!skb2) {
7834a992bbdSJeremy Kerr 			rc = -ENOMEM;
7844a992bbdSJeremy Kerr 			break;
7854a992bbdSJeremy Kerr 		}
7864a992bbdSJeremy Kerr 
7874a992bbdSJeremy Kerr 		/* generic skb copy */
7884a992bbdSJeremy Kerr 		skb2->protocol = skb->protocol;
7894a992bbdSJeremy Kerr 		skb2->priority = skb->priority;
7904a992bbdSJeremy Kerr 		skb2->dev = skb->dev;
7914a992bbdSJeremy Kerr 		memcpy(skb2->cb, skb->cb, sizeof(skb2->cb));
7924a992bbdSJeremy Kerr 
7934a992bbdSJeremy Kerr 		if (skb->sk)
7944a992bbdSJeremy Kerr 			skb_set_owner_w(skb2, skb->sk);
7954a992bbdSJeremy Kerr 
7964a992bbdSJeremy Kerr 		/* establish packet */
7974a992bbdSJeremy Kerr 		skb_reserve(skb2, MCTP_HEADER_MAXLEN);
7984a992bbdSJeremy Kerr 		skb_reset_network_header(skb2);
7994a992bbdSJeremy Kerr 		skb_put(skb2, hlen + size);
8004a992bbdSJeremy Kerr 		skb2->transport_header = skb2->network_header + hlen;
8014a992bbdSJeremy Kerr 
8024a992bbdSJeremy Kerr 		/* copy header fields, calculate SOM/EOM flags & seq */
8034a992bbdSJeremy Kerr 		hdr2 = mctp_hdr(skb2);
8044a992bbdSJeremy Kerr 		hdr2->ver = hdr->ver;
8054a992bbdSJeremy Kerr 		hdr2->dest = hdr->dest;
8064a992bbdSJeremy Kerr 		hdr2->src = hdr->src;
8074a992bbdSJeremy Kerr 		hdr2->flags_seq_tag = tag &
8084a992bbdSJeremy Kerr 			(MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
8094a992bbdSJeremy Kerr 
8104a992bbdSJeremy Kerr 		if (pos == 0)
8114a992bbdSJeremy Kerr 			hdr2->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
8124a992bbdSJeremy Kerr 
8134a992bbdSJeremy Kerr 		if (pos + size == skb->len)
8144a992bbdSJeremy Kerr 			hdr2->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
8154a992bbdSJeremy Kerr 
8164a992bbdSJeremy Kerr 		hdr2->flags_seq_tag |= seq << MCTP_HDR_SEQ_SHIFT;
8174a992bbdSJeremy Kerr 
8184a992bbdSJeremy Kerr 		/* copy message payload */
8194a992bbdSJeremy Kerr 		skb_copy_bits(skb, pos, skb_transport_header(skb2), size);
8204a992bbdSJeremy Kerr 
82199ce45d5SJeremy Kerr 		/* do route */
8224a992bbdSJeremy Kerr 		rc = rt->output(rt, skb2);
8234a992bbdSJeremy Kerr 		if (rc)
8244a992bbdSJeremy Kerr 			break;
8254a992bbdSJeremy Kerr 
8264a992bbdSJeremy Kerr 		seq = (seq + 1) & MCTP_HDR_SEQ_MASK;
8274a992bbdSJeremy Kerr 		pos += size;
8284a992bbdSJeremy Kerr 	}
8294a992bbdSJeremy Kerr 
8304a992bbdSJeremy Kerr 	consume_skb(skb);
8314a992bbdSJeremy Kerr 	return rc;
8324a992bbdSJeremy Kerr }
8334a992bbdSJeremy Kerr 
834889b7da2SJeremy Kerr int mctp_local_output(struct sock *sk, struct mctp_route *rt,
835889b7da2SJeremy Kerr 		      struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag)
836889b7da2SJeremy Kerr {
837833ef3b9SJeremy Kerr 	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
838889b7da2SJeremy Kerr 	struct mctp_skb_cb *cb = mctp_cb(skb);
839dc121c00SMatt Johnston 	struct mctp_route tmp_rt = {0};
840212c10c3SJeremy Kerr 	struct mctp_sk_key *key;
841889b7da2SJeremy Kerr 	struct mctp_hdr *hdr;
842889b7da2SJeremy Kerr 	unsigned long flags;
8434a992bbdSJeremy Kerr 	unsigned int mtu;
844889b7da2SJeremy Kerr 	mctp_eid_t saddr;
84599ce45d5SJeremy Kerr 	bool ext_rt;
846889b7da2SJeremy Kerr 	int rc;
847833ef3b9SJeremy Kerr 	u8 tag;
848889b7da2SJeremy Kerr 
84999ce45d5SJeremy Kerr 	rc = -ENODEV;
85099ce45d5SJeremy Kerr 
85199ce45d5SJeremy Kerr 	if (rt) {
85299ce45d5SJeremy Kerr 		ext_rt = false;
853889b7da2SJeremy Kerr 		if (WARN_ON(!rt->dev))
85499ce45d5SJeremy Kerr 			goto out_release;
85599ce45d5SJeremy Kerr 
85699ce45d5SJeremy Kerr 	} else if (cb->ifindex) {
857*e297db3eSMatt Johnston 		struct net_device *dev;
858*e297db3eSMatt Johnston 
85999ce45d5SJeremy Kerr 		ext_rt = true;
86099ce45d5SJeremy Kerr 		rt = &tmp_rt;
86199ce45d5SJeremy Kerr 
86299ce45d5SJeremy Kerr 		rcu_read_lock();
86399ce45d5SJeremy Kerr 		dev = dev_get_by_index_rcu(sock_net(sk), cb->ifindex);
86499ce45d5SJeremy Kerr 		if (!dev) {
86599ce45d5SJeremy Kerr 			rcu_read_unlock();
86699ce45d5SJeremy Kerr 			return rc;
86799ce45d5SJeremy Kerr 		}
86899ce45d5SJeremy Kerr 		rt->dev = __mctp_dev_get(dev);
86999ce45d5SJeremy Kerr 		rcu_read_unlock();
87099ce45d5SJeremy Kerr 
87199ce45d5SJeremy Kerr 		if (!rt->dev)
87299ce45d5SJeremy Kerr 			goto out_release;
87399ce45d5SJeremy Kerr 
87499ce45d5SJeremy Kerr 		/* establish temporary route - we set up enough to keep
87599ce45d5SJeremy Kerr 		 * mctp_route_output happy
87699ce45d5SJeremy Kerr 		 */
87799ce45d5SJeremy Kerr 		rt->output = mctp_route_output;
87899ce45d5SJeremy Kerr 		rt->mtu = 0;
87999ce45d5SJeremy Kerr 
88099ce45d5SJeremy Kerr 	} else {
881889b7da2SJeremy Kerr 		return -EINVAL;
88299ce45d5SJeremy Kerr 	}
883889b7da2SJeremy Kerr 
884889b7da2SJeremy Kerr 	spin_lock_irqsave(&rt->dev->addrs_lock, flags);
885889b7da2SJeremy Kerr 	if (rt->dev->num_addrs == 0) {
886889b7da2SJeremy Kerr 		rc = -EHOSTUNREACH;
887889b7da2SJeremy Kerr 	} else {
888889b7da2SJeremy Kerr 		/* use the outbound interface's first address as our source */
889889b7da2SJeremy Kerr 		saddr = rt->dev->addrs[0];
890889b7da2SJeremy Kerr 		rc = 0;
891889b7da2SJeremy Kerr 	}
892889b7da2SJeremy Kerr 	spin_unlock_irqrestore(&rt->dev->addrs_lock, flags);
893889b7da2SJeremy Kerr 
894889b7da2SJeremy Kerr 	if (rc)
89599ce45d5SJeremy Kerr 		goto out_release;
896889b7da2SJeremy Kerr 
89763ed1aabSMatt Johnston 	if (req_tag & MCTP_TAG_OWNER) {
89863ed1aabSMatt Johnston 		if (req_tag & MCTP_TAG_PREALLOC)
89963ed1aabSMatt Johnston 			key = mctp_lookup_prealloc_tag(msk, daddr,
90063ed1aabSMatt Johnston 						       req_tag, &tag);
90163ed1aabSMatt Johnston 		else
90263ed1aabSMatt Johnston 			key = mctp_alloc_local_tag(msk, daddr, saddr,
90363ed1aabSMatt Johnston 						   false, &tag);
90463ed1aabSMatt Johnston 
905212c10c3SJeremy Kerr 		if (IS_ERR(key)) {
906212c10c3SJeremy Kerr 			rc = PTR_ERR(key);
90799ce45d5SJeremy Kerr 			goto out_release;
908212c10c3SJeremy Kerr 		}
90967737c45SJeremy Kerr 		mctp_skb_set_flow(skb, key);
910212c10c3SJeremy Kerr 		/* done with the key in this scope */
911212c10c3SJeremy Kerr 		mctp_key_unref(key);
912833ef3b9SJeremy Kerr 		tag |= MCTP_HDR_FLAG_TO;
913833ef3b9SJeremy Kerr 	} else {
914212c10c3SJeremy Kerr 		key = NULL;
91563ed1aabSMatt Johnston 		tag = req_tag & MCTP_TAG_MASK;
916833ef3b9SJeremy Kerr 	}
917833ef3b9SJeremy Kerr 
9184a992bbdSJeremy Kerr 	skb->protocol = htons(ETH_P_MCTP);
9194a992bbdSJeremy Kerr 	skb->priority = 0;
920889b7da2SJeremy Kerr 	skb_reset_transport_header(skb);
921889b7da2SJeremy Kerr 	skb_push(skb, sizeof(struct mctp_hdr));
922889b7da2SJeremy Kerr 	skb_reset_network_header(skb);
923833ef3b9SJeremy Kerr 	skb->dev = rt->dev->dev;
924889b7da2SJeremy Kerr 
925889b7da2SJeremy Kerr 	/* cb->net will have been set on initial ingress */
926889b7da2SJeremy Kerr 	cb->src = saddr;
927889b7da2SJeremy Kerr 
9284a992bbdSJeremy Kerr 	/* set up common header fields */
9294a992bbdSJeremy Kerr 	hdr = mctp_hdr(skb);
9304a992bbdSJeremy Kerr 	hdr->ver = 1;
9314a992bbdSJeremy Kerr 	hdr->dest = daddr;
9324a992bbdSJeremy Kerr 	hdr->src = saddr;
9334a992bbdSJeremy Kerr 
9344a992bbdSJeremy Kerr 	mtu = mctp_route_mtu(rt);
9354a992bbdSJeremy Kerr 
9364a992bbdSJeremy Kerr 	if (skb->len + sizeof(struct mctp_hdr) <= mtu) {
93799ce45d5SJeremy Kerr 		hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
93899ce45d5SJeremy Kerr 			MCTP_HDR_FLAG_EOM | tag;
93999ce45d5SJeremy Kerr 		rc = rt->output(rt, skb);
9404a992bbdSJeremy Kerr 	} else {
94199ce45d5SJeremy Kerr 		rc = mctp_do_fragment_route(rt, skb, mtu, tag);
9424a992bbdSJeremy Kerr 	}
94399ce45d5SJeremy Kerr 
94499ce45d5SJeremy Kerr out_release:
94599ce45d5SJeremy Kerr 	if (!ext_rt)
94699ce45d5SJeremy Kerr 		mctp_route_release(rt);
94799ce45d5SJeremy Kerr 
948dc121c00SMatt Johnston 	mctp_dev_put(tmp_rt.dev);
94999ce45d5SJeremy Kerr 
95099ce45d5SJeremy Kerr 	return rc;
951889b7da2SJeremy Kerr }
952889b7da2SJeremy Kerr 
953889b7da2SJeremy Kerr /* route management */
95406d2f4c5SMatt Johnston static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start,
95506d2f4c5SMatt Johnston 			  unsigned int daddr_extent, unsigned int mtu,
95683f0a0b7SJeremy Kerr 			  unsigned char type)
957889b7da2SJeremy Kerr {
95883f0a0b7SJeremy Kerr 	int (*rtfn)(struct mctp_route *rt, struct sk_buff *skb);
959889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
960889b7da2SJeremy Kerr 	struct mctp_route *rt, *ert;
961889b7da2SJeremy Kerr 
962cb196b72SJeremy Kerr 	if (!mctp_address_unicast(daddr_start))
96306d2f4c5SMatt Johnston 		return -EINVAL;
96406d2f4c5SMatt Johnston 
96506d2f4c5SMatt Johnston 	if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
96606d2f4c5SMatt Johnston 		return -EINVAL;
96706d2f4c5SMatt Johnston 
96883f0a0b7SJeremy Kerr 	switch (type) {
96983f0a0b7SJeremy Kerr 	case RTN_LOCAL:
97083f0a0b7SJeremy Kerr 		rtfn = mctp_route_input;
97183f0a0b7SJeremy Kerr 		break;
97283f0a0b7SJeremy Kerr 	case RTN_UNICAST:
97383f0a0b7SJeremy Kerr 		rtfn = mctp_route_output;
97483f0a0b7SJeremy Kerr 		break;
97583f0a0b7SJeremy Kerr 	default:
97683f0a0b7SJeremy Kerr 		return -EINVAL;
97783f0a0b7SJeremy Kerr 	}
97883f0a0b7SJeremy Kerr 
979889b7da2SJeremy Kerr 	rt = mctp_route_alloc();
980889b7da2SJeremy Kerr 	if (!rt)
981889b7da2SJeremy Kerr 		return -ENOMEM;
982889b7da2SJeremy Kerr 
98306d2f4c5SMatt Johnston 	rt->min = daddr_start;
98406d2f4c5SMatt Johnston 	rt->max = daddr_start + daddr_extent;
98506d2f4c5SMatt Johnston 	rt->mtu = mtu;
986889b7da2SJeremy Kerr 	rt->dev = mdev;
98743f55f23SJeremy Kerr 	mctp_dev_hold(rt->dev);
98883f0a0b7SJeremy Kerr 	rt->type = type;
98983f0a0b7SJeremy Kerr 	rt->output = rtfn;
990889b7da2SJeremy Kerr 
991889b7da2SJeremy Kerr 	ASSERT_RTNL();
992889b7da2SJeremy Kerr 	/* Prevent duplicate identical routes. */
993889b7da2SJeremy Kerr 	list_for_each_entry(ert, &net->mctp.routes, list) {
994889b7da2SJeremy Kerr 		if (mctp_rt_compare_exact(rt, ert)) {
995889b7da2SJeremy Kerr 			mctp_route_release(rt);
996889b7da2SJeremy Kerr 			return -EEXIST;
997889b7da2SJeremy Kerr 		}
998889b7da2SJeremy Kerr 	}
999889b7da2SJeremy Kerr 
1000889b7da2SJeremy Kerr 	list_add_rcu(&rt->list, &net->mctp.routes);
1001889b7da2SJeremy Kerr 
1002889b7da2SJeremy Kerr 	return 0;
1003889b7da2SJeremy Kerr }
1004889b7da2SJeremy Kerr 
100506d2f4c5SMatt Johnston static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
100676d00160SMatt Johnston 			     unsigned int daddr_extent, unsigned char type)
1007889b7da2SJeremy Kerr {
1008889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
1009889b7da2SJeremy Kerr 	struct mctp_route *rt, *tmp;
101006d2f4c5SMatt Johnston 	mctp_eid_t daddr_end;
101106d2f4c5SMatt Johnston 	bool dropped;
101206d2f4c5SMatt Johnston 
101306d2f4c5SMatt Johnston 	if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
101406d2f4c5SMatt Johnston 		return -EINVAL;
101506d2f4c5SMatt Johnston 
101606d2f4c5SMatt Johnston 	daddr_end = daddr_start + daddr_extent;
101706d2f4c5SMatt Johnston 	dropped = false;
1018889b7da2SJeremy Kerr 
1019889b7da2SJeremy Kerr 	ASSERT_RTNL();
1020889b7da2SJeremy Kerr 
1021889b7da2SJeremy Kerr 	list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
102206d2f4c5SMatt Johnston 		if (rt->dev == mdev &&
102376d00160SMatt Johnston 		    rt->min == daddr_start && rt->max == daddr_end &&
102476d00160SMatt Johnston 		    rt->type == type) {
1025889b7da2SJeremy Kerr 			list_del_rcu(&rt->list);
1026889b7da2SJeremy Kerr 			/* TODO: immediate RTM_DELROUTE */
1027889b7da2SJeremy Kerr 			mctp_route_release(rt);
102806d2f4c5SMatt Johnston 			dropped = true;
1029889b7da2SJeremy Kerr 		}
1030889b7da2SJeremy Kerr 	}
1031889b7da2SJeremy Kerr 
103206d2f4c5SMatt Johnston 	return dropped ? 0 : -ENOENT;
103306d2f4c5SMatt Johnston }
103406d2f4c5SMatt Johnston 
103506d2f4c5SMatt Johnston int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr)
103606d2f4c5SMatt Johnston {
103783f0a0b7SJeremy Kerr 	return mctp_route_add(mdev, addr, 0, 0, RTN_LOCAL);
103806d2f4c5SMatt Johnston }
103906d2f4c5SMatt Johnston 
104006d2f4c5SMatt Johnston int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr)
104106d2f4c5SMatt Johnston {
104276d00160SMatt Johnston 	return mctp_route_remove(mdev, addr, 0, RTN_LOCAL);
1043889b7da2SJeremy Kerr }
1044889b7da2SJeremy Kerr 
1045889b7da2SJeremy Kerr /* removes all entries for a given device */
1046889b7da2SJeremy Kerr void mctp_route_remove_dev(struct mctp_dev *mdev)
1047889b7da2SJeremy Kerr {
1048889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
1049889b7da2SJeremy Kerr 	struct mctp_route *rt, *tmp;
1050889b7da2SJeremy Kerr 
1051889b7da2SJeremy Kerr 	ASSERT_RTNL();
1052889b7da2SJeremy Kerr 	list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
1053889b7da2SJeremy Kerr 		if (rt->dev == mdev) {
1054889b7da2SJeremy Kerr 			list_del_rcu(&rt->list);
1055889b7da2SJeremy Kerr 			/* TODO: immediate RTM_DELROUTE */
1056889b7da2SJeremy Kerr 			mctp_route_release(rt);
1057889b7da2SJeremy Kerr 		}
1058889b7da2SJeremy Kerr 	}
1059889b7da2SJeremy Kerr }
1060889b7da2SJeremy Kerr 
1061889b7da2SJeremy Kerr /* Incoming packet-handling */
1062889b7da2SJeremy Kerr 
1063889b7da2SJeremy Kerr static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
1064889b7da2SJeremy Kerr 				struct packet_type *pt,
1065889b7da2SJeremy Kerr 				struct net_device *orig_dev)
1066889b7da2SJeremy Kerr {
1067889b7da2SJeremy Kerr 	struct net *net = dev_net(dev);
1068f364dd71SMatt Johnston 	struct mctp_dev *mdev;
1069889b7da2SJeremy Kerr 	struct mctp_skb_cb *cb;
1070889b7da2SJeremy Kerr 	struct mctp_route *rt;
1071889b7da2SJeremy Kerr 	struct mctp_hdr *mh;
1072889b7da2SJeremy Kerr 
1073f364dd71SMatt Johnston 	rcu_read_lock();
1074f364dd71SMatt Johnston 	mdev = __mctp_dev_get(dev);
1075f364dd71SMatt Johnston 	rcu_read_unlock();
1076f364dd71SMatt Johnston 	if (!mdev) {
1077889b7da2SJeremy Kerr 		/* basic non-data sanity checks */
1078889b7da2SJeremy Kerr 		goto err_drop;
1079f364dd71SMatt Johnston 	}
1080889b7da2SJeremy Kerr 
1081889b7da2SJeremy Kerr 	if (!pskb_may_pull(skb, sizeof(struct mctp_hdr)))
1082889b7da2SJeremy Kerr 		goto err_drop;
1083889b7da2SJeremy Kerr 
1084889b7da2SJeremy Kerr 	skb_reset_transport_header(skb);
1085889b7da2SJeremy Kerr 	skb_reset_network_header(skb);
1086889b7da2SJeremy Kerr 
1087889b7da2SJeremy Kerr 	/* We have enough for a header; decode and route */
1088889b7da2SJeremy Kerr 	mh = mctp_hdr(skb);
1089889b7da2SJeremy Kerr 	if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX)
1090889b7da2SJeremy Kerr 		goto err_drop;
1091889b7da2SJeremy Kerr 
109286cdfd63SJeremy Kerr 	/* source must be valid unicast or null; drop reserved ranges and
109386cdfd63SJeremy Kerr 	 * broadcast
109486cdfd63SJeremy Kerr 	 */
109586cdfd63SJeremy Kerr 	if (!(mctp_address_unicast(mh->src) || mctp_address_null(mh->src)))
109686cdfd63SJeremy Kerr 		goto err_drop;
109786cdfd63SJeremy Kerr 
109886cdfd63SJeremy Kerr 	/* dest address: as above, but allow broadcast */
109986cdfd63SJeremy Kerr 	if (!(mctp_address_unicast(mh->dest) || mctp_address_null(mh->dest) ||
110086cdfd63SJeremy Kerr 	      mctp_address_broadcast(mh->dest)))
110186cdfd63SJeremy Kerr 		goto err_drop;
110286cdfd63SJeremy Kerr 
110399ce45d5SJeremy Kerr 	/* MCTP drivers must populate halen/haddr */
110499ce45d5SJeremy Kerr 	if (dev->type == ARPHRD_MCTP) {
110599ce45d5SJeremy Kerr 		cb = mctp_cb(skb);
110699ce45d5SJeremy Kerr 	} else {
1107889b7da2SJeremy Kerr 		cb = __mctp_cb(skb);
110899ce45d5SJeremy Kerr 		cb->halen = 0;
110999ce45d5SJeremy Kerr 	}
1110f364dd71SMatt Johnston 	cb->net = READ_ONCE(mdev->net);
111199ce45d5SJeremy Kerr 	cb->ifindex = dev->ifindex;
1112889b7da2SJeremy Kerr 
1113889b7da2SJeremy Kerr 	rt = mctp_route_lookup(net, cb->net, mh->dest);
11141f6c77acSJeremy Kerr 
11151f6c77acSJeremy Kerr 	/* NULL EID, but addressed to our physical address */
11161f6c77acSJeremy Kerr 	if (!rt && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
11171f6c77acSJeremy Kerr 		rt = mctp_route_lookup_null(net, dev);
11181f6c77acSJeremy Kerr 
1119889b7da2SJeremy Kerr 	if (!rt)
1120889b7da2SJeremy Kerr 		goto err_drop;
1121889b7da2SJeremy Kerr 
112299ce45d5SJeremy Kerr 	rt->output(rt, skb);
112399ce45d5SJeremy Kerr 	mctp_route_release(rt);
1124dc121c00SMatt Johnston 	mctp_dev_put(mdev);
1125889b7da2SJeremy Kerr 
1126889b7da2SJeremy Kerr 	return NET_RX_SUCCESS;
1127889b7da2SJeremy Kerr 
1128889b7da2SJeremy Kerr err_drop:
1129889b7da2SJeremy Kerr 	kfree_skb(skb);
1130dc121c00SMatt Johnston 	mctp_dev_put(mdev);
1131889b7da2SJeremy Kerr 	return NET_RX_DROP;
1132889b7da2SJeremy Kerr }
1133889b7da2SJeremy Kerr 
1134889b7da2SJeremy Kerr static struct packet_type mctp_packet_type = {
1135889b7da2SJeremy Kerr 	.type = cpu_to_be16(ETH_P_MCTP),
1136889b7da2SJeremy Kerr 	.func = mctp_pkttype_receive,
1137889b7da2SJeremy Kerr };
1138889b7da2SJeremy Kerr 
113906d2f4c5SMatt Johnston /* netlink interface */
114006d2f4c5SMatt Johnston 
114106d2f4c5SMatt Johnston static const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = {
114206d2f4c5SMatt Johnston 	[RTA_DST]		= { .type = NLA_U8 },
114306d2f4c5SMatt Johnston 	[RTA_METRICS]		= { .type = NLA_NESTED },
114406d2f4c5SMatt Johnston 	[RTA_OIF]		= { .type = NLA_U32 },
114506d2f4c5SMatt Johnston };
114606d2f4c5SMatt Johnston 
114706d2f4c5SMatt Johnston /* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing.
114806d2f4c5SMatt Johnston  * tb must hold RTA_MAX+1 elements.
114906d2f4c5SMatt Johnston  */
115006d2f4c5SMatt Johnston static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
115106d2f4c5SMatt Johnston 			      struct netlink_ext_ack *extack,
115206d2f4c5SMatt Johnston 			      struct nlattr **tb, struct rtmsg **rtm,
115306d2f4c5SMatt Johnston 			      struct mctp_dev **mdev, mctp_eid_t *daddr_start)
115406d2f4c5SMatt Johnston {
115506d2f4c5SMatt Johnston 	struct net *net = sock_net(skb->sk);
115606d2f4c5SMatt Johnston 	struct net_device *dev;
115706d2f4c5SMatt Johnston 	unsigned int ifindex;
115806d2f4c5SMatt Johnston 	int rc;
115906d2f4c5SMatt Johnston 
116006d2f4c5SMatt Johnston 	rc = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
116106d2f4c5SMatt Johnston 			 rta_mctp_policy, extack);
116206d2f4c5SMatt Johnston 	if (rc < 0) {
116306d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "incorrect format");
116406d2f4c5SMatt Johnston 		return rc;
116506d2f4c5SMatt Johnston 	}
116606d2f4c5SMatt Johnston 
116706d2f4c5SMatt Johnston 	if (!tb[RTA_DST]) {
116806d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "dst EID missing");
116906d2f4c5SMatt Johnston 		return -EINVAL;
117006d2f4c5SMatt Johnston 	}
117106d2f4c5SMatt Johnston 	*daddr_start = nla_get_u8(tb[RTA_DST]);
117206d2f4c5SMatt Johnston 
117306d2f4c5SMatt Johnston 	if (!tb[RTA_OIF]) {
117406d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "ifindex missing");
117506d2f4c5SMatt Johnston 		return -EINVAL;
117606d2f4c5SMatt Johnston 	}
117706d2f4c5SMatt Johnston 	ifindex = nla_get_u32(tb[RTA_OIF]);
117806d2f4c5SMatt Johnston 
117906d2f4c5SMatt Johnston 	*rtm = nlmsg_data(nlh);
118006d2f4c5SMatt Johnston 	if ((*rtm)->rtm_family != AF_MCTP) {
118106d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "route family must be AF_MCTP");
118206d2f4c5SMatt Johnston 		return -EINVAL;
118306d2f4c5SMatt Johnston 	}
118406d2f4c5SMatt Johnston 
118506d2f4c5SMatt Johnston 	dev = __dev_get_by_index(net, ifindex);
118606d2f4c5SMatt Johnston 	if (!dev) {
118706d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "bad ifindex");
118806d2f4c5SMatt Johnston 		return -ENODEV;
118906d2f4c5SMatt Johnston 	}
119006d2f4c5SMatt Johnston 	*mdev = mctp_dev_get_rtnl(dev);
119106d2f4c5SMatt Johnston 	if (!*mdev)
119206d2f4c5SMatt Johnston 		return -ENODEV;
119306d2f4c5SMatt Johnston 
119406d2f4c5SMatt Johnston 	if (dev->flags & IFF_LOOPBACK) {
119506d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "no routes to loopback");
119606d2f4c5SMatt Johnston 		return -EINVAL;
119706d2f4c5SMatt Johnston 	}
119806d2f4c5SMatt Johnston 
119906d2f4c5SMatt Johnston 	return 0;
120006d2f4c5SMatt Johnston }
120106d2f4c5SMatt Johnston 
12026183569dSMatt Johnston static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
12036183569dSMatt Johnston 	[RTAX_MTU]		= { .type = NLA_U32 },
12046183569dSMatt Johnston };
12056183569dSMatt Johnston 
120606d2f4c5SMatt Johnston static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
120706d2f4c5SMatt Johnston 			 struct netlink_ext_ack *extack)
120806d2f4c5SMatt Johnston {
120906d2f4c5SMatt Johnston 	struct nlattr *tb[RTA_MAX + 1];
12106183569dSMatt Johnston 	struct nlattr *tbx[RTAX_MAX + 1];
121106d2f4c5SMatt Johnston 	mctp_eid_t daddr_start;
121206d2f4c5SMatt Johnston 	struct mctp_dev *mdev;
121306d2f4c5SMatt Johnston 	struct rtmsg *rtm;
121406d2f4c5SMatt Johnston 	unsigned int mtu;
121506d2f4c5SMatt Johnston 	int rc;
121606d2f4c5SMatt Johnston 
121706d2f4c5SMatt Johnston 	rc = mctp_route_nlparse(skb, nlh, extack, tb,
121806d2f4c5SMatt Johnston 				&rtm, &mdev, &daddr_start);
121906d2f4c5SMatt Johnston 	if (rc < 0)
122006d2f4c5SMatt Johnston 		return rc;
122106d2f4c5SMatt Johnston 
122206d2f4c5SMatt Johnston 	if (rtm->rtm_type != RTN_UNICAST) {
122306d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
122406d2f4c5SMatt Johnston 		return -EINVAL;
122506d2f4c5SMatt Johnston 	}
122606d2f4c5SMatt Johnston 
122706d2f4c5SMatt Johnston 	mtu = 0;
12286183569dSMatt Johnston 	if (tb[RTA_METRICS]) {
12296183569dSMatt Johnston 		rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS],
12306183569dSMatt Johnston 				      rta_metrics_policy, NULL);
12316183569dSMatt Johnston 		if (rc < 0)
12326183569dSMatt Johnston 			return rc;
12336183569dSMatt Johnston 		if (tbx[RTAX_MTU])
12346183569dSMatt Johnston 			mtu = nla_get_u32(tbx[RTAX_MTU]);
12356183569dSMatt Johnston 	}
123606d2f4c5SMatt Johnston 
123783f0a0b7SJeremy Kerr 	if (rtm->rtm_type != RTN_UNICAST)
123883f0a0b7SJeremy Kerr 		return -EINVAL;
123983f0a0b7SJeremy Kerr 
124083f0a0b7SJeremy Kerr 	rc = mctp_route_add(mdev, daddr_start, rtm->rtm_dst_len, mtu,
124183f0a0b7SJeremy Kerr 			    rtm->rtm_type);
124206d2f4c5SMatt Johnston 	return rc;
124306d2f4c5SMatt Johnston }
124406d2f4c5SMatt Johnston 
124506d2f4c5SMatt Johnston static int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
124606d2f4c5SMatt Johnston 			 struct netlink_ext_ack *extack)
124706d2f4c5SMatt Johnston {
124806d2f4c5SMatt Johnston 	struct nlattr *tb[RTA_MAX + 1];
124906d2f4c5SMatt Johnston 	mctp_eid_t daddr_start;
125006d2f4c5SMatt Johnston 	struct mctp_dev *mdev;
125106d2f4c5SMatt Johnston 	struct rtmsg *rtm;
125206d2f4c5SMatt Johnston 	int rc;
125306d2f4c5SMatt Johnston 
125406d2f4c5SMatt Johnston 	rc = mctp_route_nlparse(skb, nlh, extack, tb,
125506d2f4c5SMatt Johnston 				&rtm, &mdev, &daddr_start);
125606d2f4c5SMatt Johnston 	if (rc < 0)
125706d2f4c5SMatt Johnston 		return rc;
125806d2f4c5SMatt Johnston 
125906d2f4c5SMatt Johnston 	/* we only have unicast routes */
126006d2f4c5SMatt Johnston 	if (rtm->rtm_type != RTN_UNICAST)
126106d2f4c5SMatt Johnston 		return -EINVAL;
126206d2f4c5SMatt Johnston 
126376d00160SMatt Johnston 	rc = mctp_route_remove(mdev, daddr_start, rtm->rtm_dst_len, RTN_UNICAST);
126406d2f4c5SMatt Johnston 	return rc;
126506d2f4c5SMatt Johnston }
126606d2f4c5SMatt Johnston 
126706d2f4c5SMatt Johnston static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
126806d2f4c5SMatt Johnston 			    u32 portid, u32 seq, int event, unsigned int flags)
126906d2f4c5SMatt Johnston {
127006d2f4c5SMatt Johnston 	struct nlmsghdr *nlh;
127106d2f4c5SMatt Johnston 	struct rtmsg *hdr;
127206d2f4c5SMatt Johnston 	void *metrics;
127306d2f4c5SMatt Johnston 
127406d2f4c5SMatt Johnston 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags);
127506d2f4c5SMatt Johnston 	if (!nlh)
127606d2f4c5SMatt Johnston 		return -EMSGSIZE;
127706d2f4c5SMatt Johnston 
127806d2f4c5SMatt Johnston 	hdr = nlmsg_data(nlh);
127906d2f4c5SMatt Johnston 	hdr->rtm_family = AF_MCTP;
128006d2f4c5SMatt Johnston 
128106d2f4c5SMatt Johnston 	/* we use the _len fields as a number of EIDs, rather than
128206d2f4c5SMatt Johnston 	 * a number of bits in the address
128306d2f4c5SMatt Johnston 	 */
128406d2f4c5SMatt Johnston 	hdr->rtm_dst_len = rt->max - rt->min;
128506d2f4c5SMatt Johnston 	hdr->rtm_src_len = 0;
128606d2f4c5SMatt Johnston 	hdr->rtm_tos = 0;
128706d2f4c5SMatt Johnston 	hdr->rtm_table = RT_TABLE_DEFAULT;
128806d2f4c5SMatt Johnston 	hdr->rtm_protocol = RTPROT_STATIC; /* everything is user-defined */
128906d2f4c5SMatt Johnston 	hdr->rtm_scope = RT_SCOPE_LINK; /* TODO: scope in mctp_route? */
129083f0a0b7SJeremy Kerr 	hdr->rtm_type = rt->type;
129106d2f4c5SMatt Johnston 
129206d2f4c5SMatt Johnston 	if (nla_put_u8(skb, RTA_DST, rt->min))
129306d2f4c5SMatt Johnston 		goto cancel;
129406d2f4c5SMatt Johnston 
129506d2f4c5SMatt Johnston 	metrics = nla_nest_start_noflag(skb, RTA_METRICS);
129606d2f4c5SMatt Johnston 	if (!metrics)
129706d2f4c5SMatt Johnston 		goto cancel;
129806d2f4c5SMatt Johnston 
129906d2f4c5SMatt Johnston 	if (rt->mtu) {
130006d2f4c5SMatt Johnston 		if (nla_put_u32(skb, RTAX_MTU, rt->mtu))
130106d2f4c5SMatt Johnston 			goto cancel;
130206d2f4c5SMatt Johnston 	}
130306d2f4c5SMatt Johnston 
130406d2f4c5SMatt Johnston 	nla_nest_end(skb, metrics);
130506d2f4c5SMatt Johnston 
130606d2f4c5SMatt Johnston 	if (rt->dev) {
130706d2f4c5SMatt Johnston 		if (nla_put_u32(skb, RTA_OIF, rt->dev->dev->ifindex))
130806d2f4c5SMatt Johnston 			goto cancel;
130906d2f4c5SMatt Johnston 	}
131006d2f4c5SMatt Johnston 
131106d2f4c5SMatt Johnston 	/* TODO: conditional neighbour physaddr? */
131206d2f4c5SMatt Johnston 
131306d2f4c5SMatt Johnston 	nlmsg_end(skb, nlh);
131406d2f4c5SMatt Johnston 
131506d2f4c5SMatt Johnston 	return 0;
131606d2f4c5SMatt Johnston 
131706d2f4c5SMatt Johnston cancel:
131806d2f4c5SMatt Johnston 	nlmsg_cancel(skb, nlh);
131906d2f4c5SMatt Johnston 	return -EMSGSIZE;
132006d2f4c5SMatt Johnston }
132106d2f4c5SMatt Johnston 
132206d2f4c5SMatt Johnston static int mctp_dump_rtinfo(struct sk_buff *skb, struct netlink_callback *cb)
132306d2f4c5SMatt Johnston {
132406d2f4c5SMatt Johnston 	struct net *net = sock_net(skb->sk);
132506d2f4c5SMatt Johnston 	struct mctp_route *rt;
132606d2f4c5SMatt Johnston 	int s_idx, idx;
132706d2f4c5SMatt Johnston 
132806d2f4c5SMatt Johnston 	/* TODO: allow filtering on route data, possibly under
132906d2f4c5SMatt Johnston 	 * cb->strict_check
133006d2f4c5SMatt Johnston 	 */
133106d2f4c5SMatt Johnston 
133206d2f4c5SMatt Johnston 	/* TODO: change to struct overlay */
133306d2f4c5SMatt Johnston 	s_idx = cb->args[0];
133406d2f4c5SMatt Johnston 	idx = 0;
133506d2f4c5SMatt Johnston 
133606d2f4c5SMatt Johnston 	rcu_read_lock();
133706d2f4c5SMatt Johnston 	list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
133806d2f4c5SMatt Johnston 		if (idx++ < s_idx)
133906d2f4c5SMatt Johnston 			continue;
134006d2f4c5SMatt Johnston 		if (mctp_fill_rtinfo(skb, rt,
134106d2f4c5SMatt Johnston 				     NETLINK_CB(cb->skb).portid,
134206d2f4c5SMatt Johnston 				     cb->nlh->nlmsg_seq,
134306d2f4c5SMatt Johnston 				     RTM_NEWROUTE, NLM_F_MULTI) < 0)
134406d2f4c5SMatt Johnston 			break;
134506d2f4c5SMatt Johnston 	}
134606d2f4c5SMatt Johnston 
134706d2f4c5SMatt Johnston 	rcu_read_unlock();
134806d2f4c5SMatt Johnston 	cb->args[0] = idx;
134906d2f4c5SMatt Johnston 
135006d2f4c5SMatt Johnston 	return skb->len;
135106d2f4c5SMatt Johnston }
135206d2f4c5SMatt Johnston 
1353889b7da2SJeremy Kerr /* net namespace implementation */
1354889b7da2SJeremy Kerr static int __net_init mctp_routes_net_init(struct net *net)
1355889b7da2SJeremy Kerr {
1356889b7da2SJeremy Kerr 	struct netns_mctp *ns = &net->mctp;
1357889b7da2SJeremy Kerr 
1358889b7da2SJeremy Kerr 	INIT_LIST_HEAD(&ns->routes);
1359833ef3b9SJeremy Kerr 	INIT_HLIST_HEAD(&ns->binds);
1360833ef3b9SJeremy Kerr 	mutex_init(&ns->bind_lock);
1361833ef3b9SJeremy Kerr 	INIT_HLIST_HEAD(&ns->keys);
1362833ef3b9SJeremy Kerr 	spin_lock_init(&ns->keys_lock);
136303f2bbc4SMatt Johnston 	WARN_ON(mctp_default_net_set(net, MCTP_INITIAL_DEFAULT_NET));
1364889b7da2SJeremy Kerr 	return 0;
1365889b7da2SJeremy Kerr }
1366889b7da2SJeremy Kerr 
1367889b7da2SJeremy Kerr static void __net_exit mctp_routes_net_exit(struct net *net)
1368889b7da2SJeremy Kerr {
1369889b7da2SJeremy Kerr 	struct mctp_route *rt;
1370889b7da2SJeremy Kerr 
1371581edcd0SJeremy Kerr 	rcu_read_lock();
1372889b7da2SJeremy Kerr 	list_for_each_entry_rcu(rt, &net->mctp.routes, list)
1373889b7da2SJeremy Kerr 		mctp_route_release(rt);
1374581edcd0SJeremy Kerr 	rcu_read_unlock();
1375889b7da2SJeremy Kerr }
1376889b7da2SJeremy Kerr 
1377889b7da2SJeremy Kerr static struct pernet_operations mctp_net_ops = {
1378889b7da2SJeremy Kerr 	.init = mctp_routes_net_init,
1379889b7da2SJeremy Kerr 	.exit = mctp_routes_net_exit,
1380889b7da2SJeremy Kerr };
1381889b7da2SJeremy Kerr 
1382889b7da2SJeremy Kerr int __init mctp_routes_init(void)
1383889b7da2SJeremy Kerr {
1384889b7da2SJeremy Kerr 	dev_add_pack(&mctp_packet_type);
138506d2f4c5SMatt Johnston 
138606d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_GETROUTE,
138706d2f4c5SMatt Johnston 			     NULL, mctp_dump_rtinfo, 0);
138806d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_NEWROUTE,
138906d2f4c5SMatt Johnston 			     mctp_newroute, NULL, 0);
139006d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_DELROUTE,
139106d2f4c5SMatt Johnston 			     mctp_delroute, NULL, 0);
139206d2f4c5SMatt Johnston 
1393889b7da2SJeremy Kerr 	return register_pernet_subsys(&mctp_net_ops);
1394889b7da2SJeremy Kerr }
1395889b7da2SJeremy Kerr 
1396889b7da2SJeremy Kerr void __exit mctp_routes_exit(void)
1397889b7da2SJeremy Kerr {
1398889b7da2SJeremy Kerr 	unregister_pernet_subsys(&mctp_net_ops);
139906d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_DELROUTE);
140006d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_NEWROUTE);
140106d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_GETROUTE);
1402889b7da2SJeremy Kerr 	dev_remove_pack(&mctp_packet_type);
1403889b7da2SJeremy Kerr }
1404161eba50SJeremy Kerr 
1405161eba50SJeremy Kerr #if IS_ENABLED(CONFIG_MCTP_TEST)
1406161eba50SJeremy Kerr #include "test/route-test.c"
1407161eba50SJeremy Kerr #endif
1408