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