Cloud with rain
.:G
G:.
0 and 1 serie, black on white
pulled card
myjsp.feelinglinux.com
ver. 1.1.9-4
Hallo, welcome to my world.
Here you can find some stuff about computer science.
<<< Enjoy your visit! >>>
0 and 1 serie, white on black

Indirizzi IPv4 e maschere. Esempi di calcolo in Java

        Scritto: Giansante Gabriele, 26/10/2016     

Indirizzamento IPv4
Funzionamento maschere di rete
Esempi Java relativi ad IPv4 e maschere

Introduzione

Lo scopo principale di questo articolo e' la realizzazione di alcuni metodi Java per effettuare alcune operazioni su indirizzi IP e maschere di rete.

Inizialmente e' presente un accenno all'indirizzamento IP (versione 4) ed alla gestione delle maschere per la definizione delle reti.
Successivamente passeremo alla pratica, ovvero la realizzazione di semplici routine in Java (facilmente trasportabili in altri linguaggi di programmazione) per la gestione di IP e maschere. In particolare, vedremo attraverso i metodi Java realizzati
  • come ottenere la rete a cui appartiene un IP, dati l'IP stesso e la maschera di rete
  • come ottenere il range completo di IP che compongono una rete (data come IP e maschera)
  • come verificare che un IP sia all'interno di una data rete (definita come ip base e maschera)
  • come verificare che una rete sia inclusa in un'altra rete (definita come ip base e maschera)
  • come convertire una maschera di rete dal formato IP (es. "255.248.0.0") al formato intero (es. "/13")
  • come convertire una maschera di rete dal formato intero (es. "/13") al formato IP (es. "255.248.0.0")
Pubblicita'

Indirizzamento IPv4

Vediamo solo qualche piccolo cenno relativo all'indirizzamento IP (per gli scopi dell'articolo non interessano la storia, le scelte passate o troppi dettagli). "IP" sta per "Internet Protocol" e "v4" per "version 4". L'indirizzamento IP rappresenta in poche parole un modo per individuare e raggiungere tutti gli host (computer, dispositivi di varia natura, ecc.) su internet.
Un indirizzo IPv4 in notazione decimale assume la forma di 4 numeri separati da un ".". Ogni numero puo' assumere un valore fra 0 e 255 (byte). Con questo tipo di indirizzamento possiamo avere un numero di indirizzi distinti al massimo pari alle combinazioni dei possibili valori dei 4 byte che compongono un indirizzo.
Questo numero massimo possiamo calcolarlo semplicemente come "X^Y" (X elevato ad Y) dove X e' il numero di possibili valori che puo' assumere un byte (256) ed Y il numero di byte possibili (4). Alternativamente, se consideriamo la codifica binaria, ogni byte e' composto da 8 bit, per un totale di 32 bit (4 byte). In questo caso avremmo X pari a 2 (0 ed 1 come possibili valori) e 32 il numero di bit possibili.
In entrambi i casi si avra' lo stesso risultato
X^Y = 256^4 = 2^32 = 4294967296
Gli indirizzi IP vengono raggruppati in reti piu' o meno grandi mediante l'uso di maschere di bit (maschera di rete). Una maschera e' una sequenza di 32 bit che, associata ad un indirizzo IP, ne determina le possibili variazioni di valore. L'insieme di questi possibili valori dell'IP formano una rete. Nel prossimo paragrafo sara' descritto piu' in dettaglio il funzionamento delle maschere di rete. Comunque, brevemente, una maschera puo' essere composta solo da numeri particolari del tipo (con la numerazione binaria)
1{0,n}0{0,m}
cioe' una sequenza di 1 (eventualmente nulla, lunga massimo "n") seguita da una sequenza di 0 (eventualmente nulla, lunga massimo "m"). Il totale delle cifre (n+m) deve essere 32. Puo' essere scritta come numero intero (numero di 1 della prima sequenza, cioe' "n") o come un IP, cioe', raggruppando le cifre binarie 8 a 8 come sequenza di 4 byte (un byte e' formato da 8 bit) separati dal punto.
     Binario: 11111111 11111100 00000000 00000000
        Byte: 255      252      0        0

      Intero: 14
          IP: 255.252.0.0
In base ai valori che assume, puo' definire una rete piu' o meno grande (si vedra' piu' avanti come) con un numero ben preciso di indirizzi IP associati, pari ad una potenza del 2 (nell'esempio 2^18=262144 IP).
Indico una rete nel seguente modo
    <IP rete>/<maschera>
    es.
      192.168.2.0/24
      192.168.2.0/255.255.255.0

Non tutte le reti sono utilizzabili. Nell'RFC 6890 vengono elencate le reti riservate per scopi speciali. Riporto per comodita' nella seguente tabella alcuni dati presenti nell'RFC (le descrizioni sono quelle originali, i calcoli sono stati effettuati con le routine mostrate nel paragrafo relativo agli esempi in Java).

Rete Range Descrizione
0.0.0.0/8 0.0.0.0 - 0.255.255.255
(16777216)
"This host on this network" (RFC1122)
10.0.0.0/8 10.0.0.0 - 10.255.255.255
(16777216)
Private-Use (RFC1918)
100.64.0.0/10 100.64.0.0 - 100.127.255.255
(4194304)
Shared Address Space (RFC6598)
127.0.0.0/8 127.0.0.0 - 127.255.255.255
(16777216)
Loopback (RFC1122)
169.254.0.0/16 169.254.0.0 - 169.254.255.255
(65536)
Link Local (RFC3927)
172.16.0.0/12 172.16.0.0 - 172.31.255.255
(1048576)
Private-Use (RFC1918)
192.0.0.0/24 192.0.0.0 - 192.0.0.255
(256)
IETF Protocol Assignments (RFC6890)
192.0.0.0/29 192.0.0.0 - 192.0.0.7
(8)
DS-Lite (RFC6333)
192.0.2.0/24 192.0.2.0 - 192.0.2.255
(256)
Documentation (TEST-NET-1) (RFC5737)
192.88.99.0/24 192.88.99.0 - 192.88.99.255
(256)
6to4 Relay Anycast (RFC3068)
192.168.0.0/16 192.168.0.0 - 192.168.255.255
(65536)
Private-Use (RFC1918)
198.18.0.0/15 198.18.0.0 - 198.19.255.255
(131072)
Benchmarking (RFC2544)
198.51.100.0/24 198.51.100.0 - 198.51.100.255
(256)
Documentation (TEST-NET-2) (RFC5737)
203.0.113.0/24 203.0.113.0 - 203.0.113.255
(256)
Documentation (TEST-NET-3) (RFC5737)
240.0.0.0/4 240.0.0.0 - 255.255.255.255
(268435456)
Reserved (RFC1112)
255.255.255.255/32 255.255.255.255
(1)
Limited Broadcast (RFC0919)
Reti riservate per scopi particolari

Nella tabella si nota la presenza di alcune reti destinate ad uso privato (evidenziate in giallo). Queste reti (ma non solo queste) non possono essere pubbliche, cioe' esposte direttamente su internet. Vengono usate localmente per realizzare sottoreti isolate o non isolate che comunque accedono ad internet solamente attraverso un gateway (router, host, ecc.) con indirizzo pubblico (o appartenente ad un'altra rete privata che a sua volta ha accesso ad internet nello stesso modo).
Schematicamente ed approssimativamente, il tutto puo' essere, ad esempio, visto cosi':

Rete Pubblica (WAN): 
               [IP base rete] ... [81.174.20.165] ... [IP broadcast]
                                         |
Reti private (LAN):              [192.168.0.0/16]
                                         |
                                        ...
                                         |
                            +------------+-----------+
                            |            |           |
                    [192.168.10.0/24]   ...   [192.168.45.0/24]
                            |                        | 
                           ...                      ...
Nello schema ho una rete pubblica di cui fa parte l'IP "81.174.20.165". Questo IP potrebbe essere l'indirizzo lato WAN (Wide Area Network) cioe' verso l'esterno, di un router che ha configurata come LAN (Local Area Network) la rete "192.168.0.0/16". A sua volta, questa rete privata, potrebbe avere al di sotto altre sottoreti accessibili mediante access point WIFI, altri router, host o qualsiasi altro modo possibile. Il punto e' che, essendo la rete "192.168.0.0/16" una rete privata, non verra' mai esposta all'esterno con il proprio indirizzamento ma solo con l'IP "81.174.20.165". Chiunque voglia accedere ad un host della rete privata dovra' farlo indirizzandosi verso l'IP pubblico (ci pensera' il router ad instradare le richieste verso l'host corretto nella rete privata).
Con questo modo di utilizzare le reti private, praticamente e' stato possibile superare i limiti legati al numero massimo di IP pubblici possibili. Infatti, potenzialmente quasi ogni IP pubblico che non sia un IP base di rete o un IP di broadcast (il primo e l'ultimo di una rete) puo' avere dietro a sua volta una rete privata, moltiplicando esponenzialmente il numero di host che possono essere messi on-line. Poco importa il fatto che due o piu' host con IP pubblici diversi abbiano dietro la stessa identica sottorete privata, quella che conta e' l'interfaccia pubblica.
A proposito del'esempio, L'IP "81.174.20.165" e' preso a caso fra quelli della mia rete (tra l'altro corrisponde al server che ospita questo sito, non ad un router) giusto per non usare IP di qualcun altro; lo schema delle reti private, inoltre, e' frutto della fantasia.

