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