Hello
One special case I’m dealing with at the moment is that when expanding the existing VPNs based on crypto ACLs, I get an excel sheet and I need to decide if the new connection between company A and company B necessitates a new crypto SA, in other words: do I need to add an entry in the crypto ACL, or perhaps this new connection is part of the existing old SAs.
This also forms part of the idempotency check that I’m trying to implement, because it doesn’t make sense to add a new ACL entry, if the new entries is a subset of any of the existing entries. So the new entry would need to be compared to each and every one of the existing entries.
An example: I have the following crypto ACL:
10 permit ip 1.1.1.0 0.0.0.255 2.2.2.0 0.0.0.255
This creates a single crypto SA.
The client now sends me an excel file, with the following change:
Please add a connection between 1.1.1.64 to 3.3.3.1
I would now need to create a new SA:
20 permit ip 1.1.1.0 0.0.0.255 3.3.3.0 0.0.0.255
Of course, i could add an SA for single hosts but then if the client sends a similar request for 1.1.1.72 to 3.3.3.55, the crypto ACL needs to be modified on both sides. I don’t want (or don’t need) redundant communication so i prefer to add /24 subnets (or even /16 if i can).
Here comes my pain point. Looking at the excel sheet and deciding whether the new entry is needed or not is manual work, so if I have a non-technical coordinator, they’re not able to say whether the workflow should be: coordinator>VPN specialist>inform other VPN specialist (other GW)> FW specialist>back to coordinator>process people or simply Coordinator>FW specialist>Coordinator.
The logic should be as follows: For the following change when entry 20 is added, when are we allowed to skip entry 20?
10 permit ip A1 B1
20 permit ip A2 B2
Entry 20 is not needed only if:
A2==A1 && B2==B1
OR
A2 is part of A1 && B2 is part of B1.
Let’s look at the netaddr python library documentation:
>>> s1 = IPSet(['192.0.2.0', '192.0.2.1', '192.0.2.2']) >>> s2 = IPSet(['192.0.2.2', '192.0.2.3', '192.0.2.4']) >>> s1 & s2 IPSet(['192.0.2.2/32']) >>> s1.isdisjoint(s2) False >>> s1 = IPSet(['192.0.2.0', '192.0.2.1']) >>> s2 = IPSet(['192.0.2.3', '192.0.2.4']) >>> s1 & s2 IPSet([]) >>> s1.isdisjoint(s2) True
So if we use the disjoint method, our ACL idempotency check should now be (relatively) easy to write up.
In the end I implemented it in a different way:
from cisco_acl.regexes import ace_match from netaddr import IPSet from netaddr import IPNetwork import iplib source1 = input('what is the source ip ?') wildcard = input('what is the wildcard ?') wildcard_converted = iplib.convert_nm(wildcard, 'bit') source1 = source1 + '/' + wildcard_converted dest1 = input('what is the dest ip ?') wildcard = input('what is the wildcard ?') wildcard_converted = iplib.convert_nm(wildcard, 'bit') dest1 = dest1 + '/' + wildcard_converted source2 = input('what is the source ip ?') wildcard = input('what is the wildcard ?') wildcard_converted = iplib.convert_nm(wildcard, 'bit') source2 = source2 + '/' + wildcard_converted dest2 = input('what is the dest ip ?') wildcard = input('what is the wildcard ?') wildcard_converted = iplib.convert_nm(wildcard, 'bit') dest2 = dest2 + '/' + wildcard_converted result1 = IPNetwork(source2) in IPSet([source1]) result2 = IPNetwork(dest2) in IPSet([dest1]) if result1 and result2: print('we should not add this') else: print('we should add this')
tode@DESKTOP:~$ python3 aclscript.py what is the source ip ?192.168.0.0 what is the wildcard ?0.0.0.255 what is the dest ip ?172.16.1.0 what is the wildcard ?0.0.0.127 what is the source ip ?192.168.0.0 what is the wildcard ?0.0.0.31 what is the dest ip ?172.16.1.0 what is the wildcard ?0.0.0.3 we should not add this
Here i needed to think how I would get the input and compare it with the new input. I came up with this. I will gather existing ACL entries as one dictionary and compare it with the new dictionary of new entries.
from cisco_acl.regexes import ace_match from netaddr import IPSet from netaddr import IPNetwork import iplib dictionary1 = {"192.168.0.0/24": "10.0.0.0/24", "192.168.1.0/24": "10.0.0.0/30"} dictionary3 = {"192.168.3.0/24": "10.0.0.0/29", "192.168.4.0/24": "10.0.1.0/27"} for newkey in dictionary3: for oldkey in dictionary1: if IPNetwork(newkey) in IPSet([oldkey]) and IPNetwork(dictionary3[newkey]) in IPSet([dictionary1[oldkey]]): print('nothing to do') else: print('something to do')
Now i need to populate the dictionaries with fields from an excel sheet.
If the value of row 1, column C = OLD, i should populate olddictionary,
if the value of row 1, column C = NEW, i should populate newdictionary,

