virDomainNetDefFormat(virBufferPtr buf,
                      virDomainNetDefPtr def,
                      virDomainXMLOptionPtr xmlopt,
                      unsigned int flags)
{
    virDomainNetType actualType = virDomainNetGetActualType(def);
    bool publicActual = false;
    int sourceLines = 0;
    const char *typeStr;
    virDomainHostdevDefPtr hostdef = NULL;
    char macstr[VIR_MAC_STRING_BUFLEN];
    g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
    const char *prefix = xmlopt ? xmlopt->config.netPrefix : NULL;

    /* publicActual is true if we should report the current state in
     * def->data.network.actual *instead of* the config (*not* in
     * addition to)
     */
    if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
        def->data.network.actual &&
        !(flags & (VIR_DOMAIN_DEF_FORMAT_INACTIVE |
                   VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET |
                   VIR_DOMAIN_DEF_FORMAT_MIGRATABLE)))
        publicActual = true;

    if (publicActual) {
        if (!(typeStr = virDomainNetTypeToString(actualType))) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unexpected actual net type %d"), actualType);
            return -1;
        }
        if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV)
            hostdef = virDomainNetGetActualHostdev(def);
    } else {
        if (!(typeStr = virDomainNetTypeToString(def->type))) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unexpected net type %d"), def->type);
            return -1;
        }
        if (def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)
            hostdef = &def->data.hostdev.def;
    }

    virBufferAsprintf(buf, "<interface type='%s'", typeStr);
    if (hostdef && hostdef->managed)
        virBufferAddLit(buf, " managed='yes'");
    if (def->trustGuestRxFilters)
        virBufferAsprintf(buf, " trustGuestRxFilters='%s'",
                          virTristateBoolTypeToString(def->trustGuestRxFilters));
    virBufferAddLit(buf, ">\n");

    virBufferAdjustIndent(buf, 2);
    virBufferAsprintf(buf, "<mac address='%s'/>\n",
                      virMacAddrFormat(&def->mac, macstr));

    if (publicActual) {
        /* when there is a virDomainActualNetDef, and we haven't been
         * asked to 1) report the domain's inactive XML, or 2) give
         * the internal version of the ActualNetDef separately in an
         * <actual> subelement, we can just put the ActualDef data in
         * the standard place...  (this is for public reporting of
         * interface status)
         */
        if (virDomainActualNetDefContentsFormat(buf, def, false, flags) < 0)
            return -1;
    } else {
        /* ...but if we've asked for the inactive XML (rather than
         * status), or to report the ActualDef as a separate <actual>
         * subelement (this is how we privately store interface
         * status), or there simply *isn't* any ActualNetDef, then
         * format the NetDef's data here, and optionally format the
         * ActualNetDef as an <actual> subelement of this element.
         */
        switch (def->type) {
        case VIR_DOMAIN_NET_TYPE_NETWORK:
            virBufferEscapeString(buf, "<source network='%s'",
                                  def->data.network.name);
            virBufferEscapeString(buf, " portgroup='%s'",
                                  def->data.network.portgroup);
            if (virUUIDIsValid(def->data.network.portid) &&
                !(flags & (VIR_DOMAIN_DEF_FORMAT_INACTIVE))) {
                char portidstr[VIR_UUID_STRING_BUFLEN];
                virUUIDFormat(def->data.network.portid, portidstr);
                virBufferEscapeString(buf, " portid='%s'", portidstr);
            }
            sourceLines++;
            break;

        case VIR_DOMAIN_NET_TYPE_ETHERNET:
            break;

        case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
            if (def->data.vhostuser->type == VIR_DOMAIN_CHR_TYPE_UNIX) {
                virBufferAddLit(buf, "<source type='unix'");
                virBufferEscapeString(buf, " path='%s'",
                                      def->data.vhostuser->data.nix.path);
                virBufferAsprintf(buf, " mode='%s'",
                                  def->data.vhostuser->data.nix.listen ?
                                  "server"  : "client");
                sourceLines++;
                if (def->data.vhostuser->data.nix.reconnect.enabled) {
                    virBufferAddLit(buf, ">\n");
                    sourceLines++;
                    virBufferAdjustIndent(buf, 2);
                    virDomainChrSourceReconnectDefFormat(buf,
                                                         &def->data.vhostuser->data.nix.reconnect);
                    virBufferAdjustIndent(buf, -2);
                }

            }
            break;

        case VIR_DOMAIN_NET_TYPE_BRIDGE:
           if (def->data.bridge.brname) {
               virBufferEscapeString(buf, "<source bridge='%s'",
                                     def->data.bridge.brname);
               sourceLines++;
           }
            break;

        case VIR_DOMAIN_NET_TYPE_SERVER:
        case VIR_DOMAIN_NET_TYPE_CLIENT:
        case VIR_DOMAIN_NET_TYPE_MCAST:
        case VIR_DOMAIN_NET_TYPE_UDP:
            if (def->data.socket.address) {
                virBufferAsprintf(buf, "<source address='%s' port='%d'",
                                  def->data.socket.address,
                                  def->data.socket.port);
            } else {
                virBufferAsprintf(buf, "<source port='%d'",
                                  def->data.socket.port);
            }
            sourceLines++;

            if (def->type != VIR_DOMAIN_NET_TYPE_UDP)
                break;

            virBufferAddLit(buf, ">\n");
            sourceLines++;
            virBufferAdjustIndent(buf, 2);

            virBufferAsprintf(buf, "<local address='%s' port='%d'/>\n",
                              def->data.socket.localaddr,
                              def->data.socket.localport);
            virBufferAdjustIndent(buf, -2);
            break;

        case VIR_DOMAIN_NET_TYPE_INTERNAL:
            if (def->data.internal.name) {
                virBufferEscapeString(buf, "<source name='%s'",
                                      def->data.internal.name);
                sourceLines++;
            }
            break;

        case VIR_DOMAIN_NET_TYPE_DIRECT:
            virBufferEscapeString(buf, "<source dev='%s'",
                                  def->data.direct.linkdev);
            virBufferAsprintf(buf, " mode='%s'",
                              virNetDevMacVLanModeTypeToString(def->data.direct.mode));
            sourceLines++;
            break;

        case VIR_DOMAIN_NET_TYPE_HOSTDEV:
            if (virDomainHostdevDefFormatSubsys(buf, &def->data.hostdev.def,
                                                flags, true) < 0) {
                return -1;
            }
            break;

        case VIR_DOMAIN_NET_TYPE_USER:
        case VIR_DOMAIN_NET_TYPE_LAST:
            break;
        }

        /* if sourceLines == 0 - no <source> info at all so far
         *    sourceLines == 1 - first line written, no terminating ">"
         *    sourceLines > 1 - multiple lines, including subelements
         */
        if (def->hostIP.nips || def->hostIP.nroutes) {
            if (sourceLines == 0) {
                virBufferAddLit(buf, "<source>\n");
                sourceLines += 2;
            } else if (sourceLines == 1) {
                virBufferAddLit(buf, ">\n");
                sourceLines++;
            }
            virBufferAdjustIndent(buf, 2);
            if (virDomainNetIPInfoFormat(buf, &def->hostIP) < 0)
                return -1;
            virBufferAdjustIndent(buf, -2);
        }
        if (sourceLines == 1)
            virBufferAddLit(buf, "/>\n");
        else if (sourceLines > 1)
            virBufferAddLit(buf, "</source>\n");

        if (virNetDevVlanFormat(&def->vlan, buf) < 0)
            return -1;
        if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
            return -1;
        if (virNetDevBandwidthFormat(def->bandwidth, 0, buf) < 0)
            return -1;
        virNetworkPortOptionsFormat(def->isolatedPort, buf);

        /* ONLY for internal status storage - format the ActualNetDef
         * as a subelement of <interface> so that no persistent config
         * data is overwritten.
         */
        if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
            (flags & VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET) &&
            (virDomainActualNetDefFormat(buf, def, flags) < 0))
            return -1;

    }

    if (virDomainNetIPInfoFormat(buf, &def->guestIP) < 0)
        return -1;

    virBufferEscapeString(buf, "<script path='%s'/>\n",
                          def->script);
    virBufferEscapeString(buf, "<backenddomain name='%s'/>\n", def->domain_name);

    if (def->ifname &&
        (def->managed_tap == VIR_TRISTATE_BOOL_NO ||
         !((flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE) &&
           (STRPREFIX(def->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
            (prefix && STRPREFIX(def->ifname, prefix)))))) {
        /* Skip auto-generated target names for inactive config. */
        virBufferEscapeString(&attrBuf, " dev='%s'", def->ifname);
    }
    if (def->managed_tap != VIR_TRISTATE_BOOL_ABSENT) {
        virBufferAsprintf(&attrBuf, " managed='%s'",
                          virTristateBoolTypeToString(def->managed_tap));
    }

    virXMLFormatElement(buf, "target", &attrBuf, NULL);

    if (def->ifname_guest || def->ifname_guest_actual) {
        virBufferAddLit(buf, "<guest");
        /* Skip auto-generated target names for inactive config. */
        if (def->ifname_guest)
            virBufferEscapeString(buf, " dev='%s'", def->ifname_guest);

        /* Only set if the host is running, so shouldn't pollute output */
        if (def->ifname_guest_actual)
            virBufferEscapeString(buf, " actual='%s'", def->ifname_guest_actual);
        virBufferAddLit(buf, "/>\n");
    }
    if (virDomainNetGetModelString(def)) {
        virBufferEscapeString(buf, "<model type='%s'/>\n",
                              virDomainNetGetModelString(def));
        if (virDomainNetIsVirtioModel(def)) {
            int rc = 0;
            g_autofree char *str = NULL;
            g_autofree char *gueststr = NULL;
            g_autofree char *hoststr = NULL;

            if (virDomainVirtioNetDriverFormat(&str, def) < 0 ||
                virDomainVirtioNetGuestOptsFormat(&gueststr, def) < 0 ||
                virDomainVirtioNetHostOptsFormat(&hoststr, def) < 0)
                rc = -1;

            if (!gueststr && !hoststr) {
                if (str)
                    virBufferAsprintf(buf, "<driver%s/>\n", str);
            } else {
                if (str)
                    virBufferAsprintf(buf, "<driver%s>\n", str);
                else
                    virBufferAddLit(buf, "<driver>\n");
                virBufferAdjustIndent(buf, 2);
                if (hoststr)
                    virBufferAsprintf(buf, "<host %s/>\n", hoststr);
                if (gueststr)
                    virBufferAsprintf(buf, "<guest %s/>\n", gueststr);
                virBufferAdjustIndent(buf, -2);
                virBufferAddLit(buf, "</driver>\n");
            }

            if (rc < 0)
                return -1;
        }
    }
    if (def->backend.tap || def->backend.vhost) {
        virBufferAddLit(buf, "<backend");
        virBufferEscapeString(buf, " tap='%s'", def->backend.tap);
        virBufferEscapeString(buf, " vhost='%s'", def->backend.vhost);
        virBufferAddLit(buf, "/>\n");
    }
    if (def->filter) {
        if (virNWFilterFormatParamAttributes(buf, def->filterparams,
                                             def->filter) < 0)
            return -1;
    }

    if (def->tune.sndbuf_specified) {
        virBufferAddLit(buf,   "<tune>\n");
        virBufferAdjustIndent(buf, 2);
        virBufferAsprintf(buf, "<sndbuf>%lu</sndbuf>\n", def->tune.sndbuf);
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf,   "</tune>\n");
    }

    if (def->teaming.type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE) {
        virBufferAsprintf(buf, "<teaming type='%s'",
                          virDomainNetTeamingTypeToString(def->teaming.type));
        virBufferEscapeString(buf, " persistent='%s'", def->teaming.persistent);
        virBufferAddLit(buf, "/>\n");
    }
    if (def->linkstate) {
        virBufferAsprintf(buf, "<link state='%s'/>\n",
                          virDomainNetInterfaceLinkStateTypeToString(def->linkstate));
    }

    if (def->mtu)
        virBufferAsprintf(buf, "<mtu size='%u'/>\n", def->mtu);

    virDomainNetDefCoalesceFormatXML(buf, def->coalesce);

    if (virDomainDeviceInfoFormat(buf, &def->info,
                                  flags | VIR_DOMAIN_DEF_FORMAT_ALLOW_BOOT
                                  | VIR_DOMAIN_DEF_FORMAT_ALLOW_ROM) < 0) {
        return -1;
    }

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</interface>\n");
    return 0;
}