# -- coding: utf-8 -- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import logging from netaddr import IPAddress, IPNetwork import subprocess import time from . import CsHelper from .CsDatabag import CsDataBag from .CsApp import CsApache, CsDnsmasq, CsPasswdSvc from .CsRoute import CsRoute from .CsRule import CsRule from .CsStaticRoutes import CsStaticRoutes VRRP_TYPES = ['guest'] class CsAddress(CsDataBag): def compare(self): for dev in CsDevice('', self.config).list(): ip = CsIP(dev, self.config) ip.compare(self.dbag) def get_interfaces(self): interfaces = [] for dev in self.dbag: if dev == "id": continue for ip in self.dbag[dev]: interfaces.append(CsInterface(ip, self.config)) return interfaces def get_guest_if(self): """ Return CsInterface object for the lowest in use guest interface """ guest_interface = None lowest_device = 1000 for interface in self.get_interfaces(): if interface.is_guest() and interface.is_added(): device = interface.get_device() device_suffix = int(''.join([digit for digit in device if digit.isdigit()])) if device_suffix < lowest_device: lowest_device = device_suffix guest_interface = interface logging.debug("Guest interface will be set on device '%s' and IP '%s'" % (guest_interface.get_device(), guest_interface.get_ip())) return guest_interface def get_guest_ip(self): """ Return the ip of the first guest interface For use with routers not vpcrouters """ ip = self.get_guest_if() if ip: return ip.get_ip() return None def get_guest_netmask(self): """ Return the netmask of the first guest interface For use with routers not vpcrouters """ ip = self.get_guest_if() if ip: return ip.get_netmask() return "255.255.255.0" def needs_vrrp(self, o): """ Returns if the ip needs to be managed by keepalived or not """ if "nw_type" in o and o['nw_type'] in VRRP_TYPES: return True return False def get_control_if(self): """ Return the address object that has the control interface """ for interface in self.get_interfaces(): if interface.is_control(): return interface return None def process(self): for dev in self.dbag: if dev == "id": continue ip = CsIP(dev, self.config) for address in self.dbag[dev]: ip.setAddress(address) logging.info("Address found in DataBag ==> %s" % address) if not address['add'] and not ip.configured(): logging.info("Skipping %s as the add flag is set to %s " % (address['public_ip'], address['add'])) continue if ip.configured(): logging.info( "Address %s on device %s already configured", ip.ip(), dev) ip.post_configure(address) else: logging.info( "Address %s on device %s not configured", ip.ip(), dev) if CsDevice(dev, self.config).waitfordevice(): ip.configure(address) class CsInterface: """ Hold one single ip """ def __init__(self, o, config): self.address = o self.config = config def get_ip(self): return self.get_attr("public_ip") def get_ip6(self): if not self.config.is_vpc(): return self.config.cmdline().get_dev_ip6prelen(self.get_device()) if self.is_public(): return self.config.guestnetwork().get_router_ip6prelen() elif self.is_guest(): return self.config.guestnetwork().get_dev_ip6prelen(self.get_device()) return self.get_attr("public_ip6") def get_network(self): return self.get_attr("network") def get_netmask(self): return self.get_attr("netmask") def get_gateway(self): if self.config.is_vpc() or not self.is_guest(): return self.get_attr("gateway") else: return self.config.cmdline().get_guest_gw() def get_gateway6(self): if self.config.is_vpc(): if self.is_public(): return self.config.guestnetwork().get_router_ip6gateway() elif self.is_guest(): return self.config.guestnetwork().get_dev_ip6gateway(self.get_device()) else: if self.is_public(): return self.config.cmdline().get_ip6gateway() elif self.is_guest(): return self.config.cmdline().get_guest_ip6gateway() return self.get_attr("gateway6") def ip_in_subnet(self, ip): ipo = IPAddress(ip) net = IPNetwork("%s/%s" % (self.get_ip(), self.get_size())) return ipo in net def get_gateway_cidr(self): return "%s/%s" % (self.get_gateway(), self.get_size()) def get_gateway6_cidr(self): gw6 = self.get_gateway6() cidr6_size = self.get_cidr6_size() if not gw6 or not cidr6_size or gw6 == "ERROR" or cidr6_size == "ERROR": return False return "%s/%s" % (self.get_gateway6(), self.get_cidr6_size()) def get_size(self): """ Return the network size in bits (24, 16, 8 etc) """ return self.get_attr("size") def get_cidr6_size(self): if self.config.is_vpc() and self.is_guest(): return self.config.guestnetwork().get_dev_ip6cidr(self.get_device()) elif not self.config.is_vpc() and self.is_guest(): return self.config.cmdline().get_guest_ip6cidr_size() return self.get_attr("size6") def get_device(self): return self.get_attr("device") def get_cidr(self): return self.get_attr("cidr") def get_broadcast(self): return self.get_attr("broadcast") def get_attr(self, attr): if attr in self.address: return self.address[attr] else: return "ERROR" def needs_vrrp(self): """ Returns if the ip needs to be managed by keepalived or not """ if "nw_type" in self.address and self.address['nw_type'] in VRRP_TYPES: return True return False def is_control(self): if "nw_type" in self.address and self.address['nw_type'] in ['control']: return True return False def is_guest(self): if "nw_type" in self.address and self.address['nw_type'] in ['guest']: return True return False def is_public(self): if "nw_type" in self.address and self.address['nw_type'] in ['public']: return True return False def is_private_gateway(self): if "is_private_gateway" in self.address: return self.address['is_private_gateway'] return False def is_added(self): return self.get_attr("add") def to_str(self): return self.address class CsDevice: """ Configure Network Devices """ def __init__(self, dev, config): self.devlist = [] self.dev = dev self.buildlist() self.table = '' self.tableNo = '' if dev != '': self.tableNo = dev[3:] self.table = "Table_%s" % dev self.fw = config.get_fw() self.cl = config.cmdline() def configure_rp(self): """ Configure Reverse Path Filtering """ filename = "/proc/sys/net/ipv4/conf/%s/rp_filter" % self.dev CsHelper.updatefile(filename, "1\n", "w") def buildlist(self): """ List all available network devices on the system """ self.devlist = [] for line in open('/proc/net/dev'): vals = line.lstrip().split(':') if (not vals[0].startswith("eth")): continue self.devlist.append(vals[0]) def waitfordevice(self, timeout=2): count = 0 while count < timeout: if self.dev in self.devlist: return True time.sleep(1) count += 1 self.buildlist() logging.error( "Device %s cannot be configured - device was not found", self.dev) return False def list(self): return self.devlist class CsIP: DEFAULT_MTU = '1500' def __init__(self, dev, config): self.dev = dev self.dnum = hex(100 + int(dev[3:])) self.iplist = {} self.address = {} self.list() self.fw = config.get_fw() self.cl = config.cmdline() self.config = config self.nft_ipv4_fw = config.get_nft_ipv4_fw() self.nft_ipv4_acl = config.get_nft_ipv4_acl() def setAddress(self, address): self.address = address def getAddress(self): return self.address def configure(self, address): # When "add" is false, it means that the IP has to be removed. if address["add"]: try: logging.info("Configuring address %s on device %s", self.ip(), self.dev) cmd = "ip addr add dev %s %s brd +" % (self.dev, self.ip()) CsHelper.execute(cmd) cmd = "ifconfig %s mtu %s" % (self.dev, self.mtu()) CsHelper.execute(cmd) except Exception as e: logging.info("Exception occurred ==> %s" % e) self.post_configure(address) else: # delete method performs post_configure, so no need to call post_configure here self.delete(self.ip()) def post_configure(self, address): """ The steps that must be done after a device is configured """ route = CsRoute() if not self.get_type() in ["control"]: route.add_table(self.dev) CsRule(self.dev).addMark() interfaces = [CsInterface(address, self.config)] CsHelper.reconfigure_interfaces(self.cl, interfaces) if self.get_type() in ['public'] and not self.config.is_routed(): self.set_mark() if 'gateway' in self.address: self.arpPing() CsRpsrfs(self.dev).enable() self.post_config_change("add") '''For isolated/redundant and dhcpsrvr routers, call this method after the post_config is complete ''' if self.get_type() in ["control"]: self.setup_router_control() if self.config.is_vpc() or self.cl.is_redundant(): # The code looks redundant here, but we actually have to cater for routers and # VPC routers in a different manner. Please do not remove this block otherwise # The VPC default route will be broken. if self.get_type() in ["public"] and address["device"] == CsHelper.PUBLIC_INTERFACES[self.cl.get_type()]: gateway = str(address["gateway"]) route.add_defaultroute(gateway) else: # once we start processing public ip's we need to verify there # is a default route and add if needed if self.cl.get_gateway(): route.add_defaultroute(self.cl.get_gateway()) if self.config.is_router() and self.cl.get_ip6gateway(): route.add_defaultroute_v6(self.cl.get_ip6gateway()) def set_mark(self): cmd = "-A PREROUTING -i %s -m state --state NEW -j CONNMARK --set-xmark %s/0xffffffff" % \ (self.getDevice(), self.dnum) self.fw.append(["mangle", "", cmd]) def get_type(self): """ Return the type of the IP guest control public """ if "nw_type" in self.address: return self.address['nw_type'] return "unknown" def get_ip_address(self): """ Return ip address if known """ if "public_ip" in self.address: return self.address['public_ip'] return "unknown" def mtu(self): logging.info(self.address) if "mtu" in self.address: return self.address['mtu'] return CsIP.DEFAULT_MTU def setup_router_control_routing(self): if self.config.is_vpc(): self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': 'iifname "eth0" tcp dport 3922 ct state established,new counter accept'}) else: self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': 'iifname "eth1" tcp dport 3922 ct state established,new counter accept'}) def setup_router_control(self): if self.config.is_routed(): self.setup_router_control_routing() return if self.config.is_vpc(): return self.fw.append( ["filter", "", "-A FW_OUTBOUND -m state --state RELATED,ESTABLISHED -j ACCEPT"]) self.fw.append( ["filter", "", "-A INPUT -i eth1 -p tcp -m tcp --dport 3922 -m state --state NEW,ESTABLISHED -j ACCEPT"]) self.fw.append(["filter", "", "-P INPUT DROP"]) self.fw.append(["filter", "", "-P FORWARD DROP"]) def fw_router(self): if self.config.is_vpc() or self.config.is_routed(): return self.fw.append(["mangle", "front", "-A PREROUTING " + "-m state --state RELATED,ESTABLISHED " + "-j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff"]) self.fw.append(["mangle", "front", "-A POSTROUTING " + "-p udp -m udp --dport 68 -j CHECKSUM --checksum-fill"]) if self.get_type() in ["public"]: self.fw.append(["mangle", "front", "-A PREROUTING " + "-d %s/32 -j VPN_%s" % (self.address['public_ip'], self.address['public_ip'])]) self.fw.append(["mangle", "front", "-A PREROUTING " + "-d %s/32 -j FIREWALL_%s" % (self.address['public_ip'], self.address['public_ip'])]) self.fw.append(["mangle", "front", "-A FIREWALL_%s " % self.address['public_ip'] + "-m state --state RELATED,ESTABLISHED -j RETURN"]) self.fw.append(["mangle", "", "-A FIREWALL_%s -j DROP" % self.address['public_ip']]) self.fw.append(["mangle", "", "-I VPN_%s -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.address['public_ip']]) self.fw.append(["mangle", "", "-A VPN_%s -j RETURN" % self.address['public_ip']]) self.fw.append( ["", "front", "-A FORWARD -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "front", "-A INPUT -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "front", "-A OUTPUT -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -i eth0 -o %s" % (self.dev, self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -i %s -o eth0" % (self.dev, self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -o %s ! -i eth0 -p tcp" % (self.dev, self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -i %s ! -o eth0 -p tcp" % (self.dev, self.dev)]) self.fw.append( ["nat", "", "-A POSTROUTING -o %s -j SNAT --to-source %s" % (self.dev, self.cl.get_eth2_ip())]) self.fw.append(["mangle", "", "-A PREROUTING -i %s -m state --state NEW " % self.dev + "-j CONNMARK --set-xmark %s/0xffffffff" % self.dnum]) self.fw.append(["filter", "", "-A FORWARD -i %s -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.dev]) self.fw.append(["filter", "", "-A FORWARD -i eth0 -o %s -j FW_OUTBOUND" % self.dev]) self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -d 225.0.0.50/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.dev]) self.fw.append(["filter", "", "-A INPUT -p icmp -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"]) if self.get_type() in ["guest"]: guestNetworkCidr = self.address['network'] self.fw.append( ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev]) self.fw.append( ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 80 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A FORWARD -i %s -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT" % self.dev]) self.fw.append( ["filter", "", "-A FORWARD -i %s -o %s -m state --state NEW -j ACCEPT" % (self.dev, self.dev)]) self.fw.append( ["filter", "", "-A FORWARD -i eth0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT"]) self.fw.append(['', 'front', '-A FORWARD -j NETWORK_STATS']) self.fw.append(['', 'front', '-A INPUT -j NETWORK_STATS']) self.fw.append(['', 'front', '-A OUTPUT -j NETWORK_STATS']) self.fw.append(['', '', '-A NETWORK_STATS -i eth0 -o eth2']) self.fw.append(['', '', '-A NETWORK_STATS -i eth2 -o eth0']) self.fw.append(['', '', '-A NETWORK_STATS -o eth2 ! -i eth0 -p tcp']) self.fw.append(['', '', '-A NETWORK_STATS -i eth2 ! -o eth0 -p tcp']) def fw_vpcrouter(self): if not self.config.is_vpc() or self.config.is_routed(): return self.fw.append(["filter", "", "-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT"]) if self.get_type() in ["guest"]: self.fw.append(["mangle", "front", "-A PREROUTING " + " -i %s -m state --state RELATED,ESTABLISHED " % self.dev + "-j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff"]) guestNetworkCidr = self.address['network'] self.fw.append(["filter", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % (guestNetworkCidr, self.dev, self.dev)]) self.fw.append( ["filter", "front", "-A ACL_INBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev]) self.fw.append( ["filter", "front", "-A ACL_INBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev]) self.fw.append( ["filter", "", "-A ACL_INBOUND_%s -j DROP" % self.dev]) self.fw.append( ["mangle", "front", "-A ACL_OUTBOUND_%s -d 225.0.0.50/32 -j ACCEPT" % self.dev]) self.fw.append( ["mangle", "front", "-A ACL_OUTBOUND_%s -d 224.0.0.18/32 -j ACCEPT" % self.dev]) self.fw.append( ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 67 -j ACCEPT" % self.dev]) self.fw.append( ["mangle", "front", "-A POSTROUTING " + "-p udp -m udp --dport 68 -j CHECKSUM --checksum-fill"]) self.fw.append( ["filter", "", "-A INPUT -i %s -p udp -m udp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 53 -s %s -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 80 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 443 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append( ["filter", "", "-A INPUT -i %s -p tcp -m tcp --dport 8080 -s %s -m state --state NEW -j ACCEPT" % (self.dev, guestNetworkCidr)]) self.fw.append(["mangle", "", "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" % (self.dev, guestNetworkCidr, self.address['gateway'], self.dev)]) if self.is_private_gateway(): self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % (self.address['network'], self.dev, self.dev)]) self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (self.address['network'], self.dev)]) self.fw.append(["filter", "", "-A ACL_INBOUND_%s -j DROP" % self.dev]) self.fw.append(["mangle", "", "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" % (self.dev, self.address['network'], self.address['gateway'], self.dev)]) self.fw.append(["mangle", "front", "-A PREROUTING -s %s -d %s -m state --state NEW -j MARK --set-xmark %s/0xffffffff" % (self.cl.get_vpccidr(), self.address['network'], hex(100 + int(self.dev[3:])))]) static_routes = CsStaticRoutes("staticroutes", self.config) if static_routes: for item in static_routes.get_bag(): if item == "id": continue static_route = static_routes.get_bag()[item] if static_route['ip_address'] == self.address['public_ip'] and not static_route['revoke']: self.fw.append(["mangle", "", "-A PREROUTING -m state --state NEW -i %s -s %s ! -d %s/32 -j ACL_OUTBOUND_%s" % (self.dev, static_route['network'], static_route['ip_address'], self.dev)]) self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" % (static_route['network'], self.dev, self.dev)]) self.fw.append(["filter", "front", "-A FORWARD -d %s -o %s -m state --state RELATED,ESTABLISHED -j ACCEPT" % (static_route['network'], self.dev)]) if self.address["source_nat"]: self.fw.append(["nat", "front", "-A POSTROUTING -o %s -j SNAT --to-source %s" % (self.dev, self.address['public_ip'])]) if self.get_gateway() == self.get_ip_address(): for inf, addresses in self.config.address().dbag.items(): if not inf.startswith("eth"): continue for address in addresses: if "nw_type" in address and address["nw_type"] == "guest" and address["add"]: self.fw.append(["filter", "front", "-A FORWARD -s %s -d %s -j ACL_INBOUND_%s" % (address["network"], self.address["network"], self.dev)]) self.fw.append(["filter", "front", "-A FORWARD -s %s -d %s -j ACL_INBOUND_%s" % (self.address["network"], address["network"], address["device"])]) # Accept packet from private gateway if VPC VR is used as gateway self.fw.append(["filter", "", "-A FORWARD -s %s ! -d %s -j ACCEPT" % (self.address['network'], self.address['network'])]) if self.get_type() in ["public"]: self.fw.append( ["mangle", "", "-A FORWARD -j VPN_STATS_%s" % self.dev]) self.fw.append( ["mangle", "", "-A VPN_STATS_%s -o %s -m mark --mark 0x525/0xffffffff" % (self.dev, self.dev)]) self.fw.append( ["mangle", "", "-A VPN_STATS_%s -i %s -m mark --mark 0x524/0xffffffff" % (self.dev, self.dev)]) self.fw.append( ["", "front", "-A FORWARD -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "front", "-A INPUT -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "front", "-A OUTPUT -j NETWORK_STATS_%s" % self.dev]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -s %s -o %s" % (self.dev, self.cl.get_vpccidr(), self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s -d %s -i %s" % (self.dev, self.cl.get_vpccidr(), self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s ! -s %s -o %s -p tcp" % (self.dev, self.cl.get_vpccidr(), self.dev)]) self.fw.append( ["", "", "-A NETWORK_STATS_%s ! -d %s -i %s -p tcp" % (self.dev, self.cl.get_vpccidr(), self.dev)]) self.fw.append(["filter", "", "-A INPUT -d 224.0.0.18/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -d 225.0.0.50/32 -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -p icmp -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i lo -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -i eth0 -p tcp -m tcp --dport 3922 -m state --state NEW,ESTABLISHED -j ACCEPT"]) self.fw.append(["filter", "", "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT"]) self.fw.append(["filter", "", "-P INPUT DROP"]) self.fw.append(["filter", "", "-P FORWARD DROP"]) def fw_router_routing(self): if self.config.is_vpc() or not self.config.is_routed(): return # Add default rules for INPUT chain self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname lo counter accept"}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname eth2 ct state related,established counter accept"}) # Add default rules for FORWARD chain self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', 'rule': 'iifname "eth2" oifname "eth0" ct state related,established counter accept'}) self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', 'rule': 'iifname "eth0" oifname "eth0" ct state new counter accept'}) self.nft_ipv4_fw.append({'type': "", 'chain': 'FORWARD', 'rule': 'iifname "eth0" oifname "eth0" ct state related,established counter accept'}) if self.get_type() in ["guest"]: guestNetworkCidr = self.address['network'] self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ct state related,established counter accept" % self.dev}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s udp dport 67 counter accept" % self.dev}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s udp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 80 ct state new counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 443 ct state new counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_fw.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 8080 ct state new counter accept" % (self.dev, guestNetworkCidr)}) def fw_vpcrouter_routing(self): if not self.config.is_vpc() or not self.config.is_routed(): return # Add default rules for INPUT chain for VPC self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname lo counter accept"}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname eth1 ct state related,established counter accept"}) if self.get_type() in ["guest"]: guestNetworkCidr = self.address['network'] self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ct state related,established counter accept" % self.dev}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s udp dport 67 counter accept" % self.dev}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s udp dport 53 counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 80 ct state new counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 443 ct state new counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'INPUT', 'rule': "iifname %s ip saddr %s tcp dport 8080 ct state new counter accept" % (self.dev, guestNetworkCidr)}) # Add default rules for FORWARD chain for VPC tiers self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', 'rule': "oifname %s ip daddr %s ct state related,established counter accept" % (self.dev, guestNetworkCidr)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', 'rule': "iifname %s oifname %s ct state new counter accept" % (self.dev, self.dev)}) self.nft_ipv4_acl.append({'type': "", 'chain': 'FORWARD', 'rule': "iifname %s oifname %s ct state related,established counter accept" % (self.dev, self.dev)}) def post_config_change(self, method): route = CsRoute() tableName = "Table_" + self.dev if method == "add": if not self.config.is_vpc(): if self.get_type() in ["public"]: route.set_route("table %s throw %s proto static" % (tableName, self.config.address().dbag['eth0'][0]['network'])) route.set_route("table %s throw %s proto static" % (tableName, self.config.address().dbag['eth1'][0]['network'])) # add 'default via gateway' rule in the device specific routing table if "gateway" in self.address and self.address["gateway"] and self.address["gateway"] != "None": route.add_route(self.dev, self.address["gateway"]) if "network" in self.address and self.address["network"]: route.add_network_route(self.dev, str(self.address["network"])) if self.get_type() in ["public"]: CsRule(self.dev).addRule("from " + str(self.address["network"])) if self.config.is_vpc(): if self.get_type() in ["public"] and "gateway" in self.address and self.address["gateway"] and self.address["gateway"] != "None": route.add_route(self.dev, self.address["gateway"]) for inf, addresses in self.config.address().dbag.items(): if not inf.startswith("eth"): continue for address in addresses: if "nw_type" in address and address["nw_type"] == "guest": route.add_network_route(self.dev, str(address["network"])) if self.get_type() in ["public"]: CsRule(self.dev).addRule("from " + str(self.address["network"])) route.add_network_route(self.dev, str(self.address["network"])) CsHelper.execute("sudo ip route flush cache") elif method == "delete": # treat the last IP to be dis-associated with interface as special case to clean up the routing rules if self.get_type() in ["public"] and (not self.config.is_vpc()) and (len(self.iplist) == 0): CsHelper.execute("sudo ip rule delete table " + tableName) CsHelper.execute("sudo ip route flush table " + tableName) CsHelper.execute("sudo ip route flush cache") CsRule(self.dev).delMark() self.fw_router() self.fw_vpcrouter() self.fw_router_routing() self.fw_vpcrouter_routing() cmdline = self.config.cmdline() # On deletion nw_type will no longer be known if self.get_type() in ('guest'): if self.config.is_vpc() or self.config.is_router(): CsDevice(self.dev, self.config).configure_rp() logging.error( "Not able to setup source-nat for a regular router yet") if (self.config.has_dns() or self.config.is_dhcp()) and self.config.expose_dns(): logging.info("Making dns publicly available") dns = CsDnsmasq(self) dns.add_firewall_rules() else: logging.info("Not making dns publicly available") if self.config.has_metadata(): if method == "add": app = CsApache(self) app.setup() elif method == "delete": app = CsApache(self) app.remove() # If redundant then this is dealt with # by the primary backup functions if not cmdline.is_redundant(): if method == "add": CsPasswdSvc(self.address['public_ip']).start() elif method == "delete": CsPasswdSvc(self.address['public_ip']).stop() elif cmdline.is_primary(): if method == "add": CsPasswdSvc(self.get_gateway() + "," + self.address['public_ip']).start() elif method == "delete": CsPasswdSvc(self.get_gateway() + "," + self.address['public_ip']).stop() if self.get_type() == "public" and self.config.is_vpc() and method == "add" and not self.config.is_routed(): if self.address["source_nat"]: vpccidr = cmdline.get_vpccidr() self.fw.append( ["filter", 3, "-A FORWARD -s %s ! -d %s -j ACCEPT" % (vpccidr, vpccidr)]) self.fw.append( ["nat", "", "-A POSTROUTING -j SNAT -o %s --to-source %s" % (self.dev, self.address['public_ip'])]) elif cmdline.get_source_nat_ip() and not self.is_private_gateway(): self.fw.append( ["nat", "", "-A POSTROUTING -j SNAT -o %s --to-source %s" % (self.dev, cmdline.get_source_nat_ip())]) def list(self): self.iplist = {} cmd = ("ip addr show dev " + self.dev) for i in CsHelper.execute(cmd): vals = i.lstrip().split() if (vals[0] == 'inet'): cidr = vals[1] self.iplist[cidr] = self.dev def configured(self): if self.address['cidr'] in list(self.iplist.keys()): return True return False def needs_vrrp(self): """ Returns if the ip needs to be managed by keepalived or not """ if "nw_type" in self.address and self.address['nw_type'] in VRRP_TYPES: return True return False def is_public(self): if "nw_type" in self.address and self.address['nw_type'] in ['public']: return True return False def is_private_gateway(self): if "is_private_gateway" in self.address: return self.address['is_private_gateway'] return False def ip(self): return str(self.address['cidr']) def getDevice(self): return self.dev def hasIP(self, ip): return ip in list(self.address.values()) def arpPing(self): cmd = "arping -c 1 -I %s -A -U -s %s %s" % ( self.dev, self.address['public_ip'], self.address['gateway']) if not self.cl.is_redundant() and (not self.address['gateway'] or self.address['gateway'] == "None"): cmd = "arping -c 1 -I %s -A -U %s" % (self.dev, self.address['public_ip']) CsHelper.execute2(cmd, False) # Delete any ips that are configured but not in the bag def compare(self, bag): if len(self.iplist) > 0 and (self.dev not in list(bag.keys()) or len(bag[self.dev]) == 0): # Remove all IPs on this device logging.info( "Will remove all configured addresses on device %s", self.dev) self.delete("all") app = CsApache(self) app.remove() # This condition should not really happen but did :) # It means an apache file got orphaned after a guest network address # was deleted if len(self.iplist) == 0 and (self.dev not in list(bag.keys()) or len(bag[self.dev]) == 0): app = CsApache(self) app.remove() for ip in self.iplist: found = False if self.dev in list(bag.keys()): for address in bag[self.dev]: self.setAddress(address) if (self.hasIP(ip) or self.is_guest_gateway(address, ip)) and address["add"]: logging.debug("The IP address in '%s' will be configured" % address) found = True if not found: self.delete(ip) def get_gateway(self): interface = CsInterface(self.address, self.config) return interface.get_gateway() def is_guest_gateway(self, bag, ip): """ Exclude the vrrp maintained addresses on a redundant router """ interface = CsInterface(bag, self.config) if not self.config.cl.is_redundant(): return False rip = ip.split('/')[0] logging.info("Checking if cidr is a gateway for rVPC. IP ==> %s / device ==> %s", ip, self.dev) gw = interface.get_gateway() logging.info("Interface has the following gateway ==> %s", gw) if bag['nw_type'] == "guest" and rip == gw: return True return False def delete(self, ip): remove = [] if ip == "all": logging.info("Removing addresses from device %s", self.dev) remove = list(self.iplist.keys()) else: remove.append(ip) for ip in remove: cmd = "ip addr del dev %s %s" % (self.dev, ip) subprocess.call(cmd, shell=True) logging.info("Removed address %s from device %s", ip, self.dev) self.post_config_change("delete") class CsRpsrfs: """ Configure rpsrfs if there is more than one cpu """ def __init__(self, dev): self.dev = dev def enable(self): if not self.inKernel(): return cpus = self.cpus() if cpus < 2: return val = format((1 << cpus) - 1, "x") filename = "/sys/class/net/%s/queues/rx-0/rps_cpus" % (self.dev) CsHelper.updatefile(filename, val, "w+") CsHelper.updatefile( "/proc/sys/net/core/rps_sock_flow_entries", "256", "w+") filename = "/sys/class/net/%s/queues/rx-0/rps_flow_cnt" % (self.dev) CsHelper.updatefile(filename, "256", "w+") logging.debug("rpsfr is configured for %s cpus" % (cpus)) def inKernel(self): try: open('/etc/rpsrfsenable') except IOError: logging.debug("rpsfr is not present in the kernel") return False else: logging.debug("rpsfr is present in the kernel") return True def cpus(self): count = 0 for line in open('/proc/cpuinfo'): if "processor" not in line: continue count += 1 if count < 2: logging.debug("Single CPU machine") return count