• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
19import logging
20import multiprocessing
21import socket
22import time
23
24import acts.signals
25
26# http://www.secdev.org/projects/scapy/
27# On ubuntu, sudo pip3 install scapy
28import scapy.all as scapy
29
30MOBLY_CONTROLLER_CONFIG_NAME = 'PacketSender'
31ACTS_CONTROLLER_REFERENCE_NAME = 'packet_senders'
32
33GET_FROM_LOCAL_INTERFACE = 'get_local'
34MAC_BROADCAST = 'ff:ff:ff:ff:ff:ff'
35IPV4_BROADCAST = '255.255.255.255'
36ARP_DST = '00:00:00:00:00:00'
37RA_MAC = '33:33:00:00:00:01'
38RA_IP = 'ff02::1'
39RA_PREFIX = 'd00d::'
40RA_PREFIX_LEN = 64
41DHCP_OFFER_OP = 2
42DHCP_OFFER_SRC_PORT = 67
43DHCP_OFFER_DST_PORT = 68
44DHCP_TRANS_ID = 0x01020304
45DNS_LEN = 3
46PING6_DATA = 'BEST PING6 EVER'
47PING4_TYPE = 8
48MDNS_TTL = 255
49MDNS_QTYPE = 'PTR'
50MDNS_UDP_PORT = 5353
51MDNS_V4_IP_DST = '224.0.0.251'
52MDNS_V4_MAC_DST = '01:00:5E:00:00:FB'
53MDNS_RECURSIVE = 1
54MDNS_V6_IP_DST = 'FF02::FB'
55MDNS_V6_MAC_DST = '33:33:00:00:00:FB'
56ETH_TYPE_IP = 2048
57SAP_SPANNING_TREE = 0x42
58SNAP_OUI = 12
59SNAP_SSAP = 170
60SNAP_DSAP = 170
61SNAP_CTRL = 3
62LLC_XID_CONTROL = 191
63PAD_LEN_BYTES = 128
64
65
66def create(configs):
67    """Creates PacketSender controllers from a json config.
68
69    Args:
70        The json configs that represent this controller
71
72    Returns:
73        A new PacketSender
74    """
75    return [PacketSender(c) for c in configs]
76
77
78def destroy(objs):
79    """Destroys a list of PacketSenders and stops sending (if active).
80
81    Args:
82        objs: A list of PacketSenders
83    """
84    for pkt_sender in objs:
85        pkt_sender.stop_sending(True)
86    return
87
88
89def get_info(objs):
90    """Get information on a list of packet senders.
91
92    Args:
93        objs: A list of PacketSenders
94
95    Returns:
96        Network interface name that is being used by each packet sender
97    """
98    return [pkt_sender.interface for pkt_sender in objs]
99
100
101class ThreadSendPacket(multiprocessing.Process):
102    """Creates a thread that keeps sending the same packet until a stop signal.
103
104    Attributes:
105        stop_signal: signal to stop the thread execution
106        packet: desired packet to keep sending
107        interval: interval between consecutive packets (s)
108        interface: network interface name (e.g., 'eth0')
109        log: object used for logging
110    """
111
112    def __init__(self, signal, packet, interval, interface, log):
113        multiprocessing.Process.__init__(self)
114        self.stop_signal = signal
115        self.packet = packet
116        self.interval = interval
117        self.interface = interface
118        self.log = log
119
120    def run(self):
121        self.log.info('Packet Sending Started.')
122        while True:
123            if self.stop_signal.is_set():
124                # Poison pill means shutdown
125                self.log.info('Packet Sending Stopped.')
126                break
127
128            try:
129                scapy.sendp(self.packet, iface=self.interface, verbose=0)
130                time.sleep(self.interval)
131            except Exception:
132                self.log.exception('Exception when trying to send packet')
133                return
134
135        return
136
137
138class PacketSenderError(acts.signals.ControllerError):
139    """Raises exceptions encountered in packet sender lib."""
140
141
142class PacketSender(object):
143    """Send any custom packet over a desired interface.
144
145    Attributes:
146        log: class logging object
147        thread_active: indicates whether or not the send thread is active
148        thread_send: thread object for the concurrent packet transmissions
149        stop_signal: event to stop the thread
150        interface: network interface name (e.g., 'eth0')
151    """
152
153    def __init__(self, ifname):
154        """Initiallize the PacketGenerator class.
155
156        Args:
157            ifname: network interface name that will be used packet generator
158        """
159        self.log = logging.getLogger()
160        self.packet = None
161        self.thread_active = False
162        self.thread_send = None
163        self.stop_signal = multiprocessing.Event()
164        self.interface = ifname
165
166    def send_ntimes(self, packet, ntimes, interval):
167        """Sends a packet ntimes at a given interval.
168
169        Args:
170            packet: custom built packet from Layer 2 up to Application layer
171            ntimes: number of packets to send
172            interval: interval between consecutive packet transmissions (s)
173        """
174        if packet is None:
175            raise PacketSenderError(
176                'There is no packet to send. Create a packet first.')
177
178        for _ in range(ntimes):
179            try:
180                scapy.sendp(packet, iface=self.interface, verbose=0)
181                time.sleep(interval)
182            except socket.error as excpt:
183                self.log.exception('Caught socket exception : %s' % excpt)
184                return
185
186    def send_receive_ntimes(self, packet, ntimes, interval):
187        """Sends a packet and receives the reply ntimes at a given interval.
188
189        Args:
190            packet: custom built packet from Layer 2 up to Application layer
191            ntimes: number of packets to send
192            interval: interval between consecutive packet transmissions and
193                      the corresponding reply (s)
194        """
195        if packet is None:
196            raise PacketSenderError(
197                'There is no packet to send. Create a packet first.')
198
199        for _ in range(ntimes):
200            try:
201                scapy.srp1(
202                    packet, iface=self.interface, timeout=interval, verbose=0)
203                time.sleep(interval)
204            except socket.error as excpt:
205                self.log.exception('Caught socket exception : %s' % excpt)
206                return
207
208    def start_sending(self, packet, interval):
209        """Sends packets in parallel with the main process.
210
211        Creates a thread and keeps sending the same packet at a given interval
212        until a stop signal is received
213
214        Args:
215            packet: custom built packet from Layer 2 up to Application layer
216            interval: interval between consecutive packets (s)
217        """
218        if packet is None:
219            raise PacketSenderError(
220                'There is no packet to send. Create a packet first.')
221
222        if self.thread_active:
223            raise PacketSenderError(
224                ('There is already an active thread. Stop it'
225                 'before starting another transmission.'))
226
227        self.thread_send = ThreadSendPacket(self.stop_signal, packet, interval,
228                                            self.interface, self.log)
229        self.thread_send.start()
230        self.thread_active = True
231
232    def stop_sending(self, ignore_status=False):
233        """Stops the concurrent thread that is continuously sending packets.
234
235       """
236        if not self.thread_active:
237            if ignore_status:
238                return
239            else:
240                raise PacketSenderError(
241                    'Error: There is no acive thread running to stop.')
242
243        # Stop thread
244        self.stop_signal.set()
245        self.thread_send.join()
246
247        # Just as precaution
248        if self.thread_send.is_alive():
249            self.thread_send.terminate()
250            self.log.warning('Packet Sending forced to terminate')
251
252        self.stop_signal.clear()
253        self.thread_send = None
254        self.thread_active = False
255
256
257class ArpGenerator(object):
258    """Creates a custom ARP packet
259
260    Attributes:
261        packet: desired built custom packet
262        src_mac: MAC address (Layer 2) of the source node
263        src_ipv4: IPv4 address (Layer 3) of the source node
264        dst_ipv4: IPv4 address (Layer 3) of the destination node
265    """
266
267    def __init__(self, **config_params):
268        """Initialize the class with the required network and packet params.
269
270        Args:
271            config_params: a dictionary with all the necessary packet fields.
272              Some fields can be generated automatically. For example:
273              {'subnet_mask': '255.255.255.0',
274               'dst_ipv4': '192.168.1.3',
275               'src_ipv4: 'get_local', ...
276              The key can also be 'get_local' which means the code will read
277              and use the local interface parameters
278        """
279        interf = config_params['interf']
280        self.packet = None
281        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
282            self.src_mac = scapy.get_if_hwaddr(interf)
283        else:
284            self.src_mac = config_params['src_mac']
285
286        self.dst_ipv4 = config_params['dst_ipv4']
287        if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
288            self.src_ipv4 = scapy.get_if_addr(interf)
289        else:
290            self.src_ipv4 = config_params['src_ipv4']
291
292    def generate(self,
293                 op='who-has',
294                 ip_dst=None,
295                 ip_src=None,
296                 hwsrc=None,
297                 hwdst=None,
298                 eth_dst=None):
299        """Generates a custom ARP packet.
300
301        Args:
302            op: ARP type (request or reply)
303            ip_dst: ARP ipv4 destination (Optional)
304            ip_src: ARP ipv4 source address (Optional)
305            hwsrc: ARP hardware source address (Optional)
306            hwdst: ARP hardware destination address (Optional)
307            eth_dst: Ethernet (layer 2) destination address (Optional)
308        """
309        # Create IP layer
310        hw_src = (hwsrc if hwsrc is not None else self.src_mac)
311        hw_dst = (hwdst if hwdst is not None else ARP_DST)
312        ipv4_dst = (ip_dst if ip_dst is not None else self.dst_ipv4)
313        ipv4_src = (ip_src if ip_src is not None else self.src_ipv4)
314        ip4 = scapy.ARP(
315            op=op, pdst=ipv4_dst, psrc=ipv4_src, hwdst=hw_dst, hwsrc=hw_src)
316
317        # Create Ethernet layer
318        mac_dst = (eth_dst if eth_dst is not None else MAC_BROADCAST)
319        ethernet = scapy.Ether(src=self.src_mac, dst=mac_dst)
320
321        self.packet = ethernet / ip4
322        return self.packet
323
324
325class DhcpOfferGenerator(object):
326    """Creates a custom DHCP offer packet
327
328    Attributes:
329        packet: desired built custom packet
330        subnet_mask: local network subnet mask
331        src_mac: MAC address (Layer 2) of the source node
332        dst_mac: MAC address (Layer 2) of the destination node
333        src_ipv4: IPv4 address (Layer 3) of the source node
334        dst_ipv4: IPv4 address (Layer 3) of the destination node
335        gw_ipv4: IPv4 address (Layer 3) of the Gateway
336    """
337
338    def __init__(self, **config_params):
339        """Initialize the class with the required network and packet params.
340
341        Args:
342            config_params: contains all the necessary packet parameters.
343              Some fields can be generated automatically. For example:
344              {'subnet_mask': '255.255.255.0',
345               'dst_ipv4': '192.168.1.3',
346               'src_ipv4: 'get_local', ...
347              The key can also be 'get_local' which means the code will read
348              and use the local interface parameters
349        """
350        interf = config_params['interf']
351        self.packet = None
352        self.subnet_mask = config_params['subnet_mask']
353        self.dst_mac = config_params['dst_mac']
354        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
355            self.src_mac = scapy.get_if_hwaddr(interf)
356        else:
357            self.src_mac = config_params['src_mac']
358
359        self.dst_ipv4 = config_params['dst_ipv4']
360        if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
361            self.src_ipv4 = scapy.get_if_addr(interf)
362        else:
363            self.src_ipv4 = config_params['src_ipv4']
364
365        self.gw_ipv4 = config_params['gw_ipv4']
366
367    def generate(self, cha_mac=None, dst_ip=None):
368        """Generates a DHCP offer packet.
369
370        Args:
371            cha_mac: hardware target address for DHCP offer (Optional)
372            dst_ip: ipv4 address of target host for renewal (Optional)
373        """
374
375        # Create DHCP layer
376        dhcp = scapy.DHCP(options=[
377            ('message-type', 'offer'),
378            ('subnet_mask', self.subnet_mask),
379            ('server_id', self.src_ipv4),
380            ('end'),
381        ])
382
383        # Overwrite standard DHCP fields
384        sta_hw = (cha_mac if cha_mac is not None else self.dst_mac)
385        sta_ip = (dst_ip if dst_ip is not None else self.dst_ipv4)
386
387        # Create Boot
388        bootp = scapy.BOOTP(
389            op=DHCP_OFFER_OP,
390            yiaddr=sta_ip,
391            siaddr=self.src_ipv4,
392            giaddr=self.gw_ipv4,
393            chaddr=scapy.mac2str(sta_hw),
394            xid=DHCP_TRANS_ID)
395
396        # Create UDP
397        udp = scapy.UDP(sport=DHCP_OFFER_SRC_PORT, dport=DHCP_OFFER_DST_PORT)
398
399        # Create IP layer
400        ip4 = scapy.IP(src=self.src_ipv4, dst=IPV4_BROADCAST)
401
402        # Create Ethernet layer
403        ethernet = scapy.Ether(dst=MAC_BROADCAST, src=self.src_mac)
404
405        self.packet = ethernet / ip4 / udp / bootp / dhcp
406        return self.packet
407
408
409class NsGenerator(object):
410    """Creates a custom Neighbor Solicitation (NS) packet
411
412    Attributes:
413        packet: desired built custom packet
414        src_mac: MAC address (Layer 2) of the source node
415        src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
416        src_ipv6: IPv6 address (Layer 3) of the source node
417        dst_ipv6: IPv6 address (Layer 3) of the destination node
418    """
419
420    def __init__(self, **config_params):
421        """Initialize the class with the required network and packet params.
422
423        Args:
424            config_params: contains all the necessary packet parameters.
425              Some fields can be generated automatically. For example:
426              {'subnet_mask': '255.255.255.0',
427               'dst_ipv4': '192.168.1.3',
428               'src_ipv4: 'get_local', ...
429              The key can also be 'get_local' which means the code will read
430              and use the local interface parameters
431        """
432        interf = config_params['interf']
433        self.packet = None
434        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
435            self.src_mac = scapy.get_if_hwaddr(interf)
436        else:
437            self.src_mac = config_params['src_mac']
438
439        self.dst_ipv6 = config_params['dst_ipv6']
440        self.src_ipv6_type = config_params['src_ipv6_type']
441        if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
442            self.src_ipv6 = get_if_addr6(interf, self.src_ipv6_type)
443        else:
444            self.src_ipv6 = config_params['src_ipv6']
445
446    def generate(self, ip_dst=None, eth_dst=None):
447        """Generates a Neighbor Solicitation (NS) packet (ICMP over IPv6).
448
449        Args:
450            ip_dst: NS ipv6 destination (Optional)
451            eth_dst: Ethernet (layer 2) destination address (Optional)
452        """
453        # Compute IP addresses
454        target_ip6 = ip_dst if ip_dst is not None else self.dst_ipv6
455        ndst_ip = socket.inet_pton(socket.AF_INET6, target_ip6)
456        nnode_mcast = scapy.in6_getnsma(ndst_ip)
457        node_mcast = socket.inet_ntop(socket.AF_INET6, nnode_mcast)
458        # Compute MAC addresses
459        hw_dst = (eth_dst
460                  if eth_dst is not None else scapy.in6_getnsmac(nnode_mcast))
461
462        # Create IPv6 layer
463        base = scapy.IPv6(dst=node_mcast, src=self.src_ipv6)
464        neighbor_solicitation = scapy.ICMPv6ND_NS(tgt=target_ip6)
465        src_ll_addr = scapy.ICMPv6NDOptSrcLLAddr(lladdr=self.src_mac)
466        ip6 = base / neighbor_solicitation / src_ll_addr
467
468        # Create Ethernet layer
469        ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
470
471        self.packet = ethernet / ip6
472        return self.packet
473
474
475class RaGenerator(object):
476    """Creates a custom Router Advertisement (RA) packet
477
478    Attributes:
479        packet: desired built custom packet
480        src_mac: MAC address (Layer 2) of the source node
481        src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
482        src_ipv6: IPv6 address (Layer 3) of the source node
483    """
484
485    def __init__(self, **config_params):
486        """Initialize the class with the required network and packet params.
487
488        Args:
489            config_params: contains all the necessary packet parameters.
490              Some fields can be generated automatically. For example:
491              {'subnet_mask': '255.255.255.0',
492               'dst_ipv4': '192.168.1.3',
493               'src_ipv4: 'get_local', ...
494              The key can also be 'get_local' which means the code will read
495              and use the local interface parameters
496        """
497        interf = config_params['interf']
498        self.packet = None
499        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
500            self.src_mac = scapy.get_if_hwaddr(interf)
501        else:
502            self.src_mac = config_params['src_mac']
503
504        self.src_ipv6_type = config_params['src_ipv6_type']
505        if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
506            self.src_ipv6 = get_if_addr6(interf, self.src_ipv6_type)
507        else:
508            self.src_ipv6 = config_params['src_ipv6']
509
510    def generate(self,
511                 lifetime,
512                 enableDNS=False,
513                 dns_lifetime=0,
514                 ip_dst=None,
515                 eth_dst=None):
516        """Generates a Router Advertisement (RA) packet (ICMP over IPv6).
517
518        Args:
519            lifetime: RA lifetime
520            enableDNS: Add RDNSS option to RA (Optional)
521            dns_lifetime: Set DNS server lifetime (Optional)
522            ip_dst: IPv6 destination address (Optional)
523            eth_dst: Ethernet (layer 2) destination address (Optional)
524        """
525        # Overwrite standard fields if desired
526        ip6_dst = (ip_dst if ip_dst is not None else RA_IP)
527        hw_dst = (eth_dst if eth_dst is not None else RA_MAC)
528
529        # Create IPv6 layer
530        base = scapy.IPv6(dst=ip6_dst, src=self.src_ipv6)
531        router_solicitation = scapy.ICMPv6ND_RA(routerlifetime=lifetime)
532        src_ll_addr = scapy.ICMPv6NDOptSrcLLAddr(lladdr=self.src_mac)
533        prefix = scapy.ICMPv6NDOptPrefixInfo(
534            prefixlen=RA_PREFIX_LEN, prefix=RA_PREFIX)
535        if enableDNS:
536            rndss = scapy.ICMPv6NDOptRDNSS(
537                lifetime=dns_lifetime, dns=[self.src_ipv6], len=DNS_LEN)
538            ip6 = base / router_solicitation / src_ll_addr / prefix / rndss
539        else:
540            ip6 = base / router_solicitation / src_ll_addr / prefix
541
542        # Create Ethernet layer
543        ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
544
545        self.packet = ethernet / ip6
546        return self.packet
547
548
549class Ping6Generator(object):
550    """Creates a custom Ping v6 packet (i.e., ICMP over IPv6)
551
552    Attributes:
553        packet: desired built custom packet
554        src_mac: MAC address (Layer 2) of the source node
555        dst_mac: MAC address (Layer 2) of the destination node
556        src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
557        src_ipv6: IPv6 address (Layer 3) of the source node
558        dst_ipv6: IPv6 address (Layer 3) of the destination node
559    """
560
561    def __init__(self, **config_params):
562        """Initialize the class with the required network and packet params.
563
564        Args:
565            config_params: contains all the necessary packet parameters.
566              Some fields can be generated automatically. For example:
567              {'subnet_mask': '255.255.255.0',
568               'dst_ipv4': '192.168.1.3',
569               'src_ipv4: 'get_local', ...
570              The key can also be 'get_local' which means the code will read
571              and use the local interface parameters
572        """
573        interf = config_params['interf']
574        self.packet = None
575        self.dst_mac = config_params['dst_mac']
576        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
577            self.src_mac = scapy.get_if_hwaddr(interf)
578        else:
579            self.src_mac = config_params['src_mac']
580
581        self.dst_ipv6 = config_params['dst_ipv6']
582        self.src_ipv6_type = config_params['src_ipv6_type']
583        if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
584            self.src_ipv6 = get_if_addr6(interf, self.src_ipv6_type)
585        else:
586            self.src_ipv6 = config_params['src_ipv6']
587
588    def generate(self, ip_dst=None, eth_dst=None):
589        """Generates a Ping6 packet (i.e., Echo Request)
590
591        Args:
592            ip_dst: IPv6 destination address (Optional)
593            eth_dst: Ethernet (layer 2) destination address (Optional)
594        """
595        # Overwrite standard fields if desired
596        ip6_dst = (ip_dst if ip_dst is not None else self.dst_ipv6)
597        hw_dst = (eth_dst if eth_dst is not None else self.dst_mac)
598
599        # Create IPv6 layer
600        base = scapy.IPv6(dst=ip6_dst, src=self.src_ipv6)
601        echo_request = scapy.ICMPv6EchoRequest(data=PING6_DATA)
602
603        ip6 = base / echo_request
604
605        # Create Ethernet layer
606        ethernet = scapy.Ether(src=self.src_mac, dst=hw_dst)
607
608        self.packet = ethernet / ip6
609        return self.packet
610
611
612class Ping4Generator(object):
613    """Creates a custom Ping v4 packet (i.e., ICMP over IPv4)
614
615    Attributes:
616        packet: desired built custom packet
617        src_mac: MAC address (Layer 2) of the source node
618        dst_mac: MAC address (Layer 2) of the destination node
619        src_ipv4: IPv4 address (Layer 3) of the source node
620        dst_ipv4: IPv4 address (Layer 3) of the destination node
621    """
622
623    def __init__(self, **config_params):
624        """Initialize the class with the required network and packet params.
625
626        Args:
627            config_params: contains all the necessary packet parameters.
628              Some fields can be generated automatically. For example:
629              {'subnet_mask': '255.255.255.0',
630               'dst_ipv4': '192.168.1.3',
631               'src_ipv4: 'get_local', ...
632              The key can also be 'get_local' which means the code will read
633              and use the local interface parameters
634        """
635        interf = config_params['interf']
636        self.packet = None
637        self.dst_mac = config_params['dst_mac']
638        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
639            self.src_mac = scapy.get_if_hwaddr(interf)
640        else:
641            self.src_mac = config_params['src_mac']
642
643        self.dst_ipv4 = config_params['dst_ipv4']
644        if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
645            self.src_ipv4 = scapy.get_if_addr(interf)
646        else:
647            self.src_ipv4 = config_params['src_ipv4']
648
649    def generate(self, ip_dst=None, eth_dst=None):
650        """Generates a Ping4 packet (i.e., Echo Request)
651
652        Args:
653            ip_dst: IP destination address (Optional)
654            eth_dst: Ethernet (layer 2) destination address (Optional)
655        """
656
657        # Overwrite standard fields if desired
658        sta_ip = (ip_dst if ip_dst is not None else self.dst_ipv4)
659        sta_hw = (eth_dst if eth_dst is not None else self.dst_mac)
660
661        # Create IPv6 layer
662        base = scapy.IP(src=self.src_ipv4, dst=sta_ip)
663        echo_request = scapy.ICMP(type=PING4_TYPE)
664
665        ip4 = base / echo_request
666
667        # Create Ethernet layer
668        ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
669
670        self.packet = ethernet / ip4
671        return self.packet
672
673
674class Mdns6Generator(object):
675    """Creates a custom mDNS IPv6 packet
676
677    Attributes:
678        packet: desired built custom packet
679        src_mac: MAC address (Layer 2) of the source node
680        src_ipv6_type: IPv6 source address type (e.g., Link Local, Global, etc)
681        src_ipv6: IPv6 address (Layer 3) of the source node
682    """
683
684    def __init__(self, **config_params):
685        """Initialize the class with the required network and packet params.
686
687        Args:
688            config_params: contains all the necessary packet parameters.
689              Some fields can be generated automatically. For example:
690              {'subnet_mask': '255.255.255.0',
691               'dst_ipv4': '192.168.1.3',
692               'src_ipv4: 'get_local', ...
693              The key can also be 'get_local' which means the code will read
694              and use the local interface parameters
695        """
696        interf = config_params['interf']
697        self.packet = None
698        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
699            self.src_mac = scapy.get_if_hwaddr(interf)
700        else:
701            self.src_mac = config_params['src_mac']
702
703        self.src_ipv6_type = config_params['src_ipv6_type']
704        if config_params['src_ipv6'] == GET_FROM_LOCAL_INTERFACE:
705            self.src_ipv6 = get_if_addr6(interf, self.src_ipv6_type)
706        else:
707            self.src_ipv6 = config_params['src_ipv6']
708
709    def generate(self, ip_dst=None, eth_dst=None):
710        """Generates a mDNS v6 packet for multicast DNS config
711
712        Args:
713            ip_dst: IPv6 destination address (Optional)
714            eth_dst: Ethernet (layer 2) destination address (Optional)
715        """
716
717        # Overwrite standard fields if desired
718        sta_ip = (ip_dst if ip_dst is not None else MDNS_V6_IP_DST)
719        sta_hw = (eth_dst if eth_dst is not None else MDNS_V6_MAC_DST)
720
721        # Create mDNS layer
722        qdServer = scapy.DNSQR(qname=self.src_ipv6, qtype=MDNS_QTYPE)
723        mDNS = scapy.DNS(rd=MDNS_RECURSIVE, qd=qdServer)
724
725        # Create UDP
726        udp = scapy.UDP(sport=MDNS_UDP_PORT, dport=MDNS_UDP_PORT)
727
728        # Create IP layer
729        ip6 = scapy.IPv6(src=self.src_ipv6, dst=sta_ip)
730
731        # Create Ethernet layer
732        ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
733
734        self.packet = ethernet / ip6 / udp / mDNS
735        return self.packet
736
737
738class Mdns4Generator(object):
739    """Creates a custom mDNS v4 packet
740
741    Attributes:
742        packet: desired built custom packet
743        src_mac: MAC address (Layer 2) of the source node
744        src_ipv4: IPv4 address (Layer 3) of the source node
745    """
746
747    def __init__(self, **config_params):
748        """Initialize the class with the required network and packet params.
749
750        Args:
751            config_params: contains all the necessary packet parameters.
752              Some fields can be generated automatically. For example:
753              {'subnet_mask': '255.255.255.0',
754               'dst_ipv4': '192.168.1.3',
755               'src_ipv4: 'get_local', ...
756              The key can also be 'get_local' which means the code will read
757              and use the local interface parameters
758        """
759        interf = config_params['interf']
760        self.packet = None
761        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
762            self.src_mac = scapy.get_if_hwaddr(interf)
763        else:
764            self.src_mac = config_params['src_mac']
765
766        if config_params['src_ipv4'] == GET_FROM_LOCAL_INTERFACE:
767            self.src_ipv4 = scapy.get_if_addr(interf)
768        else:
769            self.src_ipv4 = config_params['src_ipv4']
770
771    def generate(self, ip_dst=None, eth_dst=None):
772        """Generates a mDNS v4 packet for multicast DNS config
773
774        Args:
775            ip_dst: IP destination address (Optional)
776            eth_dst: Ethernet (layer 2) destination address (Optional)
777        """
778
779        # Overwrite standard fields if desired
780        sta_ip = (ip_dst if ip_dst is not None else MDNS_V4_IP_DST)
781        sta_hw = (eth_dst if eth_dst is not None else MDNS_V4_MAC_DST)
782
783        # Create mDNS layer
784        qdServer = scapy.DNSQR(qname=self.src_ipv4, qtype=MDNS_QTYPE)
785        mDNS = scapy.DNS(rd=MDNS_RECURSIVE, qd=qdServer)
786
787        # Create UDP
788        udp = scapy.UDP(sport=MDNS_UDP_PORT, dport=MDNS_UDP_PORT)
789
790        # Create IP layer
791        ip4 = scapy.IP(src=self.src_ipv4, dst=sta_ip, ttl=255)
792
793        # Create Ethernet layer
794        ethernet = scapy.Ether(src=self.src_mac, dst=sta_hw)
795
796        self.packet = ethernet / ip4 / udp / mDNS
797        return self.packet
798
799
800class Dot3Generator(object):
801    """Creates a custom 802.3 Ethernet Frame
802
803    Attributes:
804        packet: desired built custom packet
805        src_mac: MAC address (Layer 2) of the source node
806        src_ipv4: IPv4 address (Layer 3) of the source node
807    """
808
809    def __init__(self, **config_params):
810        """Initialize the class with the required network and packet params.
811
812        Args:
813            config_params: contains all the necessary packet parameters.
814              Some fields can be generated automatically. For example:
815              {'subnet_mask': '255.255.255.0',
816               'dst_ipv4': '192.168.1.3',
817               'src_ipv4: 'get_local', ...
818              The key can also be 'get_local' which means the code will read
819              and use the local interface parameters
820        """
821        interf = config_params['interf']
822        self.packet = None
823        self.dst_mac = config_params['dst_mac']
824        if config_params['src_mac'] == GET_FROM_LOCAL_INTERFACE:
825            self.src_mac = scapy.get_if_hwaddr(interf)
826        else:
827            self.src_mac = config_params['src_mac']
828
829    def _build_ether(self, eth_dst=None):
830        """Creates the basic frame for 802.3
831
832        Args:
833            eth_dst: Ethernet (layer 2) destination address (Optional)
834        """
835        # Overwrite standard fields if desired
836        sta_hw = (eth_dst if eth_dst is not None else self.dst_mac)
837        # Create Ethernet layer
838        dot3_base = scapy.Dot3(src=self.src_mac, dst=sta_hw)
839
840        return dot3_base
841
842    def _pad_frame(self, frame):
843        """Pads the frame with default length and values
844
845        Args:
846            frame: Ethernet (layer 2) to be padded
847        """
848        frame.len = PAD_LEN_BYTES
849        pad = scapy.Padding()
850        pad.load = '\x00' * PAD_LEN_BYTES
851        return frame / pad
852
853    def generate(self, eth_dst=None):
854        """Generates the basic 802.3 frame and adds padding
855
856        Args:
857            eth_dst: Ethernet (layer 2) destination address (Optional)
858        """
859        # Create 802.3 Base
860        ethernet = self._build_ether(eth_dst)
861
862        self.packet = self._pad_frame(ethernet)
863        return self.packet
864
865    def generate_llc(self, eth_dst=None, dsap=2, ssap=3, ctrl=LLC_XID_CONTROL):
866        """Generates the 802.3 frame with LLC and adds padding
867
868        Args:
869            eth_dst: Ethernet (layer 2) destination address (Optional)
870            dsap: Destination Service Access Point (Optional)
871            ssap: Source Service Access Point (Optional)
872            ctrl: Control (Optional)
873        """
874        # Create 802.3 Base
875        ethernet = self._build_ether(eth_dst)
876
877        # Create LLC layer
878        llc = scapy.LLC(dsap=dsap, ssap=ssap, ctrl=ctrl)
879
880        # Append and create packet
881        self.packet = self._pad_frame(ethernet / llc)
882        return self.packet
883
884    def generate_snap(self,
885                      eth_dst=None,
886                      dsap=SNAP_DSAP,
887                      ssap=SNAP_SSAP,
888                      ctrl=SNAP_CTRL,
889                      oui=SNAP_OUI,
890                      code=ETH_TYPE_IP):
891        """Generates the 802.3 frame with LLC and SNAP and adds padding
892
893        Args:
894            eth_dst: Ethernet (layer 2) destination address (Optional)
895            dsap: Destination Service Access Point (Optional)
896            ssap: Source Service Access Point (Optional)
897            ctrl: Control (Optional)
898            oid: Protocol Id or Org Code (Optional)
899            code: EtherType (Optional)
900        """
901        # Create 802.3 Base
902        ethernet = self._build_ether(eth_dst)
903
904        # Create 802.2 LLC header
905        llc = scapy.LLC(dsap=dsap, ssap=ssap, ctrl=ctrl)
906
907        # Create 802.3 SNAP header
908        snap = scapy.SNAP(OUI=oui, code=code)
909
910        # Append and create packet
911        self.packet = self._pad_frame(ethernet / llc / snap)
912        return self.packet
913
914
915def get_if_addr6(intf, address_type):
916    """Returns the Ipv6 address from a given local interface.
917
918    Returns the desired IPv6 address from the interface 'intf' in human
919    readable form. The address type is indicated by the IPv6 constants like
920    IPV6_ADDR_LINKLOCAL, IPV6_ADDR_GLOBAL, etc. If no address is found,
921    None is returned.
922
923    Args:
924        intf: desired interface name
925        address_type: addrees typle like LINKLOCAL or GLOBAL
926
927    Returns:
928        Ipv6 address of the specified interface in human readable format
929    """
930    for if_list in scapy.in6_getifaddr():
931        if if_list[2] == intf and if_list[1] == address_type:
932            return if_list[0]
933
934    return None
935
936