Beispiel #1
0
    def on(self, target, component):
        if not commonl.prctl_cap_get_effective() & 1 << 12:
            # If we don't have network setting privilege,
            # don't even go there
            # CAP_NET_ADMIN is 12 (from /usr/include/linux/prctl.h.
            #
            # Fail here (upon use) instead of during server startup,
            # because maybe we don't really care about it and we have
            # a default configuration with the thargets that would
            # need this.
            raise RuntimeError("daemon lacks CAP_NET_ADMIN: unable to"
                               " add networking capabilities ")
        if_name, ic_name = self._component_validate(target, component)

        if not commonl.if_present(f"b{ic_name}"):
            target.log.info(f"{ic_name}: assuming network off since netif "
                            f"b{ic_name} is not present")
            return

        commonl.if_remove_maybe(if_name)  # ensure no leftovers
        subprocess.check_call(["ip", "tuntap", "add", if_name, "mode", "tap"],
                              stderr=subprocess.STDOUT)
        subprocess.check_call(
            ["ip", "link", "set", if_name, "master", "b" + ic_name],
            stderr=subprocess.STDOUT)
        # promisc on: needed so we can wireshark in
        subprocess.check_call(
            ["ip", "link", "set", if_name, "promisc", "on", "up"],
            stderr=subprocess.STDOUT)
        # We don't assign IP addresses here -- we leave it for the
        # client; if we do, for example QEMU won't work as it is just
        # used to associate the interface
        target.fsdb.set(component, if_name)
Beispiel #2
0
    def _qemu_preexec_nw(self):
        # This is called by subprocess.Popen after spawning to run
        # qemu from tt_qemu.power_on_do() right before spawning Qemu
        # for us.
        #
        # See doc block on top of this class and on :class:`vlan_pci`
        # for why file descriptor 0.
        #
        # We will find out which is the index of the TAP device
        # assigned to this, created on _image_power_on_pre and open
        # file desctriptor 0 to it, then leave it open for Qemu to
        # tap into it.
        count = 0
        for ic_name, ic_kws in list(self.tags.get('interconnects', {}).items()):
            if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws:
                continue
            if count > 0:
                raise NotImplementedError(
                    "QEMU Networking cannot implement "
                    "multiple networks at this time "
                    "(when trying to connec to %s)" % ic_name)
            kws = dict(ic_kws)
            kws.update(self.kws)
            kws['ic_name'] = ic_name
            if not commonl.if_present("_b%s" % ic_name):
                self.log.warning("network %s powered off? networking "
                                 "disabled" % ic_name)
                # If the network is not powered up, skip it
                # FIXME: replace with it calling vlan_pci.something()
                # that brings up the basic interface (_bICNAME) so
                # that once we power the network, it works
                continue

            tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws)
            assign = 0
            # Need to wait for udev to reconfigure this for us to have
            # access; this is done by a udev rule installed
            # (/usr/lib/udev/rules.d/80-ttbd.rules)
            tapdevname = "/dev/tap%d" % tapindex
            count = 1
            top = 4
            while not os.access(tapdevname, os.R_OK | os.W_OK):
                if count >= top:
                    msg = "%s: timed out waiting for udev to set " \
                        "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \
                        % tapdevname
                    self.log.error(msg)
                    raise RuntimeError(msg)
                time.sleep(0.25)
                count += 1
            fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0)
            if fd != assign:
                # there, reassign it to fd @assign
                os.dup2(fd, assign)
                os.close(fd)
                # leave fd assign open for QEMU!
            count += 1
