aboutsummaryrefslogtreecommitdiffstats
path: root/puppet/controller-role.yaml
AgeCommit message (Collapse)AuthorFilesLines
2017-06-08Merge "Use Deployment actions for blacklist"Jenkins1-5/+20
2017-06-07Use Deployment actions for blacklistJames Slagle1-5/+20
Instead of using the Heat condition directly on the Deployment resources, use it to set the action list to an empty list when the server is blacklisted. This has a couple advantages over the previous approach in that the actual resources are not deleted and recreated when servers are added and removed from the blacklist. Recreating the resources can be problematic, as it would then force the Deployments to re-run when a server is removed from the blacklist. That is likely not always desirable, especially in the case of NetworkDeloyment. Additionally, you will still see the resources for a blacklisted server in the stack, just with an empty set of actions. This has the benefit of preserving the history of the previous time the Deployment was triggered. implements blueprint disable-deployments Change-Id: I3d0263a6319ae4871b1ae11383ae838bd2540d36
2017-06-06Convert puppet and docker steps to ansibleSteven Hardy1-0/+1
Replace the multiple SoftwareDeployment resources with a common playbook that runs on all roles, consuming the configuration data written via the HostPrepAnsible tasks. This hopefully simplifies things, and will enable re-running the deploy steps for minor updates (we'll need some way to detect a container should be replaced, but that will be done via a follow-up patch). Change-Id: I674a4d9d2c77d1f6fbdb0996f6c9321848e32662
2017-05-31Server blacklist supportJames Slagle1-0/+20
Adds the ability to blacklist servers from all SoftwareDeployment resources. The servers are specified in a new list parameter, DeploymentServerBlacklist by the Heat assigned name (overcloud-compute-0, etc). implements blueprint disable-deployments Change-Id: I46941e54a476c7cc8645cd1aff391c9c6c5434de
2017-05-19Update the template_version alias for all the templates to pike.Carlos Camacho1-1/+1
Master is now the development branch for pike changing the release alias name. Change-Id: I938e4a983e361aefcaa0bd9a4226c296c5823127
2017-05-18Merge "Enable splay for os-collect-config"Jenkins1-0/+10
2017-05-01Enable splay for os-collect-configAlex Schultz1-0/+10
At scale, having the os-collect-config instances all check in at the same time can cause performance problems. This change enables splay and sets it to a default maximum random sleep of 30 seconds prior to the os-collect-config polling. Change-Id: Iab8b51f4e5fb4727b8aa7e081f5cbfcbf11f7fcb Depends-On: I88f623c9e8db9ed4a186918206a63faec8f7f673 Closes-Bug: #1677314
2017-04-20Merge "Pluggable server type per Role"Jenkins1-1/+1
2017-04-18Merge "SSH known_hosts config"Jenkins1-0/+37
2017-04-13SSH known_hosts configOliver Walsh1-0/+37
Fetch the host public keys from each node, combine them all and write to the system-wide ssh known hosts. The alternative of disabling host key verification is vulnerable to a MITM attack. Change-Id: Ib572b5910720b1991812256e68c975f7fbe2239c
2017-04-13Pluggable server type per RoleJames Slagle1-1/+1
The server resource type, OS::TripleO::Server can now be mapped per role instead of globally. This allows users to mix baremetal (OS::Nova::Server) and deployed-server (OS::Heat::DeployedServer) server resources in the same deployment. blueprint pluggable-server-type-per-role Change-Id: Ib9e9abe2ba5103db221f0b485c46704b1e260dbf
2017-04-10Add composable role support for NetApp Cinder back endAlan Bishop1-1/+0
Convert NetApp Cinder back end to support composable roles via new "CinderBackendNetApp" service. Closes-Bug: #1680568 Change-Id: Ia3a78a48c32997c9d3cbe1629c2043cfc5249e1c
2017-03-01Make UpdateDeployment depend on NetworkDeploymentSteven Hardy1-0/+1
Prior to https://review.openstack.org/#/c/271450/ os-net-config was applied via os-refresh-config directly, which meant that even though UpdateDeployment and NetworkDeployment can be created concurrently, we'd always do the os-net-config step first. However now that we apply both steps via scripts (which are both handled via the same heat-config hook) we should add an explicit dependency to ensure the network is always fully configured before attempting to run any update. This should avoid the risk of e.g running an update on initial deployment before the network connectivity to access yum repos is in place. Change-Id: Idff7a95afe7b49b6384b1d0c78e76522fb1f8eb7 Related-Bug: #1666227
2017-02-16Add Newton to Ocata UpgradeInitCommonCommandmarios1-0/+9
This adds the UpgradeInitCommonCommand for newton..ocata common UpgradeInit commands. This comes before the ansible upgrade steps so we need to do things like remove the old newton hieradata and install the ansible-pacemaker module and ansible heat-agent plugin This defaults to '' and is set in the major-upgrade-composable-steps and unset in the major-upgrade-converge environment files. Change-Id: I0c7a32194c0069b63a501a913c17907b47c9cc16
2017-02-08Merge "Composable service support for Cinder Dell EMC Storage Center"Jenkins1-1/+0
2017-02-07Composable services support for Cinder Dell EMC PS Seriesrajinir1-1/+0
Updated the heat templates for Cinder Dell EMC PS Series backend to use composable services and rebranding of EQLX to Dell EMC PS Series Closes-Bug: #1661313 Change-Id: Id9d6f172f3f79a31788b26c7776d738fda5a30fa
2017-02-03Composable service support for Cinder Dell EMC Storage Centerrajinir1-1/+0
Updated the heat templates for Cinder Dell EMC Storage Center Backend to use composable services Closes-Bug: #1661314 Change-Id: I454549c45da7388f0e42975c9f4637dde9ec51e3
2017-01-04Merge "Add pre-network hook and example showing config-then-reboot"Jenkins1-0/+6
2016-12-23Bump template version for all templates to "ocata"Steven Hardy1-1/+1
Heat now supports release name aliases, so we can replace the inconsistent mix of date related versions with one consistent version that aligns with the supported version of heat for this t-h-t branch. This should also help new users who sometimes copy/paste old templates and discover intrinsic functions in the t-h-t docs don't work because their template version is too old. Change-Id: Ib415e7290fea27447460baa280291492df197e54
2016-12-22Merge "Introduce role-specific NodeUserData, use for docker"Jenkins1-0/+7
2016-12-22Add hook to generate metadata from service profilesJuan Antonio Osorio Robles1-0/+4
This enables the deployer to dynamically add nova metadata to the servers based on the output of service profiles that implement the metadata_settings key in the role_data output for the profiles. One can set an implementation via the OS::TripleO::ServerMetadataHook resource, which currently is set as OS::Heat::None. So, because of the default implementation, if left untouched it actually does nothing. Currently, besides the list, which is metadata_settings, this hook also takes the name of the node that it's setting the metadata for. This is useful for nova vendordata plugins that can parse said metadata. Change-Id: I8a937f711f0b90156fbb6c4632760435ef846474
2016-12-19Introduce role-specific NodeUserData, use for dockerSteve Baker1-0/+7
Currently when the docker environments are invoked, every node has the boot script run which replaces os-collect-config with the heat-agents container. This should only be happening on Compute nodes currently, and each role will be converted to heat-agents one at a time. This change implements a role-specific NodeUserData resource and uses that mechanism to run docker/firstboot/install_docker_agents.yaml only on Compute nodes. Change-Id: Id81811dbcaf0e661c3980aa25f3ca80db5ef0954
2016-12-19Move UpgradeInitCommand to role templatesSteven Hardy1-1/+27
We can't run this during the upgrade steps, because there are things which need to happen before any role configuration happens, e.g installing the new hiera heat-config hook, which must be done before e.g "ControllerDeployment" runs or the stack update hangs. Partially-Implements: blueprint overcloud-upgrades-per-service Change-Id: I365b57513590662c3f78a33dc625747f457c48c5
2016-12-16Introduce role-specific nova-server-metadataJuan Antonio Osorio Robles1-2/+14
We could already pass metadata to the nova server instances (on creation) via the ServerMetadata parameter, however, there was no way of doing this per-role. This introduces that by adding a {{role}}ServerMetadata parameter for each role. This parameter gets merged with the ServerMetadata parameter and allows this functionality. Note that both default to {}, and so does the result of merging those parameters with their default values. So nothing changes for the default settings. Change-Id: I334edcc51ce7ee82fc13b6cf4c0d74ccb7db099c
2016-12-15Add pre-network hook and example showing config-then-rebootSteven Hardy1-0/+6
There are some requirements for early configuration that involves e.g setting kernel parameters then rebooting. Currently this can be done via cloud-init, e.g firstboot templates, but there's been discussion around enabling a SoftwareDeployment approach instead. The main advantage of doing it this way is there's an error path if something goes wrong with the config (except triggering the reboot as we have to use NO_SIGNAL for that). Change-Id: Ia54ee654f755631b8062eb5c209a60c6f9161500
2016-12-02Move nodes' fqdns to a map to remove clutterJuan Antonio Osorio Robles1-113/+110
There were several instances where the short-names/FQDNs where being gotten in the same way in the role's templates. So this introduces a mapping to get these values in order to reduce clutter. Change-Id: Ie7df360bb69d56655f3e0fcbbf4d297db39b7a26
2016-12-01Introduce network-based FQDNs via hieraJuan Antonio Osorio Robles1-0/+36
Currently, one can get the network-based FQDNs via a custom puppet fact. This is currently unreliable, as it's based on the ::hostname fact which we assume it's set correctly by nova. However, this is not necessarily the case (for instance, if you use pre-deployed services such as we do with the multinode-jobs). In these cases, the ::hostname fact will return something other than what we specified in nova, and effectively breaks the configurations in we relly too much on the network-based FQDN facts. By using hiera instead, we avoid this issue as we set those values to be exactly what we expect (as we set them in the OS::TripleO::Server resource. Change-Id: I6ce31237098f57bdc0adfd3c42feef0073c224fb
2016-11-30Hiera optimization: use a new hiera hookDan Prince1-49/+43
This patch optimizes how we deploy hiera by using a new heat hook specifically designed to help compose hiera within heat templates. As part of this change: - we update all the 'hiera' software configurations to set the group to hiera instead of os-apply-config. - The new format uses JSON instead of YAML. The hook actually writes out the hiera JSON directly so no conversion takes place. Arrays, Strings, Booleans all stay in their native formats. As such we can avoid having to do many of the awkward string and list conversions in t-h-t to support the previous YAML formatting. - The new hook prefers JSON over YAML so upgrading users will have the new files prefered. (we will post a cleanup routine for the old files soon but this isn't a new behavior, JSON is now simply prefered.) - A lot of services required edits to account for default settings that worked in YAML that no longer work correctly in the native JSON format. In almost all these cases I think the resulting codes looks cleaner and is more explicit with regards to what is getting configured in hiera on the actual nodes. Depends-On: I6a383b1ad4ec29458569763bd3f56fd3f2bd726b Closes-bug: #1596373 Change-Id: Ibe7e2044e200e2c947223286fdf4fd5bcf98c2e1
2016-11-22Make the CloudDomain defaults match the doc stringsJulie Pichon1-0/+1
Not having the default easily accessible is causing issues for the UI, as it cannot guess at it and can accidentally overwrite the value with an empty string (the expected default when unset). The default is already helpfully spelled out in the doc string for each file, this updates the parameter to match it. Change-Id: Ic284f9904e8f1d01cc717d59a0759f679d94106d Closes-Bug: #1643670
2016-10-06Merge "restore missing fluentd client functionality"Jenkins1-8/+0
2016-10-05restore missing fluentd client functionalityLars Kellogg-Stedman1-8/+0
in the great rebase following the JINJA ALL THE THINGS changes we lost critical functionality in the fluentd client service. This review restores the missing features. Change-Id: I7c23f16f81e75f3da6a24587b2eb8385b3e920a4 Closes-bug: 1630692
2016-10-05Select per-network hostnames for service_node_namesSteven Hardy1-0/+45
Co-Authored-By: Juan Antonio Osorio Robles <jaosorior@redhat.com> Depends-On: Ic6fec1057439ed9122d44ef294be890d3ff8a8ee Change-Id: I754c4a41d8a294a4c7c18bd282ae014efd4b9b16 Closes-Bug: #1628521
2016-10-04Move the main template files for defalut services to new syntax generationCarlos Camacho1-0/+483
When generating these templates, we should create them with the "-role" appended as they will be generated from a role.role.j2.yaml file. i.e. role.role.j2.yaml will generate <service>-role.yaml config.role.j2.yaml will generate <service>-config.yaml Partial-Bug: #1626976 Change-Id: I614dc462fd7fc088b67634d489d8e7b68e7d4ab1
ight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * QEMU System Emulator
 *
 * Copyright (c) 2003-2008 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "qemu/osdep.h"
#include "net/slirp.h"


#ifndef _WIN32
#include <pwd.h>
#include <sys/wait.h>
#endif
#include "net/net.h"
#include "clients.h"
#include "hub.h"
#include "monitor/monitor.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
#include "slirp/libslirp.h"
#include "slirp/ip6.h"
#include "sysemu/char.h"
#include "qemu/cutils.h"

static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
{
    const char *p, *p1;
    int len;
    p = *pp;
    p1 = strchr(p, sep);
    if (!p1)
        return -1;
    len = p1 - p;
    p1++;
    if (buf_size > 0) {
        if (len > buf_size - 1)
            len = buf_size - 1;
        memcpy(buf, p, len);
        buf[len] = '\0';
    }
    *pp = p1;
    return 0;
}

/* slirp network adapter */

#define SLIRP_CFG_HOSTFWD 1
#define SLIRP_CFG_LEGACY  2

struct slirp_config_str {
    struct slirp_config_str *next;
    int flags;
    char str[1024];
    int legacy_format;
};

typedef struct SlirpState {
    NetClientState nc;
    QTAILQ_ENTRY(SlirpState) entry;
    Slirp *slirp;
#ifndef _WIN32
    char smb_dir[128];
#endif
} SlirpState;

static struct slirp_config_str *slirp_configs;
const char *legacy_tftp_prefix;
const char *legacy_bootp_filename;
static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
    QTAILQ_HEAD_INITIALIZER(slirp_stacks);

static int slirp_hostfwd(SlirpState *s, const char *redir_str,
                         int legacy_format);
static int slirp_guestfwd(SlirpState *s, const char *config_str,
                          int legacy_format);

#ifndef _WIN32
static const char *legacy_smb_export;

static int slirp_smb(SlirpState *s, const char *exported_dir,
                     struct in_addr vserver_addr);
static void slirp_smb_cleanup(SlirpState *s);
#else
static inline void slirp_smb_cleanup(SlirpState *s) { }
#endif

void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
{
    SlirpState *s = opaque;

    qemu_send_packet(&s->nc, pkt, pkt_len);
}

static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
    SlirpState *s = DO_UPCAST(SlirpState, nc, nc);

    slirp_input(s->slirp, buf, size);

    return size;
}

static void net_slirp_cleanup(NetClientState *nc)
{
    SlirpState *s = DO_UPCAST(SlirpState, nc, nc);

    slirp_cleanup(s->slirp);
    slirp_smb_cleanup(s);
    QTAILQ_REMOVE(&slirp_stacks, s, entry);
}

static NetClientInfo net_slirp_info = {
    .type = NET_CLIENT_OPTIONS_KIND_USER,
    .size = sizeof(SlirpState),
    .receive = net_slirp_receive,
    .cleanup = net_slirp_cleanup,
};

static int net_slirp_init(NetClientState *peer, const char *model,
                          const char *name, int restricted,
                          bool ipv4, const char *vnetwork, const char *vhost,
                          bool ipv6, const char *vprefix6, int vprefix6_len,
                          const char *vhost6,
                          const char *vhostname, const char *tftp_export,
                          const char *bootfile, const char *vdhcp_start,
                          const char *vnameserver, const char *vnameserver6,
                          const char *smb_export, const char *vsmbserver,
                          const char **dnssearch)
{
    /* default settings according to historic slirp */
    struct in_addr net  = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
    struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
    struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
    struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
    struct in_addr dns  = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
    struct in6_addr ip6_prefix;
    struct in6_addr ip6_host;
    struct in6_addr ip6_dns;
#ifndef _WIN32
    struct in_addr smbsrv = { .s_addr = 0 };
#endif
    NetClientState *nc;
    SlirpState *s;
    char buf[20];
    uint32_t addr;
    int shift;
    char *end;
    struct slirp_config_str *config;

    if (!ipv4 && (vnetwork || vhost || vnameserver)) {
        return -1;
    }

    if (!ipv6 && (vprefix6 || vhost6 || vnameserver6)) {
        return -1;
    }

    if (!ipv4 && !ipv6) {
        /* It doesn't make sense to disable both */
        return -1;
    }

    if (!tftp_export) {
        tftp_export = legacy_tftp_prefix;
    }
    if (!bootfile) {
        bootfile = legacy_bootp_filename;
    }

    if (vnetwork) {
        if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
            if (!inet_aton(vnetwork, &net)) {
                return -1;
            }
            addr = ntohl(net.s_addr);
            if (!(addr & 0x80000000)) {
                mask.s_addr = htonl(0xff000000); /* class A */
            } else if ((addr & 0xfff00000) == 0xac100000) {
                mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
            } else if ((addr & 0xc0000000) == 0x80000000) {
                mask.s_addr = htonl(0xffff0000); /* class B */
            } else if ((addr & 0xffff0000) == 0xc0a80000) {
                mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
            } else if ((addr & 0xffff0000) == 0xc6120000) {
                mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
            } else if ((addr & 0xe0000000) == 0xe0000000) {
                mask.s_addr = htonl(0xffffff00); /* class C */
            } else {
                mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
            }
        } else {
            if (!inet_aton(buf, &net)) {
                return -1;
            }
            shift = strtol(vnetwork, &end, 10);
            if (*end != '\0') {
                if (!inet_aton(vnetwork, &mask)) {
                    return -1;
                }
            } else if (shift < 4 || shift > 32) {
                return -1;
            } else {
                mask.s_addr = htonl(0xffffffff << (32 - shift));
            }
        }
        net.s_addr &= mask.s_addr;
        host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
        dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
        dns.s_addr  = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
    }

    if (vhost && !inet_aton(vhost, &host)) {
        return -1;
    }
    if ((host.s_addr & mask.s_addr) != net.s_addr) {
        return -1;
    }

    if (vnameserver && !inet_aton(vnameserver, &dns)) {
        return -1;
    }
    if ((dns.s_addr & mask.s_addr) != net.s_addr ||
        dns.s_addr == host.s_addr) {
        return -1;
    }

    if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
        return -1;
    }
    if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
        dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
        return -1;
    }

