26
26
27
27
import os
28
28
import sys
29
+ import json
29
30
import time
30
31
31
32
from happy .ReturnMsg import ReturnMsg
@@ -92,6 +93,8 @@ def __init__(self, opts=options):
92
93
self .isp_pool = None
93
94
self .init_happy_isp (isp_id = (self .isp_id + '_' ))
94
95
96
+ self .iptable_rules = list ()
97
+
95
98
def __pre_check (self ):
96
99
if isinstance (self .seed , int ) and not (0 < self .seed < 253 ):
97
100
emsg = "seed %s is not in range[1, 252]" % self .seed
@@ -228,6 +231,7 @@ def __connect_internet_to_isp(self):
228
231
cmd = "ip link set " + self .internet_host_end + " netns %s" % self .bridge
229
232
cmd = self .runAsRoot (cmd )
230
233
ret = self .CallAtHost (cmd )
234
+
231
235
cmd = "ip netns exec %s brctl addif %s " % (self .bridge , self .bridge ) + self .internet_host_end
232
236
cmd = self .runAsRoot (cmd )
233
237
ret = self .CallAtHost (cmd )
@@ -251,17 +255,28 @@ def __nmconf(self):
251
255
# configure nmcli
252
256
tries = 3
253
257
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
255
270
time .sleep (1 )
256
271
state = self .getHostNMInterfaceStatus (self .internet_node_end )
257
272
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
263
278
264
- cmd = "nmcli dev disconnect iface " + self .internet_node_end
279
+ cmd = "nmcli dev disconnect " + self .internet_node_end
265
280
cmd = self .runAsRoot (cmd )
266
281
ret = self .CallAtHost (cmd )
267
282
@@ -371,10 +386,7 @@ def __nat_host(self):
371
386
self .exit ()
372
387
373
388
# configure nat on host
374
- if self .add :
375
- status = 1
376
- else :
377
- status = 0
389
+ status = 1 if self .add else 0
378
390
379
391
cmd = "sysctl -n -w net.ipv6.conf.all.forwarding=%d" % (status )
380
392
cmd = self .runAsRoot (cmd )
@@ -388,35 +400,87 @@ def __nat_host(self):
388
400
return
389
401
390
402
# 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 ))
391
428
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 )
404
430
405
431
def __nat_isp_node (self ):
406
432
# configure nat on node
407
433
# 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 (keyward in rule for keyward 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
408
467
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 )
420
484
421
485
def __delete_isp (self ):
422
486
# delete isp network namespace
@@ -451,7 +515,7 @@ def run(self):
451
515
with self .getStateLockManager (lock_id = "rt" ):
452
516
self .__route ()
453
517
self .__nat_isp_node ()
454
-
518
+ self . __save_iptable_commands ()
455
519
with self .getStateLockManager ():
456
520
self .__internet_state ()
457
521
self .writeState ()
@@ -474,5 +538,6 @@ def run(self):
474
538
self .removeGlobalIsp ()
475
539
476
540
self .writeIspState ()
541
+ self .__flush_iptable_commands ()
477
542
478
543
return ReturnMsg (0 )
0 commit comments