|
98 | 98 | required: false |
99 | 99 | type: list |
100 | 100 | elements: str |
| 101 | + interface_pci_id: |
| 102 | + description: |
| 103 | + List of inteface PCI device ID strings. |
| 104 | + PCI device ID needs to correspond to a named network interface. |
| 105 | + required: false |
| 106 | + type: list |
| 107 | + elements: str |
101 | 108 | icmp_block: |
102 | 109 | description: |
103 | 110 | List of ICMP type strings to block. |
|
205 | 212 |
|
206 | 213 | from distutils.version import LooseVersion |
207 | 214 | from ansible.module_utils.basic import AnsibleModule |
| 215 | +import re |
| 216 | +import os |
208 | 217 |
|
209 | 218 | try: |
210 | 219 | import firewall.config |
|
222 | 231 | except ImportError: |
223 | 232 | HAS_FIREWALLD = False |
224 | 233 |
|
| 234 | +try: |
| 235 | + from firewall.core.fw_nm import ( |
| 236 | + nm_is_imported, |
| 237 | + nm_get_connection_of_interface, |
| 238 | + nm_get_zone_of_connection, |
| 239 | + nm_set_zone_of_connection, |
| 240 | + nm_get_interfaces, |
| 241 | + nm_get_client, |
| 242 | + ) |
| 243 | + |
| 244 | + NM_IMPORTED = nm_is_imported() |
| 245 | +except ImportError: |
| 246 | + NM_IMPORTED = False |
| 247 | + |
| 248 | + |
| 249 | +PCI_REGEX = re.compile("[0-9a-fA-F]{4}:[0-9a-fA-F]{4}") |
| 250 | + |
| 251 | + |
| 252 | +def try_get_connection_of_interface(interface): |
| 253 | + try: |
| 254 | + return nm_get_connection_of_interface(interface) |
| 255 | + except Exception: |
| 256 | + return None |
| 257 | + |
| 258 | + |
| 259 | +def try_set_zone_of_interface(module, _zone, interface): |
| 260 | + if NM_IMPORTED: |
| 261 | + connection = try_get_connection_of_interface(interface) |
| 262 | + if connection is not None: |
| 263 | + if _zone == "": |
| 264 | + zone_string = "the default zone" |
| 265 | + else: |
| 266 | + zone_string = _zone |
| 267 | + if _zone == nm_get_zone_of_connection(connection): |
| 268 | + module.log( |
| 269 | + msg="The interface is under control of NetworkManager and already bound to '%s'" |
| 270 | + % zone_string |
| 271 | + ) |
| 272 | + elif not module.check_mode: |
| 273 | + nm_set_zone_of_connection(_zone, connection) |
| 274 | + return True |
| 275 | + return False |
| 276 | + |
| 277 | + |
| 278 | +# Above: adapted from firewall-cmd source code |
| 279 | + |
225 | 280 |
|
226 | 281 | def create_service(module, fw, service): |
227 | 282 | if not module.check_mode: |
@@ -269,6 +324,46 @@ def handle_interface_permanent( |
269 | 324 | fw_settings.addInterface(item) |
270 | 325 |
|
271 | 326 |
|
| 327 | +pci_ids = None |
| 328 | + |
| 329 | + |
| 330 | +def get_interface_pci(): |
| 331 | + pci_dict = {} |
| 332 | + for interface in nm_get_interfaces(): |
| 333 | + # udi/device/[vendor, device] |
| 334 | + interface_ids = [] |
| 335 | + device_udi = nm_get_client().get_device_by_iface(interface).get_udi() |
| 336 | + device_path = os.path.join(device_udi, "device") |
| 337 | + for field in ["vendor", "device"]: |
| 338 | + with open(os.path.join(device_path, field)) as _file: |
| 339 | + interface_ids.append(_file.readline().strip(" \n")[2:]) |
| 340 | + interface_ids = ":".join(interface_ids) |
| 341 | + if interface_ids not in pci_dict: |
| 342 | + pci_dict[interface_ids] = [interface] |
| 343 | + else: |
| 344 | + pci_dict[interface_ids].append(interface) |
| 345 | + return pci_dict |
| 346 | + |
| 347 | + |
| 348 | +def parse_pci_id(module, item): |
| 349 | + if PCI_REGEX.search(item): |
| 350 | + global pci_ids |
| 351 | + if not pci_ids: |
| 352 | + pci_ids = get_interface_pci() |
| 353 | + |
| 354 | + interface_name = pci_ids.get(item) |
| 355 | + if interface_name: |
| 356 | + return interface_name |
| 357 | + |
| 358 | + module.warn(msg="No network interfaces found with PCI device ID %s" % item) |
| 359 | + else: |
| 360 | + module.fail_json( |
| 361 | + msg="PCI id %s does not match format: XXXX:XXXX (X = hexadecimal number)" |
| 362 | + % item |
| 363 | + ) |
| 364 | + return [] |
| 365 | + |
| 366 | + |
272 | 367 | def parse_port(module, item): |
273 | 368 | _port, _protocol = item.split("/") |
274 | 369 | if _protocol is None: |
@@ -447,6 +542,9 @@ def main(): |
447 | 542 | rich_rule=dict(required=False, type="list", elements="str", default=[]), |
448 | 543 | source=dict(required=False, type="list", elements="str", default=[]), |
449 | 544 | interface=dict(required=False, type="list", elements="str", default=[]), |
| 545 | + interface_pci_id=dict( |
| 546 | + required=False, type="list", elements="str", default=[] |
| 547 | + ), |
450 | 548 | icmp_block=dict(required=False, type="list", elements="str", default=[]), |
451 | 549 | icmp_block_inversion=dict(required=False, type="bool", default=None), |
452 | 550 | timeout=dict(required=False, type="int", default=0), |
@@ -532,6 +630,10 @@ def main(): |
532 | 630 | elif destination_ipv6 and ip_type == "ipv6": |
533 | 631 | module.fail_json(msg="cannot have more than one destination ipv6") |
534 | 632 | interface = module.params["interface"] |
| 633 | + for _interface in module.params["interface_pci_id"]: |
| 634 | + for interface_name in parse_pci_id(module, _interface): |
| 635 | + if interface_name not in interface: |
| 636 | + interface.append(interface_name) |
535 | 637 | icmp_block = module.params["icmp_block"] |
536 | 638 | icmp_block_inversion = module.params["icmp_block_inversion"] |
537 | 639 | timeout = module.params["timeout"] |
@@ -1091,21 +1193,27 @@ def exception_handler(exception_message): |
1091 | 1193 | if not module.check_mode: |
1092 | 1194 | fw.changeZoneOfInterface(zone, item) |
1093 | 1195 | changed = True |
1094 | | - if permanent and not fw_settings.queryInterface(item): |
1095 | | - if not module.check_mode: |
1096 | | - handle_interface_permanent( |
1097 | | - zone, item, fw_zone, fw_settings, fw, fw_offline, module |
1098 | | - ) |
1099 | | - changed = True |
| 1196 | + if permanent: |
| 1197 | + if try_set_zone_of_interface(module, zone, item): |
| 1198 | + changed = True |
| 1199 | + elif not fw_settings.queryInterface(item): |
| 1200 | + if not module.check_mode: |
| 1201 | + handle_interface_permanent( |
| 1202 | + zone, item, fw_zone, fw_settings, fw, fw_offline, module |
| 1203 | + ) |
| 1204 | + changed = True |
1100 | 1205 | elif state == "disabled": |
1101 | 1206 | if runtime and fw.queryInterface(zone, item): |
1102 | 1207 | if not module.check_mode: |
1103 | 1208 | fw.removeInterface(zone, item) |
1104 | 1209 | changed = True |
1105 | | - if permanent and fw_settings.queryInterface(item): |
1106 | | - if not module.check_mode: |
1107 | | - fw_settings.removeInterface(item) |
1108 | | - changed = True |
| 1210 | + if permanent: |
| 1211 | + if try_set_zone_of_interface(module, "", item): |
| 1212 | + changed = True |
| 1213 | + elif fw_settings.queryInterface(item): |
| 1214 | + if not module.check_mode: |
| 1215 | + fw_settings.removeInterface(item) |
| 1216 | + changed = True |
1109 | 1217 |
|
1110 | 1218 | # icmp_block |
1111 | 1219 | for item in icmp_block: |
|
0 commit comments