2626
2727import os
2828import sys
29+ import json
2930import time
3031
3132from happy .ReturnMsg import ReturnMsg
@@ -92,6 +93,8 @@ def __init__(self, opts=options):
9293 self .isp_pool = None
9394 self .init_happy_isp (isp_id = (self .isp_id + '_' ))
9495
96+ self .iptable_rules = list ()
97+
9598 def __pre_check (self ):
9699 if isinstance (self .seed , int ) and not (0 < self .seed < 253 ):
97100 emsg = "seed %s is not in range[1, 252]" % self .seed
@@ -228,6 +231,7 @@ def __connect_internet_to_isp(self):
228231 cmd = "ip link set " + self .internet_host_end + " netns %s" % self .bridge
229232 cmd = self .runAsRoot (cmd )
230233 ret = self .CallAtHost (cmd )
234+
231235 cmd = "ip netns exec %s brctl addif %s " % (self .bridge , self .bridge ) + self .internet_host_end
232236 cmd = self .runAsRoot (cmd )
233237 ret = self .CallAtHost (cmd )
@@ -251,17 +255,28 @@ def __nmconf(self):
251255 # configure nmcli
252256 tries = 3
253257 state = self .getHostNMInterfaceStatus (self .internet_node_end )
254- while state != "connecting" :
258+
259+ if state is None :
260+ return
261+ elif state == "unmanaged" :
262+ cmd = "nmcli dev set {} managed yes" .format (self .internet_node_end )
263+ cmd = self .runAsRoot (cmd )
264+ ret = self .CallAtHost (cmd )
265+ state = self .getHostNMInterfaceStatus (self .internet_node_end )
266+
267+ while tries > 0 :
268+ if state == "connecting" :
269+ break
255270 time .sleep (1 )
256271 state = self .getHostNMInterfaceStatus (self .internet_node_end )
257272 tries -= 1
258- if tries <= 0 :
259- emsg = "Failed to setup host interface %s with nmcli. Internet may not working." % \
260- ( self .internet_node_end )
261- self .logger .warning ("[localhost] HappyInternet: %s" % (emsg ))
262- return
273+ else :
274+ emsg = "Failed to setup host interface {} with nmcli. Internet may not working." . format (
275+ self .internet_node_end )
276+ self .logger .warning ("[localhost] HappyInternet: {}" . format (emsg ))
277+ return
263278
264- cmd = "nmcli dev disconnect iface " + self .internet_node_end
279+ cmd = "nmcli dev disconnect " + self .internet_node_end
265280 cmd = self .runAsRoot (cmd )
266281 ret = self .CallAtHost (cmd )
267282
@@ -371,10 +386,7 @@ def __nat_host(self):
371386 self .exit ()
372387
373388 # configure nat on host
374- if self .add :
375- status = 1
376- else :
377- status = 0
389+ status = 1 if self .add else 0
378390
379391 cmd = "sysctl -n -w net.ipv6.conf.all.forwarding=%d" % (status )
380392 cmd = self .runAsRoot (cmd )
@@ -388,35 +400,87 @@ def __nat_host(self):
388400 return
389401
390402 # Post routing on host
403+ iptable_cmd_list = [
404+ "POSTROUTING -o {} -j MASQUERADE" .format (self .iface ),
405+ "FORWARD -i {} -o {} -m state --state RELATED,ESTABLISHED -j ACCEPT" .format (self .iface ,
406+ self .internet_node_end ),
407+ "FORWARD -i {} -o {} -j ACCEPT" .format (self .internet_node_end , self .iface )
408+ ]
409+ for rule in iptable_cmd_list :
410+ table = "-t filter"
411+ if any (keyword in rule for keyword in ('POSTROUTING' , 'PREROUTING' )):
412+ table = "-t nat"
413+ # Checking if rule exists
414+ cmd = "iptables {} -C {}" .format (table , rule )
415+ cmd = self .runAsRoot (cmd )
416+ ret = self .CallAtHost (cmd )
417+ if not ret :
418+ self .logger .info ("iptables rule exists..do nothing.." )
419+ self .iptable_rules .append (rule )
420+ continue
421+ # Add iptable rule
422+ cmd = "iptables {} -A {}" .format (table , rule )
423+ cmd = self .runAsRoot (cmd )
424+ ret = self .CallAtHost (cmd )
425+ if ret :
426+ _ , err = self .CallAtHostForOutput (cmd )
427+ raise Exception ("Unable to add iptable rule: \n Err: {}" .format (err ))
391428
392- cmd = "iptables -t nat -A POSTROUTING -o " + self .iface + " -j MASQUERADE"
393- cmd = self .runAsRoot (cmd )
394- ret = self .CallAtHost (cmd )
395-
396- cmd = "iptables -A FORWARD -i " + self .iface + " -o " + self .internet_node_end + \
397- " -m state --state RELATED,ESTABLISHED -j ACCEPT"
398- cmd = self .runAsRoot (cmd )
399- ret = self .CallAtHost (cmd )
400-
401- cmd = "iptables -A FORWARD -i " + self .internet_node_end + " -o " + self .iface + " -j ACCEPT"
402- cmd = self .runAsRoot (cmd )
403- ret = self .CallAtHost (cmd )
429+ self .iptable_rules .append (rule )
404430
405431 def __nat_isp_node (self ):
406432 # configure nat on node
407433 # Post routing on node
434+ iptable_cmd_list = [
435+ "POSTROUTING -o {} -j MASQUERADE" .format (self .isp_node_end ),
436+ "FORWARD -o {} -m state --state RELATED,ESTABLISHED -j ACCEPT" .format (self .isp_node_end ),
437+ "FORWARD -i {} -j ACCEPT" .format (self .isp_node_end )
438+ ]
439+ for rule in iptable_cmd_list :
440+ table = "-t filter"
441+ if any (keyword in rule for keyword in ('POSTROUTING' , 'PREROUTING' )):
442+ table = "-t nat"
443+ cmd = "iptables {} -A {}" .format (table , rule )
444+ cmd = self .runAsRoot (cmd )
445+ ret = self .CallAtNode (self .node_id , cmd )
446+
447+ def __save_iptable_commands (self ):
448+ """
449+ API to save successfully executed iptable rules for later restore back to
450+ origin iptable settings.
451+ """
452+ if not len (self .iptable_rules ):
453+ self .logger .warn ("iptable: Nothing to be save, "
454+ "Please check and see if that is correct." )
455+ return
456+ isp_state_dict = json .load (open (self .isp_state_file , 'r' ))
457+ isp_state_dict .update ({"isp_state_fw" : self .iptable_rules })
458+ with open (self .isp_state_file , 'w' ) as fp :
459+ json .dump (isp_state_dict , fp )
460+
461+ def __flush_iptable_commands (self ):
462+ """API to remove iptable rules that is create by happy"""
463+ if not os .path .isfile (self .isp_state_file ):
464+ self .logger .warn ("Unable to run iptable rules flush, "
465+ "firewall config file is missing..do nothing" )
466+ return
408467
409- cmd = "iptables -t nat -A POSTROUTING -o " + self .isp_node_end + " -j MASQUERADE"
410- cmd = self .runAsRoot (cmd )
411- ret = self .CallAtNode (self .node_id , cmd )
412-
413- cmd = "iptables -A FORWARD -o " + self .isp_node_end + " -m state --state RELATED,ESTABLISHED -j ACCEPT"
414- cmd = self .runAsRoot (cmd )
415- ret = self .CallAtNode (self .node_id , cmd )
416-
417- cmd = "iptables -A FORWARD -i " + self .isp_node_end + " -j ACCEPT"
418- cmd = self .runAsRoot (cmd )
419- ret = self .CallAtNode (self .node_id , cmd )
468+ with open (self .isp_state_file , 'r' ) as fp :
469+ fw_dict = json .load (fp ).get ("isp_state_fw" , None )
470+ if not fw_dict :
471+ self .logger .warn ("No added firewall rules need to be flush. "
472+ "Do nothing..." )
473+ return
474+ for rule in fw_dict :
475+ table = "-t filter"
476+ if any (keyword in rule for keyword in ('POSTROUTING' , 'PREROUTING' )):
477+ table = "-t nat"
478+ cmd = "iptables {} -C {}" .format (table , rule )
479+ cmd = self .runAsRoot (cmd )
480+ while not self .CallAtHost (cmd ):
481+ cmd1 = "iptables {} -D {}" .format (table , rule )
482+ cmd1 = self .runAsRoot (cmd1 )
483+ ret = self .CallAtHost (cmd1 )
420484
421485 def __delete_isp (self ):
422486 # delete isp network namespace
@@ -451,7 +515,7 @@ def run(self):
451515 with self .getStateLockManager (lock_id = "rt" ):
452516 self .__route ()
453517 self .__nat_isp_node ()
454-
518+ self . __save_iptable_commands ()
455519 with self .getStateLockManager ():
456520 self .__internet_state ()
457521 self .writeState ()
@@ -474,5 +538,6 @@ def run(self):
474538 self .removeGlobalIsp ()
475539
476540 self .writeIspState ()
541+ self .__flush_iptable_commands ()
477542
478543 return ReturnMsg (0 )
0 commit comments