Ejemplo n.º 1
0
def SendRTSol():
    """send a simple Router Solicitation message on all the configured interfaces"""

    # get all the interfaces on
    # which we should send a message on
    nc = NeighCache()
    configured_addresses = nc.dump_addresses()
    interfaces = used_interfaces(configured_addresses)

    for iface in interfaces:

        p = Ether(src=get_if_hwaddr(iface)) / \
            IPv6(src = "::",dst = "ff02::2")/ \
            ICMPv6ND_RS()
        sendp(p, iface=iface, verbose=NDprotector.verbose)
        warn("Sending an RS on interface %s\n" % iface)
    def __init__(self):
        """initialize the Ephemeral Address Pool"""

        # DP borg, all instances share the same variables
        self.__dict__ = self.__shared_object
        # DP borg
        if not hasattr(self, "freePool"):
            self.freePool = []
            self.freePoolLock = RLock()
        if not hasattr(self, "inUsePool"):
            self.inUsePool = []
            self.inUsePoolLock = RLock()
        if not hasattr(self, "TBremovedAddr"):
            self.TBremovedAddr = {}
            self.TBremovedAddrLock = RLock()

        # some connxion are required to the Neighbor Cache
        # it seems better to always stay connected
        if not hasattr(self, "nc"):
            self.nc = NeighCache()

        if not hasattr(self, "clean"):
            self.clean = True
