mirror of
https://github.com/apache/cloudstack
synced 2026-08-09 16:13:13 +00:00
This PR contains 3 features - IPv4 Static Routing (Routed mode) #9346 Design document: https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=306153967 - AS Numbers Management #9410 Design Document: https://cwiki.apache.org/confluence/display/CLOUDSTACK/BGP+AS+Numbers+Management - Dynamic routing Design Document: https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=315492858 - Document: https://github.com/apache/cloudstack-documentation/pull/419 Rename nsx mode to routing mode by ``` git grep -l nsx_mode |xargs sed -i "s/nsx_mode/routing_mode/g" git grep -l nsxmode |xargs sed -i "s/nsxmode/routingmode/g" git grep -l nsxMode |xargs sed -i "s/nsxMode/routingMode/g" git grep -l NsxMode |xargs sed -i "s/NsxMode/RoutingMode/g" ``` - re-organize sql changes - fix NPE as rules do not have public ip - fix missing destination cidr in ingress rules - disable network usage for routed network - fix DB exception as network_id is -1 during network creation - apply ingress/egress routing rules - VR changes to configure nft rules for isolated network - VR: setup nft rule for control network - VR: flush all iptables rules - fix NPE which is because ingress rules do not have public ip associated - fix dest cidr is missing in nft tables - add ip4 routing and ip4 routes to list network and list vpc response - fix ingress rule is missing when vr is restarted - fix icmp types in nft rules - add tab to manage routing firewall rules - fix ingress rules are not applied when VR is restarted - add default rules in FORWARD chain - fix create vpc offerings - fix public ip is not assigned to vpc - fix network offering is not listed when create vpc tier - add is_routing to boot args of vpc vr - remove table ip4_firewall in vpc vr - release or remove subnet when remove a network - implemenent fw_vpcrouter_routing - fix wrong ip familty when flush ipv4 rules - fix acl rules are not applied due to wrong version (should be 6 which means ip6 rules are removed) - add default rules for vpc tiers so that tcp connections (e.g. ssh) work - append policy rules after default rules - remove /usr/local/cloud/systemvm/ in routers - throw an exception when allocate subnet with cidrsize - fix some TODOs - add new parameters to update API - return type Ipv4GuestSubnetNetworkMap when get or create subnet - fix firewall rules are broken - add domain_id and account_id to db - add domain/account/project to ipv4 subnet response - create ipv4 subnet for domain/account/project - check conflict when update ipv4 subnet - ui changes - add parent subnet to response - add list for ipv4 subnet - implement some methods - fix list subnets for guest networks by zoneid - UI changes - fix delete ipv4 subnet for network - fix ipv4 subnet is set to zone guest network cidr if cidrsize is specified - add zone info to response if parent subnet is null but network is not - fix gateway/cidr is not set when create network with cidrsize - fix order of nft rules in the VRs * Routed v24 - add classes in marvin base.py * Routed v25 - add test_01_subnet_zone - fix dedicate to domain/account failure - list subnets for network by keyword and subnet * Routed v26: implement subnet auto-allocation - add utils for split ip ranges into small subnets - add utils to get start/end ip of a cidr - implement subnet auto-generation - add global settings * Routed 27: add subnet for VPC - add db column for vpc_id - add db record for vpc - remove db record when delete a vpc - add checkConflicts methods - remove duplicated settings - check ipv4 cidr when create subnet * Routed v28: update smoke tests - update test_ipv4_routing.py - search subnets by networkid * Routed 29: fix vpc and add more tests - fix createnetwork in vpc - add vpc id/name to response - fix zone id/name are not displayed in some cases - add smoke test for vpc - add smoke tests for failed cases - add smoke test for connectivity checks - marvin: add "-q" to ssh command * Routed 31: ui and smoke tests - UI: add link to network in list view - add nftables rules check in VRs * Routed 32: add chain OUTPUT and more rules - fix the issue 80/443/8080 is not reachable from VR itself ``` 2024-06-27 10:21:52,121 INFO Executing: systemctl start cloud-password-server@172.31.1.1 2024-06-27 10:21:52,128 INFO Service cloud-password-server@172.31.1.1 start 2024-06-27 10:21:52,129 INFO Executing: ps aux 2024-06-27 10:24:02,175 ERROR Failed to update password server due to: <urlopen error [Errno 110] Connection timed out> ``` * Routed: fix dns search from VMs in Isolated networks * Routed: fix VPC dns issue due to gateway IP is missing in cloud.conf This is caused by NSX integration, and fixed by https://github.com/apache/cloudstack/pull/9102/ * Routed: rename routing_mode to network_mode * Routed: replace centos5.5 template in smoke test as dhclient does not work in the vms // this does not work refer to https://dominikrys.com/posts/disable-udp-checksum-validation/#ignoring-udp-checksums-with-nftables and https://forum.openwrt.org/t/udp-checksum-with-nftables/161522/11 the vm should have checksum offloading disabled * Routed: fix smoke test due to wrong cidrlist of egress rules and missing ingress rule from VR * PR 9346: fix lint error schema-41910to42000.sql * PR 9346: ui polish v1 * PR 9346: create VPC with cidrsize * Routed: fix test failures with test_network_ipv6 and test_vpc_ipv6 due to 'ssh -q' * Routed: fix /usr/local/cloud/systemvm/ are removed after SSVM/CPVM reboot * Routed: fix IP of additional nics of VPC VR is not gateway * PR 9346: fix cidrsize check when create VPC with cidrsize * Routed: fix test/integration/smoke/test_ipv4_routing.py:279:16: E713 test for membership should be 'not in' * PR9346: fix/Update api * PR 9346: set response object name * PR9346: UI refactor and small fixes * PR9346: change return type of getNetworkMode * PR9346: move IPv4 subnet to seperated tab * PR9346: revert IpRangesTabGuest.vue back to original * PR9346: fix remove ipv4 subnet on UI * PR9346: fix test_ipv4_routing.py * AS Number Range Management * Create AS Number Range for a Zone * Fix build * Add ListASNRange and fix create ASN range * Add List AS numbers * Add UI for AS Numbers * Fix UI and filter AS Numbers * Add AS Number on Isolated network creation and refactor UI and response * Release AS Number * Add network offering new columns * Add UI support to view and add AS number and configure network offering * Automatically assign AS Number if not specify AS number * update variable name * Fix routing mode check * UI: Only allow selecting AS number when routing mode is Dynamic and specifyAsNumber is true * UI: Only pass AS number when supported by the network offering * Release AS number on network deletion * Add deleteASNRange command (#81) * API: List ASNumbers by asnumber (#83) --------- Co-authored-by: Pearl Dsilva <pearl1594@gmail.com> * AS number management extensions * Support AS number on VPC tier creation based on the offering * Fix delete AS Range * Fix UI values * UI: Minor fix for releasing AS number * UI: Move management of AS Range to Zone details view * Fix specify_as_number column in network_offering table to set the default false * Add events for AS number operations * Allow users to list AS Numbers and fix network form for Normal users * Add AS number details to list networks response * Fix Allocated time format * Fix Allocated time format * support in details view too * Fix: Do not release AS number if acquired network requires AS number * Fix: Do not release AS number if acquired network requires AS number * Fix typo * Fix allocated release * Fix event type * UI: Add Routing mode and Specify AS to the network offering details * UI: Add Routing mode and Specify AS to the network offering details * Address comment * Fix release AS number of network deletion * Fix release AS number of network deletion * Fix * Restore release to its place based on the boolean * Rename boolean * API: Add networkId as listASNumber parameter * Add Network name to the search view filter for AS numbers * Present allocated time in human readable format - Pubilc IP / AS Numbers * Add account / domain filter for AS numbers * Add support for AS numbers on VPC offerings * Refactor AS number allocation to VPC and non VPC isolated networks * Checkstyle * Add support for AS numbers on VPC offerings * extend vpc offering view and vpcoffering response * merge https://github.com/shapeblue/cloudstack-playtika/pull/115 and change network_id of as_numbers to include vpc_id * Display AS number of VPC tiers as the AS number of the VPC * extend asnumber response and ui support * improve UI and as number response to view VPC details * List only dynamic offerings for vpc tiers with specify as numbers * Fix release AS number * Fix AS number displayed as 0 when no AS number assigned * Fix VPC offering creation without specify AS --------- Co-authored-by: nvazquez <nicovazquez90@gmail.com> * Fix release AS number on VPC deletion * Update server/src/main/java/com/cloud/dc/BGPServiceImpl.java * Update server/src/main/java/com/cloud/dc/BGPServiceImpl.java * Fix missing column on asnumber table * Fix listASNumbers API to support vpcid and obtain AS number from vpc for tiers * Prevent listing 0 AS number for VPC * Fix create Isolated Network form * Update server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java * Update server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java * Dynamic: move routingmode/specifyasn after networkmode in AddNetworkOffering.vue on UI * Dynamic: fix ip4routing in network response * Dynamic/systemvm: add FRR to systemvm template * Dynamic: BGP peers (DB,VO,Dao) * Dynamic: BGP peers (VR/server) * Dynamic: v3 - remove BgpPeer class - fix vpc vr has bgp peers of only 1 tier - rename ip4_cidr to guest_ip4_cidr - rename ip6_cidr to guest_ip6_cidr - generate /etc/frr/frr.conf - apply BGP peers on Dynamic-Routed network even if there is no BGP peers * Dynamic v4: fix vpc vr - fix duplicated guest cidr in frr.conf in vpc vr todo - restart frr / reload frr (reload will cause bgp session to Policy state) - apis for bgp peers - assign/release bgp peer from/to network * Dynamic v5: add apis for bgp peers * Dynamic v6: fix bugs - set response object name - remove required as number when update - fix checks when update - allow regular users to list bgp peers * Dynamic v7: move apis to bgp sub-dir * Dynamic v8: add tab for manage BGP peers on UI * Dynamic v9: fix update bgp with same config * Dynamiv v10: add changeBgpPeersForNetworkCmd * Dynamic v11: create network with bgppeerids - create network with bgppeerids - add marvin classes - add smoke tests - remove uuid from bgp_peer_network_map - fix created/removed in bgp_peer_network_map - remove bgppeers when remove a network - UI: fix delete bgp peer * Dynamic v12: add test for vpc tiers * Dynamic v13: bug fixes - fix change BGP peers for network in Allocated state - fix listing network returns removed record - fix all vpc tiers have the same settings - remove BGP peers as part of network removal - remove FRR settings for vpc tiers without any BGP peers - UI: fix no error msg when change BGP peers * Dynamic v14: assign BGP Peers for VPC instead of VPC tiers - create vpc with bgppeerids - do not allow create/update vpc tier with bgppeerids - apply all bgp peers when create/delete a vpc tier - UI: change bgp peers for vpc - test: update tests on vpc * Dynamic: fix build errors after merging as number PR * Dynamic: fix TODOs * Dynamic: fix smoke test on VPC * Allow creation of networks by users with as numbers * Address review comments * Move BGPService to bgp package and inject it on BaseCmd * Revert changes for CKS and address more comments * Display left side menu option for AS number only for root admin * Dynamic: create/update BGP peer with details refer to https://docs.frrouting.org/en/latest/bgp.html * Dynamic: fix build error and remove access to ListBgpPeers cmd for regular users * Dynamic: assign all zone BGP peers to user networks * Dynamic: show BGP peer info of networks only for root admin * AS number: disable specifyasnumber for non-NSX offerings * Dynamic: pass bgppeer details to command and fix typo with ip6 addr * Dynamic: list BGP peers by isdedicated, and fix change bgppeers for network/vpc * Dynamic: add UI labels * Dynamic: add bgp peers to vpc response * Dynamic: list bgp peers by keyword, fix list by asnumber * Dynamic: fix list bgppeers by keyword and db schema * Dynamic: fix list bgppeers do not return dedicated peers * Dynamic: update UI when create network/vpc offering * Update server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * Update tools/marvin/setup.py * Dynamic: network mode must be same when update a network with new offering * Dynamic: add method networkModel.isAnyServiceSupportedInNetwork * Dynamic: rename APIs and classes * Dynamic: fix unit tests due to previous changes * Dynamic: validateNetworkCidrSize when auto-create subnet * Dynamic: check AS number overlap * Dynamic: add ActionEvent * Dynamic: small code optimization * Dynamic: fix ui bugs after api rename * Dynamic: add marvin and test for ASN ranges and AS numbers * Dynamic: add account setting use.system.bgp.peers also - change the default value of routed.ipv4.vpc.max.cidr.size and routed.ipv4.vpc.min.cidr.size - change the category of settings * static: fix ui error when delete zone ipv4 subnets * static: small UI polish * Dynamic: throw exception when as number is required but not passed * Dynamic: fix typo when create FRR directory which causes network deletion failures * Dynamic: connect to ALL (or ALL dedicated) BGP peers if no BGP peer mapping for the network/vpc * Dynamic: throw exception when as number is required for VPC but not passed * Dynamic: list bgp peers by useSystemBgpPeers * Dynamic: fix frr config in VPC VR when change bgp peers * Dynamic: create frr config even if there is no VPC tiers * Dynamic: list bgp peers by zoneid (required for account) and account * Dynamic: only apply FRR config for vpc tiers with dynamic routing * Dynamic: donot send commands to router if commands size is 0 * Dynamic: fix 'new IPv6 address is not valid' when update bgp peer without IPv6 * Dynamic: throw exception if fail to allocate AS number when create network/vpc with dynamic routing * Dynamic: enable ipv6 unicast and 'ip nht resolve-via-default' * Dynamic: delete network/vpc if fail to allocate AS number when create network/vpc with dynamic routing * test: add unit tests for ASN APIs * test: add unit tests for core module * test: add unit tests for API responses * test: add unit tests for BgpPeerTO * test: add minor changes * test: add tests for create/delete/update/list RoutingFirewallRuleCmd * Static: show ip4 routes for vpc tiers * test: fix smoke test failure caused by type change of as number * test: add test for Ipv4SubnetForZoneCmd * test: add test for Ipv4SubnetForGuestNetworkCmd and BgpPeerCmd * UI: do not show redundant router when network mode is ROUTED as RVR is not supported * UI: hide 'Conserve mode' when networkmode is ROUTED * test: add unit tests for ListASNumbersCmdTest * Static: remove allocated IPv4 subnet when delete a network or vpc * test: add unit tests for BgpPeersRules * Dynamic: set ipv4routing from network offering * server: list as numbers and ipv4 subnets by keyword * server: remove dedicated bgp peers and ipv4 subnets when delete an account or domain * server: fix dedicated ipv4 subnet is allocated to other accounts * UI: fix allocated time format * server: ignore project is projectid is -1 so bgppeers/ipv4subnets works in project view * UI: add project column to bgp peers and ipv4 subnets * server: fix list AS numbers by domain admin or normal user * server: fix network creation when ipv4 subnet is dedicated * UI: polish network.js * Dynamic: fix frr config for ipv6 routing * Static routing: support cks cluster * Static: get/create IPv4 subnet from dedicated subnets at first * Dynamic: add BGP peers tab * Static: remove redundant loops * api: add since to api and response * server: add unit tests --------- Co-authored-by: Nicolas Vazquez <nicovazquez90@gmail.com> Co-authored-by: Pearl Dsilva <pearl1594@gmail.com> Co-authored-by: Harikrishna Patnala <harikrishna.patnala@gmail.com> Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
939 lines
41 KiB
Python
Executable File
939 lines
41 KiB
Python
Executable File
# -- 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
|
|
|
|
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", "", "-A FORWARD -d %s -o %s -j ACL_INBOUND_%s" %
|
|
(self.address['network'], self.dev, 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:])))])
|
|
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
|