Prima di passare al dettaglio di come funzionano le maschere di rete, vorrei dare alcuni link esterni al sito utili a chi volesse approfondire il discorso dell'Internet Protocol, andando oltre gli scopi di quest'articolo:


Funzionamento maschere di rete

Come visto, la funzione della maschera, in associazione ad un indirizzo IP di base e' quella di definire interamente una rete.
Pubblicita'
Prima di parlare di come usare la maschera di rete, vediamo tecnicamente come e' formata e quali sono le regole che segue.
La maschera di rete puo' essere indicata come un numero intero oppure come un indirizzo IP. La corrispondenza fra la notazione IP e la notazione intera e' una corrispondenza biunivoca, ovvero e' possibile passare da una notazione all'altra in modo univoco e viceversa.
Il nome stesso di maschera suggerisce la sua funzione, cioe', in pratica, stabilire quali parti dell'indirizzo IP di base rimangono fisse e quali possono variare, definendo esplicitamente l'intero indirizzamento di una rete. Consideriamo la numerazione binaria invece che quella decimale con cui siamo soliti "giocare".
Un indirizzo IPv4 e' formato da 4 numeri interi con valore da 0 a 255 (1 byte, in numerazione binaria pari ad una sequenza di 8 bit).
Ogni byte puo' essere espresso con il corrispondente numero binario ad 8 cifre, in totale la maschera di tipo IP e' formata da una sequenza di 4 byte o di 32 bit.
Quello che distingue una maschera di tipo IP da un semplice indirizzo IP e' che e' costituita da una sequenza di bit tale che i primi "n" siano "1" ed i successivi "m" siano "0", con "n+m=32" (8*4). Vediamo due esempi di maschere, dove, per comodita', rappresento i numeri sia in binario che come byte.
    255.255.255.0
    11111111 (255) . 11111111 (255) . 11111111 (255) . 00000000 (0)

    255.248.0.0
    11111111 (255) . 11111000 (248) . 00000000 (0)   . 00000000 (0)

La prima maschera (255.255.255.0), letta in binario, e' formata da una sequenza di 24 bit pari ad 1 e di 8 pari a 0.
La seconda maschera (255.248.0.0), letta in binario, e' formata da una sequenza di 13 bit pari ad 1 e di 19 pari a 0.
Il numero di bit iniziali ad "1" non e' un numero casuale e corrisponde effettivamente alla notazione intera di una maschera. La maschera in notazione intera rappresenta il numero di bit a "1" iniziali che la compongono. Infatti, la maschera "255.255.255.0" puo' essere indicata anche con "24" (24 bit), mentre "255.248.0.0" puo' essere indicata come "13" (13 bit). Il passaggio da IP ad intero e' quindi immediato ed univoco.
Stabilita la regola per cui si ha sempre una sequenza di "1" (eventualmente di lunghezza nulla) seguita da una sequenza di "0", la seguente tabella mostra tutti i possibili valori di una maschera (fedelmente alla regola), affiancando la notazione intera, la notazione ad IP e la notazione binaria.