Ejemplo n.º 3
0
def callback(i, payload):
    """a callback function called on each ingoing packets"""

    data = payload.get_data()
    packet = IPv6(data)

    # we try to extract the SSAO if available
    try:
        (signalgs,
         verifalgs) = SigAlgList_split(packet[ICMPv6NDOptSSA].sigalgs)
    except AttributeError:
        signalgs = []
        verifalgs = []

    # may be buggy due to the order of get_if_list()
    # (in that case, we could use the if_nametoindex() )
    interface = get_if_list()[payload.get_indev() - 1]

    nc = NeighCache()

    if packet.haslayer(ICMPv6ND_NS) \
       or packet.haslayer(ICMPv6ND_NA):

        secured = False
        try:
            address = packet[IPv6].src
            # we can receive messages from other node performing a DAD
            if address == "::":
                address = packet.tgt
            cga_prop = CGAverify(address, packet.getlayer(CGAParams))
            if not cga_prop:
                warn("an ingoing packet has failed CGA verification test\n")

            else:
                # only perform the RSA signature here,
                # Timestamp and Nonce verification are performed in the NC
                # FIXME: this is not "good", we are subject to a DoS attack here

                list_of_pubkeys = CGAPKExtListtoPubKeyList(
                    packet[CGAParams].ext)
                list_of_pubkeys.insert(0, packet[CGAParams].pubkey)
                pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos]
                if isinstance(pubkey, PubKey) and \
                    not (NDprotector.min_RSA_key_size <= pubkey.modulusLen
                        and pubkey.modulusLen <= NDprotector.max_RSA_key_size):
                    warn("A message we received contains"
                         " a Public Key whose size is not allowed\n")
                else:  # Public Key is of a correct size

                    if packet[ICMPv6NDOptUSSig].keyh !=\
                            get_public_key_hash(pubkey, packet[ICMPv6NDOptUSSig].sigtypeID) :
                        warn("Public key contained in the CGA PDS does not match " \
                              "the Public Key hash in the Universal Sig option\n")
                    else:
                        if not packet[ICMPv6NDOptUSSig].verify_sig(pubkey):
                            warn(
                                "an ingoing packet has failed Universal Signature verification\n"
                            )
                        else:
                            secured = True

        except AttributeError:
            warn("An unsecured packet passed\n")

        # if the status is true, we allow the packet to pass
        status = False

        # we can extract the Source Link Layer option from a NS
        if packet.haslayer(ICMPv6ND_NS):
            mac_address = None
            try:
                mac_address = packet.getlayer(ICMPv6NDOptSrcLLAddr).lladdr
            except AttributeError:
                pass

            # the request does not contain
            # any information on L2 address
            if mac_address == None:
                status = True
            else:

                src_address = packet.getlayer(IPv6).src
                if secured:
                    public_key = str(packet.getlayer(CGAParams).pubkey)
                    timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp
                    warn(
                        "NC updating an entry with an secured NS message from the address: %s\n"
                        % packet.src)
                    status = nc.update(src_address,
                                       mac_address,
                                       public_key,
                                       timestamp,
                                       secured,
                                       ssao=(signalgs, verifalgs))
                    if not status:
                        warn(
                            "NS Message from %s rejected (due to a bad timestamp or nonce)\n"
                            % packet.src)

                    # record the nonce if any
                    if packet.haslayer(ICMPv6NDOptNonce):
                        nc.record_nonce_in(packet[IPv6].src, packet[IPv6].dst,
                                           packet[ICMPv6NDOptNonce].nonce)

                elif NDprotector.mixed_mode == True:
                    warn(
                        "NC updating an entry with an unsecured NS message from the address: %s\n"
                        % packet.src)
                    status = nc.update(src_address,
                                       mac_address,
                                       None,
                                       None,
                                       secured,
                                       ssao=(signalgs, verifalgs))
                else:
                    warn(
                        "NC not updating an entry with an unsecured NS message from the address: %s\n"
                        % packet.src)
                    status = False

        # this is a NA, we extract the Target Link Layer option
        else:
            target = packet.tgt

            mac_address = None
            try:
                mac_address = packet.getlayer(ICMPv6NDOptDstLLAddr).lladdr
            except AttributeError:
                pass

            if mac_address == None:
                status = True
            else:

                if secured:

                    public_key = str(packet.getlayer(CGAParams).pubkey)
                    timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp
                    try:
                        nonce = packet.getlayer(ICMPv6NDOptNonce).nonce
                    except AttributeError:
                        nonce = None
                    warn(
                        "NC updating an entry with an secured NA message from the address: %s\n"
                        % packet.src)

                    # multicasted messages may not contain any nonce as they are sent spontaneously
                    if packet[IPv6].dst != "ff02::1":
                        status = nc.check_nonce_in(packet[IPv6].src,
                                                   packet[IPv6].dst, nonce)
                    # only update when the nonce is correct
                    status = status and nc.update(target, mac_address, public_key,\
                                                  timestamp, secured, ssao= (signalgs, verifalgs))
                elif NDprotector.mixed_mode == True:
                    warn(
                        "NC updating an entry with an unsecured NA message from the address: %s\n"
                        % packet.src)
                    status = nc.update(target, mac_address, None, None,\
                                       secured, ssao= (signalgs, verifalgs))
                else:
                    warn(
                        "NC not updating an entry with an unsecured NA message from the address: %s\n"
                        % packet.src)
                    status = False

    elif packet.haslayer(ICMPv6ND_RA):
        if NDprotector.is_router == True:
            # a router should not receive RA and process them anyway
            status = False
        else:
            status = False
            # check RSA signature, extract PK

            certcache = CertCache()

            # extract the prefix(es) from the RA message
            prefixes = []
            try:
                next_prefix = packet.getlayer(ICMPv6NDOptPrefixInfo)
                while next_prefix:
                    if next_prefix.validlifetime != 0 and next_prefix.prefixlen == 64:
                        prefixes.append(next_prefix.prefix)
                    next_prefix = next_prefix.payload.getlayer(
                        ICMPv6NDOptPrefixInfo)
            except AttributeError:
                pass
            warn("RA contains following prefix(es): %s\n" % ` prefixes `)

            if packet.haslayer(ICMPv6NDOptUSSig):
                secured = False

                list_of_pubkeys = CGAPKExtListtoPubKeyList(
                    packet[CGAParams].ext)
                list_of_pubkeys.insert(0, packet[CGAParams].pubkey)
                pubkey = list_of_pubkeys[packet[ICMPv6NDOptUSSig].pos]
                pkhash = get_public_key_hash(
                    pubkey, packet[ICMPv6NDOptUSSig].sigtypeID)


                if (isinstance(pubkey,PubKey) and \
                   (NDprotector.min_RSA_key_size <= pubkey.modulusLen
                   and pubkey.modulusLen <= NDprotector.max_RSA_key_size))\
                   or\
                   (NDprotector.ECCsupport and isinstance(pubkey, ECCkey)):

                    if CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \
                       packet[ICMPv6NDOptUSSig].keyh ==  pkhash and \
                       packet[ICMPv6NDOptUSSig].verify_sig(pubkey):
                        secured = True
                    else:
                        warn(
                            "an ingoing RA has failed CGA verification or Universal Signature test\n"
                        )
                else:
                    warn("A message we received contains"
                         " a Public Key whose size is not allowed\n")

                # extract the Link-Layer Address  from the RA message if any
                mac_address = None
                try:
                    mac_address = packet[ICMPv6NDOptSrcLLAddr].lladdr
                except AttributeError:
                    pass

                if secured:
                    keyh = packet[ICMPv6NDOptUSSig].keyh
                    if certcache.trustable_hash(keyh, prefixes,\
                            packet[ICMPv6NDOptUSSig].sigtypeID):
                        warn(
                            "Public Key associated to the signature corresponds to a certificate\n"
                        )
                    else:
                        # we need to send some CPS in order to recover the certpath
                        secured = False

                        import random

                        warn("Sending a CPS message\n")
                        req_id = random.randrange(1, 0xFFFF)
                        certcache.storeid(req_id)
                        p = Ether(src=get_if_hwaddr(interface),dst=mac_address) / \
                            IPv6(src="::", dst=packet[IPv6].src)/ \
                            ICMPv6SEND_CPS(id=req_id,comp=0xffff)
                        sendp(p, iface=interface, verbose=NDprotector.verbose)

                        # we wait for the answers
                        time.sleep(0.4)

                        # we check if the certificate path is now valid
                        secured = certcache.trustable_hash(keyh, prefixes,\
                                packet[ICMPv6NDOptUSSig].sigtypeID)

                # check the nonce if the destination address is not ff02::1
                if secured and not packet[
                        IPv6].dst == "ff02::1" and packet.haslayer(
                            ICMPv6NDOptNonce):
                    nonce = packet.getlayer(ICMPv6NDOptNonce).nonce
                    status = nc.check_nonce_in(packet[IPv6].src,
                                               packet[IPv6].dst, nonce)
                elif secured and packet[IPv6].dst == "ff02::1":
                    status = True

                public_key = str(packet.getlayer(CGAParams).pubkey)
                timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp

                if status:
                    warn(
                        "NC updating an entry with an secured RA message from the address: %s\n"
                        % packet.src)
                    status = nc.update(packet[IPv6].src, mac_address, public_key,\
                                       timestamp,secured, ssao= (signalgs, verifalgs))

                if status:

                    configured_addresses = nc.dump_addresses()

                    # Address Autoconfiguration part

                    if NDprotector.assign_addresses:
                        # extract the prefix(es) from the RA message that contains the Autonomous flag
                        prefixes_aut = []
                        try:
                            next_prefix = packet.getlayer(
                                ICMPv6NDOptPrefixInfo)
                            while next_prefix:
                                if next_prefix.prefixlen == 64 \
                                   and next_prefix.A == 1 \
                                   and next_prefix.validlifetime >= next_prefix.preferredlifetime:

                                    # we update the prefix cache
                                    # (note that the prefix cache will take care
                                    # of prefix whose valid lifetime is now 0)
                                    nc.update_prefix(
                                        next_prefix.prefix,
                                        next_prefix.preferredlifetime,
                                        next_prefix.validlifetime)

                                    # we will do autoconfiguration on these addresses
                                    if next_prefix.validlifetime != 0:
                                        prefixes_aut.append(
                                            (next_prefix.prefix,
                                             next_prefix.validlifetime,
                                             next_prefix.preferredlifetime))

                                    # set the Autonomous flag to 0 so linux kernel won't
                                    # set a new address too
                                    next_prefix.A = 0

                                next_prefix = next_prefix.payload.getlayer(
                                    ICMPv6NDOptPrefixInfo)
                        except AttributeError:
                            pass

                        for (prefix, valid, preferred) in prefixes_aut:
                            for address in configured_addresses:
                                # extract the prefix from the address
                                if address.get_prefix() == prefix:
                                    # we have a correspondance to this address
                                    # refresh the valid and preferred lifetime
                                    address.modify(valid, preferred)
                                    # let the RA message go
                                    break
                            # if no address exists for this prefix
                            else:
                                # create an address for each prefixes
                                new_address = Address(
                                    prefix=prefix,
                                    key=NDprotector.default_publickey,
                                    sec=1,
                                    interface=interface,
                                    autoconf=True,
                                    valid=valid,
                                    preferred=preferred)
                                warn(
                                    "created a new address (%s) for prefix %s\n"
                                    % (str(new_address), prefix))
                                nc.store_address(new_address)

                        # force checksum calculation
                        del (packet[IPv6].payload.cksum)
                        packet = IPv6(str(packet))

                        # force checksum calculation

                        # TC 02/19/10: for the record,
                        # that was the previous ugly version
                        # dirty hack to copy the message, except the PIO

                        # let the RA message go , but only after the prefix(es) has been removed
                        # (avoid that kernel autoconfiguration process get involved)
                        # eth = Ether(data)

                        # payld = packet
                        # while payld and not isinstance(payld,NoPayload):
                        #     current_layer = payld
                        #     try:
                        #         payld = payld.payload

                        #     except AttributeError:
                        #         payld = None

                        #     try:
                        #         if not isinstance(current_layer,ICMPv6NDOptPrefixInfo):
                        #             current_layer.remove_payload()
                        #             new_eth /= current_layer
                        #     except AttributeError:
                        #         payld = None

                        # TC 03/15/10: previous version included a Ethernet header that does not seem appropriate
                        # new_eth = Ether(src=eth.src, dst=eth.dst) / packet
                        # payload.set_verdict_modified(nfqueue.NF_ACCEPT,str(new_eth),len(str(new_eth)))
                        payload.set_verdict_modified(nfqueue.NF_ACCEPT,
                                                     str(packet),
                                                     len(str(packet)))
                        return 0

            elif NDprotector.mixed_mode:
                status = True
            else:
                status = False

    elif packet.haslayer(ICMPv6ND_RS):
        if NDprotector.is_router == True:
            # if the message does not come from the unspecified address,
            # perform normal checks

            if packet[IPv6].src == "::":
                # nothing to do on a message with the unspecified address as
                # source address
                status = True
            elif packet.haslayer(ICMPv6NDOptUSSig):

                if not (CGAverify(packet[IPv6].src,packet.getlayer(CGAParams)) and \
                   packet[ICMPv6NDOptUSSig].keyh == \
                    get_public_key_hash(packet[CGAParams].pubkey,packet[ICMPv6NDOptUSSig].sigtypeID) and \
                   packet[ICMPv6NDOptUSSig].verify_sig(packet[CGAParams].pubkey)):
                    warn(
                        "an ingoing packet has failed CGA verification or USO signature test\n"
                    )
                    status = False
                else:

                    # record the nonce if any
                    if packet.haslayer(ICMPv6NDOptNonce):
                        nc.record_nonce_in(packet[IPv6].src, packet[IPv6].dst,
                                           packet[ICMPv6NDOptNonce].nonce)

                    src_address = packet[IPv6].src
                    mac_address = None
                    try:
                        mac_address = packet.getlayer(
                            ICMPv6NDOptSrcLLAddr).lladdr
                    except AttributeError:
                        pass

                    public_key = str(packet.getlayer(CGAParams).pubkey)
                    timestamp = packet.getlayer(ICMPv6NDOptTimestamp).timestamp
                    warn(
                        "NC updating an entry with an secured RS message from the address: %s\n"
                        % packet.src)
                    status = nc.update(src_address,
                                       mac_address,
                                       public_key,
                                       timestamp,
                                       secured=True,
                                       ssao=(signalgs, verifalgs))

            elif NDprotector.mixed_mode:
                warn("letting in an unsecured RS message\n")
                status = True

            else:
                warn("dropping an unsecured RS message\n")
                status = False
        else:
            # Host do not process RS messages
            status = False

    else:
        warn("letting in a non NDP message\n")
        status = True

    if status:
        payload.set_verdict(nfqueue.NF_ACCEPT)
    else:
        payload.set_verdict(nfqueue.NF_DROP)
    return 0
