| 567 | | # Think before modify it : for instance, FE::1 does exist and is unicast |
|---|
| 568 | | # there are many others like that. |
|---|
| 569 | | # TODO : integrate Unique Local Addresses |
|---|
| 570 | | def in6_getAddrType(addr): |
|---|
| 571 | | naddr = inet_pton(socket.AF_INET6, addr) |
|---|
| 572 | | paddr = inet_ntop(socket.AF_INET6, naddr) # normalize |
|---|
| 573 | | addrType = 0 |
|---|
| 574 | | # _Assignable_ Global Unicast Address space |
|---|
| 575 | | # is defined in RFC 3513 as those in 2000::/3 |
|---|
| 576 | | if ((struct.unpack("B", naddr[0])[0] & 0xE0) == 0x20): |
|---|
| 577 | | addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) |
|---|
| 578 | | if naddr[:2] == ' \x02': # Mark 6to4 @ |
|---|
| 579 | | addrType |= IPV6_ADDR_6TO4 |
|---|
| 580 | | elif naddr[0] == '\xff': # multicast |
|---|
| 581 | | addrScope = paddr[3] |
|---|
| 582 | | if addrScope == '2': |
|---|
| 583 | | addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) |
|---|
| 584 | | elif addrScope == 'e': |
|---|
| 585 | | addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
|---|
| 586 | | else: |
|---|
| 587 | | addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
|---|
| 588 | | elif ((naddr[0] == '\xfe') and ((int(paddr[2], 16) & 0xC) == 0x8)): |
|---|
| 589 | | addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) |
|---|
| 590 | | elif paddr == "::1": |
|---|
| 591 | | addrType = IPV6_ADDR_LOOPBACK |
|---|
| 592 | | elif paddr == "::": |
|---|
| 593 | | addrType = IPV6_ADDR_UNSPECIFIED |
|---|
| 594 | | else: |
|---|
| 595 | | # Everything else is global unicast (RFC 3513) |
|---|
| 596 | | # Even old deprecated (RFC3879) Site-Local addresses |
|---|
| 597 | | addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) |
|---|
| 598 | | |
|---|
| 599 | | return addrType |
|---|
| 600 | | |
|---|
| 601 | | def find_ifaddr2(addr, plen, laddr): |
|---|
| 602 | | dstAddrType = in6_getAddrType(addr) |
|---|
| 603 | | |
|---|
| 604 | | if dstAddrType == IPV6_ADDR_UNSPECIFIED: # Shouldn't happen as dst addr |
|---|
| 605 | | return None |
|---|
| 606 | | |
|---|
| 607 | | if dstAddrType == IPV6_ADDR_LOOPBACK: |
|---|
| 608 | | return None |
|---|
| 609 | | |
|---|
| 610 | | tmp = [[]] + map(lambda (x,y,z): (in6_getAddrType(x), x, y, z), laddr) |
|---|
| 611 | | def filterSameScope(l, t): |
|---|
| 612 | | if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0: |
|---|
| 613 | | l.append(t) |
|---|
| 614 | | return l |
|---|
| 615 | | sameScope = reduce(filterSameScope, tmp) |
|---|
| 616 | | |
|---|
| 617 | | l = len(sameScope) |
|---|
| 618 | | if l == 1: # Only one address for our scope |
|---|
| 619 | | return sameScope[0][1] |
|---|
| 620 | | |
|---|
| 621 | | elif l > 1: # Muliple addresses for our scope |
|---|
| 622 | | stfAddr = filter(lambda x: x[0] & IPV6_ADDR_6TO4, sameScope) |
|---|
| 623 | | nativeAddr = filter(lambda x: not (x[0] & IPV6_ADDR_6TO4), sameScope) |
|---|
| 624 | | |
|---|
| 625 | | if not (dstAddrType & IPV6_ADDR_6TO4): # destination is not 6to4 |
|---|
| 626 | | if len(nativeAddr) != 0: |
|---|
| 627 | | return nativeAddr[0][1] |
|---|
| 628 | | return stfAddr[0][1] |
|---|
| 629 | | |
|---|
| 630 | | else: # Destination is 6to4, try to use source 6to4 addr if any |
|---|
| 631 | | if len(stfAddr) != 0: |
|---|
| 632 | | return stfAddr[0][1] |
|---|
| 633 | | return nativeAddr[0][1] |
|---|
| 634 | | else: |
|---|
| 635 | | return None |
|---|
| 636 | | |
|---|
| 637 | | |
|---|
| 638 | | def in6_mactoifaceid(mac, ulbit=None): |
|---|
| 639 | | """ |
|---|
| 640 | | Compute the interface ID in modified EUI-64 format associated |
|---|
| 641 | | to the Ethernet address provided as input. |
|---|
| 642 | | value taken by U/L bit in the interface identifier is basically |
|---|
| 643 | | the reversed value of that in given MAC address it can be forced |
|---|
| 644 | | to a specific value by using optional 'ulbit' parameter. |
|---|
| 645 | | """ |
|---|
| 646 | | if len(mac) != 17: return None |
|---|
| 647 | | m = "".join(mac.split(':')) |
|---|
| 648 | | if len(m) != 12: return None |
|---|
| 649 | | first = int(m[0:2], 16) |
|---|
| 650 | | if ulbit is None or not (ulbit == 0 or ulbit == 1): |
|---|
| 651 | | ulbit = [1,'-',0][first & 0x02] |
|---|
| 652 | | ulbit *= 2 |
|---|
| 653 | | first = "%.02x" % ((first & 0xFD) | ulbit) |
|---|
| 654 | | eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] |
|---|
| 655 | | return eui64.upper() |
|---|
| 656 | | |
|---|
| 657 | | def in6_ifaceidtomac(ifaceid): # TODO: finish commenting function behavior |
|---|
| 658 | | """ |
|---|
| 659 | | Extract the mac address from provided iface ID. Iface ID is provided |
|---|
| 660 | | in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None |
|---|
| 661 | | is returned on error. |
|---|
| 662 | | """ |
|---|
| 663 | | try: |
|---|
| 664 | | ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] |
|---|
| 665 | | except: |
|---|
| 666 | | return None |
|---|
| 667 | | if ifaceid[3:5] != '\xff\xfe': |
|---|
| 668 | | return None |
|---|
| 669 | | first = struct.unpack("B", ifaceid[:1])[0] |
|---|
| 670 | | ulbit = 2*[1,'-',0][first & 0x02] |
|---|
| 671 | | first = struct.pack("B", ((first & 0xFD) | ulbit)) |
|---|
| 672 | | oui = first + ifaceid[1:3] |
|---|
| 673 | | end = ifaceid[5:] |
|---|
| 674 | | l = map(lambda x: "%.02x" % struct.unpack("B", x)[0], list(oui+end)) |
|---|
| 675 | | return ":".join(l) |
|---|
| 676 | | |
|---|
| 677 | | def in6_addrtomac(addr): |
|---|
| 678 | | """ |
|---|
| 679 | | Extract the mac address from provided address. None is returned |
|---|
| 680 | | on error. |
|---|
| 681 | | """ |
|---|
| 682 | | mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") |
|---|
| 683 | | x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) |
|---|
| 684 | | ifaceid = inet_ntop(socket.AF_INET6, x)[2:] |
|---|
| 685 | | return in6_ifaceidtomac(ifaceid) |
|---|
| 686 | | |
|---|
| 687 | | def in6_addrtovendor(addr): |
|---|
| 688 | | """ |
|---|
| 689 | | Extract the MAC address from a modified EUI-64 constructed IPv6 |
|---|
| 690 | | address provided and use the IANA oui.txt file to get the vendor. |
|---|
| 691 | | The database used for the conversion is the one loaded by Scapy, |
|---|
| 692 | | based on Wireshark (/usr/share/wireshark/wireshark/manuf) None |
|---|
| 693 | | is returned on error, "UNKNOWN" if the vendor is unknown. |
|---|
| 694 | | """ |
|---|
| 695 | | mac = in6_addrtomac(addr) |
|---|
| 696 | | if mac is None: |
|---|
| 697 | | return None |
|---|
| 698 | | |
|---|
| 699 | | res = conf.manufdb._get_manuf(mac) |
|---|
| 700 | | if len(res) == 17 and res.count(':') != 5: # Mac address, i.e. unknown |
|---|
| 701 | | res = "UNKNOWN" |
|---|
| 702 | | |
|---|
| 703 | | return res |
|---|
| 704 | | |
|---|
| 705 | | def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): |
|---|
| 706 | | """ |
|---|
| 707 | | Generate a Link-Scoped Multicast Address as described in RFC 4489. |
|---|
| 708 | | Returned value is in printable notation. |
|---|
| 709 | | |
|---|
| 710 | | 'addr' parameter specifies the link-local address to use for generating |
|---|
| 711 | | Link-scoped multicast address IID. |
|---|
| 712 | | |
|---|
| 713 | | By default, the function returns a ::/96 prefix (aka last 32 bits of |
|---|
| 714 | | returned address are null). If a group id is provided through 'grpid' |
|---|
| 715 | | parameter, last 32 bits of the address are set to that value (accepted |
|---|
| 716 | | formats : '\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). |
|---|
| 717 | | |
|---|
| 718 | | By default, generated address scope is Link-Local (2). That value can |
|---|
| 719 | | be modified by passing a specific 'scope' value as an argument of the |
|---|
| 720 | | function. RFC 4489 only authorizes scope values <= 2. Enforcement |
|---|
| 721 | | is performed by the function (None will be returned). |
|---|
| 722 | | |
|---|
| 723 | | If no link-local address can be used to generate the Link-Scoped IPv6 |
|---|
| 724 | | Multicast address, or if another error occurs, None is returned. |
|---|
| 725 | | """ |
|---|
| 726 | | if not scope in [0, 1, 2]: |
|---|
| 727 | | return None |
|---|
| 728 | | try: |
|---|
| 729 | | if not in6_islladdr(addr): |
|---|
| 730 | | return None |
|---|
| 731 | | addr = inet_pton(socket.AF_INET6, addr) |
|---|
| 732 | | except: |
|---|
| 733 | | warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") |
|---|
| 734 | | return None |
|---|
| 735 | | |
|---|
| 736 | | iid = addr[8:] |
|---|
| 737 | | |
|---|
| 738 | | if grpid is None: |
|---|
| 739 | | grpid = '\x00\x00\x00\x00' |
|---|
| 740 | | else: |
|---|
| 741 | | if type(grpid) is str: |
|---|
| 742 | | if len(grpid) == 8: |
|---|
| 743 | | try: |
|---|
| 744 | | grpid = int(grpid, 16) & 0xffffffff |
|---|
| 745 | | except: |
|---|
| 746 | | warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
|---|
| 747 | | return None |
|---|
| 748 | | elif len(grpid) == 4: |
|---|
| 749 | | try: |
|---|
| 750 | | grpid = struct.unpack("!I", grpid)[0] |
|---|
| 751 | | except: |
|---|
| 752 | | warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
|---|
| 753 | | return None |
|---|
| 754 | | grpid = struct.pack("!I", grpid) |
|---|
| 755 | | |
|---|
| 756 | | flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) |
|---|
| 757 | | plen = '\xff' |
|---|
| 758 | | res = '\x00' |
|---|
| 759 | | a = '\xff' + flgscope + res + plen + iid + grpid |
|---|
| 760 | | |
|---|
| 761 | | return inet_ntop(socket.AF_INET6, a) |
|---|
| 762 | | |
|---|
| 763 | | def in6_get6to4Prefix(addr): |
|---|
| 764 | | """ |
|---|
| 765 | | Returns the /48 6to4 prefix associated with provided IPv4 address |
|---|
| 766 | | On error, None is returned. No check is performed on public/private |
|---|
| 767 | | status of the address |
|---|
| 768 | | """ |
|---|
| 769 | | try: |
|---|
| 770 | | addr = inet_pton(socket.AF_INET, addr) |
|---|
| 771 | | addr = inet_ntop(socket.AF_INET6, '\x20\x02'+addr+'\x00'*10) |
|---|
| 772 | | except: |
|---|
| 773 | | return None |
|---|
| 774 | | return addr |
|---|
| 775 | | |
|---|
| 776 | | def in6_6to4ExtractAddr(addr): |
|---|
| 777 | | """ |
|---|
| 778 | | Extract IPv4 address embbeded in 6to4 address. Passed address must be |
|---|
| 779 | | a 6to4 addrees. None is returned on error. |
|---|
| 780 | | """ |
|---|
| 781 | | try: |
|---|
| 782 | | addr = inet_pton(socket.AF_INET6, addr) |
|---|
| 783 | | except: |
|---|
| 784 | | return None |
|---|
| 785 | | if addr[:2] != " \x02": |
|---|
| 786 | | return None |
|---|
| 787 | | return inet_ntop(socket.AF_INET, addr[2:6]) |
|---|
| 788 | | |
|---|
| 789 | | |
|---|
| 790 | | def in6_getLocalUniquePrefix(): |
|---|
| 791 | | """ |
|---|
| 792 | | Returns a pseudo-randomly generated Local Unique prefix. Function |
|---|
| 793 | | follows recommandation of Section 3.2.2 of RFC 4193 for prefix |
|---|
| 794 | | generation. |
|---|
| 795 | | """ |
|---|
| 796 | | # Extracted from RFC 1305 (NTP) : |
|---|
| 797 | | # NTP timestamps are represented as a 64-bit unsigned fixed-point number, |
|---|
| 798 | | # in seconds relative to 0h on 1 January 1900. The integer part is in the |
|---|
| 799 | | # first 32 bits and the fraction part in the last 32 bits. |
|---|
| 800 | | |
|---|
| 801 | | # epoch = (1900, 1, 1, 0, 0, 0, 5, 1, 0) |
|---|
| 802 | | # x = time.time() |
|---|
| 803 | | # from time import gmtime, strftime, gmtime, mktime |
|---|
| 804 | | # delta = mktime(gmtime(0)) - mktime(self.epoch) |
|---|
| 805 | | # x = x-delta |
|---|
| 806 | | |
|---|
| 807 | | tod = time.time() # time of day. Will bother with epoch later |
|---|
| 808 | | i = int(tod) |
|---|
| 809 | | j = int((tod - i)*(2**32)) |
|---|
| 810 | | tod = struct.pack("!II", i,j) |
|---|
| 811 | | # TODO: Add some check regarding system address gathering |
|---|
| 812 | | rawmac = get_if_raw_hwaddr(conf.iface6)[1] |
|---|
| 813 | | mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac))) |
|---|
| 814 | | # construct modified EUI-64 ID |
|---|
| 815 | | eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] |
|---|
| 816 | | import sha |
|---|
| 817 | | globalid = sha.new(tod+eui64).digest()[:5] |
|---|
| 818 | | return inet_ntop(socket.AF_INET6, '\xfd' + globalid + '\x00'*10) |
|---|
| 819 | | |
|---|
| 820 | | def in6_getRandomizedIfaceId(ifaceid, previous=None): |
|---|
| 821 | | """ |
|---|
| 822 | | Implements the interface ID generation algorithm described in RFC 3041. |
|---|
| 823 | | The function takes the Modified EUI-64 interface identifier generated |
|---|
| 824 | | as described in RFC 4291 and an optional previous history value (the |
|---|
| 825 | | first element of the output of this function). If no previous interface |
|---|
| 826 | | identifier is provided, a random one is generated. The function returns |
|---|
| 827 | | a tuple containing the randomized interface identifier and the history |
|---|
| 828 | | value (for possible future use). Input and output values are provided in |
|---|
| 829 | | a "printable" format as depicted below. |
|---|
| 830 | | |
|---|
| 831 | | ex: |
|---|
| 832 | | |
|---|
| 833 | | >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') |
|---|
| 834 | | ('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') |
|---|
| 835 | | |
|---|
| 836 | | >>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', |
|---|
| 837 | | previous='d006:d540:db11:b092') |
|---|
| 838 | | ('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') |
|---|
| 839 | | """ |
|---|
| 840 | | |
|---|
| 841 | | s = "" |
|---|
| 842 | | if previous is None: |
|---|
| 843 | | d = "".join(map(chr, range(256))) |
|---|
| 844 | | for i in range(8): |
|---|
| 845 | | s += random.choice(d) |
|---|
| 846 | | previous = s |
|---|
| 847 | | s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous |
|---|
| 848 | | import md5 |
|---|
| 849 | | s = md5.new(s).digest() |
|---|
| 850 | | s1,s2 = s[:8],s[8:] |
|---|
| 851 | | s1 = chr(ord(s1[0]) | 0x04) + s1[1:] |
|---|
| 852 | | s1 = inet_ntop(socket.AF_INET6, "\xff"*8 + s1)[20:] |
|---|
| 853 | | s2 = inet_ntop(socket.AF_INET6, "\xff"*8 + s2)[20:] |
|---|
| 854 | | return (s1, s2) |
|---|
| 855 | | |
|---|
| 856 | | |
|---|
| 857 | | _rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', |
|---|
| 858 | | 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', |
|---|
| 859 | | 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', |
|---|
| 860 | | 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', |
|---|
| 861 | | 'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', |
|---|
| 862 | | '>','?','@','^','_','`','{','|','}','~' ] |
|---|
| 863 | | |
|---|
| 864 | | def in6_ctop(addr): |
|---|
| 865 | | """ |
|---|
| 866 | | Convert an IPv6 address in Compact Representation Notation |
|---|
| 867 | | (RFC 1924) to printable representation ;-) |
|---|
| 868 | | Returns None on error. |
|---|
| 869 | | """ |
|---|
| 870 | | if len(addr) != 20 or not reduce(lambda x,y: x and y, |
|---|
| 871 | | map(lambda x: x in _rfc1924map, addr)): |
|---|
| 872 | | return None |
|---|
| 873 | | i = 0 |
|---|
| 874 | | for c in addr: |
|---|
| 875 | | j = _rfc1924map.index(c) |
|---|
| 876 | | i = 85*i + j |
|---|
| 877 | | res = [] |
|---|
| 878 | | for j in range(4): |
|---|
| 879 | | res.append(struct.pack("!I", i%2**32)) |
|---|
| 880 | | i = i/(2**32) |
|---|
| 881 | | res.reverse() |
|---|
| 882 | | return inet_ntop(socket.AF_INET6, "".join(res)) |
|---|
| 883 | | |
|---|
| 884 | | def in6_ptoc(addr): |
|---|
| 885 | | """ |
|---|
| 886 | | Converts an IPv6 address in printable representation to RFC |
|---|
| 887 | | 1924 Compact Representation ;-) |
|---|
| 888 | | Returns None on error. |
|---|
| 889 | | """ |
|---|
| 890 | | try: |
|---|
| 891 | | d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) |
|---|
| 892 | | except: |
|---|
| 893 | | return None |
|---|
| 894 | | res = 0 |
|---|
| 895 | | m = [2**96, 2**64, 2**32, 1] |
|---|
| 896 | | for i in range(4): |
|---|
| 897 | | res += d[i]*m[i] |
|---|
| 898 | | rem = res |
|---|
| 899 | | res = [] |
|---|
| 900 | | while rem: |
|---|
| 901 | | res.append(_rfc1924map[rem%85]) |
|---|
| 902 | | rem = rem/85 |
|---|
| 903 | | res.reverse() |
|---|
| 904 | | return "".join(res) |
|---|
| 905 | | |
|---|
| 906 | | |
|---|
| 907 | | def in6_isaddr6to4(x): |
|---|
| 908 | | """ |
|---|
| 909 | | Return True if provided address (in printable format) is a 6to4 |
|---|
| 910 | | address (being in 2002::/16). |
|---|
| 911 | | """ |
|---|
| 912 | | x = inet_pton(socket.AF_INET6, x) |
|---|
| 913 | | return x[:2] == ' \x02' |
|---|
| 914 | | |
|---|
| 915 | | conf.teredoPrefix = "2001::" # old one was 3ffe:831f (it is a /32) |
|---|
| 916 | | conf.teredoServerPort = 3544 |
|---|
| 917 | | |
|---|
| 918 | | def in6_isaddrTeredo(x): |
|---|
| 919 | | """ |
|---|
| 920 | | Return True if provided address is a Teredo, meaning it is under |
|---|
| 921 | | the /32 conf.teredoPrefix prefix value (by default, 2001::). |
|---|
| 922 | | Otherwise, False is returned. Address must be passed in printable |
|---|
| 923 | | format. |
|---|
| 924 | | """ |
|---|
| 925 | | our = inet_pton(socket.AF_INET6, x)[0:4] |
|---|
| 926 | | teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] |
|---|
| 927 | | return teredoPrefix == our |
|---|
| 928 | | |
|---|
| 929 | | def teredoAddrExtractInfo(x): |
|---|
| 930 | | """ |
|---|
| 931 | | Extract information from a Teredo address. Return value is |
|---|
| 932 | | a 4-tuple made of IPv4 address of Teredo server, flag value (int), |
|---|
| 933 | | mapped address (non obfuscated) and mapped port (non obfuscated). |
|---|
| 934 | | No specific checks are performed on passed address. |
|---|
| 935 | | """ |
|---|
| 936 | | addr = inet_pton(socket.AF_INET6, x) |
|---|
| 937 | | server = inet_ntop(socket.AF_INET, addr[4:8]) |
|---|
| 938 | | flag = struct.unpack("!H",addr[8:10])[0] |
|---|
| 939 | | mappedport = struct.unpack("!H",strxor(addr[10:12],'\xff'*2))[0] |
|---|
| 940 | | mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],'\xff'*4)) |
|---|
| 941 | | return server, flag, mappedaddr, mappedport |
|---|
| 942 | | |
|---|
| 943 | | def in6_iseui64(x): |
|---|
| 944 | | """ |
|---|
| 945 | | Return True if provided address has an interface identifier part |
|---|
| 946 | | created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). |
|---|
| 947 | | Otherwise, False is returned. Address must be passed in printable |
|---|
| 948 | | format. |
|---|
| 949 | | """ |
|---|
| 950 | | eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') |
|---|
| 951 | | x = in6_and(inet_pton(socket.AF_INET6, x), eui64) |
|---|
| 952 | | return x == eui64 |
|---|
| 953 | | |
|---|
| 954 | | def in6_isanycast(x): # RFC 2526 |
|---|
| 955 | | if in6_iseui64(x): |
|---|
| 956 | | s = '::fdff:ffff:ffff:ff80' |
|---|
| 957 | | x = in6_and(x, inet_pton(socket.AF_INET6, '::ffff:ffff:ffff:ff80')) |
|---|
| 958 | | x = in6_and(x, inet_pton(socket.AF_INET6, s)) |
|---|
| 959 | | return x == inet_pton(socket.AF_INET6, s) |
|---|
| 960 | | else: |
|---|
| 961 | | # not EUI-64 |
|---|
| 962 | | #| n bits | 121-n bits | 7 bits | |
|---|
| 963 | | #+---------------------------------+------------------+------------+ |
|---|
| 964 | | #| subnet prefix | 1111111...111111 | anycast ID | |
|---|
| 965 | | #+---------------------------------+------------------+------------+ |
|---|
| 966 | | # | interface identifier field | |
|---|
| 967 | | warning('in6_isanycast(): TODO not EUI-64') |
|---|
| 968 | | return 0 |
|---|
| 969 | | |
|---|
| 970 | | def _in6_bitops(a1, a2, operator=0): |
|---|
| 971 | | a1 = struct.unpack('4I', a1) |
|---|
| 972 | | a2 = struct.unpack('4I', a2) |
|---|
| 973 | | fop = [ lambda x,y: x | y, |
|---|
| 974 | | lambda x,y: x & y, |
|---|
| 975 | | lambda x,y: x ^ y |
|---|
| 976 | | ] |
|---|
| 977 | | ret = map(fop[operator%len(fop)], a1, a2) |
|---|
| 978 | | t = ''.join(map(lambda x: struct.pack('I', x), ret)) |
|---|
| 979 | | return t |
|---|
| 980 | | |
|---|
| 981 | | def in6_or(a1, a2): |
|---|
| 982 | | """ |
|---|
| 983 | | Provides a bit to bit OR of provided addresses. They must be |
|---|
| 984 | | passed in network format. Return value is also an IPv6 address |
|---|
| 985 | | in network format. |
|---|
| 986 | | """ |
|---|
| 987 | | return _in6_bitops(a1, a2, 0) |
|---|
| 988 | | |
|---|
| 989 | | def in6_and(a1, a2): |
|---|
| 990 | | """ |
|---|
| 991 | | Provides a bit to bit AND of provided addresses. They must be |
|---|
| 992 | | passed in network format. Return value is also an IPv6 address |
|---|
| 993 | | in network format. |
|---|
| 994 | | """ |
|---|
| 995 | | return _in6_bitops(a1, a2, 1) |
|---|
| 996 | | |
|---|
| 997 | | def in6_xor(a1, a2): |
|---|
| 998 | | """ |
|---|
| 999 | | Provides a bit to bit XOR of provided addresses. They must be |
|---|
| 1000 | | passed in network format. Return value is also an IPv6 address |
|---|
| 1001 | | in network format. |
|---|
| 1002 | | """ |
|---|
| 1003 | | return _in6_bitops(a1, a2, 2) |
|---|
| 1004 | | |
|---|
| 1005 | | def in6_cidr2mask(m): |
|---|
| 1006 | | """ |
|---|
| 1007 | | Return the mask (bitstring) associated with provided length |
|---|
| 1008 | | value. For instance if function is called on 48, return value is |
|---|
| 1009 | | '\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. |
|---|
| 1010 | | |
|---|
| 1011 | | """ |
|---|
| 1012 | | if m > 128 or m < 0: |
|---|
| 1013 | | raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) |
|---|
| 1014 | | |
|---|
| 1015 | | t = [] |
|---|
| 1016 | | for i in xrange(0, 4): |
|---|
| 1017 | | t.append(max(0, 2**32 - 2**(32-min(32, m)))) |
|---|
| 1018 | | m -= 32 |
|---|
| 1019 | | |
|---|
| 1020 | | return ''.join(map(lambda x: struct.pack('!I', x), t)) |
|---|
| 1021 | | |
|---|
| 1022 | | def in6_getnsma(a): |
|---|
| 1023 | | """ |
|---|
| 1024 | | Return link-local solicited-node multicast address for given |
|---|
| 1025 | | address. Passed address must be provided in network format. |
|---|
| 1026 | | Returned value is also in network format. |
|---|
| 1027 | | """ |
|---|
| 1028 | | |
|---|
| 1029 | | r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) |
|---|
| 1030 | | r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) |
|---|
| 1031 | | return r |
|---|
| 1032 | | |
|---|
| 1033 | | def in6_getnsmac(a): # return multicast Ethernet address associated with multicast v6 destination |
|---|
| 1034 | | """ |
|---|
| 1035 | | Return the multicast mac address associated with provided |
|---|
| 1036 | | IPv6 address. Passed address must be in network format. |
|---|
| 1037 | | """ |
|---|
| 1038 | | |
|---|
| 1039 | | a = struct.unpack('16B', a)[-4:] |
|---|
| 1040 | | mac = '33:33:' |
|---|
| 1041 | | mac += ':'.join(map(lambda x: '%.2x' %x, a)) |
|---|
| 1042 | | return mac |
|---|
| 1043 | | |
|---|
| 1044 | | def in6_getha(prefix): |
|---|
| 1045 | | """ |
|---|
| 1046 | | Return the anycast address associated with all home agents on a given |
|---|
| 1047 | | subnet. |
|---|
| 1048 | | """ |
|---|
| 1049 | | r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) |
|---|
| 1050 | | r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) |
|---|
| 1051 | | return inet_ntop(socket.AF_INET6, r) |
|---|
| 1052 | | |
|---|
| 1053 | | def in6_ptop(str): |
|---|
| 1054 | | """ |
|---|
| 1055 | | Normalizes IPv6 addresses provided in printable format, returning the |
|---|
| 1056 | | same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) |
|---|
| 1057 | | """ |
|---|
| 1058 | | return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str)) |
|---|
| 1059 | | |
|---|
| 1060 | | def in6_isincluded(addr, prefix, plen): |
|---|
| 1061 | | """ |
|---|
| 1062 | | Returns True when 'addr' belongs to prefix/plen. False otherwise. |
|---|
| 1063 | | """ |
|---|
| 1064 | | temp = inet_pton(socket.AF_INET6, addr) |
|---|
| 1065 | | pref = in6_cidr2mask(plen) |
|---|
| 1066 | | zero = inet_pton(socket.AF_INET6, prefix) |
|---|
| 1067 | | return zero == in6_and(temp, pref) |
|---|
| 1068 | | |
|---|
| 1069 | | def in6_isdocaddr(str): |
|---|
| 1070 | | """ |
|---|
| 1071 | | Returns True if provided address in printable format belongs to |
|---|
| 1072 | | 2001:db8::/32 address space reserved for documentation (as defined |
|---|
| 1073 | | in RFC 3849). |
|---|
| 1074 | | """ |
|---|
| 1075 | | return in6_isincluded(str, '2001:db8::', 32) |
|---|
| 1076 | | |
|---|
| 1077 | | def in6_islladdr(str): |
|---|
| 1078 | | """ |
|---|
| 1079 | | Returns True if provided address in printable format belongs to |
|---|
| 1080 | | _allocated_ link-local unicast address space (fe80::/10) |
|---|
| 1081 | | """ |
|---|
| 1082 | | return in6_isincluded(str, 'fe80::', 10) |
|---|
| 1083 | | |
|---|
| 1084 | | def in6_issladdr(str): |
|---|
| 1085 | | """ |
|---|
| 1086 | | Returns True if provided address in printable format belongs to |
|---|
| 1087 | | _allocated_ site-local address space (fec0::/10). This prefix has |
|---|
| 1088 | | been deprecated, address being now reserved by IANA. Function |
|---|
| 1089 | | will remain for historic reasons. |
|---|
| 1090 | | """ |
|---|
| 1091 | | return in6_isincluded(str, 'fec0::', 10) |
|---|
| 1092 | | |
|---|
| 1093 | | def in6_isuladdr(str): |
|---|
| 1094 | | """ |
|---|
| 1095 | | Returns True if provided address in printable format belongs to |
|---|
| 1096 | | Unique local address space (fc00::/7). |
|---|
| 1097 | | """ |
|---|
| 1098 | | return in6_isincluded(str, 'fc::', 7) |
|---|
| 1099 | | |
|---|
| 1100 | | # TODO : we should see the status of Unique Local addresses against |
|---|
| 1101 | | # global address space. |
|---|
| 1102 | | # Up-to-date information is available through RFC 3587. |
|---|
| 1103 | | # We should review function behavior based on its content. |
|---|
| 1104 | | def in6_isgladdr(str): |
|---|
| 1105 | | """ |
|---|
| 1106 | | Returns True if provided address in printable format belongs to |
|---|
| 1107 | | _allocated_ global address space (2000::/3). Please note that, |
|---|
| 1108 | | Unique Local addresses (FC00::/7) are not part of global address |
|---|
| 1109 | | space, and won't match. |
|---|
| 1110 | | """ |
|---|
| 1111 | | return in6_isincluded(str, '2000::', 3) |
|---|
| 1112 | | |
|---|
| 1113 | | def in6_ismaddr(str): |
|---|
| 1114 | | """ |
|---|
| 1115 | | Returns True if provided address in printable format belongs to |
|---|
| 1116 | | allocated Multicast address space (ff00::/8). |
|---|
| 1117 | | """ |
|---|
| 1118 | | return in6_isincluded(str, 'ff00::', 8) |
|---|
| 1119 | | |
|---|
| 1120 | | def in6_ismnladdr(str): |
|---|
| 1121 | | """ |
|---|
| 1122 | | Returns True if address belongs to node-local multicast address |
|---|
| 1123 | | space (ff01::/16) as defined in RFC |
|---|
| 1124 | | """ |
|---|
| 1125 | | return in6_isincluded(str, 'ff01::', 16) |
|---|
| 1126 | | |
|---|
| 1127 | | def in6_ismgladdr(str): |
|---|
| 1128 | | """ |
|---|
| 1129 | | Returns True if address belongs to global multicast address |
|---|
| 1130 | | space (ff0e::/16). |
|---|
| 1131 | | """ |
|---|
| 1132 | | return in6_isincluded(str, 'ff0e::', 16) |
|---|
| 1133 | | |
|---|
| 1134 | | def in6_ismlladdr(str): |
|---|
| 1135 | | """ |
|---|
| 1136 | | Returns True if address balongs to link-local multicast address |
|---|
| 1137 | | space (ff02::/16) |
|---|
| 1138 | | """ |
|---|
| 1139 | | return in6_isincluded(str, 'ff02::', 16) |
|---|
| 1140 | | |
|---|
| 1141 | | def in6_ismsladdr(str): |
|---|
| 1142 | | """ |
|---|
| 1143 | | Returns True if address belongs to site-local multicast address |
|---|
| 1144 | | space (ff05::/16). Site local address space has been deprecated. |
|---|
| 1145 | | Function remains for historic reasons. |
|---|
| 1146 | | """ |
|---|
| 1147 | | return in6_isincluded(str, 'ff05::', 16) |
|---|
| 1148 | | |
|---|
| 1149 | | def in6_isaddrllallnodes(str): |
|---|
| 1150 | | """ |
|---|
| 1151 | | Returns True if address is the link-local all-nodes multicast |
|---|
| 1152 | | address (ff02::1). |
|---|
| 1153 | | """ |
|---|
| 1154 | | return (inet_pton(socket.AF_INET6, "ff02::1") == |
|---|
| 1155 | | inet_pton(socket.AF_INET6, str)) |
|---|
| 1156 | | |
|---|
| 1157 | | def in6_isaddrllallservers(str): |
|---|
| 1158 | | """ |
|---|
| 1159 | | Returns True if address is the link-local all-servers multicast |
|---|
| 1160 | | address (ff02::2). |
|---|
| 1161 | | """ |
|---|
| 1162 | | return (inet_pton(socket.AF_INET6, "ff02::2") == |
|---|
| 1163 | | inet_pton(socket.AF_INET6, str)) |
|---|
| 1164 | | |
|---|
| 1165 | | |
|---|
| 1166 | | def in6_getscope(addr): |
|---|
| 1167 | | """ |
|---|
| 1168 | | Returns the scope of the address. |
|---|
| 1169 | | """ |
|---|
| 1170 | | if in6_isgladdr(addr): |
|---|
| 1171 | | scope = IPV6_ADDR_GLOBAL |
|---|
| 1172 | | elif in6_islladdr(addr): |
|---|
| 1173 | | scope = IPV6_ADDR_LINKLOCAL |
|---|
| 1174 | | elif in6_issladdr(addr): |
|---|
| 1175 | | scope = IPV6_ADDR_SITELOCAL |
|---|
| 1176 | | elif in6_ismaddr(addr): |
|---|
| 1177 | | scope = IPV6_ADDR_MULTICAST |
|---|
| 1178 | | elif addr == '::1': |
|---|
| 1179 | | scope = IPV6_ADDR_LOOPBACK |
|---|
| 1180 | | else: |
|---|
| 1181 | | scope = -1 |
|---|
| 1182 | | return scope |
|---|
| | 568 | |
|---|
| | 569 | |
|---|
| | 570 | |
|---|