【问题标题】:Create collection of bitwise matches from an integer range从整数范围创建按位匹配的集合
【发布时间】:2018-09-02 16:03:16
【问题描述】:

The OVS documentation

... 以下列格式描述填充规则:

范围匹配可以表示为按位匹配的集合。为了 例如,假设目标是将 TCP 源端口 1000 与 1999 年,包括在内。 1000和1999的二进制表示为:

01111101000
11111001111

以下一系列按位匹配将匹配 1000 和 1999 以及 之间的所有值:

01111101xxx
0111111xxxx
10xxxxxxxxx
110xxxxxxxx
1110xxxxxxx
11110xxxxxx
1111100xxxx

可以写成如下匹配:

tcp,tp_src=0x03e8/0xfff8
tcp,tp_src=0x03f0/0xfff0
tcp,tp_src=0x0400/0xfe00
tcp,tp_src=0x0600/0xff00
tcp,tp_src=0x0700/0xff80
tcp,tp_src=0x0780/0xffc0
tcp,tp_src=0x07c0/0xfff0

我正在尝试根据 perl 中的最小和最大整数值确定生成这些匹配项的正确方法。我查看了模块 Bit::Vector ,但我无法弄清楚如何有效地为此目的使用它。

【问题讨论】:

    标签: perl binary openvswitch


    【解决方案1】:

    假设我们试图解决十进制的等效问题。

    假设您想要 567(含)到 1203(不含)。

    1. 放大阶段
      1. 您递增 1,直到您获得 10 的倍数,否则您将超出范围。
        1. ⇒598(创建 597-597)
        2. ⇒599(创建 598-598)
        3. ⇒600(创建 599-599)
      2. 增加 10 直到 100 的倍数,否则会超出范围。
      3. 增加 100 直到获得 1000 的倍数,否则会超出范围。
        1. ⇒700(创建 600-699)
        2. ⇒800(创建 700-799)
        3. ⇒900(创建 800-899)
        4. ⇒1000(创建 900-999)
      4. 增加 1000 直到获得 10000 的倍数,否则会超出范围。
        1. [会超出限制]
    2. 收缩阶段
      1. 增加 100 直到超出范围。
        1. ⇒1100(创建 1000-1099)
        2. ⇒1200(创建 1100-1199)
      2. 增加 10 直到超出范围。
      3. 您递增 1,直到超出范围。
        1. ⇒1201(创建 1200-1200)
        2. ⇒1202(创建 1201-1201)
        3. ⇒1203(创建 1202-1202)

    二进制相同,但使用 2 的幂而不是 10 的幂。

    my $start = 1000;
    my $end   = 1999 + 1;
    
    my @ranges;
    
    my $this = $start;
    my $this_power = 1;
    OUTER: while (1) {
       my $next_power = $this_power * 2;
       while ($this % $next_power) {
          my $next = $this + $this_power;
          last OUTER if $next > $end;
    
          my $mask = ~($this_power - 1) & 0xFFFF;
          push @ranges, sprintf("0x%04x/0x%x", $this, $mask);
          $this = $next;
       }
    
       $this_power = $next_power;
    }
    
    while ($this_power > 1) {
       $this_power /= 2;
       while (1) {
          my $next = $this + $this_power;
          last if $next > $end;
    
          my $mask = ~($this_power - 1) & 0xFFFF;
          push @ranges, sprintf("0x%04x/0x%x", $this, $mask);
          $this = $next;
       }
    }
    
    say for @ranges;
    

    我们可以利用我们正在处理二进制文件这一事实来优化它。

    my $start = 1000;
    my $end   = 1999 + 1;
    
    my @ranges;
    
    my $this = $start;
    my $power = 1;
    my $mask = 0xFFFF;
    while ($start & $mask) {
       if ($this & $power) {
          push @ranges, sprintf("0x%04x/0x%x", $this, $mask);
          $this += $power;
       }
    
       $mask &= ~$power;
       $power <<= 1;
    }
    
    while ($end & ~$mask) {
       $power >>= 1;
       $mask |= $power;
    
       if ($end & $power) {
          push @ranges, sprintf("0x%04x/0x%x", $this, $mask);
          $this |= $power;
       }
    }
    
    say for @ranges;
    

    输出:

    0x03e8/0xfff8
    0x03f0/0xfff0
    0x0400/0xfe00
    0x0600/0xff00
    0x0700/0xff80
    0x0780/0xffc0
    0x07c0/0xfff0
    

    【讨论】:

      【解决方案2】:

      我尝试使用@ikegami 提供的非常优雅的解决方案,但发现存在导致端口超出范围或缺少端口(例如 1-6、1000-4000、1000-10000)的边缘情况。这种替代方法似乎可以避免这些问题。

      my $LIMIT = 65535;
      
      sub maxPort {
          my ($port, $mask) = @_;
      
          my $xid = $LIMIT - $mask;
          my $nid = $port & $mask;
          return $nid + $xid;
      }
      
      sub portMask {
          my ($port, $end) = @_;
      
          my $mask = $LIMIT;
          my $test_mask = $LIMIT;
          my $bit = 1;
          my $net = $port & $LIMIT;
          my $max_port = maxPort($net, $LIMIT);
      
          while ($net && ($max_port <= $end)) {
              $net = $port & $test_mask;
              if ($net < $port) {
                  last;
              }
              $max_port = maxPort($net, $test_mask);
              if ($max_port <= $end) {
                  $mask = $test_mask;
              }
              $test_mask -= $bit;
              $bit <<= 1;
          }
      
          return $mask;
      }
      
      sub maskRange {
          my ($start, $end) = @_;
      
          my @portMasks;
      
          if (($end <= $start) || ($end > $LIMIT)) {
              exit 1;
          }
      
          my $mask = $LIMIT;
          my $port = $start;
      
          while ($port <= $end) {
              $mask = portMask($port, $end);
              push @portMasks, sprintf("0x%04x/0x%x", $port, $mask);
              $port = maxPort($port, $mask) + 1;
          }
      
          return @portMasks;
      }
      
      my @ranges = maskRange(1000, 1999);
      
      for (@ranges) {
          print("$_", "\n");
      }
      

      输出:

      0x03e8/0xfff8
      0x03f0/0xfff0
      0x0400/0xfe00
      0x0600/0xff00
      0x0700/0xff80
      0x0780/0xffc0
      0x07c0/0xfff0
      

      【讨论】:

        猜你喜欢
        • 2012-12-27
        • 2018-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多