]> git.proxmox.com Git - ovs.git/blame - lib/wmi.c
ovs-router: introduce pkt-mark.
[ovs.git] / lib / wmi.c
CommitLineData
da467899
AS
1/*
2 * Copyright (c) 2016 Cloudbase Solutions Srl
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
18#include "wmi.h"
19#include <stdlib.h>
20#include <stdio.h>
21#include <tchar.h>
22#include "openvswitch/vlog.h"
23
24VLOG_DEFINE_THIS_MODULE(wmi);
25
26/* WMI Job values. */
27enum job_status
28{
29 job_starting = 3,
30 job_running = 4,
31 job_completed = 7,
32 job_wait = 4096
33};
34
35static char *
36sanitize_port_name(char *name)
37{
38 char *p1, *p2;
39 p1 = p2 = name;
40
41 while (*p1) {
42 if ((*p1) == '\'' || (*p1) == '\"') {
43 p1++;
44 } else {
45 *p2 = *p1;
46 p2++;
47 p1++;
48 }
49 }
50 *p2 = '\0';
51 return name;
52}
53
54/* This function will output the appropriate message for a given HRESULT.*/
55static void
56get_hres_error(HRESULT hres)
57{
58 char *error_msg = NULL;
59
60 if (FACILITY_WINDOWS == HRESULT_FACILITY(hres)) {
61 hres = HRESULT_CODE(hres);
62 }
63
64 VLOG_ERR("%s", ovs_format_message(hres));
65}
66
67static boolean
68check_return_value(HRESULT hres)
69{
70 if (FAILED(hres)) {
71 get_hres_error(hres);
72 return false;
73 }
74
75 return true;
76}
77
78static HRESULT
79get_variant_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
80 VARIANT *value)
81{
82 HRESULT hres;
83
84 VariantInit(value);
85
86 hres = pcls_obj->lpVtbl->Get(pcls_obj, field_name, 0, value, 0, 0);
87
88 if (FAILED(hres)) {
89 VariantClear(value);
90 }
91
92 return hres;
93}
94
95/* This function retrieves the uint16_t value from a given class object with
96 * the field name field_name. */
97static HRESULT
98get_uint16_t_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
99 uint16_t *value)
100{
101 VARIANT vt_prop;
102 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
103 *value = V_UI2(&vt_prop);
104
105 return hres;
106}
107
108/* This function retrieves the unsigned int values from a given class object
109 * with the field name field_name. */
110static HRESULT
111get_uint_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
112 unsigned int *value)
113{
114 VARIANT vt_prop;
115 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
116 *value = V_UI4(&vt_prop);
117
118 return hres;
119}
120
121/* This function retrieves the unsigned short value from a given class object
122 * with the field name field_name. */
123static HRESULT
124get_ushort_value(IWbemClassObject *pcls_obj, wchar_t *field_name,
125 unsigned short *value)
126{
127 VARIANT vt_prop;
128 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
129 *value = V_UI2(&vt_prop);
130
131 return hres;
132}
133
134/* This function retrieves the BSTR value from a given class object with
135 * the field name field_name, to a preallocated destination dest and with the
136 * maximum length max_dest_lgth. */
137static HRESULT
138get_str_value(IWbemClassObject *pcls_obj, wchar_t *field_name, wchar_t *dest,
139 int max_dest_lgth)
140{
141 VARIANT vt_prop;
142 HRESULT hres = get_variant_value(pcls_obj, field_name, &vt_prop);
143
144 if (wcscpy_s(dest, max_dest_lgth, vt_prop.bstrVal)) {
145 VariantClear(&vt_prop);
146 VLOG_WARN("get_str_value, wcscpy_s failed :%s", ovs_strerror(errno));
147 return WBEM_E_FAILED;
148 }
149
150 VariantClear(&vt_prop);
151 return S_OK;
152}
153
154/* This function waits for a WMI job to finish and retrieves the error code
155 * if the job failed */
156static HRESULT
157wait_for_job(IWbemServices *psvc, wchar_t *job_path)
158{
159 IWbemClassObject *pcls_obj = NULL;
160 HRESULT retval = 0;
161 uint16_t job_state = 0;
162 uint16_t error = 0;
163
164 do {
165 if(!check_return_value(psvc->lpVtbl->GetObject(psvc, job_path, 0, NULL,
166 &pcls_obj, NULL))) {
167 retval = WBEM_E_FAILED;
168 break;
169 }
170
171 retval = get_uint16_t_value(pcls_obj, L"JobState", &job_state);
172 if (FAILED(retval)) {
173 break;
174 }
175
176 if (job_state == job_starting || job_state == job_running) {
177 Sleep(200);
178 } else if (job_state == job_completed) {
179 break;
180 } else {
181 /* Error occurred. */
182 retval = get_uint16_t_value(pcls_obj, L"ErrorCode", &error);
183 if (FAILED(retval)) {
184 break;
185 }
186 VLOG_WARN("Job failed with error: %d", error);
187 retval = WBEM_E_FAILED;;
188 break;
189 }
190
191 if (pcls_obj != NULL) {
192 pcls_obj->lpVtbl->Release(pcls_obj);
193 pcls_obj = NULL;
194 }
195 } while(TRUE);
196
197 if (pcls_obj != NULL) {
198 pcls_obj->lpVtbl->Release(pcls_obj);
199 pcls_obj = NULL;
200 }
201
202 return retval;
203}
204
205/* This function will initialize DCOM retrieving the WMI locator's ploc and
206 * the context associated to it. */
207static boolean
208initialize_wmi(IWbemLocator **ploc, IWbemContext **pcontext)
209{
210 HRESULT hres = 0;
211
212 /* Initialize COM. */
213 hres = CoInitialize(NULL);
214
215 if (FAILED(hres)) {
216 return false;
217 }
218
219 /* Initialize COM security. */
220 hres = CoInitializeSecurity(NULL,
221 -1,
222 NULL,
223 NULL,
224 RPC_C_AUTHN_LEVEL_DEFAULT,
225 RPC_C_IMP_LEVEL_IMPERSONATE,
226 NULL,
227 EOAC_NONE,
228 NULL);
229
230 if (FAILED(hres)) {
231 return false;
232 }
233
234 /* Fill context. */
235 hres = CoCreateInstance(&CLSID_WbemContext,
236 NULL,
237 CLSCTX_INPROC_SERVER,
238 &IID_IWbemContext,
239 (void**)pcontext);
240
241 if (FAILED(hres)) {
242 return false;
243 }
244
245 fill_context(*pcontext);
246
247 /* Initialize locator's (ploc) to WMI. */
248 hres = CoCreateInstance(&CLSID_WbemLocator,
249 NULL,
250 CLSCTX_INPROC_SERVER,
251 &IID_IWbemLocator,
252 (LPVOID *)ploc);
253
254 if (FAILED(hres)) {
255 return false;
256 }
257
258 return true;
259}
260
261/* This function connects the WMI locator's ploc to a given WMI provider
262 * defined in server and also sets the required security levels for a local
263 * connection to it. */
264static boolean
265connect_set_security(IWbemLocator *ploc, IWbemContext *pcontext,
266 wchar_t *server, IWbemServices **psvc)
267{
268 HRESULT hres = 0;
269
270 /* Connect to server. */
271 hres = ploc->lpVtbl->ConnectServer(ploc,
272 server,
273 NULL,
274 NULL,
275 0,
276 0,
277 0,
278 pcontext,
279 psvc);
280
281 if (FAILED(hres)) {
282 return false;
283 }
284
285 /* Set security levels. */
286 hres = CoSetProxyBlanket((IUnknown *) *psvc,
287 RPC_C_AUTHN_WINNT,
288 RPC_C_AUTHZ_NONE,
289 NULL,
290 RPC_C_AUTHN_LEVEL_CALL,
291 RPC_C_IMP_LEVEL_IMPERSONATE,
292 NULL,
293 EOAC_NONE);
294
295 if (FAILED(hres)) {
296 return false;
297 }
298
299 return true;
300}
301
302/* This function retrieves the first class object of a given enumeration
303 * outputted by a query and fails if it could not retrieve the object or there
304 * was no object to retrieve */
305static boolean
306get_first_element(IEnumWbemClassObject *penumerate,
307 IWbemClassObject **pcls_obj)
308{
309 unsigned long retval = 0;
310
311 if (penumerate == NULL) {
312 VLOG_WARN("Enumeration Class Object is NULL. Cannot get the first"
313 "object");
314 return false;
315 }
316
317 HRESULT hres = penumerate->lpVtbl->Next(penumerate, WBEM_INFINITE, 1,
318 pcls_obj, &retval);
319
320
321 if (!check_return_value(hres) || retval == 0) {
322 return false;
323 }
324
325 return true;
326}
327
328/* This function is a wrapper that transforms a char * into a wchar_t * */
329static boolean
330tranform_wide(char *name, wchar_t *wide_name)
331{
332 unsigned long size = strlen(name) + 1;
333 long long ret = 0;
334
335 if (wide_name == NULL) {
336 VLOG_WARN("Provided wide string is NULL");
337 return false;
338 }
339
340 ret = mbstowcs(wide_name, name, size);
341
342 if (ret == -1) {
343 VLOG_WARN("Invalid multibyte character is encountered");
344 return false;
345 } else if (ret == size) {
346 VLOG_WARN("Returned wide string not NULL terminated");
347 return false;
348 }
349
350 return true;
351}
352
c4129270
SV
353#define WMI_QUERY_COUNT 2048
354
da467899
AS
355/* This function will delete a switch internal port with a given name as input
356 * executing "RemoveResourceSettings" as per documentation:
357 * https://msdn.microsoft.com/en-us/library/hh850277%28v=vs.85%29.aspx
358 * allocating the data and populating the needed fields to execute the
359 * method */
360boolean
361delete_wmi_port(char *name)
362{
363 HRESULT hres = 0;
364 boolean retval = true;
365
366 IWbemLocator *ploc = NULL;
367 IWbemServices *psvc = NULL;
368 IWbemContext *pcontext = NULL;
369 IWbemClassObject *pclass_instance = NULL;
370 IWbemClassObject *pinput_params = NULL;
371 IWbemClassObject *pcls_obj = NULL;
372 IWbemClassObject *pout_params = NULL;
373 IEnumWbemClassObject *penumerate = NULL;
374
375 sanitize_port_name(name);
376 VARIANT vt_prop;
377 VARIANT variant_array;
378 wchar_t *wide_name = NULL;
379 VariantInit(&vt_prop);
380 VariantInit(&variant_array);
381
382 LONG count[1];
383 SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
384 if (psa == NULL) {
385 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
386 retval = false;
387 goto error;
388 }
389
390 if (!initialize_wmi(&ploc, &pcontext)) {
391 VLOG_WARN("Could not initialize DCOM");
392 retval = false;
393 goto error;
394 }
395
396 if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
397 &psvc)) {
398 VLOG_WARN("Could not connect and set security for virtualization");
399 retval = false;
400 goto error;
401 }
402
403
404 /* Get the port with the element name equal to the name input. */
c4129270 405 wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * from "
da467899
AS
406 L"Msvm_EthernetPortAllocationSettingData WHERE ElementName = \"" ;
407
408 wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t));
409 if (wide_name == NULL) {
410 VLOG_WARN("Could not allocate memory for wide string");
411 retval = false;
412 goto error;
413 }
414
415 if (!tranform_wide(name, wide_name)) {
416 retval = false;
417 goto error;
418 }
c4129270 419 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
da467899 420
c4129270 421 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
422
423 hres = psvc->lpVtbl->ExecQuery(psvc,
424 L"WQL",
425 internal_port_query,
426 WBEM_FLAG_FORWARD_ONLY |
427 WBEM_FLAG_RETURN_IMMEDIATELY,
428 NULL,
429 &penumerate);
430
431 if (FAILED(hres)) {
432 retval = false;
433 goto error;
434 }
435
436 /* Get the element path on the switch which will be deleted. */
437 if (!get_first_element(penumerate, &pcls_obj)) {
438 retval = false;
439 goto error;
440 }
441 penumerate->lpVtbl->Release(penumerate);
442 penumerate = NULL;
443
444 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
445
446 if (FAILED(hres)) {
447 retval = false;
448 goto error;
449 }
450 pcls_obj->lpVtbl->Release(pcls_obj);
451 pcls_obj = NULL;
452
453 /* Get the class object and the parameters it can have. */
454 hres = psvc->lpVtbl->GetObject(psvc,
455 L"Msvm_VirtualEthernetSwitchManagementService", 0, NULL, &pcls_obj,
456 NULL);
457
458 if (FAILED(hres)) {
459 retval = false;
460 goto error;
461 }
462
463 hres = pcls_obj->lpVtbl->GetMethod(pcls_obj, L"RemoveResourceSettings", 0,
464 &pinput_params, NULL);
465
466 if (FAILED(hres)) {
467 retval = false;
468 goto error;
469 }
470 pcls_obj->lpVtbl->Release(pcls_obj);
471 pcls_obj = NULL;
472
473 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
474 &pclass_instance);
475 if (FAILED(hres)) {
476 retval = false;
477 goto error;
478 }
479
480 count[0] = 0;
481
482 hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
483
484 if (FAILED(hres)) {
485 retval = false;
486 goto error;
487 }
488
489 VariantClear(&vt_prop);
490 VariantInit(&vt_prop);
491 variant_array.vt = VT_ARRAY | VT_BSTR;
492 variant_array.parray = psa;
493
494 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings", 0,
495 &variant_array, 0);
496 if (FAILED(hres)) {
497 retval = false;
498 goto error;
499 }
500
501 /* Get the object of the Msvm_VirtualEthernetSwitchManagementService which
502 * we need to invoke the port deletion. */
503 hres = psvc->lpVtbl->ExecQuery(psvc,
504 L"WQL",
505 L"SELECT * FROM "
506 L"Msvm_VirtualEthernetSwitchManagementService",
507 WBEM_FLAG_FORWARD_ONLY |
508 WBEM_FLAG_RETURN_IMMEDIATELY,
509 NULL,
510 &penumerate);
511
512 if (FAILED(hres)) {
513 retval = false;
514 goto error;
515 }
516
517 if (!get_first_element(penumerate, &pcls_obj)) {
518 retval = false;
519 goto error;
520 }
521 penumerate->lpVtbl->Release(penumerate);
522 penumerate = NULL;
523
524 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
525
526 if (FAILED(hres)) {
527 retval = false;
528 goto error;
529 }
530
531 pcls_obj->lpVtbl->Release(pcls_obj);
532 pcls_obj = NULL;
533
534 /* Invoke the delete port method. */
535 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
536 L"RemoveResourceSettings", 0,
537 pcontext, pclass_instance, &pout_params,
538 NULL);
539 if (FAILED(hres)) {
540 retval = false;
541 goto error;
542 }
543 VariantClear(&vt_prop);
544 VariantInit(&vt_prop);
545
546 hres = pout_params->lpVtbl->Get(pout_params, L"ReturnValue", 0,
547 &vt_prop, NULL, 0);
548
549 if (FAILED(hres)) {
550 retval = false;
551 goto error;
552 }
553
554 unsigned int retvalue = 0;
555 hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
556 if (FAILED(hres)) {
557 retval = false;
558 goto error;
559 }
560
561 if (retvalue != 0 && retvalue != job_wait) {
562 retval = false;
563 goto error;
564 }
565
566 if (retvalue == job_wait) {
567 WCHAR job_path[2048];
568 hres = get_str_value(pout_params, L"Job", job_path,
569 sizeof(job_path) / sizeof(WCHAR));
570 if (FAILED(hres)) {
571 retval = false;
572 goto error;
573 }
574 hres = wait_for_job(psvc, job_path);
575 if (FAILED(hres)) {
576 retval = false;
577 }
578 }
579
580error:
581 VariantClear(&vt_prop);
582
583 if (pcontext != NULL) {
584 pcontext->lpVtbl->Release(pcontext);
585 pcontext = NULL;
586 }
587 if (psa != NULL) {
588 SafeArrayDestroy(psa);
589 psa = NULL;
590 }
591 if (pcls_obj != NULL) {
592 pcls_obj->lpVtbl->Release(pcls_obj);
593 pcls_obj = NULL;
594 }
595 if (wide_name != NULL) {
596 free(wide_name);
597 wide_name = NULL;
598 }
599 if (!retval) {
600 get_hres_error(hres);
601 }
602 if (pinput_params != NULL) {
603 pinput_params->lpVtbl->Release(pinput_params);
604 pinput_params = NULL;
605 }
606 if (pout_params != NULL) {
607 pout_params->lpVtbl->Release(pout_params);
608 pout_params = NULL;
609 }
610 if (psvc != NULL) {
611 psvc->lpVtbl->Release(psvc);
612 psvc = NULL;
613 }
614 if (ploc != NULL) {
615 ploc->lpVtbl->Release(ploc);
616 ploc = NULL;
617 }
618 if (pclass_instance != NULL) {
619 pclass_instance->lpVtbl->Release(pclass_instance);
620 pclass_instance = NULL;
621 }
622 if (penumerate != NULL) {
623 penumerate->lpVtbl->Release(penumerate);
624 penumerate = NULL;
625 }
626
627 CoUninitialize();
628 return retval;
629}
630
c4129270 631
da467899
AS
632/* This function will create an internal port on the switch given a given name
633 * executing the method AddResourceSettings as per documentation:
634 * https://msdn.microsoft.com/en-us/library/hh850019%28v=vs.85%29.aspx.
635 * It will verify if the port is already defined, in which case it will use
636 * the specific port, and if the forwarding extension "Open vSwitch Extension"
637 * is enabled and running only on a single switch.
638 * After the port is created and bound to the switch we will disable the
639 * created net adapter and rename it to match the OVS bridge name .*/
640boolean
641create_wmi_port(char *name) {
642 HRESULT hres = 0;
643 boolean retval = true;
644
645 BSTR text_object_string = NULL;
646
647 IWbemLocator *ploc = NULL;
648 IWbemContext *pcontext = NULL;
649 IWbemServices *psvc = NULL;
650 IEnumWbemClassObject *penumerate = NULL;
651 IWbemClassObject *default_settings_data = NULL;
652 IWbemClassObject *default_system = NULL;
653 IWbemClassObject *pcls_obj = NULL;
654 IWbemClassObject *pclass = NULL;
655 IWbemClassObject *pinput_params = NULL;
656 IWbemClassObject *pclass_instance = NULL;
657 IWbemObjectTextSrc *text_object = NULL;
658 IWbemClassObject *pout_params = NULL;
659
660 wchar_t *wide_name = NULL;
661 VARIANT vt_prop;
662 VARIANT switch_setting_path;
663 VARIANT new_name;
664 SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
665 VARIANT variant_array;
666 LONG count[1];
667
668 VariantInit(&vt_prop);
669 VariantInit(&switch_setting_path);
670 sanitize_port_name(name);
671
672 if (psa == NULL) {
673 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
674 retval = false;
675 goto error;
676 }
677
678 if (!initialize_wmi(&ploc, &pcontext)) {
679 VLOG_WARN("Could not initialize DCOM");
680 retval = false;
681 goto error;
682 }
683
684 if (!connect_set_security(ploc, pcontext, L"Root\\Virtualization\\v2",
685 &psvc)) {
686 VLOG_WARN("Could not connect and set security for virtualization");
687 retval = false;
688 goto error;
689 }
690
691 /* Check if the element already exists on the switch. */
c4129270 692 wchar_t internal_port_query[WMI_QUERY_COUNT] = L"SELECT * FROM "
da467899
AS
693 L"Msvm_InternalEthernetPort WHERE ElementName = \"";
694
695 wide_name = malloc((strlen(name) + 1) * sizeof(wchar_t));
696 if (wide_name == NULL) {
697 VLOG_WARN("Could not allocate memory for wide string");
698 retval = false;
699 goto error;
700 }
701
702 if (!tranform_wide(name, wide_name)) {
703 retval = false;
704 goto error;
705 }
da467899 706
c4129270
SV
707 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
708
709 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
710 hres = psvc->lpVtbl->ExecQuery(psvc,
711 L"WQL",
712 internal_port_query,
713 WBEM_FLAG_FORWARD_ONLY |
714 WBEM_FLAG_RETURN_IMMEDIATELY,
715 NULL,
716 &penumerate);
717
718 if (FAILED(hres)) {
719 retval = false;
720 goto error;
721 }
722
723 if (get_first_element(penumerate, &pcls_obj)) {
724 VLOG_WARN("Port with name: %s already defined on the switch", name);
725 goto error;
726 }
727 penumerate->lpVtbl->Release(penumerate);
728 penumerate = NULL;
729
730 /* Check if the extension is enabled and running. Also check if the
731 * the extension is enabled on more than one switch. */
732 hres = psvc->lpVtbl->ExecQuery(psvc,
733 L"WQL",
734 L"SELECT * "
735 L"FROM Msvm_EthernetSwitchExtension "
736 L"WHERE "
737 L"ElementName=\"Open vSwitch Extension\" "
738 L"AND EnabledState=2 "
739 L"AND HealthState=5",
740 WBEM_FLAG_FORWARD_ONLY |
741 WBEM_FLAG_RETURN_IMMEDIATELY,
742 NULL,
743 &penumerate);
744
745 if (FAILED(hres)) {
746 retval = false;
747 goto error;
748 }
749
750 if (!get_first_element(penumerate, &pcls_obj)) {
751 VLOG_WARN("Open vSwitch Extension is not enabled on any switch");
752 retval = false;
753 goto error;
754 }
c4129270 755 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
756 L"SELECT * FROM Msvm_VirtualEthernetSwitch WHERE Name = \"");
757
758 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"SystemName", 0,
759 &vt_prop, 0, 0);
760 if (FAILED(hres)) {
761 retval = false;
762 goto error;
763 }
764
c4129270 765 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
766 vt_prop.bstrVal);
767
768 VariantClear(&vt_prop);
769 pcls_obj->lpVtbl->Release(pcls_obj);
770 pcls_obj = NULL;
771
772 if (get_first_element(penumerate, &pcls_obj)) {
773 VLOG_WARN("The extension is activated on more than one switch, "
774 "aborting operation. Please activate the extension on a "
775 "single switch");
776 retval = false;
777 goto error;
778 }
779 penumerate->lpVtbl->Release(penumerate);
780 penumerate = NULL;
781 if (pcls_obj != NULL) {
782 pcls_obj->lpVtbl->Release(pcls_obj);
783 pcls_obj = NULL;
784 }
785
786 /* Get the switch object on which the extension is activated. */
c4129270 787 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"\"");
da467899
AS
788 hres = psvc->lpVtbl->ExecQuery(psvc,
789 L"WQL",
790 internal_port_query,
791 WBEM_FLAG_FORWARD_ONLY |
792 WBEM_FLAG_RETURN_IMMEDIATELY,
793 NULL,
794 &penumerate);
795
796 if (FAILED(hres)) {
797 retval = false;
798 goto error;
799 }
800
801 if (!get_first_element(penumerate, &pcls_obj)) {
802 VLOG_WARN("Could not get the switch object on which the extension is"
803 "activated");
804 retval = false;
805 goto error;
806 }
807 penumerate->lpVtbl->Release(penumerate);
808 penumerate = NULL;
809
810 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"ElementName", 0, &vt_prop, 0, 0);
811
812 if (FAILED(hres)) {
813 retval = false;
814 goto error;
815 }
816
c4129270 817 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
818 L"SELECT * FROM Msvm_VirtualEthernetSwitchSettingData WHERE "
819 L"ElementName = \"");
820
c4129270 821 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
822 vt_prop.bstrVal);
823 VariantClear(&vt_prop);
824
825 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"Name", 0, &vt_prop, 0, 0);
826
827 if (FAILED(hres)) {
828 retval = false;
829 goto error;
830 }
831 pcls_obj->lpVtbl->Release(pcls_obj);
832 pcls_obj = NULL;
833
834 /* Should be enough to give the InstanceID, from msdn documentation:
835 * Uniquely identifies an instance of this class. This property is
836 * inherited from CIM_SettingData and is always
837 * set to "Microsoft:GUID\DeviceSpecificData". */
c4129270 838 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899 839 L"\" AND InstanceID = \"Microsoft:");
c4129270 840 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899 841 vt_prop.bstrVal);
c4129270 842 wcscat_s(internal_port_query, WMI_QUERY_COUNT,
da467899
AS
843 L"\"");
844
845 VariantClear(&vt_prop);
846
847 /* Retrieve the Msvm_VirtualEthernetSwitchSettingData pinned to the switch
848 * object on which the extension is activated. */
849 hres = psvc->lpVtbl->ExecQuery(psvc,
850 L"WQL",
851 internal_port_query,
852 WBEM_FLAG_FORWARD_ONLY |
853 WBEM_FLAG_RETURN_IMMEDIATELY,
854 NULL,
855 &penumerate);
856
857 if (FAILED(hres)) {
858 retval = false;
859 goto error;
860 }
861
862 if (!get_first_element(penumerate, &pcls_obj)) {
863 VLOG_WARN("Could not get the first "
864 "Msvm_VirtualEthernetSwitchSettingData object");
865 retval = false;
866 goto error;
867 }
868 penumerate->lpVtbl->Release(penumerate);
869 penumerate = NULL;
870
871 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &switch_setting_path,
872 0, 0);
873
874 if (FAILED(hres)) {
875 retval = false;
876 goto error;
877 }
878 pcls_obj->lpVtbl->Release(pcls_obj);
879 pcls_obj = NULL;
880
881 /* Retrieve a default allocation port. This object will be later filled
882 * with optional data to create an switch internal port. */
883 hres = psvc->lpVtbl->ExecQuery(psvc,
884 L"WQL",
885 L"SELECT * FROM "
886 L"Msvm_EthernetPortAllocationSettingData "
887 L"WHERE InstanceID LIKE '%%%%\\\\Default' "
888 L"AND ResourceSubType = "
889 L"'Microsoft:Hyper-V:Ethernet Connection'",
890 WBEM_FLAG_FORWARD_ONLY |
891 WBEM_FLAG_RETURN_IMMEDIATELY,
892 NULL,
893 &penumerate);
894
895 if (FAILED(hres)) {
896 retval = false;
897 goto error;
898 }
899
900 if (!get_first_element(penumerate, &default_settings_data)) {
901 VLOG_WARN("Could not retrieve default allocation port object");
902 retval = false;
903 goto error;
904 }
905 penumerate->lpVtbl->Release(penumerate);
906 penumerate = NULL;
907
908 /* Retrieve the default computer system on which the port allocation will
909 * be hosted.
910 * Instead of querying using Description, we can query using InstallDate.
911 * From MSDN documentation regarding InstallDate:
912 * The date and time the virtual machine configuration was created for
913 * a virtual machine, or Null, for a management operating system. */
914 hres = psvc->lpVtbl->ExecQuery(psvc,
915 L"WQL",
916 L"SELECT * FROM Msvm_ComputerSystem WHERE "
917 L"InstallDate is NULL",
918 WBEM_FLAG_FORWARD_ONLY |
919 WBEM_FLAG_RETURN_IMMEDIATELY,
920 NULL,
921 &penumerate);
922
923 if (FAILED(hres)) {
924 retval = false;
925 goto error;
926 }
927
928 if (!get_first_element(penumerate, &default_system)) {
929 VLOG_WARN("Could not retrieve default computer system object");
930 retval = false;
931 goto error;
932 }
933
934 hres = default_system->lpVtbl->Get(default_system, L"__PATH",
935 0, &vt_prop, 0, 0);
936 if (FAILED(hres)) {
937 retval = false;
938 goto error;
939 }
940 penumerate->lpVtbl->Release(penumerate);
941 penumerate = NULL;
942
943 count[0] = 0;
944 hres = SafeArrayPutElement(psa, count, vt_prop.bstrVal);
945
946 if (FAILED(hres)) {
947 retval = false;
948 goto error;
949 }
950
951 VariantClear(&vt_prop);
952 variant_array.vt = VT_ARRAY | VT_BSTR;
953 variant_array.parray = psa;
954 hres = default_settings_data->lpVtbl->Put(default_settings_data,
955 L"HostResource", 0,
956 &variant_array, 0);
957 if (FAILED(hres)) {
958 retval = false;
959 goto error;
960 }
961
962 hres = psvc->lpVtbl->GetObject(psvc,
963 L"Msvm_VirtualEthernetSwitchManagementService",
964 0, NULL, &pclass, NULL);
965 if (FAILED(hres)) {
966 retval = false;
967 goto error;
968 }
969
970 hres = pclass->lpVtbl->GetMethod(pclass, L"AddResourceSettings", 0,
971 &pinput_params, NULL);
972 if (FAILED(hres)) {
973 retval = false;
974 goto error;
975 }
976
977 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
978 &pclass_instance);
979 if (FAILED(hres)) {
980 retval = false;
981 goto error;
982 }
983
984 /* Store the switch setting path retrieved above in the affected
985 * configuration field of the class instance. */
986 hres = pclass_instance->lpVtbl->Put(pclass_instance,
987 L"AffectedConfiguration", 0,
988 &switch_setting_path, 0);
989
990 if (FAILED(hres)) {
991 retval = false;
992 goto error;
993 }
994
995 /* Store the port name in the ElementName field of the default allocation
996 * data. */
997 vt_prop.vt = VT_BSTR;
998 vt_prop.bstrVal = SysAllocString(wide_name);
999 hres = default_settings_data->lpVtbl->Put(default_settings_data,
1000 L"ElementName", 0,
1001 &vt_prop, 0);
1002 VariantClear(&vt_prop);
1003 if (FAILED(hres)) {
1004 retval = false;
1005 goto error;
1006 }
1007
1008 /* Retrieve and store the serialized data of the modified default switch
1009 * settings data. */
1010 hres = CoCreateInstance(&CLSID_WbemObjectTextSrc,
1011 NULL,
1012 CLSCTX_INPROC_SERVER,
1013 &IID_IWbemObjectTextSrc,
1014 (void**)&text_object);
1015 if (FAILED(hres)) {
1016 retval = false;
1017 goto error;
1018 }
1019
1020 hres = text_object->lpVtbl->GetText(text_object, 0,
1021 default_settings_data,
1022 WMI_OBJ_TEXT_WMI_DTD_2_0,
1023 pcontext,
1024 &text_object_string);
1025 if (FAILED(hres)) {
1026 retval = false;
1027 goto error;
1028 }
1029 hres = SafeArrayDestroy(psa);
1030 if (FAILED(hres)) {
1031 VLOG_WARN("Could not clear the data of the array");
1032 retval = false;
1033 goto error;
1034 }
1035
1036 psa = SafeArrayCreateVector(VT_BSTR, 0, 1);
1037
1038 if (psa == NULL) {
1039 VLOG_WARN("Could not allocate memory for a SAFEARRAY");
1040 retval = false;
1041 goto error;
1042 }
1043
1044 count[0] = 0;
1045 variant_array.parray = psa;
1046 hres = SafeArrayPutElement(psa, count, text_object_string);
1047 if (FAILED(hres)) {
1048 retval = false;
1049 goto error;
1050 }
1051 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"ResourceSettings",
1052 0, &variant_array, 0);
1053 if (FAILED(hres)) {
1054 retval = false;
1055 goto error;
1056 }
1057
1058 /* Get the object of the switch service. */
1059 hres = psvc->lpVtbl->ExecQuery(psvc,
1060 L"WQL",
1061 L"SELECT * FROM "
1062 L"Msvm_VirtualEthernetSwitchManagementService",
1063 WBEM_FLAG_FORWARD_ONLY |
1064 WBEM_FLAG_RETURN_IMMEDIATELY,
1065 NULL,
1066 &penumerate);
1067 if (FAILED(hres)) {
1068 retval = false;
1069 goto error;
1070 }
1071
1072 if (!get_first_element(penumerate, &pcls_obj)) {
1073 VLOG_WARN("Could not get the object of the switch service");
1074 retval = false;
1075 goto error;
1076 }
1077 penumerate->lpVtbl->Release(penumerate);
1078 penumerate = NULL;
1079
1080 hres = pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
1081 if (FAILED(hres)) {
1082 retval = false;
1083 goto error;
1084 }
1085 pcls_obj->lpVtbl->Release(pcls_obj);
1086 pcls_obj = NULL;
1087
1088 /* Try to add the port to the switch. */
1089 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal,
1090 L"AddResourceSettings", 0,
1091 pcontext, pclass_instance, &pout_params,
1092 NULL);
1093 if (FAILED(hres)) {
1094 retval = false;
1095 goto error;
1096 }
1097
1098 unsigned int retvalue = 0;
1099 hres = get_uint_value(pout_params, L"ReturnValue", &retvalue);
1100 if (FAILED(hres)) {
1101 retval = false;
1102 goto error;
1103 }
1104
1105 if (retvalue != 0 && retvalue != job_wait) {
1106 retval = false;
1107 goto error;
1108 }
1109
1110 if (retvalue == job_wait) {
1111 WCHAR job_path[2048];
1112 hres = get_str_value(pout_params, L"Job", job_path,
1113 sizeof(job_path) / sizeof(WCHAR));
1114 if (FAILED(hres)) {
1115 retval = false;
1116 goto error;
1117 }
1118 hres = wait_for_job(psvc, job_path);
1119 if (FAILED(hres)) {
1120 retval = false;
1121 goto error;
1122 }
1123 }
1124
1125 pclass->lpVtbl->Release(pclass);
1126 pclass = NULL;
1127 pclass_instance->lpVtbl->Release(pclass_instance);
1128 pclass_instance = NULL;
1129 pinput_params->lpVtbl->Release(pinput_params);
1130 pinput_params = NULL;
1131 psvc->lpVtbl->Release(psvc);
1132 psvc = NULL;
1133 VariantClear(&vt_prop);
1134
1135 if (!connect_set_security(ploc, pcontext, L"Root\\StandardCimv2",
1136 &psvc)) {
1137 VLOG_WARN("Could not connect and set security for CIM");
1138 retval = false;
1139 goto error;
1140 }
1141
c4129270 1142 wcscpy_s(internal_port_query, WMI_QUERY_COUNT,
da467899 1143 L"SELECT * FROM MSFT_NetAdapter WHERE Name LIKE '%%");
c4129270
SV
1144 wcscat_s(internal_port_query, WMI_QUERY_COUNT, wide_name);
1145 wcscat_s(internal_port_query, WMI_QUERY_COUNT, L"%%'");
da467899
AS
1146
1147 /* Get the object with the port name equal to name on the CIM. */
1148 hres = psvc->lpVtbl->ExecQuery(psvc,
1149 L"WQL",
1150 internal_port_query,
1151 WBEM_FLAG_FORWARD_ONLY |
1152 WBEM_FLAG_RETURN_IMMEDIATELY,
1153 NULL,
1154 &penumerate);
1155
1156 if (!get_first_element(penumerate, &pcls_obj)) {
1157 VLOG_WARN("Element name: %s not found in CIM", name);
1158 retval = false;
1159 goto error;
1160 }
1161 penumerate->lpVtbl->Release(penumerate);
1162 penumerate = NULL;
1163 pcls_obj->lpVtbl->Get(pcls_obj, L"__PATH", 0, &vt_prop, 0, 0);
1164 pcls_obj->lpVtbl->Release(pcls_obj);
1165 pcls_obj = NULL;
1166
1167 /* Disable the adapter with port name equal with name. */
1168 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Disable", 0,
1169 pcontext, NULL, NULL, NULL);
1170
1171 if (FAILED(hres)) {
1172 retval = false;
1173 goto error;
1174 }
1175
1176 hres = psvc->lpVtbl->GetObject(psvc, L"MSFT_NetAdapter", 0, NULL, &pclass,
1177 NULL);
1178 if (FAILED(hres)) {
1179 retval = false;
1180 goto error;
1181 }
1182
1183 hres = pclass->lpVtbl->GetMethod(pclass, L"Rename", 0, &pinput_params,
1184 NULL);
1185 if (FAILED(hres)) {
1186 retval = false;
1187 goto error;
1188 }
1189
1190 hres = pinput_params->lpVtbl->SpawnInstance(pinput_params, 0,
1191 &pclass_instance);
1192 if (FAILED(hres)) {
1193 retval = false;
1194 goto error;
1195 }
1196
1197 VariantInit(&new_name);
1198 new_name.vt = VT_BSTR;
1199 new_name.bstrVal = wide_name;
1200 hres = pclass_instance->lpVtbl->Put(pclass_instance, L"NewName", 0,
1201 &new_name, 0);
1202 if (FAILED(hres)) {
1203 retval = false;
1204 goto error;
1205 }
1206 hres = psvc->lpVtbl->ExecMethod(psvc, vt_prop.bstrVal, L"Rename", 0,
1207 pcontext, pclass_instance, NULL, NULL);
1208 if (FAILED(hres)) {
1209 retval = false;
1210 }
1211
1212error:
1213 if (text_object_string != NULL) {
1214 SysFreeString(text_object_string);
1215 text_object_string = NULL;
1216 }
1217 if (psa != NULL) {
1218 SafeArrayDestroy(psa);
1219 psa = NULL;
1220 }
1221 if (ploc != NULL) {
1222 ploc->lpVtbl->Release(ploc);
1223 ploc = NULL;
1224 }
1225 if (pcontext != NULL) {
1226 pcontext->lpVtbl->Release(pcontext);
1227 pcontext = NULL;
1228 }
1229 if (psvc != NULL) {
1230 psvc->lpVtbl->Release(psvc);
1231 psvc = NULL;
1232 }
1233 if (penumerate != NULL) {
1234 penumerate->lpVtbl->Release(penumerate);
1235 penumerate = NULL;
1236 }
1237 if (default_settings_data != NULL) {
1238 default_settings_data->lpVtbl->Release(default_settings_data);
1239 default_settings_data = NULL;
1240 }
1241 if (default_system != NULL) {
1242 default_system->lpVtbl->Release(default_system);
1243 default_system = NULL;
1244 }
1245 if (pcls_obj != NULL) {
1246 pcls_obj->lpVtbl->Release(pcls_obj);
1247 pcls_obj = NULL;
1248 }
1249 if (pclass != NULL) {
1250 pclass->lpVtbl->Release(pclass);
1251 pclass = NULL;
1252 }
1253 if (pinput_params != NULL) {
1254 pinput_params->lpVtbl->Release(pinput_params);
1255 pinput_params = NULL;
1256 }
1257 if (pclass_instance != NULL) {
1258 pclass_instance->lpVtbl->Release(pclass_instance);
1259 pclass_instance = NULL;
1260 }
1261 if (text_object != NULL) {
1262 text_object->lpVtbl->Release(text_object);
1263 text_object = NULL;
1264 }
1265 if (pout_params != NULL) {
1266 pout_params->lpVtbl->Release(pout_params);
1267 pout_params = NULL;
1268 }
1269 if (wide_name != NULL) {
1270 free(wide_name);
1271 wide_name = NULL;
1272 }
1273 VariantClear(&vt_prop);
1274 VariantClear(&switch_setting_path);
1275
1276 if (!retval) {
1277 get_hres_error(hres);
1278 }
1279 CoUninitialize();
1280 return retval;
1281}