Convertisseur d'adresse IP
Une adresse IPv4 peut s'écrire de plusieurs manières. Saisissez une adresse au format classique (192.168.1.1), un entier 32 bits ou une valeur hexadécimale : l'outil affiche instantanément toutes les représentations. Le guide qui suit explique le calcul, les usages réels de ces conversions et les pièges qui font échouer un contrôle de sécurité.
L'essentiel
- Une adresse IPv4 tient sur 32 bits : c'est un entier compris entre 0 et 4 294 967 295.
203.0.113.42et3405803818désignent exactement la même destination. - Quatre écritures coexistent (décimale pointée, entier, hexadécimal, binaire) et aucune n'est plus « vraie » que les autres.
- Stocker une adresse en entier permet des comparaisons de plages exactes. En texte,
203.0.113.42se trie avant203.0.113.5, ce qui fausse tout classement. http://3221226219/est une adresse IP déguisée (192.0.2.235) : savoir la décoder évite d'ouvrir un lien d'hameçonnage.::ffff:203.0.113.42n'est pas une adresse IPv6 « étrange » : c'est une adresse IPv4 vue par un serveur en double pile.- Les zéros de tête sont le piège numéro un :
010.0.0.1vaut8.0.0.1pour certaines bibliothèques, et une erreur pour d'autres.
Une adresse IPv4 est un nombre
Le RFC 791, qui définit le protocole IP en 1981, ne parle pas de points. Il décrit un champ d'adresse de 32 bits, ni plus ni moins. La notation « décimale pointée » que tout le monde connaît est une convention d'écriture ajoutée pour le confort des humains : on découpe ces 32 bits en quatre tranches de 8 bits (quatre octets, chacun compris entre 0 et 255) et on les sépare par des points.
Retrouver le nombre derrière l'écriture est une simple addition pondérée. Chaque octet a un poids fixe, exactement comme les chiffres d'un nombre décimal ont un poids de 1, 10, 100 :
Adresse : 203.0.113.42 203 × 16 777 216 = 3 405 774 848 ← 203 × 2^24 (premier octet) 0 × 65 536 = 0 ← 0 × 2^16 (deuxième octet) 113 × 256 = 28 928 ← 113 × 2^8 (troisième octet) 42 × 1 = 42 ← 42 × 2^0 (quatrième octet) ---------------------------------- Entier 32 bits = 3 405 803 818
L'adresse 203.0.113.42 est donc le nombre 3405803818. Ces deux chaînes désignent rigoureusement la même destination : la seconde n'est ni un code, ni un identifiant dérivé, c'est la valeur elle-même. Le préfixe 203.0.113.0/24 utilisé ici n'appartient à personne : il est réservé par le RFC 5737 à la documentation, au même titre que 192.0.2.0/24, ce que détaille notre page sur les adresses IP réservées.
L'opération inverse consiste à extraire chaque octet par décalage de bits, ce que fait tout équipement réseau des milliards de fois par jour :
n = 3405803818
(n >> 24) & 255 → 203 # on décale de 24 bits, on garde les 8 derniers
(n >> 16) & 255 → 0
(n >> 8) & 255 → 113
n & 255 → 42
Puisque 32 bits offrent 232 combinaisons, il existe 4 294 967 296 adresses IPv4 : de 0.0.0.0 à 255.255.255.255. Une part notable est réservée et jamais routée sur Internet, si bien que le nombre réellement attribuable est sensiblement inférieur à ces 4,3 milliards. C'est cette limite arithmétique, et non une décision commerciale, qui a rendu IPv6 nécessaire.
Les quatre écritures d'une même adresse
Le convertisseur ci-dessus affiche quatre lignes pour une seule adresse. Voici ce que chacune représente, sur l'exemple 203.0.113.42 :
| Écriture | Valeur |
|---|---|
| Décimale pointée | 203.0.113.42 |
| Entier 32 bits | 3405803818 |
| Hexadécimal | 0xCB00712A |
| Binaire | 11001011.00000000.01110001.00101010 |
La correspondance est plus lisible qu'il n'y paraît. En hexadécimal, deux chiffres valent exactement un octet : CB = 203, 00 = 0, 71 = 113, 2A = 42. Une adresse IPv4 s'écrit donc toujours avec huit chiffres hexadécimaux, jamais plus. En binaire, chaque groupe de huit bits correspond au même octet ; c'est la seule écriture qui montre où passe la frontière entre partie réseau et partie hôte, sujet traité en détail dans notre table des masques de sous-réseau.
Deux variantes plus rares circulent également. L'octal pointé écrit chaque octet en base 8 avec un zéro devant : 0313.0.0161.052. L'hexadécimal pointé mélange les bases : 0xCB.0x00.0x71.0x2A. Ces formes ne servent presque jamais à des fins légitimes ; elles sont surtout utilisées pour brouiller une adresse, comme expliqué plus bas.
Repère : un octet à 255 s'écrit FF en hexadécimal et 11111111 en binaire. Un masque 255.255.255.0 devient donc 0xFFFFFF00, une forme que l'on croise encore dans certaines configurations d'équipements et de systèmes anciens.
Pourquoi ces conversions servent vraiment
Convertir une adresse n'est pas un exercice de style. Quatre situations concrètes l'imposent régulièrement.
Stocker une adresse en entier dans une base de données
Une adresse IPv4 stockée en VARCHAR(15) occupe jusqu'à seize octets et se compare caractère par caractère. En INT UNSIGNED, elle occupe quatre octets et se compare en une seule instruction processeur. Surtout, le classement redevient juste : en texte, 203.0.113.42 se place avant 203.0.113.5, parce que le caractère « 4 » précède le caractère « 5 ». En entier, l'ordre est celui du réseau.
L'intérêt devient décisif dès qu'il faut savoir à quelle plage appartient une adresse : géolocalisation, table d'opérateurs, liste de blocage :
-- Chaque plage est décrite par deux entiers non signés (4 octets chacun) CREATE TABLE plages_geo ( ip_debut INT UNSIGNED NOT NULL, ip_fin INT UNSIGNED NOT NULL, pays CHAR(2) NOT NULL, PRIMARY KEY (ip_debut) ); -- Retrouver la plage contenant 203.0.113.42 SELECT pays FROM plages_geo WHERE INET_ATON('203.0.113.42') BETWEEN ip_debut AND ip_fin LIMIT 1;
Une précision que les tutoriels omettent souvent : un BETWEEN portant sur deux colonnes différentes n'exploite l'index que sur la première. Sur une table de plusieurs centaines de milliers de plages, la formulation WHERE ip_debut <= INET_ATON(...) ORDER BY ip_debut DESC LIMIT 1, suivie d'une vérification de ip_fin côté application, est généralement plus rapide. Pour IPv6, l'entier ne suffit plus : on stocke les 16 octets bruts dans un VARBINARY(16) via INET6_ATON(), ou l'on utilise le type inet natif de PostgreSQL. Rappelons au passage qu'une adresse IP est une donnée à caractère personnel : sa conservation obéit au RGPD, durée de rétention comprise.
Lire un journal ou une configuration exprimée en entier
Certains fichiers système n'affichent jamais d'adresse lisible. Sous Linux, /proc/net/tcp liste les connexions en hexadécimal :
sl local_address rem_address st ... 0: 0100007F:0CEA 00000000:0000 0A ...
0100007F se lit par mots de 32 bits en ordre inverse (petit-boutiste) : les octets 01 00 00 7F relus à l'envers donnent 7F 00 00 01, soit 127.0.0.1. Le port 0CEA vaut 3306. On retrouve la même logique dans les tables de routage brutes, dans certains journaux applicatifs qui enregistrent l'entier renvoyé par la couche réseau, et dans les exports d'équipements qui écrivent les masques en hexadécimal. Un journal d'accès web, lui, associe habituellement l'adresse au code HTTP renvoyé ; savoir passer d'une écriture à l'autre permet de recouper deux sources qui ne parlent pas le même dialecte.
Reconnaître une adresse déguisée dans un lien
Un message d'hameçonnage cherche à masquer sa destination. Écrire http://3221226219/ plutôt que http://192.0.2.235/ suffit à décourager la vérification : la cible n'est plus reconnaissable comme une adresse IP. Les variantes hexadécimale (http://0xC00002EB/) et octale (http://0300.0.2.0353/) reposent sur le même principe.
Ces formes ne sont pas théoriques. Nous avons vérifié qu'un client HTTP courant résout aussi bien http://2130706433/ que http://0x7f000001/ ou http://0177.0.0.1/ vers 127.0.0.1. L'angle utile est purement défensif : un lien dont l'hôte est une suite de chiffres, commence par 0x ou contient un octet à zéro de tête doit être traité comme suspect. Un service légitime s'annonce par un nom de domaine.
Attention : ne cliquez pas sur le lien pour « voir où il mène ». Recopiez uniquement le nombre dans le convertisseur ci-dessus, puis interrogez l'adresse obtenue avec notre recherche d'IP pour savoir à quel opérateur ou hébergeur elle appartient. La même prudence vaut pour les liens et les en-têtes reçus par courriel, sujet développé dans notre guide adresse IP et courrier électronique.
Lire une règle de pare-feu
Les règles de filtrage raisonnent en préfixes, pas en adresses isolées. Une entrée du type permit 203.0.113.0 0.0.0.255 utilise un masque générique : les bits à 0 doivent correspondre, les bits à 1 sont ignorés. C'est l'inverse exact du masque de sous-réseau 255.255.255.0. Passer les deux valeurs en binaire lève instantanément l'ambiguïté : 11111111.11111111.11111111.00000000 face à 00000000.00000000.00000000.11111111. Pour déterminer la plage réellement couverte par une règle, notre calculateur de sous-réseau fait le calcul complet, et la distinction entre adresses publiques et privées évite d'ouvrir vers Internet une règle prévue pour le réseau local.
IPv4 mappée en IPv6
Un jour ou l'autre, un journal de serveur affiche ::ffff:203.0.113.42 à la place d'une adresse IPv4 familière. Ce n'est ni une erreur ni une adresse IPv6 exotique : c'est la représentation IPv4 mappée définie par le RFC 4291, qui occupe le préfixe ::ffff:0:0/96.
Sa structure est mécanique. Sur les 16 octets d'une adresse IPv6, les 10 premiers sont nuls, les 2 suivants valent ff ff, et les 4 derniers contiennent l'adresse IPv4 :
::ffff:203.0.113.42 → 16 octets : 00 00 00 00 00 00 00 00 00 00 ff ff cb 00 71 2a |------------ 80 bits à 0 -----| ffff | 203.0.113.42 |
On la rencontre systématiquement dans le même contexte : un serveur en double pile qui écoute sur une socket IPv6 sans avoir activé l'option IPv6 uniquement. Le système d'exploitation accepte alors aussi les connexions IPv4 et les présente à l'application sous forme mappée. En PHP, $_SERVER['REMOTE_ADDR'] vaut alors ::ffff:203.0.113.42 pour un visiteur pourtant venu en IPv4 ; les bibliothèques réseau de Java et de plusieurs autres environnements se comportent de la même manière. Une même machine peut donc apparaître sous deux formes dans vos journaux selon le chemin emprunté, ce qui fausse les décomptes de visiteurs uniques et les listes d'autorisation.
La conséquence pratique est concrète : filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) rejette la forme mappée, qui est techniquement une adresse IPv6. Si votre application compare l'adresse du visiteur à une liste d'adresses IPv4, normalisez d'abord : retirez le préfixe ::ffff: lorsque le reste est une adresse IPv4 valide, ou comparez les 16 octets renvoyés par inet_pton(). Deux précisions pour finir. D'une part, l'ancienne forme « IPv4 compatible » (::203.0.113.42, sans le ffff) est officiellement abandonnée : ne l'implémentez pas. D'autre part, une adresse mappée est une convention d'interface de programmation, pas un format de paquet : elle ne circule pas telle quelle sur le réseau. Si votre logement est déjà en IPv6 à la maison, vos connexions natives apparaîtront sous une vraie adresse IPv6, sans ffff.
La notation compacte IPv6
Une adresse IPv6 fait 128 bits, écrits en huit groupes de quatre chiffres hexadécimaux séparés par des deux-points. Sous forme complète, cela donne 2001:0db8:0000:0000:0000:ff00:0042:8329, long et pénible. Deux règles de simplification s'appliquent, et c'est ici que beaucoup de lecteurs se trompent.
Règle 1 : supprimer les zéros de tête de chaque groupe. 0db8 devient db8, 0042 devient 42, 0000 devient 0. Attention : seuls les zéros en tête disparaissent. ff00 reste ff00, jamais ff.
Règle 2 : remplacer une seule séquence de groupes nuls par ::. Le double deux-points représente « autant de groupes à zéro qu'il en faut pour arriver à huit ». Le décodeur compte les groupes présents et complète. Cette reconstruction n'est possible que s'il n'y a qu'un seul :: dans l'adresse : avec deux, il serait impossible de savoir combien de zéros vont d'un côté et combien de l'autre.
Le RFC 5952 ajoute trois recommandations pour obtenir une écriture canonique, celle que produisent les outils et que devraient utiliser vos journaux : compresser la plus longue séquence de zéros ; en cas d'égalité, compresser la première ; ne jamais utiliser :: pour un unique groupe nul. Les lettres s'écrivent en minuscules.
| Adresse complète | Écriture correcte | Écriture à éviter |
|---|---|---|
2001:0db8:0000:0000:0000:ff00:0042:8329 |
2001:db8::ff00:42:8329 |
2001:db8:0:0:0:ff00:42:8329, séquence non compressée |
2001:0db8:0000:0001:0000:0000:0000:0001 |
2001:db8:0:1::1 |
2001:db8::1:0:0:0:1, la plus longue séquence n'est pas celle compressée |
2001:0db8:0000:0000:0001:0000:0000:0001 |
2001:db8::1:0:0:1 |
2001:db8:0:0:1::1, en cas d'égalité, on compresse la première séquence |
2001:0db8:0000:0001:0002:0003:0004:0005 |
2001:db8:0:1:2:3:4:5 |
2001:db8::1:2:3:4:5, :: pour un seul groupe nul |
0000:0000:0000:0000:0000:0000:0000:0001 |
::1 (bouclage) |
0:0:0:0:0:0:0:1, inutilement verbeux |
| : | : | 2001:db8::1::1, invalide : deux ::, adresse ambiguë |
Deux cas particuliers méritent d'être connus. :: seul est l'adresse non spécifiée, l'équivalent IPv6 de 0.0.0.0 : elle signifie « n'importe quelle interface » dans une configuration d'écoute. Et une adresse de lien local s'accompagne souvent d'un identifiant de zone, comme fe80::1%eth0 : le suffixe après le pourcentage désigne l'interface, il ne fait pas partie de l'adresse. Enfin, la forme mappée vue plus haut peut légitimement s'écrire de deux façons : la plupart des outils affichent ::ffff:203.0.113.42, mais certaines bibliothèques préfèrent la forme entièrement hexadécimale ::ffff:cb00:712a. Les deux désignent la même adresse ; toute comparaison de chaînes entre les deux échouera. Le vocabulaire complet est repris dans notre glossaire réseau.
Faire la conversion soi-même
Le convertisseur ci-dessus dépanne, mais un script est souvent plus pratique. Voici les équivalents exacts dans quatre environnements courants.
PHP : les deux fonctions historiques restent les plus directes :
ip2long('203.0.113.42'); // 3405803818
long2ip(3405803818); // '203.0.113.42'
sprintf('%u', ip2long($ip)); // force l'affichage non signé
// IPv6 (et IPv4) : 16 ou 4 octets bruts, dans les deux sens
bin2hex(inet_pton('203.0.113.42')); // 'cb00712a'
inet_ntop(inet_pton('2001:0db8::0001')); // '2001:db8::1' (forme canonique)
Python : le module ipaddress de la bibliothèque standard couvre IPv4 et IPv6 avec la même interface :
import ipaddress
a = ipaddress.ip_address('203.0.113.42')
int(a) # 3405803818
a.packed.hex() # 'cb00712a'
format(a, 'b') # '11001011000000000111000100101010'
ipaddress.ip_address(3405803818) # IPv4Address('203.0.113.42')
Ligne de commande : l'arithmétique du shell suffit, sans installer quoi que ce soit :
# décimal pointé → entier IFS=. read -r a b c d <<< "203.0.113.42" echo $(( (a<<24) + (b<<16) + (c<<8) + d )) # 3405803818 # entier → décimal pointé, puis hexadécimal n=3405803818 printf '%d.%d.%d.%d\n' $((n>>24&255)) $((n>>16&255)) $((n>>8&255)) $((n&255)) printf '%08X\n' $n # CB00712A
PowerShell : la classe IPAddress de .NET fait le travail, à condition d'inverser l'ordre des octets :
$b = [System.Net.IPAddress]::Parse('203.0.113.42').GetAddressBytes()
[Array]::Reverse($b)
[System.BitConverter]::ToUInt32($b, 0) # 3405803818
$c = [System.BitConverter]::GetBytes([uint32]3405803818)
[Array]::Reverse($c)
([System.Net.IPAddress]::new([byte[]]$c)).IPAddressToString # 203.0.113.42
Piège PowerShell : la propriété .Address semble faire la même chose, mais elle renvoie les octets lus dans l'ordre inverse : pour 203.0.113.42, elle donne 712048843 au lieu de 3405803818. Elle est d'ailleurs marquée comme obsolète. Passez toujours par GetAddressBytes().
Les pièges classiques
Les erreurs de conversion ne sont pas de simples coquilles : elles ont servi de base à plusieurs contournements de contrôles d'accès. Quatre méritent votre attention.
Les zéros de tête et l'octal
La vieille fonction C inet_aton() accepte des octets en décimal, mais aussi en octal lorsqu'ils commencent par un zéro, et en hexadécimal lorsqu'ils commencent par 0x. 010.0.0.1 y devient donc 8.0.0.1. Le problème vient de l'hétérogénéité : certaines bibliothèques appliquent cette règle, d'autres refusent la saisie, d'autres encore ignoraient simplement le zéro et lisaient 010 comme 10. Le module ipaddress de Python a précisément été corrigé pour cela en 2021 (CVE-2021-29921). Une même chaîne pouvait donc désigner deux adresses différentes selon le composant qui la lisait : le validateur autorisait 010.0.0.1 en croyant voir 10.0.0.1, tandis que le client HTTP contactait 8.0.0.1.
Les adresses écrites avec moins de quatre parties
inet_aton() accepte aussi les formes abrégées : avec trois parties, la dernière couvre 16 bits ; avec deux parties, la dernière couvre 24 bits ; avec une seule, elle couvre les 32 bits. D'où les surprises classiques : 127.1 vaut 127.0.0.1, 8.8 vaut 8.0.0.8 (et non 8.8.8.8), et 2130706433 vaut également 127.0.0.1. Là encore, un contrôle de sécurité qui exige quatre nombres séparés par des points laissera passer 127.1 sans le reconnaître comme une adresse de bouclage.
Le dépassement de l'entier signé
Un entier 32 bits signé s'arrête à 2 147 483 647, ce qui correspond exactement à 127.255.255.255. Toute adresse à partir de 128.0.0.0 dépasse cette borne. Les symptômes sont reconnaissables : une colonne INT signée refuse la valeur ou la tronque là où INT UNSIGNED l'accepte ; ip2long() renvoie un nombre négatif sur une plateforme 32 bits, à afficher avec sprintf('%u', $n). En JavaScript, les opérateurs de bits travaillent sur 32 bits signés : 3405803818 | 0 vaut -889163478. C'est la raison pour laquelle le convertisseur de cette page utilise des multiplications et des divisions plutôt que des décalages ; l'autre correctif consiste à terminer le calcul par >>> 0, qui réinterprète le résultat en non signé.
Les différences de comportement entre langages
Le tableau suivant résume les comportements documentés de chaque implémentation. Il illustre le principe à retenir : ne présumez jamais que deux composants de votre chaîne logicielle interprètent une adresse de la même façon.
| Chaîne saisie | PHP filter_var |
Résolveur système (glibc) | Python ipaddress |
Python socket.inet_aton |
.NET (Windows PowerShell 5.1) |
|---|---|---|---|---|---|
010.0.0.1 |
refusé | 8.0.0.1 |
erreur (zéros de tête) | 8.0.0.1 |
8.0.0.1 |
127.1 |
refusé | 127.0.0.1 |
erreur (4 octets attendus) | 127.0.0.1 |
127.0.0.1 |
8.8 |
refusé | 8.0.0.8 |
erreur (4 octets attendus) | 8.0.0.8 |
8.0.0.8 |
2130706433 |
refusé | 127.0.0.1 |
erreur (4 octets attendus) | 127.0.0.1 (entier accepté) |
127.0.0.1 |
La conclusion opérationnelle tient en une phrase : validez avec la fonction la plus stricte disponible (filter_var($ip, FILTER_VALIDATE_IP) en PHP, ipaddress en Python) puis réécrivez l'adresse sous sa forme canonique et n'utilisez plus que celle-là dans la suite du traitement. N'écrivez jamais votre propre expression régulière pour valider une adresse : elle acceptera des formes que le reste du système interprétera différemment.
Questions fréquentes
Pourquoi une adresse IPv4 s'écrit-elle parfois ::ffff:203.0.113.42 ?
Parce que le serveur écoute sur une socket IPv6 acceptant aussi les connexions IPv4. Le noyau lui présente alors chaque client IPv4 sous la forme dite « IPv4 mappée », définie par le RFC 4291 : 96 bits d'en-tête (::ffff:) suivis des 32 bits de l'adresse IPv4. C'est bien la même machine, vue à travers une interface de programmation IPv6.
Faut-il stocker les adresses IP en entier ou en texte dans une base de données ?
En entier si vous devez comparer des plages ou trier : un INT UNSIGNED occupe 4 octets, se compare en une instruction et se prête aux requêtes BETWEEN. En texte, 203.0.113.42 se classe avant 203.0.113.5, ce qui rend tout tri faux. Pour IPv6, préférez un VARBINARY(16) alimenté par INET6_ATON(), ou le type inet natif de PostgreSQL.
Pourquoi ip2long() renvoie-t-il parfois un nombre négatif ?
Sur une plateforme où l'entier PHP tient sur 32 bits signés, tout ce qui dépasse 2 147 483 647 (c'est-à-dire toute adresse à partir de 128.0.0.0) repasse en négatif. La solution n'est pas de corriger le nombre à la main mais de l'afficher avec sprintf('%u', $n). Sur les installations 64 bits actuelles, y compris celles des hébergeurs mutualisés français, ip2long() renvoie directement une valeur positive.
Une URL comme http://3221226219/ peut-elle vraiment fonctionner ?
Oui. Ce nombre est l'écriture entière de 192.0.2.235, et la plupart des clients HTTP acceptent encore cette forme, tout comme 0x en hexadécimal ou les octets en octal. C'est pour cela qu'on la rencontre dans des messages d'hameçonnage : elle masque la destination réelle. Décodez le nombre ici plutôt que d'ouvrir le lien, puis vérifiez le propriétaire de l'adresse avec notre recherche d'IP.
Peut-on convertir une adresse IPv6 en un seul entier ?
Oui, mais il faut 128 bits : 2001:db8::1 vaut 42 540 766 411 282 592 856 903 984 951 653 826 561. Aucun entier natif ne contient une telle valeur, d'où le recours à une bibliothèque de grands nombres (GMP ou BCMath en PHP, entiers illimités en Python). En pratique, on manipule plutôt les 16 octets bruts renvoyés par inet_pton().
Pourquoi 010.0.0.1 ne vaut-il pas 10.0.0.1 ?
Parce que plusieurs bibliothèques héritées de la fonction C inet_aton() lisent un octet commençant par zéro comme un nombre octal : 010 vaut alors 8, et l'adresse devient 8.0.0.1. D'autres implémentations refusent la saisie, d'autres encore ignorent le zéro. Ne saisissez jamais de zéro de tête et rejetez-les dans vos propres validations.