Home | History | Annotate | Download | only in controllers
      1 #./usr/bin/env python3.4
      2 #
      3 #   Copyright 2017 - The Android Open Source Project
      4 #
      5 #   Licensed under the Apache License, Version 2.0 (the "License");
      6 #   you may not use this file except in compliance with the License.
      7 #   You may obtain a copy of the License at
      8 #
      9 #       http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 #   Unless required by applicable law or agreed to in writing, software
     12 #   distributed under the License is distributed on an "AS IS" BASIS,
     13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 #   See the License for the specific language governing permissions and
     15 #   limitations under the License.
     16 """Collection of utility functions to generate and send custom packets.
     17 
     18 """
     19 import logging
     20 import multiprocessing
     21 import socket
     22 import time
     23 
     24 import acts.signals
     25 from acts.test_utils.wifi import wifi_power_test_utils as wputils
     26 # http://www.secdev.org/projects/scapy/
     27 # On ubuntu, sudo pip3 install scapy-python3
     28 import scapy.all as scapy
     29 
     30 ACTS_CONTROLLER_CONFIG_NAME = 'PacketSender'
     31 ACTS_CONTROLLER_REFERENCE_NAME = 'packet_senders'
     32 
     33 GET_FROM_LOCAL_INTERFACE = 'get_local'
     34 MAC_BROADCAST = 'ff:ff:ff:ff:ff:ff'
     35 IPV4_BROADCAST = '255.255.255.255'
     36 ARP_DST = '00:00:00:00:00:00'
     37 RA_MAC = '33:33:00:00:00:01'
     38 RA_IP = 'ff02::1'
     39 RA_PREFIX = 'd00d::'
     40 RA_PREFIX_LEN = 64
     41 DHCP_OFFER_OP = 2
     42 DHCP_OFFER_SRC_PORT = 67
     43 DHCP_OFFER_DST_PORT = 68
     44 DHCP_TRANS_ID = 0x01020304
     45 DNS_LEN = 3
     46 PING6_DATA = 'BEST PING6 EVER'
     47 PING4_TYPE = 8
     48 MDNS_TTL = 255
     49 MDNS_QTYPE = 'PTR'
     50 MDNS_UDP_PORT = 5353
     51 MDNS_V4_IP_DST = '224.0.0.251'
     52 MDNS_V4_MAC_DST = '01:00:5E:00:00:FB'
     53 MDNS_RECURSIVE = 1
     54 MDNS_V6_IP_DST = 'FF02::FB'
     55 MDNS_V6_MAC_DST = '33:33:00:00:00:FB'
     56 
     57 
     58 def create(configs):
     59     """Creates PacketSender controllers from a json config.
     60 
     61     Args:
     62         The json configs that represent this controller
     63 
     64     Returns:
     65         A new PacketSender
     66     """
     67     return [PacketSender(c) for c in configs]
     68 
     69 
     70 def destroy(objs):
     71     """Destroys a list of PacketSenders and stops sending (if active).
     72 
     73     Args:
     74         objs: A list of PacketSenders
     75     """
     76     for pkt_sender in objs:
     77         pkt_sender.stop_sending(True)
     78     return
     79 
     80 
     81 def get_info(objs):
     82     """Get information on a list of packet senders.
     83 
     84     Args:
     85         objs: A list of PacketSenders
     86 
     87     Returns:
     88         Network interface name that is being used by each packet sender
     89     """
     90     return [pkt_sender.interface for pkt_sender in objs]
     91 
     92 
     93 class ThreadSendPacket(multiprocessing.Process):
     94     """Creates a thread that keeps sending the same packet until a stop signal.
     95 
     96     Attributes:
     97         stop_signal: signal to stop the thread execution
     98         packet: desired packet to keep sending
     99         interval: interval between consecutive packets (s)
    100         interface: network interface name (e.g., 'eth0')
    101         log: object used for logging
    102     """
    103 
    104     def __init__(self, signal, packet, interval, interface, log):
    105         multiprocessing.Process.__init__(self)
    106         self.stop_signal = signal
    107         self.packet = packet
    108         self.interval = interval
    109         self.interface = interface
    110         self.log = log
    111 
    112     def run(self):
    113         self.log.info('Packet Sending Started.')
    114         while True:
    115             if self.stop_signal.is_set():
    116                 # Poison pill means shutdown
    117                 self.log.info('Packet Sending Stopped.')
    118                 break
    119 
    120             try:
    121                 scapy.sendp(self.packet, iface=self.interface, verbose=0)
    122                 time.sleep(self.interval)
    123             except Exception:
    124                 self.log.exception('Exception when trying to send packet')
    125                 return
    126 
    127         return
    128 
    129 
    130 class PacketSenderError(acts.signals.ControllerError):
    131     """Raises exceptions encountered in packet sender lib."""
    132 
    133 
    134 class PacketSender(object):
    135     """Send any custom packet over a desired interface.
    136 
    137     Attributes:
    138         log: class logging object
    139         thread_active: indicates whether or not the send thread is active
    140         thread_send: thread object for the concurrent packet transmissions
    141         stop_signal: event to stop the thread
    142         interface: network interface name (e.g., 'eth0')
    143     """
    144 
    145     def __init__(self, ifname):
    146         """Initiallize the PacketGenerator class.
    147 
    148         Args:
    149             ifname: network interface name that will be used packet generator
    150         """
    151         self.log = logging.getLogger()
    152         self.packet = None
    153         self.thread_active = False
    154         self.thread_send = None
    155         self.stop_signal = multiprocessing.Event()
    156         self.interface = ifname
    157 
    158     def send_ntimes(self, packet, ntimes, interval):
    159         """Sends a packet ntimes at a given interval.
    160 
    161         Args:
    162             packet: custom built packet from Layer 2 up to Application layer
    163             ntimes: number of packets to send
    164             interval: interval between consecutive packet transmissions (s)
    165         """
    166         if packet is None:
    167             raise PacketSenderError(
    168                 'There is no packet to send. Create a packet first.')
    169 
    170         for _ in range(ntimes):
    171             try:
    172                 scapy.sendp(packet, iface=self.interface, verbose=0)
    173                 time.sleep(interval)
    174             except socket.error as excpt:
    175                 self.log.exception('Caught socket exception : %s' % excpt)
    176                 return
    177 
    178     def send_receive_ntimes(self, packet, ntimes, interval):
    179         """Sends a packet and receives the reply ntimes at a given interval.
    180 
    181         Args:
    182             packet: custom built packet from Layer 2 up to Application layer
    183             ntimes: number of packets to send
    184             interval: interval between consecutive packet transmissions and
    185                       the corresponding reply (s)
    186         """
    187         if packet is None:
    188             raise PacketSenderError(
    189                 'There is no packet to send. Create a packet first.')
    190 
    191         for _ in range(ntimes):
    192             try:
    193                 scapy.srp1(
    194                     packet, iface=self.interface, timeout=interval, verbose=0)
    195                 time.sleep(interval)
    196             except socket.error as excpt:
    197                 self.log.exception('Caught socket exception : %s' % excpt)
    198                 return
    199 
    200     def start_sending(self, packet, interval):
    201         """Sends packets in parallel with the main process.
    202 
    203         Creates a thread and keeps sending the same packet at a given interval
    204         until a stop signal is received
    205 
    206         Args:
    207             packet: custom built packet from Layer 2 up to Application layer
    208             interval: interval between consecutive packets (s)
    209         """
    210         if packet is None:
    211             raise PacketSenderError(
    212                 'There is no packet to send. Create a packet first.')
    213 
    214         if self.thread_active:
    215             raise PacketSenderError(
    216                 ('There is already an active thread. Stop it'
    217                  'before starting another transmission.'))
    218 
    219         self.thread_send = ThreadSendPacket(self.stop_signal, packet, interval,
    220                                             self.interface, self.log)
    221         self.thread_send.start()
    222         self.thread_active = True
    223 
    224     def stop_sending(self, ignore_status=False):
    225         """Stops the concurrent thread that is continuously sending packets.
    226 
    227        """
    228         if not self.thread_active:
    229             if ignore_status:
    230                 return
    231             else:
    232                 raise PacketSenderError(
    233                     'Error: There is no acive thread running to stop.')
    234 
    235         # Stop thread
    236         self.stop_signal.set()
    237         self.thread_send.join()
    238 
    239         # Just as precaution
    240         if self.thread_send.is_alive():
    241             self.thread_send.terminate()
    242             self.log.warning('Packet Sending forced to terminate')
    243 
    244         self.stop_signal.clear()
    245         self.thread_send = None
    246         self.thread_active = False
    247 
    248 
    249 class ArpGenerator(object):
    250     """Creates a custom ARP packet
    251 
    252     Attributes:
    253         packet: desired built custom packet
    254         src_mac: MAC address (Layer 2) of the source node
    255         src_ipv4: IPv4 address (Layer 3) of the source node
    256         dst_ipv4: IPv4 address (Layer 3) of the destination node
    257     """
    258 
    259     def __init__(self, **config_params):
    260         """Initialize the class with the required network and packet params.
    261 
    262         Args:
    263             config_params: a dictionary with all the necessary packet fields.
    264               Some fields can be generated automatically. For example:
    265               {'subnet_mask': '255.255.255.0',
    266                'dst_ipv4': '192.168.1.3',
    267                'src_ipv4: 'get_local', ...
    268               The key can also be 'get_local' which means the code will read
    269               and use the local interface parameters
    270         """
    271         interf = config_params['interf']
    272         self.packet = None
    273         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    274             self.src_mac = scapy.get_if_hwaddr(interf)
    275         else:
    276             self.src_mac = config_params['src_mac']
    277 
    278         self.dst_ipv4 = config_params['dst_ipv4']
    279         if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
    280             self.src_ipv4 = scapy.get_if_addr(interf)
    281         else:
    282             self.src_ipv4 = config_params['src_ipv4']
    283 
    284     def generate(self, ip_dst=None, hwsrc=None, hwdst=None, eth_dst=None):
    285         """Generates a custom ARP packet.
    286 
    287         Args:
    288             ip_dst: ARP ipv4 destination (Optional)
    289             hwsrc: ARP hardware source address (Optional)
    290             hwdst: ARP hardware destination address (Optional)
    291             eth_dst: Ethernet (layer 2) destination address (Optional)
    292         """
    293         # Create IP layer
    294         hw_src = (hwsrc if hwsrc is not None else self.src_mac)
    295         hw_dst = (hwdst if hwdst is not None else ARP_DST)
    296         ipv4_dst = (ip_dst if ip_dst is not None else self.dst_ipv4)
    297         ip4 = scapy.ARP(
    298             pdst=ipv4_dst, psrc=self.src_ipv4, hwdst=hw_dst, hwsrc=hw_src)
    299 
    300         # Create Ethernet layer
    301         mac_dst = (eth_dst if eth_dst is not None else MAC_BROADCAST)
    302         ethernet = scapy.Ether(src=self.src_mac, dst=mac_dst)
    303 
    304         self.packet = ethernet / ip4
    305         return self.packet
    306 
    307 
    308 class DhcpOfferGenerator(object):
    309     """Creates a custom DHCP offer packet
    310 
    311     Attributes:
    312         packet: desired built custom packet
    313         subnet_mask: local network subnet mask
    314         src_mac: MAC address (Layer 2) of the source node
    315         dst_mac: MAC address (Layer 2) of the destination node
    316         src_ipv4: IPv4 address (Layer 3) of the source node
    317         dst_ipv4: IPv4 address (Layer 3) of the destination node
    318         gw_ipv4: IPv4 address (Layer 3) of the Gateway
    319     """
    320 
    321     def __init__(self, **config_params):
    322         """Initialize the class with the required network and packet params.
    323 
    324         Args:
    325             config_params: contains all the necessary packet parameters.
    326               Some fields can be generated automatically. For example:
    327               {'subnet_mask': '255.255.255.0',
    328                'dst_ipv4': '192.168.1.3',
    329                'src_ipv4: 'get_local', ...
    330               The key can also be 'get_local' which means the code will read
    331               and use the local interface parameters
    332         """
    333         interf = config_params['interf']
    334         self.packet = None
    335         self.subnet_mask = config_params['subnet_mask']
    336         self.dst_mac = config_params['dst_mac']
    337         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    338             self.src_mac = scapy.get_if_hwaddr(interf)
    339         else:
    340             self.src_mac = config_params['src_mac']
    341 
    342         self.dst_ipv4 = config_params['dst_ipv4']
    343         if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
    344             self.src_ipv4 = scapy.get_if_addr(interf)
    345         else:
    346             self.src_ipv4 = config_params['src_ipv4']
    347 
    348         self.gw_ipv4 = config_params['gw_ipv4']
    349 
    350     def generate(self, cha_mac=None, dst_ip=None):
    351         """Generates a DHCP offer packet.
    352 
    353         Args:
    354             cha_mac: hardware target address for DHCP offer (Optional)
    355             dst_ip: ipv4 address of target host for renewal (Optional)
    356         """
    357 
    358         # Create DHCP layer
    359         dhcp = scapy.DHCP(options=[
    360             ('message-type', 'offer'),
    361             ('subnet_mask', self.subnet_mask),
    362             ('server_id', self.src_ipv4),
    363             ('end'),
    364         ])
    365 
    366         # Overwrite standard DHCP fields
    367         sta_hw = (cha_mac if cha_mac is not None else self.dst_mac)
    368         sta_ip = (dst_ip if dst_ip is not None else self.dst_ipv4)
    369 
    370         # Create Boot
    371         bootp = scapy.BOOTP(
    372             op=DHCP_OFFER_OP,
    373             yiaddr=sta_ip,
    374             siaddr=self.src_ipv4,
    375             giaddr=self.gw_ipv4,
    376             chaddr=scapy.mac2str(sta_hw),
    377             xid=DHCP_TRANS_ID)
    378 
    379         # Create UDP
    380         udp = scapy.UDP(sport=DHCP_OFFER_SRC_PORT, dport=DHCP_OFFER_DST_PORT)
    381 
    382         # Create IP layer
    383         ip4 = scapy.IP(src=self.src_ipv4, dst=IPV4_BROADCAST)
    384 
    385         # Create Ethernet layer
    386         ethernet = scapy.Ether(dst=MAC_BROADCAST, src=self.src_mac)
    387 
    388         self.packet = ethernet / ip4 / udp / bootp / dhcp
    389         return self.packet
    390 
    391 
    392 class NsGenerator(object):
    393     """Creates a custom Neighbor Solicitation (NS) packet
    394 
    395     Attributes:
    396         packet: desired built custom packet
    397         src_mac: MAC address (Layer 2) of the source node
    398         src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
    399         src_ipv6: IPv6 address (Layer 3) of the source node
    400         dst_ipv6: IPv6 address (Layer 3) of the destination node
    401     """
    402 
    403     def __init__(self, **config_params):
    404         """Initialize the class with the required network and packet params.
    405 
    406         Args:
    407             config_params: contains all the necessary packet parameters.
    408               Some fields can be generated automatically. For example:
    409               {'subnet_mask': '255.255.255.0',
    410                'dst_ipv4': '192.168.1.3',
    411                'src_ipv4: 'get_local', ...
    412               The key can also be 'get_local' which means the code will read
    413               and use the local interface parameters
    414         """
    415         interf = config_params['interf']
    416         self.packet = None
    417         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    418             self.src_mac = scapy.get_if_hwaddr(interf)
    419         else:
    420             self.src_mac = config_params['src_mac']
    421 
    422         self.dst_ipv6 = config_params['dst_ipv6']
    423         self.src_ipv6_type = config_params['src_ipv6_type']
    424         if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
    425             self.src_ipv6 = wputils.get_if_addr6(interf, self.src_ipv6_type)
    426         else:
    427             self.src_ipv6 = config_params['src_ipv6']
    428 
    429     def generate(self, ip_dst=None, eth_dst=None):
    430         """Generates a Neighbor Solicitation (NS) packet (ICMP over IPv6).
    431 
    432         Args:
    433             ip_dst: NS ipv6 destination (Optional)
    434             eth_dst: Ethernet (layer 2) destination address (Optional)
    435         """
    436         # Compute IP addresses
    437         target_ip6 = ip_dst if ip_dst is not None else self.dst_ipv6
    438         ndst_ip = socket.inet_pton(socket.AF_INET6, target_ip6)
    439         nnode_mcast = scapy.in6_getnsma(ndst_ip)
    440         node_mcast = socket.inet_ntop(socket.AF_INET6, nnode_mcast)
    441         # Compute MAC addresses
    442         hw_dst = (eth_dst
    443                   if eth_dst is not None else scapy.in6_getnsmac(nnode_mcast))
    444 
    445         # Create IPv6 layer
    446         base = scapy.IPv6(dst=node_mcast, src=self.src_ipv6)
    447         neighbor_solicitation = scapy.ICMPv6ND_NS(tgt=target_ip6)
    448         src_ll_addr = scapy.ICMPv6NDOptSrcLLAddr(lladdr=self.src_mac)
    449         ip6 = base / neighbor_solicitation / src_ll_addr
    450 
    451         # Create Ethernet layer
    452         ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
    453 
    454         self.packet = ethernet / ip6
    455         return self.packet
    456 
    457 
    458 class RaGenerator(object):
    459     """Creates a custom Router Advertisement (RA) packet
    460 
    461     Attributes:
    462         packet: desired built custom packet
    463         src_mac: MAC address (Layer 2) of the source node
    464         src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
    465         src_ipv6: IPv6 address (Layer 3) of the source node
    466     """
    467 
    468     def __init__(self, **config_params):
    469         """Initialize the class with the required network and packet params.
    470 
    471         Args:
    472             config_params: contains all the necessary packet parameters.
    473               Some fields can be generated automatically. For example:
    474               {'subnet_mask': '255.255.255.0',
    475                'dst_ipv4': '192.168.1.3',
    476                'src_ipv4: 'get_local', ...
    477               The key can also be 'get_local' which means the code will read
    478               and use the local interface parameters
    479         """
    480         interf = config_params['interf']
    481         self.packet = None
    482         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    483             self.src_mac = scapy.get_if_hwaddr(interf)
    484         else:
    485             self.src_mac = config_params['src_mac']
    486 
    487         self.src_ipv6_type = config_params['src_ipv6_type']
    488         if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
    489             self.src_ipv6 = wputils.get_if_addr6(interf, self.src_ipv6_type)
    490         else:
    491             self.src_ipv6 = config_params['src_ipv6']
    492 
    493     def generate(self,
    494                  lifetime,
    495                  enableDNS=False,
    496                  dns_lifetime=0,
    497                  ip_dst=None,
    498                  eth_dst=None):
    499         """Generates a Router Advertisement (RA) packet (ICMP over IPv6).
    500 
    501         Args:
    502             lifetime: RA lifetime
    503             enableDNS: Add RDNSS option to RA (Optional)
    504             dns_lifetime: Set DNS server lifetime (Optional)
    505             ip_dst: IPv6 destination address (Optional)
    506             eth_dst: Ethernet (layer 2) destination address (Optional)
    507         """
    508         # Overwrite standard fields if desired
    509         ip6_dst = (ip_dst if ip_dst is not None else RA_IP)
    510         hw_dst = (eth_dst if eth_dst is not None else RA_MAC)
    511 
    512         # Create IPv6 layer
    513         base = scapy.IPv6(dst=ip6_dst, src=self.src_ipv6)
    514         router_solicitation = scapy.ICMPv6ND_RA(routerlifetime=lifetime)
    515         src_ll_addr = scapy.ICMPv6NDOptSrcLLAddr(lladdr=self.src_mac)
    516         prefix = scapy.ICMPv6NDOptPrefixInfo(
    517             prefixlen=RA_PREFIX_LEN, prefix=RA_PREFIX)
    518         if enableDNS:
    519             rndss = scapy.ICMPv6NDOptRDNSS(
    520                 lifetime=dns_lifetime, dns=[self.src_ipv6], len=DNS_LEN)
    521             ip6 = base / router_solicitation / src_ll_addr / prefix / rndss
    522         else:
    523             ip6 = base / router_solicitation / src_ll_addr / prefix
    524 
    525         # Create Ethernet layer
    526         ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
    527 
    528         self.packet = ethernet / ip6
    529         return self.packet
    530 
    531 
    532 class Ping6Generator(object):
    533     """Creates a custom Ping v6 packet (i.e., ICMP over IPv6)
    534 
    535     Attributes:
    536         packet: desired built custom packet
    537         src_mac: MAC address (Layer 2) of the source node
    538         dst_mac: MAC address (Layer 2) of the destination node
    539         src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
    540         src_ipv6: IPv6 address (Layer 3) of the source node
    541         dst_ipv6: IPv6 address (Layer 3) of the destination node
    542     """
    543 
    544     def __init__(self, **config_params):
    545         """Initialize the class with the required network and packet params.
    546 
    547         Args:
    548             config_params: contains all the necessary packet parameters.
    549               Some fields can be generated automatically. For example:
    550               {'subnet_mask': '255.255.255.0',
    551                'dst_ipv4': '192.168.1.3',
    552                'src_ipv4: 'get_local', ...
    553               The key can also be 'get_local' which means the code will read
    554               and use the local interface parameters
    555         """
    556         interf = config_params['interf']
    557         self.packet = None
    558         self.dst_mac = config_params['dst_mac']
    559         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    560             self.src_mac = scapy.get_if_hwaddr(interf)
    561         else:
    562             self.src_mac = config_params['src_mac']
    563 
    564         self.dst_ipv6 = config_params['dst_ipv6']
    565         self.src_ipv6_type = config_params['src_ipv6_type']
    566         if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
    567             self.src_ipv6 = wputils.get_if_addr6(interf, self.src_ipv6_type)
    568         else:
    569             self.src_ipv6 = config_params['src_ipv6']
    570 
    571     def generate(self, ip_dst=None, eth_dst=None):
    572         """Generates a Ping6 packet (i.e., Echo Request)
    573 
    574         Args:
    575             ip_dst: IPv6 destination address (Optional)
    576             eth_dst: Ethernet (layer 2) destination address (Optional)
    577         """
    578         # Overwrite standard fields if desired
    579         ip6_dst = (ip_dst if ip_dst is not None else self.dst_ipv6)
    580         hw_dst = (eth_dst if eth_dst is not None else self.dst_mac)
    581 
    582         # Create IPv6 layer
    583         base = scapy.IPv6(dst=ip6_dst, src=self.src_ipv6)
    584         echo_request = scapy.ICMPv6EchoRequest(data=PING6_DATA)
    585 
    586         ip6 = base / echo_request
    587 
    588         # Create Ethernet layer
    589         ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
    590 
    591         self.packet = ethernet / ip6
    592         return self.packet
    593 
    594 
    595 class Ping4Generator(object):
    596     """Creates a custom Ping v4 packet (i.e., ICMP over IPv4)
    597 
    598     Attributes:
    599         packet: desired built custom packet
    600         src_mac: MAC address (Layer 2) of the source node
    601         dst_mac: MAC address (Layer 2) of the destination node
    602         src_ipv4: IPv4 address (Layer 3) of the source node
    603         dst_ipv4: IPv4 address (Layer 3) of the destination node
    604     """
    605 
    606     def __init__(self, **config_params):
    607         """Initialize the class with the required network and packet params.
    608 
    609         Args:
    610             config_params: contains all the necessary packet parameters.
    611               Some fields can be generated automatically. For example:
    612               {'subnet_mask': '255.255.255.0',
    613                'dst_ipv4': '192.168.1.3',
    614                'src_ipv4: 'get_local', ...
    615               The key can also be 'get_local' which means the code will read
    616               and use the local interface parameters
    617         """
    618         interf = config_params['interf']
    619         self.packet = None
    620         self.dst_mac = config_params['dst_mac']
    621         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    622             self.src_mac = scapy.get_if_hwaddr(interf)
    623         else:
    624             self.src_mac = config_params['src_mac']
    625 
    626         self.dst_ipv4 = config_params['dst_ipv4']
    627         if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
    628             self.src_ipv4 = scapy.get_if_addr(interf)
    629         else:
    630             self.src_ipv4 = config_params['src_ipv4']
    631 
    632     def generate(self, ip_dst=None, eth_dst=None):
    633         """Generates a Ping4 packet (i.e., Echo Request)
    634 
    635         Args:
    636             ip_dst: IP destination address (Optional)
    637             eth_dst: Ethernet (layer 2) destination address (Optional)
    638         """
    639 
    640         # Overwrite standard fields if desired
    641         sta_ip = (ip_dst if ip_dst is not None else self.dst_ipv4)
    642         sta_hw = (eth_dst if eth_dst is not None else self.dst_mac)
    643 
    644         # Create IPv6 layer
    645         base = scapy.IP(src=self.src_ipv4, dst=sta_ip)
    646         echo_request = scapy.ICMP(type=PING4_TYPE)
    647 
    648         ip4 = base / echo_request
    649 
    650         # Create Ethernet layer
    651         ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
    652 
    653         self.packet = ethernet / ip4
    654         return self.packet
    655 
    656 
    657 class Mdns6Generator(object):
    658     """Creates a custom mDNS IPv6 packet
    659 
    660     Attributes:
    661         packet: desired built custom packet
    662         src_mac: MAC address (Layer 2) of the source node
    663         src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
    664         src_ipv6: IPv6 address (Layer 3) of the source node
    665     """
    666 
    667     def __init__(self, **config_params):
    668         """Initialize the class with the required network and packet params.
    669 
    670         Args:
    671             config_params: contains all the necessary packet parameters.
    672               Some fields can be generated automatically. For example:
    673               {'subnet_mask': '255.255.255.0',
    674                'dst_ipv4': '192.168.1.3',
    675                'src_ipv4: 'get_local', ...
    676               The key can also be 'get_local' which means the code will read
    677               and use the local interface parameters
    678         """
    679         interf = config_params['interf']
    680         self.packet = None
    681         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    682             self.src_mac = scapy.get_if_hwaddr(interf)
    683         else:
    684             self.src_mac = config_params['src_mac']
    685 
    686         self.src_ipv6_type = config_params['src_ipv6_type']
    687         if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
    688             self.src_ipv6 = wputils.get_if_addr6(interf, self.src_ipv6_type)
    689         else:
    690             self.src_ipv6 = config_params['src_ipv6']
    691 
    692     def generate(self, ip_dst=None, eth_dst=None):
    693         """Generates a mDNS v6 packet for multicast DNS config
    694 
    695         Args:
    696             ip_dst: IPv6 destination address (Optional)
    697             eth_dst: Ethernet (layer 2) destination address (Optional)
    698         """
    699 
    700         # Overwrite standard fields if desired
    701         sta_ip = (ip_dst if ip_dst is not None else MDNS_V6_IP_DST)
    702         sta_hw = (eth_dst if eth_dst is not None else MDNS_V6_MAC_DST)
    703 
    704         # Create mDNS layer
    705         qdServer = scapy.DNSQR(qname=self.src_ipv6, qtype=MDNS_QTYPE)
    706         mDNS = scapy.DNS(rd=MDNS_RECURSIVE, qd=qdServer)
    707 
    708         # Create UDP
    709         udp = scapy.UDP(sport=MDNS_UDP_PORT, dport=MDNS_UDP_PORT)
    710 
    711         # Create IP layer
    712         ip6 = scapy.IPv6(src=self.src_ipv6, dst=sta_ip)
    713 
    714         # Create Ethernet layer
    715         ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
    716 
    717         self.packet = ethernet / ip6 / udp / mDNS
    718         return self.packet
    719 
    720 
    721 class Mdns4Generator(object):
    722     """Creates a custom mDNS v4 packet
    723 
    724     Attributes:
    725         packet: desired built custom packet
    726         src_mac: MAC address (Layer 2) of the source node
    727         src_ipv4: IPv4 address (Layer 3) of the source node
    728     """
    729 
    730     def __init__(self, **config_params):
    731         """Initialize the class with the required network and packet params.
    732 
    733         Args:
    734             config_params: contains all the necessary packet parameters.
    735               Some fields can be generated automatically. For example:
    736               {'subnet_mask': '255.255.255.0',
    737                'dst_ipv4': '192.168.1.3',
    738                'src_ipv4: 'get_local', ...
    739               The key can also be 'get_local' which means the code will read
    740               and use the local interface parameters
    741         """
    742         interf = config_params['interf']
    743         self.packet = None
    744         if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
    745             self.src_mac = scapy.get_if_hwaddr(interf)
    746         else:
    747             self.src_mac = config_params['src_mac']
    748 
    749         if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
    750             self.src_ipv4 = scapy.get_if_addr(interf)
    751         else:
    752             self.src_ipv4 = config_params['src_ipv4']
    753 
    754     def generate(self, ip_dst=None, eth_dst=None):
    755         """Generates a mDNS v4 packet for multicast DNS config
    756 
    757         Args:
    758             ip_dst: IP destination address (Optional)
    759             eth_dst: Ethernet (layer 2) destination address (Optional)
    760         """
    761 
    762         # Overwrite standard fields if desired
    763         sta_ip = (ip_dst if ip_dst is not None else MDNS_V4_IP_DST)
    764         sta_hw = (eth_dst if eth_dst is not None else MDNS_V4_MAC_DST)
    765 
    766         # Create mDNS layer
    767         qdServer = scapy.DNSQR(qname=self.src_ipv4, qtype=MDNS_QTYPE)
    768         mDNS = scapy.DNS(rd=MDNS_RECURSIVE, qd=qdServer)
    769 
    770         # Create UDP
    771         udp = scapy.UDP(sport=MDNS_UDP_PORT, dport=MDNS_UDP_PORT)
    772 
    773         # Create IP layer
    774         ip4 = scapy.IP(src=self.src_ipv4, dst=sta_ip, ttl=255)
    775 
    776         # Create Ethernet layer
    777         ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
    778 
    779         self.packet = ethernet / ip4 / udp / mDNS
    780         return self.packet
    781