-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadv.py
executable file
·177 lines (162 loc) · 5.46 KB
/
adv.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/bin/env python3.4
# encoding: utf-8
"""
Parses suplied output of the command
"show route received-protocol bgp IP detail | displat xml" and send the result
to the exabgp neighbor as a valid bgp update
TODO:
- add lables and mpbgp
"""
import re
import ipaddress
from sys import stdout
from time import sleep
from lxml import etree
from update import Update
from bintrees import RBTree
from xml.etree.ElementTree import iterparse
def send_update(tree):
'''
sends updates into stdout so exabgp can intercept/parse and send them to BGP
neighbor
'''
#sleep(5)
##Iterate through messages
#for _, value in tree.iter_items():
# stdout.write('announce ' + str(value) + '\n')
# stdout.flush()
#I tried to send vpnv4 update, it doesn't work
#also exabgp has starnge behaviour when it converts extended-comminity from
#from string to a send value, so it's better to use hex from as below
stdout.write('announce route 1.4.0.0/16 rd 65000:1 next-hop self community 100:1 extended-community 0x0002FDE900000001 label 1000\n')
stdout.flush()
stdout.write('announce route 60.60.60.60/16 rd 1.1.1.1:5000 next-hop '
+ '172.30.152.20 extended-community 0x0102010101011388 '
+ 'label 301301\n')
stdout.flush()
##Loop endlessly to allow ExaBGP to continue running
while True:
sleep(1)
def ip_to_int(prefix):
'''
converts ip address/prefix into integer
'''
#prefix = prefix.split('.')
#return int(prefix[0])*(256**3) \
# + int(prefix[1])*(256**2) \
# + int(prefix[2])*(256) \
# + int(prefix[3]) \
return int(ipaddress.ip_address(prefix))
def get_as_path_origin_atomic(aspath):
'''
extracts as path, nh, origin attributes from suplied string
'''
asp = aspath.split(" ")
atomic = False
if asp[-1] not in ['I', '?']:
atomic = True
if atomic:
aggregator = ":".join(asp[-2:])
asp = [item for item in asp[2:-3] if item != '']
origin = 'incomplete' if asp[-2] == '?' else 'igp'
as_path = asp[:-2]
return (' '.join(as_path), origin, atomic, aggregator)
else:
origin = 'incomplete' if asp[-1] == '?' else 'igp'
as_path = asp[2:-1]
return (' '.join(as_path), origin)
class XMLNamespaces:
'''
junos uses xml namespace, so find, findall, iter, etc have to
wrap namespaces into utility class
'''
def __init__(self, **kwargs):
self._namespaces = {}
for name, uri in kwargs.items():
self.register(name, uri)
def register(self, name, uri):
'''
put uri into dict for future calls
'''
self._namespaces[name] = '{'+uri+'}'
def __call__(self, path):
return path.format_map(self._namespaces)
def parse_and_remove(file, path):
'''
allows incremental processing of XML documents
'''
path_parts = path.split('/')
context = iterparse(file, ('start', 'end'))
next(context)
tag_stack = []
elem_stack = []
for event, elem in context:
if event == 'start':
tag = re.sub('{[^}]*}', '', elem.tag)
tag_stack.append(tag)
elem_stack.append(elem)
elif event == 'end':
if tag_stack == path_parts:
yield elem
elem_stack[-2].remove(elem)
try:
tag_stack.pop()
elem_stack.pop()
except IndexError:
pass
def create_tree(file, tree, prep, ns):
for i in file:
prefix = i.find(ns('{prep}rt-destination')).text
mask = i.find(ns('{prep}rt-prefix-length')).text
key = (ip_to_int(prefix), mask)
kwargs = {}
kwargs['nh'] = i.find(ns('{prep}rt-entry/{prep}nh/{prep}to')).text
kwargs['nh'] = 'self'
if key[0] > 4294967295:
kwargs['nh'] = '::ffff:172.30.152.20'
asp = i.find(ns('{prep}rt-entry/{prep}as-path')).text.rstrip()
asp = get_as_path_origin_atomic(asp)
community = ''
for comm in i.findall(ns('{prep}rt-entry/{prep}communities/{prep}community')):
community += comm.text + ' '
kwargs['community'] = community
if len(asp) == 2:
kwargs['as_path'] = asp[0]
kwargs['origin'] = asp[1]
else:
kwargs['as_path'] = asp[0]
kwargs['origin'] = asp[1]
kwargs['aggregator'] = asp[3]
med = i.find(ns('{prep}rt-entry/{prep}med'))
lp = i.find(ns('{prep}rt-entry/{prep}local-preference'))
if med is not None:
kwargs['med'] = med.text
if lp is not None:
kwargs['lp'] = lp.text
update = Update(prefix, mask, **kwargs)
tree.insert(key, update)
def main():
'''
main
'''
files = []
try:
input_xml = parse_and_remove('/home/den/small_table_xml',
'route-information/route-table/rt')
files.append(input_xml)
except FileNotFoundError:
pass
try:
ipv6_input_xml = parse_and_remove('/home/den/ipv6_xml',
'route-information/route-table/rt')
files.append(ipv6_input_xml)
except FileNotFoundError:
pass
tree = RBTree()
prep = 'http://xml.juniper.net/junos/12.3R3/junos-routing'
ns = XMLNamespaces(prep=prep)
for file in files:
create_tree(file, tree, prep, ns)
send_update(tree)
if __name__ == '__main__':
main()