xref: /kernel/linux/linux-6.6/net/mctp/af_mctp.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Management Component Transport Protocol (MCTP)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2021 Code Construct
662306a36Sopenharmony_ci * Copyright (c) 2021 Google
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/compat.h>
1062306a36Sopenharmony_ci#include <linux/if_arp.h>
1162306a36Sopenharmony_ci#include <linux/net.h>
1262306a36Sopenharmony_ci#include <linux/mctp.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/socket.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <net/mctp.h>
1762306a36Sopenharmony_ci#include <net/mctpdevice.h>
1862306a36Sopenharmony_ci#include <net/sock.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
2162306a36Sopenharmony_ci#include <trace/events/mctp.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* socket implementation */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void mctp_sk_expire_keys(struct timer_list *timer);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int mctp_release(struct socket *sock)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (sk) {
3262306a36Sopenharmony_ci		sock->sk = NULL;
3362306a36Sopenharmony_ci		sk->sk_prot->close(sk, 0);
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Generic sockaddr checks, padding checks only so far */
4062306a36Sopenharmony_cistatic bool mctp_sockaddr_is_ok(const struct sockaddr_mctp *addr)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	return !addr->__smctp_pad0 && !addr->__smctp_pad1;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool mctp_sockaddr_ext_is_ok(const struct sockaddr_mctp_ext *addr)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	return !addr->__smctp_pad0[0] &&
4862306a36Sopenharmony_ci	       !addr->__smctp_pad0[1] &&
4962306a36Sopenharmony_ci	       !addr->__smctp_pad0[2];
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int mctp_bind(struct socket *sock, struct sockaddr *addr, int addrlen)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct sock *sk = sock->sk;
5562306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
5662306a36Sopenharmony_ci	struct sockaddr_mctp *smctp;
5762306a36Sopenharmony_ci	int rc;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (addrlen < sizeof(*smctp))
6062306a36Sopenharmony_ci		return -EINVAL;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (addr->sa_family != AF_MCTP)
6362306a36Sopenharmony_ci		return -EAFNOSUPPORT;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (!capable(CAP_NET_BIND_SERVICE))
6662306a36Sopenharmony_ci		return -EACCES;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* it's a valid sockaddr for MCTP, cast and do protocol checks */
6962306a36Sopenharmony_ci	smctp = (struct sockaddr_mctp *)addr;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	if (!mctp_sockaddr_is_ok(smctp))
7262306a36Sopenharmony_ci		return -EINVAL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	lock_sock(sk);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* TODO: allow rebind */
7762306a36Sopenharmony_ci	if (sk_hashed(sk)) {
7862306a36Sopenharmony_ci		rc = -EADDRINUSE;
7962306a36Sopenharmony_ci		goto out_release;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	msk->bind_net = smctp->smctp_network;
8262306a36Sopenharmony_ci	msk->bind_addr = smctp->smctp_addr.s_addr;
8362306a36Sopenharmony_ci	msk->bind_type = smctp->smctp_type & 0x7f; /* ignore the IC bit */
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	rc = sk->sk_prot->hash(sk);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciout_release:
8862306a36Sopenharmony_ci	release_sock(sk);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return rc;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int mctp_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name);
9662306a36Sopenharmony_ci	int rc, addrlen = msg->msg_namelen;
9762306a36Sopenharmony_ci	struct sock *sk = sock->sk;
9862306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
9962306a36Sopenharmony_ci	struct mctp_skb_cb *cb;
10062306a36Sopenharmony_ci	struct mctp_route *rt;
10162306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
10262306a36Sopenharmony_ci	int hlen;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (addr) {
10562306a36Sopenharmony_ci		const u8 tagbits = MCTP_TAG_MASK | MCTP_TAG_OWNER |
10662306a36Sopenharmony_ci			MCTP_TAG_PREALLOC;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		if (addrlen < sizeof(struct sockaddr_mctp))
10962306a36Sopenharmony_ci			return -EINVAL;
11062306a36Sopenharmony_ci		if (addr->smctp_family != AF_MCTP)
11162306a36Sopenharmony_ci			return -EINVAL;
11262306a36Sopenharmony_ci		if (!mctp_sockaddr_is_ok(addr))
11362306a36Sopenharmony_ci			return -EINVAL;
11462306a36Sopenharmony_ci		if (addr->smctp_tag & ~tagbits)
11562306a36Sopenharmony_ci			return -EINVAL;
11662306a36Sopenharmony_ci		/* can't preallocate a non-owned tag */
11762306a36Sopenharmony_ci		if (addr->smctp_tag & MCTP_TAG_PREALLOC &&
11862306a36Sopenharmony_ci		    !(addr->smctp_tag & MCTP_TAG_OWNER))
11962306a36Sopenharmony_ci			return -EINVAL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		/* TODO: connect()ed sockets */
12362306a36Sopenharmony_ci		return -EDESTADDRREQ;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!capable(CAP_NET_RAW))
12762306a36Sopenharmony_ci		return -EACCES;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (addr->smctp_network == MCTP_NET_ANY)
13062306a36Sopenharmony_ci		addr->smctp_network = mctp_default_net(sock_net(sk));
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* direct addressing */
13362306a36Sopenharmony_ci	if (msk->addr_ext && addrlen >= sizeof(struct sockaddr_mctp_ext)) {
13462306a36Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_mctp_ext *,
13562306a36Sopenharmony_ci				 extaddr, msg->msg_name);
13662306a36Sopenharmony_ci		struct net_device *dev;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		rc = -EINVAL;
13962306a36Sopenharmony_ci		rcu_read_lock();
14062306a36Sopenharmony_ci		dev = dev_get_by_index_rcu(sock_net(sk), extaddr->smctp_ifindex);
14162306a36Sopenharmony_ci		/* check for correct halen */
14262306a36Sopenharmony_ci		if (dev && extaddr->smctp_halen == dev->addr_len) {
14362306a36Sopenharmony_ci			hlen = LL_RESERVED_SPACE(dev) + sizeof(struct mctp_hdr);
14462306a36Sopenharmony_ci			rc = 0;
14562306a36Sopenharmony_ci		}
14662306a36Sopenharmony_ci		rcu_read_unlock();
14762306a36Sopenharmony_ci		if (rc)
14862306a36Sopenharmony_ci			goto err_free;
14962306a36Sopenharmony_ci		rt = NULL;
15062306a36Sopenharmony_ci	} else {
15162306a36Sopenharmony_ci		rt = mctp_route_lookup(sock_net(sk), addr->smctp_network,
15262306a36Sopenharmony_ci				       addr->smctp_addr.s_addr);
15362306a36Sopenharmony_ci		if (!rt) {
15462306a36Sopenharmony_ci			rc = -EHOSTUNREACH;
15562306a36Sopenharmony_ci			goto err_free;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci		hlen = LL_RESERVED_SPACE(rt->dev->dev) + sizeof(struct mctp_hdr);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	skb = sock_alloc_send_skb(sk, hlen + 1 + len,
16162306a36Sopenharmony_ci				  msg->msg_flags & MSG_DONTWAIT, &rc);
16262306a36Sopenharmony_ci	if (!skb)
16362306a36Sopenharmony_ci		return rc;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	skb_reserve(skb, hlen);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* set type as fist byte in payload */
16862306a36Sopenharmony_ci	*(u8 *)skb_put(skb, 1) = addr->smctp_type;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	rc = memcpy_from_msg((void *)skb_put(skb, len), msg, len);
17162306a36Sopenharmony_ci	if (rc < 0)
17262306a36Sopenharmony_ci		goto err_free;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* set up cb */
17562306a36Sopenharmony_ci	cb = __mctp_cb(skb);
17662306a36Sopenharmony_ci	cb->net = addr->smctp_network;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (!rt) {
17962306a36Sopenharmony_ci		/* fill extended address in cb */
18062306a36Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_mctp_ext *,
18162306a36Sopenharmony_ci				 extaddr, msg->msg_name);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		if (!mctp_sockaddr_ext_is_ok(extaddr) ||
18462306a36Sopenharmony_ci		    extaddr->smctp_halen > sizeof(cb->haddr)) {
18562306a36Sopenharmony_ci			rc = -EINVAL;
18662306a36Sopenharmony_ci			goto err_free;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		cb->ifindex = extaddr->smctp_ifindex;
19062306a36Sopenharmony_ci		/* smctp_halen is checked above */
19162306a36Sopenharmony_ci		cb->halen = extaddr->smctp_halen;
19262306a36Sopenharmony_ci		memcpy(cb->haddr, extaddr->smctp_haddr, cb->halen);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	rc = mctp_local_output(sk, rt, skb, addr->smctp_addr.s_addr,
19662306a36Sopenharmony_ci			       addr->smctp_tag);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return rc ? : len;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cierr_free:
20162306a36Sopenharmony_ci	kfree_skb(skb);
20262306a36Sopenharmony_ci	return rc;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int mctp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
20662306a36Sopenharmony_ci			int flags)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	DECLARE_SOCKADDR(struct sockaddr_mctp *, addr, msg->msg_name);
20962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
21062306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
21162306a36Sopenharmony_ci	struct sk_buff *skb;
21262306a36Sopenharmony_ci	size_t msglen;
21362306a36Sopenharmony_ci	u8 type;
21462306a36Sopenharmony_ci	int rc;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (flags & ~(MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK))
21762306a36Sopenharmony_ci		return -EOPNOTSUPP;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	skb = skb_recv_datagram(sk, flags, &rc);
22062306a36Sopenharmony_ci	if (!skb)
22162306a36Sopenharmony_ci		return rc;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!skb->len) {
22462306a36Sopenharmony_ci		rc = 0;
22562306a36Sopenharmony_ci		goto out_free;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* extract message type, remove from data */
22962306a36Sopenharmony_ci	type = *((u8 *)skb->data);
23062306a36Sopenharmony_ci	msglen = skb->len - 1;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (len < msglen)
23362306a36Sopenharmony_ci		msg->msg_flags |= MSG_TRUNC;
23462306a36Sopenharmony_ci	else
23562306a36Sopenharmony_ci		len = msglen;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	rc = skb_copy_datagram_msg(skb, 1, msg, len);
23862306a36Sopenharmony_ci	if (rc < 0)
23962306a36Sopenharmony_ci		goto out_free;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	sock_recv_cmsgs(msg, sk, skb);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (addr) {
24462306a36Sopenharmony_ci		struct mctp_skb_cb *cb = mctp_cb(skb);
24562306a36Sopenharmony_ci		/* TODO: expand mctp_skb_cb for header fields? */
24662306a36Sopenharmony_ci		struct mctp_hdr *hdr = mctp_hdr(skb);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		addr = msg->msg_name;
24962306a36Sopenharmony_ci		addr->smctp_family = AF_MCTP;
25062306a36Sopenharmony_ci		addr->__smctp_pad0 = 0;
25162306a36Sopenharmony_ci		addr->smctp_network = cb->net;
25262306a36Sopenharmony_ci		addr->smctp_addr.s_addr = hdr->src;
25362306a36Sopenharmony_ci		addr->smctp_type = type;
25462306a36Sopenharmony_ci		addr->smctp_tag = hdr->flags_seq_tag &
25562306a36Sopenharmony_ci					(MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO);
25662306a36Sopenharmony_ci		addr->__smctp_pad1 = 0;
25762306a36Sopenharmony_ci		msg->msg_namelen = sizeof(*addr);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (msk->addr_ext) {
26062306a36Sopenharmony_ci			DECLARE_SOCKADDR(struct sockaddr_mctp_ext *, ae,
26162306a36Sopenharmony_ci					 msg->msg_name);
26262306a36Sopenharmony_ci			msg->msg_namelen = sizeof(*ae);
26362306a36Sopenharmony_ci			ae->smctp_ifindex = cb->ifindex;
26462306a36Sopenharmony_ci			ae->smctp_halen = cb->halen;
26562306a36Sopenharmony_ci			memset(ae->__smctp_pad0, 0x0, sizeof(ae->__smctp_pad0));
26662306a36Sopenharmony_ci			memset(ae->smctp_haddr, 0x0, sizeof(ae->smctp_haddr));
26762306a36Sopenharmony_ci			memcpy(ae->smctp_haddr, cb->haddr, cb->halen);
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	rc = len;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (flags & MSG_TRUNC)
27462306a36Sopenharmony_ci		rc = msglen;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ciout_free:
27762306a36Sopenharmony_ci	skb_free_datagram(sk, skb);
27862306a36Sopenharmony_ci	return rc;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/* We're done with the key; invalidate, stop reassembly, and remove from lists.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_cistatic void __mctp_key_remove(struct mctp_sk_key *key, struct net *net,
28462306a36Sopenharmony_ci			      unsigned long flags, unsigned long reason)
28562306a36Sopenharmony_ci__releases(&key->lock)
28662306a36Sopenharmony_ci__must_hold(&net->mctp.keys_lock)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct sk_buff *skb;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	trace_mctp_key_release(key, reason);
29162306a36Sopenharmony_ci	skb = key->reasm_head;
29262306a36Sopenharmony_ci	key->reasm_head = NULL;
29362306a36Sopenharmony_ci	key->reasm_dead = true;
29462306a36Sopenharmony_ci	key->valid = false;
29562306a36Sopenharmony_ci	mctp_dev_release_key(key->dev, key);
29662306a36Sopenharmony_ci	spin_unlock_irqrestore(&key->lock, flags);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!hlist_unhashed(&key->hlist)) {
29962306a36Sopenharmony_ci		hlist_del_init(&key->hlist);
30062306a36Sopenharmony_ci		hlist_del_init(&key->sklist);
30162306a36Sopenharmony_ci		/* unref for the lists */
30262306a36Sopenharmony_ci		mctp_key_unref(key);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	kfree_skb(skb);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int mctp_setsockopt(struct socket *sock, int level, int optname,
30962306a36Sopenharmony_ci			   sockptr_t optval, unsigned int optlen)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk);
31262306a36Sopenharmony_ci	int val;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (level != SOL_MCTP)
31562306a36Sopenharmony_ci		return -EINVAL;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (optname == MCTP_OPT_ADDR_EXT) {
31862306a36Sopenharmony_ci		if (optlen != sizeof(int))
31962306a36Sopenharmony_ci			return -EINVAL;
32062306a36Sopenharmony_ci		if (copy_from_sockptr(&val, optval, sizeof(int)))
32162306a36Sopenharmony_ci			return -EFAULT;
32262306a36Sopenharmony_ci		msk->addr_ext = val;
32362306a36Sopenharmony_ci		return 0;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return -ENOPROTOOPT;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int mctp_getsockopt(struct socket *sock, int level, int optname,
33062306a36Sopenharmony_ci			   char __user *optval, int __user *optlen)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk);
33362306a36Sopenharmony_ci	int len, val;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (level != SOL_MCTP)
33662306a36Sopenharmony_ci		return -EINVAL;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (get_user(len, optlen))
33962306a36Sopenharmony_ci		return -EFAULT;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (optname == MCTP_OPT_ADDR_EXT) {
34262306a36Sopenharmony_ci		if (len != sizeof(int))
34362306a36Sopenharmony_ci			return -EINVAL;
34462306a36Sopenharmony_ci		val = !!msk->addr_ext;
34562306a36Sopenharmony_ci		if (copy_to_user(optval, &val, len))
34662306a36Sopenharmony_ci			return -EFAULT;
34762306a36Sopenharmony_ci		return 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return -EINVAL;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int mctp_ioctl_alloctag(struct mctp_sock *msk, unsigned long arg)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	struct net *net = sock_net(&msk->sk);
35662306a36Sopenharmony_ci	struct mctp_sk_key *key = NULL;
35762306a36Sopenharmony_ci	struct mctp_ioc_tag_ctl ctl;
35862306a36Sopenharmony_ci	unsigned long flags;
35962306a36Sopenharmony_ci	u8 tag;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl)))
36262306a36Sopenharmony_ci		return -EFAULT;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (ctl.tag)
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (ctl.flags)
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	key = mctp_alloc_local_tag(msk, ctl.peer_addr, MCTP_ADDR_ANY,
37162306a36Sopenharmony_ci				   true, &tag);
37262306a36Sopenharmony_ci	if (IS_ERR(key))
37362306a36Sopenharmony_ci		return PTR_ERR(key);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ctl.tag = tag | MCTP_TAG_OWNER | MCTP_TAG_PREALLOC;
37662306a36Sopenharmony_ci	if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl))) {
37762306a36Sopenharmony_ci		unsigned long fl2;
37862306a36Sopenharmony_ci		/* Unwind our key allocation: the keys list lock needs to be
37962306a36Sopenharmony_ci		 * taken before the individual key locks, and we need a valid
38062306a36Sopenharmony_ci		 * flags value (fl2) to pass to __mctp_key_remove, hence the
38162306a36Sopenharmony_ci		 * second spin_lock_irqsave() rather than a plain spin_lock().
38262306a36Sopenharmony_ci		 */
38362306a36Sopenharmony_ci		spin_lock_irqsave(&net->mctp.keys_lock, flags);
38462306a36Sopenharmony_ci		spin_lock_irqsave(&key->lock, fl2);
38562306a36Sopenharmony_ci		__mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_DROPPED);
38662306a36Sopenharmony_ci		mctp_key_unref(key);
38762306a36Sopenharmony_ci		spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
38862306a36Sopenharmony_ci		return -EFAULT;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mctp_key_unref(key);
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int mctp_ioctl_droptag(struct mctp_sock *msk, unsigned long arg)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct net *net = sock_net(&msk->sk);
39862306a36Sopenharmony_ci	struct mctp_ioc_tag_ctl ctl;
39962306a36Sopenharmony_ci	unsigned long flags, fl2;
40062306a36Sopenharmony_ci	struct mctp_sk_key *key;
40162306a36Sopenharmony_ci	struct hlist_node *tmp;
40262306a36Sopenharmony_ci	int rc;
40362306a36Sopenharmony_ci	u8 tag;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (copy_from_user(&ctl, (void __user *)arg, sizeof(ctl)))
40662306a36Sopenharmony_ci		return -EFAULT;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (ctl.flags)
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Must be a local tag, TO set, preallocated */
41262306a36Sopenharmony_ci	if ((ctl.tag & ~MCTP_TAG_MASK) != (MCTP_TAG_OWNER | MCTP_TAG_PREALLOC))
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	tag = ctl.tag & MCTP_TAG_MASK;
41662306a36Sopenharmony_ci	rc = -EINVAL;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_lock_irqsave(&net->mctp.keys_lock, flags);
41962306a36Sopenharmony_ci	hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) {
42062306a36Sopenharmony_ci		/* we do an irqsave here, even though we know the irq state,
42162306a36Sopenharmony_ci		 * so we have the flags to pass to __mctp_key_remove
42262306a36Sopenharmony_ci		 */
42362306a36Sopenharmony_ci		spin_lock_irqsave(&key->lock, fl2);
42462306a36Sopenharmony_ci		if (key->manual_alloc &&
42562306a36Sopenharmony_ci		    ctl.peer_addr == key->peer_addr &&
42662306a36Sopenharmony_ci		    tag == key->tag) {
42762306a36Sopenharmony_ci			__mctp_key_remove(key, net, fl2,
42862306a36Sopenharmony_ci					  MCTP_TRACE_KEY_DROPPED);
42962306a36Sopenharmony_ci			rc = 0;
43062306a36Sopenharmony_ci		} else {
43162306a36Sopenharmony_ci			spin_unlock_irqrestore(&key->lock, fl2);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return rc;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int mctp_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sock->sk, struct mctp_sock, sk);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	switch (cmd) {
44462306a36Sopenharmony_ci	case SIOCMCTPALLOCTAG:
44562306a36Sopenharmony_ci		return mctp_ioctl_alloctag(msk, arg);
44662306a36Sopenharmony_ci	case SIOCMCTPDROPTAG:
44762306a36Sopenharmony_ci		return mctp_ioctl_droptag(msk, arg);
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return -EINVAL;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
45462306a36Sopenharmony_cistatic int mctp_compat_ioctl(struct socket *sock, unsigned int cmd,
45562306a36Sopenharmony_ci			     unsigned long arg)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	void __user *argp = compat_ptr(arg);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	switch (cmd) {
46062306a36Sopenharmony_ci	/* These have compatible ptr layouts */
46162306a36Sopenharmony_ci	case SIOCMCTPALLOCTAG:
46262306a36Sopenharmony_ci	case SIOCMCTPDROPTAG:
46362306a36Sopenharmony_ci		return mctp_ioctl(sock, cmd, (unsigned long)argp);
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return -ENOIOCTLCMD;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci#endif
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic const struct proto_ops mctp_dgram_ops = {
47162306a36Sopenharmony_ci	.family		= PF_MCTP,
47262306a36Sopenharmony_ci	.release	= mctp_release,
47362306a36Sopenharmony_ci	.bind		= mctp_bind,
47462306a36Sopenharmony_ci	.connect	= sock_no_connect,
47562306a36Sopenharmony_ci	.socketpair	= sock_no_socketpair,
47662306a36Sopenharmony_ci	.accept		= sock_no_accept,
47762306a36Sopenharmony_ci	.getname	= sock_no_getname,
47862306a36Sopenharmony_ci	.poll		= datagram_poll,
47962306a36Sopenharmony_ci	.ioctl		= mctp_ioctl,
48062306a36Sopenharmony_ci	.gettstamp	= sock_gettstamp,
48162306a36Sopenharmony_ci	.listen		= sock_no_listen,
48262306a36Sopenharmony_ci	.shutdown	= sock_no_shutdown,
48362306a36Sopenharmony_ci	.setsockopt	= mctp_setsockopt,
48462306a36Sopenharmony_ci	.getsockopt	= mctp_getsockopt,
48562306a36Sopenharmony_ci	.sendmsg	= mctp_sendmsg,
48662306a36Sopenharmony_ci	.recvmsg	= mctp_recvmsg,
48762306a36Sopenharmony_ci	.mmap		= sock_no_mmap,
48862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
48962306a36Sopenharmony_ci	.compat_ioctl	= mctp_compat_ioctl,
49062306a36Sopenharmony_ci#endif
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void mctp_sk_expire_keys(struct timer_list *timer)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(timer, struct mctp_sock,
49662306a36Sopenharmony_ci					     key_expiry);
49762306a36Sopenharmony_ci	struct net *net = sock_net(&msk->sk);
49862306a36Sopenharmony_ci	unsigned long next_expiry, flags, fl2;
49962306a36Sopenharmony_ci	struct mctp_sk_key *key;
50062306a36Sopenharmony_ci	struct hlist_node *tmp;
50162306a36Sopenharmony_ci	bool next_expiry_valid = false;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	spin_lock_irqsave(&net->mctp.keys_lock, flags);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) {
50662306a36Sopenharmony_ci		/* don't expire. manual_alloc is immutable, no locking
50762306a36Sopenharmony_ci		 * required.
50862306a36Sopenharmony_ci		 */
50962306a36Sopenharmony_ci		if (key->manual_alloc)
51062306a36Sopenharmony_ci			continue;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		spin_lock_irqsave(&key->lock, fl2);
51362306a36Sopenharmony_ci		if (!time_after_eq(key->expiry, jiffies)) {
51462306a36Sopenharmony_ci			__mctp_key_remove(key, net, fl2,
51562306a36Sopenharmony_ci					  MCTP_TRACE_KEY_TIMEOUT);
51662306a36Sopenharmony_ci			continue;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (next_expiry_valid) {
52062306a36Sopenharmony_ci			if (time_before(key->expiry, next_expiry))
52162306a36Sopenharmony_ci				next_expiry = key->expiry;
52262306a36Sopenharmony_ci		} else {
52362306a36Sopenharmony_ci			next_expiry = key->expiry;
52462306a36Sopenharmony_ci			next_expiry_valid = true;
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci		spin_unlock_irqrestore(&key->lock, fl2);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (next_expiry_valid)
53262306a36Sopenharmony_ci		mod_timer(timer, next_expiry);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int mctp_sk_init(struct sock *sk)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	INIT_HLIST_HEAD(&msk->keys);
54062306a36Sopenharmony_ci	timer_setup(&msk->key_expiry, mctp_sk_expire_keys, 0);
54162306a36Sopenharmony_ci	return 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic void mctp_sk_close(struct sock *sk, long timeout)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	sk_common_release(sk);
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int mctp_sk_hash(struct sock *sk)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct net *net = sock_net(sk);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	mutex_lock(&net->mctp.bind_lock);
55462306a36Sopenharmony_ci	sk_add_node_rcu(sk, &net->mctp.binds);
55562306a36Sopenharmony_ci	mutex_unlock(&net->mctp.bind_lock);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return 0;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void mctp_sk_unhash(struct sock *sk)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
56362306a36Sopenharmony_ci	struct net *net = sock_net(sk);
56462306a36Sopenharmony_ci	unsigned long flags, fl2;
56562306a36Sopenharmony_ci	struct mctp_sk_key *key;
56662306a36Sopenharmony_ci	struct hlist_node *tmp;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* remove from any type-based binds */
56962306a36Sopenharmony_ci	mutex_lock(&net->mctp.bind_lock);
57062306a36Sopenharmony_ci	sk_del_node_init_rcu(sk);
57162306a36Sopenharmony_ci	mutex_unlock(&net->mctp.bind_lock);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* remove tag allocations */
57462306a36Sopenharmony_ci	spin_lock_irqsave(&net->mctp.keys_lock, flags);
57562306a36Sopenharmony_ci	hlist_for_each_entry_safe(key, tmp, &msk->keys, sklist) {
57662306a36Sopenharmony_ci		spin_lock_irqsave(&key->lock, fl2);
57762306a36Sopenharmony_ci		__mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_CLOSED);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci	sock_set_flag(sk, SOCK_DEAD);
58062306a36Sopenharmony_ci	spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Since there are no more tag allocations (we have removed all of the
58362306a36Sopenharmony_ci	 * keys), stop any pending expiry events. the timer cannot be re-queued
58462306a36Sopenharmony_ci	 * as the sk is no longer observable
58562306a36Sopenharmony_ci	 */
58662306a36Sopenharmony_ci	del_timer_sync(&msk->key_expiry);
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void mctp_sk_destruct(struct sock *sk)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	skb_queue_purge(&sk->sk_receive_queue);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct proto mctp_proto = {
59562306a36Sopenharmony_ci	.name		= "MCTP",
59662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
59762306a36Sopenharmony_ci	.obj_size	= sizeof(struct mctp_sock),
59862306a36Sopenharmony_ci	.init		= mctp_sk_init,
59962306a36Sopenharmony_ci	.close		= mctp_sk_close,
60062306a36Sopenharmony_ci	.hash		= mctp_sk_hash,
60162306a36Sopenharmony_ci	.unhash		= mctp_sk_unhash,
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int mctp_pf_create(struct net *net, struct socket *sock,
60562306a36Sopenharmony_ci			  int protocol, int kern)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	const struct proto_ops *ops;
60862306a36Sopenharmony_ci	struct proto *proto;
60962306a36Sopenharmony_ci	struct sock *sk;
61062306a36Sopenharmony_ci	int rc;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (protocol)
61362306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* only datagram sockets are supported */
61662306a36Sopenharmony_ci	if (sock->type != SOCK_DGRAM)
61762306a36Sopenharmony_ci		return -ESOCKTNOSUPPORT;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	proto = &mctp_proto;
62062306a36Sopenharmony_ci	ops = &mctp_dgram_ops;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	sock->state = SS_UNCONNECTED;
62362306a36Sopenharmony_ci	sock->ops = ops;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	sk = sk_alloc(net, PF_MCTP, GFP_KERNEL, proto, kern);
62662306a36Sopenharmony_ci	if (!sk)
62762306a36Sopenharmony_ci		return -ENOMEM;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	sock_init_data(sock, sk);
63062306a36Sopenharmony_ci	sk->sk_destruct = mctp_sk_destruct;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	rc = 0;
63362306a36Sopenharmony_ci	if (sk->sk_prot->init)
63462306a36Sopenharmony_ci		rc = sk->sk_prot->init(sk);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (rc)
63762306a36Sopenharmony_ci		goto err_sk_put;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cierr_sk_put:
64262306a36Sopenharmony_ci	sock_orphan(sk);
64362306a36Sopenharmony_ci	sock_put(sk);
64462306a36Sopenharmony_ci	return rc;
64562306a36Sopenharmony_ci}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic struct net_proto_family mctp_pf = {
64862306a36Sopenharmony_ci	.family = PF_MCTP,
64962306a36Sopenharmony_ci	.create = mctp_pf_create,
65062306a36Sopenharmony_ci	.owner = THIS_MODULE,
65162306a36Sopenharmony_ci};
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic __init int mctp_init(void)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	int rc;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* ensure our uapi tag definitions match the header format */
65862306a36Sopenharmony_ci	BUILD_BUG_ON(MCTP_TAG_OWNER != MCTP_HDR_FLAG_TO);
65962306a36Sopenharmony_ci	BUILD_BUG_ON(MCTP_TAG_MASK != MCTP_HDR_TAG_MASK);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	pr_info("mctp: management component transport protocol core\n");
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	rc = sock_register(&mctp_pf);
66462306a36Sopenharmony_ci	if (rc)
66562306a36Sopenharmony_ci		return rc;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	rc = proto_register(&mctp_proto, 0);
66862306a36Sopenharmony_ci	if (rc)
66962306a36Sopenharmony_ci		goto err_unreg_sock;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	rc = mctp_routes_init();
67262306a36Sopenharmony_ci	if (rc)
67362306a36Sopenharmony_ci		goto err_unreg_proto;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	rc = mctp_neigh_init();
67662306a36Sopenharmony_ci	if (rc)
67762306a36Sopenharmony_ci		goto err_unreg_routes;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	mctp_device_init();
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return 0;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cierr_unreg_routes:
68462306a36Sopenharmony_ci	mctp_routes_exit();
68562306a36Sopenharmony_cierr_unreg_proto:
68662306a36Sopenharmony_ci	proto_unregister(&mctp_proto);
68762306a36Sopenharmony_cierr_unreg_sock:
68862306a36Sopenharmony_ci	sock_unregister(PF_MCTP);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return rc;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic __exit void mctp_exit(void)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	mctp_device_exit();
69662306a36Sopenharmony_ci	mctp_neigh_exit();
69762306a36Sopenharmony_ci	mctp_routes_exit();
69862306a36Sopenharmony_ci	proto_unregister(&mctp_proto);
69962306a36Sopenharmony_ci	sock_unregister(PF_MCTP);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cisubsys_initcall(mctp_init);
70362306a36Sopenharmony_cimodule_exit(mctp_exit);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ciMODULE_DESCRIPTION("MCTP core");
70662306a36Sopenharmony_ciMODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_MCTP);
709