WedX - журнал о программировании и компьютерных науках

Какой самый эффективный способ простого сравнения IP-адресов?

Я работаю в node.js и хочу сделать что-то вроде следующего псевдокода...

let ip_range = [50.1.100.1, 51.1.30.1]; // Converted from a CIDR string.

let ip_address = 50.2.200.2; // Input IP address.

if(ip_address >= ip_range[0] && ip_address <= ip_range[1])
    block(ip_address);

Любые идеи о самом быстром способе сделать это?

Я уже проверил cidr-js, и он предоставляет функции для преобразования CIDR, но не для сравнения IP-адресов. Похоже, что node-ip может быть хорошим выбором.

Спасибо!

10.07.2015

  • Почему вы отказываетесь от CIDR? CIDR — это битовые маски, позволяющие вам просто сравнивать первые N битов, что является максимально эффективным. 10.07.2015
  • Определенно. Я действительно немного думал об этом. CIDR вводятся в виде строк. Это проблема для битмаскирования или я должен просто преобразовать в число? 10.07.2015
  • Предполагая, что вы имеете дело только с адресами IPv4, просто преобразуйте адреса в 32-битные целые числа и сравните. 10.07.2015
  • Вам нужно будет преобразовать строки в числа, но тогда вы сможете выполнить побитовое сравнение между CIDR и IP (я полагаю, (cidr & ip) == cidr). 10.07.2015
  • Я верю, что это работает. Прямо сейчас я просто конвертирую их в длинные и таким образом провожу сравнение. Спасибо за вашу помощь! 10.07.2015
  • Эти IP-адреса в вашем коде не являются допустимыми литералами. Вы имели в виду использовать строки? 12.07.2015

Ответы:


1

IP-адреса, какими мы их знаем, представляют собой просто строковые представления 32-битных числовых значений.

Преобразовывая строковое представление обратно в его числовое значение, становится тривиально легко проверить принадлежность к диапазону адресов, используя собственные числовые сравнения, как в следующем коде:

var atoi = function atoi(addr) {
  var parts = addr.split('.').map(function(str) {
    return parseInt(str); 
  });

  return (parts[0] ? parts[0] << 24 : 0) +
         (parts[1] ? parts[1] << 16 : 0) +
         (parts[2] ? parts[2] << 8  : 0) +
          parts[3];
};

var checkIpaddrInRange = function checkIpaddrInRange(ipaddr, start, end) {
  var num = atoi(ipaddr);
  return (num >= atoi(start)) && (num <= atoi(end));
}


checkIpaddrInRange('10.0.1.1', '10.0.0.1', '10.0.2.1'); // => true

checkIpaddrInRange('10.0.3.1', '10.0.0.1', '10.0.2.1'); // => false

См. скрипку.

Вот то же самое, полностью прокомментированное и правильно проверенное на ошибки:

/**
 * Checks if ipaddr is valid.
 * @property {string} ipaddr
 * @throws Error 
 */
var assertIsIpaddr = function assertIsIpaddr(ipaddr) {

  if('string' !== typeof ipaddr && ipaddr) {
    throw new Error('ipaddr must be a non-empty string');
  }

  var parts=ipaddr.split(/\./);

  if(parts.length !== 4){
    throw new Error('ipaddr must have four octets');
  }

  var i=0;
  parts.map(function(str){
      var val=parseInt(str),
          octet = 4 - i++;;
      if(val < 0 || val > 255){
        throw new Error('octet '+octet+' must be between 0 and 255');
      }
  });
};

/**
 * Converts an ipaddr to a 32bit integer value.
 * @property {string} addr - the ipaddr to convert
 * @returns {number}
 */
var atoi = function atoi(addr) {

  // test for validity - will throw!
  assertIsIpaddr(addr);

  // convert octets to numbers
  var parts = addr.split('.').map(function(str) {
    return parseInt(str); 
  });

  // construct result
  var result = (parts[0] ? parts[0] << 24 : 0) +   // if > 0, shift 4th octet left by 24
               (parts[1] ? parts[1] << 16 : 0) +   // if > 0, shift 3rd octet left by 16
               (parts[2] ? parts[2] << 8  : 0) +   // if > 0, shift 2nd octet left by 8
                parts[3];

  // note that if all octets are 255, result will overflow 
  // JavaScript (32bit) number to become -1, so we have to 
  // special case it. I think throwing an error here is a 
  // reasonable solution, since 255.255.255.255 is actually 
  // a broadcast addr.

  if(result < 0) {
    throw new Error('255.255.255.255 is not a legal host ipaddr');
  }

  return result;
};

/**
 * Checks ipaddr membership within a range of ipaddrs.
 * @property {string} ipaddr - ipaddr to check
 * @property {string} start - the start of the ipaddr range 
 * @property {string} end - the end of the ipaddr range
 * @returns {boolean} - true if ipaddr is between start and end (inclusive)
 */
var checkIpaddrInRange = function checkIpaddrInRange(ipaddr, start, end) {
  var num = atoi(ipaddr);
  return (num >= atoi(start)) && (num <= atoi(end));
}

// OK, test it out...

checkIpaddrInRange('10.0.1.1','10.0.0.1','10.0.2.1'); // => true

checkIpaddrInRange('10.0.3.1','10.0.0.1','10.0.2.1'); // => false
12.07.2015
  • Этот код не работает, если первая часть больше 127. 128 << 24 дает -2147483648. Чтобы заставить его работать, я заменил битовые операции на умножение const result = (parts[0] ? parts[0] * Math.pow(256, 3) : 0) + (parts[1] ? parts[1] * Math.pow(256, 2) : 0) + (parts[2] ? parts[2] * Math.pow(256, 1) : 0) + parts[3]; 24.12.2019

  • 2

    Вероятно, есть более простой способ сделать это, но вот как я могу подойти к этому.

    function pad(n) {
        return (n.length < 3) ? pad('0' + n) : n;
    }
    
    // return a number with each part of the IP padded out to 3 digits
    function convert(ip) {
        return parseInt(ip.split('.').map(function (el) {
           return pad(el);
        }).join(''), 10);
    }
    
    // check the number against the range
    function check(range, ip) {
      ip = convert(ip);
      return ip >= range[0] && ip <= range[1];
    }
    
    // convert the range
    var range = ['50.1.100.1', '51.1.30.1'].map(function (el) {
      return convert(el);
    });
    
    check(range, '51.1.20.2'); // true
    check(range, '51.1.40.2'); // false
    

    ДЕМО

    10.07.2015

    3

    Используйте ipaddr.js (npm i ipaddr.js):

    const ipaddr = require('ipaddr.js')
    
    const addr = ipaddr.parse('2001:db8:1234::1')
    
    addr.match(ipaddr.parseCIDR('2001:db8::/32')) // => true
    

    Or:

    const rangeList = {
      documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ],
      tunnelProviders: [
        [ ipaddr.parse('2001:470::'), 32 ], // he.net
        [ ipaddr.parse('2001:5c0::'), 32 ]  // freenet6
      ]
    }
    ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown') // => "tunnelProviders"
    

    Официальная документация

    04.10.2020

    4

    Может быть, короткий подход будет

    const ipAddress = '10.120.238.254';
    
    const asNumber = ipAdress.split('.')
     .map(p => parseInt(p))
     .reverse()
     .reduce((acc,val,i) => acc+(val*(256**i)),0)
    

    Затем сравните числа

    28.10.2020
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]