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