Maschera
(numero intero)
Maschera
(indirizzo IP)
Maschera
(numero binario)
0 0.0.0.0 00000000 00000000 00000000 00000000
1 128.0.0.0 10000000 00000000 00000000 00000000
2 192.0.0.0 11000000 00000000 00000000 00000000
3 224.0.0.0 11100000 00000000 00000000 00000000
4 240.0.0.0 11110000 00000000 00000000 00000000
5 248.0.0.0 11111000 00000000 00000000 00000000
6 252.0.0.0 11111100 00000000 00000000 00000000
7 254.0.0.0 11111110 00000000 00000000 00000000
8 255.0.0.0 11111111 00000000 00000000 00000000
9 255.128.0.0 11111111 10000000 00000000 00000000
10 255.192.0.0 11111111 11000000 00000000 00000000
11 255.224.0.0 11111111 11100000 00000000 00000000
12 255.240.0.0 11111111 11110000 00000000 00000000
13 255.248.0.0 11111111 11111000 00000000 00000000
14 255.252.0.0 11111111 11111100 00000000 00000000
15 255.254.0.0 11111111 11111110 00000000 00000000
16 255.255.0.0 11111111 11111111 00000000 00000000
17 255.255.128.0 11111111 11111111 10000000 00000000
18 255.255.192.0 11111111 11111111 11000000 00000000
19 255.255.224.0 11111111 11111111 11100000 00000000
20 255.255.240.0 11111111 11111111 11110000 00000000
21 255.255.248.0 11111111 11111111 11111000 00000000
22 255.255.252.0 11111111 11111111 11111100 00000000
23 255.255.254.0 11111111 11111111 11111110 00000000
24 255.255.255.0 11111111 11111111 11111111 00000000
25 255.255.255.128 11111111 11111111 11111111 10000000
26 255.255.255.192 11111111 11111111 11111111 11000000
27 255.255.255.224 11111111 11111111 11111111 11100000
28 255.255.255.240 11111111 11111111 11111111 11110000
29 255.255.255.248 11111111 11111111 11111111 11111000
30 255.255.255.252 11111111 11111111 11111111 11111100
31 255.255.255.254 11111111 11111111 11111111 11111110
32 255.255.255.255 11111111 11111111 11111111 11111111
Possibili valori di una maschera

Applicando la maschera di rete ad un indirizzo IP qualsiasi, si puo' determinare con precisione qualsiasi informazione sulla rete a cui appartiene l'IP stesso.
Come gia' detto, la maschera definisce la parte di un IP che puo' variare e quella che invece rimane fissa. La sequenza di "1" rappresenta la parte che non puo' variare.
Vediamo un esempio (ho incolonnato appositamente i numeri binari uno sotto l'altro):
    Rete: 192.168.32.0/20
  
    Ip di rete: 192.168.32.0  = 11000000 10101000 00100000 00000000
    Maschera: 20 = 20 bit a 1 = 11111111 11111111 11110000 00000000
    Parte fissa:                11000000 10101000 0010
    Parte variabile:                                  XXXX XXXXXXXX

La parte variabile potra' assumere tutti i numeri compresi fra "0000 00000000" e "1111 11111111" (bit meno significativo a destra).
Il primo numero, appeso (concatenato) alla parte fissa coincide con l'IP di base (ip di rete), ovvero il primo IP della rete (limite inferiore). L'ultimo numero, appeso (concatenato) alla parte fissa, e'
11000000 10101000 00101111 11111111
ovvero, trasformando i gruppi di 8 bit in byte, "192.168.47.255". Questo numero e' l'ultimo IP della rete (limite superiore), cioe' l'IP di broadcast.
Piu' formalmente, otteniamo l'ultimo IP mediante negazione logica della maschera e con un'opearzione di OR logico fra il risultato della negazione e l'IP di base.
    Maschera=      11111111 11111111 11110000 00000000
 1) Negazione
    maschera=      00000000 00000000 00001111 11111111
    IP base:       11000000 10101000 00100000 00000000
 2) IP | Masch. =  11000000 10101000 00101111 11111111
    IP broadcast = 11000000 10101000 00101111 11111111

Abbiamo scoperto quindi, tramite la maschera, che la rete "192.168.32.0/20" va dall'IP "192.168.32.0" all'IP "192.168.47.255".
Sappiamo anche che "1111 11111111", essendo l'ultimo numero variabile possibile, trasformato in decimale ed aumentato di 1 (per comprendere "0000 00000000"), rappresenta il numero di IP presenti nella rete. Il valore "1111 11111111" corrisponde a "4095", ovvero "4096 IP" totali nella rete. Matematicamente, trattandosi di numerazione binaria, il numero di IP lo otteniamo con "2^(numeroBitParteVariabile)", in questo caso "2^12=4096".
Questo e' il numero totale di IP della rete. Il numero di IP utilizzabili ed assegnabili a dispositivi vari e' piu' basso e non comprende due IP riservati: IP identificativo di rete (quello che io chiamo IP di base); IP di broadcast. Quindi, in realta' avremo il "totale" degli IP della rete ed il numero di IP effettivamente utilizzabili ("totale-2").

In questo esempio siamo partiti dall'IP base della rete, ma le informazioni le avremmo potute ottenere anche con qualsiasi altro IP appatenente alla stessa rete.
Partendo infatti da un IP qualsiasi e dalla maschera che identifica la rete in cui si trova, possiamo ricavare l'IP base della rete mediante una semplice operazione AND logica fra la maschera e l'IP. Data la natura della maschera, l'AND lascera' inalterata la parte fissa azzerando la parte variabile.
    IP:       192.168.35.27 = 11000000 10101000 00100011 00011011
    MASCHERA: 20            = 11111111 11111111 11110000 00000000
              IP & MASCH. --> 11000000 10101000 00100000 00000000

