xref: /openbmc/linux/net/mctp/route.c (revision be503653)
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 */
mctp_route_discard(struct mctp_route * route,struct sk_buff * skb)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 
mctp_lookup_bind(struct net * net,struct sk_buff * skb)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 
mctp_key_match(struct mctp_sk_key * key,mctp_eid_t local,mctp_eid_t peer,u8 tag)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  */
mctp_lookup_key(struct net * net,struct sk_buff * skb,mctp_eid_t peer,unsigned long * irqflags)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 
mctp_key_alloc(struct mctp_sock * msk,mctp_eid_t local,mctp_eid_t peer,u8 tag,gfp_t gfp)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);
150de8a6b15SJeremy Kerr 	sock_hold(key->sk);
1514a992bbdSJeremy Kerr 
1524a992bbdSJeremy Kerr 	return key;
1534a992bbdSJeremy Kerr }
1544a992bbdSJeremy Kerr 
mctp_key_unref(struct mctp_sk_key * key)15573c61845SJeremy Kerr void mctp_key_unref(struct mctp_sk_key *key)
15673c61845SJeremy Kerr {
15767737c45SJeremy Kerr 	unsigned long flags;
15867737c45SJeremy Kerr 
15967737c45SJeremy Kerr 	if (!refcount_dec_and_test(&key->refs))
16067737c45SJeremy Kerr 		return;
16167737c45SJeremy Kerr 
16267737c45SJeremy Kerr 	/* even though no refs exist here, the lock allows us to stay
16367737c45SJeremy Kerr 	 * consistent with the locking requirement of mctp_dev_release_key
16467737c45SJeremy Kerr 	 */
16567737c45SJeremy Kerr 	spin_lock_irqsave(&key->lock, flags);
16667737c45SJeremy Kerr 	mctp_dev_release_key(key->dev, key);
16767737c45SJeremy Kerr 	spin_unlock_irqrestore(&key->lock, flags);
16867737c45SJeremy Kerr 
169de8a6b15SJeremy Kerr 	sock_put(key->sk);
17073c61845SJeremy Kerr 	kfree(key);
17173c61845SJeremy Kerr }
17273c61845SJeremy Kerr 
mctp_key_add(struct mctp_sk_key * key,struct mctp_sock * msk)1734a992bbdSJeremy Kerr static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
1744a992bbdSJeremy Kerr {
1754a992bbdSJeremy Kerr 	struct net *net = sock_net(&msk->sk);
1764a992bbdSJeremy Kerr 	struct mctp_sk_key *tmp;
1774a992bbdSJeremy Kerr 	unsigned long flags;
1784a992bbdSJeremy Kerr 	int rc = 0;
1794a992bbdSJeremy Kerr 
1804a992bbdSJeremy Kerr 	spin_lock_irqsave(&net->mctp.keys_lock, flags);
1814a992bbdSJeremy Kerr 
182b98e1a04SJeremy Kerr 	if (sock_flag(&msk->sk, SOCK_DEAD)) {
183b98e1a04SJeremy Kerr 		rc = -EINVAL;
184b98e1a04SJeremy Kerr 		goto out_unlock;
185b98e1a04SJeremy Kerr 	}
186b98e1a04SJeremy Kerr 
1874a992bbdSJeremy Kerr 	hlist_for_each_entry(tmp, &net->mctp.keys, hlist) {
1884a992bbdSJeremy Kerr 		if (mctp_key_match(tmp, key->local_addr, key->peer_addr,
1894a992bbdSJeremy Kerr 				   key->tag)) {
19073c61845SJeremy Kerr 			spin_lock(&tmp->lock);
19173c61845SJeremy Kerr 			if (tmp->valid)
1924a992bbdSJeremy Kerr 				rc = -EEXIST;
19373c61845SJeremy Kerr 			spin_unlock(&tmp->lock);
19473c61845SJeremy Kerr 			if (rc)
1954a992bbdSJeremy Kerr 				break;
1964a992bbdSJeremy Kerr 		}
1974a992bbdSJeremy Kerr 	}
1984a992bbdSJeremy Kerr 
1994a992bbdSJeremy Kerr 	if (!rc) {
20073c61845SJeremy Kerr 		refcount_inc(&key->refs);
2017b14e15aSJeremy Kerr 		key->expiry = jiffies + mctp_key_lifetime;
2027b14e15aSJeremy Kerr 		timer_reduce(&msk->key_expiry, key->expiry);
2037b14e15aSJeremy Kerr 
2044a992bbdSJeremy Kerr 		hlist_add_head(&key->hlist, &net->mctp.keys);
2054a992bbdSJeremy Kerr 		hlist_add_head(&key->sklist, &msk->keys);
2064a992bbdSJeremy Kerr 	}
2074a992bbdSJeremy Kerr 
208b98e1a04SJeremy Kerr out_unlock:
2094a992bbdSJeremy Kerr 	spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
2104a992bbdSJeremy Kerr 
2114a992bbdSJeremy Kerr 	return rc;
2124a992bbdSJeremy Kerr }
2134a992bbdSJeremy Kerr 
21463ed1aabSMatt Johnston /* Helper for mctp_route_input().
21563ed1aabSMatt Johnston  * We're done with the key; unlock and unref the key.
21663ed1aabSMatt Johnston  * For the usual case of automatic expiry we remove the key from lists.
21763ed1aabSMatt Johnston  * In the case that manual allocation is set on a key we release the lock
21863ed1aabSMatt Johnston  * and local ref, reset reassembly, but don't remove from lists.
2194a992bbdSJeremy Kerr  */
__mctp_key_done_in(struct mctp_sk_key * key,struct net * net,unsigned long flags,unsigned long reason)22063ed1aabSMatt Johnston static void __mctp_key_done_in(struct mctp_sk_key *key, struct net *net,
22163ed1aabSMatt Johnston 			       unsigned long flags, unsigned long reason)
22273c61845SJeremy Kerr __releases(&key->lock)
2234a992bbdSJeremy Kerr {
2244a992bbdSJeremy Kerr 	struct sk_buff *skb;
2254a992bbdSJeremy Kerr 
22663ed1aabSMatt Johnston 	trace_mctp_key_release(key, reason);
2274a992bbdSJeremy Kerr 	skb = key->reasm_head;
2284a992bbdSJeremy Kerr 	key->reasm_head = NULL;
22963ed1aabSMatt Johnston 
23063ed1aabSMatt Johnston 	if (!key->manual_alloc) {
2314a992bbdSJeremy Kerr 		key->reasm_dead = true;
23273c61845SJeremy Kerr 		key->valid = false;
23367737c45SJeremy Kerr 		mctp_dev_release_key(key->dev, key);
23463ed1aabSMatt Johnston 	}
23573c61845SJeremy Kerr 	spin_unlock_irqrestore(&key->lock, flags);
2364a992bbdSJeremy Kerr 
23763ed1aabSMatt Johnston 	if (!key->manual_alloc) {
2384a992bbdSJeremy Kerr 		spin_lock_irqsave(&net->mctp.keys_lock, flags);
2393a732b46SJeremy Kerr 		if (!hlist_unhashed(&key->hlist)) {
2403a732b46SJeremy Kerr 			hlist_del_init(&key->hlist);
2413a732b46SJeremy Kerr 			hlist_del_init(&key->sklist);
24273c61845SJeremy Kerr 			mctp_key_unref(key);
24363ed1aabSMatt Johnston 		}
2443a732b46SJeremy Kerr 		spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
2453a732b46SJeremy Kerr 	}
24673c61845SJeremy Kerr 
24773c61845SJeremy Kerr 	/* and one for the local reference */
24873c61845SJeremy Kerr 	mctp_key_unref(key);
2494a992bbdSJeremy Kerr 
2504a992bbdSJeremy Kerr 	kfree_skb(skb);
2514a992bbdSJeremy Kerr }
2524a992bbdSJeremy Kerr 
25367737c45SJeremy Kerr #ifdef CONFIG_MCTP_FLOWS
mctp_skb_set_flow(struct sk_buff * skb,struct mctp_sk_key * key)25467737c45SJeremy Kerr static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key)
25567737c45SJeremy Kerr {
25667737c45SJeremy Kerr 	struct mctp_flow *flow;
25767737c45SJeremy Kerr 
25867737c45SJeremy Kerr 	flow = skb_ext_add(skb, SKB_EXT_MCTP);
25967737c45SJeremy Kerr 	if (!flow)
26067737c45SJeremy Kerr 		return;
26167737c45SJeremy Kerr 
26267737c45SJeremy Kerr 	refcount_inc(&key->refs);
26367737c45SJeremy Kerr 	flow->key = key;
26467737c45SJeremy Kerr }
26567737c45SJeremy Kerr 
mctp_flow_prepare_output(struct sk_buff * skb,struct mctp_dev * dev)26667737c45SJeremy Kerr static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev)
26767737c45SJeremy Kerr {
26867737c45SJeremy Kerr 	struct mctp_sk_key *key;
26967737c45SJeremy Kerr 	struct mctp_flow *flow;
27067737c45SJeremy Kerr 
27167737c45SJeremy Kerr 	flow = skb_ext_find(skb, SKB_EXT_MCTP);
27267737c45SJeremy Kerr 	if (!flow)
27367737c45SJeremy Kerr 		return;
27467737c45SJeremy Kerr 
27567737c45SJeremy Kerr 	key = flow->key;
27667737c45SJeremy Kerr 
27767737c45SJeremy Kerr 	if (WARN_ON(key->dev && key->dev != dev))
27867737c45SJeremy Kerr 		return;
27967737c45SJeremy Kerr 
28067737c45SJeremy Kerr 	mctp_dev_set_key(dev, key);
28167737c45SJeremy Kerr }
28267737c45SJeremy Kerr #else
mctp_skb_set_flow(struct sk_buff * skb,struct mctp_sk_key * key)28367737c45SJeremy Kerr static void mctp_skb_set_flow(struct sk_buff *skb, struct mctp_sk_key *key) {}
mctp_flow_prepare_output(struct sk_buff * skb,struct mctp_dev * dev)28467737c45SJeremy Kerr static void mctp_flow_prepare_output(struct sk_buff *skb, struct mctp_dev *dev) {}
28567737c45SJeremy Kerr #endif
28667737c45SJeremy Kerr 
mctp_frag_queue(struct mctp_sk_key * key,struct sk_buff * skb)2874a992bbdSJeremy Kerr static int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb)
2884a992bbdSJeremy Kerr {
2894a992bbdSJeremy Kerr 	struct mctp_hdr *hdr = mctp_hdr(skb);
2904a992bbdSJeremy Kerr 	u8 exp_seq, this_seq;
2914a992bbdSJeremy Kerr 
2924a992bbdSJeremy Kerr 	this_seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT)
2934a992bbdSJeremy Kerr 		& MCTP_HDR_SEQ_MASK;
2944a992bbdSJeremy Kerr 
2954a992bbdSJeremy Kerr 	if (!key->reasm_head) {
2964a992bbdSJeremy Kerr 		key->reasm_head = skb;
2974a992bbdSJeremy Kerr 		key->reasm_tailp = &(skb_shinfo(skb)->frag_list);
2984a992bbdSJeremy Kerr 		key->last_seq = this_seq;
2994a992bbdSJeremy Kerr 		return 0;
3004a992bbdSJeremy Kerr 	}
3014a992bbdSJeremy Kerr 
3024a992bbdSJeremy Kerr 	exp_seq = (key->last_seq + 1) & MCTP_HDR_SEQ_MASK;
3034a992bbdSJeremy Kerr 
3044a992bbdSJeremy Kerr 	if (this_seq != exp_seq)
3054a992bbdSJeremy Kerr 		return -EINVAL;
3064a992bbdSJeremy Kerr 
3074a992bbdSJeremy Kerr 	if (key->reasm_head->len + skb->len > mctp_message_maxlen)
3084a992bbdSJeremy Kerr 		return -EINVAL;
3094a992bbdSJeremy Kerr 
3104a992bbdSJeremy Kerr 	skb->next = NULL;
3114a992bbdSJeremy Kerr 	skb->sk = NULL;
3124a992bbdSJeremy Kerr 	*key->reasm_tailp = skb;
3134a992bbdSJeremy Kerr 	key->reasm_tailp = &skb->next;
3144a992bbdSJeremy Kerr 
3154a992bbdSJeremy Kerr 	key->last_seq = this_seq;
3164a992bbdSJeremy Kerr 
3174a992bbdSJeremy Kerr 	key->reasm_head->data_len += skb->len;
3184a992bbdSJeremy Kerr 	key->reasm_head->len += skb->len;
3194a992bbdSJeremy Kerr 	key->reasm_head->truesize += skb->truesize;
3204a992bbdSJeremy Kerr 
3214a992bbdSJeremy Kerr 	return 0;
3224a992bbdSJeremy Kerr }
3234a992bbdSJeremy Kerr 
mctp_route_input(struct mctp_route * route,struct sk_buff * skb)324889b7da2SJeremy Kerr static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
325889b7da2SJeremy Kerr {
3266e54ea37SPaolo Abeni 	struct mctp_sk_key *key, *any_key = NULL;
327833ef3b9SJeremy Kerr 	struct net *net = dev_net(skb->dev);
328833ef3b9SJeremy Kerr 	struct mctp_sock *msk;
329833ef3b9SJeremy Kerr 	struct mctp_hdr *mh;
3304a992bbdSJeremy Kerr 	unsigned long f;
3314a992bbdSJeremy Kerr 	u8 tag, flags;
3324a992bbdSJeremy Kerr 	int rc;
333833ef3b9SJeremy Kerr 
334833ef3b9SJeremy Kerr 	msk = NULL;
3354a992bbdSJeremy Kerr 	rc = -EINVAL;
336833ef3b9SJeremy Kerr 
337833ef3b9SJeremy Kerr 	/* we may be receiving a locally-routed packet; drop source sk
338833ef3b9SJeremy Kerr 	 * accounting
339833ef3b9SJeremy Kerr 	 */
340833ef3b9SJeremy Kerr 	skb_orphan(skb);
341833ef3b9SJeremy Kerr 
342833ef3b9SJeremy Kerr 	/* ensure we have enough data for a header and a type */
343833ef3b9SJeremy Kerr 	if (skb->len < sizeof(struct mctp_hdr) + 1)
3444a992bbdSJeremy Kerr 		goto out;
345833ef3b9SJeremy Kerr 
346833ef3b9SJeremy Kerr 	/* grab header, advance data ptr */
347833ef3b9SJeremy Kerr 	mh = mctp_hdr(skb);
348833ef3b9SJeremy Kerr 	skb_pull(skb, sizeof(struct mctp_hdr));
349833ef3b9SJeremy Kerr 
350833ef3b9SJeremy Kerr 	if (mh->ver != 1)
3514a992bbdSJeremy Kerr 		goto out;
352833ef3b9SJeremy Kerr 
3534a992bbdSJeremy Kerr 	flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
3544a992bbdSJeremy Kerr 	tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
355833ef3b9SJeremy Kerr 
356833ef3b9SJeremy Kerr 	rcu_read_lock();
3574a992bbdSJeremy Kerr 
35873c61845SJeremy Kerr 	/* lookup socket / reasm context, exactly matching (src,dest,tag).
35973c61845SJeremy Kerr 	 * we hold a ref on the key, and key->lock held.
36073c61845SJeremy Kerr 	 */
36173c61845SJeremy Kerr 	key = mctp_lookup_key(net, skb, mh->src, &f);
362833ef3b9SJeremy Kerr 
3634a992bbdSJeremy Kerr 	if (flags & MCTP_HDR_FLAG_SOM) {
3644a992bbdSJeremy Kerr 		if (key) {
365833ef3b9SJeremy Kerr 			msk = container_of(key->sk, struct mctp_sock, sk);
3664a992bbdSJeremy Kerr 		} else {
3674a992bbdSJeremy Kerr 			/* first response to a broadcast? do a more general
3684a992bbdSJeremy Kerr 			 * key lookup to find the socket, but don't use this
3694a992bbdSJeremy Kerr 			 * key for reassembly - we'll create a more specific
3704a992bbdSJeremy Kerr 			 * one for future packets if required (ie, !EOM).
3714a992bbdSJeremy Kerr 			 */
3726e54ea37SPaolo Abeni 			any_key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
3736e54ea37SPaolo Abeni 			if (any_key) {
3746e54ea37SPaolo Abeni 				msk = container_of(any_key->sk,
3754a992bbdSJeremy Kerr 						   struct mctp_sock, sk);
3766e54ea37SPaolo Abeni 				spin_unlock_irqrestore(&any_key->lock, f);
3774a992bbdSJeremy Kerr 			}
3784a992bbdSJeremy Kerr 		}
3794a992bbdSJeremy Kerr 
3804a992bbdSJeremy Kerr 		if (!key && !msk && (tag & MCTP_HDR_FLAG_TO))
381833ef3b9SJeremy Kerr 			msk = mctp_lookup_bind(net, skb);
382833ef3b9SJeremy Kerr 
3834a992bbdSJeremy Kerr 		if (!msk) {
3844a992bbdSJeremy Kerr 			rc = -ENOENT;
3854a992bbdSJeremy Kerr 			goto out_unlock;
3864a992bbdSJeremy Kerr 		}
387833ef3b9SJeremy Kerr 
3884a992bbdSJeremy Kerr 		/* single-packet message? deliver to socket, clean up any
3894a992bbdSJeremy Kerr 		 * pending key.
3904a992bbdSJeremy Kerr 		 */
3914a992bbdSJeremy Kerr 		if (flags & MCTP_HDR_FLAG_EOM) {
392833ef3b9SJeremy Kerr 			sock_queue_rcv_skb(&msk->sk, skb);
3934a992bbdSJeremy Kerr 			if (key) {
3944a992bbdSJeremy Kerr 				/* we've hit a pending reassembly; not much we
3954a992bbdSJeremy Kerr 				 * can do but drop it
3964a992bbdSJeremy Kerr 				 */
39763ed1aabSMatt Johnston 				__mctp_key_done_in(key, net, f,
3984f9e1ba6SJeremy Kerr 						   MCTP_TRACE_KEY_REPLIED);
39973c61845SJeremy Kerr 				key = NULL;
4004a992bbdSJeremy Kerr 			}
4014a992bbdSJeremy Kerr 			rc = 0;
4024a992bbdSJeremy Kerr 			goto out_unlock;
4034a992bbdSJeremy Kerr 		}
404833ef3b9SJeremy Kerr 
4054a992bbdSJeremy Kerr 		/* broadcast response or a bind() - create a key for further
4064a992bbdSJeremy Kerr 		 * packets for this message
4074a992bbdSJeremy Kerr 		 */
4084a992bbdSJeremy Kerr 		if (!key) {
4094a992bbdSJeremy Kerr 			key = mctp_key_alloc(msk, mh->dest, mh->src,
4104a992bbdSJeremy Kerr 					     tag, GFP_ATOMIC);
4114a992bbdSJeremy Kerr 			if (!key) {
4124a992bbdSJeremy Kerr 				rc = -ENOMEM;
4134a992bbdSJeremy Kerr 				goto out_unlock;
4144a992bbdSJeremy Kerr 			}
4154a992bbdSJeremy Kerr 
41673c61845SJeremy Kerr 			/* we can queue without the key lock here, as the
4174a992bbdSJeremy Kerr 			 * key isn't observable yet
4184a992bbdSJeremy Kerr 			 */
4194a992bbdSJeremy Kerr 			mctp_frag_queue(key, skb);
4204a992bbdSJeremy Kerr 
4214a992bbdSJeremy Kerr 			/* if the key_add fails, we've raced with another
4224a992bbdSJeremy Kerr 			 * SOM packet with the same src, dest and tag. There's
4234a992bbdSJeremy Kerr 			 * no way to distinguish future packets, so all we
4244a992bbdSJeremy Kerr 			 * can do is drop; we'll free the skb on exit from
4254a992bbdSJeremy Kerr 			 * this function.
4264a992bbdSJeremy Kerr 			 */
4274a992bbdSJeremy Kerr 			rc = mctp_key_add(key, msk);
428de8a6b15SJeremy Kerr 			if (!rc)
4294f9e1ba6SJeremy Kerr 				trace_mctp_key_acquire(key);
4304f9e1ba6SJeremy Kerr 
431de8a6b15SJeremy Kerr 			/* we don't need to release key->lock on exit, so
432de8a6b15SJeremy Kerr 			 * clean up here and suppress the unlock via
433de8a6b15SJeremy Kerr 			 * setting to NULL
434de8a6b15SJeremy Kerr 			 */
4350b93aed2SMatt Johnston 			mctp_key_unref(key);
43673c61845SJeremy Kerr 			key = NULL;
4374a992bbdSJeremy Kerr 
43873c61845SJeremy Kerr 		} else {
4394a992bbdSJeremy Kerr 			if (key->reasm_head || key->reasm_dead) {
4404a992bbdSJeremy Kerr 				/* duplicate start? drop everything */
44163ed1aabSMatt Johnston 				__mctp_key_done_in(key, net, f,
4424f9e1ba6SJeremy Kerr 						   MCTP_TRACE_KEY_INVALIDATED);
4434a992bbdSJeremy Kerr 				rc = -EEXIST;
44473c61845SJeremy Kerr 				key = NULL;
4454a992bbdSJeremy Kerr 			} else {
4464a992bbdSJeremy Kerr 				rc = mctp_frag_queue(key, skb);
4474a992bbdSJeremy Kerr 			}
4484a992bbdSJeremy Kerr 		}
4494a992bbdSJeremy Kerr 
4504a992bbdSJeremy Kerr 	} else if (key) {
4514a992bbdSJeremy Kerr 		/* this packet continues a previous message; reassemble
4524a992bbdSJeremy Kerr 		 * using the message-specific key
4534a992bbdSJeremy Kerr 		 */
4544a992bbdSJeremy Kerr 
4554a992bbdSJeremy Kerr 		/* we need to be continuing an existing reassembly... */
4564a992bbdSJeremy Kerr 		if (!key->reasm_head)
4574a992bbdSJeremy Kerr 			rc = -EINVAL;
4584a992bbdSJeremy Kerr 		else
4594a992bbdSJeremy Kerr 			rc = mctp_frag_queue(key, skb);
4604a992bbdSJeremy Kerr 
4614a992bbdSJeremy Kerr 		/* end of message? deliver to socket, and we're done with
4624a992bbdSJeremy Kerr 		 * the reassembly/response key
4634a992bbdSJeremy Kerr 		 */
4644a992bbdSJeremy Kerr 		if (!rc && flags & MCTP_HDR_FLAG_EOM) {
4654a992bbdSJeremy Kerr 			sock_queue_rcv_skb(key->sk, key->reasm_head);
4664a992bbdSJeremy Kerr 			key->reasm_head = NULL;
46763ed1aabSMatt Johnston 			__mctp_key_done_in(key, net, f, MCTP_TRACE_KEY_REPLIED);
46873c61845SJeremy Kerr 			key = NULL;
4694a992bbdSJeremy Kerr 		}
4704a992bbdSJeremy Kerr 
4714a992bbdSJeremy Kerr 	} else {
4724a992bbdSJeremy Kerr 		/* not a start, no matching key */
4734a992bbdSJeremy Kerr 		rc = -ENOENT;
4744a992bbdSJeremy Kerr 	}
4754a992bbdSJeremy Kerr 
4764a992bbdSJeremy Kerr out_unlock:
477833ef3b9SJeremy Kerr 	rcu_read_unlock();
47873c61845SJeremy Kerr 	if (key) {
47973c61845SJeremy Kerr 		spin_unlock_irqrestore(&key->lock, f);
48073c61845SJeremy Kerr 		mctp_key_unref(key);
48173c61845SJeremy Kerr 	}
4826e54ea37SPaolo Abeni 	if (any_key)
4836e54ea37SPaolo Abeni 		mctp_key_unref(any_key);
4844a992bbdSJeremy Kerr out:
4854a992bbdSJeremy Kerr 	if (rc)
486889b7da2SJeremy Kerr 		kfree_skb(skb);
4874a992bbdSJeremy Kerr 	return rc;
4884a992bbdSJeremy Kerr }
4894a992bbdSJeremy Kerr 
mctp_route_mtu(struct mctp_route * rt)4904a992bbdSJeremy Kerr static unsigned int mctp_route_mtu(struct mctp_route *rt)
4914a992bbdSJeremy Kerr {
4924a992bbdSJeremy Kerr 	return rt->mtu ?: READ_ONCE(rt->dev->dev->mtu);
493889b7da2SJeremy Kerr }
494889b7da2SJeremy Kerr 
mctp_route_output(struct mctp_route * route,struct sk_buff * skb)49506d2f4c5SMatt Johnston static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
496889b7da2SJeremy Kerr {
49799ce45d5SJeremy Kerr 	struct mctp_skb_cb *cb = mctp_cb(skb);
49826ab3fcaSMatt Johnston 	struct mctp_hdr *hdr = mctp_hdr(skb);
49926ab3fcaSMatt Johnston 	char daddr_buf[MAX_ADDR_LEN];
50026ab3fcaSMatt Johnston 	char *daddr = NULL;
501889b7da2SJeremy Kerr 	unsigned int mtu;
502889b7da2SJeremy Kerr 	int rc;
503889b7da2SJeremy Kerr 
504889b7da2SJeremy Kerr 	skb->protocol = htons(ETH_P_MCTP);
505889b7da2SJeremy Kerr 
506889b7da2SJeremy Kerr 	mtu = READ_ONCE(skb->dev->mtu);
507889b7da2SJeremy Kerr 	if (skb->len > mtu) {
508889b7da2SJeremy Kerr 		kfree_skb(skb);
509889b7da2SJeremy Kerr 		return -EMSGSIZE;
510889b7da2SJeremy Kerr 	}
511889b7da2SJeremy Kerr 
51299ce45d5SJeremy Kerr 	if (cb->ifindex) {
51399ce45d5SJeremy Kerr 		/* direct route; use the hwaddr we stashed in sendmsg */
5144a9dda1cSMatt Johnston 		if (cb->halen != skb->dev->addr_len) {
5154a9dda1cSMatt Johnston 			/* sanity check, sendmsg should have already caught this */
5164a9dda1cSMatt Johnston 			kfree_skb(skb);
5174a9dda1cSMatt Johnston 			return -EMSGSIZE;
5184a9dda1cSMatt Johnston 		}
51999ce45d5SJeremy Kerr 		daddr = cb->haddr;
52099ce45d5SJeremy Kerr 	} else {
52126ab3fcaSMatt Johnston 		/* If lookup fails let the device handle daddr==NULL */
52226ab3fcaSMatt Johnston 		if (mctp_neigh_lookup(route->dev, hdr->dest, daddr_buf) == 0)
52326ab3fcaSMatt Johnston 			daddr = daddr_buf;
52499ce45d5SJeremy Kerr 	}
52526ab3fcaSMatt Johnston 
526889b7da2SJeremy Kerr 	rc = dev_hard_header(skb, skb->dev, ntohs(skb->protocol),
52726ab3fcaSMatt Johnston 			     daddr, skb->dev->dev_addr, skb->len);
52860be976aSMatt Johnston 	if (rc < 0) {
529889b7da2SJeremy Kerr 		kfree_skb(skb);
530889b7da2SJeremy Kerr 		return -EHOSTUNREACH;
531889b7da2SJeremy Kerr 	}
532889b7da2SJeremy Kerr 
53367737c45SJeremy Kerr 	mctp_flow_prepare_output(skb, route->dev);
53467737c45SJeremy Kerr 
535889b7da2SJeremy Kerr 	rc = dev_queue_xmit(skb);
536889b7da2SJeremy Kerr 	if (rc)
537889b7da2SJeremy Kerr 		rc = net_xmit_errno(rc);
538889b7da2SJeremy Kerr 
539889b7da2SJeremy Kerr 	return rc;
540889b7da2SJeremy Kerr }
541889b7da2SJeremy Kerr 
542889b7da2SJeremy Kerr /* route alloc/release */
mctp_route_release(struct mctp_route * rt)543889b7da2SJeremy Kerr static void mctp_route_release(struct mctp_route *rt)
544889b7da2SJeremy Kerr {
545889b7da2SJeremy Kerr 	if (refcount_dec_and_test(&rt->refs)) {
54643f55f23SJeremy Kerr 		mctp_dev_put(rt->dev);
547889b7da2SJeremy Kerr 		kfree_rcu(rt, rcu);
548889b7da2SJeremy Kerr 	}
549889b7da2SJeremy Kerr }
550889b7da2SJeremy Kerr 
551889b7da2SJeremy Kerr /* returns a route with the refcount at 1 */
mctp_route_alloc(void)552889b7da2SJeremy Kerr static struct mctp_route *mctp_route_alloc(void)
553889b7da2SJeremy Kerr {
554889b7da2SJeremy Kerr 	struct mctp_route *rt;
555889b7da2SJeremy Kerr 
556889b7da2SJeremy Kerr 	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
557889b7da2SJeremy Kerr 	if (!rt)
558889b7da2SJeremy Kerr 		return NULL;
559889b7da2SJeremy Kerr 
560889b7da2SJeremy Kerr 	INIT_LIST_HEAD(&rt->list);
561889b7da2SJeremy Kerr 	refcount_set(&rt->refs, 1);
562889b7da2SJeremy Kerr 	rt->output = mctp_route_discard;
563889b7da2SJeremy Kerr 
564889b7da2SJeremy Kerr 	return rt;
565889b7da2SJeremy Kerr }
566889b7da2SJeremy Kerr 
mctp_default_net(struct net * net)56703f2bbc4SMatt Johnston unsigned int mctp_default_net(struct net *net)
56803f2bbc4SMatt Johnston {
56903f2bbc4SMatt Johnston 	return READ_ONCE(net->mctp.default_net);
57003f2bbc4SMatt Johnston }
57103f2bbc4SMatt Johnston 
mctp_default_net_set(struct net * net,unsigned int index)57203f2bbc4SMatt Johnston int mctp_default_net_set(struct net *net, unsigned int index)
57303f2bbc4SMatt Johnston {
57403f2bbc4SMatt Johnston 	if (index == 0)
57503f2bbc4SMatt Johnston 		return -EINVAL;
57603f2bbc4SMatt Johnston 	WRITE_ONCE(net->mctp.default_net, index);
57703f2bbc4SMatt Johnston 	return 0;
57803f2bbc4SMatt Johnston }
57903f2bbc4SMatt Johnston 
580833ef3b9SJeremy Kerr /* tag management */
mctp_reserve_tag(struct net * net,struct mctp_sk_key * key,struct mctp_sock * msk)581833ef3b9SJeremy Kerr static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key,
582833ef3b9SJeremy Kerr 			     struct mctp_sock *msk)
583833ef3b9SJeremy Kerr {
584833ef3b9SJeremy Kerr 	struct netns_mctp *mns = &net->mctp;
585833ef3b9SJeremy Kerr 
586833ef3b9SJeremy Kerr 	lockdep_assert_held(&mns->keys_lock);
587833ef3b9SJeremy Kerr 
5887b14e15aSJeremy Kerr 	key->expiry = jiffies + mctp_key_lifetime;
5897b14e15aSJeremy Kerr 	timer_reduce(&msk->key_expiry, key->expiry);
5907b14e15aSJeremy Kerr 
591833ef3b9SJeremy Kerr 	/* we hold the net->key_lock here, allowing updates to both
592833ef3b9SJeremy Kerr 	 * then net and sk
593833ef3b9SJeremy Kerr 	 */
594833ef3b9SJeremy Kerr 	hlist_add_head_rcu(&key->hlist, &mns->keys);
595833ef3b9SJeremy Kerr 	hlist_add_head_rcu(&key->sklist, &msk->keys);
59673c61845SJeremy Kerr 	refcount_inc(&key->refs);
597833ef3b9SJeremy Kerr }
598833ef3b9SJeremy Kerr 
599833ef3b9SJeremy Kerr /* Allocate a locally-owned tag value for (saddr, daddr), and reserve
600833ef3b9SJeremy Kerr  * it for the socket msk
601833ef3b9SJeremy Kerr  */
mctp_alloc_local_tag(struct mctp_sock * msk,mctp_eid_t daddr,mctp_eid_t saddr,bool manual,u8 * tagp)60263ed1aabSMatt Johnston struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
60363ed1aabSMatt Johnston 					 mctp_eid_t daddr, mctp_eid_t saddr,
60463ed1aabSMatt Johnston 					 bool manual, u8 *tagp)
605833ef3b9SJeremy Kerr {
606833ef3b9SJeremy Kerr 	struct net *net = sock_net(&msk->sk);
607833ef3b9SJeremy Kerr 	struct netns_mctp *mns = &net->mctp;
608833ef3b9SJeremy Kerr 	struct mctp_sk_key *key, *tmp;
609833ef3b9SJeremy Kerr 	unsigned long flags;
610833ef3b9SJeremy Kerr 	u8 tagbits;
611833ef3b9SJeremy Kerr 
6121f6c77acSJeremy Kerr 	/* for NULL destination EIDs, we may get a response from any peer */
6131f6c77acSJeremy Kerr 	if (daddr == MCTP_ADDR_NULL)
6141f6c77acSJeremy Kerr 		daddr = MCTP_ADDR_ANY;
6151f6c77acSJeremy Kerr 
616833ef3b9SJeremy Kerr 	/* be optimistic, alloc now */
6174a992bbdSJeremy Kerr 	key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL);
618833ef3b9SJeremy Kerr 	if (!key)
619212c10c3SJeremy Kerr 		return ERR_PTR(-ENOMEM);
620833ef3b9SJeremy Kerr 
621833ef3b9SJeremy Kerr 	/* 8 possible tag values */
622833ef3b9SJeremy Kerr 	tagbits = 0xff;
623833ef3b9SJeremy Kerr 
624833ef3b9SJeremy Kerr 	spin_lock_irqsave(&mns->keys_lock, flags);
625833ef3b9SJeremy Kerr 
626833ef3b9SJeremy Kerr 	/* Walk through the existing keys, looking for potential conflicting
627833ef3b9SJeremy Kerr 	 * tags. If we find a conflict, clear that bit from tagbits
628833ef3b9SJeremy Kerr 	 */
629833ef3b9SJeremy Kerr 	hlist_for_each_entry(tmp, &mns->keys, hlist) {
63073c61845SJeremy Kerr 		/* We can check the lookup fields (*_addr, tag) without the
63173c61845SJeremy Kerr 		 * lock held, they don't change over the lifetime of the key.
63273c61845SJeremy Kerr 		 */
63373c61845SJeremy Kerr 
634833ef3b9SJeremy Kerr 		/* if we don't own the tag, it can't conflict */
635833ef3b9SJeremy Kerr 		if (tmp->tag & MCTP_HDR_FLAG_TO)
636833ef3b9SJeremy Kerr 			continue;
637833ef3b9SJeremy Kerr 
6388069b22dSJeremy Kerr 		if (!(mctp_address_matches(tmp->peer_addr, daddr) &&
6390de55a7dSJeremy Kerr 		      mctp_address_matches(tmp->local_addr, saddr)))
64073c61845SJeremy Kerr 			continue;
64173c61845SJeremy Kerr 
64273c61845SJeremy Kerr 		spin_lock(&tmp->lock);
64373c61845SJeremy Kerr 		/* key must still be valid. If we find a match, clear the
64473c61845SJeremy Kerr 		 * potential tag value
64573c61845SJeremy Kerr 		 */
64673c61845SJeremy Kerr 		if (tmp->valid)
647833ef3b9SJeremy Kerr 			tagbits &= ~(1 << tmp->tag);
64873c61845SJeremy Kerr 		spin_unlock(&tmp->lock);
649833ef3b9SJeremy Kerr 
650833ef3b9SJeremy Kerr 		if (!tagbits)
651833ef3b9SJeremy Kerr 			break;
652833ef3b9SJeremy Kerr 	}
653833ef3b9SJeremy Kerr 
654833ef3b9SJeremy Kerr 	if (tagbits) {
655833ef3b9SJeremy Kerr 		key->tag = __ffs(tagbits);
656833ef3b9SJeremy Kerr 		mctp_reserve_tag(net, key, msk);
6574f9e1ba6SJeremy Kerr 		trace_mctp_key_acquire(key);
6584f9e1ba6SJeremy Kerr 
65963ed1aabSMatt Johnston 		key->manual_alloc = manual;
660833ef3b9SJeremy Kerr 		*tagp = key->tag;
661833ef3b9SJeremy Kerr 	}
662833ef3b9SJeremy Kerr 
663833ef3b9SJeremy Kerr 	spin_unlock_irqrestore(&mns->keys_lock, flags);
664833ef3b9SJeremy Kerr 
665212c10c3SJeremy Kerr 	if (!tagbits) {
66618a3d49aSJeremy Kerr 		mctp_key_unref(key);
667212c10c3SJeremy Kerr 		return ERR_PTR(-EBUSY);
668212c10c3SJeremy Kerr 	}
669833ef3b9SJeremy Kerr 
670212c10c3SJeremy Kerr 	return key;
671833ef3b9SJeremy Kerr }
672833ef3b9SJeremy Kerr 
mctp_lookup_prealloc_tag(struct mctp_sock * msk,mctp_eid_t daddr,u8 req_tag,u8 * tagp)67363ed1aabSMatt Johnston static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
67463ed1aabSMatt Johnston 						    mctp_eid_t daddr,
67563ed1aabSMatt Johnston 						    u8 req_tag, u8 *tagp)
67663ed1aabSMatt Johnston {
67763ed1aabSMatt Johnston 	struct net *net = sock_net(&msk->sk);
67863ed1aabSMatt Johnston 	struct netns_mctp *mns = &net->mctp;
67963ed1aabSMatt Johnston 	struct mctp_sk_key *key, *tmp;
68063ed1aabSMatt Johnston 	unsigned long flags;
68163ed1aabSMatt Johnston 
68263ed1aabSMatt Johnston 	req_tag &= ~(MCTP_TAG_PREALLOC | MCTP_TAG_OWNER);
68363ed1aabSMatt Johnston 	key = NULL;
68463ed1aabSMatt Johnston 
68563ed1aabSMatt Johnston 	spin_lock_irqsave(&mns->keys_lock, flags);
68663ed1aabSMatt Johnston 
68763ed1aabSMatt Johnston 	hlist_for_each_entry(tmp, &mns->keys, hlist) {
68863ed1aabSMatt Johnston 		if (tmp->tag != req_tag)
68963ed1aabSMatt Johnston 			continue;
69063ed1aabSMatt Johnston 
69163ed1aabSMatt Johnston 		if (!mctp_address_matches(tmp->peer_addr, daddr))
69263ed1aabSMatt Johnston 			continue;
69363ed1aabSMatt Johnston 
69463ed1aabSMatt Johnston 		if (!tmp->manual_alloc)
69563ed1aabSMatt Johnston 			continue;
69663ed1aabSMatt Johnston 
69763ed1aabSMatt Johnston 		spin_lock(&tmp->lock);
69863ed1aabSMatt Johnston 		if (tmp->valid) {
69963ed1aabSMatt Johnston 			key = tmp;
70063ed1aabSMatt Johnston 			refcount_inc(&key->refs);
70163ed1aabSMatt Johnston 			spin_unlock(&tmp->lock);
70263ed1aabSMatt Johnston 			break;
70363ed1aabSMatt Johnston 		}
70463ed1aabSMatt Johnston 		spin_unlock(&tmp->lock);
70563ed1aabSMatt Johnston 	}
70663ed1aabSMatt Johnston 	spin_unlock_irqrestore(&mns->keys_lock, flags);
70763ed1aabSMatt Johnston 
70863ed1aabSMatt Johnston 	if (!key)
70963ed1aabSMatt Johnston 		return ERR_PTR(-ENOENT);
71063ed1aabSMatt Johnston 
71163ed1aabSMatt Johnston 	if (tagp)
71263ed1aabSMatt Johnston 		*tagp = key->tag;
71363ed1aabSMatt Johnston 
71463ed1aabSMatt Johnston 	return key;
71563ed1aabSMatt Johnston }
71663ed1aabSMatt Johnston 
717889b7da2SJeremy Kerr /* routing lookups */
mctp_rt_match_eid(struct mctp_route * rt,unsigned int net,mctp_eid_t eid)718889b7da2SJeremy Kerr static bool mctp_rt_match_eid(struct mctp_route *rt,
719889b7da2SJeremy Kerr 			      unsigned int net, mctp_eid_t eid)
720889b7da2SJeremy Kerr {
721889b7da2SJeremy Kerr 	return READ_ONCE(rt->dev->net) == net &&
722889b7da2SJeremy Kerr 		rt->min <= eid && rt->max >= eid;
723889b7da2SJeremy Kerr }
724889b7da2SJeremy Kerr 
725889b7da2SJeremy Kerr /* compares match, used for duplicate prevention */
mctp_rt_compare_exact(struct mctp_route * rt1,struct mctp_route * rt2)726889b7da2SJeremy Kerr static bool mctp_rt_compare_exact(struct mctp_route *rt1,
727889b7da2SJeremy Kerr 				  struct mctp_route *rt2)
728889b7da2SJeremy Kerr {
729889b7da2SJeremy Kerr 	ASSERT_RTNL();
730889b7da2SJeremy Kerr 	return rt1->dev->net == rt2->dev->net &&
731889b7da2SJeremy Kerr 		rt1->min == rt2->min &&
732889b7da2SJeremy Kerr 		rt1->max == rt2->max;
733889b7da2SJeremy Kerr }
734889b7da2SJeremy Kerr 
mctp_route_lookup(struct net * net,unsigned int dnet,mctp_eid_t daddr)735889b7da2SJeremy Kerr struct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet,
736889b7da2SJeremy Kerr 				     mctp_eid_t daddr)
737889b7da2SJeremy Kerr {
738889b7da2SJeremy Kerr 	struct mctp_route *tmp, *rt = NULL;
739889b7da2SJeremy Kerr 
7405093bbfcSJeremy Kerr 	rcu_read_lock();
7415093bbfcSJeremy Kerr 
742889b7da2SJeremy Kerr 	list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
743889b7da2SJeremy Kerr 		/* TODO: add metrics */
744889b7da2SJeremy Kerr 		if (mctp_rt_match_eid(tmp, dnet, daddr)) {
745889b7da2SJeremy Kerr 			if (refcount_inc_not_zero(&tmp->refs)) {
746889b7da2SJeremy Kerr 				rt = tmp;
747889b7da2SJeremy Kerr 				break;
748889b7da2SJeremy Kerr 			}
749889b7da2SJeremy Kerr 		}
750889b7da2SJeremy Kerr 	}
751889b7da2SJeremy Kerr 
7525093bbfcSJeremy Kerr 	rcu_read_unlock();
7535093bbfcSJeremy Kerr 
754889b7da2SJeremy Kerr 	return rt;
755889b7da2SJeremy Kerr }
756889b7da2SJeremy Kerr 
mctp_route_lookup_null(struct net * net,struct net_device * dev)7571f6c77acSJeremy Kerr static struct mctp_route *mctp_route_lookup_null(struct net *net,
7581f6c77acSJeremy Kerr 						 struct net_device *dev)
7591f6c77acSJeremy Kerr {
7605093bbfcSJeremy Kerr 	struct mctp_route *tmp, *rt = NULL;
7611f6c77acSJeremy Kerr 
7625093bbfcSJeremy Kerr 	rcu_read_lock();
7635093bbfcSJeremy Kerr 
7645093bbfcSJeremy Kerr 	list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
7655093bbfcSJeremy Kerr 		if (tmp->dev->dev == dev && tmp->type == RTN_LOCAL &&
7665093bbfcSJeremy Kerr 		    refcount_inc_not_zero(&tmp->refs)) {
7675093bbfcSJeremy Kerr 			rt = tmp;
7685093bbfcSJeremy Kerr 			break;
7695093bbfcSJeremy Kerr 		}
7701f6c77acSJeremy Kerr 	}
7711f6c77acSJeremy Kerr 
7725093bbfcSJeremy Kerr 	rcu_read_unlock();
7735093bbfcSJeremy Kerr 
7745093bbfcSJeremy Kerr 	return rt;
7751f6c77acSJeremy Kerr }
7761f6c77acSJeremy Kerr 
mctp_do_fragment_route(struct mctp_route * rt,struct sk_buff * skb,unsigned int mtu,u8 tag)7774a992bbdSJeremy Kerr static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
7784a992bbdSJeremy Kerr 				  unsigned int mtu, u8 tag)
7794a992bbdSJeremy Kerr {
7804a992bbdSJeremy Kerr 	const unsigned int hlen = sizeof(struct mctp_hdr);
7814a992bbdSJeremy Kerr 	struct mctp_hdr *hdr, *hdr2;
7824a9dda1cSMatt Johnston 	unsigned int pos, size, headroom;
7834a992bbdSJeremy Kerr 	struct sk_buff *skb2;
7844a992bbdSJeremy Kerr 	int rc;
7854a992bbdSJeremy Kerr 	u8 seq;
7864a992bbdSJeremy Kerr 
7874a992bbdSJeremy Kerr 	hdr = mctp_hdr(skb);
7884a992bbdSJeremy Kerr 	seq = 0;
7894a992bbdSJeremy Kerr 	rc = 0;
7904a992bbdSJeremy Kerr 
7914a992bbdSJeremy Kerr 	if (mtu < hlen + 1) {
7924a992bbdSJeremy Kerr 		kfree_skb(skb);
7934a992bbdSJeremy Kerr 		return -EMSGSIZE;
7944a992bbdSJeremy Kerr 	}
7954a992bbdSJeremy Kerr 
7964a9dda1cSMatt Johnston 	/* keep same headroom as the original skb */
7974a9dda1cSMatt Johnston 	headroom = skb_headroom(skb);
7984a9dda1cSMatt Johnston 
7994a992bbdSJeremy Kerr 	/* we've got the header */
8004a992bbdSJeremy Kerr 	skb_pull(skb, hlen);
8014a992bbdSJeremy Kerr 
8024a992bbdSJeremy Kerr 	for (pos = 0; pos < skb->len;) {
8034a992bbdSJeremy Kerr 		/* size of message payload */
8044a992bbdSJeremy Kerr 		size = min(mtu - hlen, skb->len - pos);
8054a992bbdSJeremy Kerr 
8064a9dda1cSMatt Johnston 		skb2 = alloc_skb(headroom + hlen + size, GFP_KERNEL);
8074a992bbdSJeremy Kerr 		if (!skb2) {
8084a992bbdSJeremy Kerr 			rc = -ENOMEM;
8094a992bbdSJeremy Kerr 			break;
8104a992bbdSJeremy Kerr 		}
8114a992bbdSJeremy Kerr 
8124a992bbdSJeremy Kerr 		/* generic skb copy */
8134a992bbdSJeremy Kerr 		skb2->protocol = skb->protocol;
8144a992bbdSJeremy Kerr 		skb2->priority = skb->priority;
8154a992bbdSJeremy Kerr 		skb2->dev = skb->dev;
8164a992bbdSJeremy Kerr 		memcpy(skb2->cb, skb->cb, sizeof(skb2->cb));
8174a992bbdSJeremy Kerr 
8184a992bbdSJeremy Kerr 		if (skb->sk)
8194a992bbdSJeremy Kerr 			skb_set_owner_w(skb2, skb->sk);
8204a992bbdSJeremy Kerr 
8214a992bbdSJeremy Kerr 		/* establish packet */
8224a9dda1cSMatt Johnston 		skb_reserve(skb2, headroom);
8234a992bbdSJeremy Kerr 		skb_reset_network_header(skb2);
8244a992bbdSJeremy Kerr 		skb_put(skb2, hlen + size);
8254a992bbdSJeremy Kerr 		skb2->transport_header = skb2->network_header + hlen;
8264a992bbdSJeremy Kerr 
8274a992bbdSJeremy Kerr 		/* copy header fields, calculate SOM/EOM flags & seq */
8284a992bbdSJeremy Kerr 		hdr2 = mctp_hdr(skb2);
8294a992bbdSJeremy Kerr 		hdr2->ver = hdr->ver;
8304a992bbdSJeremy Kerr 		hdr2->dest = hdr->dest;
8314a992bbdSJeremy Kerr 		hdr2->src = hdr->src;
8324a992bbdSJeremy Kerr 		hdr2->flags_seq_tag = tag &
8334a992bbdSJeremy Kerr 			(MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
8344a992bbdSJeremy Kerr 
8354a992bbdSJeremy Kerr 		if (pos == 0)
8364a992bbdSJeremy Kerr 			hdr2->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
8374a992bbdSJeremy Kerr 
8384a992bbdSJeremy Kerr 		if (pos + size == skb->len)
8394a992bbdSJeremy Kerr 			hdr2->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
8404a992bbdSJeremy Kerr 
8414a992bbdSJeremy Kerr 		hdr2->flags_seq_tag |= seq << MCTP_HDR_SEQ_SHIFT;
8424a992bbdSJeremy Kerr 
8434a992bbdSJeremy Kerr 		/* copy message payload */
8444a992bbdSJeremy Kerr 		skb_copy_bits(skb, pos, skb_transport_header(skb2), size);
8454a992bbdSJeremy Kerr 
846*be503653SJeremy Kerr 		/* we need to copy the extensions, for MCTP flow data */
847*be503653SJeremy Kerr 		skb_ext_copy(skb2, skb);
848*be503653SJeremy Kerr 
84999ce45d5SJeremy Kerr 		/* do route */
8504a992bbdSJeremy Kerr 		rc = rt->output(rt, skb2);
8514a992bbdSJeremy Kerr 		if (rc)
8524a992bbdSJeremy Kerr 			break;
8534a992bbdSJeremy Kerr 
8544a992bbdSJeremy Kerr 		seq = (seq + 1) & MCTP_HDR_SEQ_MASK;
8554a992bbdSJeremy Kerr 		pos += size;
8564a992bbdSJeremy Kerr 	}
8574a992bbdSJeremy Kerr 
8584a992bbdSJeremy Kerr 	consume_skb(skb);
8594a992bbdSJeremy Kerr 	return rc;
8604a992bbdSJeremy Kerr }
8614a992bbdSJeremy Kerr 
mctp_local_output(struct sock * sk,struct mctp_route * rt,struct sk_buff * skb,mctp_eid_t daddr,u8 req_tag)862889b7da2SJeremy Kerr int mctp_local_output(struct sock *sk, struct mctp_route *rt,
863889b7da2SJeremy Kerr 		      struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag)
864889b7da2SJeremy Kerr {
865833ef3b9SJeremy Kerr 	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
866889b7da2SJeremy Kerr 	struct mctp_skb_cb *cb = mctp_cb(skb);
867dc121c00SMatt Johnston 	struct mctp_route tmp_rt = {0};
868212c10c3SJeremy Kerr 	struct mctp_sk_key *key;
869889b7da2SJeremy Kerr 	struct mctp_hdr *hdr;
870889b7da2SJeremy Kerr 	unsigned long flags;
8714a992bbdSJeremy Kerr 	unsigned int mtu;
872889b7da2SJeremy Kerr 	mctp_eid_t saddr;
87399ce45d5SJeremy Kerr 	bool ext_rt;
874889b7da2SJeremy Kerr 	int rc;
875833ef3b9SJeremy Kerr 	u8 tag;
876889b7da2SJeremy Kerr 
87799ce45d5SJeremy Kerr 	rc = -ENODEV;
87899ce45d5SJeremy Kerr 
87999ce45d5SJeremy Kerr 	if (rt) {
88099ce45d5SJeremy Kerr 		ext_rt = false;
881889b7da2SJeremy Kerr 		if (WARN_ON(!rt->dev))
88299ce45d5SJeremy Kerr 			goto out_release;
88399ce45d5SJeremy Kerr 
88499ce45d5SJeremy Kerr 	} else if (cb->ifindex) {
885e297db3eSMatt Johnston 		struct net_device *dev;
886e297db3eSMatt Johnston 
88799ce45d5SJeremy Kerr 		ext_rt = true;
88899ce45d5SJeremy Kerr 		rt = &tmp_rt;
88999ce45d5SJeremy Kerr 
89099ce45d5SJeremy Kerr 		rcu_read_lock();
89199ce45d5SJeremy Kerr 		dev = dev_get_by_index_rcu(sock_net(sk), cb->ifindex);
89299ce45d5SJeremy Kerr 		if (!dev) {
89399ce45d5SJeremy Kerr 			rcu_read_unlock();
894cbebc55cSJeremy Kerr 			goto out_free;
89599ce45d5SJeremy Kerr 		}
89699ce45d5SJeremy Kerr 		rt->dev = __mctp_dev_get(dev);
89799ce45d5SJeremy Kerr 		rcu_read_unlock();
89899ce45d5SJeremy Kerr 
89999ce45d5SJeremy Kerr 		if (!rt->dev)
90099ce45d5SJeremy Kerr 			goto out_release;
90199ce45d5SJeremy Kerr 
90299ce45d5SJeremy Kerr 		/* establish temporary route - we set up enough to keep
90399ce45d5SJeremy Kerr 		 * mctp_route_output happy
90499ce45d5SJeremy Kerr 		 */
90599ce45d5SJeremy Kerr 		rt->output = mctp_route_output;
90699ce45d5SJeremy Kerr 		rt->mtu = 0;
90799ce45d5SJeremy Kerr 
90899ce45d5SJeremy Kerr 	} else {
909cbebc55cSJeremy Kerr 		rc = -EINVAL;
910cbebc55cSJeremy Kerr 		goto out_free;
91199ce45d5SJeremy Kerr 	}
912889b7da2SJeremy Kerr 
913889b7da2SJeremy Kerr 	spin_lock_irqsave(&rt->dev->addrs_lock, flags);
914889b7da2SJeremy Kerr 	if (rt->dev->num_addrs == 0) {
915889b7da2SJeremy Kerr 		rc = -EHOSTUNREACH;
916889b7da2SJeremy Kerr 	} else {
917889b7da2SJeremy Kerr 		/* use the outbound interface's first address as our source */
918889b7da2SJeremy Kerr 		saddr = rt->dev->addrs[0];
919889b7da2SJeremy Kerr 		rc = 0;
920889b7da2SJeremy Kerr 	}
921889b7da2SJeremy Kerr 	spin_unlock_irqrestore(&rt->dev->addrs_lock, flags);
922889b7da2SJeremy Kerr 
923889b7da2SJeremy Kerr 	if (rc)
92499ce45d5SJeremy Kerr 		goto out_release;
925889b7da2SJeremy Kerr 
92663ed1aabSMatt Johnston 	if (req_tag & MCTP_TAG_OWNER) {
92763ed1aabSMatt Johnston 		if (req_tag & MCTP_TAG_PREALLOC)
92863ed1aabSMatt Johnston 			key = mctp_lookup_prealloc_tag(msk, daddr,
92963ed1aabSMatt Johnston 						       req_tag, &tag);
93063ed1aabSMatt Johnston 		else
93163ed1aabSMatt Johnston 			key = mctp_alloc_local_tag(msk, daddr, saddr,
93263ed1aabSMatt Johnston 						   false, &tag);
93363ed1aabSMatt Johnston 
934212c10c3SJeremy Kerr 		if (IS_ERR(key)) {
935212c10c3SJeremy Kerr 			rc = PTR_ERR(key);
93699ce45d5SJeremy Kerr 			goto out_release;
937212c10c3SJeremy Kerr 		}
93867737c45SJeremy Kerr 		mctp_skb_set_flow(skb, key);
939212c10c3SJeremy Kerr 		/* done with the key in this scope */
940212c10c3SJeremy Kerr 		mctp_key_unref(key);
941833ef3b9SJeremy Kerr 		tag |= MCTP_HDR_FLAG_TO;
942833ef3b9SJeremy Kerr 	} else {
943212c10c3SJeremy Kerr 		key = NULL;
94463ed1aabSMatt Johnston 		tag = req_tag & MCTP_TAG_MASK;
945833ef3b9SJeremy Kerr 	}
946833ef3b9SJeremy Kerr 
9474a992bbdSJeremy Kerr 	skb->protocol = htons(ETH_P_MCTP);
9484a992bbdSJeremy Kerr 	skb->priority = 0;
949889b7da2SJeremy Kerr 	skb_reset_transport_header(skb);
950889b7da2SJeremy Kerr 	skb_push(skb, sizeof(struct mctp_hdr));
951889b7da2SJeremy Kerr 	skb_reset_network_header(skb);
952833ef3b9SJeremy Kerr 	skb->dev = rt->dev->dev;
953889b7da2SJeremy Kerr 
954889b7da2SJeremy Kerr 	/* cb->net will have been set on initial ingress */
955889b7da2SJeremy Kerr 	cb->src = saddr;
956889b7da2SJeremy Kerr 
9574a992bbdSJeremy Kerr 	/* set up common header fields */
9584a992bbdSJeremy Kerr 	hdr = mctp_hdr(skb);
9594a992bbdSJeremy Kerr 	hdr->ver = 1;
9604a992bbdSJeremy Kerr 	hdr->dest = daddr;
9614a992bbdSJeremy Kerr 	hdr->src = saddr;
9624a992bbdSJeremy Kerr 
9634a992bbdSJeremy Kerr 	mtu = mctp_route_mtu(rt);
9644a992bbdSJeremy Kerr 
9654a992bbdSJeremy Kerr 	if (skb->len + sizeof(struct mctp_hdr) <= mtu) {
96699ce45d5SJeremy Kerr 		hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
96799ce45d5SJeremy Kerr 			MCTP_HDR_FLAG_EOM | tag;
96899ce45d5SJeremy Kerr 		rc = rt->output(rt, skb);
9694a992bbdSJeremy Kerr 	} else {
97099ce45d5SJeremy Kerr 		rc = mctp_do_fragment_route(rt, skb, mtu, tag);
9714a992bbdSJeremy Kerr 	}
97299ce45d5SJeremy Kerr 
973cbebc55cSJeremy Kerr 	/* route output functions consume the skb, even on error */
974cbebc55cSJeremy Kerr 	skb = NULL;
975cbebc55cSJeremy Kerr 
97699ce45d5SJeremy Kerr out_release:
97799ce45d5SJeremy Kerr 	if (!ext_rt)
97899ce45d5SJeremy Kerr 		mctp_route_release(rt);
97999ce45d5SJeremy Kerr 
980dc121c00SMatt Johnston 	mctp_dev_put(tmp_rt.dev);
98199ce45d5SJeremy Kerr 
982cbebc55cSJeremy Kerr out_free:
983cbebc55cSJeremy Kerr 	kfree_skb(skb);
98499ce45d5SJeremy Kerr 	return rc;
985889b7da2SJeremy Kerr }
986889b7da2SJeremy Kerr 
987889b7da2SJeremy Kerr /* route management */
mctp_route_add(struct mctp_dev * mdev,mctp_eid_t daddr_start,unsigned int daddr_extent,unsigned int mtu,unsigned char type)98806d2f4c5SMatt Johnston static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start,
98906d2f4c5SMatt Johnston 			  unsigned int daddr_extent, unsigned int mtu,
99083f0a0b7SJeremy Kerr 			  unsigned char type)
991889b7da2SJeremy Kerr {
99283f0a0b7SJeremy Kerr 	int (*rtfn)(struct mctp_route *rt, struct sk_buff *skb);
993889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
994889b7da2SJeremy Kerr 	struct mctp_route *rt, *ert;
995889b7da2SJeremy Kerr 
996cb196b72SJeremy Kerr 	if (!mctp_address_unicast(daddr_start))
99706d2f4c5SMatt Johnston 		return -EINVAL;
99806d2f4c5SMatt Johnston 
99906d2f4c5SMatt Johnston 	if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
100006d2f4c5SMatt Johnston 		return -EINVAL;
100106d2f4c5SMatt Johnston 
100283f0a0b7SJeremy Kerr 	switch (type) {
100383f0a0b7SJeremy Kerr 	case RTN_LOCAL:
100483f0a0b7SJeremy Kerr 		rtfn = mctp_route_input;
100583f0a0b7SJeremy Kerr 		break;
100683f0a0b7SJeremy Kerr 	case RTN_UNICAST:
100783f0a0b7SJeremy Kerr 		rtfn = mctp_route_output;
100883f0a0b7SJeremy Kerr 		break;
100983f0a0b7SJeremy Kerr 	default:
101083f0a0b7SJeremy Kerr 		return -EINVAL;
101183f0a0b7SJeremy Kerr 	}
101283f0a0b7SJeremy Kerr 
1013889b7da2SJeremy Kerr 	rt = mctp_route_alloc();
1014889b7da2SJeremy Kerr 	if (!rt)
1015889b7da2SJeremy Kerr 		return -ENOMEM;
1016889b7da2SJeremy Kerr 
101706d2f4c5SMatt Johnston 	rt->min = daddr_start;
101806d2f4c5SMatt Johnston 	rt->max = daddr_start + daddr_extent;
101906d2f4c5SMatt Johnston 	rt->mtu = mtu;
1020889b7da2SJeremy Kerr 	rt->dev = mdev;
102143f55f23SJeremy Kerr 	mctp_dev_hold(rt->dev);
102283f0a0b7SJeremy Kerr 	rt->type = type;
102383f0a0b7SJeremy Kerr 	rt->output = rtfn;
1024889b7da2SJeremy Kerr 
1025889b7da2SJeremy Kerr 	ASSERT_RTNL();
1026889b7da2SJeremy Kerr 	/* Prevent duplicate identical routes. */
1027889b7da2SJeremy Kerr 	list_for_each_entry(ert, &net->mctp.routes, list) {
1028889b7da2SJeremy Kerr 		if (mctp_rt_compare_exact(rt, ert)) {
1029889b7da2SJeremy Kerr 			mctp_route_release(rt);
1030889b7da2SJeremy Kerr 			return -EEXIST;
1031889b7da2SJeremy Kerr 		}
1032889b7da2SJeremy Kerr 	}
1033889b7da2SJeremy Kerr 
1034889b7da2SJeremy Kerr 	list_add_rcu(&rt->list, &net->mctp.routes);
1035889b7da2SJeremy Kerr 
1036889b7da2SJeremy Kerr 	return 0;
1037889b7da2SJeremy Kerr }
1038889b7da2SJeremy Kerr 
mctp_route_remove(struct mctp_dev * mdev,mctp_eid_t daddr_start,unsigned int daddr_extent,unsigned char type)103906d2f4c5SMatt Johnston static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
104076d00160SMatt Johnston 			     unsigned int daddr_extent, unsigned char type)
1041889b7da2SJeremy Kerr {
1042889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
1043889b7da2SJeremy Kerr 	struct mctp_route *rt, *tmp;
104406d2f4c5SMatt Johnston 	mctp_eid_t daddr_end;
104506d2f4c5SMatt Johnston 	bool dropped;
104606d2f4c5SMatt Johnston 
104706d2f4c5SMatt Johnston 	if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
104806d2f4c5SMatt Johnston 		return -EINVAL;
104906d2f4c5SMatt Johnston 
105006d2f4c5SMatt Johnston 	daddr_end = daddr_start + daddr_extent;
105106d2f4c5SMatt Johnston 	dropped = false;
1052889b7da2SJeremy Kerr 
1053889b7da2SJeremy Kerr 	ASSERT_RTNL();
1054889b7da2SJeremy Kerr 
1055889b7da2SJeremy Kerr 	list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
105606d2f4c5SMatt Johnston 		if (rt->dev == mdev &&
105776d00160SMatt Johnston 		    rt->min == daddr_start && rt->max == daddr_end &&
105876d00160SMatt Johnston 		    rt->type == type) {
1059889b7da2SJeremy Kerr 			list_del_rcu(&rt->list);
1060889b7da2SJeremy Kerr 			/* TODO: immediate RTM_DELROUTE */
1061889b7da2SJeremy Kerr 			mctp_route_release(rt);
106206d2f4c5SMatt Johnston 			dropped = true;
1063889b7da2SJeremy Kerr 		}
1064889b7da2SJeremy Kerr 	}
1065889b7da2SJeremy Kerr 
106606d2f4c5SMatt Johnston 	return dropped ? 0 : -ENOENT;
106706d2f4c5SMatt Johnston }
106806d2f4c5SMatt Johnston 
mctp_route_add_local(struct mctp_dev * mdev,mctp_eid_t addr)106906d2f4c5SMatt Johnston int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr)
107006d2f4c5SMatt Johnston {
107183f0a0b7SJeremy Kerr 	return mctp_route_add(mdev, addr, 0, 0, RTN_LOCAL);
107206d2f4c5SMatt Johnston }
107306d2f4c5SMatt Johnston 
mctp_route_remove_local(struct mctp_dev * mdev,mctp_eid_t addr)107406d2f4c5SMatt Johnston int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr)
107506d2f4c5SMatt Johnston {
107676d00160SMatt Johnston 	return mctp_route_remove(mdev, addr, 0, RTN_LOCAL);
1077889b7da2SJeremy Kerr }
1078889b7da2SJeremy Kerr 
1079889b7da2SJeremy Kerr /* removes all entries for a given device */
mctp_route_remove_dev(struct mctp_dev * mdev)1080889b7da2SJeremy Kerr void mctp_route_remove_dev(struct mctp_dev *mdev)
1081889b7da2SJeremy Kerr {
1082889b7da2SJeremy Kerr 	struct net *net = dev_net(mdev->dev);
1083889b7da2SJeremy Kerr 	struct mctp_route *rt, *tmp;
1084889b7da2SJeremy Kerr 
1085889b7da2SJeremy Kerr 	ASSERT_RTNL();
1086889b7da2SJeremy Kerr 	list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
1087889b7da2SJeremy Kerr 		if (rt->dev == mdev) {
1088889b7da2SJeremy Kerr 			list_del_rcu(&rt->list);
1089889b7da2SJeremy Kerr 			/* TODO: immediate RTM_DELROUTE */
1090889b7da2SJeremy Kerr 			mctp_route_release(rt);
1091889b7da2SJeremy Kerr 		}
1092889b7da2SJeremy Kerr 	}
1093889b7da2SJeremy Kerr }
1094889b7da2SJeremy Kerr 
1095889b7da2SJeremy Kerr /* Incoming packet-handling */
1096889b7da2SJeremy Kerr 
mctp_pkttype_receive(struct sk_buff * skb,struct net_device * dev,struct packet_type * pt,struct net_device * orig_dev)1097889b7da2SJeremy Kerr static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
1098889b7da2SJeremy Kerr 				struct packet_type *pt,
1099889b7da2SJeremy Kerr 				struct net_device *orig_dev)
1100889b7da2SJeremy Kerr {
1101889b7da2SJeremy Kerr 	struct net *net = dev_net(dev);
1102f364dd71SMatt Johnston 	struct mctp_dev *mdev;
1103889b7da2SJeremy Kerr 	struct mctp_skb_cb *cb;
1104889b7da2SJeremy Kerr 	struct mctp_route *rt;
1105889b7da2SJeremy Kerr 	struct mctp_hdr *mh;
1106889b7da2SJeremy Kerr 
1107f364dd71SMatt Johnston 	rcu_read_lock();
1108f364dd71SMatt Johnston 	mdev = __mctp_dev_get(dev);
1109f364dd71SMatt Johnston 	rcu_read_unlock();
1110f364dd71SMatt Johnston 	if (!mdev) {
1111889b7da2SJeremy Kerr 		/* basic non-data sanity checks */
1112889b7da2SJeremy Kerr 		goto err_drop;
1113f364dd71SMatt Johnston 	}
1114889b7da2SJeremy Kerr 
1115889b7da2SJeremy Kerr 	if (!pskb_may_pull(skb, sizeof(struct mctp_hdr)))
1116889b7da2SJeremy Kerr 		goto err_drop;
1117889b7da2SJeremy Kerr 
1118889b7da2SJeremy Kerr 	skb_reset_transport_header(skb);
1119889b7da2SJeremy Kerr 	skb_reset_network_header(skb);
1120889b7da2SJeremy Kerr 
1121889b7da2SJeremy Kerr 	/* We have enough for a header; decode and route */
1122889b7da2SJeremy Kerr 	mh = mctp_hdr(skb);
1123889b7da2SJeremy Kerr 	if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX)
1124889b7da2SJeremy Kerr 		goto err_drop;
1125889b7da2SJeremy Kerr 
112686cdfd63SJeremy Kerr 	/* source must be valid unicast or null; drop reserved ranges and
112786cdfd63SJeremy Kerr 	 * broadcast
112886cdfd63SJeremy Kerr 	 */
112986cdfd63SJeremy Kerr 	if (!(mctp_address_unicast(mh->src) || mctp_address_null(mh->src)))
113086cdfd63SJeremy Kerr 		goto err_drop;
113186cdfd63SJeremy Kerr 
113286cdfd63SJeremy Kerr 	/* dest address: as above, but allow broadcast */
113386cdfd63SJeremy Kerr 	if (!(mctp_address_unicast(mh->dest) || mctp_address_null(mh->dest) ||
113486cdfd63SJeremy Kerr 	      mctp_address_broadcast(mh->dest)))
113586cdfd63SJeremy Kerr 		goto err_drop;
113686cdfd63SJeremy Kerr 
113799ce45d5SJeremy Kerr 	/* MCTP drivers must populate halen/haddr */
113899ce45d5SJeremy Kerr 	if (dev->type == ARPHRD_MCTP) {
113999ce45d5SJeremy Kerr 		cb = mctp_cb(skb);
114099ce45d5SJeremy Kerr 	} else {
1141889b7da2SJeremy Kerr 		cb = __mctp_cb(skb);
114299ce45d5SJeremy Kerr 		cb->halen = 0;
114399ce45d5SJeremy Kerr 	}
1144f364dd71SMatt Johnston 	cb->net = READ_ONCE(mdev->net);
114599ce45d5SJeremy Kerr 	cb->ifindex = dev->ifindex;
1146889b7da2SJeremy Kerr 
1147889b7da2SJeremy Kerr 	rt = mctp_route_lookup(net, cb->net, mh->dest);
11481f6c77acSJeremy Kerr 
11491f6c77acSJeremy Kerr 	/* NULL EID, but addressed to our physical address */
11501f6c77acSJeremy Kerr 	if (!rt && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
11511f6c77acSJeremy Kerr 		rt = mctp_route_lookup_null(net, dev);
11521f6c77acSJeremy Kerr 
1153889b7da2SJeremy Kerr 	if (!rt)
1154889b7da2SJeremy Kerr 		goto err_drop;
1155889b7da2SJeremy Kerr 
115699ce45d5SJeremy Kerr 	rt->output(rt, skb);
115799ce45d5SJeremy Kerr 	mctp_route_release(rt);
1158dc121c00SMatt Johnston 	mctp_dev_put(mdev);
1159889b7da2SJeremy Kerr 
1160889b7da2SJeremy Kerr 	return NET_RX_SUCCESS;
1161889b7da2SJeremy Kerr 
1162889b7da2SJeremy Kerr err_drop:
1163889b7da2SJeremy Kerr 	kfree_skb(skb);
1164dc121c00SMatt Johnston 	mctp_dev_put(mdev);
1165889b7da2SJeremy Kerr 	return NET_RX_DROP;
1166889b7da2SJeremy Kerr }
1167889b7da2SJeremy Kerr 
1168889b7da2SJeremy Kerr static struct packet_type mctp_packet_type = {
1169889b7da2SJeremy Kerr 	.type = cpu_to_be16(ETH_P_MCTP),
1170889b7da2SJeremy Kerr 	.func = mctp_pkttype_receive,
1171889b7da2SJeremy Kerr };
1172889b7da2SJeremy Kerr 
117306d2f4c5SMatt Johnston /* netlink interface */
117406d2f4c5SMatt Johnston 
117506d2f4c5SMatt Johnston static const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = {
117606d2f4c5SMatt Johnston 	[RTA_DST]		= { .type = NLA_U8 },
117706d2f4c5SMatt Johnston 	[RTA_METRICS]		= { .type = NLA_NESTED },
117806d2f4c5SMatt Johnston 	[RTA_OIF]		= { .type = NLA_U32 },
117906d2f4c5SMatt Johnston };
118006d2f4c5SMatt Johnston 
118106d2f4c5SMatt Johnston /* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing.
118206d2f4c5SMatt Johnston  * tb must hold RTA_MAX+1 elements.
118306d2f4c5SMatt Johnston  */
mctp_route_nlparse(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack,struct nlattr ** tb,struct rtmsg ** rtm,struct mctp_dev ** mdev,mctp_eid_t * daddr_start)118406d2f4c5SMatt Johnston static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
118506d2f4c5SMatt Johnston 			      struct netlink_ext_ack *extack,
118606d2f4c5SMatt Johnston 			      struct nlattr **tb, struct rtmsg **rtm,
118706d2f4c5SMatt Johnston 			      struct mctp_dev **mdev, mctp_eid_t *daddr_start)
118806d2f4c5SMatt Johnston {
118906d2f4c5SMatt Johnston 	struct net *net = sock_net(skb->sk);
119006d2f4c5SMatt Johnston 	struct net_device *dev;
119106d2f4c5SMatt Johnston 	unsigned int ifindex;
119206d2f4c5SMatt Johnston 	int rc;
119306d2f4c5SMatt Johnston 
119406d2f4c5SMatt Johnston 	rc = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
119506d2f4c5SMatt Johnston 			 rta_mctp_policy, extack);
119606d2f4c5SMatt Johnston 	if (rc < 0) {
119706d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "incorrect format");
119806d2f4c5SMatt Johnston 		return rc;
119906d2f4c5SMatt Johnston 	}
120006d2f4c5SMatt Johnston 
120106d2f4c5SMatt Johnston 	if (!tb[RTA_DST]) {
120206d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "dst EID missing");
120306d2f4c5SMatt Johnston 		return -EINVAL;
120406d2f4c5SMatt Johnston 	}
120506d2f4c5SMatt Johnston 	*daddr_start = nla_get_u8(tb[RTA_DST]);
120606d2f4c5SMatt Johnston 
120706d2f4c5SMatt Johnston 	if (!tb[RTA_OIF]) {
120806d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "ifindex missing");
120906d2f4c5SMatt Johnston 		return -EINVAL;
121006d2f4c5SMatt Johnston 	}
121106d2f4c5SMatt Johnston 	ifindex = nla_get_u32(tb[RTA_OIF]);
121206d2f4c5SMatt Johnston 
121306d2f4c5SMatt Johnston 	*rtm = nlmsg_data(nlh);
121406d2f4c5SMatt Johnston 	if ((*rtm)->rtm_family != AF_MCTP) {
121506d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "route family must be AF_MCTP");
121606d2f4c5SMatt Johnston 		return -EINVAL;
121706d2f4c5SMatt Johnston 	}
121806d2f4c5SMatt Johnston 
121906d2f4c5SMatt Johnston 	dev = __dev_get_by_index(net, ifindex);
122006d2f4c5SMatt Johnston 	if (!dev) {
122106d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "bad ifindex");
122206d2f4c5SMatt Johnston 		return -ENODEV;
122306d2f4c5SMatt Johnston 	}
122406d2f4c5SMatt Johnston 	*mdev = mctp_dev_get_rtnl(dev);
122506d2f4c5SMatt Johnston 	if (!*mdev)
122606d2f4c5SMatt Johnston 		return -ENODEV;
122706d2f4c5SMatt Johnston 
122806d2f4c5SMatt Johnston 	if (dev->flags & IFF_LOOPBACK) {
122906d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "no routes to loopback");
123006d2f4c5SMatt Johnston 		return -EINVAL;
123106d2f4c5SMatt Johnston 	}
123206d2f4c5SMatt Johnston 
123306d2f4c5SMatt Johnston 	return 0;
123406d2f4c5SMatt Johnston }
123506d2f4c5SMatt Johnston 
12366183569dSMatt Johnston static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
12376183569dSMatt Johnston 	[RTAX_MTU]		= { .type = NLA_U32 },
12386183569dSMatt Johnston };
12396183569dSMatt Johnston 
mctp_newroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)124006d2f4c5SMatt Johnston static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
124106d2f4c5SMatt Johnston 			 struct netlink_ext_ack *extack)
124206d2f4c5SMatt Johnston {
124306d2f4c5SMatt Johnston 	struct nlattr *tb[RTA_MAX + 1];
12446183569dSMatt Johnston 	struct nlattr *tbx[RTAX_MAX + 1];
124506d2f4c5SMatt Johnston 	mctp_eid_t daddr_start;
124606d2f4c5SMatt Johnston 	struct mctp_dev *mdev;
124706d2f4c5SMatt Johnston 	struct rtmsg *rtm;
124806d2f4c5SMatt Johnston 	unsigned int mtu;
124906d2f4c5SMatt Johnston 	int rc;
125006d2f4c5SMatt Johnston 
125106d2f4c5SMatt Johnston 	rc = mctp_route_nlparse(skb, nlh, extack, tb,
125206d2f4c5SMatt Johnston 				&rtm, &mdev, &daddr_start);
125306d2f4c5SMatt Johnston 	if (rc < 0)
125406d2f4c5SMatt Johnston 		return rc;
125506d2f4c5SMatt Johnston 
125606d2f4c5SMatt Johnston 	if (rtm->rtm_type != RTN_UNICAST) {
125706d2f4c5SMatt Johnston 		NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
125806d2f4c5SMatt Johnston 		return -EINVAL;
125906d2f4c5SMatt Johnston 	}
126006d2f4c5SMatt Johnston 
126106d2f4c5SMatt Johnston 	mtu = 0;
12626183569dSMatt Johnston 	if (tb[RTA_METRICS]) {
12636183569dSMatt Johnston 		rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS],
12646183569dSMatt Johnston 				      rta_metrics_policy, NULL);
12656183569dSMatt Johnston 		if (rc < 0)
12666183569dSMatt Johnston 			return rc;
12676183569dSMatt Johnston 		if (tbx[RTAX_MTU])
12686183569dSMatt Johnston 			mtu = nla_get_u32(tbx[RTAX_MTU]);
12696183569dSMatt Johnston 	}
127006d2f4c5SMatt Johnston 
127183f0a0b7SJeremy Kerr 	rc = mctp_route_add(mdev, daddr_start, rtm->rtm_dst_len, mtu,
127283f0a0b7SJeremy Kerr 			    rtm->rtm_type);
127306d2f4c5SMatt Johnston 	return rc;
127406d2f4c5SMatt Johnston }
127506d2f4c5SMatt Johnston 
mctp_delroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)127606d2f4c5SMatt Johnston static int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
127706d2f4c5SMatt Johnston 			 struct netlink_ext_ack *extack)
127806d2f4c5SMatt Johnston {
127906d2f4c5SMatt Johnston 	struct nlattr *tb[RTA_MAX + 1];
128006d2f4c5SMatt Johnston 	mctp_eid_t daddr_start;
128106d2f4c5SMatt Johnston 	struct mctp_dev *mdev;
128206d2f4c5SMatt Johnston 	struct rtmsg *rtm;
128306d2f4c5SMatt Johnston 	int rc;
128406d2f4c5SMatt Johnston 
128506d2f4c5SMatt Johnston 	rc = mctp_route_nlparse(skb, nlh, extack, tb,
128606d2f4c5SMatt Johnston 				&rtm, &mdev, &daddr_start);
128706d2f4c5SMatt Johnston 	if (rc < 0)
128806d2f4c5SMatt Johnston 		return rc;
128906d2f4c5SMatt Johnston 
129006d2f4c5SMatt Johnston 	/* we only have unicast routes */
129106d2f4c5SMatt Johnston 	if (rtm->rtm_type != RTN_UNICAST)
129206d2f4c5SMatt Johnston 		return -EINVAL;
129306d2f4c5SMatt Johnston 
129476d00160SMatt Johnston 	rc = mctp_route_remove(mdev, daddr_start, rtm->rtm_dst_len, RTN_UNICAST);
129506d2f4c5SMatt Johnston 	return rc;
129606d2f4c5SMatt Johnston }
129706d2f4c5SMatt Johnston 
mctp_fill_rtinfo(struct sk_buff * skb,struct mctp_route * rt,u32 portid,u32 seq,int event,unsigned int flags)129806d2f4c5SMatt Johnston static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
129906d2f4c5SMatt Johnston 			    u32 portid, u32 seq, int event, unsigned int flags)
130006d2f4c5SMatt Johnston {
130106d2f4c5SMatt Johnston 	struct nlmsghdr *nlh;
130206d2f4c5SMatt Johnston 	struct rtmsg *hdr;
130306d2f4c5SMatt Johnston 	void *metrics;
130406d2f4c5SMatt Johnston 
130506d2f4c5SMatt Johnston 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags);
130606d2f4c5SMatt Johnston 	if (!nlh)
130706d2f4c5SMatt Johnston 		return -EMSGSIZE;
130806d2f4c5SMatt Johnston 
130906d2f4c5SMatt Johnston 	hdr = nlmsg_data(nlh);
131006d2f4c5SMatt Johnston 	hdr->rtm_family = AF_MCTP;
131106d2f4c5SMatt Johnston 
131206d2f4c5SMatt Johnston 	/* we use the _len fields as a number of EIDs, rather than
131306d2f4c5SMatt Johnston 	 * a number of bits in the address
131406d2f4c5SMatt Johnston 	 */
131506d2f4c5SMatt Johnston 	hdr->rtm_dst_len = rt->max - rt->min;
131606d2f4c5SMatt Johnston 	hdr->rtm_src_len = 0;
131706d2f4c5SMatt Johnston 	hdr->rtm_tos = 0;
131806d2f4c5SMatt Johnston 	hdr->rtm_table = RT_TABLE_DEFAULT;
131906d2f4c5SMatt Johnston 	hdr->rtm_protocol = RTPROT_STATIC; /* everything is user-defined */
132006d2f4c5SMatt Johnston 	hdr->rtm_scope = RT_SCOPE_LINK; /* TODO: scope in mctp_route? */
132183f0a0b7SJeremy Kerr 	hdr->rtm_type = rt->type;
132206d2f4c5SMatt Johnston 
132306d2f4c5SMatt Johnston 	if (nla_put_u8(skb, RTA_DST, rt->min))
132406d2f4c5SMatt Johnston 		goto cancel;
132506d2f4c5SMatt Johnston 
132606d2f4c5SMatt Johnston 	metrics = nla_nest_start_noflag(skb, RTA_METRICS);
132706d2f4c5SMatt Johnston 	if (!metrics)
132806d2f4c5SMatt Johnston 		goto cancel;
132906d2f4c5SMatt Johnston 
133006d2f4c5SMatt Johnston 	if (rt->mtu) {
133106d2f4c5SMatt Johnston 		if (nla_put_u32(skb, RTAX_MTU, rt->mtu))
133206d2f4c5SMatt Johnston 			goto cancel;
133306d2f4c5SMatt Johnston 	}
133406d2f4c5SMatt Johnston 
133506d2f4c5SMatt Johnston 	nla_nest_end(skb, metrics);
133606d2f4c5SMatt Johnston 
133706d2f4c5SMatt Johnston 	if (rt->dev) {
133806d2f4c5SMatt Johnston 		if (nla_put_u32(skb, RTA_OIF, rt->dev->dev->ifindex))
133906d2f4c5SMatt Johnston 			goto cancel;
134006d2f4c5SMatt Johnston 	}
134106d2f4c5SMatt Johnston 
134206d2f4c5SMatt Johnston 	/* TODO: conditional neighbour physaddr? */
134306d2f4c5SMatt Johnston 
134406d2f4c5SMatt Johnston 	nlmsg_end(skb, nlh);
134506d2f4c5SMatt Johnston 
134606d2f4c5SMatt Johnston 	return 0;
134706d2f4c5SMatt Johnston 
134806d2f4c5SMatt Johnston cancel:
134906d2f4c5SMatt Johnston 	nlmsg_cancel(skb, nlh);
135006d2f4c5SMatt Johnston 	return -EMSGSIZE;
135106d2f4c5SMatt Johnston }
135206d2f4c5SMatt Johnston 
mctp_dump_rtinfo(struct sk_buff * skb,struct netlink_callback * cb)135306d2f4c5SMatt Johnston static int mctp_dump_rtinfo(struct sk_buff *skb, struct netlink_callback *cb)
135406d2f4c5SMatt Johnston {
135506d2f4c5SMatt Johnston 	struct net *net = sock_net(skb->sk);
135606d2f4c5SMatt Johnston 	struct mctp_route *rt;
135706d2f4c5SMatt Johnston 	int s_idx, idx;
135806d2f4c5SMatt Johnston 
135906d2f4c5SMatt Johnston 	/* TODO: allow filtering on route data, possibly under
136006d2f4c5SMatt Johnston 	 * cb->strict_check
136106d2f4c5SMatt Johnston 	 */
136206d2f4c5SMatt Johnston 
136306d2f4c5SMatt Johnston 	/* TODO: change to struct overlay */
136406d2f4c5SMatt Johnston 	s_idx = cb->args[0];
136506d2f4c5SMatt Johnston 	idx = 0;
136606d2f4c5SMatt Johnston 
136706d2f4c5SMatt Johnston 	rcu_read_lock();
136806d2f4c5SMatt Johnston 	list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
136906d2f4c5SMatt Johnston 		if (idx++ < s_idx)
137006d2f4c5SMatt Johnston 			continue;
137106d2f4c5SMatt Johnston 		if (mctp_fill_rtinfo(skb, rt,
137206d2f4c5SMatt Johnston 				     NETLINK_CB(cb->skb).portid,
137306d2f4c5SMatt Johnston 				     cb->nlh->nlmsg_seq,
137406d2f4c5SMatt Johnston 				     RTM_NEWROUTE, NLM_F_MULTI) < 0)
137506d2f4c5SMatt Johnston 			break;
137606d2f4c5SMatt Johnston 	}
137706d2f4c5SMatt Johnston 
137806d2f4c5SMatt Johnston 	rcu_read_unlock();
137906d2f4c5SMatt Johnston 	cb->args[0] = idx;
138006d2f4c5SMatt Johnston 
138106d2f4c5SMatt Johnston 	return skb->len;
138206d2f4c5SMatt Johnston }
138306d2f4c5SMatt Johnston 
1384889b7da2SJeremy Kerr /* net namespace implementation */
mctp_routes_net_init(struct net * net)1385889b7da2SJeremy Kerr static int __net_init mctp_routes_net_init(struct net *net)
1386889b7da2SJeremy Kerr {
1387889b7da2SJeremy Kerr 	struct netns_mctp *ns = &net->mctp;
1388889b7da2SJeremy Kerr 
1389889b7da2SJeremy Kerr 	INIT_LIST_HEAD(&ns->routes);
1390833ef3b9SJeremy Kerr 	INIT_HLIST_HEAD(&ns->binds);
1391833ef3b9SJeremy Kerr 	mutex_init(&ns->bind_lock);
1392833ef3b9SJeremy Kerr 	INIT_HLIST_HEAD(&ns->keys);
1393833ef3b9SJeremy Kerr 	spin_lock_init(&ns->keys_lock);
139403f2bbc4SMatt Johnston 	WARN_ON(mctp_default_net_set(net, MCTP_INITIAL_DEFAULT_NET));
1395889b7da2SJeremy Kerr 	return 0;
1396889b7da2SJeremy Kerr }
1397889b7da2SJeremy Kerr 
mctp_routes_net_exit(struct net * net)1398889b7da2SJeremy Kerr static void __net_exit mctp_routes_net_exit(struct net *net)
1399889b7da2SJeremy Kerr {
1400889b7da2SJeremy Kerr 	struct mctp_route *rt;
1401889b7da2SJeremy Kerr 
1402581edcd0SJeremy Kerr 	rcu_read_lock();
1403889b7da2SJeremy Kerr 	list_for_each_entry_rcu(rt, &net->mctp.routes, list)
1404889b7da2SJeremy Kerr 		mctp_route_release(rt);
1405581edcd0SJeremy Kerr 	rcu_read_unlock();
1406889b7da2SJeremy Kerr }
1407889b7da2SJeremy Kerr 
1408889b7da2SJeremy Kerr static struct pernet_operations mctp_net_ops = {
1409889b7da2SJeremy Kerr 	.init = mctp_routes_net_init,
1410889b7da2SJeremy Kerr 	.exit = mctp_routes_net_exit,
1411889b7da2SJeremy Kerr };
1412889b7da2SJeremy Kerr 
mctp_routes_init(void)1413889b7da2SJeremy Kerr int __init mctp_routes_init(void)
1414889b7da2SJeremy Kerr {
1415889b7da2SJeremy Kerr 	dev_add_pack(&mctp_packet_type);
141606d2f4c5SMatt Johnston 
141706d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_GETROUTE,
141806d2f4c5SMatt Johnston 			     NULL, mctp_dump_rtinfo, 0);
141906d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_NEWROUTE,
142006d2f4c5SMatt Johnston 			     mctp_newroute, NULL, 0);
142106d2f4c5SMatt Johnston 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_DELROUTE,
142206d2f4c5SMatt Johnston 			     mctp_delroute, NULL, 0);
142306d2f4c5SMatt Johnston 
1424889b7da2SJeremy Kerr 	return register_pernet_subsys(&mctp_net_ops);
1425889b7da2SJeremy Kerr }
1426889b7da2SJeremy Kerr 
mctp_routes_exit(void)1427d4072058SWei Yongjun void mctp_routes_exit(void)
1428889b7da2SJeremy Kerr {
1429889b7da2SJeremy Kerr 	unregister_pernet_subsys(&mctp_net_ops);
143006d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_DELROUTE);
143106d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_NEWROUTE);
143206d2f4c5SMatt Johnston 	rtnl_unregister(PF_MCTP, RTM_GETROUTE);
1433889b7da2SJeremy Kerr 	dev_remove_pack(&mctp_packet_type);
1434889b7da2SJeremy Kerr }
1435161eba50SJeremy Kerr 
1436161eba50SJeremy Kerr #if IS_ENABLED(CONFIG_MCTP_TEST)
1437161eba50SJeremy Kerr #include "test/route-test.c"
1438161eba50SJeremy Kerr #endif
1439