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
337*1ec141d8SJeremy Kerr /* We may be receiving a locally-routed packet; drop source sk
338*1ec141d8SJeremy Kerr * accounting.
339*1ec141d8SJeremy Kerr *
340*1ec141d8SJeremy Kerr * From here, we will either queue the skb - either to a frag_queue, or
341*1ec141d8SJeremy Kerr * to a receiving socket. When that succeeds, we clear the skb pointer;
342*1ec141d8SJeremy Kerr * a non-NULL skb on exit will be otherwise unowned, and hence
343*1ec141d8SJeremy Kerr * kfree_skb()-ed.
344833ef3b9SJeremy Kerr */
345833ef3b9SJeremy Kerr skb_orphan(skb);
346833ef3b9SJeremy Kerr
347833ef3b9SJeremy Kerr /* ensure we have enough data for a header and a type */
348833ef3b9SJeremy Kerr if (skb->len < sizeof(struct mctp_hdr) + 1)
3494a992bbdSJeremy Kerr goto out;
350833ef3b9SJeremy Kerr
351833ef3b9SJeremy Kerr /* grab header, advance data ptr */
352833ef3b9SJeremy Kerr mh = mctp_hdr(skb);
353833ef3b9SJeremy Kerr skb_pull(skb, sizeof(struct mctp_hdr));
354833ef3b9SJeremy Kerr
355833ef3b9SJeremy Kerr if (mh->ver != 1)
3564a992bbdSJeremy Kerr goto out;
357833ef3b9SJeremy Kerr
3584a992bbdSJeremy Kerr flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM);
3594a992bbdSJeremy Kerr tag = mh->flags_seq_tag & (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
360833ef3b9SJeremy Kerr
361833ef3b9SJeremy Kerr rcu_read_lock();
3624a992bbdSJeremy Kerr
36373c61845SJeremy Kerr /* lookup socket / reasm context, exactly matching (src,dest,tag).
36473c61845SJeremy Kerr * we hold a ref on the key, and key->lock held.
36573c61845SJeremy Kerr */
36673c61845SJeremy Kerr key = mctp_lookup_key(net, skb, mh->src, &f);
367833ef3b9SJeremy Kerr
3684a992bbdSJeremy Kerr if (flags & MCTP_HDR_FLAG_SOM) {
3694a992bbdSJeremy Kerr if (key) {
370833ef3b9SJeremy Kerr msk = container_of(key->sk, struct mctp_sock, sk);
3714a992bbdSJeremy Kerr } else {
3724a992bbdSJeremy Kerr /* first response to a broadcast? do a more general
3734a992bbdSJeremy Kerr * key lookup to find the socket, but don't use this
3744a992bbdSJeremy Kerr * key for reassembly - we'll create a more specific
3754a992bbdSJeremy Kerr * one for future packets if required (ie, !EOM).
3764a992bbdSJeremy Kerr */
3776e54ea37SPaolo Abeni any_key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
3786e54ea37SPaolo Abeni if (any_key) {
3796e54ea37SPaolo Abeni msk = container_of(any_key->sk,
3804a992bbdSJeremy Kerr struct mctp_sock, sk);
3816e54ea37SPaolo Abeni spin_unlock_irqrestore(&any_key->lock, f);
3824a992bbdSJeremy Kerr }
3834a992bbdSJeremy Kerr }
3844a992bbdSJeremy Kerr
3854a992bbdSJeremy Kerr if (!key && !msk && (tag & MCTP_HDR_FLAG_TO))
386833ef3b9SJeremy Kerr msk = mctp_lookup_bind(net, skb);
387833ef3b9SJeremy Kerr
3884a992bbdSJeremy Kerr if (!msk) {
3894a992bbdSJeremy Kerr rc = -ENOENT;
3904a992bbdSJeremy Kerr goto out_unlock;
3914a992bbdSJeremy Kerr }
392833ef3b9SJeremy Kerr
3934a992bbdSJeremy Kerr /* single-packet message? deliver to socket, clean up any
3944a992bbdSJeremy Kerr * pending key.
3954a992bbdSJeremy Kerr */
3964a992bbdSJeremy Kerr if (flags & MCTP_HDR_FLAG_EOM) {
397*1ec141d8SJeremy Kerr rc = sock_queue_rcv_skb(&msk->sk, skb);
398*1ec141d8SJeremy Kerr if (!rc)
399*1ec141d8SJeremy Kerr skb = NULL;
4004a992bbdSJeremy Kerr if (key) {
4014a992bbdSJeremy Kerr /* we've hit a pending reassembly; not much we
4024a992bbdSJeremy Kerr * can do but drop it
4034a992bbdSJeremy Kerr */
40463ed1aabSMatt Johnston __mctp_key_done_in(key, net, f,
4054f9e1ba6SJeremy Kerr MCTP_TRACE_KEY_REPLIED);
40673c61845SJeremy Kerr key = NULL;
4074a992bbdSJeremy Kerr }
4084a992bbdSJeremy Kerr goto out_unlock;
4094a992bbdSJeremy Kerr }
410833ef3b9SJeremy Kerr
4114a992bbdSJeremy Kerr /* broadcast response or a bind() - create a key for further
4124a992bbdSJeremy Kerr * packets for this message
4134a992bbdSJeremy Kerr */
4144a992bbdSJeremy Kerr if (!key) {
4154a992bbdSJeremy Kerr key = mctp_key_alloc(msk, mh->dest, mh->src,
4164a992bbdSJeremy Kerr tag, GFP_ATOMIC);
4174a992bbdSJeremy Kerr if (!key) {
4184a992bbdSJeremy Kerr rc = -ENOMEM;
4194a992bbdSJeremy Kerr goto out_unlock;
4204a992bbdSJeremy Kerr }
4214a992bbdSJeremy Kerr
42273c61845SJeremy Kerr /* we can queue without the key lock here, as the
4234a992bbdSJeremy Kerr * key isn't observable yet
4244a992bbdSJeremy Kerr */
4254a992bbdSJeremy Kerr mctp_frag_queue(key, skb);
4264a992bbdSJeremy Kerr
4274a992bbdSJeremy Kerr /* if the key_add fails, we've raced with another
4284a992bbdSJeremy Kerr * SOM packet with the same src, dest and tag. There's
4294a992bbdSJeremy Kerr * no way to distinguish future packets, so all we
4304a992bbdSJeremy Kerr * can do is drop; we'll free the skb on exit from
4314a992bbdSJeremy Kerr * this function.
4324a992bbdSJeremy Kerr */
4334a992bbdSJeremy Kerr rc = mctp_key_add(key, msk);
434*1ec141d8SJeremy Kerr if (!rc) {
4354f9e1ba6SJeremy Kerr trace_mctp_key_acquire(key);
436*1ec141d8SJeremy Kerr skb = NULL;
437*1ec141d8SJeremy Kerr }
4384f9e1ba6SJeremy Kerr
439de8a6b15SJeremy Kerr /* we don't need to release key->lock on exit, so
440de8a6b15SJeremy Kerr * clean up here and suppress the unlock via
441de8a6b15SJeremy Kerr * setting to NULL
442de8a6b15SJeremy Kerr */
4430b93aed2SMatt Johnston mctp_key_unref(key);
44473c61845SJeremy Kerr key = NULL;
4454a992bbdSJeremy Kerr
44673c61845SJeremy Kerr } else {
4474a992bbdSJeremy Kerr if (key->reasm_head || key->reasm_dead) {
4484a992bbdSJeremy Kerr /* duplicate start? drop everything */
44963ed1aabSMatt Johnston __mctp_key_done_in(key, net, f,
4504f9e1ba6SJeremy Kerr MCTP_TRACE_KEY_INVALIDATED);
4514a992bbdSJeremy Kerr rc = -EEXIST;
45273c61845SJeremy Kerr key = NULL;
4534a992bbdSJeremy Kerr } else {
4544a992bbdSJeremy Kerr rc = mctp_frag_queue(key, skb);
455*1ec141d8SJeremy Kerr if (!rc)
456*1ec141d8SJeremy Kerr skb = NULL;
4574a992bbdSJeremy Kerr }
4584a992bbdSJeremy Kerr }
4594a992bbdSJeremy Kerr
4604a992bbdSJeremy Kerr } else if (key) {
4614a992bbdSJeremy Kerr /* this packet continues a previous message; reassemble
4624a992bbdSJeremy Kerr * using the message-specific key
4634a992bbdSJeremy Kerr */
4644a992bbdSJeremy Kerr
4654a992bbdSJeremy Kerr /* we need to be continuing an existing reassembly... */
4664a992bbdSJeremy Kerr if (!key->reasm_head)
4674a992bbdSJeremy Kerr rc = -EINVAL;
4684a992bbdSJeremy Kerr else
4694a992bbdSJeremy Kerr rc = mctp_frag_queue(key, skb);
4704a992bbdSJeremy Kerr
471*1ec141d8SJeremy Kerr if (rc)
472*1ec141d8SJeremy Kerr goto out_unlock;
473*1ec141d8SJeremy Kerr
474*1ec141d8SJeremy Kerr /* we've queued; the queue owns the skb now */
475*1ec141d8SJeremy Kerr skb = NULL;
476*1ec141d8SJeremy Kerr
4774a992bbdSJeremy Kerr /* end of message? deliver to socket, and we're done with
4784a992bbdSJeremy Kerr * the reassembly/response key
4794a992bbdSJeremy Kerr */
480*1ec141d8SJeremy Kerr if (flags & MCTP_HDR_FLAG_EOM) {
481*1ec141d8SJeremy Kerr rc = sock_queue_rcv_skb(key->sk, key->reasm_head);
482*1ec141d8SJeremy Kerr if (!rc)
4834a992bbdSJeremy Kerr key->reasm_head = NULL;
48463ed1aabSMatt Johnston __mctp_key_done_in(key, net, f, MCTP_TRACE_KEY_REPLIED);
48573c61845SJeremy Kerr key = NULL;
4864a992bbdSJeremy Kerr }
4874a992bbdSJeremy Kerr
4884a992bbdSJeremy Kerr } else {
4894a992bbdSJeremy Kerr /* not a start, no matching key */
4904a992bbdSJeremy Kerr rc = -ENOENT;
4914a992bbdSJeremy Kerr }
4924a992bbdSJeremy Kerr
4934a992bbdSJeremy Kerr out_unlock:
494833ef3b9SJeremy Kerr rcu_read_unlock();
49573c61845SJeremy Kerr if (key) {
49673c61845SJeremy Kerr spin_unlock_irqrestore(&key->lock, f);
49773c61845SJeremy Kerr mctp_key_unref(key);
49873c61845SJeremy Kerr }
4996e54ea37SPaolo Abeni if (any_key)
5006e54ea37SPaolo Abeni mctp_key_unref(any_key);
5014a992bbdSJeremy Kerr out:
502889b7da2SJeremy Kerr kfree_skb(skb);
5034a992bbdSJeremy Kerr return rc;
5044a992bbdSJeremy Kerr }
5054a992bbdSJeremy Kerr
mctp_route_mtu(struct mctp_route * rt)5064a992bbdSJeremy Kerr static unsigned int mctp_route_mtu(struct mctp_route *rt)
5074a992bbdSJeremy Kerr {
5084a992bbdSJeremy Kerr return rt->mtu ?: READ_ONCE(rt->dev->dev->mtu);
509889b7da2SJeremy Kerr }
510889b7da2SJeremy Kerr
mctp_route_output(struct mctp_route * route,struct sk_buff * skb)51106d2f4c5SMatt Johnston static int mctp_route_output(struct mctp_route *route, struct sk_buff *skb)
512889b7da2SJeremy Kerr {
51399ce45d5SJeremy Kerr struct mctp_skb_cb *cb = mctp_cb(skb);
51426ab3fcaSMatt Johnston struct mctp_hdr *hdr = mctp_hdr(skb);
51526ab3fcaSMatt Johnston char daddr_buf[MAX_ADDR_LEN];
51626ab3fcaSMatt Johnston char *daddr = NULL;
517889b7da2SJeremy Kerr unsigned int mtu;
518889b7da2SJeremy Kerr int rc;
519889b7da2SJeremy Kerr
520889b7da2SJeremy Kerr skb->protocol = htons(ETH_P_MCTP);
521889b7da2SJeremy Kerr
522889b7da2SJeremy Kerr mtu = READ_ONCE(skb->dev->mtu);
523889b7da2SJeremy Kerr if (skb->len > mtu) {
524889b7da2SJeremy Kerr kfree_skb(skb);
525889b7da2SJeremy Kerr return -EMSGSIZE;
526889b7da2SJeremy Kerr }
527889b7da2SJeremy Kerr
52899ce45d5SJeremy Kerr if (cb->ifindex) {
52999ce45d5SJeremy Kerr /* direct route; use the hwaddr we stashed in sendmsg */
5304a9dda1cSMatt Johnston if (cb->halen != skb->dev->addr_len) {
5314a9dda1cSMatt Johnston /* sanity check, sendmsg should have already caught this */
5324a9dda1cSMatt Johnston kfree_skb(skb);
5334a9dda1cSMatt Johnston return -EMSGSIZE;
5344a9dda1cSMatt Johnston }
53599ce45d5SJeremy Kerr daddr = cb->haddr;
53699ce45d5SJeremy Kerr } else {
53726ab3fcaSMatt Johnston /* If lookup fails let the device handle daddr==NULL */
53826ab3fcaSMatt Johnston if (mctp_neigh_lookup(route->dev, hdr->dest, daddr_buf) == 0)
53926ab3fcaSMatt Johnston daddr = daddr_buf;
54099ce45d5SJeremy Kerr }
54126ab3fcaSMatt Johnston
542889b7da2SJeremy Kerr rc = dev_hard_header(skb, skb->dev, ntohs(skb->protocol),
54326ab3fcaSMatt Johnston daddr, skb->dev->dev_addr, skb->len);
54460be976aSMatt Johnston if (rc < 0) {
545889b7da2SJeremy Kerr kfree_skb(skb);
546889b7da2SJeremy Kerr return -EHOSTUNREACH;
547889b7da2SJeremy Kerr }
548889b7da2SJeremy Kerr
54967737c45SJeremy Kerr mctp_flow_prepare_output(skb, route->dev);
55067737c45SJeremy Kerr
551889b7da2SJeremy Kerr rc = dev_queue_xmit(skb);
552889b7da2SJeremy Kerr if (rc)
553889b7da2SJeremy Kerr rc = net_xmit_errno(rc);
554889b7da2SJeremy Kerr
555889b7da2SJeremy Kerr return rc;
556889b7da2SJeremy Kerr }
557889b7da2SJeremy Kerr
558889b7da2SJeremy Kerr /* route alloc/release */
mctp_route_release(struct mctp_route * rt)559889b7da2SJeremy Kerr static void mctp_route_release(struct mctp_route *rt)
560889b7da2SJeremy Kerr {
561889b7da2SJeremy Kerr if (refcount_dec_and_test(&rt->refs)) {
56243f55f23SJeremy Kerr mctp_dev_put(rt->dev);
563889b7da2SJeremy Kerr kfree_rcu(rt, rcu);
564889b7da2SJeremy Kerr }
565889b7da2SJeremy Kerr }
566889b7da2SJeremy Kerr
567889b7da2SJeremy Kerr /* returns a route with the refcount at 1 */
mctp_route_alloc(void)568889b7da2SJeremy Kerr static struct mctp_route *mctp_route_alloc(void)
569889b7da2SJeremy Kerr {
570889b7da2SJeremy Kerr struct mctp_route *rt;
571889b7da2SJeremy Kerr
572889b7da2SJeremy Kerr rt = kzalloc(sizeof(*rt), GFP_KERNEL);
573889b7da2SJeremy Kerr if (!rt)
574889b7da2SJeremy Kerr return NULL;
575889b7da2SJeremy Kerr
576889b7da2SJeremy Kerr INIT_LIST_HEAD(&rt->list);
577889b7da2SJeremy Kerr refcount_set(&rt->refs, 1);
578889b7da2SJeremy Kerr rt->output = mctp_route_discard;
579889b7da2SJeremy Kerr
580889b7da2SJeremy Kerr return rt;
581889b7da2SJeremy Kerr }
582889b7da2SJeremy Kerr
mctp_default_net(struct net * net)58303f2bbc4SMatt Johnston unsigned int mctp_default_net(struct net *net)
58403f2bbc4SMatt Johnston {
58503f2bbc4SMatt Johnston return READ_ONCE(net->mctp.default_net);
58603f2bbc4SMatt Johnston }
58703f2bbc4SMatt Johnston
mctp_default_net_set(struct net * net,unsigned int index)58803f2bbc4SMatt Johnston int mctp_default_net_set(struct net *net, unsigned int index)
58903f2bbc4SMatt Johnston {
59003f2bbc4SMatt Johnston if (index == 0)
59103f2bbc4SMatt Johnston return -EINVAL;
59203f2bbc4SMatt Johnston WRITE_ONCE(net->mctp.default_net, index);
59303f2bbc4SMatt Johnston return 0;
59403f2bbc4SMatt Johnston }
59503f2bbc4SMatt Johnston
596833ef3b9SJeremy Kerr /* tag management */
mctp_reserve_tag(struct net * net,struct mctp_sk_key * key,struct mctp_sock * msk)597833ef3b9SJeremy Kerr static void mctp_reserve_tag(struct net *net, struct mctp_sk_key *key,
598833ef3b9SJeremy Kerr struct mctp_sock *msk)
599833ef3b9SJeremy Kerr {
600833ef3b9SJeremy Kerr struct netns_mctp *mns = &net->mctp;
601833ef3b9SJeremy Kerr
602833ef3b9SJeremy Kerr lockdep_assert_held(&mns->keys_lock);
603833ef3b9SJeremy Kerr
6047b14e15aSJeremy Kerr key->expiry = jiffies + mctp_key_lifetime;
6057b14e15aSJeremy Kerr timer_reduce(&msk->key_expiry, key->expiry);
6067b14e15aSJeremy Kerr
607833ef3b9SJeremy Kerr /* we hold the net->key_lock here, allowing updates to both
608833ef3b9SJeremy Kerr * then net and sk
609833ef3b9SJeremy Kerr */
610833ef3b9SJeremy Kerr hlist_add_head_rcu(&key->hlist, &mns->keys);
611833ef3b9SJeremy Kerr hlist_add_head_rcu(&key->sklist, &msk->keys);
61273c61845SJeremy Kerr refcount_inc(&key->refs);
613833ef3b9SJeremy Kerr }
614833ef3b9SJeremy Kerr
615833ef3b9SJeremy Kerr /* Allocate a locally-owned tag value for (saddr, daddr), and reserve
616833ef3b9SJeremy Kerr * it for the socket msk
617833ef3b9SJeremy Kerr */
mctp_alloc_local_tag(struct mctp_sock * msk,mctp_eid_t daddr,mctp_eid_t saddr,bool manual,u8 * tagp)61863ed1aabSMatt Johnston struct mctp_sk_key *mctp_alloc_local_tag(struct mctp_sock *msk,
61963ed1aabSMatt Johnston mctp_eid_t daddr, mctp_eid_t saddr,
62063ed1aabSMatt Johnston bool manual, u8 *tagp)
621833ef3b9SJeremy Kerr {
622833ef3b9SJeremy Kerr struct net *net = sock_net(&msk->sk);
623833ef3b9SJeremy Kerr struct netns_mctp *mns = &net->mctp;
624833ef3b9SJeremy Kerr struct mctp_sk_key *key, *tmp;
625833ef3b9SJeremy Kerr unsigned long flags;
626833ef3b9SJeremy Kerr u8 tagbits;
627833ef3b9SJeremy Kerr
6281f6c77acSJeremy Kerr /* for NULL destination EIDs, we may get a response from any peer */
6291f6c77acSJeremy Kerr if (daddr == MCTP_ADDR_NULL)
6301f6c77acSJeremy Kerr daddr = MCTP_ADDR_ANY;
6311f6c77acSJeremy Kerr
632833ef3b9SJeremy Kerr /* be optimistic, alloc now */
6334a992bbdSJeremy Kerr key = mctp_key_alloc(msk, saddr, daddr, 0, GFP_KERNEL);
634833ef3b9SJeremy Kerr if (!key)
635212c10c3SJeremy Kerr return ERR_PTR(-ENOMEM);
636833ef3b9SJeremy Kerr
637833ef3b9SJeremy Kerr /* 8 possible tag values */
638833ef3b9SJeremy Kerr tagbits = 0xff;
639833ef3b9SJeremy Kerr
640833ef3b9SJeremy Kerr spin_lock_irqsave(&mns->keys_lock, flags);
641833ef3b9SJeremy Kerr
642833ef3b9SJeremy Kerr /* Walk through the existing keys, looking for potential conflicting
643833ef3b9SJeremy Kerr * tags. If we find a conflict, clear that bit from tagbits
644833ef3b9SJeremy Kerr */
645833ef3b9SJeremy Kerr hlist_for_each_entry(tmp, &mns->keys, hlist) {
64673c61845SJeremy Kerr /* We can check the lookup fields (*_addr, tag) without the
64773c61845SJeremy Kerr * lock held, they don't change over the lifetime of the key.
64873c61845SJeremy Kerr */
64973c61845SJeremy Kerr
650833ef3b9SJeremy Kerr /* if we don't own the tag, it can't conflict */
651833ef3b9SJeremy Kerr if (tmp->tag & MCTP_HDR_FLAG_TO)
652833ef3b9SJeremy Kerr continue;
653833ef3b9SJeremy Kerr
6548069b22dSJeremy Kerr if (!(mctp_address_matches(tmp->peer_addr, daddr) &&
6550de55a7dSJeremy Kerr mctp_address_matches(tmp->local_addr, saddr)))
65673c61845SJeremy Kerr continue;
65773c61845SJeremy Kerr
65873c61845SJeremy Kerr spin_lock(&tmp->lock);
65973c61845SJeremy Kerr /* key must still be valid. If we find a match, clear the
66073c61845SJeremy Kerr * potential tag value
66173c61845SJeremy Kerr */
66273c61845SJeremy Kerr if (tmp->valid)
663833ef3b9SJeremy Kerr tagbits &= ~(1 << tmp->tag);
66473c61845SJeremy Kerr spin_unlock(&tmp->lock);
665833ef3b9SJeremy Kerr
666833ef3b9SJeremy Kerr if (!tagbits)
667833ef3b9SJeremy Kerr break;
668833ef3b9SJeremy Kerr }
669833ef3b9SJeremy Kerr
670833ef3b9SJeremy Kerr if (tagbits) {
671833ef3b9SJeremy Kerr key->tag = __ffs(tagbits);
672833ef3b9SJeremy Kerr mctp_reserve_tag(net, key, msk);
6734f9e1ba6SJeremy Kerr trace_mctp_key_acquire(key);
6744f9e1ba6SJeremy Kerr
67563ed1aabSMatt Johnston key->manual_alloc = manual;
676833ef3b9SJeremy Kerr *tagp = key->tag;
677833ef3b9SJeremy Kerr }
678833ef3b9SJeremy Kerr
679833ef3b9SJeremy Kerr spin_unlock_irqrestore(&mns->keys_lock, flags);
680833ef3b9SJeremy Kerr
681212c10c3SJeremy Kerr if (!tagbits) {
68218a3d49aSJeremy Kerr mctp_key_unref(key);
683212c10c3SJeremy Kerr return ERR_PTR(-EBUSY);
684212c10c3SJeremy Kerr }
685833ef3b9SJeremy Kerr
686212c10c3SJeremy Kerr return key;
687833ef3b9SJeremy Kerr }
688833ef3b9SJeremy Kerr
mctp_lookup_prealloc_tag(struct mctp_sock * msk,mctp_eid_t daddr,u8 req_tag,u8 * tagp)68963ed1aabSMatt Johnston static struct mctp_sk_key *mctp_lookup_prealloc_tag(struct mctp_sock *msk,
69063ed1aabSMatt Johnston mctp_eid_t daddr,
69163ed1aabSMatt Johnston u8 req_tag, u8 *tagp)
69263ed1aabSMatt Johnston {
69363ed1aabSMatt Johnston struct net *net = sock_net(&msk->sk);
69463ed1aabSMatt Johnston struct netns_mctp *mns = &net->mctp;
69563ed1aabSMatt Johnston struct mctp_sk_key *key, *tmp;
69663ed1aabSMatt Johnston unsigned long flags;
69763ed1aabSMatt Johnston
69863ed1aabSMatt Johnston req_tag &= ~(MCTP_TAG_PREALLOC | MCTP_TAG_OWNER);
69963ed1aabSMatt Johnston key = NULL;
70063ed1aabSMatt Johnston
70163ed1aabSMatt Johnston spin_lock_irqsave(&mns->keys_lock, flags);
70263ed1aabSMatt Johnston
70363ed1aabSMatt Johnston hlist_for_each_entry(tmp, &mns->keys, hlist) {
70463ed1aabSMatt Johnston if (tmp->tag != req_tag)
70563ed1aabSMatt Johnston continue;
70663ed1aabSMatt Johnston
70763ed1aabSMatt Johnston if (!mctp_address_matches(tmp->peer_addr, daddr))
70863ed1aabSMatt Johnston continue;
70963ed1aabSMatt Johnston
71063ed1aabSMatt Johnston if (!tmp->manual_alloc)
71163ed1aabSMatt Johnston continue;
71263ed1aabSMatt Johnston
71363ed1aabSMatt Johnston spin_lock(&tmp->lock);
71463ed1aabSMatt Johnston if (tmp->valid) {
71563ed1aabSMatt Johnston key = tmp;
71663ed1aabSMatt Johnston refcount_inc(&key->refs);
71763ed1aabSMatt Johnston spin_unlock(&tmp->lock);
71863ed1aabSMatt Johnston break;
71963ed1aabSMatt Johnston }
72063ed1aabSMatt Johnston spin_unlock(&tmp->lock);
72163ed1aabSMatt Johnston }
72263ed1aabSMatt Johnston spin_unlock_irqrestore(&mns->keys_lock, flags);
72363ed1aabSMatt Johnston
72463ed1aabSMatt Johnston if (!key)
72563ed1aabSMatt Johnston return ERR_PTR(-ENOENT);
72663ed1aabSMatt Johnston
72763ed1aabSMatt Johnston if (tagp)
72863ed1aabSMatt Johnston *tagp = key->tag;
72963ed1aabSMatt Johnston
73063ed1aabSMatt Johnston return key;
73163ed1aabSMatt Johnston }
73263ed1aabSMatt Johnston
733889b7da2SJeremy Kerr /* routing lookups */
mctp_rt_match_eid(struct mctp_route * rt,unsigned int net,mctp_eid_t eid)734889b7da2SJeremy Kerr static bool mctp_rt_match_eid(struct mctp_route *rt,
735889b7da2SJeremy Kerr unsigned int net, mctp_eid_t eid)
736889b7da2SJeremy Kerr {
737889b7da2SJeremy Kerr return READ_ONCE(rt->dev->net) == net &&
738889b7da2SJeremy Kerr rt->min <= eid && rt->max >= eid;
739889b7da2SJeremy Kerr }
740889b7da2SJeremy Kerr
741889b7da2SJeremy Kerr /* compares match, used for duplicate prevention */
mctp_rt_compare_exact(struct mctp_route * rt1,struct mctp_route * rt2)742889b7da2SJeremy Kerr static bool mctp_rt_compare_exact(struct mctp_route *rt1,
743889b7da2SJeremy Kerr struct mctp_route *rt2)
744889b7da2SJeremy Kerr {
745889b7da2SJeremy Kerr ASSERT_RTNL();
746889b7da2SJeremy Kerr return rt1->dev->net == rt2->dev->net &&
747889b7da2SJeremy Kerr rt1->min == rt2->min &&
748889b7da2SJeremy Kerr rt1->max == rt2->max;
749889b7da2SJeremy Kerr }
750889b7da2SJeremy Kerr
mctp_route_lookup(struct net * net,unsigned int dnet,mctp_eid_t daddr)751889b7da2SJeremy Kerr struct mctp_route *mctp_route_lookup(struct net *net, unsigned int dnet,
752889b7da2SJeremy Kerr mctp_eid_t daddr)
753889b7da2SJeremy Kerr {
754889b7da2SJeremy Kerr struct mctp_route *tmp, *rt = NULL;
755889b7da2SJeremy Kerr
7565093bbfcSJeremy Kerr rcu_read_lock();
7575093bbfcSJeremy Kerr
758889b7da2SJeremy Kerr list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
759889b7da2SJeremy Kerr /* TODO: add metrics */
760889b7da2SJeremy Kerr if (mctp_rt_match_eid(tmp, dnet, daddr)) {
761889b7da2SJeremy Kerr if (refcount_inc_not_zero(&tmp->refs)) {
762889b7da2SJeremy Kerr rt = tmp;
763889b7da2SJeremy Kerr break;
764889b7da2SJeremy Kerr }
765889b7da2SJeremy Kerr }
766889b7da2SJeremy Kerr }
767889b7da2SJeremy Kerr
7685093bbfcSJeremy Kerr rcu_read_unlock();
7695093bbfcSJeremy Kerr
770889b7da2SJeremy Kerr return rt;
771889b7da2SJeremy Kerr }
772889b7da2SJeremy Kerr
mctp_route_lookup_null(struct net * net,struct net_device * dev)7731f6c77acSJeremy Kerr static struct mctp_route *mctp_route_lookup_null(struct net *net,
7741f6c77acSJeremy Kerr struct net_device *dev)
7751f6c77acSJeremy Kerr {
7765093bbfcSJeremy Kerr struct mctp_route *tmp, *rt = NULL;
7771f6c77acSJeremy Kerr
7785093bbfcSJeremy Kerr rcu_read_lock();
7795093bbfcSJeremy Kerr
7805093bbfcSJeremy Kerr list_for_each_entry_rcu(tmp, &net->mctp.routes, list) {
7815093bbfcSJeremy Kerr if (tmp->dev->dev == dev && tmp->type == RTN_LOCAL &&
7825093bbfcSJeremy Kerr refcount_inc_not_zero(&tmp->refs)) {
7835093bbfcSJeremy Kerr rt = tmp;
7845093bbfcSJeremy Kerr break;
7855093bbfcSJeremy Kerr }
7861f6c77acSJeremy Kerr }
7871f6c77acSJeremy Kerr
7885093bbfcSJeremy Kerr rcu_read_unlock();
7895093bbfcSJeremy Kerr
7905093bbfcSJeremy Kerr return rt;
7911f6c77acSJeremy Kerr }
7921f6c77acSJeremy Kerr
mctp_do_fragment_route(struct mctp_route * rt,struct sk_buff * skb,unsigned int mtu,u8 tag)7934a992bbdSJeremy Kerr static int mctp_do_fragment_route(struct mctp_route *rt, struct sk_buff *skb,
7944a992bbdSJeremy Kerr unsigned int mtu, u8 tag)
7954a992bbdSJeremy Kerr {
7964a992bbdSJeremy Kerr const unsigned int hlen = sizeof(struct mctp_hdr);
7974a992bbdSJeremy Kerr struct mctp_hdr *hdr, *hdr2;
7984a9dda1cSMatt Johnston unsigned int pos, size, headroom;
7994a992bbdSJeremy Kerr struct sk_buff *skb2;
8004a992bbdSJeremy Kerr int rc;
8014a992bbdSJeremy Kerr u8 seq;
8024a992bbdSJeremy Kerr
8034a992bbdSJeremy Kerr hdr = mctp_hdr(skb);
8044a992bbdSJeremy Kerr seq = 0;
8054a992bbdSJeremy Kerr rc = 0;
8064a992bbdSJeremy Kerr
8074a992bbdSJeremy Kerr if (mtu < hlen + 1) {
8084a992bbdSJeremy Kerr kfree_skb(skb);
8094a992bbdSJeremy Kerr return -EMSGSIZE;
8104a992bbdSJeremy Kerr }
8114a992bbdSJeremy Kerr
8124a9dda1cSMatt Johnston /* keep same headroom as the original skb */
8134a9dda1cSMatt Johnston headroom = skb_headroom(skb);
8144a9dda1cSMatt Johnston
8154a992bbdSJeremy Kerr /* we've got the header */
8164a992bbdSJeremy Kerr skb_pull(skb, hlen);
8174a992bbdSJeremy Kerr
8184a992bbdSJeremy Kerr for (pos = 0; pos < skb->len;) {
8194a992bbdSJeremy Kerr /* size of message payload */
8204a992bbdSJeremy Kerr size = min(mtu - hlen, skb->len - pos);
8214a992bbdSJeremy Kerr
8224a9dda1cSMatt Johnston skb2 = alloc_skb(headroom + hlen + size, GFP_KERNEL);
8234a992bbdSJeremy Kerr if (!skb2) {
8244a992bbdSJeremy Kerr rc = -ENOMEM;
8254a992bbdSJeremy Kerr break;
8264a992bbdSJeremy Kerr }
8274a992bbdSJeremy Kerr
8284a992bbdSJeremy Kerr /* generic skb copy */
8294a992bbdSJeremy Kerr skb2->protocol = skb->protocol;
8304a992bbdSJeremy Kerr skb2->priority = skb->priority;
8314a992bbdSJeremy Kerr skb2->dev = skb->dev;
8324a992bbdSJeremy Kerr memcpy(skb2->cb, skb->cb, sizeof(skb2->cb));
8334a992bbdSJeremy Kerr
8344a992bbdSJeremy Kerr if (skb->sk)
8354a992bbdSJeremy Kerr skb_set_owner_w(skb2, skb->sk);
8364a992bbdSJeremy Kerr
8374a992bbdSJeremy Kerr /* establish packet */
8384a9dda1cSMatt Johnston skb_reserve(skb2, headroom);
8394a992bbdSJeremy Kerr skb_reset_network_header(skb2);
8404a992bbdSJeremy Kerr skb_put(skb2, hlen + size);
8414a992bbdSJeremy Kerr skb2->transport_header = skb2->network_header + hlen;
8424a992bbdSJeremy Kerr
8434a992bbdSJeremy Kerr /* copy header fields, calculate SOM/EOM flags & seq */
8444a992bbdSJeremy Kerr hdr2 = mctp_hdr(skb2);
8454a992bbdSJeremy Kerr hdr2->ver = hdr->ver;
8464a992bbdSJeremy Kerr hdr2->dest = hdr->dest;
8474a992bbdSJeremy Kerr hdr2->src = hdr->src;
8484a992bbdSJeremy Kerr hdr2->flags_seq_tag = tag &
8494a992bbdSJeremy Kerr (MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
8504a992bbdSJeremy Kerr
8514a992bbdSJeremy Kerr if (pos == 0)
8524a992bbdSJeremy Kerr hdr2->flags_seq_tag |= MCTP_HDR_FLAG_SOM;
8534a992bbdSJeremy Kerr
8544a992bbdSJeremy Kerr if (pos + size == skb->len)
8554a992bbdSJeremy Kerr hdr2->flags_seq_tag |= MCTP_HDR_FLAG_EOM;
8564a992bbdSJeremy Kerr
8574a992bbdSJeremy Kerr hdr2->flags_seq_tag |= seq << MCTP_HDR_SEQ_SHIFT;
8584a992bbdSJeremy Kerr
8594a992bbdSJeremy Kerr /* copy message payload */
8604a992bbdSJeremy Kerr skb_copy_bits(skb, pos, skb_transport_header(skb2), size);
8614a992bbdSJeremy Kerr
862be503653SJeremy Kerr /* we need to copy the extensions, for MCTP flow data */
863be503653SJeremy Kerr skb_ext_copy(skb2, skb);
864be503653SJeremy Kerr
86599ce45d5SJeremy Kerr /* do route */
8664a992bbdSJeremy Kerr rc = rt->output(rt, skb2);
8674a992bbdSJeremy Kerr if (rc)
8684a992bbdSJeremy Kerr break;
8694a992bbdSJeremy Kerr
8704a992bbdSJeremy Kerr seq = (seq + 1) & MCTP_HDR_SEQ_MASK;
8714a992bbdSJeremy Kerr pos += size;
8724a992bbdSJeremy Kerr }
8734a992bbdSJeremy Kerr
8744a992bbdSJeremy Kerr consume_skb(skb);
8754a992bbdSJeremy Kerr return rc;
8764a992bbdSJeremy Kerr }
8774a992bbdSJeremy Kerr
mctp_local_output(struct sock * sk,struct mctp_route * rt,struct sk_buff * skb,mctp_eid_t daddr,u8 req_tag)878889b7da2SJeremy Kerr int mctp_local_output(struct sock *sk, struct mctp_route *rt,
879889b7da2SJeremy Kerr struct sk_buff *skb, mctp_eid_t daddr, u8 req_tag)
880889b7da2SJeremy Kerr {
881833ef3b9SJeremy Kerr struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
882889b7da2SJeremy Kerr struct mctp_skb_cb *cb = mctp_cb(skb);
883dc121c00SMatt Johnston struct mctp_route tmp_rt = {0};
884212c10c3SJeremy Kerr struct mctp_sk_key *key;
885889b7da2SJeremy Kerr struct mctp_hdr *hdr;
886889b7da2SJeremy Kerr unsigned long flags;
8874a992bbdSJeremy Kerr unsigned int mtu;
888889b7da2SJeremy Kerr mctp_eid_t saddr;
88999ce45d5SJeremy Kerr bool ext_rt;
890889b7da2SJeremy Kerr int rc;
891833ef3b9SJeremy Kerr u8 tag;
892889b7da2SJeremy Kerr
89399ce45d5SJeremy Kerr rc = -ENODEV;
89499ce45d5SJeremy Kerr
89599ce45d5SJeremy Kerr if (rt) {
89699ce45d5SJeremy Kerr ext_rt = false;
897889b7da2SJeremy Kerr if (WARN_ON(!rt->dev))
89899ce45d5SJeremy Kerr goto out_release;
89999ce45d5SJeremy Kerr
90099ce45d5SJeremy Kerr } else if (cb->ifindex) {
901e297db3eSMatt Johnston struct net_device *dev;
902e297db3eSMatt Johnston
90399ce45d5SJeremy Kerr ext_rt = true;
90499ce45d5SJeremy Kerr rt = &tmp_rt;
90599ce45d5SJeremy Kerr
90699ce45d5SJeremy Kerr rcu_read_lock();
90799ce45d5SJeremy Kerr dev = dev_get_by_index_rcu(sock_net(sk), cb->ifindex);
90899ce45d5SJeremy Kerr if (!dev) {
90999ce45d5SJeremy Kerr rcu_read_unlock();
910cbebc55cSJeremy Kerr goto out_free;
91199ce45d5SJeremy Kerr }
91299ce45d5SJeremy Kerr rt->dev = __mctp_dev_get(dev);
91399ce45d5SJeremy Kerr rcu_read_unlock();
91499ce45d5SJeremy Kerr
91599ce45d5SJeremy Kerr if (!rt->dev)
91699ce45d5SJeremy Kerr goto out_release;
91799ce45d5SJeremy Kerr
91899ce45d5SJeremy Kerr /* establish temporary route - we set up enough to keep
91999ce45d5SJeremy Kerr * mctp_route_output happy
92099ce45d5SJeremy Kerr */
92199ce45d5SJeremy Kerr rt->output = mctp_route_output;
92299ce45d5SJeremy Kerr rt->mtu = 0;
92399ce45d5SJeremy Kerr
92499ce45d5SJeremy Kerr } else {
925cbebc55cSJeremy Kerr rc = -EINVAL;
926cbebc55cSJeremy Kerr goto out_free;
92799ce45d5SJeremy Kerr }
928889b7da2SJeremy Kerr
929889b7da2SJeremy Kerr spin_lock_irqsave(&rt->dev->addrs_lock, flags);
930889b7da2SJeremy Kerr if (rt->dev->num_addrs == 0) {
931889b7da2SJeremy Kerr rc = -EHOSTUNREACH;
932889b7da2SJeremy Kerr } else {
933889b7da2SJeremy Kerr /* use the outbound interface's first address as our source */
934889b7da2SJeremy Kerr saddr = rt->dev->addrs[0];
935889b7da2SJeremy Kerr rc = 0;
936889b7da2SJeremy Kerr }
937889b7da2SJeremy Kerr spin_unlock_irqrestore(&rt->dev->addrs_lock, flags);
938889b7da2SJeremy Kerr
939889b7da2SJeremy Kerr if (rc)
94099ce45d5SJeremy Kerr goto out_release;
941889b7da2SJeremy Kerr
94263ed1aabSMatt Johnston if (req_tag & MCTP_TAG_OWNER) {
94363ed1aabSMatt Johnston if (req_tag & MCTP_TAG_PREALLOC)
94463ed1aabSMatt Johnston key = mctp_lookup_prealloc_tag(msk, daddr,
94563ed1aabSMatt Johnston req_tag, &tag);
94663ed1aabSMatt Johnston else
94763ed1aabSMatt Johnston key = mctp_alloc_local_tag(msk, daddr, saddr,
94863ed1aabSMatt Johnston false, &tag);
94963ed1aabSMatt Johnston
950212c10c3SJeremy Kerr if (IS_ERR(key)) {
951212c10c3SJeremy Kerr rc = PTR_ERR(key);
95299ce45d5SJeremy Kerr goto out_release;
953212c10c3SJeremy Kerr }
95467737c45SJeremy Kerr mctp_skb_set_flow(skb, key);
955212c10c3SJeremy Kerr /* done with the key in this scope */
956212c10c3SJeremy Kerr mctp_key_unref(key);
957833ef3b9SJeremy Kerr tag |= MCTP_HDR_FLAG_TO;
958833ef3b9SJeremy Kerr } else {
959212c10c3SJeremy Kerr key = NULL;
96063ed1aabSMatt Johnston tag = req_tag & MCTP_TAG_MASK;
961833ef3b9SJeremy Kerr }
962833ef3b9SJeremy Kerr
9634a992bbdSJeremy Kerr skb->protocol = htons(ETH_P_MCTP);
9644a992bbdSJeremy Kerr skb->priority = 0;
965889b7da2SJeremy Kerr skb_reset_transport_header(skb);
966889b7da2SJeremy Kerr skb_push(skb, sizeof(struct mctp_hdr));
967889b7da2SJeremy Kerr skb_reset_network_header(skb);
968833ef3b9SJeremy Kerr skb->dev = rt->dev->dev;
969889b7da2SJeremy Kerr
970889b7da2SJeremy Kerr /* cb->net will have been set on initial ingress */
971889b7da2SJeremy Kerr cb->src = saddr;
972889b7da2SJeremy Kerr
9734a992bbdSJeremy Kerr /* set up common header fields */
9744a992bbdSJeremy Kerr hdr = mctp_hdr(skb);
9754a992bbdSJeremy Kerr hdr->ver = 1;
9764a992bbdSJeremy Kerr hdr->dest = daddr;
9774a992bbdSJeremy Kerr hdr->src = saddr;
9784a992bbdSJeremy Kerr
9794a992bbdSJeremy Kerr mtu = mctp_route_mtu(rt);
9804a992bbdSJeremy Kerr
9814a992bbdSJeremy Kerr if (skb->len + sizeof(struct mctp_hdr) <= mtu) {
98299ce45d5SJeremy Kerr hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM |
98399ce45d5SJeremy Kerr MCTP_HDR_FLAG_EOM | tag;
98499ce45d5SJeremy Kerr rc = rt->output(rt, skb);
9854a992bbdSJeremy Kerr } else {
98699ce45d5SJeremy Kerr rc = mctp_do_fragment_route(rt, skb, mtu, tag);
9874a992bbdSJeremy Kerr }
98899ce45d5SJeremy Kerr
989cbebc55cSJeremy Kerr /* route output functions consume the skb, even on error */
990cbebc55cSJeremy Kerr skb = NULL;
991cbebc55cSJeremy Kerr
99299ce45d5SJeremy Kerr out_release:
99399ce45d5SJeremy Kerr if (!ext_rt)
99499ce45d5SJeremy Kerr mctp_route_release(rt);
99599ce45d5SJeremy Kerr
996dc121c00SMatt Johnston mctp_dev_put(tmp_rt.dev);
99799ce45d5SJeremy Kerr
998cbebc55cSJeremy Kerr out_free:
999cbebc55cSJeremy Kerr kfree_skb(skb);
100099ce45d5SJeremy Kerr return rc;
1001889b7da2SJeremy Kerr }
1002889b7da2SJeremy Kerr
1003889b7da2SJeremy 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)100406d2f4c5SMatt Johnston static int mctp_route_add(struct mctp_dev *mdev, mctp_eid_t daddr_start,
100506d2f4c5SMatt Johnston unsigned int daddr_extent, unsigned int mtu,
100683f0a0b7SJeremy Kerr unsigned char type)
1007889b7da2SJeremy Kerr {
100883f0a0b7SJeremy Kerr int (*rtfn)(struct mctp_route *rt, struct sk_buff *skb);
1009889b7da2SJeremy Kerr struct net *net = dev_net(mdev->dev);
1010889b7da2SJeremy Kerr struct mctp_route *rt, *ert;
1011889b7da2SJeremy Kerr
1012cb196b72SJeremy Kerr if (!mctp_address_unicast(daddr_start))
101306d2f4c5SMatt Johnston return -EINVAL;
101406d2f4c5SMatt Johnston
101506d2f4c5SMatt Johnston if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
101606d2f4c5SMatt Johnston return -EINVAL;
101706d2f4c5SMatt Johnston
101883f0a0b7SJeremy Kerr switch (type) {
101983f0a0b7SJeremy Kerr case RTN_LOCAL:
102083f0a0b7SJeremy Kerr rtfn = mctp_route_input;
102183f0a0b7SJeremy Kerr break;
102283f0a0b7SJeremy Kerr case RTN_UNICAST:
102383f0a0b7SJeremy Kerr rtfn = mctp_route_output;
102483f0a0b7SJeremy Kerr break;
102583f0a0b7SJeremy Kerr default:
102683f0a0b7SJeremy Kerr return -EINVAL;
102783f0a0b7SJeremy Kerr }
102883f0a0b7SJeremy Kerr
1029889b7da2SJeremy Kerr rt = mctp_route_alloc();
1030889b7da2SJeremy Kerr if (!rt)
1031889b7da2SJeremy Kerr return -ENOMEM;
1032889b7da2SJeremy Kerr
103306d2f4c5SMatt Johnston rt->min = daddr_start;
103406d2f4c5SMatt Johnston rt->max = daddr_start + daddr_extent;
103506d2f4c5SMatt Johnston rt->mtu = mtu;
1036889b7da2SJeremy Kerr rt->dev = mdev;
103743f55f23SJeremy Kerr mctp_dev_hold(rt->dev);
103883f0a0b7SJeremy Kerr rt->type = type;
103983f0a0b7SJeremy Kerr rt->output = rtfn;
1040889b7da2SJeremy Kerr
1041889b7da2SJeremy Kerr ASSERT_RTNL();
1042889b7da2SJeremy Kerr /* Prevent duplicate identical routes. */
1043889b7da2SJeremy Kerr list_for_each_entry(ert, &net->mctp.routes, list) {
1044889b7da2SJeremy Kerr if (mctp_rt_compare_exact(rt, ert)) {
1045889b7da2SJeremy Kerr mctp_route_release(rt);
1046889b7da2SJeremy Kerr return -EEXIST;
1047889b7da2SJeremy Kerr }
1048889b7da2SJeremy Kerr }
1049889b7da2SJeremy Kerr
1050889b7da2SJeremy Kerr list_add_rcu(&rt->list, &net->mctp.routes);
1051889b7da2SJeremy Kerr
1052889b7da2SJeremy Kerr return 0;
1053889b7da2SJeremy Kerr }
1054889b7da2SJeremy Kerr
mctp_route_remove(struct mctp_dev * mdev,mctp_eid_t daddr_start,unsigned int daddr_extent,unsigned char type)105506d2f4c5SMatt Johnston static int mctp_route_remove(struct mctp_dev *mdev, mctp_eid_t daddr_start,
105676d00160SMatt Johnston unsigned int daddr_extent, unsigned char type)
1057889b7da2SJeremy Kerr {
1058889b7da2SJeremy Kerr struct net *net = dev_net(mdev->dev);
1059889b7da2SJeremy Kerr struct mctp_route *rt, *tmp;
106006d2f4c5SMatt Johnston mctp_eid_t daddr_end;
106106d2f4c5SMatt Johnston bool dropped;
106206d2f4c5SMatt Johnston
106306d2f4c5SMatt Johnston if (daddr_extent > 0xff || daddr_start + daddr_extent >= 255)
106406d2f4c5SMatt Johnston return -EINVAL;
106506d2f4c5SMatt Johnston
106606d2f4c5SMatt Johnston daddr_end = daddr_start + daddr_extent;
106706d2f4c5SMatt Johnston dropped = false;
1068889b7da2SJeremy Kerr
1069889b7da2SJeremy Kerr ASSERT_RTNL();
1070889b7da2SJeremy Kerr
1071889b7da2SJeremy Kerr list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
107206d2f4c5SMatt Johnston if (rt->dev == mdev &&
107376d00160SMatt Johnston rt->min == daddr_start && rt->max == daddr_end &&
107476d00160SMatt Johnston rt->type == type) {
1075889b7da2SJeremy Kerr list_del_rcu(&rt->list);
1076889b7da2SJeremy Kerr /* TODO: immediate RTM_DELROUTE */
1077889b7da2SJeremy Kerr mctp_route_release(rt);
107806d2f4c5SMatt Johnston dropped = true;
1079889b7da2SJeremy Kerr }
1080889b7da2SJeremy Kerr }
1081889b7da2SJeremy Kerr
108206d2f4c5SMatt Johnston return dropped ? 0 : -ENOENT;
108306d2f4c5SMatt Johnston }
108406d2f4c5SMatt Johnston
mctp_route_add_local(struct mctp_dev * mdev,mctp_eid_t addr)108506d2f4c5SMatt Johnston int mctp_route_add_local(struct mctp_dev *mdev, mctp_eid_t addr)
108606d2f4c5SMatt Johnston {
108783f0a0b7SJeremy Kerr return mctp_route_add(mdev, addr, 0, 0, RTN_LOCAL);
108806d2f4c5SMatt Johnston }
108906d2f4c5SMatt Johnston
mctp_route_remove_local(struct mctp_dev * mdev,mctp_eid_t addr)109006d2f4c5SMatt Johnston int mctp_route_remove_local(struct mctp_dev *mdev, mctp_eid_t addr)
109106d2f4c5SMatt Johnston {
109276d00160SMatt Johnston return mctp_route_remove(mdev, addr, 0, RTN_LOCAL);
1093889b7da2SJeremy Kerr }
1094889b7da2SJeremy Kerr
1095889b7da2SJeremy Kerr /* removes all entries for a given device */
mctp_route_remove_dev(struct mctp_dev * mdev)1096889b7da2SJeremy Kerr void mctp_route_remove_dev(struct mctp_dev *mdev)
1097889b7da2SJeremy Kerr {
1098889b7da2SJeremy Kerr struct net *net = dev_net(mdev->dev);
1099889b7da2SJeremy Kerr struct mctp_route *rt, *tmp;
1100889b7da2SJeremy Kerr
1101889b7da2SJeremy Kerr ASSERT_RTNL();
1102889b7da2SJeremy Kerr list_for_each_entry_safe(rt, tmp, &net->mctp.routes, list) {
1103889b7da2SJeremy Kerr if (rt->dev == mdev) {
1104889b7da2SJeremy Kerr list_del_rcu(&rt->list);
1105889b7da2SJeremy Kerr /* TODO: immediate RTM_DELROUTE */
1106889b7da2SJeremy Kerr mctp_route_release(rt);
1107889b7da2SJeremy Kerr }
1108889b7da2SJeremy Kerr }
1109889b7da2SJeremy Kerr }
1110889b7da2SJeremy Kerr
1111889b7da2SJeremy Kerr /* Incoming packet-handling */
1112889b7da2SJeremy Kerr
mctp_pkttype_receive(struct sk_buff * skb,struct net_device * dev,struct packet_type * pt,struct net_device * orig_dev)1113889b7da2SJeremy Kerr static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev,
1114889b7da2SJeremy Kerr struct packet_type *pt,
1115889b7da2SJeremy Kerr struct net_device *orig_dev)
1116889b7da2SJeremy Kerr {
1117889b7da2SJeremy Kerr struct net *net = dev_net(dev);
1118f364dd71SMatt Johnston struct mctp_dev *mdev;
1119889b7da2SJeremy Kerr struct mctp_skb_cb *cb;
1120889b7da2SJeremy Kerr struct mctp_route *rt;
1121889b7da2SJeremy Kerr struct mctp_hdr *mh;
1122889b7da2SJeremy Kerr
1123f364dd71SMatt Johnston rcu_read_lock();
1124f364dd71SMatt Johnston mdev = __mctp_dev_get(dev);
1125f364dd71SMatt Johnston rcu_read_unlock();
1126f364dd71SMatt Johnston if (!mdev) {
1127889b7da2SJeremy Kerr /* basic non-data sanity checks */
1128889b7da2SJeremy Kerr goto err_drop;
1129f364dd71SMatt Johnston }
1130889b7da2SJeremy Kerr
1131889b7da2SJeremy Kerr if (!pskb_may_pull(skb, sizeof(struct mctp_hdr)))
1132889b7da2SJeremy Kerr goto err_drop;
1133889b7da2SJeremy Kerr
1134889b7da2SJeremy Kerr skb_reset_transport_header(skb);
1135889b7da2SJeremy Kerr skb_reset_network_header(skb);
1136889b7da2SJeremy Kerr
1137889b7da2SJeremy Kerr /* We have enough for a header; decode and route */
1138889b7da2SJeremy Kerr mh = mctp_hdr(skb);
1139889b7da2SJeremy Kerr if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX)
1140889b7da2SJeremy Kerr goto err_drop;
1141889b7da2SJeremy Kerr
114286cdfd63SJeremy Kerr /* source must be valid unicast or null; drop reserved ranges and
114386cdfd63SJeremy Kerr * broadcast
114486cdfd63SJeremy Kerr */
114586cdfd63SJeremy Kerr if (!(mctp_address_unicast(mh->src) || mctp_address_null(mh->src)))
114686cdfd63SJeremy Kerr goto err_drop;
114786cdfd63SJeremy Kerr
114886cdfd63SJeremy Kerr /* dest address: as above, but allow broadcast */
114986cdfd63SJeremy Kerr if (!(mctp_address_unicast(mh->dest) || mctp_address_null(mh->dest) ||
115086cdfd63SJeremy Kerr mctp_address_broadcast(mh->dest)))
115186cdfd63SJeremy Kerr goto err_drop;
115286cdfd63SJeremy Kerr
115399ce45d5SJeremy Kerr /* MCTP drivers must populate halen/haddr */
115499ce45d5SJeremy Kerr if (dev->type == ARPHRD_MCTP) {
115599ce45d5SJeremy Kerr cb = mctp_cb(skb);
115699ce45d5SJeremy Kerr } else {
1157889b7da2SJeremy Kerr cb = __mctp_cb(skb);
115899ce45d5SJeremy Kerr cb->halen = 0;
115999ce45d5SJeremy Kerr }
1160f364dd71SMatt Johnston cb->net = READ_ONCE(mdev->net);
116199ce45d5SJeremy Kerr cb->ifindex = dev->ifindex;
1162889b7da2SJeremy Kerr
1163889b7da2SJeremy Kerr rt = mctp_route_lookup(net, cb->net, mh->dest);
11641f6c77acSJeremy Kerr
11651f6c77acSJeremy Kerr /* NULL EID, but addressed to our physical address */
11661f6c77acSJeremy Kerr if (!rt && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST)
11671f6c77acSJeremy Kerr rt = mctp_route_lookup_null(net, dev);
11681f6c77acSJeremy Kerr
1169889b7da2SJeremy Kerr if (!rt)
1170889b7da2SJeremy Kerr goto err_drop;
1171889b7da2SJeremy Kerr
117299ce45d5SJeremy Kerr rt->output(rt, skb);
117399ce45d5SJeremy Kerr mctp_route_release(rt);
1174dc121c00SMatt Johnston mctp_dev_put(mdev);
1175889b7da2SJeremy Kerr
1176889b7da2SJeremy Kerr return NET_RX_SUCCESS;
1177889b7da2SJeremy Kerr
1178889b7da2SJeremy Kerr err_drop:
1179889b7da2SJeremy Kerr kfree_skb(skb);
1180dc121c00SMatt Johnston mctp_dev_put(mdev);
1181889b7da2SJeremy Kerr return NET_RX_DROP;
1182889b7da2SJeremy Kerr }
1183889b7da2SJeremy Kerr
1184889b7da2SJeremy Kerr static struct packet_type mctp_packet_type = {
1185889b7da2SJeremy Kerr .type = cpu_to_be16(ETH_P_MCTP),
1186889b7da2SJeremy Kerr .func = mctp_pkttype_receive,
1187889b7da2SJeremy Kerr };
1188889b7da2SJeremy Kerr
118906d2f4c5SMatt Johnston /* netlink interface */
119006d2f4c5SMatt Johnston
119106d2f4c5SMatt Johnston static const struct nla_policy rta_mctp_policy[RTA_MAX + 1] = {
119206d2f4c5SMatt Johnston [RTA_DST] = { .type = NLA_U8 },
119306d2f4c5SMatt Johnston [RTA_METRICS] = { .type = NLA_NESTED },
119406d2f4c5SMatt Johnston [RTA_OIF] = { .type = NLA_U32 },
119506d2f4c5SMatt Johnston };
119606d2f4c5SMatt Johnston
119706d2f4c5SMatt Johnston /* Common part for RTM_NEWROUTE and RTM_DELROUTE parsing.
119806d2f4c5SMatt Johnston * tb must hold RTA_MAX+1 elements.
119906d2f4c5SMatt 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)120006d2f4c5SMatt Johnston static int mctp_route_nlparse(struct sk_buff *skb, struct nlmsghdr *nlh,
120106d2f4c5SMatt Johnston struct netlink_ext_ack *extack,
120206d2f4c5SMatt Johnston struct nlattr **tb, struct rtmsg **rtm,
120306d2f4c5SMatt Johnston struct mctp_dev **mdev, mctp_eid_t *daddr_start)
120406d2f4c5SMatt Johnston {
120506d2f4c5SMatt Johnston struct net *net = sock_net(skb->sk);
120606d2f4c5SMatt Johnston struct net_device *dev;
120706d2f4c5SMatt Johnston unsigned int ifindex;
120806d2f4c5SMatt Johnston int rc;
120906d2f4c5SMatt Johnston
121006d2f4c5SMatt Johnston rc = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX,
121106d2f4c5SMatt Johnston rta_mctp_policy, extack);
121206d2f4c5SMatt Johnston if (rc < 0) {
121306d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "incorrect format");
121406d2f4c5SMatt Johnston return rc;
121506d2f4c5SMatt Johnston }
121606d2f4c5SMatt Johnston
121706d2f4c5SMatt Johnston if (!tb[RTA_DST]) {
121806d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "dst EID missing");
121906d2f4c5SMatt Johnston return -EINVAL;
122006d2f4c5SMatt Johnston }
122106d2f4c5SMatt Johnston *daddr_start = nla_get_u8(tb[RTA_DST]);
122206d2f4c5SMatt Johnston
122306d2f4c5SMatt Johnston if (!tb[RTA_OIF]) {
122406d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "ifindex missing");
122506d2f4c5SMatt Johnston return -EINVAL;
122606d2f4c5SMatt Johnston }
122706d2f4c5SMatt Johnston ifindex = nla_get_u32(tb[RTA_OIF]);
122806d2f4c5SMatt Johnston
122906d2f4c5SMatt Johnston *rtm = nlmsg_data(nlh);
123006d2f4c5SMatt Johnston if ((*rtm)->rtm_family != AF_MCTP) {
123106d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "route family must be AF_MCTP");
123206d2f4c5SMatt Johnston return -EINVAL;
123306d2f4c5SMatt Johnston }
123406d2f4c5SMatt Johnston
123506d2f4c5SMatt Johnston dev = __dev_get_by_index(net, ifindex);
123606d2f4c5SMatt Johnston if (!dev) {
123706d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "bad ifindex");
123806d2f4c5SMatt Johnston return -ENODEV;
123906d2f4c5SMatt Johnston }
124006d2f4c5SMatt Johnston *mdev = mctp_dev_get_rtnl(dev);
124106d2f4c5SMatt Johnston if (!*mdev)
124206d2f4c5SMatt Johnston return -ENODEV;
124306d2f4c5SMatt Johnston
124406d2f4c5SMatt Johnston if (dev->flags & IFF_LOOPBACK) {
124506d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "no routes to loopback");
124606d2f4c5SMatt Johnston return -EINVAL;
124706d2f4c5SMatt Johnston }
124806d2f4c5SMatt Johnston
124906d2f4c5SMatt Johnston return 0;
125006d2f4c5SMatt Johnston }
125106d2f4c5SMatt Johnston
12526183569dSMatt Johnston static const struct nla_policy rta_metrics_policy[RTAX_MAX + 1] = {
12536183569dSMatt Johnston [RTAX_MTU] = { .type = NLA_U32 },
12546183569dSMatt Johnston };
12556183569dSMatt Johnston
mctp_newroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)125606d2f4c5SMatt Johnston static int mctp_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
125706d2f4c5SMatt Johnston struct netlink_ext_ack *extack)
125806d2f4c5SMatt Johnston {
125906d2f4c5SMatt Johnston struct nlattr *tb[RTA_MAX + 1];
12606183569dSMatt Johnston struct nlattr *tbx[RTAX_MAX + 1];
126106d2f4c5SMatt Johnston mctp_eid_t daddr_start;
126206d2f4c5SMatt Johnston struct mctp_dev *mdev;
126306d2f4c5SMatt Johnston struct rtmsg *rtm;
126406d2f4c5SMatt Johnston unsigned int mtu;
126506d2f4c5SMatt Johnston int rc;
126606d2f4c5SMatt Johnston
126706d2f4c5SMatt Johnston rc = mctp_route_nlparse(skb, nlh, extack, tb,
126806d2f4c5SMatt Johnston &rtm, &mdev, &daddr_start);
126906d2f4c5SMatt Johnston if (rc < 0)
127006d2f4c5SMatt Johnston return rc;
127106d2f4c5SMatt Johnston
127206d2f4c5SMatt Johnston if (rtm->rtm_type != RTN_UNICAST) {
127306d2f4c5SMatt Johnston NL_SET_ERR_MSG(extack, "rtm_type must be RTN_UNICAST");
127406d2f4c5SMatt Johnston return -EINVAL;
127506d2f4c5SMatt Johnston }
127606d2f4c5SMatt Johnston
127706d2f4c5SMatt Johnston mtu = 0;
12786183569dSMatt Johnston if (tb[RTA_METRICS]) {
12796183569dSMatt Johnston rc = nla_parse_nested(tbx, RTAX_MAX, tb[RTA_METRICS],
12806183569dSMatt Johnston rta_metrics_policy, NULL);
12816183569dSMatt Johnston if (rc < 0)
12826183569dSMatt Johnston return rc;
12836183569dSMatt Johnston if (tbx[RTAX_MTU])
12846183569dSMatt Johnston mtu = nla_get_u32(tbx[RTAX_MTU]);
12856183569dSMatt Johnston }
128606d2f4c5SMatt Johnston
128783f0a0b7SJeremy Kerr rc = mctp_route_add(mdev, daddr_start, rtm->rtm_dst_len, mtu,
128883f0a0b7SJeremy Kerr rtm->rtm_type);
128906d2f4c5SMatt Johnston return rc;
129006d2f4c5SMatt Johnston }
129106d2f4c5SMatt Johnston
mctp_delroute(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)129206d2f4c5SMatt Johnston static int mctp_delroute(struct sk_buff *skb, struct nlmsghdr *nlh,
129306d2f4c5SMatt Johnston struct netlink_ext_ack *extack)
129406d2f4c5SMatt Johnston {
129506d2f4c5SMatt Johnston struct nlattr *tb[RTA_MAX + 1];
129606d2f4c5SMatt Johnston mctp_eid_t daddr_start;
129706d2f4c5SMatt Johnston struct mctp_dev *mdev;
129806d2f4c5SMatt Johnston struct rtmsg *rtm;
129906d2f4c5SMatt Johnston int rc;
130006d2f4c5SMatt Johnston
130106d2f4c5SMatt Johnston rc = mctp_route_nlparse(skb, nlh, extack, tb,
130206d2f4c5SMatt Johnston &rtm, &mdev, &daddr_start);
130306d2f4c5SMatt Johnston if (rc < 0)
130406d2f4c5SMatt Johnston return rc;
130506d2f4c5SMatt Johnston
130606d2f4c5SMatt Johnston /* we only have unicast routes */
130706d2f4c5SMatt Johnston if (rtm->rtm_type != RTN_UNICAST)
130806d2f4c5SMatt Johnston return -EINVAL;
130906d2f4c5SMatt Johnston
131076d00160SMatt Johnston rc = mctp_route_remove(mdev, daddr_start, rtm->rtm_dst_len, RTN_UNICAST);
131106d2f4c5SMatt Johnston return rc;
131206d2f4c5SMatt Johnston }
131306d2f4c5SMatt Johnston
mctp_fill_rtinfo(struct sk_buff * skb,struct mctp_route * rt,u32 portid,u32 seq,int event,unsigned int flags)131406d2f4c5SMatt Johnston static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt,
131506d2f4c5SMatt Johnston u32 portid, u32 seq, int event, unsigned int flags)
131606d2f4c5SMatt Johnston {
131706d2f4c5SMatt Johnston struct nlmsghdr *nlh;
131806d2f4c5SMatt Johnston struct rtmsg *hdr;
131906d2f4c5SMatt Johnston void *metrics;
132006d2f4c5SMatt Johnston
132106d2f4c5SMatt Johnston nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags);
132206d2f4c5SMatt Johnston if (!nlh)
132306d2f4c5SMatt Johnston return -EMSGSIZE;
132406d2f4c5SMatt Johnston
132506d2f4c5SMatt Johnston hdr = nlmsg_data(nlh);
132606d2f4c5SMatt Johnston hdr->rtm_family = AF_MCTP;
132706d2f4c5SMatt Johnston
132806d2f4c5SMatt Johnston /* we use the _len fields as a number of EIDs, rather than
132906d2f4c5SMatt Johnston * a number of bits in the address
133006d2f4c5SMatt Johnston */
133106d2f4c5SMatt Johnston hdr->rtm_dst_len = rt->max - rt->min;
133206d2f4c5SMatt Johnston hdr->rtm_src_len = 0;
133306d2f4c5SMatt Johnston hdr->rtm_tos = 0;
133406d2f4c5SMatt Johnston hdr->rtm_table = RT_TABLE_DEFAULT;
133506d2f4c5SMatt Johnston hdr->rtm_protocol = RTPROT_STATIC; /* everything is user-defined */
133606d2f4c5SMatt Johnston hdr->rtm_scope = RT_SCOPE_LINK; /* TODO: scope in mctp_route? */
133783f0a0b7SJeremy Kerr hdr->rtm_type = rt->type;
133806d2f4c5SMatt Johnston
133906d2f4c5SMatt Johnston if (nla_put_u8(skb, RTA_DST, rt->min))
134006d2f4c5SMatt Johnston goto cancel;
134106d2f4c5SMatt Johnston
134206d2f4c5SMatt Johnston metrics = nla_nest_start_noflag(skb, RTA_METRICS);
134306d2f4c5SMatt Johnston if (!metrics)
134406d2f4c5SMatt Johnston goto cancel;
134506d2f4c5SMatt Johnston
134606d2f4c5SMatt Johnston if (rt->mtu) {
134706d2f4c5SMatt Johnston if (nla_put_u32(skb, RTAX_MTU, rt->mtu))
134806d2f4c5SMatt Johnston goto cancel;
134906d2f4c5SMatt Johnston }
135006d2f4c5SMatt Johnston
135106d2f4c5SMatt Johnston nla_nest_end(skb, metrics);
135206d2f4c5SMatt Johnston
135306d2f4c5SMatt Johnston if (rt->dev) {
135406d2f4c5SMatt Johnston if (nla_put_u32(skb, RTA_OIF, rt->dev->dev->ifindex))
135506d2f4c5SMatt Johnston goto cancel;
135606d2f4c5SMatt Johnston }
135706d2f4c5SMatt Johnston
135806d2f4c5SMatt Johnston /* TODO: conditional neighbour physaddr? */
135906d2f4c5SMatt Johnston
136006d2f4c5SMatt Johnston nlmsg_end(skb, nlh);
136106d2f4c5SMatt Johnston
136206d2f4c5SMatt Johnston return 0;
136306d2f4c5SMatt Johnston
136406d2f4c5SMatt Johnston cancel:
136506d2f4c5SMatt Johnston nlmsg_cancel(skb, nlh);
136606d2f4c5SMatt Johnston return -EMSGSIZE;
136706d2f4c5SMatt Johnston }
136806d2f4c5SMatt Johnston
mctp_dump_rtinfo(struct sk_buff * skb,struct netlink_callback * cb)136906d2f4c5SMatt Johnston static int mctp_dump_rtinfo(struct sk_buff *skb, struct netlink_callback *cb)
137006d2f4c5SMatt Johnston {
137106d2f4c5SMatt Johnston struct net *net = sock_net(skb->sk);
137206d2f4c5SMatt Johnston struct mctp_route *rt;
137306d2f4c5SMatt Johnston int s_idx, idx;
137406d2f4c5SMatt Johnston
137506d2f4c5SMatt Johnston /* TODO: allow filtering on route data, possibly under
137606d2f4c5SMatt Johnston * cb->strict_check
137706d2f4c5SMatt Johnston */
137806d2f4c5SMatt Johnston
137906d2f4c5SMatt Johnston /* TODO: change to struct overlay */
138006d2f4c5SMatt Johnston s_idx = cb->args[0];
138106d2f4c5SMatt Johnston idx = 0;
138206d2f4c5SMatt Johnston
138306d2f4c5SMatt Johnston rcu_read_lock();
138406d2f4c5SMatt Johnston list_for_each_entry_rcu(rt, &net->mctp.routes, list) {
138506d2f4c5SMatt Johnston if (idx++ < s_idx)
138606d2f4c5SMatt Johnston continue;
138706d2f4c5SMatt Johnston if (mctp_fill_rtinfo(skb, rt,
138806d2f4c5SMatt Johnston NETLINK_CB(cb->skb).portid,
138906d2f4c5SMatt Johnston cb->nlh->nlmsg_seq,
139006d2f4c5SMatt Johnston RTM_NEWROUTE, NLM_F_MULTI) < 0)
139106d2f4c5SMatt Johnston break;
139206d2f4c5SMatt Johnston }
139306d2f4c5SMatt Johnston
139406d2f4c5SMatt Johnston rcu_read_unlock();
139506d2f4c5SMatt Johnston cb->args[0] = idx;
139606d2f4c5SMatt Johnston
139706d2f4c5SMatt Johnston return skb->len;
139806d2f4c5SMatt Johnston }
139906d2f4c5SMatt Johnston
1400889b7da2SJeremy Kerr /* net namespace implementation */
mctp_routes_net_init(struct net * net)1401889b7da2SJeremy Kerr static int __net_init mctp_routes_net_init(struct net *net)
1402889b7da2SJeremy Kerr {
1403889b7da2SJeremy Kerr struct netns_mctp *ns = &net->mctp;
1404889b7da2SJeremy Kerr
1405889b7da2SJeremy Kerr INIT_LIST_HEAD(&ns->routes);
1406833ef3b9SJeremy Kerr INIT_HLIST_HEAD(&ns->binds);
1407833ef3b9SJeremy Kerr mutex_init(&ns->bind_lock);
1408833ef3b9SJeremy Kerr INIT_HLIST_HEAD(&ns->keys);
1409833ef3b9SJeremy Kerr spin_lock_init(&ns->keys_lock);
141003f2bbc4SMatt Johnston WARN_ON(mctp_default_net_set(net, MCTP_INITIAL_DEFAULT_NET));
1411889b7da2SJeremy Kerr return 0;
1412889b7da2SJeremy Kerr }
1413889b7da2SJeremy Kerr
mctp_routes_net_exit(struct net * net)1414889b7da2SJeremy Kerr static void __net_exit mctp_routes_net_exit(struct net *net)
1415889b7da2SJeremy Kerr {
1416889b7da2SJeremy Kerr struct mctp_route *rt;
1417889b7da2SJeremy Kerr
1418581edcd0SJeremy Kerr rcu_read_lock();
1419889b7da2SJeremy Kerr list_for_each_entry_rcu(rt, &net->mctp.routes, list)
1420889b7da2SJeremy Kerr mctp_route_release(rt);
1421581edcd0SJeremy Kerr rcu_read_unlock();
1422889b7da2SJeremy Kerr }
1423889b7da2SJeremy Kerr
1424889b7da2SJeremy Kerr static struct pernet_operations mctp_net_ops = {
1425889b7da2SJeremy Kerr .init = mctp_routes_net_init,
1426889b7da2SJeremy Kerr .exit = mctp_routes_net_exit,
1427889b7da2SJeremy Kerr };
1428889b7da2SJeremy Kerr
1429f4df31a0SKuniyuki Iwashima static const struct rtnl_msg_handler mctp_route_rtnl_msg_handlers[] = {
1430f4df31a0SKuniyuki Iwashima {THIS_MODULE, PF_MCTP, RTM_NEWROUTE, mctp_newroute, NULL, 0},
1431f4df31a0SKuniyuki Iwashima {THIS_MODULE, PF_MCTP, RTM_DELROUTE, mctp_delroute, NULL, 0},
1432f4df31a0SKuniyuki Iwashima {THIS_MODULE, PF_MCTP, RTM_GETROUTE, NULL, mctp_dump_rtinfo, 0},
1433f4df31a0SKuniyuki Iwashima };
1434f4df31a0SKuniyuki Iwashima
mctp_routes_init(void)1435889b7da2SJeremy Kerr int __init mctp_routes_init(void)
1436889b7da2SJeremy Kerr {
1437f4df31a0SKuniyuki Iwashima int err;
1438f4df31a0SKuniyuki Iwashima
1439889b7da2SJeremy Kerr dev_add_pack(&mctp_packet_type);
144006d2f4c5SMatt Johnston
1441f4df31a0SKuniyuki Iwashima err = register_pernet_subsys(&mctp_net_ops);
1442f4df31a0SKuniyuki Iwashima if (err)
1443f4df31a0SKuniyuki Iwashima goto err_pernet;
144406d2f4c5SMatt Johnston
1445f4df31a0SKuniyuki Iwashima err = rtnl_register_many(mctp_route_rtnl_msg_handlers);
1446f4df31a0SKuniyuki Iwashima if (err)
1447f4df31a0SKuniyuki Iwashima goto err_rtnl;
1448f4df31a0SKuniyuki Iwashima
1449f4df31a0SKuniyuki Iwashima return 0;
1450f4df31a0SKuniyuki Iwashima
1451f4df31a0SKuniyuki Iwashima err_rtnl:
1452f4df31a0SKuniyuki Iwashima unregister_pernet_subsys(&mctp_net_ops);
1453f4df31a0SKuniyuki Iwashima err_pernet:
1454f4df31a0SKuniyuki Iwashima dev_remove_pack(&mctp_packet_type);
1455f4df31a0SKuniyuki Iwashima return err;
1456889b7da2SJeremy Kerr }
1457889b7da2SJeremy Kerr
mctp_routes_exit(void)1458d4072058SWei Yongjun void mctp_routes_exit(void)
1459889b7da2SJeremy Kerr {
1460f4df31a0SKuniyuki Iwashima rtnl_unregister_many(mctp_route_rtnl_msg_handlers);
1461889b7da2SJeremy Kerr unregister_pernet_subsys(&mctp_net_ops);
1462889b7da2SJeremy Kerr dev_remove_pack(&mctp_packet_type);
1463889b7da2SJeremy Kerr }
1464161eba50SJeremy Kerr
1465161eba50SJeremy Kerr #if IS_ENABLED(CONFIG_MCTP_TEST)
1466161eba50SJeremy Kerr #include "test/route-test.c"
1467161eba50SJeremy Kerr #endif
1468