Skip to content

Commit

Permalink
[pcmping]: Add pcmping tool to get portchannel member packet loss sta…
Browse files Browse the repository at this point in the history
…te (sonic-net#293)

* [pcmping]: Add pcmping tool to get portchannel member packet loss state

Signed-off-by: Sihui Han <sihan@microsoft.com>

* Update as comments

Signed-off-by: Sihui Han <sihan@microsoft.com>

* Use jumbo frame and enhance the code

Signed-off-by: Sihui Han <sihan@microsoft.com>

* [pcmping]: use dscp 0 to create packet

Signed-off-by: Sihui Han <sihan@microsoft.com>
  • Loading branch information
sihuihan88 authored and lguohan committed Sep 2, 2018
1 parent 3222d7e commit dfb557c
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
173 changes: 173 additions & 0 deletions scripts/pcmping
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/usr/bin/env python

"""
Portchannel member ping: get portchannel member packet loss state.
"""

from scapy.all import *
import argparse
import random
import socket
import select
import time
import ipaddr as ipaddress
import sys
import swsssdk

RCV_SIZE = 20480
RCV_TIMEOUT = 10000
SELECT_TIMEOUT = 2
PROBE_PACKET_LEN = 9000
DHCP_DEFAULT = 0

def receive(my_socket, timeout, exp_pkt, time_sent):
timeLeft = timeout
delay = 0
while True:
startedSelect = time.time()
whatReady = select.select(my_socket, [], [], timeLeft)
if whatReady[0] == []: # Timeout
return False, delay, None
timeReceived = time.time()

for sel in whatReady[0]:
recPacket = sel.recv(RCV_SIZE)
e = str(exp_pkt)
# exclude ethernet header
p = str(recPacket[14:])
if e == p:
delay = (timeReceived - time_sent) * 1000
return True, delay, sel
timeLeft = timeLeft - (timeReceived - startedSelect)
if timeLeft <= 0:
return False, delay, None

def find_probe_packet(interface, dst_out, dst_in, sockets, exp_socket, max_iter):
dscp = DHCP_DEFAULT
tos = dscp << 2
src_out = dst_in
decap_valid = False
for x in range(0, max_iter):
print "---------- Attempting to create probe packet that goes through %s, iteration: %d ---------" % (interface, x)
ip_src = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
ip_src =ipaddress.IPv4Address(unicode(ip_src,'utf-8'))
while ip_src == ipaddress.IPv4Address(unicode(dst_in,'utf-8')) or \
ip_src.is_multicast or ip_src.is_private or ip_src.is_reserved:
ip_src = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
ip_src = ipaddress.IPv4Address(unicode(ip_src,'utf-8'))
inner_pkt = IP(src=ip_src, dst=dst_in, tos=tos, ttl=64, id=1, ihl=None,proto=4)/ICMP()
inner_pkt = inner_pkt/("".join([chr(x%256) for x in xrange(PROBE_PACKET_LEN - len(inner_pkt))]))
pkt = IP(src=src_out, dst=dst_out, tos=tos)/inner_pkt
exp_pkt = inner_pkt
exp_pkt['IP'].ttl = 63

time_sent = time.time()
res = send(pkt, iface=interface, verbose=False)
have_received, delay, recv_socket = receive(sockets, SELECT_TIMEOUT, exp_pkt, time_sent)
if have_received and recv_socket == exp_socket:
return pkt, exp_pkt
elif have_received:
decap_valid = True

if decap_valid:
print ("Decap works properly. link %s is unreachable, Please check!\n" % interface)
sys.exit(0)
else:
print ("Decap using %s doesn't work properly, Please Check!\n" % (dst_out))
sys.exit(0)

def get_portchannel(interface):
configdb = swsssdk.ConfigDBConnector()
configdb.connect()
portchannels = configdb.get_table("PORTCHANNEL")
for key, value in portchannels.iteritems():
if interface in value['members']:
return key, value
sys.stderr.write("Interface %s is not a portchannel member. Please Check!\n" % interface)
sys.exit(1)

def get_portchannel_ipv4(portchannel_name):
configdb = swsssdk.ConfigDBConnector()
configdb.connect()
config = configdb.get_config()
portchannel_interfaces = config["PORTCHANNEL_INTERFACE"]
for key in portchannel_interfaces.keys():
pc, ip = key
ip = ip.split("/")[0]
if pc == portchannel_name and ipaddress.IPAddress(ip).version == 4:
return ip
sys.stderr.write("Portchannel %s doesn't have IPV4 address. Please Check!\n" % portchannel)
sys.exit(1)

def create_socket(interface, portchannel):
# create sockets
try:
sockets = []
for iface in portchannel['members']:
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_IP))
s.bind((iface, 0))
s.settimeout(RCV_TIMEOUT)
if iface == interface:
exp_socket = s
sockets.append(s)
except:
sys.stderr.write("Unable to create socket. Check your permissions\n")
sys.exit(1)
return sockets, exp_socket

def print_statistics(interface, total_count, recv_count):
print "\n--- %s ping statistics ---" % interface
print ("%d packets transmitted, %d received, %0.2f%% packet loss" % (total_count, recv_count, (total_count-recv_count)*1.0/total_count*100))

def main():
parser = argparse.ArgumentParser(description='Check portchannel member link state',
version='1.0.0',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Example:
pcmping -i Ethernet8 -ip 10.10.10.10
pcmping -i Ethernet8 -ip 10.10.10.10 -c 5
pcmping -i Ethernet8 -ip 10.10.10.10 -c 5 -t 20
"""
)

parser.add_argument('-i', '--interface', type=str, required=True, help='Portchannel member interface')
parser.add_argument('-ip','--decapip', type=str, required=True, help='Neighbor decap ip (usually same as neighbor loopback ip)')
parser.add_argument('-c', '--count', type=int, help='Number of decap packets to be sent.', default=0)
parser.add_argument('-t', '--trial', type=int, help='Maximum attempt times to create probe packet.\nPlease increase the value as the number of portchannel members increases', default=20)
args = parser.parse_args()

interface = args.interface
decap_ip = args.decapip
exp_count = args.count
max_trial = args.trial

portchannel_name, portchannel = get_portchannel(interface)
portchannel_ip = get_portchannel_ipv4(portchannel_name)
sockets, exp_socket = create_socket(interface, portchannel)
pkt, exp_pkt = find_probe_packet(interface, decap_ip, portchannel_ip, sockets, exp_socket, max_trial)
print "Preparation done! Start probing ............"
print "PORTCHANNEL Member PING %d btyes of data. PORTCHANNEL: %s, INTERFACE: %s" % (PROBE_PACKET_LEN, portchannel_name, interface)
recv_count = 0
total_count = 0
try:
while True:
time_sent = time.time()
res = send(pkt, iface=interface, verbose=False)
have_received, delay, recv_socket = receive(sockets, SELECT_TIMEOUT, exp_pkt, time_sent)
total_count = total_count + 1
if have_received:
print "%d bytes from %s: time=%0.4fms" % (PROBE_PACKET_LEN, interface, delay)
recv_count = recv_count + 1
else:
print "packet not received!"
if total_count == exp_count:
break;
time.sleep(1)
except KeyboardInterrupt:
print_statistics(interface, total_count, recv_count)
return
print_statistics(interface, total_count, recv_count)

if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def get_test_suite():
'scripts/generate_dump',
'scripts/intfutil',
'scripts/lldpshow',
'scripts/pcmping',
'scripts/port2alias',
'scripts/portconfig',
'scripts/portstat',
Expand Down

0 comments on commit dfb557c

Please sign in to comment.