Skip to content
This repository was archived by the owner on Nov 9, 2023. It is now read-only.

Add feature to clean up iptable rules after test is done or topology #40

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 100 additions & 35 deletions happy/HappyInternet.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import os
import sys
import json
import time

from happy.ReturnMsg import ReturnMsg
Expand Down Expand Up @@ -92,6 +93,8 @@ def __init__(self, opts=options):
self.isp_pool = None
self.init_happy_isp(isp_id=(self.isp_id + '_'))

self.iptable_rules = list()

def __pre_check(self):
if isinstance(self.seed, int) and not (0 < self.seed < 253):
emsg = "seed %s is not in range[1, 252]" % self.seed
Expand Down Expand Up @@ -228,6 +231,7 @@ def __connect_internet_to_isp(self):
cmd = "ip link set " + self.internet_host_end + " netns %s" % self.bridge
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)

cmd = "ip netns exec %s brctl addif %s " % (self.bridge, self.bridge) + self.internet_host_end
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)
Expand All @@ -251,17 +255,28 @@ def __nmconf(self):
# configure nmcli
tries = 3
state = self.getHostNMInterfaceStatus(self.internet_node_end)
while state != "connecting":

if state is None:
return
elif state == "unmanaged":
cmd = "nmcli dev set {} managed yes".format(self.internet_node_end)
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)
state = self.getHostNMInterfaceStatus(self.internet_node_end)

while tries > 0:
if state == "connecting":
break
time.sleep(1)
state = self.getHostNMInterfaceStatus(self.internet_node_end)
tries -= 1
if tries <= 0:
emsg = "Failed to setup host interface %s with nmcli. Internet may not working." % \
(self.internet_node_end)
self.logger.warning("[localhost] HappyInternet: %s" % (emsg))
return
else:
emsg = "Failed to setup host interface {} with nmcli. Internet may not working.".format(
self.internet_node_end)
self.logger.warning("[localhost] HappyInternet: {}".format(emsg))
return

cmd = "nmcli dev disconnect iface " + self.internet_node_end
cmd = "nmcli dev disconnect " + self.internet_node_end
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)

Expand Down Expand Up @@ -371,10 +386,7 @@ def __nat_host(self):
self.exit()

# configure nat on host
if self.add:
status = 1
else:
status = 0
status = 1 if self.add else 0

cmd = "sysctl -n -w net.ipv6.conf.all.forwarding=%d" % (status)
cmd = self.runAsRoot(cmd)
Expand All @@ -388,35 +400,87 @@ def __nat_host(self):
return

# Post routing on host
iptable_cmd_list = [
"POSTROUTING -o {} -j MASQUERADE".format(self.iface),
"FORWARD -i {} -o {} -m state --state RELATED,ESTABLISHED -j ACCEPT".format(self.iface,
self.internet_node_end),
"FORWARD -i {} -o {} -j ACCEPT".format(self.internet_node_end, self.iface)
]
for rule in iptable_cmd_list:
table = "-t filter"
if any(keyword in rule for keyword in ('POSTROUTING', 'PREROUTING')):
table = "-t nat"
# Checking if rule exists
cmd = "iptables {} -C {}".format(table, rule)
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)
if not ret:
self.logger.info("iptables rule exists..do nothing..")
self.iptable_rules.append(rule)
continue
# Add iptable rule
cmd = "iptables {} -A {}".format(table, rule)
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)
if ret:
_, err = self.CallAtHostForOutput(cmd)
raise Exception("Unable to add iptable rule: \nErr: {}".format(err))

cmd = "iptables -t nat -A POSTROUTING -o " + self.iface + " -j MASQUERADE"
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)

cmd = "iptables -A FORWARD -i " + self.iface + " -o " + self.internet_node_end + \
" -m state --state RELATED,ESTABLISHED -j ACCEPT"
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)

cmd = "iptables -A FORWARD -i " + self.internet_node_end + " -o " + self.iface + " -j ACCEPT"
cmd = self.runAsRoot(cmd)
ret = self.CallAtHost(cmd)
self.iptable_rules.append(rule)

def __nat_isp_node(self):
# configure nat on node
# Post routing on node
iptable_cmd_list = [
"POSTROUTING -o {} -j MASQUERADE".format(self.isp_node_end),
"FORWARD -o {} -m state --state RELATED,ESTABLISHED -j ACCEPT".format(self.isp_node_end),
"FORWARD -i {} -j ACCEPT".format(self.isp_node_end)
]
for rule in iptable_cmd_list:
table = "-t filter"
if any(keyword in rule for keyword in ('POSTROUTING', 'PREROUTING')):
table = "-t nat"
cmd = "iptables {} -A {}".format(table, rule)
cmd = self.runAsRoot(cmd)
ret = self.CallAtNode(self.node_id, cmd)

def __save_iptable_commands(self):
"""
API to save successfully executed iptable rules for later restore back to
origin iptable settings.
"""
if not len(self.iptable_rules):
self.logger.warn("iptable: Nothing to be save, "
"please check and see if that is correct.")
return
isp_state_dict = json.load(open(self.isp_state_file, 'r'))
isp_state_dict.update({"isp_state_fw": self.iptable_rules})
with open(self.isp_state_file, 'w') as fp:
json.dump(isp_state_dict, fp)

def __flush_iptable_commands(self):
"""API to remove iptable rules that is create by happy"""
if not os.path.isfile(self.isp_state_file):
self.logger.warn("Unable to run iptable rules flush, isp state file is missing."
"Do nothing..")
return

cmd = "iptables -t nat -A POSTROUTING -o " + self.isp_node_end + " -j MASQUERADE"
cmd = self.runAsRoot(cmd)
ret = self.CallAtNode(self.node_id, cmd)

cmd = "iptables -A FORWARD -o " + self.isp_node_end + " -m state --state RELATED,ESTABLISHED -j ACCEPT"
cmd = self.runAsRoot(cmd)
ret = self.CallAtNode(self.node_id, cmd)

cmd = "iptables -A FORWARD -i " + self.isp_node_end + " -j ACCEPT"
cmd = self.runAsRoot(cmd)
ret = self.CallAtNode(self.node_id, cmd)
with open(self.isp_state_file, 'r') as fp:
fw_dict = json.load(fp).get("isp_state_fw", None)
if not fw_dict:
self.logger.warn("No added firewall rules need to be flush. "
"Do nothing...")
return
for rule in fw_dict:
table = "-t filter"
if any(keyword in rule for keyword in ('POSTROUTING', 'PREROUTING')):
table = "-t nat"
cmd = "iptables {} -C {}".format(table, rule)
cmd = self.runAsRoot(cmd)
while not self.CallAtHost(cmd):
cmd1 = "iptables {} -D {}".format(table, rule)
cmd1 = self.runAsRoot(cmd1)
ret = self.CallAtHost(cmd1)

def __delete_isp(self):
# delete isp network namespace
Expand Down Expand Up @@ -451,7 +515,7 @@ def run(self):
with self.getStateLockManager(lock_id="rt"):
self.__route()
self.__nat_isp_node()

self.__save_iptable_commands()
with self.getStateLockManager():
self.__internet_state()
self.writeState()
Expand All @@ -474,5 +538,6 @@ def run(self):
self.removeGlobalIsp()

self.writeIspState()
self.__flush_iptable_commands()

return ReturnMsg(0)