2015-08-12 12 views
6

Sto memorizzando l'indirizzo IP in che supporta un array di quattro 32-bit, indirizzo [4]. Essenzialmente un numero a 128 bit.Come posso sottrarre due indirizzi IPv6 (numeri 128bit) in C/C++?

Sto provando a calcolare il numero di IP in un dato intervallo IPv6 (quanti IP ci sono tra). Quindi si tratta di sottrarre l'uno dall'altro utilizzando due array con una lunghezza di quattro.

Il problema è che non è disponibile il tipo di dati 128 bit, non è possibile convertire in decimali.

Grazie mille!

+0

In pratica, non tutti gli intervalli saranno compresi tra/32 e/96? – MSalters

+1

IPv6 è progettato in modo da avere sempre più indirizzi di quanti ne avrai mai bisogno, quindi spero che non avrai mai bisogno di questo codice. Pensare al numero di indirizzi con IPv6 è di solito un segno che sei ancora bloccato in una mentalità IPv4 :) –

+1

Con alcune eccezioni, un intervallo di indirizzi IPv6 (subnet) sarà a/64, e che equivale a 18.446.744.073.709.551.616 indirizzi per sottorete . Per altre dimensioni, è sempre possibile calcolare 2^(lunghezza maschera 128). –

risposta

3

È possibile utilizzare una sorta di libreria big-int (se è possibile tollerare LGPL, GMP è la scelta). Fortunatamente, la sottrazione a 128 bit è facile da simulare a mano se necessario. Ecco una dimostrazione veloce e sporco di calcolare il valore assoluto di (a-b), per i valori di 128 bit:

#include <iostream> 
#include <iomanip> 

struct U128 
{ 
    unsigned long long hi; 
    unsigned long long lo; 
}; 

bool subtract(U128& a, U128 b) 
{ 
    unsigned long long carry = b.lo > a.lo; 
    a.lo -= b.lo; 
    unsigned long long carry2 = b.hi > a.hi || a.hi == b.hi && carry; 
    a.hi -= carry; 
    a.hi -= b.hi; 
    return carry2 != 0; 
} 

int main() 
{ 
    U128 ipAddressA = { 45345, 345345 }; 
    U128 ipAddressB = { 45345, 345346 }; 

    bool carry = subtract(ipAddressA, ipAddressB); 

    // Carry being set means that we underflowed; that ipAddressB was > ipAddressA. 
    // Lets just compute 0 - ipAddressA as a means to calculate the negation 
    // (0-x) of our current value. This gives us the absolute value of the 
    // difference. 
    if (carry) 
    { 
     ipAddressB = ipAddressA; 
     ipAddressA = { 0, 0 }; 
     subtract(ipAddressA, ipAddressB); 
    } 

    // Print gigantic hex string of the 128-bit value 
    std::cout.fill ('0'); 
    std::cout << std::hex << std::setw(16) << ipAddressA.hi << std::setw(16) << ipAddressA.lo << std::endl; 
} 

Questo ti dà il valore assoluto della differenza. Se l'intervallo non è enorme (64 bit o meno), quindi ipAddressA.lo può essere la risposta come semplice unsigned long long.

Se si dispone di problemi di imperfezioni, è possibile utilizzare i componenti intrinseci del compilatore per sfruttare alcune architetture, ad esempio amd64, se si desidera che sia ottimale su tale processore. _subborrow_u64 è l'intrinseco di amd64 per il necessario lavoro di sottrazione.

1

La struttura memorizza l'indirizzo nell'ordine dei byte di rete - o "big endian" - con il byte più significativo @s6_addr[0]. Non puoi contare sul fatto che gli altri membri del sindacato abbiano un nome o una definizione coerenti. Anche se si accede all'unione tramite un campo (non portatile) uint32_t, i valori devono essere convertiti con ntohl. Quindi un metodo portatile per trovare la differenza richiede un po 'di lavoro.

È possibile convertire in6_addr in uint64_t[2]. Attaccando con convenzioni tipico 'bignum', usiamo [0] per le basse 64-bit e [1] per le alte 64 bit:

static inline void 
in6_to_u64 (uint64_t dst[2], const struct in6_addr *src) 
{ 
    uint64_t hi = 0, lo = 0; 

    for (unsigned int i = 0; i < 8; i++) 
    { 
     hi = (hi << 8) | src->s6_addr[i]; 
     lo = (lo << 8) | src->s6_addr[i + 8]; 
    } 

    dst[0] = lo, dst[1] = hi; 
} 

e la differenza:

static inline unsigned int 
u64_diff (uint64_t d[2], const uint64_t x[2], const uint64_t y[2]) 
{ 
    unsigned int b = 0, bi; 

    for (unsigned int i = 0; i < 2; i++) 
    { 
     uint64_t di, xi, yi, tmp; 

     xi = x[i], yi = y[i]; 
     tmp = xi - yi; 
     di = tmp - b, bi = tmp > xi; 
     d[i] = di, b = bi | (di > tmp); 
    } 

    return b; /* borrow flag = (x < y) */ 
} 
+0

Quale sarebbe stato il metodo se potessi contare sull'esistenza di uint64_t. Quindi, per semplificare le cose, ho ** uint64_t addr_begin [2] ** e ** uint64_t addr_end [2] **, dove è garantito che ** addr_end> = addr_begin **. Come posso archiviare il risultato in ** uint64_t risultato [2] **? Grazie mille Brett, per il tuo tempo. – shadowyman

+1

@shadowyman - esempi aggiunti. la differenza u128 usa la stessa idea sottrazione/prestito come il 'vettore' del byte. –