#ifndef _WIN32
    if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
        return -1;
    }
#endif

#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
    /* No inet_pton helper before Vista... */
    if (vprefix6) {
        /* Unsupported */
        return -1;
    }
    memset(&ip6_prefix, 0, sizeof(ip6_prefix));
    ip6_prefix.s6_addr[0] = 0xfe;
    ip6_prefix.s6_addr[1] = 0xc0;
#else
    if (!vprefix6) {
        vprefix6 = "fec0::";
    }
    if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
        return -1;
    }
#endif

    if (!vprefix6_len) {
        vprefix6_len = 64;
    }
    if (vprefix6_len < 0 || vprefix6_len > 126) {
        return -1;
    }

    if (vhost6) {
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
        return -1;
#else
        if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
            return -1;
        }
        if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
            return -1;
        }
#endif
    } else {
        ip6_host = ip6_prefix;
        ip6_host.s6_addr[15] |= 2;
    }

    if (vnameserver6) {
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
        return -1;
#else
        if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
            return -1;
        }
        if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
            return -1;
        }
#endif
    } else {
        ip6_dns = ip6_prefix;
        ip6_dns.s6_addr[15] |= 3;
    }


    nc = qemu_new_net_client(&net_slirp_info, peer, model, name);

    snprintf(nc->info_str, sizeof(nc->info_str),
             "net=%s,restrict=%s", inet_ntoa(net),
             restricted ? "on" : "off");

    s = DO_UPCAST(SlirpState, nc, nc);

    s->slirp = slirp_init(restricted, ipv4, net, mask, host,
                          ipv6, ip6_prefix, vprefix6_len, ip6_host,
                          vhostname, tftp_export, bootfile, dhcp,
                          dns, ip6_dns, dnssearch, s);
    QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);

    for (config = slirp_configs; config; config = config->next) {
        if (config->flags & SLIRP_CFG_HOSTFWD) {
            if (slirp_hostfwd(s, config->str,
                              config->flags & SLIRP_CFG_LEGACY) < 0)
                goto error;
        } else {
            if (slirp_guestfwd(s, config->str,
                               config->flags & SLIRP_CFG_LEGACY) < 0)
                goto error;
        }
    }
