From 39000f461de6b85877db85488b1cc7f1fad9d359 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sun, 29 Jan 2017 17:14:41 +0200 Subject: ipv6 scan & ping Change-Id: I4f8112b4c942d149da5ea3f0ee01ac82d7fe32cc Signed-off-by: Yaroslav Brustinov --- .../rx_services/trex_stl_rx_service_api.py | 32 ++-- .../rx_services/trex_stl_rx_service_arp.py | 8 +- .../rx_services/trex_stl_rx_service_icmp.py | 6 +- .../rx_services/trex_stl_rx_service_ipv6.py | 179 +++++++++++++++++++++ 4 files changed, 204 insertions(+), 21 deletions(-) create mode 100755 scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_ipv6.py (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_api.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_api.py index b0904382..d6a620aa 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_api.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_api.py @@ -9,10 +9,16 @@ class RXServiceAPI(object): LAYER_MODE_L2 = 1 LAYER_MODE_L3 = 2 - def __init__ (self, port, layer_mode = LAYER_MODE_ANY, queue_size = 100): + def __init__(self, port, layer_mode = LAYER_MODE_ANY, queue_size = 100, timeout = None, retries = None, retry_delay = 0.1): self.port = port self.queue_size = queue_size self.layer_mode = layer_mode + self.timeout = timeout + self.retries = retries + if retries is None and timeout is None: + self.retries = 0 + self.retry_delay = retry_delay + self.init_ts = time.time() ################### virtual methods ###################### @@ -47,7 +53,7 @@ class RXServiceAPI(object): """ raise NotImplementedError() - def on_pkt_rx (self, pkt, start_ts): + def on_pkt_rx(self, pkt, start_ts): """ called for each packet arriving on RX @@ -65,13 +71,10 @@ class RXServiceAPI(object): raise NotImplementedError() - def on_timeout_err (self, retries): + def on_timeout(self): """ called when a timeout occurs - :parameters: - retries - how many times was the service retring before failing - :returns: RC object @@ -80,7 +83,7 @@ class RXServiceAPI(object): ##################### API ###################### - def execute (self, retries = 0): + def execute(self, *a, **k): # sanity check rc = self.__sanity() @@ -97,12 +100,12 @@ class RXServiceAPI(object): try: # add the stream(s) - self.port.add_streams(self.generate_request()) + self.port.add_streams(self.generate_request(*a, **k)) rc = self.port.set_rx_queue(size = self.queue_size) if not rc: return rc - return self.__execute_internal(retries) + return self.__execute_internal() finally: # best effort restore @@ -137,20 +140,21 @@ class RXServiceAPI(object): # main resolve function - def __execute_internal (self, retries): + def __execute_internal (self): - # retry for 'retries' + # retry for 'retries' or until timeout index = 0 while True: rc = self.execute_iteration() if rc is not None: return rc - if index >= retries: - return self.on_timeout_err(retries) + if (self.retries is not None and index >= self.retries or + self.timeout is not None and time.time() - self.init_ts >= self.timeout): + return self.on_timeout() index += 1 - time.sleep(0.1) + time.sleep(self.retry_delay) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_arp.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_arp.py index 3cf97045..a82c66d4 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_arp.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_arp.py @@ -8,8 +8,8 @@ from scapy.layers.l2 import Ether, ARP class RXServiceARP(RXServiceAPI): - def __init__ (self, port_id): - super(RXServiceARP, self).__init__(port_id, layer_mode = RXServiceAPI.LAYER_MODE_L3) + def __init__(self, port_id, *a, **k): + super(RXServiceARP, self).__init__(port_id, layer_mode = RXServiceAPI.LAYER_MODE_L3, *a, **k) def get_name (self): return "ARP" @@ -49,8 +49,8 @@ class RXServiceARP(RXServiceAPI): return self.port.ok({'psrc' : arp.psrc, 'hwsrc': arp.hwsrc}) - def on_timeout_err (self, retries): - return self.port.err('failed to receive ARP response ({0} retries)'.format(retries)) + def on_timeout(self): + return self.port.err('failed to receive ARP response ({0} retries)'.format(self.retries)) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_icmp.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_icmp.py index ae57b161..612b6a2d 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_icmp.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_icmp.py @@ -9,9 +9,9 @@ from scapy.layers.inet import IP, ICMP class RXServiceICMP(RXServiceAPI): - def __init__ (self, port, ping_ip, pkt_size): + def __init__ (self, port, ping_ip, pkt_size, *a, **k): - super(RXServiceICMP, self).__init__(port, layer_mode = RXServiceAPI.LAYER_MODE_L3) + super(RXServiceICMP, self).__init__(port, layer_mode = RXServiceAPI.LAYER_MODE_L3, *a, **k) self.ping_ip = ping_ip self.pkt_size = pkt_size @@ -78,6 +78,6 @@ class RXServiceICMP(RXServiceAPI): # return the str of a timeout err - def on_timeout_err (self, retries): + def on_timeout(self): return self.port.ok('Request timed out.') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_ipv6.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_ipv6.py new file mode 100755 index 00000000..c2c8ebc1 --- /dev/null +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/rx_services/trex_stl_rx_service_ipv6.py @@ -0,0 +1,179 @@ +from .trex_stl_rx_service_api import RXServiceAPI + +from ..trex_stl_streams import STLStream, STLTXSingleBurst +from ..trex_stl_packet_builder_scapy import * +from ..trex_stl_types import * + +from scapy.layers.l2 import Ether +from scapy.layers.inet6 import * +import time + + +class RXServiceIPv6(RXServiceAPI): + + def __init__(self, port, dst_ip, *a, **k): + RXServiceAPI.__init__(self, port, *a, **k) + self.attr = port.get_ts_attr() + self.src_mac = self.attr['layer_cfg']['ether']['src'] + mac_for_ip = port.info.get('hw_mac', self.src_mac) + self.src_ip = generate_ipv6(mac_for_ip) + self.mld_ip = generate_ipv6_solicited_node(mac_for_ip) + self.dst_ip = dst_ip + self.responses = {} + + def pre_execute(self): + return self.port.ok() + + def send_intermediate(self, streams): + self.port.remove_all_streams() + self.port.add_streams(streams) + mult = {'op': 'abs', 'type' : 'percentage', 'value': 100} + self.port.start(mul = mult, force = True, duration = -1, mask = 0xffffffff) + + def generate_ns(self, dst_mac, dst_ip): + pkt = (Ether(src = self.src_mac, dst = dst_mac) / + IPv6(src = self.src_ip, dst = dst_ip) / + ICMPv6ND_NS(tgt = dst_ip) / + ICMPv6NDOptSrcLLAddr(lladdr = self.src_mac)) + return STLStream(packet = STLPktBuilder(pkt = pkt), mode = STLTXSingleBurst(total_pkts = 2)) + + def generate_na(self, dst_mac, dst_ip): + pkt = (Ether(src = self.src_mac, dst = dst_mac) / + IPv6(src = self.src_ip, dst = dst_ip) / + ICMPv6ND_NA(tgt = self.src_ip, R = 0, S = 1, O = 1)) + return STLStream(packet = STLPktBuilder(pkt = pkt), mode = STLTXSingleBurst(total_pkts = 2)) + + def generate_ns_na(self, dst_mac, dst_ip): + return [self.generate_ns(dst_mac, dst_ip), self.generate_na(dst_mac, dst_ip)] + + def execute(self, *a, **k): + mult = self.attr['multicast']['enabled'] + try: + if mult != True: + self.port.set_attr(multicast = True) # response might be multicast + return RXServiceAPI.execute(self, *a, **k) + finally: + if mult != True: + self.port.set_attr(multicast = False) + + +class RXServiceIPv6Scan(RXServiceIPv6): + ''' Ping with given IPv6 (usually all nodes address) and wait for responses until timeout ''' + + def get_name(self): + return 'IPv6 scanning' + + def generate_request(self): + dst_mac = multicast_mac_from_ipv6(self.dst_ip) + dst_mld_ip = 'ff02::16' + dst_mld_mac = multicast_mac_from_ipv6(dst_mld_ip) + + mld_pkt = (Ether(src = self.src_mac, dst = dst_mld_mac) / + IPv6(src = self.src_ip, dst = dst_mld_ip, hlim = 1) / + IPv6ExtHdrHopByHop(options = [RouterAlert(), PadN()]) / + ICMPv6MLReportV2() / + MLDv2Addr(type = 4, len = 0, multicast_addr = 'ff02::2')) + ping_pkt = (Ether(src = self.src_mac, dst = dst_mac) / + IPv6(src = self.src_ip, dst = self.dst_ip, hlim = 1) / + ICMPv6EchoRequest()) + + mld_stream = STLStream(packet = STLPktBuilder(pkt = mld_pkt), + mode = STLTXSingleBurst(total_pkts = 2)) + ping_stream = STLStream(packet = STLPktBuilder(pkt = ping_pkt), + mode = STLTXSingleBurst(total_pkts = 2)) + return [mld_stream, ping_stream] + + + def on_pkt_rx(self, pkt, start_ts): + # convert to scapy + scapy_pkt = Ether(pkt['binary']) + + if scapy_pkt.haslayer('ICMPv6ND_NS') and scapy_pkt.haslayer('ICMPv6NDOptSrcLLAddr'): + node_mac = scapy_pkt.getlayer(ICMPv6NDOptSrcLLAddr).lladdr + node_ip = scapy_pkt.getlayer(IPv6).src + if node_ip not in self.responses: + self.send_intermediate(self.generate_ns_na(node_mac, node_ip)) + + elif scapy_pkt.haslayer('ICMPv6ND_NA'): + is_router = scapy_pkt.getlayer(ICMPv6ND_NA).R + node_ip = scapy_pkt.getlayer(ICMPv6ND_NA).tgt + dst_ip = scapy_pkt.getlayer(IPv6).dst + node_mac = scapy_pkt.src + if node_ip not in self.responses and dst_ip == self.src_ip: + self.responses[node_ip] = {'type': 'Router' if is_router else 'Host', 'mac': node_mac} + + elif scapy_pkt.haslayer('ICMPv6EchoReply'): + node_mac = scapy_pkt.src + node_ip = scapy_pkt.getlayer(IPv6).src + if node_ip == self.dst_ip: + return self.port.ok([{'ipv6': node_ip, 'mac': node_mac}]) + if node_ip not in self.responses: + self.send_intermediate(self.generate_ns_na(node_mac, node_ip)) + + + def on_timeout(self): + return self.port.ok([{'type': v['type'], 'mac': v['mac'], 'ipv6': k} for k, v in self.responses.items()]) + + +class RXServiceICMPv6(RXServiceIPv6): + ''' + Ping some IPv6 location. + If the dest MAC is found from scanning, use it. + Otherwise, send to default port dest. + ''' + + def __init__(self, port, pkt_size, dst_mac = None, *a, **k): + super(RXServiceICMPv6, self).__init__(port, *a, **k) + self.pkt_size = pkt_size + self.dst_mac = dst_mac + + def get_name(self): + return 'PING6' + + def pre_execute(self): + if self.dst_mac is None and not self.port.is_resolved(): + 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): + attrs = self.port.get_ts_attr() + + if self.dst_mac is None: + self.dst_mac = attrs['layer_cfg']['ether']['dst'] + + ping_pkt = (Ether(src = self.src_mac, dst = self.dst_mac) / + IPv6(src = self.src_ip, dst = self.dst_ip) / + ICMPv6EchoRequest()) + pad = max(0, self.pkt_size - len(ping_pkt)) + ping_pkt /= pad * 'x' + + return STLStream( packet = STLPktBuilder(pkt = ping_pkt), mode = STLTXSingleBurst(total_pkts = 2) ) + + + def on_pkt_rx(self, pkt, start_ts): + scapy_pkt = Ether(pkt['binary']) + + if scapy_pkt.haslayer('ICMPv6EchoReply'): + node_ip = scapy_pkt.getlayer(IPv6).src + hlim = scapy_pkt.getlayer(IPv6).hlim + dst_ip = scapy_pkt.getlayer(IPv6).dst + + if dst_ip != self.src_ip: # not our ping + return + + dt = pkt['ts'] - start_ts + return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, hlim={3}'.format(node_ip, len(pkt['binary']), dt * 1000, hlim)) + + if scapy_pkt.haslayer('ICMPv6ND_NS') and scapy_pkt.haslayer('ICMPv6NDOptSrcLLAddr'): + node_mac = scapy_pkt.getlayer(ICMPv6NDOptSrcLLAddr).lladdr + node_ip = scapy_pkt.getlayer(IPv6).src + self.send_intermediate(self.generate_ns_na(node_mac, node_ip)) + + + # return the str of a timeout err + def on_timeout(self): + return self.port.ok('Request timed out.') + + -- cgit 1.2.3-korg