GNU Linux-libre 5.10.215-gnu1
[releases.git] / drivers / misc / vmw_vmci / vmci_route.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * VMware VMCI Driver
4  *
5  * Copyright (C) 2012 VMware, Inc. All rights reserved.
6  */
7
8 #include <linux/vmw_vmci_defs.h>
9 #include <linux/vmw_vmci_api.h>
10
11 #include "vmci_context.h"
12 #include "vmci_driver.h"
13 #include "vmci_route.h"
14
15 /*
16  * Make a routing decision for the given source and destination handles.
17  * This will try to determine the route using the handles and the available
18  * devices.  Will set the source context if it is invalid.
19  */
20 int vmci_route(struct vmci_handle *src,
21                const struct vmci_handle *dst,
22                bool from_guest,
23                enum vmci_route *route)
24 {
25         bool has_host_device = vmci_host_code_active();
26         bool has_guest_device = vmci_guest_code_active();
27
28         *route = VMCI_ROUTE_NONE;
29
30         /*
31          * "from_guest" is only ever set to true by
32          * IOCTL_VMCI_DATAGRAM_SEND (or by the vmkernel equivalent),
33          * which comes from the VMX, so we know it is coming from a
34          * guest.
35          *
36          * To avoid inconsistencies, test these once.  We will test
37          * them again when we do the actual send to ensure that we do
38          * not touch a non-existent device.
39          */
40
41         /* Must have a valid destination context. */
42         if (VMCI_INVALID_ID == dst->context)
43                 return VMCI_ERROR_INVALID_ARGS;
44
45         /* Anywhere to hypervisor. */
46         if (VMCI_HYPERVISOR_CONTEXT_ID == dst->context) {
47
48                 /*
49                  * If this message already came from a guest then we
50                  * cannot send it to the hypervisor.  It must come
51                  * from a local client.
52                  */
53                 if (from_guest)
54                         return VMCI_ERROR_DST_UNREACHABLE;
55
56                 /*
57                  * We must be acting as a guest in order to send to
58                  * the hypervisor.
59                  */
60                 if (!has_guest_device)
61                         return VMCI_ERROR_DEVICE_NOT_FOUND;
62
63                 /* And we cannot send if the source is the host context. */
64                 if (VMCI_HOST_CONTEXT_ID == src->context)
65                         return VMCI_ERROR_INVALID_ARGS;
66
67                 /*
68                  * If the client passed the ANON source handle then
69                  * respect it (both context and resource are invalid).
70                  * However, if they passed only an invalid context,
71                  * then they probably mean ANY, in which case we
72                  * should set the real context here before passing it
73                  * down.
74                  */
75                 if (VMCI_INVALID_ID == src->context &&
76                     VMCI_INVALID_ID != src->resource)
77                         src->context = vmci_get_context_id();
78
79                 /* Send from local client down to the hypervisor. */
80                 *route = VMCI_ROUTE_AS_GUEST;
81                 return VMCI_SUCCESS;
82         }
83
84         /* Anywhere to local client on host. */
85         if (VMCI_HOST_CONTEXT_ID == dst->context) {
86                 /*
87                  * If it is not from a guest but we are acting as a
88                  * guest, then we need to send it down to the host.
89                  * Note that if we are also acting as a host then this
90                  * will prevent us from sending from local client to
91                  * local client, but we accept that restriction as a
92                  * way to remove any ambiguity from the host context.
93                  */
94                 if (src->context == VMCI_HYPERVISOR_CONTEXT_ID) {
95                         /*
96                          * If the hypervisor is the source, this is
97                          * host local communication. The hypervisor
98                          * may send vmci event datagrams to the host
99                          * itself, but it will never send datagrams to
100                          * an "outer host" through the guest device.
101                          */
102
103                         if (has_host_device) {
104                                 *route = VMCI_ROUTE_AS_HOST;
105                                 return VMCI_SUCCESS;
106                         } else {
107                                 return VMCI_ERROR_DEVICE_NOT_FOUND;
108                         }
109                 }
110
111                 if (!from_guest && has_guest_device) {
112                         /* If no source context then use the current. */
113                         if (VMCI_INVALID_ID == src->context)
114                                 src->context = vmci_get_context_id();
115
116                         /* Send it from local client down to the host. */
117                         *route = VMCI_ROUTE_AS_GUEST;
118                         return VMCI_SUCCESS;
119                 }
120
121                 /*
122                  * Otherwise we already received it from a guest and
123                  * it is destined for a local client on this host, or
124                  * it is from another local client on this host.  We
125                  * must be acting as a host to service it.
126                  */
127                 if (!has_host_device)
128                         return VMCI_ERROR_DEVICE_NOT_FOUND;
129
130                 if (VMCI_INVALID_ID == src->context) {
131                         /*
132                          * If it came from a guest then it must have a
133                          * valid context.  Otherwise we can use the
134                          * host context.
135                          */
136                         if (from_guest)
137                                 return VMCI_ERROR_INVALID_ARGS;
138
139                         src->context = VMCI_HOST_CONTEXT_ID;
140                 }
141
142                 /* Route to local client. */
143                 *route = VMCI_ROUTE_AS_HOST;
144                 return VMCI_SUCCESS;
145         }
146
147         /*
148          * If we are acting as a host then this might be destined for
149          * a guest.
150          */
151         if (has_host_device) {
152                 /* It will have a context if it is meant for a guest. */
153                 if (vmci_ctx_exists(dst->context)) {
154                         if (VMCI_INVALID_ID == src->context) {
155                                 /*
156                                  * If it came from a guest then it
157                                  * must have a valid context.
158                                  * Otherwise we can use the host
159                                  * context.
160                                  */
161
162                                 if (from_guest)
163                                         return VMCI_ERROR_INVALID_ARGS;
164
165                                 src->context = VMCI_HOST_CONTEXT_ID;
166                         } else if (VMCI_CONTEXT_IS_VM(src->context) &&
167                                    src->context != dst->context) {
168                                 /*
169                                  * VM to VM communication is not
170                                  * allowed. Since we catch all
171                                  * communication destined for the host
172                                  * above, this must be destined for a
173                                  * VM since there is a valid context.
174                                  */
175
176                                 return VMCI_ERROR_DST_UNREACHABLE;
177                         }
178
179                         /* Pass it up to the guest. */
180                         *route = VMCI_ROUTE_AS_HOST;
181                         return VMCI_SUCCESS;
182                 } else if (!has_guest_device) {
183                         /*
184                          * The host is attempting to reach a CID
185                          * without an active context, and we can't
186                          * send it down, since we have no guest
187                          * device.
188                          */
189
190                         return VMCI_ERROR_DST_UNREACHABLE;
191                 }
192         }
193
194         /*
195          * We must be a guest trying to send to another guest, which means
196          * we need to send it down to the host. We do not filter out VM to
197          * VM communication here, since we want to be able to use the guest
198          * driver on older versions that do support VM to VM communication.
199          */
200         if (!has_guest_device) {
201                 /*
202                  * Ending up here means we have neither guest nor host
203                  * device.
204                  */
205                 return VMCI_ERROR_DEVICE_NOT_FOUND;
206         }
207
208         /* If no source context then use the current context. */
209         if (VMCI_INVALID_ID == src->context)
210                 src->context = vmci_get_context_id();
211
212         /*
213          * Send it from local client down to the host, which will
214          * route it to the other guest for us.
215          */
216         *route = VMCI_ROUTE_AS_GUEST;
217         return VMCI_SUCCESS;
218 }