Ejemplo n.º 4
0
def callback(i, payload):
    """a callback function called on each outgoing packets"""

    data = payload.get_data()

    packet = IPv6(data)

    nc = NeighCache()

    # fetching the latest configured addresses on the interfaces
    configured_addresses = nc.dump_addresses()

    if packet.haslayer(ICMPv6ND_NS) \
       or packet.haslayer(ICMPv6ND_NA):

        for addr in configured_addresses:
            if str(addr) == packet[IPv6].src:
                if packet.haslayer(ICMPv6ND_NS):
                    nonce = "".join(
                        [chr(random.randrange(255)) for i in range(6)])
                    nc.record_nonce_out(packet[IPv6].src, packet[IPv6].dst,
                                        nonce)
                else:
                    nonce = nc.pop_nonce_out(packet[IPv6].src,
                                             packet[IPv6].dst)
                data = addr.sign(data, nonce=nonce)
                warn("signing a NS or NA message\n")
                payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data),
                                             len(str(data)))
                return 0
        else:
            if NDprotector.mixed_mode:
                warn("letting go one outgoing unsecured packet\n")
                payload.set_verdict(nfqueue.NF_ACCEPT)
                return
            else:
                warn("dropping one unsecured packet\n")
                payload.set_verdict(nfqueue.NF_DROP)
                return 0
    elif packet.haslayer(ICMPv6ND_RS):
        if NDprotector.is_router == False:
            if packet[IPv6].src == "::":
                payload.set_verdict(nfqueue.NF_ACCEPT)
                return 0
            else:
                # we need to sign the message
                for addr in configured_addresses:
                    if str(addr) == packet[IPv6].src:

                        # we generate a nonce for this request
                        nonce = "".join(
                            [chr(random.randrange(255)) for i in range(6)])
                        nc.record_nonce_out(packet[IPv6].src, packet[IPv6].dst,
                                            nonce)

                        warn("signing an outgoing RS message\n")

                        data = addr.sign(data, nonce=nonce)
                        payload.set_verdict_modified(nfqueue.NF_ACCEPT,
                                                     str(data), len(str(data)))
                        return 0
                else:
                    if NDprotector.mixed_mode:
                        warn("letting go one outgoing unsecure RS packet\n")
                        payload.set_verdict(nfqueue.NF_ACCEPT)
                        return 0
                    else:
                        warn("dropping one unsecure RS packet\n")
                        payload.set_verdict(nfqueue.NF_DROP)
                        return 0
        else:
            # a router does not send this kind of messages
            payload.set_verdict(nfqueue.NF_DROP)

    elif packet.haslayer(ICMPv6ND_RA):
        if NDprotector.is_router:
            # we need to sign the message
            for addr in configured_addresses:
                if str(addr) == packet[IPv6].src:
                    nonce = nc.pop_nonce_out(packet[IPv6].src,
                                             packet[IPv6].dst)
                    if nonce != None:
                        nonce = nonce.data

                    warn("signing an outgoing RA message\n")

                    # note that if nonce is None, no nonce value will join this message
                    data = addr.sign(data, nonce=nonce)
                    payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(data),
                                                 len(str(data)))
                    return 0
            else:
                if NDprotector.mixed_mode:
                    warn("letting go one outgoing unsecure RA packet\n")
                    payload.set_verdict(nfqueue.NF_ACCEPT)
                    return 0
                else:
                    warn("dropping one unsecure RA packet\n")
                    payload.set_verdict(nfqueue.NF_DROP)
                    return 0
        else:
            # a host does not send RA messages
            payload.set_verdict(nfqueue.NF_DROP)
            return 0

    else:
        warn("letting a non NDP message go out\n")
        payload.set_verdict(nfqueue.NF_ACCEPT)

    return 0
