1 /*
2 * Copyright (C) 2009-2016 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "virnetdevvportprofile.h"
22 #include "virerror.h"
23 #include "viralloc.h"
24 #include "virstring.h"
25
26 #define VIR_FROM_THIS VIR_FROM_NET
27
28 VIR_ENUM_IMPL(virNetDevVPort,
29 VIR_NETDEV_VPORT_PROFILE_LAST,
30 "none",
31 "802.1Qbg",
32 "802.1Qbh",
33 "openvswitch",
34 "midonet",
35 );
36
37 VIR_ENUM_IMPL(virNetDevVPortProfileOp,
38 VIR_NETDEV_VPORT_PROFILE_OP_LAST,
39 "create",
40 "save",
41 "restore",
42 "destroy",
43 "migrate out",
44 "migrate in start",
45 "migrate in finish",
46 "no-op",
47 );
48
49 #if defined(WITH_LIBNL)
50
51 # include <fcntl.h>
52
53 # include <net/if.h>
54 # include <linux/if_tun.h>
55
56 # include "virnetlink.h"
57 # include "virfile.h"
58 # include "virlog.h"
59 # include "virnetdev.h"
60 # include "virsocket.h"
61
62 VIR_LOG_INIT("util.netdevvportprofile");
63
64 # define MICROSEC_PER_SEC (1000 * 1000)
65
66 # define STATUS_POLL_TIMEOUT_USEC (10 * MICROSEC_PER_SEC)
67 # define STATUS_POLL_INTERVL_USEC (MICROSEC_PER_SEC / 8)
68
69 # define LLDPAD_PID_FILE "/var/run/lldpad.pid"
70
71
72 enum virNetDevVPortProfileLinkOp {
73 VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE = 0x1,
74 VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE = 0x2,
75 VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE = 0x3,
76 VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR = 0x4,
77 };
78
79 #endif
80
81 bool
virNetDevVPortProfileEqual(const virNetDevVPortProfile * a,const virNetDevVPortProfile * b)82 virNetDevVPortProfileEqual(const virNetDevVPortProfile *a, const virNetDevVPortProfile *b)
83 {
84 /* NULL resistant */
85 if (!a && !b)
86 return true;
87
88 if (!a || !b)
89 return false;
90
91 if (a->virtPortType != b->virtPortType)
92 return false;
93
94 switch (a->virtPortType) {
95 case VIR_NETDEV_VPORT_PROFILE_NONE:
96 break;
97
98 case VIR_NETDEV_VPORT_PROFILE_8021QBG:
99 if (a->managerID != b->managerID ||
100 a->typeID != b->typeID ||
101 a->typeIDVersion != b->typeIDVersion ||
102 memcmp(a->instanceID, b->instanceID, VIR_UUID_BUFLEN) != 0)
103 return false;
104 break;
105
106 case VIR_NETDEV_VPORT_PROFILE_8021QBH:
107 if (STRNEQ(a->profileID, b->profileID))
108 return false;
109 break;
110
111 case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
112 if (STRNEQ(a->profileID, b->profileID) ||
113 memcmp(a->interfaceID, b->interfaceID, VIR_UUID_BUFLEN) != 0)
114 return false;
115 break;
116
117 default:
118 break;
119 }
120
121 return true;
122 }
123
124
virNetDevVPortProfileCopy(virNetDevVPortProfile ** dst,const virNetDevVPortProfile * src)125 int virNetDevVPortProfileCopy(virNetDevVPortProfile **dst, const virNetDevVPortProfile *src)
126 {
127 if (!src) {
128 *dst = NULL;
129 return 0;
130 }
131
132 *dst = g_new0(virNetDevVPortProfile, 1);
133 memcpy(*dst, src, sizeof(*src));
134 return 0;
135 }
136
137
138 /* virNetDevVPortProfileCheckComplete() checks that all attributes
139 * required for the type of virtport are specified. When
140 * generateMissing is true, any missing attribute that can be
141 * autogenerated, will be (instanceid, interfaceid). If virtport ==
142 * NULL or virtPortType == NONE, then the result is always 0
143 * (success). If a required attribute is missing, an error is logged
144 * and -1 is returned.
145 */
146 int
virNetDevVPortProfileCheckComplete(virNetDevVPortProfile * virtport,bool generateMissing)147 virNetDevVPortProfileCheckComplete(virNetDevVPortProfile *virtport,
148 bool generateMissing)
149 {
150 const char *missing = NULL;
151
152 if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)
153 return 0;
154
155 switch (virtport->virtPortType) {
156 case VIR_NETDEV_VPORT_PROFILE_8021QBG:
157 if (!virtport->managerID_specified) {
158 missing = "managerid";
159 } else if (!virtport->typeID_specified) {
160 missing = "typeid";
161 } else if (!virtport->typeIDVersion_specified) {
162 missing = "typeidversion";
163 } else if (!virtport->instanceID_specified) {
164 if (generateMissing) {
165 if (virUUIDGenerate(virtport->instanceID) < 0) {
166 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
167 _("cannot generate a random uuid for instanceid"));
168 return -1;
169 }
170 virtport->instanceID_specified = true;
171 } else {
172 missing = "instanceid";
173 }
174 }
175 break;
176
177 case VIR_NETDEV_VPORT_PROFILE_8021QBH:
178 if (!virtport->profileID[0])
179 missing = "profileid";
180 break;
181
182 case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
183 /* profileid is optional for openvswitch */
184 if (!virtport->interfaceID_specified) {
185 if (generateMissing) {
186 if (virUUIDGenerate(virtport->interfaceID) < 0) {
187 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
188 _("cannot generate a random uuid for interfaceid"));
189 return -1;
190 }
191 virtport->interfaceID_specified = true;
192 } else {
193 missing = "interfaceid";
194 }
195 }
196 break;
197
198 case VIR_NETDEV_VPORT_PROFILE_MIDONET:
199 if (!virtport->interfaceID_specified)
200 missing = "interfaceid";
201 break;
202 }
203
204 if (missing) {
205 virReportError(VIR_ERR_XML_ERROR,
206 _("missing %s in <virtualport type='%s'>"), missing,
207 virNetDevVPortTypeToString(virtport->virtPortType));
208 return -1;
209 }
210
211 return 0;
212 }
213
214 /* virNetDevVPortProfileCheckNoExtras() checks that there are no
215 * attributes specified in this virtport that are inappropriate for
216 * the type. if virtport == NULL or virtPortType == NONE, then the
217 * result is always 0 (success). If an extra attribute is present,
218 * an error is logged and -1 is returned.
219 */
220 int
virNetDevVPortProfileCheckNoExtras(const virNetDevVPortProfile * virtport)221 virNetDevVPortProfileCheckNoExtras(const virNetDevVPortProfile *virtport)
222 {
223 const char *extra = NULL;
224
225 if (!virtport || virtport->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)
226 return 0;
227
228 switch (virtport->virtPortType) {
229 case VIR_NETDEV_VPORT_PROFILE_8021QBG:
230 if (virtport->profileID[0])
231 extra = "profileid";
232 else if (virtport->interfaceID_specified)
233 extra = "interfaceid";
234 break;
235
236 case VIR_NETDEV_VPORT_PROFILE_8021QBH:
237 if (virtport->managerID_specified)
238 extra = "managerid";
239 else if (virtport->typeID_specified)
240 extra = "typeid";
241 else if (virtport->typeIDVersion_specified)
242 extra = "typeidversion";
243 else if (virtport->instanceID_specified)
244 extra = "instanceid";
245 else if (virtport->interfaceID_specified)
246 extra = "interfaceid";
247 break;
248
249 case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
250 if (virtport->managerID_specified)
251 extra = "managerid";
252 else if (virtport->typeID_specified)
253 extra = "typeid";
254 else if (virtport->typeIDVersion_specified)
255 extra = "typeidversion";
256 else if (virtport->instanceID_specified)
257 extra = "instanceid";
258 break;
259 }
260
261 if (extra) {
262 virReportError(VIR_ERR_XML_ERROR,
263 _("extra %s unsupported in <virtualport type='%s'>"),
264 extra,
265 virNetDevVPortTypeToString(virtport->virtPortType));
266 return -1;
267 }
268
269 return 0;
270 }
271
272 /* virNetDevVPortProfileMerge() - merge the attributes in mods into
273 * orig. If anything that is set in mods has already been set in orig
274 * *and doesn't match*, log an error and return -1, otherwise return 0.
275 */
276 static int
virNetDevVPortProfileMerge(virNetDevVPortProfile * orig,const virNetDevVPortProfile * mods)277 virNetDevVPortProfileMerge(virNetDevVPortProfile *orig,
278 const virNetDevVPortProfile *mods)
279 {
280 enum virNetDevVPortProfile otype;
281
282 if (!orig || !mods)
283 return 0;
284
285 otype = orig->virtPortType;
286
287 if (mods->virtPortType != VIR_NETDEV_VPORT_PROFILE_NONE) {
288 if (otype != VIR_NETDEV_VPORT_PROFILE_NONE &&
289 otype != mods->virtPortType) {
290 virReportError(VIR_ERR_XML_ERROR,
291 _("attempt to merge virtualports "
292 "with mismatched types (%s and %s)"),
293 virNetDevVPortTypeToString(otype),
294 virNetDevVPortTypeToString(mods->virtPortType));
295 return -1;
296 }
297 otype = orig->virtPortType = mods->virtPortType;
298 }
299
300 if (mods->managerID_specified &&
301 (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
302 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
303 if (orig->managerID_specified &&
304 (orig->managerID != mods->managerID)) {
305 virReportError(VIR_ERR_XML_ERROR,
306 _("attempt to merge virtualports "
307 "with mismatched managerids (%d and %d)"),
308 orig->managerID, mods->managerID);
309 return -1;
310 }
311 orig->managerID = mods->managerID;
312 orig->managerID_specified = true;
313 }
314
315 if (mods->typeID_specified &&
316 (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
317 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
318 if (orig->typeID_specified &&
319 (orig->typeID != mods->typeID)) {
320 virReportError(VIR_ERR_XML_ERROR,
321 _("attempt to merge virtualports "
322 "with mismatched typeids (%d and %d)"),
323 orig->typeID, mods->typeID);
324 return -1;
325 }
326 orig->typeID = mods->typeID;
327 orig->typeID_specified = true;
328 }
329
330 if (mods->typeIDVersion_specified &&
331 (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
332 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
333 if (orig->typeIDVersion_specified &&
334 (orig->typeIDVersion != mods->typeIDVersion)) {
335 virReportError(VIR_ERR_XML_ERROR,
336 _("attempt to merge virtualports with "
337 "mismatched typeidversions (%d and %d)"),
338 orig->typeIDVersion, mods->typeIDVersion);
339 return -1;
340 }
341 orig->typeIDVersion = mods->typeIDVersion;
342 orig->typeIDVersion_specified = true;
343 }
344
345 if (mods->instanceID_specified &&
346 (otype == VIR_NETDEV_VPORT_PROFILE_8021QBG ||
347 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
348 if (orig->instanceID_specified &&
349 memcmp(orig->instanceID, mods->instanceID,
350 sizeof(orig->instanceID))) {
351 char origuuid[VIR_UUID_STRING_BUFLEN];
352 char modsuuid[VIR_UUID_STRING_BUFLEN];
353
354 virReportError(VIR_ERR_XML_ERROR,
355 _("attempt to merge virtualports with "
356 "mismatched instanceids ('%s' and '%s')"),
357 virUUIDFormat(orig->instanceID, origuuid),
358 virUUIDFormat(mods->instanceID, modsuuid));
359 return -1;
360 }
361 memcpy(orig->instanceID, mods->instanceID, sizeof(orig->instanceID));
362 orig->instanceID_specified = true;
363 }
364
365 if (mods->interfaceID_specified &&
366 (otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH ||
367 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
368 if (orig->interfaceID_specified &&
369 memcmp(orig->interfaceID, mods->interfaceID,
370 sizeof(orig->interfaceID))) {
371 char origuuid[VIR_UUID_STRING_BUFLEN];
372 char modsuuid[VIR_UUID_STRING_BUFLEN];
373
374 virReportError(VIR_ERR_XML_ERROR,
375 _("attempt to merge virtualports with "
376 "mismatched interfaceids ('%s' and '%s')"),
377 virUUIDFormat(orig->interfaceID, origuuid),
378 virUUIDFormat(mods->interfaceID, modsuuid));
379 return -1;
380 }
381 memcpy(orig->interfaceID, mods->interfaceID, sizeof(orig->interfaceID));
382 orig->interfaceID_specified = true;
383 }
384
385 if (mods->profileID[0] &&
386 (otype == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH ||
387 otype == VIR_NETDEV_VPORT_PROFILE_8021QBH ||
388 otype == VIR_NETDEV_VPORT_PROFILE_NONE)) {
389 if (orig->profileID[0] &&
390 STRNEQ(orig->profileID, mods->profileID)) {
391 virReportError(VIR_ERR_XML_ERROR,
392 _("attempt to merge virtualports with "
393 "mismatched profileids ('%s' and '%s')"),
394 orig->profileID, mods->profileID);
395 return -1;
396 }
397 if (virStrcpyStatic(orig->profileID, mods->profileID) < 0) {
398 /* this should never happen - it indicates mods->profileID
399 * isn't properly null terminated. */
400 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
401 _("corrupted profileid string"));
402 return -1;
403 }
404 }
405 return 0;
406 }
407
408 /* virNetDevVPortProfileMerge3() - create a new virNetDevVPortProfile
409 * that is a combination of the three input profiles. fromInterface is
410 * highest priority and fromPortgroup is lowest. As lower priority
411 * objects' attributes are merged in, if the attribute is unset in the
412 * result object, it is set from the lower priority object, but if it
413 * is already set in the result and the lower priority object wants to
414 * change it, that is an error.
415 */
416
virNetDevVPortProfileMerge3(virNetDevVPortProfile ** result,const virNetDevVPortProfile * fromInterface,const virNetDevVPortProfile * fromNetwork,const virNetDevVPortProfile * fromPortgroup)417 int virNetDevVPortProfileMerge3(virNetDevVPortProfile **result,
418 const virNetDevVPortProfile *fromInterface,
419 const virNetDevVPortProfile *fromNetwork,
420 const virNetDevVPortProfile *fromPortgroup)
421 {
422 int ret = -1;
423 *result = NULL;
424
425 if ((!fromInterface || (fromInterface->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) &&
426 (!fromNetwork || (fromNetwork->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE)) &&
427 (!fromPortgroup || (fromPortgroup->virtPortType == VIR_NETDEV_VPORT_PROFILE_NONE))) {
428 return 0;
429 }
430
431 /* at least one of the source profiles is non-empty */
432 *result = g_new0(virNetDevVPortProfile, 1);
433
434 /* start with the interface's profile. There are no pointers in a
435 * virtualPortProfile, so a shallow copy is sufficient.
436 */
437 if (fromInterface)
438 **result = *fromInterface;
439
440 if (virNetDevVPortProfileMerge(*result, fromNetwork) < 0)
441 goto error;
442 if (virNetDevVPortProfileMerge(*result, fromPortgroup) < 0)
443 goto error;
444
445 ret = 0;
446
447 error:
448 if (ret < 0)
449 VIR_FREE(*result);
450 return ret;
451 }
452
453
454 #if defined(WITH_LIBNL)
455
456 static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
457 {
458 [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
459 };
460
461 static uint32_t
virNetDevVPortProfileGetLldpadPid(void)462 virNetDevVPortProfileGetLldpadPid(void)
463 {
464 int fd;
465 uint32_t pid = 0;
466
467 fd = open(LLDPAD_PID_FILE, O_RDONLY);
468 if (fd >= 0) {
469 char buffer[10];
470
471 if (saferead(fd, buffer, sizeof(buffer)) <= sizeof(buffer)) {
472 unsigned int res;
473 char *endptr;
474
475 if (virStrToLong_ui(buffer, &endptr, 10, &res) == 0
476 && (*endptr == '\0' || g_ascii_isspace(*endptr))
477 && res != 0) {
478 pid = res;
479 } else {
480 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
481 _("error parsing pid of lldpad"));
482 }
483 }
484 } else {
485 virReportSystemError(errno,
486 _("Error opening file %s"), LLDPAD_PID_FILE);
487 }
488
489 VIR_FORCE_CLOSE(fd);
490
491 return pid;
492 }
493
494 /**
495 * virNetDevVPortProfileGetStatus:
496 *
497 * tb: top level netlink response attributes + values
498 * vf: The virtual function used in the request
499 * instanceId: instanceId of the interface (vm uuid in case of 802.1Qbh)
500 * is8021Qbg: whether this function is call for 8021Qbg
501 * status: pointer to a uint16 where the status will be written into
502 *
503 * Get the status from the IFLA_PORT_RESPONSE field; Returns 0 in
504 * case of success, < 0 otherwise with error having been reported
505 */
506 static int
virNetDevVPortProfileGetStatus(struct nlattr ** tb,int32_t vf,const unsigned char * instanceId,bool nltarget_kernel,bool is8021Qbg,uint16_t * status)507 virNetDevVPortProfileGetStatus(struct nlattr **tb, int32_t vf,
508 const unsigned char *instanceId,
509 bool nltarget_kernel,
510 bool is8021Qbg,
511 uint16_t *status)
512 {
513 struct nlattr *tb_port[IFLA_PORT_MAX + 1] = { NULL, };
514
515 if (vf == PORT_SELF_VF && nltarget_kernel) {
516 if (tb[IFLA_PORT_SELF]) {
517 if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
518 ifla_port_policy)) {
519 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
520 _("error parsing IFLA_PORT_SELF part"));
521 return -1;
522 }
523 } else {
524 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
525 _("IFLA_PORT_SELF is missing"));
526 return -1;
527 }
528 } else {
529 if (tb[IFLA_VF_PORTS]) {
530 int rem;
531 bool found = false;
532 struct nlattr *tb_vf_ports = { NULL, };
533
534 nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) {
535
536 if (nla_type(tb_vf_ports) != IFLA_VF_PORT) {
537 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
538 _("error while iterating over "
539 "IFLA_VF_PORTS part"));
540 return -1;
541 }
542
543 if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
544 ifla_port_policy)) {
545 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
546 _("error parsing IFLA_VF_PORT part"));
547 return -1;
548 }
549
550 /* This ensures that the given VF is present in the
551 * IFLA_VF_PORTS list, and that its uuid matches the
552 * instanceId (in case we've associated it). If no
553 * instanceId is sent from the caller, that means we've
554 * disassociated it from this instanceId, and the uuid
555 * will either be unset (if still not associated with
556 * anything) or will be set to a new and different uuid
557 */
558 if ((tb_port[IFLA_PORT_VF] &&
559 vf == *(uint32_t *)RTA_DATA(tb_port[IFLA_PORT_VF])) &&
560 (!instanceId ||
561 (tb_port[IFLA_PORT_INSTANCE_UUID] &&
562 !memcmp(instanceId,
563 (unsigned char *)
564 RTA_DATA(tb_port[IFLA_PORT_INSTANCE_UUID]),
565 VIR_UUID_BUFLEN)))) {
566 found = true;
567 break;
568 }
569 }
570
571 if (!found) {
572 char instanceIdStr[VIR_UUID_STRING_BUFLEN] = "(none)";
573
574 if (instanceId)
575 virUUIDFormat(instanceId, instanceIdStr);
576
577 virReportError(VIR_ERR_INTERNAL_ERROR,
578 _("Could not find vf/instanceId %u/%s "
579 " in netlink response"),
580 vf, instanceIdStr);
581
582 /* go through all the entries again. This seems tedious,
583 * but experience has shown the resulting log to be
584 * very useful.
585 */
586 VIR_WARN("IFLA_VF_PORTS entries that were returned:");
587 nla_for_each_nested(tb_vf_ports, tb[IFLA_VF_PORTS], rem) {
588 char uuidstr[VIR_UUID_STRING_BUFLEN] = "(none)";
589
590 if (nla_parse_nested(tb_port, IFLA_PORT_MAX, tb_vf_ports,
591 ifla_port_policy)) {
592 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
593 _("error parsing IFLA_VF_PORT "
594 "during error reporting"));
595 return -1;
596 }
597 if (tb_port[IFLA_PORT_INSTANCE_UUID]) {
598 virUUIDFormat((unsigned char *)
599 RTA_DATA(tb_port[IFLA_PORT_INSTANCE_UUID]),
600 uuidstr);
601 }
602 VIR_WARN(" vf: %d uuid: %s",
603 tb_port[IFLA_PORT_VF] ?
604 *(uint32_t *)RTA_DATA(tb_port[IFLA_PORT_VF]) : -1,
605 uuidstr);
606 }
607 return -1;
608 }
609 } else {
610 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
611 _("IFLA_VF_PORTS is missing"));
612 return -1;
613 }
614 }
615
616 if (tb_port[IFLA_PORT_RESPONSE]) {
617 *status = *(uint16_t *)RTA_DATA(tb_port[IFLA_PORT_RESPONSE]);
618 } else {
619 if (is8021Qbg) {
620 /* no in-progress here; may be missing */
621 *status = PORT_PROFILE_RESPONSE_INPROGRESS;
622 } else {
623 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
624 _("no IFLA_PORT_RESPONSE found in netlink message"));
625 return -1;
626 }
627 }
628
629 return 0;
630 }
631
632
633 static int
virNetDevVPortProfileOpSetLink(const char * ifname,int ifindex,bool nltarget_kernel,const virMacAddr * macaddr,int vlanid,const char * profileId,struct ifla_port_vsi * portVsi,const unsigned char * instanceId,const unsigned char * hostUUID,int32_t vf,uint8_t op)634 virNetDevVPortProfileOpSetLink(const char *ifname, int ifindex,
635 bool nltarget_kernel,
636 const virMacAddr *macaddr,
637 int vlanid,
638 const char *profileId,
639 struct ifla_port_vsi *portVsi,
640 const unsigned char *instanceId,
641 const unsigned char *hostUUID,
642 int32_t vf,
643 uint8_t op)
644 {
645 int rc = -1;
646 struct nlmsghdr *resp = NULL;
647 struct nlmsgerr *err;
648 struct ifinfomsg ifinfo = {
649 .ifi_family = AF_UNSPEC,
650 .ifi_index = ifindex,
651 };
652 unsigned int recvbuflen = 0;
653 int src_pid = 0;
654 uint32_t dst_pid = 0;
655 struct nl_msg *nl_msg;
656 struct nlattr *vfports = NULL, *vfport;
657 char macStr[VIR_MAC_STRING_BUFLEN];
658 char hostUUIDStr[VIR_UUID_STRING_BUFLEN];
659 char instanceUUIDStr[VIR_UUID_STRING_BUFLEN];
660 const char *opName;
661
662 switch (op) {
663 case PORT_REQUEST_PREASSOCIATE:
664 opName = "PREASSOCIATE";
665 break;
666 case PORT_REQUEST_PREASSOCIATE_RR:
667 opName = "PREASSOCIATE_RR";
668 break;
669 case PORT_REQUEST_ASSOCIATE:
670 opName = "ASSOCIATE";
671 break;
672 case PORT_REQUEST_DISASSOCIATE:
673 opName = "DISASSOCIATE";
674 break;
675 default:
676 opName = "(unknown)";
677 break;
678 }
679 VIR_INFO("%s: ifname: %s ifindex: %d vf: %d vlanid: %d mac: %s "
680 "profileId: %s instanceId: %s hostUUID: %s",
681 opName, ifname ? ifname : "(unspecified)",
682 ifindex, vf, vlanid,
683 macaddr ? virMacAddrFormat(macaddr, macStr) : "(unspecified)",
684 profileId ? profileId : "(unspecified)",
685 (instanceId
686 ? virUUIDFormat(instanceId, instanceUUIDStr)
687 : "(unspecified)"),
688 (hostUUID
689 ? virUUIDFormat(hostUUID, hostUUIDStr)
690 : "(unspecified)"));
691
692 nl_msg = virNetlinkMsgNew(RTM_SETLINK, NLM_F_REQUEST);
693
694 if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
695 goto buffer_too_small;
696
697 if (ifname &&
698 nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
699 goto buffer_too_small;
700
701 if (macaddr || vlanid >= 0) {
702 struct nlattr *vfinfolist, *vfinfo;
703
704 if (!(vfinfolist = nla_nest_start(nl_msg, IFLA_VFINFO_LIST)))
705 goto buffer_too_small;
706
707 if (!(vfinfo = nla_nest_start(nl_msg, IFLA_VF_INFO)))
708 goto buffer_too_small;
709
710 if (macaddr) {
711 struct ifla_vf_mac ifla_vf_mac = {
712 .vf = vf,
713 .mac = { 0, },
714 };
715
716 virMacAddrGetRaw(macaddr, ifla_vf_mac.mac);
717
718 if (nla_put(nl_msg, IFLA_VF_MAC, sizeof(ifla_vf_mac),
719 &ifla_vf_mac) < 0)
720 goto buffer_too_small;
721 }
722
723 if (vlanid >= 0) {
724 struct ifla_vf_vlan ifla_vf_vlan = {
725 .vf = vf,
726 .vlan = vlanid,
727 .qos = 0,
728 };
729
730 if (nla_put(nl_msg, IFLA_VF_VLAN, sizeof(ifla_vf_vlan),
731 &ifla_vf_vlan) < 0)
732 goto buffer_too_small;
733 }
734
735 nla_nest_end(nl_msg, vfinfo);
736 nla_nest_end(nl_msg, vfinfolist);
737 }
738
739 if (vf == PORT_SELF_VF && nltarget_kernel) {
740 if (!(vfport = nla_nest_start(nl_msg, IFLA_PORT_SELF)))
741 goto buffer_too_small;
742 } else {
743 if (!(vfports = nla_nest_start(nl_msg, IFLA_VF_PORTS)))
744 goto buffer_too_small;
745
746 /* begin nesting vfports */
747 if (!(vfport = nla_nest_start(nl_msg, IFLA_VF_PORT)))
748 goto buffer_too_small;
749 }
750
751 if (profileId) {
752 if (nla_put(nl_msg, IFLA_PORT_PROFILE, strlen(profileId) + 1,
753 profileId) < 0)
754 goto buffer_too_small;
755 }
756
757 if (portVsi) {
758 if (nla_put(nl_msg, IFLA_PORT_VSI_TYPE, sizeof(*portVsi),
759 portVsi) < 0)
760 goto buffer_too_small;
761 }
762
763 if (instanceId) {
764 if (nla_put(nl_msg, IFLA_PORT_INSTANCE_UUID, VIR_UUID_BUFLEN,
765 instanceId) < 0)
766 goto buffer_too_small;
767 }
768
769 if (hostUUID) {
770 if (nla_put(nl_msg, IFLA_PORT_HOST_UUID, VIR_UUID_BUFLEN,
771 hostUUID) < 0)
772 goto buffer_too_small;
773 }
774
775 if (vf != PORT_SELF_VF) {
776 if (nla_put(nl_msg, IFLA_PORT_VF, sizeof(vf), &vf) < 0)
777 goto buffer_too_small;
778 }
779
780 if (nla_put(nl_msg, IFLA_PORT_REQUEST, sizeof(op), &op) < 0)
781 goto buffer_too_small;
782
783 /* end nesting of vport */
784 nla_nest_end(nl_msg, vfport);
785
786 if (vfports) {
787 /* end nesting of vfports */
788 nla_nest_end(nl_msg, vfports);
789 }
790
791 if (!nltarget_kernel) {
792 if ((src_pid = virNetlinkEventServiceLocalPid(NETLINK_ROUTE)) < 0)
793 goto cleanup;
794 if ((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0)
795 goto cleanup;
796 }
797
798 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
799 src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
800 goto cleanup;
801
802 if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
803 goto malformed_resp;
804
805 switch (resp->nlmsg_type) {
806 case NLMSG_ERROR:
807 err = (struct nlmsgerr *)NLMSG_DATA(resp);
808 if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
809 goto malformed_resp;
810
811 if (err->error) {
812 virReportSystemError(-err->error,
813 _("error during virtual port configuration of ifindex %d"),
814 ifindex);
815 goto cleanup;
816 }
817 break;
818
819 case NLMSG_DONE:
820 break;
821
822 default:
823 goto malformed_resp;
824 }
825
826 rc = 0;
827 cleanup:
828 nlmsg_free(nl_msg);
829 VIR_FREE(resp);
830 return rc;
831
832 malformed_resp:
833 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
834 _("malformed netlink response message"));
835 goto cleanup;
836
837 buffer_too_small:
838 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
839 _("allocated netlink buffer is too small"));
840 goto cleanup;
841 }
842
843
844 /**
845 * virNetDevVPortProfileGetNthParent
846 *
847 * @ifname : the name of the interface; ignored if ifindex is valid
848 * @ifindex : the index of the interface or -1 if ifname is given
849 * @nthParent : the nth parent interface to get
850 * @parent_ifindex : pointer to int
851 * @parent_ifname : pointer to buffer of size IFNAMSIZ
852 * @nth : the nth parent that is actually returned; if for example eth0.100
853 * was given and the 100th parent is to be returned, then eth0 will
854 * most likely be returned with nth set to 1 since the chain does
855 * not have more interfaces
856 *
857 * Get the nth parent interface of the given interface. 0 is the interface
858 * itself.
859 *
860 * Return 0 on success, < 0 otherwise
861 */
862 static int
virNetDevVPortProfileGetNthParent(const char * ifname,int ifindex,unsigned int nthParent,int * parent_ifindex,char * parent_ifname,unsigned int * nth)863 virNetDevVPortProfileGetNthParent(const char *ifname, int ifindex, unsigned int nthParent,
864 int *parent_ifindex, char *parent_ifname,
865 unsigned int *nth)
866 {
867 int rc = -1;
868 void *nlData = NULL;
869 struct nlattr *tb[IFLA_MAX + 1] = { NULL, };
870 bool end = false;
871 size_t i = 0;
872
873 *nth = 0;
874
875 if (ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
876 return -1;
877
878 while (!end && i <= nthParent) {
879 VIR_FREE(nlData);
880 rc = virNetlinkDumpLink(ifname, ifindex, &nlData, tb, 0, 0);
881 if (rc < 0)
882 break;
883
884 if (tb[IFLA_IFNAME]) {
885 if (virStrcpy(parent_ifname, (char*)RTA_DATA(tb[IFLA_IFNAME]),
886 IFNAMSIZ) < 0) {
887 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
888 _("buffer for root interface name is too small"));
889 rc = -1;
890 goto cleanup;
891 }
892 *parent_ifindex = ifindex;
893 }
894
895 if (tb[IFLA_LINK]) {
896 ifindex = *(int *)RTA_DATA(tb[IFLA_LINK]);
897 ifname = NULL;
898 } else {
899 end = true;
900 }
901
902 i++;
903 }
904
905 *nth = i - 1;
906
907 cleanup:
908 VIR_FREE(nlData);
909 return rc;
910 }
911
912
913 /* Returns 0 on success, -1 on general failure, and -2 on timeout */
914 static int
virNetDevVPortProfileOpCommon(const char * ifname,int ifindex,bool nltarget_kernel,const virMacAddr * macaddr,int vlanid,const char * profileId,struct ifla_port_vsi * portVsi,const unsigned char * instanceId,const unsigned char * hostUUID,int32_t vf,uint8_t op,bool setlink_only)915 virNetDevVPortProfileOpCommon(const char *ifname, int ifindex,
916 bool nltarget_kernel,
917 const virMacAddr *macaddr,
918 int vlanid,
919 const char *profileId,
920 struct ifla_port_vsi *portVsi,
921 const unsigned char *instanceId,
922 const unsigned char *hostUUID,
923 int32_t vf,
924 uint8_t op,
925 bool setlink_only)
926 {
927 int rc;
928 int src_pid = 0;
929 uint32_t dst_pid = 0;
930 void *nlData = NULL;
931 struct nlattr *tb[IFLA_MAX + 1] = { NULL, };
932 int repeats = STATUS_POLL_TIMEOUT_USEC / STATUS_POLL_INTERVL_USEC;
933 uint16_t status = 0;
934 bool is8021Qbg = (profileId == NULL);
935
936 rc = virNetDevVPortProfileOpSetLink(ifname, ifindex,
937 nltarget_kernel,
938 macaddr,
939 vlanid,
940 profileId,
941 portVsi,
942 instanceId,
943 hostUUID,
944 vf,
945 op);
946 if (rc < 0) {
947 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
948 _("sending of PortProfileRequest failed."));
949 return rc;
950 }
951
952 if (setlink_only) /* for re-associations on existing links */
953 return 0;
954
955 if (!nltarget_kernel &&
956 (((src_pid = virNetlinkEventServiceLocalPid(NETLINK_ROUTE)) < 0) ||
957 ((dst_pid = virNetDevVPortProfileGetLldpadPid()) == 0))) {
958 rc = -1;
959 goto cleanup;
960 }
961
962 while (--repeats >= 0) {
963 VIR_FREE(nlData);
964 rc = virNetlinkDumpLink(NULL, ifindex, &nlData, tb, src_pid, dst_pid);
965 if (rc < 0)
966 goto cleanup;
967
968 rc = virNetDevVPortProfileGetStatus(tb, vf, instanceId, nltarget_kernel,
969 is8021Qbg, &status);
970 if (rc < 0)
971 goto cleanup;
972 if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
973 status == PORT_VDP_RESPONSE_SUCCESS) {
974 break;
975 } else if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
976 /* keep trying... */
977 } else {
978 virReportSystemError(EINVAL,
979 _("error %d during port-profile setlink on "
980 "interface %s (%d)"),
981 status, ifname, ifindex);
982 rc = -1;
983 break;
984 }
985
986 g_usleep(STATUS_POLL_INTERVL_USEC);
987 }
988
989 if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
990 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
991 _("port-profile setlink timed out"));
992 rc = -2;
993 }
994
995 cleanup:
996 VIR_FREE(nlData);
997 return rc;
998 }
999
1000
1001 static int
virNetDevVPortProfileGetPhysdevAndVlan(const char * ifname,int * root_ifindex,char * root_ifname,int * vlanid)1002 virNetDevVPortProfileGetPhysdevAndVlan(const char *ifname, int *root_ifindex, char *root_ifname,
1003 int *vlanid)
1004 {
1005 int ret;
1006 unsigned int nth;
1007 int ifindex = -1;
1008
1009 *vlanid = -1;
1010 while (1) {
1011 if ((ret = virNetDevVPortProfileGetNthParent(ifname, ifindex, 1,
1012 root_ifindex, root_ifname, &nth)) < 0)
1013 return ret;
1014 if (nth == 0)
1015 break;
1016 if (*vlanid == -1) {
1017 if (virNetDevGetVLanID(root_ifname, vlanid) < 0) {
1018 virResetLastError();
1019 *vlanid = -1;
1020 }
1021 }
1022
1023 ifindex = *root_ifindex;
1024 ifname = NULL;
1025 }
1026
1027 return 0;
1028 }
1029
1030 /* Returns 0 on success, -1 on general failure, and -2 on timeout */
1031 static int
virNetDevVPortProfileOp8021Qbg(const char * ifname,const virMacAddr * macaddr,int vf,const virNetDevVPortProfile * virtPort,enum virNetDevVPortProfileLinkOp virtPortOp,bool setlink_only)1032 virNetDevVPortProfileOp8021Qbg(const char *ifname,
1033 const virMacAddr *macaddr,
1034 int vf,
1035 const virNetDevVPortProfile *virtPort,
1036 enum virNetDevVPortProfileLinkOp virtPortOp,
1037 bool setlink_only)
1038 {
1039 int op = PORT_REQUEST_ASSOCIATE;
1040 struct ifla_port_vsi portVsi = {
1041 .vsi_mgr_id = virtPort->managerID,
1042 .vsi_type_version = virtPort->typeIDVersion,
1043 };
1044 bool nltarget_kernel = false;
1045 int vlanid;
1046 int physdev_ifindex = 0;
1047 char physdev_ifname[IFNAMSIZ] = { 0, };
1048
1049 if (!ifname)
1050 return -1;
1051
1052 vf = PORT_SELF_VF;
1053
1054 if (virNetDevVPortProfileGetPhysdevAndVlan(ifname, &physdev_ifindex,
1055 physdev_ifname, &vlanid) < 0) {
1056 return -1;
1057 }
1058
1059 if (vlanid < 0)
1060 vlanid = 0;
1061
1062 portVsi.vsi_type_id[2] = virtPort->typeID >> 16;
1063 portVsi.vsi_type_id[1] = virtPort->typeID >> 8;
1064 portVsi.vsi_type_id[0] = virtPort->typeID;
1065
1066 switch (virtPortOp) {
1067 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE:
1068 op = PORT_REQUEST_PREASSOCIATE;
1069 break;
1070 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR:
1071 op = PORT_REQUEST_PREASSOCIATE_RR;
1072 break;
1073 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE:
1074 op = PORT_REQUEST_ASSOCIATE;
1075 break;
1076 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE:
1077 op = PORT_REQUEST_DISASSOCIATE;
1078 break;
1079 default:
1080 virReportError(VIR_ERR_INTERNAL_ERROR,
1081 _("operation type %d not supported"), virtPortOp);
1082 return -1;
1083 }
1084
1085 return virNetDevVPortProfileOpCommon(physdev_ifname, physdev_ifindex,
1086 nltarget_kernel,
1087 macaddr,
1088 vlanid,
1089 NULL,
1090 &portVsi,
1091 virtPort->instanceID,
1092 NULL,
1093 vf,
1094 op,
1095 setlink_only);
1096 }
1097
1098 /* Returns 0 on success, -1 on general failure, and -2 on timeout */
1099 static int
virNetDevVPortProfileOp8021Qbh(const char * ifname,const virMacAddr * macaddr,int32_t vf,const virNetDevVPortProfile * virtPort,const unsigned char * vm_uuid,enum virNetDevVPortProfileLinkOp virtPortOp)1100 virNetDevVPortProfileOp8021Qbh(const char *ifname,
1101 const virMacAddr *macaddr,
1102 int32_t vf,
1103 const virNetDevVPortProfile *virtPort,
1104 const unsigned char *vm_uuid,
1105 enum virNetDevVPortProfileLinkOp virtPortOp)
1106 {
1107 int rc = 0;
1108 char *physfndev = NULL;
1109 unsigned char hostuuid[VIR_UUID_BUFLEN];
1110 bool nltarget_kernel = true;
1111 int ifindex;
1112 int vlanid = -1;
1113 bool is_vf = false;
1114
1115 if (vf == -1) {
1116 int isvf_ret = virNetDevIsVirtualFunction(ifname);
1117
1118 if (isvf_ret == -1)
1119 goto cleanup;
1120 is_vf = !!isvf_ret;
1121 }
1122
1123 if (is_vf) {
1124 if (virNetDevGetVirtualFunctionInfo(ifname, &physfndev, &vf) < 0) {
1125 rc = -1;
1126 goto cleanup;
1127 }
1128 } else {
1129 physfndev = g_strdup(ifname);
1130 }
1131
1132 rc = virNetDevGetIndex(physfndev, &ifindex);
1133 if (rc < 0)
1134 goto cleanup;
1135
1136 switch (virtPortOp) {
1137 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR:
1138 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE:
1139 errno = virGetHostUUID(hostuuid);
1140 if (errno) {
1141 rc = -1;
1142 goto cleanup;
1143 }
1144
1145 rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
1146 nltarget_kernel,
1147 macaddr,
1148 vlanid,
1149 virtPort->profileID,
1150 NULL,
1151 vm_uuid,
1152 hostuuid,
1153 vf,
1154 (virtPortOp ==
1155 VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR) ?
1156 PORT_REQUEST_PREASSOCIATE_RR
1157 : PORT_REQUEST_ASSOCIATE,
1158 false);
1159 if (rc == -2)
1160 /* Association timed out, disassociate */
1161 virNetDevVPortProfileOpCommon(NULL, ifindex,
1162 nltarget_kernel,
1163 NULL,
1164 vlanid,
1165 NULL,
1166 NULL,
1167 NULL,
1168 NULL,
1169 vf,
1170 PORT_REQUEST_DISASSOCIATE,
1171 false);
1172 break;
1173
1174 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE:
1175 rc = virNetDevVPortProfileOpCommon(NULL, ifindex,
1176 nltarget_kernel,
1177 NULL,
1178 vlanid,
1179 NULL,
1180 NULL,
1181 NULL,
1182 NULL,
1183 vf,
1184 PORT_REQUEST_DISASSOCIATE,
1185 false);
1186 break;
1187
1188 case VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE:
1189 virReportError(VIR_ERR_INTERNAL_ERROR,
1190 _("operation type %d not supported"), virtPortOp);
1191 rc = -1;
1192 break;
1193 default:
1194 virReportEnumRangeError(virNetDevVPortProfileType, virtPortOp);
1195 rc = -1;
1196 break;
1197 }
1198
1199 cleanup:
1200 VIR_FREE(physfndev);
1201 return rc;
1202 }
1203
1204 /**
1205 * virNetDevVPortProfileAssociate:
1206 *
1207 * @macvtap_ifname: The name of the macvtap device
1208 * @virtPort: pointer to the object holding port profile parameters
1209 * @vmuuid : the UUID of the virtual machine
1210 * @vmOp : The VM operation (i.e., create, no-op)
1211 * @setlink_only : Only set the link - dont wait for the link to come up
1212 *
1213 * Associate a port on a switch with a profile. This function
1214 * may notify a kernel driver or an external daemon to run
1215 * the setup protocol. If profile parameters were not supplied
1216 * by the user, then this function returns without doing
1217 * anything.
1218 *
1219 * Returns 0 in case of success, < 0 otherwise with error
1220 * having been reported.
1221 */
1222 int
virNetDevVPortProfileAssociate(const char * macvtap_ifname,const virNetDevVPortProfile * virtPort,const virMacAddr * macvtap_macaddr,const char * linkdev,int vf,const unsigned char * vmuuid,virNetDevVPortProfileOp vmOp,bool setlink_only)1223 virNetDevVPortProfileAssociate(const char *macvtap_ifname,
1224 const virNetDevVPortProfile *virtPort,
1225 const virMacAddr *macvtap_macaddr,
1226 const char *linkdev,
1227 int vf,
1228 const unsigned char *vmuuid,
1229 virNetDevVPortProfileOp vmOp,
1230 bool setlink_only)
1231 {
1232 int rc = 0;
1233 char uuidStr[VIR_UUID_STRING_BUFLEN];
1234 char macStr[VIR_MAC_STRING_BUFLEN];
1235
1236 VIR_DEBUG("profile:'%p' vmOp: %s device: %s@%s mac: %s uuid: %s",
1237 virtPort, virNetDevVPortProfileOpTypeToString(vmOp),
1238 NULLSTR_EMPTY(macvtap_ifname), linkdev,
1239 (macvtap_macaddr
1240 ? virMacAddrFormat(macvtap_macaddr, macStr)
1241 : "(unspecified)"),
1242 vmuuid ? virUUIDFormat(vmuuid, uuidStr) : "(unspecified)");
1243
1244 if (!virtPort || vmOp == VIR_NETDEV_VPORT_PROFILE_OP_NO_OP)
1245 return 0;
1246
1247 switch (virtPort->virtPortType) {
1248 case VIR_NETDEV_VPORT_PROFILE_NONE:
1249 case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
1250 case VIR_NETDEV_VPORT_PROFILE_LAST:
1251 break;
1252
1253 case VIR_NETDEV_VPORT_PROFILE_8021QBG:
1254 rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr,
1255 vf, virtPort,
1256 (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
1257 ? VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE
1258 : VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE,
1259 setlink_only);
1260 break;
1261
1262 case VIR_NETDEV_VPORT_PROFILE_8021QBH:
1263 rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr, vf,
1264 virtPort, vmuuid,
1265 (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START)
1266 ? VIR_NETDEV_VPORT_PROFILE_LINK_OP_PREASSOCIATE_RR
1267 : VIR_NETDEV_VPORT_PROFILE_LINK_OP_ASSOCIATE);
1268 if (vmOp != VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_START && !rc) {
1269 /* XXX bogus error handling */
1270 ignore_value(virNetDevSetOnline(linkdev, true));
1271 }
1272
1273 break;
1274 }
1275
1276 return rc;
1277 }
1278
1279
1280 /**
1281 * virNetDevVPortProfileDisassociate:
1282 *
1283 * @macvtap_ifname: The name of the macvtap device
1284 * @macvtap_macaddr : The MAC address of the macvtap
1285 * @linkdev: The link device in case of macvtap
1286 * @virtPort: point to object holding port profile parameters
1287 *
1288 * Returns 0 in case of success, != 0 otherwise with error
1289 * having been reported.
1290 */
1291 int
virNetDevVPortProfileDisassociate(const char * macvtap_ifname,const virNetDevVPortProfile * virtPort,const virMacAddr * macvtap_macaddr,const char * linkdev,int vf,virNetDevVPortProfileOp vmOp)1292 virNetDevVPortProfileDisassociate(const char *macvtap_ifname,
1293 const virNetDevVPortProfile *virtPort,
1294 const virMacAddr *macvtap_macaddr,
1295 const char *linkdev,
1296 int vf,
1297 virNetDevVPortProfileOp vmOp)
1298 {
1299 int rc = 0;
1300 char macStr[VIR_MAC_STRING_BUFLEN];
1301
1302 VIR_DEBUG("profile:'%p' vmOp: %s device: %s@%s mac: %s",
1303 virtPort, virNetDevVPortProfileOpTypeToString(vmOp),
1304 NULLSTR_EMPTY(macvtap_ifname), linkdev,
1305 (macvtap_macaddr
1306 ? virMacAddrFormat(macvtap_macaddr, macStr)
1307 : "(unspecified)"));
1308
1309 if (!virtPort)
1310 return 0;
1311
1312 switch (virtPort->virtPortType) {
1313 case VIR_NETDEV_VPORT_PROFILE_NONE:
1314 case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
1315 case VIR_NETDEV_VPORT_PROFILE_LAST:
1316 break;
1317
1318 case VIR_NETDEV_VPORT_PROFILE_8021QBG:
1319 rc = virNetDevVPortProfileOp8021Qbg(macvtap_ifname, macvtap_macaddr, vf,
1320 virtPort,
1321 VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE, false);
1322 break;
1323
1324 case VIR_NETDEV_VPORT_PROFILE_8021QBH:
1325 /* avoid disassociating twice */
1326 if (vmOp == VIR_NETDEV_VPORT_PROFILE_OP_MIGRATE_IN_FINISH)
1327 break;
1328 if (vf < 0)
1329 ignore_value(virNetDevSetOnline(linkdev, false));
1330 rc = virNetDevVPortProfileOp8021Qbh(linkdev, macvtap_macaddr, vf,
1331 virtPort, NULL,
1332 VIR_NETDEV_VPORT_PROFILE_LINK_OP_DISASSOCIATE);
1333 break;
1334 }
1335
1336 return rc;
1337 }
1338
1339 #else /* !WITH_LIBNL */
virNetDevVPortProfileAssociate(const char * macvtap_ifname G_GNUC_UNUSED,const virNetDevVPortProfile * virtPort G_GNUC_UNUSED,const virMacAddr * macvtap_macaddr G_GNUC_UNUSED,const char * linkdev G_GNUC_UNUSED,int vf G_GNUC_UNUSED,const unsigned char * vmuuid G_GNUC_UNUSED,virNetDevVPortProfileOp vmOp G_GNUC_UNUSED,bool setlink_only G_GNUC_UNUSED)1340 int virNetDevVPortProfileAssociate(const char *macvtap_ifname G_GNUC_UNUSED,
1341 const virNetDevVPortProfile *virtPort G_GNUC_UNUSED,
1342 const virMacAddr *macvtap_macaddr G_GNUC_UNUSED,
1343 const char *linkdev G_GNUC_UNUSED,
1344 int vf G_GNUC_UNUSED,
1345 const unsigned char *vmuuid G_GNUC_UNUSED,
1346 virNetDevVPortProfileOp vmOp G_GNUC_UNUSED,
1347 bool setlink_only G_GNUC_UNUSED)
1348 {
1349 virReportSystemError(ENOSYS, "%s",
1350 _("Virtual port profile association not supported on this platform"));
1351 return -1;
1352 }
1353
virNetDevVPortProfileDisassociate(const char * macvtap_ifname G_GNUC_UNUSED,const virNetDevVPortProfile * virtPort G_GNUC_UNUSED,const virMacAddr * macvtap_macaddr G_GNUC_UNUSED,const char * linkdev G_GNUC_UNUSED,int vf G_GNUC_UNUSED,virNetDevVPortProfileOp vmOp G_GNUC_UNUSED)1354 int virNetDevVPortProfileDisassociate(const char *macvtap_ifname G_GNUC_UNUSED,
1355 const virNetDevVPortProfile *virtPort G_GNUC_UNUSED,
1356 const virMacAddr *macvtap_macaddr G_GNUC_UNUSED,
1357 const char *linkdev G_GNUC_UNUSED,
1358 int vf G_GNUC_UNUSED,
1359 virNetDevVPortProfileOp vmOp G_GNUC_UNUSED)
1360 {
1361 virReportSystemError(ENOSYS, "%s",
1362 _("Virtual port profile association not supported on this platform"));
1363 return -1;
1364 }
1365 #endif /* !WITH_LIBNL */
1366