#ifndef _WIN32
    if (!smb_export) {
        smb_export = legacy_smb_export;
    }
    if (smb_export) {
        if (slirp_smb(s, smb_export, smbsrv) < 0)
            goto error;
    }
#endif

    return 0;

error:
    qemu_del_net_client(nc);
    return -1;
}

static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
                                const char *stack)
{

    if (vlan) {
        NetClientState *nc;
        nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
        if (!nc) {
            monitor_printf(mon, "unrecognized (vlan-id, stackname) pair\n");
            return NULL;
        }
        if (strcmp(nc->model, "user")) {
            monitor_printf(mon, "invalid device specified\n");
            return NULL;
        }
        return DO_UPCAST(SlirpState, nc, nc);
    } else {
        if (QTAILQ_EMPTY(&slirp_stacks)) {
            monitor_printf(mon, "user mode network stack not in use\n");
            return NULL;
        }
        return QTAILQ_FIRST(&slirp_stacks);
    }
}

void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
{
    struct in_addr host_addr = { .s_addr = INADDR_ANY };
    int host_port;
    char buf[256];
    const char *src_str, *p;
    SlirpState *s;
    int is_udp = 0;
    int err;
    const char *arg1 = qdict_get_str(qdict, "arg1");
    const char *arg2 = qdict_get_try_str(qdict, "arg2");
    const char *arg3 = qdict_get_try_str(qdict, "arg3");

    if (arg2) {
        s = slirp_lookup(mon, arg1, arg2);
        src_str = arg3;
    } else {
        s = slirp_lookup(mon, NULL, NULL);
        src_str = arg1;
    }
    if (!s) {
        return;
    }

    p = src_str;
    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
        goto fail_syntax;
    }

    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
        is_udp = 0;
    } else if (!strcmp(buf, "udp")) {
        is_udp = 1;
    } else {
        goto fail_syntax;
    }

    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
        goto fail_syntax;
    }
    if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
        goto fail_syntax;
    }

    host_port = atoi(p);

    err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port);

    monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
                   err ? "not found" : "removed");
    return;

 fail_syntax:
    monitor_printf(mon, "invalid format\n");
}

