1 /*
2  * hyperv_network_driver.c: network driver functions for Microsoft Hyper-V hosts
3  *
4  * Copyright (C) 2020 Datto Inc
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 
24 #include "datatypes.h"
25 #include "viralloc.h"
26 #include "network_conf.h"
27 #include "hyperv_network_driver.h"
28 #include "hyperv_wmi.h"
29 
30 #define VIR_FROM_THIS VIR_FROM_HYPERV
31 
32 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
33  * Utility functions
34  */
35 
36 static virNetworkPtr
hypervMsvmVirtualSwitchToNetwork(virConnectPtr conn,Msvm_VirtualEthernetSwitch * virtualSwitch)37 hypervMsvmVirtualSwitchToNetwork(virConnectPtr conn, Msvm_VirtualEthernetSwitch *virtualSwitch)
38 {
39     unsigned char uuid[VIR_UUID_BUFLEN];
40 
41     if (virUUIDParse(virtualSwitch->data->Name, uuid) < 0) {
42         virReportError(VIR_ERR_INTERNAL_ERROR,
43                        _("Could not parse UUID from string '%s'"),
44                        virtualSwitch->data->Name);
45         return NULL;
46     }
47 
48     return virGetNetwork(conn, virtualSwitch->data->ElementName, uuid);
49 }
50 
51 
52 static virNetworkPtr
hypervNetworkLookup(virConnectPtr conn,const char * property,const char * value)53 hypervNetworkLookup(virConnectPtr conn, const char *property, const char *value)
54 {
55     hypervPrivate *priv = conn->privateData;
56     g_auto(virBuffer) query = VIR_BUFFER_INITIALIZER;
57     g_autoptr(Msvm_VirtualEthernetSwitch) virtualSwitch = NULL;
58 
59     virBufferAsprintf(&query, MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT "WHERE %s", property);
60     virBufferEscapeSQL(&query, " = '%s'", value);
61 
62     if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &virtualSwitch) < 0)
63         return NULL;
64 
65     if (!virtualSwitch) {
66         virReportError(VIR_ERR_NO_NETWORK,
67                        _("No network found with property '%s' = '%s'"), property, value);
68         return NULL;
69     }
70 
71     return hypervMsvmVirtualSwitchToNetwork(conn, virtualSwitch);
72 }
73 
74 
75 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
76  * Exported API functions
77  */
78 
79 static int
hypervConnectNumOfDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED)80 hypervConnectNumOfDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED)
81 {
82     /* Hyper-V networks are always active */
83     return 0;
84 }
85 
86 
87 static int
hypervConnectListDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED,char ** const names G_GNUC_UNUSED,int maxnames G_GNUC_UNUSED)88 hypervConnectListDefinedNetworks(virConnectPtr conn G_GNUC_UNUSED,
89                                  char **const names G_GNUC_UNUSED,
90                                  int maxnames G_GNUC_UNUSED)
91 {
92     /* Hyper-V networks are always active */
93     return 0;
94 }
95 
96 
97 #define MATCH(FLAG) (flags & (FLAG))
98 static int
hypervConnectListAllNetworks(virConnectPtr conn,virNetworkPtr ** nets,unsigned int flags)99 hypervConnectListAllNetworks(virConnectPtr conn,
100                              virNetworkPtr **nets,
101                              unsigned int flags)
102 {
103     int ret = -1;
104     hypervPrivate *priv = conn->privateData;
105     size_t count = 0;
106     size_t i;
107     g_auto(virBuffer) query = { g_string_new(MSVM_VIRTUALETHERNETSWITCH_WQL_SELECT
108                                              "WHERE HealthState = 5"), 0 };
109     g_autoptr(Msvm_VirtualEthernetSwitch) switches = NULL;
110     Msvm_VirtualEthernetSwitch *entry = NULL;
111 
112     virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1);
113 
114     /*
115      * Hyper-V networks are always active, persistent, and
116      * autostarted, so return zero elements in case we are asked
117      * for networks different than that.
118      */
119     if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) &&
120         !(MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)))
121         return 0;
122     if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT) &&
123         !(MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT)))
124         return 0;
125     if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) &&
126         !(MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART)))
127         return 0;
128 
129     if (hypervGetWmiClass(Msvm_VirtualEthernetSwitch, &switches) < 0)
130         goto cleanup;
131 
132     for (entry = switches; entry; entry = entry->next) {
133         if (nets) {
134             virNetworkPtr net = hypervMsvmVirtualSwitchToNetwork(conn, entry);
135             if (!net)
136                 goto cleanup;
137             VIR_APPEND_ELEMENT(*nets, count, net);
138         } else {
139             ++count;
140         }
141     }
142 
143     ret = count;
144 
145  cleanup:
146     if (ret < 0 && nets && *nets) {
147         for (i = 0; i < count; ++i)
148             VIR_FREE((*nets)[i]);
149         VIR_FREE(*nets);
150     }
151 
152     return ret;
153 }
154 #undef MATCH
155 
156 
157 static int
hypervConnectNumOfNetworks(virConnectPtr conn)158 hypervConnectNumOfNetworks(virConnectPtr conn)
159 {
160     return hypervConnectListAllNetworks(conn, NULL, 0);
161 }
162 
163 
164 static virNetworkPtr
hypervNetworkLookupByUUID(virConnectPtr conn,const unsigned char * uuid)165 hypervNetworkLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
166 {
167     char uuid_string[VIR_UUID_STRING_BUFLEN];
168 
169     virUUIDFormat(uuid, uuid_string);
170 
171     return hypervNetworkLookup(conn, "Name", uuid_string);
172 }
173 
174 
175 static virNetworkPtr
hypervNetworkLookupByName(virConnectPtr conn,const char * name)176 hypervNetworkLookupByName(virConnectPtr conn, const char *name)
177 {
178     return hypervNetworkLookup(conn, "ElementName", name);
179 }
180 
181 
182 static char *
hypervNetworkGetXMLDesc(virNetworkPtr network,unsigned int flags)183 hypervNetworkGetXMLDesc(virNetworkPtr network, unsigned int flags)
184 {
185     g_autoptr(virNetwork) hypervNetwork = NULL;
186     g_autoptr(virNetworkDef) def = NULL;
187 
188     def = g_new0(virNetworkDef, 1);
189 
190     hypervNetwork = hypervNetworkLookupByUUID(network->conn, network->uuid);
191     if (!hypervNetwork)
192         return NULL;
193 
194     memcpy(def->uuid, network->uuid, VIR_UUID_BUFLEN);
195     def->uuid_specified = true;
196 
197     def->name = g_strdup(hypervNetwork->name);
198 
199     def->forward.type = VIR_NETWORK_FORWARD_NONE;
200 
201     return virNetworkDefFormat(def, NULL, flags);
202 }
203 
204 
205 static int
hypervNetworkGetAutostart(virNetworkPtr network G_GNUC_UNUSED,int * autostart)206 hypervNetworkGetAutostart(virNetworkPtr network G_GNUC_UNUSED, int *autostart)
207 {
208     /* Hyper-V networks are always active */
209     *autostart = 1;
210     return 0;
211 }
212 
213 
214 static int
hypervNetworkIsActive(virNetworkPtr network G_GNUC_UNUSED)215 hypervNetworkIsActive(virNetworkPtr network G_GNUC_UNUSED)
216 {
217     /* Hyper-V networks are always active */
218     return 1;
219 }
220 
221 
222 static int
hypervNetworkIsPersistent(virNetworkPtr network G_GNUC_UNUSED)223 hypervNetworkIsPersistent(virNetworkPtr network G_GNUC_UNUSED)
224 {
225     /* Hyper-V networks are always persistent */
226     return 1;
227 }
228 
229 
230 virNetworkDriver hypervNetworkDriver = {
231     .connectNumOfNetworks = hypervConnectNumOfNetworks, /* 7.1.0 */
232     .connectNumOfDefinedNetworks = hypervConnectNumOfDefinedNetworks, /* 7.1.0 */
233     .connectListDefinedNetworks = hypervConnectListDefinedNetworks, /* 7.1.0 */
234     .connectListAllNetworks = hypervConnectListAllNetworks, /* 7.1.0 */
235     .networkLookupByUUID = hypervNetworkLookupByUUID, /* 7.1.0 */
236     .networkLookupByName = hypervNetworkLookupByName, /* 7.1.0 */
237     .networkGetXMLDesc = hypervNetworkGetXMLDesc, /* 7.1.0 */
238     .networkGetAutostart = hypervNetworkGetAutostart, /* 7.1.0 */
239     .networkIsActive = hypervNetworkIsActive, /* 7.1.0 */
240     .networkIsPersistent = hypervNetworkIsPersistent, /* 7.1.0 */
241 };
242