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