Ejemplo n.º 5
0
def callback(i, payload):
    """a callback function called on each ingoing packets"""

    data = payload.get_data()
    packet = IPv6(data)

    # if something goes wrong and makes this callback crash,
    # the packet is dropped
    payload.set_verdict(nfqueue.NF_DROP)

    # receiving interface
    interface = get_if_list()[payload.get_indev() - 1]

    # extract all the TA option from the CPS/CPA
    list_of_node_trustanchor = []
    ta = packet[ICMPv6NDOptTrustAnchor]
    while ta:
        if ta.nametype == 1:
            # we get the name field of the TA option
            list_of_node_trustanchor.append(ta.name_field)

        ta = ta.payload[ICMPv6NDOptTrustAnchor]

    if NDprotector.is_router:
        # we have a CPS
        # (filtering rules were set accordingly)

        # CPS's message ID
        req_id = packet[ICMPv6SEND_CPS].id

        dest_node = packet[IPv6].src
        if dest_node == "::":
            # if the origin is the unspecified address
            # the answer is on the All-Node multicast address
            dest_node = "ff02::1"

        src_addr = "::"
        if packet[IPv6].dst != "ff02::1":
            src_addr = packet[IPv6].dst
        else:
            # lookup for an adress on this interface:
            nc = NeighCache()
            configured_addresses = nc.dump_addresses()
            for address in configured_addresses:
                if address.get_interface() == interface:
                    src_addr = str(address)
                    break

        # send multiple as many Certification Path
        # as there is Trust Anchor options
        for path in deepcopy(NDprotector.certification_path):
            trust_anchor = path[0]

            skip_ta = True

            if list_of_node_trustanchor == []:
                skip_ta = False
            else:
                # check if this trust anchor is trusted by the node
                # if it isn't, we check for the next cert in the path
                while path and skip_ta:
                    trust_anchor = path[0]

                    for ta in list_of_node_trustanchor:
                        # we found the correct trust anchor
                        if ta in str(Cert(trust_anchor)):
                            skip_ta = False
                            break
                    else:
                        try:
                            del path[0]
                        except IndexError:
                            warn(
                                "CertPath.py - callback - this is likely to be a bug\n"
                            )

            if skip_ta:
                # we do not have a Certification Path
                # down to this Trust Anchor
                continue

            # number of certificates to send
            # (we does not count the TA as we do not send it
            num_components = len(path) - 2

            # send as many CPA as there is certificates in the Certification Path
            for cert in path[1:]:
                c = Cert(cert)

                warn("sending a CPA message\n")
                p = Ether(src=get_if_hwaddr(interface)) / \
                    IPv6(src=src_addr,dst=dest_node)/ \
                    ICMPv6SEND_CPA(id=req_id,comp=num_components,allcomp=len(path) -1)/ \
                    ICMPv6NDOptCertificate(cert=str(c))
                sendp(p, iface=interface, verbose=NDprotector.verbose)

                num_components -= 1

    else:  # we have a CPA

        # connect to the Certificate Cache for future decisions
        certcache = CertCache()

        warn("Receiving a CPA message\n")

        req_id = packet[ICMPv6SEND_CPA].id

        # we only accept CPA if they are destined to all the nodes or
        # if they are destined to our node
        if (packet[IPv6].dst == "ff02::1"
                and req_id == 0) or certcache.id_match(req_id):

            lastCPA = (packet[ICMPv6SEND_CPA].comp == 0)

            # extract all the certificates and feed them to the cache
            certopt = packet[ICMPv6NDOptCertificate]
            while certopt:
                cert = certopt.cert
                cert = cert.output("PEM")
                certcache.storecert(req_id, cert)

                certopt = certopt.payload[ICMPv6NDOptCertificate]

            # when this is the last CPA message, we ask for the
            # certificate path validation process
            if lastCPA:
                certcache.checkcertpath(req_id)
