1# 2# Copyright 2006-2009, 2013 Red Hat, Inc. 3# 4# This work is licensed under the GNU GPLv2 or later. 5# See the COPYING file in the top-level directory. 6 7import os 8import random 9 10from .device import Device 11from ..logger import log 12from ..xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty 13 14 15def _random_mac(conn): 16 """Generate a random MAC address. 17 18 00-16-3E allocated to xensource 19 52-54-00 used by qemu/kvm 20 21 The OUI list is available at https://standards.ieee.org/regauth/oui/oui.txt. 22 23 The remaining 3 fields are random, with the first bit of the first 24 random field set 0. 25 26 @return: MAC address string 27 """ 28 29 if conn.is_qemu(): 30 oui = [0x52, 0x54, 0x00] 31 else: 32 # Xen 33 oui = [0x00, 0x16, 0x3E] 34 35 mac = oui + [ 36 random.randint(0x00, 0xff), 37 random.randint(0x00, 0xff), 38 random.randint(0x00, 0xff)] 39 return ':'.join(["%02x" % x for x in mac]) 40 41 42def _default_route(): 43 route_file = "/proc/net/route" 44 if not os.path.exists(route_file): # pragma: no cover 45 log.debug("route_file=%s does not exist", route_file) 46 return None 47 48 for line in open(route_file): 49 info = line.split() 50 if len(info) != 11: # pragma: no cover 51 log.debug("Unexpected field count=%s when parsing %s", 52 len(info), route_file) 53 break 54 55 try: 56 route = int(info[1], 16) 57 if route == 0: 58 return info[0] 59 except ValueError: 60 continue 61 62 return None # pragma: no cover 63 64 65def _host_default_bridge(): 66 dev = _default_route() 67 if not dev: 68 return None # pragma: no cover 69 70 # New style peth0 == phys dev, eth0 == bridge, eth0 == default route 71 if os.path.exists("/sys/class/net/%s/bridge" % dev): 72 return dev # pragma: no cover 73 74 # Old style, peth0 == phys dev, eth0 == netloop, xenbr0 == bridge, 75 # vif0.0 == netloop attached, eth0 == default route 76 try: 77 defn = int(dev[-1]) 78 except Exception: # pragma: no cover 79 defn = -1 80 81 if (defn >= 0 and 82 os.path.exists("/sys/class/net/peth%d/brport" % defn) and 83 os.path.exists("/sys/class/net/xenbr%d/bridge" % defn)): 84 return "xenbr%d" # pragma: no cover 85 return None 86 87 88# Cache the host default bridge lookup. It can change over the lifetime 89# of a virt-manager run, but that should be rare, and this saves us 90# possibly spamming logs if host lookup goes wrong 91_HOST_DEFAULT_BRIDGE = -1 92 93 94def _default_bridge(conn): 95 if conn.is_remote(): 96 return None 97 98 global _HOST_DEFAULT_BRIDGE 99 if _HOST_DEFAULT_BRIDGE == -1: 100 try: 101 ret = _host_default_bridge() 102 except Exception: # pragma: no cover 103 log.debug("Error getting host default bridge", exc_info=True) 104 ret = None 105 _HOST_DEFAULT_BRIDGE = ret 106 107 ret = _HOST_DEFAULT_BRIDGE 108 if conn.in_testsuite(): 109 ret = "testsuitebr0" 110 return ret 111 112 113class _VirtualPort(XMLBuilder): 114 XML_NAME = "virtualport" 115 116 type = XMLProperty("./@type") 117 managerid = XMLProperty("./parameters/@managerid", is_int=True) 118 typeid = XMLProperty("./parameters/@typeid", is_int=True) 119 typeidversion = XMLProperty("./parameters/@typeidversion", is_int=True) 120 instanceid = XMLProperty("./parameters/@instanceid") 121 profileid = XMLProperty("./parameters/@profileid") 122 interfaceid = XMLProperty("./parameters/@interfaceid") 123 124 125class DeviceInterface(Device): 126 XML_NAME = "interface" 127 128 TYPE_BRIDGE = "bridge" 129 TYPE_VIRTUAL = "network" 130 TYPE_USER = "user" 131 TYPE_VHOSTUSER = "vhostuser" 132 TYPE_ETHERNET = "ethernet" 133 TYPE_DIRECT = "direct" 134 135 @staticmethod 136 def generate_mac(conn): 137 """ 138 Generate a random MAC that doesn't conflict with any VMs on 139 the connection. 140 """ 141 if conn.fake_conn_predictable(): 142 # Testing hack 143 return "00:11:22:33:44:55" 144 145 for ignore in range(256): 146 mac = _random_mac(conn) 147 try: 148 DeviceInterface.check_mac_in_use(conn, mac) 149 return mac 150 except RuntimeError: # pragma: no cover 151 continue 152 153 log.debug( # pragma: no cover 154 "Failed to generate non-conflicting MAC") 155 return None # pragma: no cover 156 157 @staticmethod 158 def check_mac_in_use(conn, searchmac): 159 """ 160 Raise RuntimeError if the passed mac conflicts with a defined VM 161 """ 162 if not searchmac: 163 return 164 165 vms = conn.fetch_all_domains() 166 for vm in vms: 167 for nic in vm.devices.interface: 168 nicmac = nic.macaddr or "" 169 if nicmac.lower() == searchmac.lower(): 170 raise RuntimeError( 171 _("The MAC address '%s' is in use " 172 "by another virtual machine.") % searchmac) 173 174 @staticmethod 175 def default_bridge(conn): 176 """ 177 Return the bridge virt-install would use as a default value, 178 if one is setup on the host 179 """ 180 return _default_bridge(conn) 181 182 183 ############### 184 # XML helpers # 185 ############### 186 187 def _get_source(self): 188 """ 189 Convenience function, try to return the relevant <source> value 190 per the network type. 191 """ 192 if self.type == self.TYPE_VIRTUAL: 193 return self.network 194 if self.type == self.TYPE_BRIDGE: 195 return self.bridge 196 if self.type == self.TYPE_DIRECT: 197 return self.source_dev 198 return None 199 def _set_source(self, newsource): 200 """ 201 Convenience function, try to set the relevant <source> value 202 per the network type 203 """ 204 self.bridge = None 205 self.network = None 206 self.source_dev = None 207 208 if self.type == self.TYPE_VIRTUAL: 209 self.network = newsource 210 elif self.type == self.TYPE_BRIDGE: 211 self.bridge = newsource 212 elif self.type == self.TYPE_DIRECT: 213 self.source_dev = newsource 214 source = property(_get_source, _set_source) 215 216 217 ################## 218 # XML properties # 219 ################## 220 221 _XML_PROP_ORDER = [ 222 "bridge", "network", "source_dev", "source_type", "source_path", 223 "source_mode", "portgroup", "macaddr", "target_dev", "model", 224 "virtualport", "filterref", "rom_bar", "rom_file", "mtu_size"] 225 226 bridge = XMLProperty("./source/@bridge") 227 network = XMLProperty("./source/@network") 228 source_dev = XMLProperty("./source/@dev") 229 230 virtualport = XMLChildProperty(_VirtualPort, is_single=True) 231 type = XMLProperty("./@type") 232 trustGuestRxFilters = XMLProperty("./@trustGuestRxFilters", is_yesno=True) 233 234 macaddr = XMLProperty("./mac/@address") 235 236 source_type = XMLProperty("./source/@type") 237 source_path = XMLProperty("./source/@path") 238 source_mode = XMLProperty("./source/@mode") 239 portgroup = XMLProperty("./source/@portgroup") 240 model = XMLProperty("./model/@type") 241 target_dev = XMLProperty("./target/@dev") 242 filterref = XMLProperty("./filterref/@filter") 243 link_state = XMLProperty("./link/@state") 244 245 driver_name = XMLProperty("./driver/@name") 246 driver_queues = XMLProperty("./driver/@queues", is_int=True) 247 248 rom_bar = XMLProperty("./rom/@bar", is_onoff=True) 249 rom_file = XMLProperty("./rom/@file") 250 251 mtu_size = XMLProperty("./mtu/@size", is_int=True) 252 253 254 ############# 255 # Build API # 256 ############# 257 258 def set_default_source(self): 259 if self.conn.is_qemu_unprivileged() or self.conn.is_test(): 260 self.type = self.TYPE_USER 261 return 262 263 nettype = DeviceInterface.TYPE_BRIDGE 264 source = DeviceInterface.default_bridge(self.conn) 265 if not source: 266 nettype = DeviceInterface.TYPE_VIRTUAL 267 source = "default" 268 269 self.type = nettype 270 self.source = source 271 272 273 ################## 274 # Default config # 275 ################## 276 277 @staticmethod 278 def default_model(guest): 279 if not guest.os.is_hvm(): 280 return None 281 if guest.supports_virtionet(): 282 return "virtio" 283 if guest.os.is_q35(): 284 return "e1000e" 285 if not guest.os.is_x86(): 286 return None 287 288 prefs = ["e1000", "rtl8139", "ne2k_pci", "pcnet"] 289 supported_models = guest.osinfo.supported_netmodels() 290 for pref in prefs: 291 if pref in supported_models: 292 return pref 293 return "e1000" 294 295 def set_defaults(self, guest): 296 if not self.type: 297 self.type = self.TYPE_BRIDGE 298 if not self.macaddr: 299 self.macaddr = self.generate_mac(self.conn) 300 if self.type == self.TYPE_BRIDGE and not self.bridge: 301 self.bridge = _default_bridge(self.conn) 302 if not self.model: 303 self.model = self.default_model(guest) 304