Abbiamo ottenuto l'IP base (ip di rete), attraverso il quale, come visto, arriviamo all'IP di broadcast (l'ultimo).

Conoscere queste informazioni ci consente di avere i mattoni necessari per operazioni piu' complesse, come, ad esempio, la verifica "meccanica" di inclusione di un IP in una data rete, oppure l'inclusione di una rete intera in un'altra rete. Ma questo verra' visto piu' in dettaglio nel paragrafo che segue.
Pubblicita'

Esempi Java relativi ad IPv4 e maschere

E' il momento di applicare la teoria alla pratica mostrando una serie di metodi Java (testati con JDK 1.8) che compiono operazioni varie utilizzando IP e maschere. Per semplicita' ho tralasciato alcuni controlli (tipo il formato dei parametri in ingrasso) che possono essere facilmente aggiunti (come, ad esempio, la validazione di un IP in formato stringa mediante espressioni regolari).
In particolare si vedranno i seguenti gruppi di metodi
  • Utilita' (operazioni binarie su IP, trasformazione IP da stringa ad array di byte, ecc.)
  • Elaborazione della maschera di una rete (conversione maschera da IP a intero e viceversa)
  • Operazioni di insiemistica su reti di IP (appartenenza IP ad una rete o una rete ad una piu' grande, ecc.)

Ho tradotto i metodi che seguono anche in Javascript alla pagina
Strumenti online: IPv4 e Reti (Javascript)
dove se ne puo' verificare il funzionamento attraverso appositi form. Inoltre, nel codice Javascript sono presenti anche delle espressioni regolari con cui verificare che una stringa rappresenti un indirizzo IP, una maschera o addirittura una rete valida.
Utilita'

andIp(int[] ip1, int[] ip2): int[]

Questo metodo effettua l'AND logico fra i byte che compongono i due indirizzi IP passati. Ogni byte del primo IP viene messo in AND con l'analogo byte del secondo IP. Questo metodo puo' essere utile nell'applicazione di una maschera ad un IP.
Ad esempio, considerando gli IP "192.168.1.54" e "255.255.255.0", dopo l'esecuzione del codice seguente
int[] ip1 = new int[]{192,168,1,54};
int[] ip2 = new int[]{255,255,255,0};
int[] ip3 = andIp(ip1, ip2);
il contenuto di "ip3" sara' {192,168,1,0} (che non "casualmente" corrisponde all'IP base della rete di cui fa parte l'"ip1" con la maschera "ip2")
    /**
     * Effettua l'AND logico dei due IP passati. L'AND avviene per singola componente, ovvero, 
     * scomposto l'IP nelle sue 4 parti, ogni parte del primo ip viene messa in and con l'analoga 
     * parte del secondo ip, a formare un ip finale con le 4 parti risultato degli and descritti.
     * [p1,p2,p3,p4] & [p5,p6,p7,p8] = [p1&p5, p2&p6, p3&p7, p4&p8]
     * @param ip1 primo ip scomposto nelle sue 4 parti
     * @param ip2 secondo ip scomposto nelle sue 4 parti
     * @return ip risultante dall'AND delle singole parti degli ip passati
     */
    public static int[] andIp(int[] ip1, int[] ip2) {
        int[] result = new int[4];
        for (int i = 0; i < 4; i++) {
            result[i] = ip1[i] & ip2[i];
        }
        return result;
    }
        


notIp(int[] ip): int[]

Questo metodo effettua la negazione logica dell'IP passato. L'operazione viene eseguita tramite sottrazione (byte a byte) dell'IP passato da "255.255.255.255" (che equivarrebbe ad un "not" bit a bit). Puo' essere utile per ottenere la negazione delle maschere.
Ad esempio, considerando l'IP "255.255.128.0", dopo l'esecuzione del codice seguente
int[] ip1 = new int[]{255,255,128,0};
int[] ip2 = notIp(ip1);
il contenuto di "ip2" sara' {0,0,127,255}.
    /**
     * Effettua il NOT logico dell'IP passato.
     * @param ip ip scomposto nelle sue 4 parti
     * @return ip risultante dal NOT delle singole parti dell'ip passato
     */
    public static int[] notIp(int[] ip) {
        int[] result = new int[4];
        int[] notMask = new int[]{255, 255, 255, 255};
        for (int i = 0; i < 4; i++) {
            result[i] = notMask[i] - ip[i];
        }
        return result;
    }
        


addIp(int[] ip1, int[] ip2): int[]

Questo metodo somma due IP byte a byte. La somma viene autolimitata a "255" (quando la somma di due byte supera il valore 255, il risultato viene imposto a "255")
Ad esempio, considerando gli IP "192.168.1.54" e "10.200.100.0", dopo l'esecuzione del codice seguente
int[] ip1 = new int[]{192,168,1,54};
int[] ip2 = new int[]{10,200,100,0};
int[] ip3 = andIp(ip1, ip2);
il contenuto di "ip3" sara' {202,255,101,54}.
    /**
     * Effettua l'addizione byte a byte fra i due IP passati. Limita 
     * il risultato massimo delle singole addizioni a 255.
     * @param ip1 primo ip scomposto nelle sue 4 parti
     * @param ip2 secondo ip scomposto nelle sue 4 parti
     * @return ip risultante dall'addizione delle singole parti 
     *    degli ip passati (con limite usperiore a 255)
     */
    public static int[] addIp(int[] ip1, int[] ip2) {
        int[] result = new int[4];
        for (int i = 0; i < 4; i++) {
            result[i] = ip1[i] + ip2[i];
            if (result[i]>255) result[i] = 255;
        }
        return result;
    }
        


equalsIp(int[] ip1, int[] ip2): boolean

Questo metodo verifica semplicemente che gli IP passati siano identici mediante controllo di uguaglianza byte a byte.
    /**
     * Controlla l'uguaglianza degli ip passati. 
     * @param ip1 Primo ip scomposto nelle sue 4 parti
     * @param ip2 Secondo ip scomposto nelle sue 4 parti
     * @return true se gli ip sono uguali, false altrimenti
     */
    public static boolean equalsIp(int[] ip1, int[] ip2) {
        boolean result = true;
        for (int i = 0; i < 4; i++) {
            if (ip1[i] != ip2[i]) {
                result = false;
                break;
            }
        }
        return result;
    }
        


lessThanIp(int[] ip1, int[] ip2): boolean

Questo metodo verifica che il primo IP sia strettamente minore del secondo.
Il controllo viene fatto byte a byte. Un IP e' maggiore di un altro quando e' piu' vicino al 255.255.255.255 dell'altro.
Dal momento che la negazione di "lessThanIp" corrisponde ad un "maggiore o uguale", per verificare che un IP sia strettamente maggiore, usare l'espressione
!lessThanIp(ip1, ip2) && !equalsIp(ip1, ip2)
Implementazione:
    /**
     * Controlla che il primo IP sia strettamente inferiore al secondo. 
     * @param ip1 Primo ip scomposto nelle sue 4 parti
     * @param ip2 Secondo ip scomposto nelle sue 4 parti
     * @return true se il primo ip e' strettamente minore del secondo
     */
    public static boolean lessThanIp(int[] ip1, int[] ip2){
        boolean result = false;
        //Va avanti fino a che i numeri sono uguali. 
        //Si ferma quando sono diversi e verifica quale e' piu' grande.
        for (int i=0; i<4; i++){
            if (ip1[i]<ip2[i]){
                //sicuramente minore
                result = true;
                break;
            } else if (ip1[i]>ip2[i]){
                //sicuramente maggiore
                break;
            }
        }
        return result;
    }
        


getIpArray(String ip): int[]

Spesso lavoriamo con gli IP in formato stringa (es. "192.168.1.54"). Questo metodo trasforma la stringa rappresentante l'IP in un array di 4 interi, ognuno dei quali rappresenta un byte dell'indirizzo IP. L'array ottenuto con questo metodo puo' essere usato negli altri metodo descritti che accettano gli IP sotto forma di array di interi.
Ad esempio, considerando la stringa "192.168.1.54", dopo l'esecuzione del codice seguente
String ipString = "192.168.1.54";
int[] ip = getIpArray(ipString);
il contenuto di "ip" sara' {192,168,1,54}.
    /**
     * Scompone l'ip passato nelle sue 4 parti
     * @param ip indirizzo ip completo da scomporre (es "123.124.125.126")
     * @return un array di lunghezza 4 contenente le singole parti dell'ip passato
     */
    public static int[] getIpArray(String ip) {
        int[] result = {0, 0, 0, 0};
        String[] stringIpArray = ip.split("\\.");
        for (int i = 0; i < stringIpArray.length; i++) {
            result[i] = Integer.parseInt(stringIpArray[i]);
        }
        return result;
    }
        


getIpString(int[] ip): String

Una volta effettuate tutte le elaborazioni con i metodi mostrati in questo articolo, si potrebbe voler ricostruire l'IP sotto forma di unica stringa. Il metodo e' banale.
Ad esempio, considerando l'array {192,168,1,54}, dopo l'esecuzione del codice seguente
int[] ipArray = new int[]{192, 168, 1, 54};
String ip = getIpString(ipArray);
il contenuto di "ip" sara' "192.168.1.54".
    /**
     * Ricompone l'ip passato concatenando le sue 4 parti
     * @param ip indirizzo ip scomposto nelle sue 4 parti (es. {123,124,125,126})
     * @return l'indirizzi IP come stringa
     */
    public static String getIpString(int[] ip) {
        String result = 
            ip[0] + "." + 
            ip[1] + "." + 
            ip[2] + "." + 
            ip[3];
        return result;
    }
        


Elaborazione della maschera di una rete

toIpMask(int mask): int[]

Questo metodo trasforma la maschera da numero intero al formato IP (4 byte). Come gia' spiegato nei paragrafi precedenti, il formato intero indica il numero iniziale di "bit" consecutivi che valgono 1 che compongono la maschera. I restanti bit sono a 0.
Ad esempio, "13" = "13 bit a 1" = "11111111.11111000.00000000.00000000" = "255.248.0.0".
Il funzionamento dell'algoritmo e' abbastanza semplice. La quaterna di byte finale e' formata sicuramente da "n" byte iniziali a 255, un byte fra 0 e 255, "m" byte finali a 0. I contatori "n" ed "m" possono valere 0 (ovvero potrebbero non esserci byte iniziali a 255 o potrebbero non esserci byte finali a 0).
Maschera = {n volte 255, 0...255, m volte 0}
Esempi:
255.255.248.0 (n=2; m=1),
0.0.0.0 (n=0; m=4),
255.255.255.248 (n=3; m=0), ecc.
Considerato che il risultato finale sara' un array di 4 elementi, viene prima individuato l'indice dell'elemento "centrale" che non sara' ne 255 ne 0, ammesso che esista.
Un byte e' formato da 8 bit, un IP e' composto da 4 byte, ovvero 8*4=32 bit.
La maschera e' sempre formata da "X" bit pari ad 1 seguiti da "(8*4-X)" bit pari a 0 (cioe' i restanti).
Possiamo dire tranquillamente che dividendo per 8 la maschera otteniamo il numero di byte (gruppi di 8 bit) pieni (tutti i bit pari ad 1), rappresentato dalla parte intera della divisione. Il resto ci dara', invece, il numero di bit pari ad 1 che formano l'inizio del byte successivo.
Ritornando agli "n" byte iniziali ed agli "m" byte finali, avremo che
n = (int) maschera/8
(n e' uguale alla parte intera della divisione per 8).
Bisogna valutare a questo punto il resto della divisione.
Se il resto e' maggiore di 0 (divisione non perfetta), vuol dire che agli "n" byte pari a 255 ne segue uno che ha valore maggiore di 0 ma minore di 255; il resto dei byte avra' valore pari a 0 ("m=4-n-1" byte). Ad esempio,
maschera = 13
(int) maschera/8 = 1
maschera%8 = 5
n = 1; m = 4-1-1 = 2
255.X.0.0, con X = 5 bit a 1 = 11111000 = 248
255.248.0.0

Se, invece, abbiamo una divisione perfetta, ovvero il resto della divisione per 8 e' 0, agli "n" byte pari a 255 ne seguono "m=4-n" byte pari a 0. Ad esempio,
maschera = 24
(int) maschera/8 = 3
maschera%8 = 0
n = 3; m = 4-3 = 1
255.255.255.0
Conosciamo quindi tutte le cifre e le posizioni in cui vanno. Rimane solo da trasformare "X" da numero binario a numero decimale. Qui io l'ho implementato come serie di operazioni logiche SHIFT ed OR, ma puo' essere fatto in qualsiasi altro modo dettato dalla propria fantasia.
    /**
     * Trasforma la maschera in formato numero intero nella 
     * maschera in formato ip, scomposto nelle sue 4 componenti.
     * La maschera in formato intero rappresenta il numero di bit 
     * settati ad 1 nella corrispondente versione 
     * binaria della maschera in formato ip: es...
     * 
     *   /24 = 11111111 | 11111111 | 11111111 | 00000000
     *   /13 = 11111111 | 11111000 | 00000000 | 00000000
     *   ecc.
     * 
     * @param mask maschera in formato numero intero (es. 19)
     * @return maschera in formato ip, scomposto nelle sue 4 parti
     */
    public static int[] toIpMask(int mask) {
        int[] result;
        if (mask==0){ 
            //Nessun calcolo necessario... 0 e' 0...
            result = new int[]{0, 0, 0, 0};
        } else {
            //Inizializzo per comodita' il risultato come se avessi una maschera "32"
            result = new int[]{255, 255, 255, 255};

            //i=0 => resto=0 => ho un numero formato da primi 
            //                  byte a 255 e gli altri a 0
            //i>0 => resto>0 => ho un numero formato da primi 
            //                  byte a 255, uno 1..254, il resto a 0
            int i = mask % 8;

            //Se i>0, count mi da la posizione nell'array dove inserire 
            //il byte disparo (diverso da 0 o 255).
            //Se i=0, count mi da, invece, la posizione del primo 0 (ho una 
            //divisione perfetta).
            int count = mask / 8;

            //conterra', alla fine, il limite per 
            //l'inserimento dei byte "0".
            int zeroLimit = count;
            if (i > 0) {
                //Calcolo numero "centrale",
                //trasformazione binario/byte
                byte temp = 0;
                //Ciclo sul numero di bit a 1
                for (int j = 0; j < i; j++) {
                    //Shift j volte dell'1, metto il risultato in 
                    //OR col valore temporaneo
                    temp |= (1 << j); 
                }
                //Ho i bit spostati a "destra", shift a "sinistra"
                //per avere prima gli 1 e poi gli 0
                result[count] = (temp << (8 - i));
            } else {
                if (count>0) {
                    zeroLimit = count - 1;
                }
            }

            //uso la variabile "zeroLimit" perche' a seconda 
            //del caso con "resto=0" o "resto>0" devo usare 
            //"count" o "count-1"
            //Metto a "0" i restanti byte
            for (int j = 3; j > zeroLimit; j--) {
                result[j] = 0;
            }
        }
        return result;
    }
        


toIntMask(int[] mask): int

Questo metodo trasforma la maschera dal formato IP (4 byte) al formato numero intero. Come gia' spiegato nei paragrafi precedenti, il formato intero indica il numero iniziale di "bit" consecutivi pari a 1 che compongono la maschera. I restanti bit sono a 0.
Ad esempio, "13" = "13 bit a 1" = "11111111.11111000.00000000.00000000" = "255.248.0.0".
L'algoritmo di conversione, in questo caso e' banalissimo.
  1. Contare quanti "255" ci sono all'inizio dell'IP
  2. Moltiplicare i "255" contati per 8 (ogni 255 corrisponde ad 8 bit 1)
  3. Se dopo l'ultimo "255" c'e' "0", il numero ottenuto fino ad ora e' la maschera (numero di bit a 1 totali)
  4. Se dopo l'ultimo "255" c'e' un numero diverso da 0, contare quanti "1" formano la prima parte del numero convertito in formato binario
  5. Aggiungere quest'ultimo numero a quello calcolato nel punto 2. La somma e' la maschera.
Ad esempio:
maschera = 255.255.192.0

Ci sono 2 "255" ("11111111"), risultato parziale = 8*2 = 16
Il numero "192" in binario e' 11000000, inizia con 2 uno.
Sommiamo 16 e 2, otteniamo 18.

XXX.YYY.ZZZ.TTT/255.255.192.0 = XXX.YYY.ZZZ.TTT/18
Vediamone l'implementazione:
    /**
     * Trasforma la maschera in formato ip (scomposta nelle sue 4 componenti)
     * nella maschera in formato numero intero.
     * La maschera in formato intero rappresenta il numero di bit 
     * settati ad 1 nella corrispondente versione 
     * binaria della maschera in formato ip: es...
     * 
     *   255.255.255.0 = 11111111 | 11111111 | 11111111 | 00000000 = /24
     *   255.248.0.0   = 11111111 | 11111000 | 00000000 | 00000000 = /13
     *   ecc.
     * 
     * @param mask maschera in formato ip (es. {255,248,0,0})
     * @return maschera in formato intero (es. 13)
     */
    public static int toIntMask(int[] mask){
        int result = 0;
        // Ciclo sui 4 compoenti della maschera/IP
        for (int i=0; i<4; i++){
            if (mask[i]==255){
                // 255 sono tutti 1 (8 bit a 1). Comincio a contarli
                result += 8;
            } else if (mask[i]==0){
                // Se la cifra e' uno zero, esco ed interrompo la 
                // "conta" perche' saranno tutti 0 anche i successivi
                break;
            } else {
                //Caso in cui il numero e' compreso fra 1 e 254.
                //Devo trovare la prima posizione dello 0 o l'ultima dell'1 
                //all'interno della sua rappresentazione binaria.
                //L'idea e' di usare le potenze di 2 (numeri con un unico bit a 1).
                //Per ottenere queste potenze di due uso il trucco di shiftare N volte 
                //verso sinistra (bit meno significativo a destra) il valore "00000001".
                //Comincio con N=4 in modo da ottimizzare potenzialmente la ricerca 
                //(binaria come primo passo, sequenziale per la rimanente meta' dei bit).
                //La potenza di 2 a cui sono arrivato con lo shift e' indicativa della 
                //posizione del bit (indice) all'interno del byte (8 bit).
                //Metto in AND il numero con la potenza del 2 ed ottengo 1 o 0 a seconda che 
                //il corrispondente bit sia settato (1) o no (0). A seconda che sia "0" o "1" 
                //capisco la direzione in cui procedere. Vado avanti nella direzione 
                //individuata, fino a contare tutti gli 1.
                int shift = 4;
                //Variabile di comodo che conterra', alla fine,
                //il numero di uno consecutivi trovati all'inizio di mask[i]
                int numberOfOne = 0;
                boolean goRight = (mask[i] & (1 << shift))>0;
                // Unico codice per entrambe le direzioni, ci pensa "goRight" a selezionare
                // indirettamente il ramo su cui continuare
                while ((goRight && ((shift--)>0)) || (!goRight && ((shift++)<7))){
                    int testValue = mask[i] & (1 << shift);
                    if (goRight && testValue==0){
                        numberOfOne = 8 - shift - 1;
                        break;
                    } else if (!goRight && testValue>0){
                        numberOfOne = 8 - shift;
                        break;
                    }
                }
                result += numberOfOne;
            }
        }
        return result;
    }
        
        



Operazioni di insiemistica su reti di IP

getNetworkBaseIp(int[] ip, int mask): int[]

Questo metodo restituisce l'IP di base della rete a cui appartiene l'IP passato in input (insieme alla maschera della rete a cui appartiene).
Ad esempio, considerando l'IP "192.168.1.54" con maschera "24", dopo l'esecuzione del codice seguente
int[] ip = new int[]{192,168,1,54};
int mask = 24;
int[] baseIp = getNetworkBaseIp(ip, mask);
il contenuto di "baseIp" sara' {192,168,1,0}, ovvero la rete di cui fa parte l'IP "192.168.1.54" e' "192.168.1.0/24"
    /**
     * Restituisce l'ip di base della rete a cui appartiene 
     * l'IP passato con la maschera passata.
     * L'ip deve essere passato gia' scomposto nei 4 numeri 0..255, 
     * la maschera e' in formato numerico intero (non in formato ip).
     * Es. getNetwork(new int[]{192,168,1,54}, 24)
     * restituisce {192,168,1,0}
     * @param ip Indirizzo ip scomposto nelle sue 4 parti
     * @param mask Maschera nel formato numero intero
     * @return L'ip base della rete (scomposto nelle sue 4 parti) 
     *     a cui appartiene l'ip passato, considerando la maschera passata
     */
    public static int[] getNetworkBaseIp(int[] ip, int mask) {
        int[] result;
        int[] maskArray = toIpMask(mask);
        result = andIp(ip, maskArray);
        return result;
    }
        


getNetworkStatistics(int[] ip, int mask): NetworkStatisticsBean

Questo metodo restituisce i dati relativi alla rete di cui fa parte l'IP passato. La rete viene ricavata dall'applicazione della maschera.
Le informazioni restituite comprendono:
  • Indirizzo base della rete (applicazione della maschera all'IP passato)
  • Indirizzo di broadcast della rete (inverto la maschera e sommo il risultato all'IP base)
  • Maschera della rete
  • Numero di IP compresi nella rete (incluso IP base e IP di broadcast, ottenuto come numero di combinazioni possibili ottenibili dai componenti dell'inversione della maschera)
Oltre al codice del metodo, e' presente anche il codice del "bean" contenente il risultato.
Ad esempio, considerando l'IP "192.168.1.54" con maschera "24", dopo l'esecuzione del codice seguente
int[] ip = new int[]{192,168,1,54};
int mask = 24;
NetworkStatisticsBean statistics = getNetworkStatistics(ip, mask);
il contenuto di "statistics" sara'
  • IP Base: {192,168,1,0}
  • IP broadcast: {192,168,1,255}
  • Maschera: 24
  • Numero di IP: 256
(gli IP utili saranno 254, tolti quello di base e quello di broadcast).

    /**
     * Restituisce le informazioni relative alla rete di cui fa 
     * parte l'ip passato con la rete identificata dalla maschera 
     * indicata
     * @param ip ip che fa parte della rete
     * @param networkMask maschera di rete
     * @return Bean contenente IP di base , IP di broadcast, maschera, 
     *     numero di IP compresi nella rete
     */
    public static NetworkStatisticsBean getNetworkStatistics(int[] ip, int networkMask){
        NetworkStatisticsBean result = new NetworkStatisticsBean();
        if (networkMask>0){
            int[] networkIp = getNetworkBaseIp(ip, networkMask);
            //calcolo la maschera che "aggiunta" all'IP di base 
            //della rete mi determina l'ampiezza della rete stessa
            int[] networkRangeMask = notIp(toIpMask(networkMask));
            //L'IP di broadcast e' l'ultimo della rete e lo ottengo aggiungendo 
            //l'inversione della maschera all'IP base
            result.setBroadcastIp(addIp(networkIp, networkRangeMask));
            result.setNetworkIp(networkIp);
            result.setMask(networkMask);
            //Il numero di IP possibili si calcola come 
            //numero di combinazioni al variare delle singole parti 
            //della inversione della maschera
            if (networkMask==32){
                result.setCount(1);
            } else {
                int count = 0;
                for (int i=0; i<4; i++){
                    int value = networkRangeMask[i];
                    if (value>0){
                        if (count==0) count += ++value;
                        else count *= ++value;
                    }
                }
                result.setCount(count);
            }
        }
        return result;
    }
        

Ed ecco l'implementazione semplice del bean contenente i dati:
    public class NetworkStatisticsBean {
        private int[] networkIp = new int[]{0,0,0,0};
        private int[] broadcastIp = new int[]{0,0,0,0};
        private int mask = 0;
        private int count = 0;
        
        public NetworkStatisticsBean(){
        }

        public int[] getNetworkIp() {
            return networkIp;
        }

        public void setNetworkIp(int[] networkIp) {
            this.networkIp = networkIp;
        }

        public int[] getBroadcastIp() {
            return broadcastIp;
        }

        public void setBroadcastIp(int[] broadcastIp) {
            this.broadcastIp = broadcastIp;
        }

        public int getMask() {
            return mask;
        }

        public void setMask(int mask) {
            this.mask = mask;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }
    }
        


isIpInsideNetwork(String ip, String network): boolean
isIpInsideNetwork(int[] ip, int[] networkBaseIp, int networkMask): boolean

Dati un IP qualsiasi ed una rete specificata come IP di base e maschera, verifica che l'IP sia all'interno della rete oppure no.
Ad esempio, considerando l'IP "192.168.1.54" e la rete "192.168.1.0/24", dopo l'esecuzione del codice seguente
String ip = "192.168.1.54";
String network = "192.168.1.0/24";
boolean result = isIpInsideNetwork(ip, network);
il risultato sara' "true" in quanto l'IP e' contenuto nella rete.
Usando, invece, l'IP "192.168.5.189" e la rete "192.168.1.0/24", il risultato sara' "false" (IP esterno alla rete).
Per comodita', vengono forniti due metodi, uno per gli indirizzi in formato testo, uno per gli indirizzi scomposti nelle relative parti atomiche (singoli byte).
    /**
     * Determina se l'IP passato e' contenuto all'interno 
     * dell'intera rete passata (formato "ipBase/intMask")
     * es. "192.168.1.0/24".
     * Chiama "isIpInsideNetwork(int[], int[], int):boolean".
     * @param ip ip da verificare
     * @param network rete intera su cui verificare la presenza dell'ip passato (formato "ip/int")
     * @return true se l'ip e' all'interno della rete, false altrimenti
     */
    public static boolean isIpInsideNetwork(String ip, String network) {
        boolean result;
        //Divido in ipBase (indice 0) e maschera (indice 1)
        String[] ipComponents = network.split("/");
        int mask = Integer.parseInt(ipComponents[1]);
        result = isIpInsideNetwork(getIpArray(ip), getIpArray(ipComponents[0]), mask);
        return result;
    }
    /**
     * Determina se l'IP passato e' contenuto all'interno 
     * dell'intera rete passata.
     * @param ip componenti ip da verificare
     * @param networkBaseIp componenti ip base della rete 
     * @param networkMask maschera della rete 
     * @return true se l'ip e' all'interno della rete, false altrimenti
     */
    public static boolean isIpInsideNetwork(int[] ip, int[] networkBaseIp, int networkMask) {
        boolean result;
        //Se la base della rete passata e' identica a quella ottenuta 
        //applicando la maschera della rete all'IP, 
        //allora l'IP e' nella rete
        result = equalsIp(networkBaseIp, getNetworkBaseIp(ip, networkMask));
        return result;
    }
        


isNetworkInsideNetwork(int[] networkIp1, int mask1, int[] networkIp2, int mask2): boolean

Date due reti (rappresentate ognuna dall'IP base e dalla maschera), verifica che la prima rete sia inclusa nella seconda, ovvero che tutti gli IP della prima rete siano anche IP della seconda.
Ad esempio, considerando le reti "192.168.0.0/15" e "192.168.0.0/14", l'esecuzione del codice seguente
int[] networkIp1 = new int[]{192.168.0.0};
int mask1 = 15;
int[] networkIp2 = new int[]{192.168.0.0};
int mask2 = 14;
boolean result = isNetworkInsideNetwork(networkIp1, mask1, networkIp2, mask2);
porta al risultato "true", cioe' la prima rete e' inclusa nella seconda.
    /**
     * Determina se la prima rete passata e' contenuta all'interno 
     * della seconda.
     * @param networkIp1 ip base della prima rete
     * @param mask1 maschera della prima rete
     * @param networkIp2 ip base della seconda rete
     * @param mask2 maschera della seconda rete
     * @return true se la prima rete e' inclusa o coincide con la seconda, false altrimenti
     */
    public static boolean isNetworkInsideNetwork(int[] networkIp1, int mask1, int[]networkIp2, int mask2){
        int[] maskIp1 = toIpMask(mask1);
        int[] broadcastIp1 = addIp(notIp(maskIp1), networkIp1);
        int[] maskIp2 = toIpMask(mask2);
        int[] broadcastIp2 = addIp(notIp(maskIp2), networkIp2);
        //L'ip base della prima rete deve essere maggiore o 
        //uguale a quello della seconda, l'ultimo ip della seconda rete 
        //dev'essere maggiore o uguale all'ultimo della prima
        return 
            !lessThanIp(networkIp1, networkIp2) && 
            !lessThanIp(broadcastIp2, broadcastIp1);
    }
        

Hai trovato utile questo articolo?
Aiutami a condividerlo o metti un "mi piace".
Grazie mille!


Gli strumenti di condivisione (Google+, Facebook) sono visibili in alto a destra solo dopo aver accettato la policy di utilizzo dei cookie per questo sito.
FAQ - Come faccio a cambiare la mia scelta?

 

Strumenti (myjsp.feelinglinux.com)
Gioco: allenamento con la tastiera Strumenti di codifica/decodifica URI (%-encoding) e Base64 Strumenti di calcolo online per IP e Reti
QUIZ GAME
Quiz game

Cerca @myjsp.feelinglinux.com

Pubblicita'