Beispiel #3
0
    def _qemu_preexec_nw(self):
        # This is called by subprocess.Popen after spawning to run
        # qemu from tt_qemu.power_on_do() right before spawning Qemu
        # for us.
        #
        # See doc block on top of this class and on :class:`vlan_pci`
        # for why file descriptor 0.
        #
        # We will find out which is the index of the TAP device
        # assigned to this, created on _image_power_on_pre and open
        # file desctriptor 0 to it, then leave it open for Qemu to
        # tap into it.
        count = 0
        for ic_name, ic_kws in self.tags.get('interconnects', {}).iteritems():
            if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws:
                continue
            if count > 0:
                raise NotImplementedError(
                    "QEMU Networking cannot implement "
                    "multiple networks at this time "
                    "(when trying to connec to %s)" % ic_name)
            kws = dict(ic_kws)
            kws.update(self.kws)
            kws['ic_name'] = ic_name
            if not commonl.if_present("_b%s" % ic_name):
                self.log.warning("network %s powered off? networking "
                                 "disabled" % ic_name)
                # If the network is not powered up, skip it
                # FIXME: replace with it calling vlan_pci.something()
                # that brings up the basic interface (_bICNAME) so
                # that once we power the network, it works
                continue

            tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws)
            assign = 0
            # Need to wait for udev to reconfigure this for us to have
            # access; this is done by a udev rule installed
            # (/usr/lib/udev/rules.d/80-ttbd.rules)
            tapdevname = "/dev/tap%d" % tapindex
            count = 1
            top = 4
            while not os.access(tapdevname, os.R_OK | os.W_OK):
                if count >= top:
                    msg = "%s: timed out waiting for udev to set " \
                        "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \
                        % tapdevname
                    self.log.error(msg)
                    raise RuntimeError(msg)
                time.sleep(0.25)
                count += 1
            fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0)
            if fd != assign:
                # there, reassign it to fd @assign
                os.dup2(fd, assign)
                os.close(fd)
                # leave fd assign open for QEMU!
            count += 1
Beispiel #4
0
    def on(self, target, component):
        if_name, ic_name = self._component_validate(target, component)

        kws = dict(
            id=target.id,
            ic_name=ic_name,
            if_name=if_name,
        )
        ic_data = target.tags['interconnects'][ic_name]
        try:
            kws['ipv4_addr'] = ic_data['ipv4_addr']
            kws['ipv4_prefix_len'] = ic_data['ipv4_prefix_len']
            kws['ipv6_addr'] = ic_data['ipv6_addr']
            kws['ipv6_prefix_len'] = ic_data['ipv6_prefix_len']
            kws['mac_addr'] = ic_data['mac_addr']
        except KeyError as e:
            raise ValueError(
                "%s: can't create TAP interface:"
                " target '%s' declares connection to interconnect '%s'"
                " but is missing field '%s'" %
                (component, target.id, ic_name, str(e)))

        if not commonl.if_present("_b%(ic_name)s" % kws):
            target.log.info(
                "%(ic_name)s: assuming network off since netif "
                "_b%(ic_name)s is not present", kws)
            return

        commonl.if_remove_maybe(if_name)  # ensure no leftovers
        subprocess.check_call(
            "ip link add "
            "  link _b%(ic_name)s"  # created by the interconnect
            "  name %(if_name)s"
            "  address %(mac_addr)s"
            "  up"
            "  type macvtap mode bridge" % kws,
            shell=True,
            stderr=subprocess.STDOUT)
        subprocess.check_call(
            "ip link set %(if_name)s"
            "  promisc on "  # needed so we can wireshark in
            "  up" % kws,
            shell=True,
            stderr=subprocess.STDOUT)
        # We don't assign IP addresses here -- we leave it for the
        # client; if we do, for example QEMU won't work as it is just
        # used to associate the interface
        target.fsdb.set(component, if_name)