Ejemplo n.º 6
0
    def sign(self,data, dad=False, nonce=None):
        """sign the data with RSA signature option, adds timestamp, nonce, etc.
        Data must be a valid NS, NA, RS, RA or ICMPv6 redirect packet"""
        ndp_msg = IPv6(data)

        if dad and ndp_msg[IPv6].src != "::" :
            raise AddressException("tried to sign a DAD message with the null address")
        elif not dad and ndp_msg[IPv6].src != self.__address:
            raise AddressException("tried to sign a message with an incorrect source address")


        if not ndp_msg.haslayer(ICMPv6ND_RS) \
           and not ndp_msg.haslayer(ICMPv6ND_RA) \
           and not ndp_msg.haslayer(ICMPv6ND_NS) \
           and not ndp_msg.haslayer(ICMPv6ND_NA) \
           and not ndp_msg.haslayer(ICMPv6ND_Redirect) :
           raise AddressException("tried to sign a packet that is not a NDP message")


        # TODO:
        # for now, we only take the first algorithm of the list,
        # latter, we will implement a round-robin mechanism
        nc = NeighCache()
        ( sign, verif ) = nc.get_ssao(ndp_msg.dst)
        neigh_sigalg = (sign + verif)
        sigtypeID = self.sign_doable(neigh_sigalg)
        if sigtypeID == None:
            if NDprotector.ECCsupport and isinstance(self.__key,ECCkey):
                sigtypeID = self.__key.get_sigtypeID()[0]
                warn("No matching Signature Algorithm to sign the message, using %s\n" \
                       % SigTypeID[sigtypeID])
            else:
                warn("No matching Signature Algorithm to sign the message, using RSA/SHA-1\n")
                sigtypeID = 0


        cgapds = CGAParams(modifier = pkcs_i2osp(self.__modifier, 16), \
                prefix = self.__prefix, \
                ccount = self.__collcount, \
                pubkey = self.__pubkey, \
                ext = self.__ext)
        # in some case, we may not really want to add a nonce value
        if nonce is None:
            # no nonce value are joined with this message
            ndp_msg /= ICMPv6NDOptCGA(cgaparams = cgapds) / \
                       ICMPv6NDOptTimestamp()
        else:

            # add the different options to a message 
            ndp_msg /= ICMPv6NDOptCGA(cgaparams = cgapds) / \
                       ICMPv6NDOptTimestamp() / \
                       ICMPv6NDOptNonce(nonce=nonce) 

        # compute the available Signature Algorithms
        sigalgs = SigAlgList_compute(sigtypeID, NDprotector.SignatureAlgorithms)

        ndp_msg /= ICMPv6NDOptSSA(sigalgs=sigalgs)

        # dirty hack: force the update of the payload length
        extra_payload_len = len(str(ndp_msg.getlayer(ICMPv6NDOptCGA)))
        ndp_msg[IPv6].plen += extra_payload_len

        # dirty hack: force to recompute the (ICMP) checksum
        del(ndp_msg[IPv6].payload.cksum)

        # freezing data inside the new option fields
        ndp_msg = IPv6(str(ndp_msg))

        # need to bind the key hash with address' PubKey
        pubkey = load_pkey(self.__key, NDprotector.ECCsupport)

        keyh = get_public_key_hash(pubkey, sigtypeID=sigtypeID)

        # adding the signature
        ndp_msg /= ICMPv6NDOptUSSig(key=self.__key, pos = self.__keypos,
                keyh = keyh, sigtypeID=sigtypeID)
        


        # dirty hack: force the update of the payload length
        extra_payload_len = len(str(ndp_msg.getlayer(ICMPv6NDOptUSSig)))
        ndp_msg[IPv6].plen += extra_payload_len


        # dirty hack: force to recompute the (ICMP) checksum (once again)
        del(ndp_msg[IPv6].payload.cksum)


        return ndp_msg