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