static int slirp_hostfwd(SlirpState *s, const char *redir_str,
                         int legacy_format)
{
    struct in_addr host_addr = { .s_addr = INADDR_ANY };
    struct in_addr guest_addr = { .s_addr = 0 };
    int host_port, guest_port;
    const char *p;
    char buf[256];
    int is_udp;
    char *end;

    p = redir_str;
    if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
        goto fail_syntax;
    }
    if (!strcmp(buf, "tcp") || buf[0] == '\0') {
        is_udp = 0;
    } else if (!strcmp(buf, "udp")) {
        is_udp = 1;
    } else {
        goto fail_syntax;
    }

    if (!legacy_format) {
        if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
            goto fail_syntax;
        }
        if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
            goto fail_syntax;
        }
    }

    if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
        goto fail_syntax;
    }
    host_port = strtol(buf, &end, 0);
    if (*end != '\0' || host_port < 1 || host_port > 65535) {
        goto fail_syntax;
    }

    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
        goto fail_syntax;
    }
    if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
        goto fail_syntax;
    }

    guest_port = strtol(p, &end, 0);
    if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
        goto fail_syntax;
    }

    if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
                          guest_port) < 0) {
        error_report("could not set up host forwarding rule '%s'",
                     redir_str);
        return -1;
    }
    return 0;

 fail_syntax:
    error_report("invalid host forwarding rule '%s'", redir_str);
    return -1;
}