Beispiel #5
0
    def _power_on_pre_nw(self):
        # We need the __init__ part doing it earlier because remember,
        # these might be running different processes, and the basic
        # self.qemu_cmdline array has to be initialized so we can
        # find the actual binary being used.
        kws = dict(self.kws)
        # Get fresh values for these keys
        for key in list(self.fsdb.keys()):
            if key.startswith("qemu-"):
                kws[key] = self.fsdb.get(key)

        # Setup network stuff, create virtual tap interfaces
        for ic_name, ic_kws in list(self.tags.get('interconnects', {}).items()):
            if 'ipv4_addr' in ic_kws or 'ipv6_addr' in ic_kws:
                _kws = dict(kws)
                _kws.update(ic_kws)
                _kws['ic_name'] = ic_name
                # QEMU device ident only allows a-zA-Z0-9-, starting
                # with letter
                _kws['ident'] = "id" + self._r_ident.sub("", _kws['mac_addr'])
                # CAP_NET_ADMIN is 12 (from /usr/include/linux/prctl.h
                if not commonl.prctl_cap_get_effective() & 1 << 12:
                    # If we don't have network setting privilege,
                    # don't even go there
                    self.log.warning("daemon lacks CAP_NET_ADMIN: unable to "
                                     "add networking capabilities ")
                    continue

                if not commonl.if_present("_b%s" % ic_name):
                    self.log.warning("network %s powered off? networking "
                                     "disabled" % ic_name)
                    # If the network is not powered up, skip it
                    # FIXME: replace with it calling vlan_pci.something()
                    # that brings up the basic interface (_bICNAME) so
                    # that once we power the network, it works
                    continue

                commonl.if_remove_maybe("t%s%s" % (ic_name, self.id))
                subprocess.check_call(
                    "ip link add "
                    "  link _b%(ic_name)s "
                    "  name t%(ic_name)s%(id)s"
                    "  address %(mac_addr)s"
                    "  up"
                    "  type macvtap mode bridge; "
                    "ip link set t%(ic_name)s%(id)s"
                    "  promisc on "
                    "  up" % _kws, shell = True)

                # Add to the command line
                # note model=virtio WORKS with QEMU's BIOS OVMW_CODE.fd
                self.qemu_cmdline_append += \
                    " -net nic,id=%(ident)s,model=virtio,macaddr=%(mac_addr)s" \
                    " -net tap,fd=0,name=%(ident)s" % _kws

            # Add a nat/host interface?
            if ic_name == 'nat_host':
                raise RuntimeError("NAT configuration is currently broken")
                # we hardcode the MAC address of the NAT ethernet
                # interface, which we'll also add in the configuration
                # for systemd-network in /etc/systemd/network for it
                # to do DHCP. There is only one of those interfaces
                # per virtual host, so it will never conflict with
                # anything and we don't use the form of addressing in
                # any MAC we generate.
                mac_addr = "02:01:01:01:01:01"
                self.qemu_cmdline_append += \
                    " -net nic,name=nat_host,model=virtio,macaddr=%s" \
                    " -net user,id=nat_host,net=192.168.200.0/24,dhcpstart=192.168.200.10 " \
                     % mac_addr

        # If no network interfaces we added, remove the default
        # networking QEMU does
        if "-net" not in self.qemu_cmdline \
           and "-net" not in self.qemu_cmdline_append:
            self.qemu_cmdline_append += "-net none "
