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