Перейти к основному содержимому

ip to int и int to ip на пальцах

· 5 мин. чтения

UPD 2023.12.15 👋

Заканчивается 2023 год, а статья до сих пор хорошая. Эта тема до сих пор находит отголоски в моих текущих задачах. Именно изучение того, как конвертируется ip толкнуло меня в понимании побитовых операций, а также сетевых масок.


Мне потребовалась функция, определяющая, находится ли IP под указанной маской и я захотел в деталях понять, как она работает. Для этого надо было понять, как IP конвертируются в целые числа и наоборот

Преобразования IP в число и обратно это побитовые операции, но я постараюсь объяснить все так, чтобы понятно было тем, кто с ними не знаком или плохо знаком. Подробнее о них можно почитать, например, здесь.

Представление IP

Как вам известно, IP состоит из 4 блоков, где число в блоке <= 255. Почему? В старые времена компьютеры поддерживали числа только до 32 бит. В 32 битах максимальное число 2^32-1 = 4294967295. В шестнадцатиричной системе (HEX) оно выглядит как FFFFFFFF или 32 ноля в двоичной (BIN).

А теперь разделим FFFFFFFF на 4 кусочка (блока): FF FF FF FF FF HEX = 255 DEC. Отсюда и получается максимально большой ИП: 255.255.255.255.

IP это HEX или BIN кусочки, разделенные точкой

Вид 255.255.255.255 в разных представлениях:

DEC 255 255 255 255 BIN 11111111 11111111 11111111 11111111 HEX FF FF FF FF

Ноли в начале чисел ничего не значат. Тоесть, 000001 == 1, поэтому если в тексте ниже сначала чисел будут ноли, то они добавлены "для красоты"

Еще пример. Возьмем любой другой ИП, например 37.230.210.51. Разделим его на кусочки: 37 230 210 51. Конвертируем каждый из этих кусочков в HEX и BIN:

DEC 037 230 210 051 BIN 00100101 11100110 11010010 00110011 HEX 25 E6 D2 33

Конвертер десятичных числел в двоичные и HEX здесь

Склеиваем, получается HEX 25E6D233 или BIN 00100101 11100110 11010010 00110011. Теперь можете конвертировать это в DEC и получите IP в виде числа, которым его удобно хранить в БД

Функция конвертации IP в число

local function ipToInt(ip) -- ip = 37.230.210.51
local int = 0

-- 31 230 210 51
local p1, p2, p3, p4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")

-- BIN | HEX
int = int + bit.lshift(p1,24) -- 00100101 00000000 00000000 00000000 | 25 00 00 00
int = int + bit.lshift(p2,16) -- 00100101 11100110 00000000 00000000 | 25 E6 00 00
int = int + bit.lshift(p3,8) -- 00100101 11100110 11010010 00000000 | 25 E6 D2 00
int = int + p4 -- 00100101 11100110 11010010 00110011 | 25 E6 D2 33

return int -- 635884083
end

Функция bit.lshift(a, b) смещает число a на b бит влево (Добавляя ноли справа). Тоесть 11010111 после смещения на 3 бита влево превратится в 11010111000. После этого смещение на 3 бита вправо вернет число в прежнее состояние, убрав ноли справа

Что происходит в функции? Она помещает каждый кусочек IP на свое место в битовом представлении и на выхлопе получается нужное число. Функция делает то же самое, что было описано ранее, только за счет математики

Конвертация числа в IP

Итак, у нас есть IP в виде DEC числа и нам нужно сделать из него глазу понятный IP. Алгоритм человеческим языком:

  1. Представляем DEC в виде BIN
  2. Вырываем с этого BIN 4 кусочка
  3. Разделяем их точкой

Lua функция:

-- Маски из примера ниже
-- FF 00 00 00 == 11111111 00000000 00000000 00000000
-- 00 FF 00 00 == 00000000 11111111 00000000 00000000
-- 00 00 FF 00 == 00000000 00000000 11111111 00000000
-- 00 00 00 FF == 00000000 00000000 00000000 11111111

local function intToIp(int) -- 635884083 (00100101 11100110 11010010 00110011)
local a = bit.band(int, 0xFF000000) -- 00100101 00000000 00000000 00000000 | 25 00 00 00 | 037.000.000.000
local b = bit.band(int, 0x00FF0000) -- 00000000 11100110 00000000 00000000 | 00 E6 00 00 | 000.230.000.000
local c = bit.band(int, 0x0000FF00) -- 00000000 00000000 11010010 00000000 | 00 00 D2 00 | 000.000.210.000
local d = bit.band(int, 0x000000FF) -- 00000000 00000000 00000000 00110011 | 00 00 00 33 | 000.000.000.051

a = bit.rshift(a, 24) -- помещаем в самое начало
b = bit.rshift(b, 16) -- на второе место
c = bit.rshift(c, 8) -- на третье
-- четвертое уже на своем

local ip = a .. "." .. b .. "." .. c .. "." .. d
return ip -- 37.230.210.51
end

print( intToIp(635884083) )

Функция bit.band(a, b) заменяет побитовый оператор AND (и). Он возвращает число, в котором только те биты, которые установлены и в a, и в b. Тоесть bit.band(0x00FF, 0xFF00) вернет 0xFFFF. Это эквивалентно bit.band(0000000011111111, 1111111100000000) == 1111111111111111.

rshift это противоположность lshift, о котором я писал выше

Бонус

Я начал изучение этого вопроса с функции, говорящей, входит ли IP в маску, поэтому делюсь и ею: lua-function-mask-has-ip

Ссылки