Beispiel #6
0
    def _power_on_pre_nw(self):
        # We need the __init__ part doing it earlier because remember,
        # these might be running different processes, and the basic
        # self.qemu_cmdline array has to be initialized so we can
        # find the actual binary being used.
        kws = dict(self.kws)
        # Get fresh values for these keys
        for key in self.fsdb.keys():
            if key.startswith("qemu-"):
                kws[key] = self.fsdb.get(key)

        # Setup network stuff, create virtual tap interfaces
        for ic_name, ic_kws in self.tags.get('interconnects', {}).iteritems():
            if 'ipv4_addr' in ic_kws or 'ipv6_addr' in ic_kws:
                _kws = dict(kws)
                _kws.update(ic_kws)
                _kws['ic_name'] = ic_name
                # QEMU device ident only allows a-zA-Z0-9-, starting
                # with letter
                _kws['ident'] = "id" + self._r_ident.sub("", _kws['mac_addr'])
                # CAP_NET_ADMIN is 12 (from /usr/include/linux/prctl.h
                if not commonl.prctl_cap_get_effective() & 1 << 12:
                    # If we don't have network setting privilege,
                    # don't even go there
                    self.log.warning("daemon lacks CAP_NET_ADMIN: unable to "
                                     "add networking capabilities ")
                    continue

                if not commonl.if_present("_b%s" % ic_name):
                    self.log.warning("network %s powered off? networking "
                                     "disabled" % ic_name)
                    # If the network is not powered up, skip it
                    # FIXME: replace with it calling vlan_pci.something()
                    # that brings up the basic interface (_bICNAME) so
                    # that once we power the network, it works
                    continue

                commonl.if_remove_maybe("t%s%s" % (ic_name, self.id))
                subprocess.check_call(
                    "ip link add "
                    "  link _b%(ic_name)s "
                    "  name t%(ic_name)s%(id)s"
                    "  address %(mac_addr)s"
                    "  up"
                    "  type macvtap mode bridge; "
                    "ip link set t%(ic_name)s%(id)s"
                    "  promisc on "
                    "  up" % _kws, shell = True)

                # Add to the command line
                # note model=virtio WORKS with QEMU's BIOS OVMW_CODE.fd
                self.qemu_cmdline_append += \
                    " -net nic,id=%(ident)s,model=virtio,macaddr=%(mac_addr)s" \
                    " -net tap,fd=0,name=%(ident)s" % _kws

            # Add a nat/host interface?
            if ic_name == 'nat_host':
                raise RuntimeError("NAT configuration is currently broken")
                # we hardcode the MAC address of the NAT ethernet
                # interface, which we'll also add in the configuration
                # for systemd-network in /etc/systemd/network for it
                # to do DHCP. There is only one of those interfaces
                # per virtual host, so it will never conflict with
                # anything and we don't use the form of addressing in
                # any MAC we generate.
                mac_addr = "02:01:01:01:01:01"
                self.qemu_cmdline_append += \
                    " -net nic,name=nat_host,model=virtio,macaddr=%s" \
                    " -net user,id=nat_host,net=192.168.200.0/24,dhcpstart=192.168.200.10 " \
                     % mac_addr

        # If no network interfaces we added, remove the default
        # networking QEMU does
        if "-net" not in self.qemu_cmdline \
           and "-net" not in self.qemu_cmdline_append:
            self.qemu_cmdline_append += "-net none "
