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 25from 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 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 = wputils.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 = wputils.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 = wputils.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 = wputils.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