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