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