from openpyxl import load_workbook from netaddr import IPSet from netaddr import IPNetwork # load excel with its path wrkbk = load_workbook("bpadb.xlsx") sh = wrkbk["Sheet1"] # to iterate through cells in Column A and print value in console src = '' dst = '' olddict = {} newdict = {} changedict = {} removaldict = {} for i in range(1,10): src = "A" + str(i) dst = "B" + str(i) comment = "C" + str(i) src_cell = sh[src] dst_cell = sh[dst] comment_cell = sh[comment] if comment_cell.value == "OLD": olddict[src_cell.value] = dst_cell.value if comment_cell.value == "NEW": newdict[src_cell.value] = dst_cell.value print('this is old dictionary ' + str(olddict)) print('this is new dictionary ' + str(newdict)) for newkey in newdict: for oldkey in olddict: if IPNetwork(newkey) in IPSet([oldkey]) and IPNetwork(newdict[newkey]) in IPSet([olddict[oldkey]]): #print('for this new entry ' + str(newkey) + ' ' + str(newdict[newkey]) + ' when compared with entry ' + str(oldkey) + ' ' + str(olddict[oldkey]) + ' there is nothing to do') removaldict[newkey] = newdict[newkey] else: #print(' you need to implement this new acl entry ' + str(newkey) + ' ' + str(newdict[newkey]) + ' because it was not inside ' + str(oldkey) + ' ' + str(olddict[oldkey])) changedict[newkey] = newdict[newkey] print('this is change dictionary ' + str(changedict)) print('this is removal dictionary ' + str(removaldict)) intersect = [] for item in changedict.keys(): if item in removaldict: intersect.append(item) #intersect.append(removaldict[item]) print('Two dictionaries intersect here so these entries should be removed: ' + str(intersect)) #print(intersect[0]) #print(intersect[1]) for i in range(0,len(intersect)): changedict.pop(intersect[i]) print('final form of changedict is ' + str(changedict))
C:\Users\tode\Desktop>python bpa_script.py this is old dictionary {'192.168.0.0/24': '172.16.0.1/32', '192.168.1.0/24': '10.0.0.0/29', '192.168.3.0/26': '169.254.0.0/31'} this is new dictionary {'192.168.1.0/27': '10.0.0.0/30', '192.168.3.0/27': '169.254.1.0/31', '192.168.3.0/28': '169.254.0.0/31', '192.168.3.0/29': '169.254.3.0/31'} this is change dictionary {'192.168.1.0/27': '10.0.0.0/30', '192.168.3.0/27': '169.254.1.0/31', '192.168.3.0/28': '169.254.0.0/31', '192.168.3.0/29': '169.254.3.0/31'} this is removal dictionary {'192.168.1.0/27': '10.0.0.0/30', '192.168.3.0/28': '169.254.0.0/31'} Two dictionaries intersect here so these entries should be removed: ['192.168.1.0/27', '192.168.3.0/28'] final form of changedict is {'192.168.3.0/27': '169.254.1.0/31', '192.168.3.0/29': '169.254.3.0/31'}