diff options
Diffstat (limited to 'net/mctp/route.c')
| -rw-r--r-- | net/mctp/route.c | 110 |
1 files changed, 51 insertions, 59 deletions
diff --git a/net/mctp/route.c b/net/mctp/route.c index 59ad60b88563..26fb8c6bbad2 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -880,9 +880,25 @@ static bool mctp_rt_compare_exact(struct mctp_route *rt1, rt1->max == rt2->max; } +static mctp_eid_t mctp_dev_saddr(struct mctp_dev *dev) +{ + mctp_eid_t addr = MCTP_ADDR_NULL; + unsigned long flags; + + spin_lock_irqsave(&dev->addrs_lock, flags); + if (dev->num_addrs) { + /* use the outbound interface's first address as our source */ + addr = dev->addrs[0]; + } + spin_unlock_irqrestore(&dev->addrs_lock, flags); + + return addr; +} + /* must only be called on a direct route, as the final output hop */ static void mctp_dst_from_route(struct mctp_dst *dst, mctp_eid_t eid, - unsigned int mtu, struct mctp_route *route) + mctp_eid_t saddr, unsigned int mtu, + struct mctp_route *route) { mctp_dev_hold(route->dev); dst->nexthop = eid; @@ -892,6 +908,7 @@ static void mctp_dst_from_route(struct mctp_dst *dst, mctp_eid_t eid, dst->mtu = min(dst->mtu, mtu); dst->halen = 0; dst->output = route->output; + dst->saddr = saddr; } int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex, @@ -924,6 +941,7 @@ int mctp_dst_from_extaddr(struct mctp_dst *dst, struct net *net, int ifindex, dst->halen = halen; dst->output = mctp_dst_output; dst->nexthop = 0; + dst->saddr = mctp_dev_saddr(dev); memcpy(dst->haddr, haddr, halen); rc = 0; @@ -978,8 +996,14 @@ int mctp_route_lookup(struct net *net, unsigned int dnet, mtu = mtu ?: rt->mtu; if (rt->dst_type == MCTP_ROUTE_DIRECT) { + mctp_eid_t saddr = mctp_dev_saddr(rt->dev); + + /* cannot do gateway-ed routes without a src */ + if (saddr == MCTP_ADDR_NULL && depth != 0) + break; + if (dst) - mctp_dst_from_route(dst, daddr, mtu, rt); + mctp_dst_from_route(dst, daddr, saddr, mtu, rt); rc = 0; break; @@ -993,29 +1017,22 @@ int mctp_route_lookup(struct net *net, unsigned int dnet, return rc; } -static int mctp_route_lookup_null(struct net *net, struct net_device *dev, - struct mctp_dst *dst) +static int mctp_dst_input_null(struct net *net, struct net_device *dev, + struct mctp_dst *dst) { - int rc = -EHOSTUNREACH; - struct mctp_route *rt; - rcu_read_lock(); + dst->dev = __mctp_dev_get(dev); + rcu_read_unlock(); - list_for_each_entry_rcu(rt, &net->mctp.routes, list) { - if (rt->dst_type != MCTP_ROUTE_DIRECT || rt->type != RTN_LOCAL) - continue; - - if (rt->dev->dev != dev) - continue; - - mctp_dst_from_route(dst, 0, 0, rt); - rc = 0; - break; - } + if (!dst->dev) + return -EHOSTUNREACH; - rcu_read_unlock(); + dst->mtu = READ_ONCE(dev->mtu); + dst->halen = 0; + dst->output = mctp_dst_input; + dst->nexthop = 0; - return rc; + return 0; } static int mctp_do_fragment_route(struct mctp_dst *dst, struct sk_buff *skb, @@ -1037,6 +1054,13 @@ static int mctp_do_fragment_route(struct mctp_dst *dst, struct sk_buff *skb, return -EMSGSIZE; } + /* within MTU? avoid the copy, send original skb */ + if (skb->len <= mtu) { + hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | + MCTP_HDR_FLAG_EOM | tag; + return dst->output(dst, skb); + } + /* keep same headroom as the original skb */ headroom = skb_headroom(skb); @@ -1109,43 +1133,25 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk); struct mctp_sk_key *key; struct mctp_hdr *hdr; - unsigned long flags; unsigned int netid; - unsigned int mtu; - mctp_eid_t saddr; - int rc; u8 tag; KUNIT_STATIC_STUB_REDIRECT(mctp_local_output, sk, dst, skb, daddr, req_tag); - rc = -ENODEV; - - spin_lock_irqsave(&dst->dev->addrs_lock, flags); - if (dst->dev->num_addrs == 0) { - rc = -EHOSTUNREACH; - } else { - /* use the outbound interface's first address as our source */ - saddr = dst->dev->addrs[0]; - rc = 0; - } - spin_unlock_irqrestore(&dst->dev->addrs_lock, flags); netid = READ_ONCE(dst->dev->net); - if (rc) - goto out_release; - if (req_tag & MCTP_TAG_OWNER) { if (req_tag & MCTP_TAG_PREALLOC) key = mctp_lookup_prealloc_tag(msk, netid, daddr, req_tag, &tag); else - key = mctp_alloc_local_tag(msk, netid, saddr, daddr, - false, &tag); + key = mctp_alloc_local_tag(msk, netid, dst->saddr, + daddr, false, &tag); if (IS_ERR(key)) { - rc = PTR_ERR(key); - goto out_release; + kfree_skb(skb); + return PTR_ERR(key); } mctp_skb_set_flow(skb, key); /* done with the key in this scope */ @@ -1168,24 +1174,10 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, hdr = mctp_hdr(skb); hdr->ver = 1; hdr->dest = daddr; - hdr->src = saddr; - - mtu = dst->mtu; - - if (skb->len + sizeof(struct mctp_hdr) <= mtu) { - hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | - MCTP_HDR_FLAG_EOM | tag; - rc = dst->output(dst, skb); - } else { - rc = mctp_do_fragment_route(dst, skb, mtu, tag); - } + hdr->src = dst->saddr; /* route output functions consume the skb, even on error */ - skb = NULL; - -out_release: - kfree_skb(skb); - return rc; + return mctp_do_fragment_route(dst, skb, dst->mtu, tag); } /* route management */ @@ -1370,7 +1362,7 @@ static int mctp_pkttype_receive(struct sk_buff *skb, struct net_device *dev, /* NULL EID, but addressed to our physical address */ if (rc && mh->dest == MCTP_ADDR_NULL && skb->pkt_type == PACKET_HOST) - rc = mctp_route_lookup_null(net, dev, &dst); + rc = mctp_dst_input_null(net, dev, &dst); if (rc) goto err_drop; |