Beispiel #7
0
    def on(self, target, _component):
        # Bring up the lower network interface; lower is called
        # whatever (if it is a physical device) or _bNAME; bring it
        # up, make it promiscuous
        mode = self._get_mode(target)
        if mode == 'vlan':
            # our lower is a physical device, our upper is a device
            # which till tag for eth vlan %(vlan)
            ifname = commonl.if_find_by_mac(target.tags['mac_addr'],
                                            physical=True)
            if not commonl.if_present("b%(id)s" % target.kws):
                # Do create the new interface only if not already
                # created, otherwise daemons that are already running
                # will stop operating
                # This function might be being called to restablish a
                # half baked operating state.
                kws = dict(target.kws)
                kws['ifname'] = ifname
                subprocess.check_call(
                    "/usr/sbin/ip link add"
                    " link %(ifname)s name b%(id)s"
                    " type vlan id %(vlan)s"
                    #" protocol VLAN_PROTO"
                    #" reorder_hdr on|off"
                    #" gvrp on|off mvrp on|off loose_binding on|off"
                    % kws,
                    shell=True)
                subprocess.check_call(  # bring lower up
                    "/usr/sbin/ip link set dev %s up promisc on" % ifname,
                    shell=True)
        elif mode == 'physical':
            ifname = commonl.if_find_by_mac(target.tags['mac_addr'])
            subprocess.check_call(  # bring lower up
                "/usr/sbin/ip link set dev %s up promisc on" % ifname,
                shell=True)
            self._if_rename(target)
        elif mode == 'virtual':
            # We create a bridge, to serve as lower
            if not commonl.if_present("b%(id)s" % target.kws):
                # Do create the new interface only if not already
                # created, otherwise daemons that are already running
                # will stop operating
                # This function might be being called to restablish a
                # half baced operating state.
                commonl.if_remove_maybe("b%(id)s" % target.kws)
                subprocess.check_call("/usr/sbin/ip link add"
                                      "  name b%(id)s"
                                      "  type bridge" % target.kws,
                                      shell=True)
                subprocess.check_call(  # bring lower up
                    "/usr/sbin/ip link set"
                    "  dev b%(id)s"
                    "  up promisc on" % target.kws,
                    shell=True)
        else:
            raise AssertionError("Unknown mode %s" % mode)

        # Configure the IP addresses for the top interface
        subprocess.check_call(  # clean up existing address
            "/usr/sbin/ip add flush dev b%(id)s " % target.kws,
            shell=True)
        subprocess.check_call(  # add IPv6
            # if this fails, check Network Manager hasn't disabled ipv6
            # sysctl -a | grep disable_ipv6 must show all to 0
            "/usr/sbin/ip addr add"
            "  %(ipv6_addr)s/%(ipv6_prefix_len)s dev b%(id)s " % target.kws,
            shell=True)
        subprocess.check_call(  # add IPv4
            "/usr/sbin/ip addr add"
            "  %(ipv4_addr)s/%(ipv4_prefix_len)d"
            "  dev b%(id)s" % target.kws,
            shell=True)

        # Bring up the top interface, which sets up ther outing
        subprocess.check_call(
            "/usr/sbin/ip link set dev b%(id)s up promisc on" % target.kws,
            shell=True)

        target.fsdb.set('power_state', 'on')

        # Start tcpdump on the network?
        #
        # The value of the tcpdump property, if not None, is the
        # filename we'll capture to.
        tcpdump = target.fsdb.get('tcpdump')
        if tcpdump:
            assert not os.path.sep in tcpdump \
                and tcpdump != "" \
                and tcpdump != os.path.pardir \
                and tcpdump != os.path.curdir, \
                "Bad filename for TCP dump capture '%s' specified as " \
                " value to property *tcpdump*: must not include" % tcpdump
            # per ttbd:make_ticket(), colon splits the real username
            # from the ticket
            owner = target.owner_get().split(":")[0]
            assert owner, "BUG? target not owned on power on?"
            capfile = os.path.join(target.files_path, owner, tcpdump)
            # Because it is in the user's area,
            # we assume the user knows what he is doing to overwrite it,
            # so we'll remove any first
            commonl.rm_f(capfile)
            pidfile = os.path.join(target.state_dir, "tcpdump.pid")
            logfile = os.path.join(target.state_dir, "tcpdump.log")
            cmdline = [
                "/usr/sbin/tcpdump", "-U", "-i",
                "b%(id)s" % target.kws, "-w", capfile
            ]
            try:
                logf = open(logfile, "a")
                target.log.info("Starting tcpdump with: %s", " ".join(cmdline))
                p = subprocess.Popen(cmdline,
                                     shell=False,
                                     cwd=target.state_dir,
                                     close_fds=True,
                                     stdout=logf,
                                     stderr=subprocess.STDOUT)
            except OSError as e:
                raise RuntimeError("tcpdump failed to start: %s" % e)
            ttbl.daemon_pid_add(p.pid)  # FIXME: race condition if it died?
            with open(pidfile, "w") as pidfilef:
                pidfilef.write("%d" % p.pid)

            pid = commonl.process_started(  # Verify it started
                pidfile,
                "/usr/sbin/tcpdump",
                verification_f=os.path.exists,
                verification_f_args=(capfile, ),
                timeout=20,
                tag="tcpdump",
                log=target.log)
            if pid == None:
                raise RuntimeError("tcpdump failed to start after 5s")