void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
{
    const char *redir_str;
    SlirpState *s;
    const char *arg1 = qdict_get_str(qdict, "arg1");
    const char *arg2 = qdict_get_try_str(qdict, "arg2");
    const char *arg3 = qdict_get_try_str(qdict, "arg3");

    if (arg2) {
        s = slirp_lookup(mon, arg1, arg2);
        redir_str = arg3;
    } else {
        s = slirp_lookup(mon, NULL, NULL);
        redir_str = arg1;
    }
    if (s) {
        slirp_hostfwd(s, redir_str, 0);
    }

}

int net_slirp_redir(const char *redir_str)
{
    struct slirp_config_str *config;

    if (QTAILQ_EMPTY(&slirp_stacks)) {
        config = g_malloc(sizeof(*config));
        pstrcpy(config->str, sizeof(config->str), redir_str);
        config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
        config->next = slirp_configs;
        slirp_configs = config;
        return 0;
    }

    return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
}

#ifndef _WIN32

/* automatic user mode samba server configuration */
static void slirp_smb_cleanup(SlirpState *s)
{
    char cmd[128];
    int ret;

    if (s->smb_dir[0] != '\0') {
        snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
        ret = system(cmd);
        if (ret == -1 || !WIFEXITED(ret)) {
            error_report("'%s' failed.", cmd);
        } else if (WEXITSTATUS(ret)) {
            error_report("'%s' failed. Error code: %d",
                         cmd, WEXITSTATUS(ret));
        }
        s->smb_dir[0] = '\0';
    }
}

