Release 4.11 net/vmw_vsock/vmci_transport.c
/*
* VMware vSockets Driver
*
* Copyright (C) 2007-2013 VMware, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation version 2 and no later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/cred.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/net.h>
#include <linux/poll.h>
#include <linux/skbuff.h>
#include <linux/smp.h>
#include <linux/socket.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <net/sock.h>
#include <net/af_vsock.h>
#include "vmci_transport_notify.h"
static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg);
static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg);
static void vmci_transport_peer_detach_cb(u32 sub_id,
const struct vmci_event_data *ed,
void *client_data);
static void vmci_transport_recv_pkt_work(struct work_struct *work);
static void vmci_transport_cleanup(struct work_struct *work);
static int vmci_transport_recv_listen(struct sock *sk,
struct vmci_transport_packet *pkt);
static int vmci_transport_recv_connecting_server(
struct sock *sk,
struct sock *pending,
struct vmci_transport_packet *pkt);
static int vmci_transport_recv_connecting_client(
struct sock *sk,
struct vmci_transport_packet *pkt);
static int vmci_transport_recv_connecting_client_negotiate(
struct sock *sk,
struct vmci_transport_packet *pkt);
static int vmci_transport_recv_connecting_client_invalid(
struct sock *sk,
struct vmci_transport_packet *pkt);
static int vmci_transport_recv_connected(struct sock *sk,
struct vmci_transport_packet *pkt);
static bool vmci_transport_old_proto_override(bool *old_pkt_proto);
static u16 vmci_transport_new_proto_supported_versions(void);
static bool vmci_transport_proto_to_notify_struct(struct sock *sk, u16 *proto,
bool old_pkt_proto);
struct vmci_transport_recv_pkt_info {
struct work_struct work;
struct sock *sk;
struct vmci_transport_packet pkt;
};
static LIST_HEAD(vmci_transport_cleanup_list);
static DEFINE_SPINLOCK(vmci_transport_cleanup_lock);
static DECLARE_WORK(vmci_transport_cleanup_work, vmci_transport_cleanup);
static struct vmci_handle vmci_transport_stream_handle = { VMCI_INVALID_ID,
VMCI_INVALID_ID };
static u32 vmci_transport_qp_resumed_sub_id = VMCI_INVALID_ID;
static int PROTOCOL_OVERRIDE = -1;
#define VMCI_TRANSPORT_DEFAULT_QP_SIZE_MIN 128
#define VMCI_TRANSPORT_DEFAULT_QP_SIZE 262144
#define VMCI_TRANSPORT_DEFAULT_QP_SIZE_MAX 262144
/* The default peer timeout indicates how long we will wait for a peer response
* to a control message.
*/
#define VSOCK_DEFAULT_CONNECT_TIMEOUT (2 * HZ)
/* Helper function to convert from a VMCI error code to a VSock error code. */
static s32 vmci_transport_error_to_vsock_error(s32 vmci_error)
{
int err;
switch (vmci_error) {
case VMCI_ERROR_NO_MEM:
err = ENOMEM;
break;
case VMCI_ERROR_DUPLICATE_ENTRY:
case VMCI_ERROR_ALREADY_EXISTS:
err = EADDRINUSE;
break;
case VMCI_ERROR_NO_ACCESS:
err = EPERM;
break;
case VMCI_ERROR_NO_RESOURCES:
err = ENOBUFS;
break;
case VMCI_ERROR_INVALID_RESOURCE:
err = EHOSTUNREACH;
break;
case VMCI_ERROR_INVALID_ARGS:
default:
err = EINVAL;
}
return err > 0 ? -err : err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 79 | 100.00% | 1 | 100.00% |
Total | 79 | 100.00% | 1 | 100.00% |
static u32 vmci_transport_peer_rid(u32 peer_cid)
{
if (VMADDR_CID_HYPERVISOR == peer_cid)
return VMCI_TRANSPORT_HYPERVISOR_PACKET_RID;
return VMCI_TRANSPORT_PACKET_RID;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Reilly Grant | 21 | 100.00% | 1 | 100.00% |
Total | 21 | 100.00% | 1 | 100.00% |
static inline void
vmci_transport_packet_init(struct vmci_transport_packet *pkt,
struct sockaddr_vm *src,
struct sockaddr_vm *dst,
u8 type,
u64 size,
u64 mode,
struct vmci_transport_waiting_info *wait,
u16 proto,
struct vmci_handle handle)
{
/* We register the stream control handler as an any cid handle so we
* must always send from a source address of VMADDR_CID_ANY
*/
pkt->dg.src = vmci_make_handle(VMADDR_CID_ANY,
VMCI_TRANSPORT_PACKET_RID);
pkt->dg.dst = vmci_make_handle(dst->svm_cid,
vmci_transport_peer_rid(dst->svm_cid));
pkt->dg.payload_size = sizeof(*pkt) - sizeof(pkt->dg);
pkt->version = VMCI_TRANSPORT_PACKET_VERSION;
pkt->type = type;
pkt->src_port = src->svm_port;
pkt->dst_port = dst->svm_port;
memset(&pkt->proto, 0, sizeof(pkt->proto));
memset(&pkt->_reserved2, 0, sizeof(pkt->_reserved2));
switch (pkt->type) {
case VMCI_TRANSPORT_PACKET_TYPE_INVALID:
pkt->u.size = 0;
break;
case VMCI_TRANSPORT_PACKET_TYPE_REQUEST:
case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE:
pkt->u.size = size;
break;
case VMCI_TRANSPORT_PACKET_TYPE_OFFER:
case VMCI_TRANSPORT_PACKET_TYPE_ATTACH:
pkt->u.handle = handle;
break;
case VMCI_TRANSPORT_PACKET_TYPE_WROTE:
case VMCI_TRANSPORT_PACKET_TYPE_READ:
case VMCI_TRANSPORT_PACKET_TYPE_RST:
pkt->u.size = 0;
break;
case VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN:
pkt->u.mode = mode;
break;
case VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ:
case VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE:
memcpy(&pkt->u.wait, wait, sizeof(pkt->u.wait));
break;
case VMCI_TRANSPORT_PACKET_TYPE_REQUEST2:
case VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2:
pkt->u.size = size;
pkt->proto = proto;
break;
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 281 | 97.91% | 1 | 50.00% |
Reilly Grant | 6 | 2.09% | 1 | 50.00% |
Total | 287 | 100.00% | 2 | 100.00% |
static inline void
vmci_transport_packet_get_addresses(struct vmci_transport_packet *pkt,
struct sockaddr_vm *local,
struct sockaddr_vm *remote)
{
vsock_addr_init(local, pkt->dg.dst.context, pkt->dst_port);
vsock_addr_init(remote, pkt->dg.src.context, pkt->src_port);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 56 | 100.00% | 1 | 100.00% |
Total | 56 | 100.00% | 1 | 100.00% |
static int
__vmci_transport_send_control_pkt(struct vmci_transport_packet *pkt,
struct sockaddr_vm *src,
struct sockaddr_vm *dst,
enum vmci_transport_packet_type type,
u64 size,
u64 mode,
struct vmci_transport_waiting_info *wait,
u16 proto,
struct vmci_handle handle,
bool convert_error)
{
int err;
vmci_transport_packet_init(pkt, src, dst, type, size, mode, wait,
proto, handle);
err = vmci_datagram_send(&pkt->dg);
if (convert_error && (err < 0))
return vmci_transport_error_to_vsock_error(err);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 99 | 100.00% | 1 | 100.00% |
Total | 99 | 100.00% | 1 | 100.00% |
static int
vmci_transport_reply_control_pkt_fast(struct vmci_transport_packet *pkt,
enum vmci_transport_packet_type type,
u64 size,
u64 mode,
struct vmci_transport_waiting_info *wait,
struct vmci_handle handle)
{
struct vmci_transport_packet reply;
struct sockaddr_vm src, dst;
if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST) {
return 0;
} else {
vmci_transport_packet_get_addresses(pkt, &src, &dst);
return __vmci_transport_send_control_pkt(&reply, &src, &dst,
type,
size, mode, wait,
VSOCK_PROTO_INVALID,
handle, true);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 94 | 100.00% | 1 | 100.00% |
Total | 94 | 100.00% | 1 | 100.00% |
static int
vmci_transport_send_control_pkt_bh(struct sockaddr_vm *src,
struct sockaddr_vm *dst,
enum vmci_transport_packet_type type,
u64 size,
u64 mode,
struct vmci_transport_waiting_info *wait,
struct vmci_handle handle)
{
/* Note that it is safe to use a single packet across all CPUs since
* two tasklets of the same type are guaranteed to not ever run
* simultaneously. If that ever changes, or VMCI stops using tasklets,
* we can use per-cpu packets.
*/
static struct vmci_transport_packet pkt;
return __vmci_transport_send_control_pkt(&pkt, src, dst, type,
size, mode, wait,
VSOCK_PROTO_INVALID, handle,
false);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 66 | 100.00% | 1 | 100.00% |
Total | 66 | 100.00% | 1 | 100.00% |
static int
vmci_transport_send_control_pkt(struct sock *sk,
enum vmci_transport_packet_type type,
u64 size,
u64 mode,
struct vmci_transport_waiting_info *wait,
u16 proto,
struct vmci_handle handle)
{
struct vmci_transport_packet *pkt;
struct vsock_sock *vsk;
int err;
vsk = vsock_sk(sk);
if (!vsock_addr_bound(&vsk->local_addr))
return -EINVAL;
if (!vsock_addr_bound(&vsk->remote_addr))
return -EINVAL;
pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
if (!pkt)
return -ENOMEM;
err = __vmci_transport_send_control_pkt(pkt, &vsk->local_addr,
&vsk->remote_addr, type, size,
mode, wait, proto, handle,
true);
kfree(pkt);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 144 | 100.00% | 1 | 100.00% |
Total | 144 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_reset_bh(struct sockaddr_vm *dst,
struct sockaddr_vm *src,
struct vmci_transport_packet *pkt)
{
if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST)
return 0;
return vmci_transport_send_control_pkt_bh(
dst, src,
VMCI_TRANSPORT_PACKET_TYPE_RST, 0,
0, NULL, VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 50 | 100.00% | 1 | 100.00% |
Total | 50 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_reset(struct sock *sk,
struct vmci_transport_packet *pkt)
{
if (pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST)
return 0;
return vmci_transport_send_control_pkt(sk,
VMCI_TRANSPORT_PACKET_TYPE_RST,
0, 0, NULL, VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_negotiate(struct sock *sk, size_t size)
{
return vmci_transport_send_control_pkt(
sk,
VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE,
size, 0, NULL,
VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 32 | 100.00% | 1 | 100.00% |
Total | 32 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_negotiate2(struct sock *sk, size_t size,
u16 version)
{
return vmci_transport_send_control_pkt(
sk,
VMCI_TRANSPORT_PACKET_TYPE_NEGOTIATE2,
size, 0, NULL, version,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 35 | 100.00% | 1 | 100.00% |
Total | 35 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_qp_offer(struct sock *sk,
struct vmci_handle handle)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_OFFER, 0,
0, NULL,
VSOCK_PROTO_INVALID, handle);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_attach(struct sock *sk,
struct vmci_handle handle)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_ATTACH,
0, 0, NULL, VSOCK_PROTO_INVALID,
handle);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
static int vmci_transport_reply_reset(struct vmci_transport_packet *pkt)
{
return vmci_transport_reply_control_pkt_fast(
pkt,
VMCI_TRANSPORT_PACKET_TYPE_RST,
0, 0, NULL,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 27 | 100.00% | 1 | 100.00% |
Total | 27 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_invalid_bh(struct sockaddr_vm *dst,
struct sockaddr_vm *src)
{
return vmci_transport_send_control_pkt_bh(
dst, src,
VMCI_TRANSPORT_PACKET_TYPE_INVALID,
0, 0, NULL, VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 34 | 100.00% | 1 | 100.00% |
Total | 34 | 100.00% | 1 | 100.00% |
int vmci_transport_send_wrote_bh(struct sockaddr_vm *dst,
struct sockaddr_vm *src)
{
return vmci_transport_send_control_pkt_bh(
dst, src,
VMCI_TRANSPORT_PACKET_TYPE_WROTE, 0,
0, NULL, VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
int vmci_transport_send_read_bh(struct sockaddr_vm *dst,
struct sockaddr_vm *src)
{
return vmci_transport_send_control_pkt_bh(
dst, src,
VMCI_TRANSPORT_PACKET_TYPE_READ, 0,
0, NULL, VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
int vmci_transport_send_wrote(struct sock *sk)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_WROTE, 0,
0, NULL, VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
int vmci_transport_send_read(struct sock *sk)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_READ, 0,
0, NULL, VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 28 | 100.00% | 1 | 100.00% |
Total | 28 | 100.00% | 1 | 100.00% |
int vmci_transport_send_waiting_write(struct sock *sk,
struct vmci_transport_waiting_info *wait)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_WAITING_WRITE,
0, 0, wait, VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
int vmci_transport_send_waiting_read(struct sock *sk,
struct vmci_transport_waiting_info *wait)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_WAITING_READ,
0, 0, wait, VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 33 | 100.00% | 1 | 100.00% |
Total | 33 | 100.00% | 1 | 100.00% |
static int vmci_transport_shutdown(struct vsock_sock *vsk, int mode)
{
return vmci_transport_send_control_pkt(
&vsk->sk,
VMCI_TRANSPORT_PACKET_TYPE_SHUTDOWN,
0, mode, NULL,
VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 35 | 100.00% | 1 | 100.00% |
Total | 35 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_conn_request(struct sock *sk, size_t size)
{
return vmci_transport_send_control_pkt(sk,
VMCI_TRANSPORT_PACKET_TYPE_REQUEST,
size, 0, NULL,
VSOCK_PROTO_INVALID,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 32 | 100.00% | 1 | 100.00% |
Total | 32 | 100.00% | 1 | 100.00% |
static int vmci_transport_send_conn_request2(struct sock *sk, size_t size,
u16 version)
{
return vmci_transport_send_control_pkt(
sk, VMCI_TRANSPORT_PACKET_TYPE_REQUEST2,
size, 0, NULL, version,
VMCI_INVALID_HANDLE);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 35 | 100.00% | 1 | 100.00% |
Total | 35 | 100.00% | 1 | 100.00% |
static struct sock *vmci_transport_get_pending(
struct sock *listener,
struct vmci_transport_packet *pkt)
{
struct vsock_sock *vlistener;
struct vsock_sock *vpending;
struct sock *pending;
struct sockaddr_vm src;
vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
vlistener = vsock_sk(listener);
list_for_each_entry(vpending, &vlistener->pending_links,
pending_links) {
if (vsock_addr_equals_addr(&src, &vpending->remote_addr) &&
pkt->dst_port == vpending->local_addr.svm_port) {
pending = sk_vsock(vpending);
sock_hold(pending);
goto found;
}
}
pending = NULL;
found:
return pending;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 103 | 85.12% | 1 | 50.00% |
Reilly Grant | 18 | 14.88% | 1 | 50.00% |
Total | 121 | 100.00% | 2 | 100.00% |
static void vmci_transport_release_pending(struct sock *pending)
{
sock_put(pending);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 16 | 100.00% | 1 | 100.00% |
Total | 16 | 100.00% | 1 | 100.00% |
/* We allow two kinds of sockets to communicate with a restricted VM: 1)
* trusted sockets 2) sockets from applications running as the same user as the
* VM (this is only true for the host side and only when using hosted products)
*/
static bool vmci_transport_is_trusted(struct vsock_sock *vsock, u32 peer_cid)
{
return vsock->trusted ||
vmci_is_context_owner(peer_cid, vsock->owner->uid);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
/* We allow sending datagrams to and receiving datagrams from a restricted VM
* only if it is trusted as described in vmci_transport_is_trusted.
*/
static bool vmci_transport_allow_dgram(struct vsock_sock *vsock, u32 peer_cid)
{
if (VMADDR_CID_HYPERVISOR == peer_cid)
return true;
if (vsock->cached_peer != peer_cid) {
vsock->cached_peer = peer_cid;
if (!vmci_transport_is_trusted(vsock, peer_cid) &&
(vmci_context_get_priv_flags(peer_cid) &
VMCI_PRIVILEGE_FLAG_RESTRICTED)) {
vsock->cached_peer_allow_dgram = false;
} else {
vsock->cached_peer_allow_dgram = true;
}
}
return vsock->cached_peer_allow_dgram;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 71 | 88.75% | 1 | 50.00% |
Reilly Grant | 9 | 11.25% | 1 | 50.00% |
Total | 80 | 100.00% | 2 | 100.00% |
static int
vmci_transport_queue_pair_alloc(struct vmci_qp **qpair,
struct vmci_handle *handle,
u64 produce_size,
u64 consume_size,
u32 peer, u32 flags, bool trusted)
{
int err = 0;
if (trusted) {
/* Try to allocate our queue pair as trusted. This will only
* work if vsock is running in the host.
*/
err = vmci_qpair_alloc(qpair, handle, produce_size,
consume_size,
peer, flags,
VMCI_PRIVILEGE_FLAG_TRUSTED);
if (err != VMCI_ERROR_NO_ACCESS)
goto out;
}
err = vmci_qpair_alloc(qpair, handle, produce_size, consume_size,
peer, flags, VMCI_NO_PRIVILEGE_FLAGS);
out:
if (err < 0) {
pr_err("Could not attach to queue pair with %d\n",
err);
err = vmci_transport_error_to_vsock_error(err);
}
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 118 | 100.00% | 1 | 100.00% |
Total | 118 | 100.00% | 1 | 100.00% |
static int
vmci_transport_datagram_create_hnd(u32 resource_id,
u32 flags,
vmci_datagram_recv_cb recv_cb,
void *client_data,
struct vmci_handle *out_handle)
{
int err = 0;
/* Try to allocate our datagram handler as trusted. This will only work
* if vsock is running in the host.
*/
err = vmci_datagram_create_handle_priv(resource_id, flags,
VMCI_PRIVILEGE_FLAG_TRUSTED,
recv_cb,
client_data, out_handle);
if (err == VMCI_ERROR_NO_ACCESS)
err = vmci_datagram_create_handle(resource_id, flags,
recv_cb, client_data,
out_handle);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 71 | 100.00% | 1 | 100.00% |
Total | 71 | 100.00% | 1 | 100.00% |
/* This is invoked as part of a tasklet that's scheduled when the VMCI
* interrupt fires. This is run in bottom-half context and if it ever needs to
* sleep it should defer that work to a work queue.
*/
static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg)
{
struct sock *sk;
size_t size;
struct sk_buff *skb;
struct vsock_sock *vsk;
sk = (struct sock *)data;
/* This handler is privileged when this module is running on the host.
* We will get datagrams from all endpoints (even VMs that are in a
* restricted context). If we get one from a restricted context then
* the destination socket must be trusted.
*
* NOTE: We access the socket struct without holding the lock here.
* This is ok because the field we are interested is never modified
* outside of the create and destruct socket functions.
*/
vsk = vsock_sk(sk);
if (!vmci_transport_allow_dgram(vsk, dg->src.context))
return VMCI_ERROR_NO_ACCESS;
size = VMCI_DG_SIZE(dg);
/* Attach the packet to the socket's receive queue as an sk_buff. */
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb)
return VMCI_ERROR_NO_MEM;
/* sk_receive_skb() will do a sock_put(), so hold here. */
sock_hold(sk);
skb_put(skb, size);
memcpy(skb->data, dg, size);
sk_receive_skb(sk, skb, 0);
return VMCI_SUCCESS;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 124 | 96.88% | 1 | 50.00% |
Asias He | 4 | 3.12% | 1 | 50.00% |
Total | 128 | 100.00% | 2 | 100.00% |
static bool vmci_transport_stream_allow(u32 cid, u32 port)
{
static const u32 non_socket_contexts[] = {
VMADDR_CID_RESERVED,
};
int i;
BUILD_BUG_ON(sizeof(cid) != sizeof(*non_socket_contexts));
for (i = 0; i < ARRAY_SIZE(non_socket_contexts); i++) {
if (cid == non_socket_contexts[i])
return false;
}
return true;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 72 | 100.00% | 1 | 100.00% |
Total | 72 | 100.00% | 1 | 100.00% |
/* This is invoked as part of a tasklet that's scheduled when the VMCI
* interrupt fires. This is run in bottom-half context but it defers most of
* its work to the packet handling work queue.
*/
static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
{
struct sock *sk;
struct sockaddr_vm dst;
struct sockaddr_vm src;
struct vmci_transport_packet *pkt;
struct vsock_sock *vsk;
bool bh_process_pkt;
int err;
sk = NULL;
err = VMCI_SUCCESS;
bh_process_pkt = false;
/* Ignore incoming packets from contexts without sockets, or resources
* that aren't vsock implementations.
*/
if (!vmci_transport_stream_allow(dg->src.context, -1)
|| vmci_transport_peer_rid(dg->src.context) != dg->src.resource)
return VMCI_ERROR_NO_ACCESS;
if (VMCI_DG_SIZE(dg) < sizeof(*pkt))
/* Drop datagrams that do not contain full VSock packets. */
return VMCI_ERROR_INVALID_ARGS;
pkt = (struct vmci_transport_packet *)dg;
/* Find the socket that should handle this packet. First we look for a
* connected socket and if there is none we look for a socket bound to
* the destintation address.
*/
vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
vsock_addr_init(&dst, pkt->dg.dst.context, pkt->dst_port);
sk = vsock_find_connected_socket(&src, &dst);
if (!sk) {
sk = vsock_find_bound_socket(&dst);
if (!sk) {
/* We could not find a socket for this specified
* address. If this packet is a RST, we just drop it.
* If it is another packet, we send a RST. Note that
* we do not send a RST reply to RSTs so that we do not
* continually send RSTs between two endpoints.
*
* Note that since this is a reply, dst is src and src
* is dst.
*/
if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
pr_err("unable to send reset\n");
err = VMCI_ERROR_NOT_FOUND;
goto out;
}
}
/* If the received packet type is beyond all types known to this
* implementation, reply with an invalid message. Hopefully this will
* help when implementing backwards compatibility in the future.
*/
if (pkt->type >= VMCI_TRANSPORT_PACKET_TYPE_MAX) {
vmci_transport_send_invalid_bh(&dst, &src);
err = VMCI_ERROR_INVALID_ARGS;
goto out;
}
/* This handler is privileged when this module is running on the host.
* We will get datagram connect requests from all endpoints (even VMs
* that are in a restricted context). If we get one from a restricted
* context then the destination socket must be trusted.
*
* NOTE: We access the socket struct without holding the lock here.
* This is ok because the field we are interested is never modified
* outside of the create and destruct socket functions.
*/
vsk = vsock_sk(sk);
if (!vmci_transport_allow_dgram(vsk, pkt->dg.src.context)) {
err = VMCI_ERROR_NO_ACCESS;
goto out;
}
/* We do most everything in a work queue, but let's fast path the
* notification of reads and writes to help data transfer performance.
* We can only do this if there is no process context code executing
* for this socket since that may change the state.
*/
bh_lock_sock(sk);
if (!sock_owned_by_user(sk)) {
/* The local context ID may be out of date, update it. */
vsk->local_addr.svm_cid = dst.svm_cid;
if (sk->sk_state == SS_CONNECTED)
vmci_trans(vsk)->notify_ops->handle_notify_pkt(
sk, pkt, true, &dst, &src,
&bh_process_pkt);
}
bh_unlock_sock(sk);
if (!bh_process_pkt) {
struct vmci_transport_recv_pkt_info *recv_pkt_info;
recv_pkt_info = kmalloc(sizeof(*recv_pkt_info), GFP_ATOMIC);
if (!recv_pkt_info) {
if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
pr_err("unable to send reset\n");
err = VMCI_ERROR_NO_MEM;
goto out;
}
recv_pkt_info->sk = sk;
memcpy(&recv_pkt_info->pkt, pkt, sizeof(recv_pkt_info->pkt));
INIT_WORK(&recv_pkt_info->work, vmci_transport_recv_pkt_work);
schedule_work(&recv_pkt_info->work);
/* Clear sk so that the reference count incremented by one of
* the Find functions above is not decremented below. We need
* that reference count for the packet handler we've scheduled
* to run.
*/
sk = NULL;
}
out:
if (sk)
sock_put(sk);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 434 | 94.76% | 1 | 33.33% |
Reilly Grant | 24 | 5.24% | 2 | 66.67% |
Total | 458 | 100.00% | 3 | 100.00% |
static void vmci_transport_handle_detach(struct sock *sk)
{
struct vsock_sock *vsk;
vsk = vsock_sk(sk);
if (!vmci_handle_is_invalid(vmci_trans(vsk)->qp_handle)) {
sock_set_flag(sk, SOCK_DONE);
/* On a detach the peer will not be sending or receiving
* anymore.
*/
vsk->peer_shutdown = SHUTDOWN_MASK;
/* We should not be sending anymore since the peer won't be
* there to receive, but we can still receive if there is data
* left in our consume queue.
*/
if (vsock_stream_has_data(vsk) <= 0) {
if (sk->sk_state == SS_CONNECTING) {
/* The peer may detach from a queue pair while
* we are still in the connecting state, i.e.,
* if the peer VM is killed after attaching to
* a queue pair, but before we complete the
* handshake. In that case, we treat the detach
* event like a reset.
*/
sk->sk_state = SS_UNCONNECTED;
sk->sk_err = ECONNRESET;
sk->sk_error_report(sk);
return;
}
sk->sk_state = SS_UNCONNECTED;
}
sk->sk_state_change(sk);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 108 | 100.00% | 1 | 100.00% |
Total | 108 | 100.00% | 1 | 100.00% |
static void vmci_transport_peer_detach_cb(u32 sub_id,
const struct vmci_event_data *e_data,
void *client_data)
{
struct vmci_transport *trans = client_data;
const struct vmci_event_payload_qp *e_payload;
e_payload = vmci_event_data_const_payload(e_data);
/* XXX This is lame, we should provide a way to lookup sockets by
* qp_handle.
*/
if (vmci_handle_is_invalid(e_payload->handle) ||
!vmci_handle_is_equal(trans->qp_handle, e_payload->handle))
return;
/* We don't ask for delayed CBs when we subscribe to this event (we
* pass 0 as flags to vmci_event_subscribe()). VMCI makes no
* guarantees in that case about what context we might be running in,
* so it could be BH or process, blockable or non-blockable. So we
* need to account for all possible contexts here.
*/
spin_lock_bh(&trans->lock);
if (!trans->sk)
goto out;
/* Apart from here, trans->lock is only grabbed as part of sk destruct,
* where trans->sk isn't locked.
*/
bh_lock_sock(trans->sk);
vmci_transport_handle_detach(trans->sk);
bh_unlock_sock(trans->sk);
out:
spin_unlock_bh(&trans->lock);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 66 | 58.41% | 1 | 33.33% |
Jorgen Hansen | 47 | 41.59% | 2 | 66.67% |
Total | 113 | 100.00% | 3 | 100.00% |
static void vmci_transport_qp_resumed_cb(u32 sub_id,
const struct vmci_event_data *e_data,
void *client_data)
{
vsock_for_each_connected_socket(vmci_transport_handle_detach);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andy King | 24 | 100.00% | 1 | 100.00% |
Total | 24 | 100.00% | 1 | 100.00% |
static void vmci_transport_recv_pkt_work(struct work_struct *work)
{
struct vmci_transport_recv_pkt_info *recv_pkt_info;
struct vmci_transport_packet *pkt;
struct sock *sk;
recv_pkt_info =
container_of(work, struct vmci_transport_recv_pkt_info, work);
sk = recv_pkt_info->sk;
pkt = &recv_pkt_info->pkt;
lock_sock(sk);
/* The local context ID may be out of date. */
vsock_sk(sk)->local_addr.svm_cid =