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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
from .trex_stl_streams import STLStream, STLTXSingleBurst
from .trex_stl_packet_builder_scapy import STLPktBuilder
from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP, ICMP
import time
# a generic abstract class for resolving using the server
class Resolver(object):
def __init__ (self, port, queue_size = 100):
self.port = port
# code to execute before sending any request - return RC object
def pre_send (self):
raise NotImplementedError()
# return a list of streams for request
def generate_request (self):
raise NotImplementedError()
# return None for more packets otherwise RC object
def on_pkt_rx (self, pkt):
raise NotImplementedError()
# return value in case of timeout
def on_timeout_err (self, retries):
raise NotImplementedError()
##################### API ######################
def resolve (self, retries = 0):
# first cleanup
rc = self.port.remove_all_streams()
if not rc:
return rc
# call the specific class implementation
rc = self.pre_send()
if not rc:
return rc
# start the iteration
try:
# add the stream(s)
self.port.add_streams(self.generate_request())
rc = self.port.set_rx_queue(size = 100)
if not rc:
return rc
return self.resolve_wrapper(retries)
finally:
# best effort restore
self.port.remove_rx_queue()
self.port.remove_all_streams()
# main resolve function
def resolve_wrapper (self, retries):
# retry for 'retries'
index = 0
while True:
rc = self.resolve_iteration()
if rc is not None:
return rc
if index >= retries:
return self.on_timeout_err(retries)
index += 1
time.sleep(0.1)
def resolve_iteration (self):
mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
if not rc:
return rc
# save the start timestamp
self.start_ts = rc.data()['ts']
# block until traffic finishes
while self.port.is_active():
time.sleep(0.01)
return self.wait_for_rx_response()
def wait_for_rx_response (self):
# we try to fetch response for 5 times
polling = 5
while polling > 0:
# fetch the queue
rx_pkts = self.port.get_rx_queue_pkts()
# might be an error
if not rx_pkts:
return rx_pkts
# for each packet - examine it
for pkt in rx_pkts.data():
rc = self.on_pkt_rx(pkt)
if rc is not None:
return rc
if polling == 0:
return None
polling -= 1
time.sleep(0.1)
class ARPResolver(Resolver):
def __init__ (self, port_id):
super(ARPResolver, self).__init__(port_id)
# before resolve
def pre_send (self):
self.dst = self.port.get_dst_addr()
self.src = self.port.get_src_addr()
if self.dst['ipv4'] is None:
return self.port.err("Port has a non-IPv4 destination: '{0}'".format(self.dst['mac']))
if self.src['ipv4'] is None:
return self.port.err('Port must have an IPv4 source address configured')
# invalidate the current ARP resolution (if exists)
return self.port.invalidate_arp()
# return a list of streams for request
def generate_request (self):
base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = self.src['ipv4'], pdst = self.dst['ipv4'], hwsrc = self.src['mac'])
s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
return [s1]
# return None in case more packets are needed else the status rc
def on_pkt_rx (self, pkt):
scapy_pkt = Ether(pkt['binary'])
if not 'ARP' in scapy_pkt:
return None
arp = scapy_pkt['ARP']
# check this is the right ARP (ARP reply with the address)
if (arp.op != 2) or (arp.psrc != self.dst['ipv4']):
return None
# update the port with L3 full configuration
rc = self.port.set_l3_mode(self.src['ipv4'], self.dst['ipv4'], arp.hwsrc)
if not rc:
return rc
return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
def on_timeout_err (self, retries):
return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
#################### ping resolver ####################
class PingResolver(Resolver):
def __init__ (self, port, ping_ip, pkt_size):
super(PingResolver, self).__init__(port)
self.ping_ip = ping_ip
self.pkt_size = pkt_size
def pre_send (self):
self.src = self.port.get_src_addr()
self.dst = self.port.get_dst_addr()
if self.src['ipv4'] is None:
return self.port.err('Ping - port does not have an IPv4 address configured')
if self.dst['mac'] is None:
return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
return self.port.ok()
# return a list of streams for request
def generate_request (self):
base_pkt = Ether(dst = self.dst['mac'])/IP(src = self.src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
pad = max(0, self.pkt_size - len(base_pkt))
base_pkt = base_pkt / (pad * 'x')
#base_pkt.show2()
s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
self.base_pkt = base_pkt
return [s1]
# return None for more packets otherwise RC object
def on_pkt_rx (self, pkt):
scapy_pkt = Ether(pkt['binary'])
if not 'ICMP' in scapy_pkt:
return None
ip = scapy_pkt['IP']
if ip.dst != self.src['ipv4']:
return None
icmp = scapy_pkt['ICMP']
dt = pkt['ts'] - self.start_ts
# echo reply
if icmp.type == 0:
# check seq
if icmp.seq != self.base_pkt['ICMP'].seq:
return None
return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
# unreachable
elif icmp.type == 3:
# check seq
if icmp.payload.seq != self.base_pkt['ICMP'].seq:
return None
return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
else:
# skip any other types
#scapy_pkt.show2()
return None
# return the str of a timeout err
def on_timeout_err (self, retries):
return self.port.ok('Request timed out.')
|