Skip to content

Commit fc3bb6a

Browse files
authored
Bfd multihop tests [202012] (#6746)
* Bfd test multihop (#6032) Cherry pick of PR #6032 for 202012 * Missed the skip mark in the cherry-pick Signed-off-by: siqbal1486 <[email protected]> * squashed multiples chages coming from various PR's to a singel PR for 202012. Signed-off-by: siqbal1486 <[email protected]>
1 parent 104a62a commit fc3bb6a

File tree

5 files changed

+501
-116
lines changed

5 files changed

+501
-116
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import binascii
2+
import socket
3+
import struct
4+
import select
5+
import json
6+
import argparse
7+
import os.path
8+
from fcntl import ioctl
9+
import logging
10+
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
11+
import scapy.all as scapy2
12+
scapy2.conf.use_pcap=True
13+
from scapy.contrib.bfd import BFD
14+
15+
def get_if(iff, cmd):
16+
s = socket.socket()
17+
ifreq = ioctl(s, cmd, struct.pack("16s16x",iff))
18+
s.close()
19+
20+
return ifreq
21+
22+
def get_mac(iff):
23+
SIOCGIFHWADDR = 0x8927 # Get hardware address
24+
return get_if(iff, SIOCGIFHWADDR)[18:24]
25+
26+
27+
class Interface(object):
28+
29+
def __init__(self, iface):
30+
self.iface = iface
31+
self.socket = None
32+
self.mac_address = get_mac(iface)
33+
34+
def __del__(self):
35+
if self.socket:
36+
self.socket.close()
37+
38+
def bind(self):
39+
self.socket = scapy2.conf.L2listen(iface=self.iface, filter="udp port 4784")
40+
41+
def handler(self):
42+
return self.socket
43+
44+
def recv(self):
45+
sniffed = self.socket.recv()
46+
pkt = sniffed[0]
47+
str_pkt = str(pkt).encode("HEX")
48+
binpkt = binascii.unhexlify(str_pkt)
49+
return binpkt
50+
51+
def send(self, data):
52+
scapy2.sendp(data, iface=self.iface)
53+
54+
def mac(self):
55+
return self.mac_address
56+
57+
def name(self):
58+
return self.iface
59+
60+
61+
class Poller(object):
62+
def __init__(self, interfaces, responder):
63+
self.responder = responder
64+
self.mapping = {}
65+
for interface in interfaces:
66+
self.mapping[interface.handler()] = interface
67+
68+
def poll(self):
69+
handlers = self.mapping.keys()
70+
while True:
71+
(rdlist, _, _) = select.select(handlers, [], [])
72+
for handler in rdlist:
73+
self.responder.action(self.mapping[handler])
74+
75+
76+
class BFDResponder(object):
77+
def __init__(self, sessions):
78+
self.sessions = sessions
79+
return
80+
81+
def action(self, interface):
82+
data = interface.recv()
83+
mac_src, mac_dst, ip_src, ip_dst, bfd_remote_disc, bfd_state = self.extract_bfd_info(data)
84+
if ip_dst not in self.sessions:
85+
return
86+
session = self.sessions[ip_dst]
87+
88+
if bfd_state== 3L:
89+
interface.send(session["pkt"])
90+
return
91+
92+
if bfd_state == 2L:
93+
return
94+
session = self.sessions[ip_dst]
95+
session["other_disc"] = bfd_remote_disc
96+
bfd_pkt_init = self.craft_bfd_packet( session, data, mac_src, mac_dst, ip_src, ip_dst, bfd_remote_disc, 2L )
97+
print("sending INIT", bfd_pkt_init)
98+
interface.send(bfd_pkt_init)
99+
bfd_pkt_init.payload.payload.payload.load.sta =3L
100+
session["pkt"] = bfd_pkt_init
101+
return
102+
103+
def extract_bfd_info(self, data):
104+
# remote_mac, remote_ip, request_ip, op_type
105+
ether = scapy2.Ether(data)
106+
mac_src= ether.src
107+
mac_dst = ether.dst
108+
ip_src = ether.payload.src
109+
ip_dst = ether.payload.dst
110+
bfdpkt = BFD(ether.payload.payload.payload.load)
111+
bfd_remote_disc =bfdpkt.my_discriminator
112+
bfd_state = bfdpkt.sta
113+
return mac_src, mac_dst, ip_src, ip_dst, bfd_remote_disc, bfd_state
114+
115+
def craft_bfd_packet(self, session, data, mac_src, mac_dst, ip_src, ip_dst, bfd_remote_disc, bfd_state):
116+
ethpart = scapy2.Ether(data)
117+
bfdpart = BFD(ethpart.payload.payload.payload.load)
118+
bfdpart.my_discriminator = session["my_disc"]
119+
bfdpart.your_discriminator = bfd_remote_disc
120+
bfdpart.sta = bfd_state
121+
122+
ethpart.payload.payload.payload.load = bfdpart
123+
ethpart.src = mac_dst
124+
ethpart.dst = mac_src
125+
ethpart.payload.src= ip_dst
126+
ethpart.payload.dst= ip_src
127+
return ethpart
128+
129+
def parse_args():
130+
parser = argparse.ArgumentParser(description='ARP autoresponder')
131+
parser.add_argument('--conf', '-c', type=str, dest='conf', default='/tmp/from_t1.json', help='path to json file with configuration')
132+
args = parser.parse_args()
133+
134+
return args
135+
136+
def main():
137+
args = parse_args()
138+
139+
if not os.path.exists(args.conf):
140+
print("Can't find file %s" % args.conf)
141+
return
142+
143+
with open(args.conf) as fp:
144+
data = json.load(fp)
145+
146+
# generate ip_sets. every ip address will have it's own uniq mac address
147+
sessions = {}
148+
local_disc_base = 0xcdba0000
149+
local_src_port = 14000
150+
ifaces = {}
151+
for bfd in data:
152+
curr_session = {}
153+
curr_session["local"] = bfd["local_addr"]
154+
curr_session["remote"] = bfd["neighbor_addr"]
155+
curr_session["intf"] = bfd["ptf_intf"]
156+
curr_session["multihop"] = bfd["multihop"]
157+
curr_session["my_disc"] = local_disc_base
158+
curr_session["other_disc"] = 0x00
159+
curr_session["mac"] = get_mac( str( bfd["ptf_intf"]))
160+
curr_session["src_port"] = local_src_port
161+
curr_session["pkt"] = ""
162+
if bfd["ptf_intf"] not in ifaces:
163+
ifaces[curr_session["intf"]] = curr_session["mac"]
164+
165+
local_disc_base +=1
166+
local_src_port +=1
167+
sessions[curr_session["local"]] = curr_session
168+
ifaceobjs =[]
169+
for iface_name in ifaces.keys():
170+
iface = Interface(str(iface_name))
171+
iface.bind()
172+
ifaceobjs.append(iface)
173+
174+
resp = BFDResponder(sessions)
175+
176+
p = Poller(ifaceobjs, resp)
177+
p.poll()
178+
179+
return
180+
181+
if __name__ == '__main__':
182+
main()

tests/bfd/conftest.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def pytest_addoption(parser):
2+
parser.addoption("--num_sessions", action="store", default=5)
3+
parser.addoption("--num_sessions_scale", action="store", default=128)

0 commit comments

Comments
 (0)