static int slirp_smb(SlirpState* s, const char *exported_dir,
                     struct in_addr vserver_addr)
{
    char smb_conf[128];
    char smb_cmdline[128];
    struct passwd *passwd;
    FILE *f;

    passwd = getpwuid(geteuid());
    if (!passwd) {
        error_report("failed to retrieve user name");
        return -1;
    }

    if (access(CONFIG_SMBD_COMMAND, F_OK)) {
        error_report("could not find '%s', please install it",
                     CONFIG_SMBD_COMMAND);
        return -1;
    }

    if (access(exported_dir, R_OK | X_OK)) {
        error_report("error accessing shared directory '%s': %s",
                     exported_dir, strerror(errno));
        return -1;
    }

    snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX");
    if (!mkdtemp(s->smb_dir)) {
        error_report("could not create samba server dir '%s'", s->smb_dir);
        s->smb_dir[0] = 0;
        return -1;
    }
    snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");

    f = fopen(smb_conf, "w");
    if (!f) {
        slirp_smb_cleanup(s);
        error_report("could not create samba server configuration file '%s'",
                     smb_conf);
        return -1;
    }
    fprintf(f,
            "[global]\n"
            "private dir=%s\n"
            "interfaces=127.0.0.1\n"
            "bind interfaces only=yes\n"
            "pid directory=%s\n"
            "lock directory=%s\n"
            "state directory=%s\n"
            "cache directory=%s\n"
            "ncalrpc dir=%s/ncalrpc\n"
            "log file=%s/log.smbd\n"
            "smb passwd file=%s/smbpasswd\n"
            "security = user\n"
            "map to guest = Bad User\n"
            "load printers = no\n"
            "printing = bsd\n"
            "disable spoolss = yes\n"
            "usershare max shares = 0\n"
            "[qemu]\n"
            "path=%s\n"
            "read only=no\n"
            "guest ok=yes\n"
            "force user=%s\n",
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            s->smb_dir,
            exported_dir,
            passwd->pw_name
            );
    fclose(f);

    snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -l %s -s %s",
             CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);

    if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 ||
        slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) {
        slirp_smb_cleanup(s);
        error_report("conflicting/invalid smbserver address");
        return -1;
    }
    return 0;
}

/* automatic user mode samba server configuration (legacy interface) */
int net_slirp_smb(const char *exported_dir)
{
    struct in_addr vserver_addr = { .s_addr = 0 };

    if (legacy_smb_export) {
        fprintf(stderr, "-smb given twice\n");
        return -1;
    }
    legacy_smb_export = exported_dir;
    if (!QTAILQ_EMPTY(&slirp_stacks)) {
        return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
                         vserver_addr);
    }
    return 0;
}

#endif /* !defined(_WIN32) */

struct GuestFwd {
    CharDriverState *hd;
    struct in_addr server;
    int port;
    Slirp *slirp;
};

static int guestfwd_can_read(void *opaque)
{
    struct GuestFwd *fwd = opaque;
    return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
}

static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
{
    struct GuestFwd *fwd = opaque;
    slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
}

static int slirp_guestfwd(SlirpState *s, const char *config_str,
                          int legacy_format)
{
    struct in_addr server = { .s_addr = 0 };
    struct GuestFwd *fwd;
    const char *p;
    char buf[128];
    char *end;
    int port;

    p = config_str;
    if (legacy_format) {
        if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
            goto fail_syntax;
        }
    } else {
        if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
            goto fail_syntax;
        }
        if (strcmp(buf, "tcp") && buf[0] != '\0') {
            goto fail_syntax;
        }
        if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
            goto fail_syntax;
        }
        if (buf[0] != '\0' && !inet_aton(buf, &server)) {
            goto fail_syntax;
        }
        if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
            goto fail_syntax;
        }
    }
    port = strtol(buf, &end, 10);
    if (*end != '\0' || port < 1 || port > 65535) {
        goto fail_syntax;
    }

    snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);

    if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) {
        if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) {
            error_report("conflicting/invalid host:port in guest forwarding "
                         "rule '%s'", config_str);
            return -1;
        }
    } else {
        fwd = g_new(struct GuestFwd, 1);
        fwd->hd = qemu_chr_new(buf, p, NULL);
        if (!fwd->hd) {
            error_report("could not open guest forwarding device '%s'", buf);
            g_free(fwd);
            return -1;
        }

        if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
            error_report("conflicting/invalid host:port in guest forwarding "
                         "rule '%s'", config_str);
            g_free(fwd);
            return -1;
        }
        fwd->server = server;
        fwd->port = port;
        fwd->slirp = s->slirp;

        qemu_chr_fe_claim_no_fail(fwd->hd);
        qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
                              NULL, fwd);
    }
    return 0;

 fail_syntax:
    error_report("invalid guest forwarding rule '%s'", config_str);
    return -1;
}

void hmp_info_usernet(Monitor *mon, const QDict *qdict)
{
    SlirpState *s;

    QTAILQ_FOREACH(s, &slirp_stacks, entry) {
        int id;
        bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0;
        monitor_printf(mon, "VLAN %d (%s):\n",
                       got_vlan_id ? id : -1,
                       s->nc.name);
        slirp_connection_info(s->slirp, mon);
    }
}

static void
net_init_slirp_configs(const StringList *fwd, int flags)
{
    while (fwd) {
        struct slirp_config_str *config;

        config = g_malloc0(sizeof(*config));
        pstrcpy(config->str, sizeof(config->str), fwd->value->str);
        config->flags = flags;
        config->next = slirp_configs;
        slirp_configs = config;

        fwd = fwd->next;
    }
}

static const char **slirp_dnssearch(const StringList *dnsname)
{
    const StringList *c = dnsname;
    size_t i = 0, num_opts = 0;
    const char **ret;

    while (c) {
        num_opts++;
        c = c->next;
    }

    if (num_opts == 0) {
        return NULL;
    }

    ret = g_malloc((num_opts + 1) * sizeof(*ret));
    c = dnsname;
    while (c) {
        ret[i++] = c->value->str;
        c = c->next;
    }
    ret[i] = NULL;
    return ret;
}

int net_init_slirp(const NetClientOptions *opts, const char *name,
                   NetClientState *peer, Error **errp)
{
    /* FIXME error_setg(errp, ...) on failure */
    struct slirp_config_str *config;
    char *vnet;
    int ret;
    const NetdevUserOptions *user;
    const char **dnssearch;
    bool ipv4 = true, ipv6 = true;

    assert(opts->type == NET_CLIENT_OPTIONS_KIND_USER);
    user = opts->u.user.data;

    if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
        (user->has_ipv4 && !user->ipv4)) {
        ipv4 = 0;
    }
    if ((user->has_ipv4 && user->ipv4 && !user->has_ipv6) ||
        (user->has_ipv6 && !user->ipv6)) {
        ipv6 = 0;
    }

    vnet = user->has_net ? g_strdup(user->net) :
           user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
           NULL;

    dnssearch = slirp_dnssearch(user->dnssearch);

    /* all optional fields are initialized to "all bits zero" */

    net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
    net_init_slirp_configs(user->guestfwd, 0);

    ret = net_slirp_init(peer, "user", name, user->q_restrict,
                         ipv4, vnet, user->host,
                         ipv6, user->ipv6_prefix, user->ipv6_prefixlen,
                         user->ipv6_host, user->hostname, user->tftp,
                         user->bootfile, user->dhcpstart,
                         user->dns, user->ipv6_dns, user->smb,
                         user->smbserver, dnssearch);

    while (slirp_configs) {
        config = slirp_configs;
        slirp_configs = config->next;
        g_free(config);
    }

    g_free(vnet);
    g_free(dnssearch);

    return ret;
}

int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
{
    if (strcmp(opts_list->name, "net") != 0 ||
        strncmp(optarg, "channel,", strlen("channel,")) != 0) {
        return 0;
    }

    error_report("The '-net channel' option is deprecated. "
                 "Please use '-netdev user,guestfwd=...' instead.");

    /* handle legacy -net channel,port:chr */
    optarg += strlen("channel,");

    if (QTAILQ_EMPTY(&slirp_stacks)) {
        struct slirp_config_str *config;

        config = g_malloc(sizeof(*config));
        pstrcpy(config->str, sizeof(config->str), optarg);
        config->flags = SLIRP_CFG_LEGACY;
        config->next = slirp_configs;
        slirp_configs = config;
        *ret = 0;
    } else {
        *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
    }

    return 1;
}