IPv4アドレスのマスクの変換
Posted feedbacks - Flatten
Nested HiddenSQL Server 2008 で確認しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | WITH
Input(id, subnetmask) AS (
SELECT 1, '255.255.255.0'
UNION ALL SELECT 2, '255.255.255.128'
UNION ALL SELECT 3, '255.255.255.255'
)
, Input_(id, subnetmask) AS (
SELECT id, subnetmask + '.' FROM Input
)
, ToNums(id, i, element, input) AS (
SELECT
id
, 1
, CAST(LEFT(subnetmask, CHARINDEX('.', subnetmask, 1) - 1) AS int)
, SUBSTRING(subnetmask, CHARINDEX('.', subnetmask, 1) + 1, LEN(subnetmask))
FROM
Input_
UNION ALL
SELECT
id
, i + 1
, CAST(LEFT(input, CHARINDEX('.', input, 1) - 1) AS int)
, SUBSTRING(input, CHARINDEX('.', input, 1) + 1, LEN(input))
FROM
ToNums
WHERE
i < 4
)
, ToBin(id, i, bin, deci, crnt) AS (
SELECT
id
, i
, CAST(CASE
WHEN element >= 128 THEN 10000000
ELSE 0
END AS bigint)
, CASE
WHEN element >= 128 THEN element - 128
ELSE element
END
, 6
FROM
ToNums
UNION ALL
SELECT
id
, i
, CASE
WHEN deci >= POWER(2, crnt) THEN POWER(10, crnt)
ELSE 0
END + bin
, CASE
WHEN deci >= POWER(2, crnt) THEN deci - POWER(2, crnt)
ELSE deci
END
, crnt - 1
FROM
ToBin
WHERE
crnt >= 0
)
, CountOne(id, result) AS (
SELECT
id
, SUM(LEN(REPLACE(STR(bin, 8), '0', '')))
FROM
ToBin
WHERE
crnt = -1
GROUP BY
id
)
SELECT id, result FROM CountOne
|
ふつーに実装。MASKアドレスじゃない場合のエラー処理とかは特にしてないです。
逆変換も付けました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | use strict;
use warnings;
sub addr2bits
{
my ($addr) = @_;
my $bits = 0;
for (split /\./, $addr) {
$_ &= 0xff;
if ( 0xff == $_ ) {
$bits += 8;
}
else {
my $b = sprintf '%08b', $_;
$bits += index($b,'0');
last;
}
}
$bits;
}
sub bits2addr
{
my ($bits) = @_;
my @addr;
for ( 1 .. 4 ) {
if ( $bits >= 8 ) {
push @addr, 0xff;
$bits -= 8;
}
elsif ( $bits > 0 ) {
push @addr, oct('0b' . '1' x $bits . '0' x (8-$bits));
$bits = 0;
}
else {
push @addr, 0;
}
}
join('.', @addr);
}
my $addr = $ARGV[0];
my $bits = addr2bits($addr);
print "$addr => $bits\n";
$addr = bits2addr($bits);
print "$bits => $addr\n";
|
C言語で。 netmask_to_prefix() : ネットマスク -> 数値 prefix_to_netmask() : 数値 -> ネットマスク エラーの場合は-1を返却。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include <stdio.h>
int netmask_to_prefix(char *s)
{
unsigned long l;
int b1, b2, b3, b4;
int n;
if (sscanf(s, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) == 4 && (b1 | b2 | b3 | b4) < 256) {
l = (b1 << 24 | b2 << 16 | b3 << 8 | b4) ^ 0xffffffffUL;
n = 32;
while (l & 1) {
n--;
l >>= 1;
}
return l ? -1 : n;
} else {
return -1;
}
}
int prefix_to_netmask(int n, char *s)
{
unsigned long l;
if (0 < n && n <= 32) {
l = -1L << (32 - n);
sprintf(s, "%d.%d.%d.%d",
(int)(l >> 24 & 255), (int)(l >> 16 & 255), (int)(l >> 8 & 255), (int)(l & 255));
return 0;
} else {
return -1;
}
}
int main()
{
char s[100];
int n;
printf("netmask? "); scanf("%s", s);
printf("result: %d\n", netmask_to_prefix(s));
printf("prefix? "); scanf("%d", &n);
if (!prefix_to_netmask(n, s)) printf("result: %s\n", s);
}
|
もっとpackをかっこよく使いたい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/usr/bin/perl
use strict;
use warnings;
sub maskstr2num{
my $mask = shift;
return if $mask !~ /^(\d{1,3}\.){3}\d+$/;
my @mask_sections = split /\./,$mask;
my $bit_mask = '';
for(@mask_sections){
return if $_ > 255;
$bit_mask .= unpack("B8",pack("C",$_));
}
return if $bit_mask !~ /^1+0+$/;
my $cnt = scalar(() = $bit_mask =~ /1/g);
return $cnt;
}
sub num2maskstr{
my $mask = shift;
return if $mask > 32 || $mask < 0;
$mask = '1'x$mask . '0'x(32-$mask);
my @mask_sections = $mask =~ /^(\d{8})(\d{8})(\d{8})(\d{8})$/;
$_ = unpack("C", pack("B8",$_)) for @mask_sections;
$mask = join '.',@mask_sections;
return $mask;
}
|
a2m '255.255.255.0' 24 a2m '255.255.255.128' 25 a2m '255.255.255.255' 32 m2a 24 255.255.255.0 m2a 25 255.255.255.128 m2a 32 255.255.255.255
1 2 | a2m =: 3 : '+/ , #: ". > ''.'' cutopen y'
m2a =: 3 : '}: ; ,&''.'' @ ": each 256 #. inv #. 32 {. y # 1'
|
ビット演算で基本に忠実に実装しました。どのぐらいの長さになるか心配だったけど、そんなに悪くない行数で収まっています。
また、できるだけ例外処理をやって、さらにどのぐらい行が増えるかも試しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | def netmask_num_of(addr):
try:
addrs = []
for a in map(long, addr.split('.')):
if a > 255 or a < 0: raise
addrs.append(a)
if len(addrs) != 4: raise
except:
raise "%s may not be an address string." % addr
n = reduce(lambda a, b: a * 256 + long(b), addrs, 0)
num = 0
topbit = 1 << 31
allbits = (1 << 32) - 1
while n & topbit:
num += 1
n = (n << 1) & allbits
if n:
raise "%s is not valid for netmask." % addr
return num
print netmask_num_of("255.255.255.0")
print netmask_num_of("255.255.255.128")
print netmask_num_of("255.255.255.255")
def netmask_str_of(num):
if num > 32 or num < 0:
raise "%s is out of range for netmask." % num
n = 0L
for i in range(32):
n = (n << 1) | ((num > i) and 1 or 0)
addrs = []
for i in range(4):
addrs.insert(0, str(n & 255))
n = n >> 8
return '.'.join(addrs)
print netmask_str_of(24)
print netmask_str_of(25)
print netmask_str_of(32)
|
エラー処理はしていません。
実行環境がRuby 1.8.6以前の場合
map(&:to_i) の部分を map{|e| e.to_i} にして下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def mask2bits(mask_str)
mask_str.split('.').map(&:to_i).inject(0){|r,e| 8.times{r+=(e&1);e>>=1};r}
end
def bits2mask(num)
['1'*num + '0'*(32-num)].pack('B*').unpack('C*')*'.'
end
if $0 == __FILE__
puts mask2bits('255.255.255.0')
puts mask2bits('255.255.255.128')
puts mask2bits('255.255.255.255')
puts bits2mask(24)
puts bits2mask(25)
puts bits2mask(32)
end
|
正変換は単に立っているビットの数を数えているだけ、逆変換は頭からビットを立たせているだけです。それだけしか出来ていないのに、ここで息切れしてしまいました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #load "str.cma"
let ni0 = Nativeint.zero;;
let ni1 = Nativeint.one;;
let (<<@) = Nativeint.shift_left;;
let (&@) = Nativeint.logand;;
let (|@) = Nativeint.logor;;
let netmask_num_of addr =
let onbits_of byte =
let rec loop ptr =
if ptr < 0 then 0 else
let checker = ni1 <<@ ptr in
if byte &@ checker = ni0 then 0 else
1 + (loop (ptr - 1))
in
loop 7
in
let addrs = List.map Nativeint.of_string
(Str.split (Str.regexp "\\.") addr) in
List.fold_left (fun a b -> a + (onbits_of b)) 0 addrs
;;
print_string ((string_of_int (
netmask_num_of "255.255.255.0")) ^ "\n");;
print_string ((string_of_int (
netmask_num_of "255.255.255.128")) ^ "\n");;
print_string ((string_of_int (
netmask_num_of "255.255.255.255")) ^ "\n");;
let netmask_str_of num =
let rec byteloop num n =
if n = 0 then [] else
let rec bitloop num b =
if num = 0 or b < 0 then ni0 else
(ni1 <<@ b) |@ (bitloop (num - 1) (b - 1))
in
[Nativeint.to_string (bitloop num 7)] @
(byteloop (num - 8) (n - 1))
in
String.concat "." (byteloop num 4)
;;
print_string ((netmask_str_of 24) ^ "\n");;
print_string ((netmask_str_of 25) ^ "\n");;
print_string ((netmask_str_of 32) ^ "\n");;
|
- Limboのキャストはtype exprのように書きます。
- int "1"の場合、数値に変換してくれます。
極力、定数は使わないように書きました。 8(ビット数)は名前を思いつかなかったので。
テストコードも含めてみましたが、 かえって見づらくなっただけかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | implement Netmask;
include "sys.m";
sys: Sys;
fprint, sprint: import sys;
include "draw.m";
Netmask: module
{
init: fn(nil: ref Draw->Context, argv: list of string);
};
IPv4len: con 4;
data := array[] of {
("255.128.0.0", 9),
("255.255.255.0", 24),
("255.255.255.128", 25),
("255.255.255.255", 32),
};
init(nil: ref Draw->Context, nil: list of string)
{
sys = load Sys Sys->PATH;
stderr := sys->fildes(2);
for(i := 0; i < len data; i++){
(addr, expected) := data[i];
actual := toint(addr);
if(actual != expected)
fprint(stderr, "%s %d vs %d\n", addr, actual, expected);
r := toaddr(expected);
if(addr != r)
fprint(stderr, "%d %s vs %s\n", expected, r, addr);
}
}
toint(addr: string): int
{
(n, l) := sys->tokenize(addr, ".");
if(n != IPv4len)
return -1;
sum := 0;
for(p := l; p != nil; p = tl p)
sum += bitcount(byte hd p);
return sum;
}
bitcount(b: byte): int
{
sum := 0;
for(n := int b; n; n &= n-1)
sum++;
return sum;
}
toaddr(n: int): string
{
N: con IPv4len*8;
if(n < 0 || n > N)
return nil;
t := (big 1 << n) - big 1;
t <<= N-n;
s := "";
for(i := 0; i < IPv4len; i++)
s += sprint(".%d", int byte (t >> (IPv4len-i-1)*8));
return s[1:];
}
|
ちょっと汚いかな・・・
1 2 3 4 5 6 7 8 9 | def toMask(ip){
ip.split(/\./).collect{
Integer.toBinaryString(it.toInteger())
}*.count("1").sum()
}
assert toMask("255.255.255.0") == 24
assert toMask("255.255.255.128") == 25
assert toMask("255.255.255.255") == 32
|
F#で、 実行例 > maskstr2num "255.255.255.0";; val it : int = 24 > maskstr2num "255.255.255.128";; val it : int = 25 > maskstr2num "255.255.255.255";; val it : int = 32 > num2maskstr 24;; val it : string = "255.255.255.0" > num2maskstr 25;; val it : string = "255.255.255.128" > num2maskstr 32;; val it : string = "255.255.255.255"
see: ビットを数える・探すアルゴリズム
1 2 3 4 5 6 7 8 9 10 11 12 13 | let bitcount bits:int =
let bits = (bits &&& 0x55555555) + (bits >>> 1 &&& 0x55555555)
let bits = (bits &&& 0x33333333) + (bits >>> 2 &&& 0x33333333)
let bits = (bits &&& 0x0f0f0f0f) + (bits >>> 4 &&& 0x0f0f0f0f)
let bits = (bits &&& 0x00ff00ff) + (bits >>> 8 &&& 0x00ff00ff) in
(bits &&& 0x0000ffff) + (bits >>>16 &&& 0x0000ffff)
let maskstr2num (mask:string) =
bitcount <| Seq.fold ( fun a b -> a * 256 + int b ) 0 (mask.Split('.'))
let num2maskstr (num:int) =
let unum = uint32 (2.0 ** (float num))-1u <<< (32-num)
System.String.Join(".", [|for i=3 downto 0 do yield ((unum &&& (0xFFu <<< i*8) >>> i*8).ToString()) |])
|
1 2 3 4 5 6 7 8 9 10 11 12 | (use srfi-1)
(use util.list)
(define (mask->nbits mask)
(count (cut eqv? #¥1 <>)
(append-map (lambda (n) (string->list (format "~b" (string->number n))))
(string-split mask #¥.))))
(define (nbits->mask nbits)
(string-join (map (lambda (bits) #`",(string->number (list->string bits) 2)")
(slices (take* (make-list nbits #¥1) 32 #t #¥0) 8))
"."))
|
やった、処理が80文字に収まった!
oct()が2進数文字列を変換できるのはあまり知られていない気がします。
1 2 3 4 5 6 7 | use List::Util qw/sum/;
sub mask2bits($) {
sum map { split //, sprintf '%b', $_ } split /\./, shift;
}
sub bits2mask($) {
join '.', map { oct "0b$_" } (1 x $_[0] . 0 x (32 - $_[0])) =~ /\d{8}/g;
}
|
x86のBSF命令 (下から数えて何ビット目が立っているか) を使ってみました。 また、uintは4バイト固定です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import std.intrinsic: bsf; // x86's BSF instruction
import std.socket;
immutable ushort DUMMY_PORT = 0;
uint mask2bits(in string mask)
{
uint addr = (new InternetAddress(mask, DUMMY_PORT)).addr;
return 32 - bsf(addr);
}
string bits2mask(in uint bits)
{
uint addr = 0xFFFF_FFFF << (32 - bits);
return (new InternetAddress(addr, DUMMY_PORT)).toAddrString;
}
void main()
{
assert (mask2bits("255.255.255.0") == 24);
assert (mask2bits("255.255.255.128") == 25);
assert (mask2bits("255.255.255.255") == 32);
assert (bits2mask(24) == "255.255.255.0");
assert (bits2mask(25) == "255.255.255.128");
assert (bits2mask(32) == "255.255.255.255");
}
|
とりあえず、InetAddressを使いました。例外処理があるので効率は・・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import java.net._
object IPNetMask {
val ipMask2NumBits = Map(
(256- 1) -> 8 ,
(256- 2) -> 7 ,
(256- 4) -> 6 ,
(256- 8) -> 5 ,
(256- 16) -> 4 ,
(256- 32) -> 3 ,
(256- 64) -> 2 ,
(256-128) -> 1 ,
0 -> 0 )
var ipNumBits2Mask = Map[Int,Int]()
for( (k,v)<-ipMask2NumBits ) ipNumBits2Mask += v->k
def main(args : Array[String]) : Unit = {
//ipMask2NumBits.foreach(println _)
//ipNumBits2Mask.foreach(println _)
for{ arg <-args
ip =InetAddress.getByName(arg) }{
ip2numbits(ip) match {
case Some(n) =>
println(ip+" : " +n+"bits")
println("reverse : "+numbits2ip(n))
case None => println("error")
}
}
()
}
def ip2numbits(ip:InetAddress) : Option[Int] = {
val rawIP = ip.getAddress.map(0x0FF & _)
try{
rawIP match{
case Seq(255,255,255,a) => return Some( 24 + ipMask2NumBits(a) )
case Seq(255,255, a,0) => return Some( 16 + ipMask2NumBits(a) )
case Seq(255, a, 0,0) => return Some( 8 + ipMask2NumBits(a))
case _ => return None
}
}catch{
case _ => return None
}
None
}
def numbits2ip(numbits:Int) : Option[InetAddress]={
try{
numbits match {
case n if (n >= 24) => return Some(InetAddress.getByName( ipNumBits2Mask(n-24).formatted("255.255.255.%d")))
case n if (n >= 16) => return Some(InetAddress.getByName( ipNumBits2Mask(n-16).formatted("255.255.%d.0")))
case n if (n >= 8) => return Some(InetAddress.getByName( ipNumBits2Mask(n- 8).formatted("255.%d.0.0")))
}
}catch{
case _ => return None
}
None
}
}
|
「ipaddr2mask」でアドレス→数値変換。 「mask2ipaddr」で数値→アドレス変換。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | function format(num, dec1, dec2, minlen){
var num1 = new Number(parseInt(num, dec1)).toString(dec2);;
while(num1.length < minlen) num1 = "0" + num1;
return num1;
}
function ipaddr2mask(ipaddr){
var ad = ipaddr.split(".");
var str = "";
for(var i = 0; i < ad.length; i++){
str += format(ad[i],10,2,8);
}
for(var i = str.length-1; i > 0 ; i--){
if(str.charAt(i)!=0){return i+1;}
}
return 0;
}
function mask2ipaddr(mask){
var m1 = format(mask,10,10,1);
var str = "";
while(str.length < 32){ str+= (str.length < m1) ? "1" : "0"; }
var ipaddr = "";
for(var i = 0; i < 4; i++){
if(i > 0){ ipaddr+="."; }
ipaddr+=format(str.substr(i*8, 8),2,10,1);
}
return ipaddr;
}
|
結構乱暴。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | #include <iostream>
#include <algorithm>
#include <functional>
#include <bitset>
#include <string>
#include <sstream>
#include <cassert>
int mask2bits(const std::string& s)
{
int n[4];
char c[3];
if( (std::istringstream(s) >> n[0] >> c[0] >> n[1] >> c[1] >> n[2] >> c[2] >> n[3]) &&
(std::count(c, c + 3, '.') == 3) &&
(std::count_if(n, n + 4, std::bind1st(std::less_equal<int>(), 0)) == 4) &&
(std::count_if(n, n + 4, std::bind2nd(std::less_equal<int>(), 255)) == 4) )
{
return std::bitset<32>((n[0] << 24) + (n[1] << 16) + (n[2] << 8) + n[3]).count();
}
else
{
return -1;
}
}
std::string bits2mask(int n)
{
if((0 <= n) && (n <= 32))
{
std::bitset<32> bits;
for(int i = 0; i < n; ++i)
{
bits.set(31 - i);
}
unsigned long m = bits.to_ulong();
std::ostringstream oss;
oss << ((m >> 24) & 0xffu) << '.' << ((m >> 16) & 0xffu) << '.' << ((m >> 8) & 0xffu) << '.' << (m & 0xffu);
return oss.str();
}
else
{
return "";
}
}
int main(int, char* [])
{
assert (mask2bits("255.255.255.0") == 24);
assert (mask2bits("255.255.255.128") == 25);
assert (mask2bits("255.255.255.255") == 32);
assert (bits2mask(24) == "255.255.255.0");
assert (bits2mask(25) == "255.255.255.128");
assert (bits2mask(32) == "255.255.255.255");
return 0;
}
|
Squeak Smalltalk で。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | | maskStrToBits |
maskStrToBits := [:maskStr |
((maskStr subStrings: '.') collect: [:each | each asInteger radix: 2])
concatenation occurrencesOf: $1].
maskStrToBits value: '255.255.255.0'. "=> 24 "
maskStrToBits value: '255.255.255.128'. "=> 25 "
maskStrToBits value: '255.255.255.255'. "=> 32 "
| bitsToMaskStr |
bitsToMaskStr := [:int |
String streamContents: [:ss |
(((String new: int withAll: $1) forceTo: 32 paddingWith: $0)
groupsOf: 8 atATimeCollect: [:each | ('2r', each) asNumber printString])
do: [:each | ss nextPutAll: each] separatedBy: [ss nextPut: $.]]].
bitsToMaskStr value: 24. "=> '255.255.255.0' "
bitsToMaskStr value: 25. "=> '255.255.255.128' "
bitsToMaskStr value: 32. "=> '255.255.255.255' "
|
すみません、昨日無駄にNativeintを使ったあげく、末尾再帰最適化されないコードを書いてしまいました。同じアルゴリズムをより正しく実装したものがこっちです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #load "str.cma"
let print_string_nl v = print_string (v ^ "\n")
let print_int_nl v = print_string_nl (string_of_int v)
let netmask_num_of addr =
let onbits_of byte =
let rec loop ptr v =
if ptr < 0 || byte land (1 lsl ptr) = 0 then v
else loop (ptr - 1) (v + 1)
in loop 7 0
in
let addrs = List.map int_of_string (Str.split (Str.regexp "\\.") addr) in
List.fold_left (fun a b -> a + (onbits_of b)) 0 addrs
;;
print_int_nl (netmask_num_of "255.255.255.0");;
print_int_nl (netmask_num_of "255.255.255.128");;
print_int_nl (netmask_num_of "255.255.255.255");;
let netmask_str_of num =
let rec byteloop num n v =
if n = 0 then v
else
let rec bitloop num b v =
if num = 0 or b < 0 then v
else bitloop (num - 1) (b - 1) ((1 lsl b) lor v)
in byteloop (num - 8) (n - 1) (v @ [string_of_int (bitloop num 7 0)])
in String.concat "." (byteloop num 4 [])
;;
print_string_nl (netmask_str_of 24);;
print_string_nl (netmask_str_of 25);;
print_string_nl (netmask_str_of 32);;
|
ナイーブに。 *Main> mask2nbits "255.255.255.0" 24 *Main> mask2nbits "255.255.255.128" 25 *Main> mask2nbits "255.255.255.255" 32 *Main> nbits2mask 24 "255.255.255.0" *Main> nbits2mask 25 "255.255.255.128" *Main> nbits2mask 28 "255.255.255.240" *Main> nbits2mask 32 "255.255.255.255"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import Data.List
import Data.Word
mask2nbits :: String -> Int
mask2nbits = length . takeWhile (1==) . toNArry 2 32 . addr2int
nbits2mask :: Int -> String
nbits2mask n = int2addr $ maxBound - (2^(32-n)-1)
int2addr :: Word32 -> String
int2addr = concat . intersperse "." . map show . toNArry 256 4
addr2int :: String -> Word32
addr2int = foldl ((+) . (256 *)) 0 . unfoldr phi
where
phi "" = Nothing
phi s = case break ('.'==) s of
(xs,_:ys) -> Just (read xs,ys)
(xs,[]) -> Just (read xs,[])
toNArry :: (Integral i) => Int -> Int -> i -> [Int]
toNArry n k i
= map fromInteger
$ snd
$ mapAccumR (flip id) (toInteger i) (replicate k (`divMod` (toInteger n)))
|
変換・逆変換の過程をたどりやすくしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import Data.Word ( Word32, Word8 )
import Data.List ( unfoldr, intersperse )
-- Int <-> String
nbits2mask = str_w8s . w8s_w32 . w32_int
mask2nbits = int_w32 . w32_w8s . w8s_str
-- Int <-> Word32
w32_int = (maxBound-) . (-1+) . (2^) . (32-)
int_w32 = (32-) . round . logBase 2 . (1+) . fromIntegral . (maxBound-)
-- Word32 <-> [Word8]
w8s_w32 = reverse . take 4 . map w8_w32 . unfoldr (Just . swap . (`divMod`256))
w32_w8s = foldl ((+) . (256*)) 0 . map w32_w8
-- [Word8] <-> String
str_w8s = concat . intersperse "." . map show
w8s_str = map read . unfoldr phi . ('.':)
where
phi [] = Nothing
phi (_:cs) = Just $ break ('.'==) cs
-- misc
swap (x,y) = (y,x)
w8_w32 = toEnum . fromEnum :: Word32 -> Word8 -- 0 <= arg < 2^8
w32_w8 = toEnum . fromEnum :: Word8 -> Word32 -- 0 <= arg < 2^8
main = mapM_ (print . test) [0..32]
where
test n = let { m = nbits2mask n; n' = mask2nbits m } in (n, m, n==n')
|
PPCRE を使ってみました。逆はビット演算で。
1 2 3 4 5 6 7 8 9 10 11 | (asdf:operate 'asdf:load-op :cl-ppcre)
(defun mask-to-bits (s)
(position #\0 (format nil "~{~8,'0B~}0"
(mapcar #'parse-integer (ppcre:split "\\." s)))))
(defun bits-to-mask (n)
(let ((m (deposit-field #xffffffff (byte n (- 32 n)) 0)))
(format nil "~D.~D.~D.~D"
(ldb (byte 8 24) m) (ldb (byte 8 16) m)
(ldb (byte 8 8) m) (ldb (byte 8 0) m))))
|
InetAddressを引数にしてネットマスクのbit数を返すメソッドと、bit数からInet4Address を返すメソッドです。ネットマスクが n bit の時、ビットパターンが2の補数表現で -2^(32 - n) になる事を利用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import java.net.*;
import java.math.*;
import java.io.*;
public class Sample {
public static int maskLength(InetAddress adrs) {
BigInteger a = new BigInteger(adrs.getAddress());
if (a.bitCount() != a.bitLength())
throw new IllegalArgumentException("Illegal mask.");
return 8 * adrs.getAddress().length - a.bitCount();
}
public static InetAddress getMask(int n) {
try {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bs);
dos.writeInt(-(1 << (32 - n)));
return Inet4Address.getByAddress(bs.toByteArray());
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static void main(String[] args) throws UnknownHostException {
System.out.println(maskLength(InetAddress.getByName("255.255.255.0")));
System.out.println(maskLength(InetAddress.getByName("255.255.255.128")));
System.out.println(maskLength(InetAddress.getByName("255.255.255.255")));
System.out.println(maskLength(InetAddress.getByName("ffff:ffff:ffff::")));
System.out.println(getMask(24));
System.out.println(getMask(25));
System.out.println(getMask(32));
}
}
|
#9133の移植です。 便利なクラスがあるんですねえ。 勉強になりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // Java版(#9133)の移植です。
import java.net._
import java.math._
import java.io._
object IPNetMask9133 {
def main(args : Array[String]) : Unit = {
System.out.println(maskLength(InetAddress.getByName("255.255.255.0")));
System.out.println(maskLength(InetAddress.getByName("255.255.255.128")));
System.out.println(maskLength(InetAddress.getByName("255.255.255.255")));
System.out.println(maskLength(InetAddress.getByName("ffff:ffff:ffff::")));
System.out.println(getMask(24));
System.out.println(getMask(25));
System.out.println(getMask(32));
}
def maskLength(adrs:InetAddress):Int = {
val a = new BigInteger(adrs.getAddress());
if (a.bitCount() != a.bitLength())
throw new IllegalArgumentException("Illegal mask.")
//
return 8 * adrs.getAddress().length - a.bitCount();
}
def getMask(n:Int) :InetAddress = {
try {
val bs = new ByteArrayOutputStream();
val dos = new DataOutputStream(bs);
dos.writeInt(-(1 << (32 - n)));
return InetAddress.getByAddress(bs.toByteArray());
} catch {
case e:Exception =>throw new IllegalStateException(e);
}
}
}
|
Scala(#9133)をErlangに移植しました。 効率はともかく、例外処理をしているので・・・
関数の仕様は、Erlang的で、成功時は {ok,****} のようなタプルが返ります。失敗時はok以外のアトム。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | -module(netmask).
-export([numbits_of/1,from_numbits/1,test/0]).
numbits_of(StrMask) ->
try
{ok,Mask} = inet_parse:address(StrMask) ,
{Class,MaskByte} = case Mask of
{255,255,255,X} -> {24,X};
{255,255, X,0} -> {16,X};
{255, X, 0,0} -> { 8,X};
_ -> throw("bad mask")
end,
{ok,
case MaskByte of
(256- 1)-> 8;
(256- 2)-> 7;
(256- 4)-> 6;
(256- 8)-> 5;
(256- 16)-> 4;
(256- 32)-> 3;
(256- 64)-> 2;
(256-128)-> 1;
0 -> 0
end + Class }% <--- result
catch
throw:E -> {thrown,E};
exit :E -> {exited,E};
error:E -> {error ,E}
end.
from_numbits(Numbits)->
try
{Class,Maskbits} =
case Numbits of
N when N>=24 -> {24,N-24} ;
N when N>=16 -> {16,N-16} ;
N when N>= 8 -> { 8,N- 8}
end ,
Maskbyte =
case Maskbits of
8 -> (256- 1) ;
7 -> (256- 2) ;
6 -> (256- 4) ;
5 -> (256- 8) ;
4 -> (256- 16) ;
3 -> (256- 32) ;
2 -> (256- 64) ;
1 -> (256-128) ;
0 -> 0
end ,
%
{ok ,
case Class of
24 -> inet_parse:ntoa( {255,255,255,Maskbyte} ) ;
16 -> inet_parse:ntoa( {255,255,Maskbyte ,0} ) ;
8 -> inet_parse:ntoa( {255,Maskbyte ,0,0} )
end }% <--- result
catch
throw:E -> {thrown,E};
exit :E -> {exited,E};
error:E -> {error ,E}
end.
test()->
lists:foreach(fun(I)->
{ok , M} = from_numbits(I),
{ok , N} = numbits_of(M),
io:format("Numbits=~p , Mask=~p , Result=~p~n",[I,M,(I==N)])
end , lists:seq(8,31) ).
|
string→intの変換のみ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class P
{
static int ToI(string mask)
{
try
{
string[] a = mask.Split('.');
if (a.Length != 4) return -1;
long l = a.Aggregate(1L, (x, y) => (x << 8) + Convert.ToByte(y));
string b = Convert.ToString(l, 2).Substring(1, 32);
int p = b.IndexOf('0');
return p < 0 ? 32 : b.IndexOf('1', p) < 0 ? p : -1;
}
catch
{
return -1;
}
}
}
|
無味乾燥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function mask2bits($ddn_str)
{
$bits = 0;
$decs = explode('.', $ddn_str);
foreach ( $decs as $dec ) {
$bits += substr_count(decbin($dec), '1');
}
return $bits;
}
function bits2mask($bits)
{
$bits_str = strrev(sprintf('%032s', str_repeat("1", $bits)));
$bins = str_split($bits_str, 8);
foreach ($bins as $bin) {
$decs[] = bindec($bin);
}
return implode('.', $decs);
}
|
Squirrel3.0で。
bits2mask()のbits=0の時が美しくない・・・
(1<<32が1になってしまう。64bitなら無視できるかも)
結果:
0.0.0.0 => 0
128.0.0.0 => 1
255.255.255.0 => 24
255.255.255.128 => 25
255.255.255.255 => 32
0 => 0.0.0.0
1 => 128.0.0.0
8 => 255.0.0.0
32 => 255.255.255.255
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | function mask2bits(mask)
{
local addr = split(mask, ".").reduce(@(a,b) (a.tointeger()<<8) + b.tointeger());
for(local i=32; addr; i--, addr=addr>>1) if (addr&1) return i;
return 0;
}
function bits2mask(bits)
{
local addr = bits ? -(1<<(32-bits)) : 0;
return format("%d.%d.%d.%d", addr>>24&0xFF, addr>>16&0xFF, addr>>8&0xFF, addr>>0&0xFF);
}
function test_mask2bits(mask)
{
print(format("%15s => %2d\n", mask, mask2bits(mask)));
}
function test_bits2mask(bits)
{
print(format("%2d => %15s\n", bits, bits2mask(bits)));
}
test_mask2bits("0.0.0.0");
test_mask2bits("128.0.0.0");
test_mask2bits("255.255.255.0");
test_mask2bits("255.255.255.128");
test_mask2bits("255.255.255.255");
test_bits2mask(0);
test_bits2mask(1);
test_bits2mask(8);
test_bits2mask(32);
|
Javaでシンプルに。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | public class SubnetMask {
public static void main( String[] args ) {
System.out.println( getMaskLength( "255.255.255.0" ) );
System.out.println( getMaskLength( "255.255.255.128" ) );
System.out.println( getMaskLength( "255.255.255.255" ) );
System.out.println( getMask( 24 ) );
System.out.println( getMask( 25 ) );
System.out.println( getMask( 32 ) );
}
// サブネットマスクの長さを求める。
public static int getMaskLength( String subnetMask ) {
/////////////////////////////////////////////////////////////
// サブネットマスクの妥当性確認
/////////////////////////////////////////////////////////////
// サブネットマスクが「数値.数値.数値.数値」の形式であることを確認する。
if ( ! subnetMask.matches( "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+" ) ) {
throw new RuntimeException( "指定されたサブネットマスクが不正です。" );
}
// サブネットマスクの数値が以下の順に並んでいることを確認する。
// 1. 255が0個以上
// 2. 254と252と248と240と224と192と128のいずれかが0個か1個
// 3. 0が0個以上」
String temp = subnetMask + ".";
if ( ! temp.matches( "(255\\.)*((254|252|248|240|224|192|128)\\.)?(0\\.)*" ) ) {
throw new RuntimeException( "指定されたサブネットマスクが不正です。" );
}
/////////////////////////////////////////////////////////////
// サブネットマスクを長さに変換
/////////////////////////////////////////////////////////////
// サブネットマスクを4つの数値に分割する。
String[] partsStr = subnetMask.split( "\\." );
int[] parts = new int[4];
for ( int i = 0; i < 4; ++i ) {
parts[i] = Integer.parseInt( partsStr[i] );
}
// 各数値のビット数を求め、ビット数を合計して長さを求める。
int result = 0;
for ( int i = 0; i < 4; ++i ) {
switch ( parts[i] ) {
case 255:
result += 8;
break;
case 254:
result += 7;
break;
case 252:
result += 6;
break;
case 248:
result += 5;
break;
case 240:
result += 4;
break;
case 224:
result += 3;
break;
case 192:
result += 2;
break;
case 128:
result += 1;
break;
case 0:
result += 0;
break;
}
}
return result;
}
// 長さからサブネットマスクを求める。
public static String getMask( final int length ) {
/////////////////////////////////////////////////////////////
// 長さの妥当性確認
/////////////////////////////////////////////////////////////
if ( length < 0 || 32 < length ) {
throw new RuntimeException( "指定された長さが不正です。" );
}
/////////////////////////////////////////////////////////////
// 長さをサブネットマスクに変換
/////////////////////////////////////////////////////////////
// サブネットマスクの4つ数値
int[] parts = new int[] { 0, 0, 0, 0 };
// 4つ数値のうち、先頭から「長さを8で割った商」個の数値を255にする。
int q = length / 8;
for ( int i = 0; i < q; ++i ) {
parts[i] = 255;
}
// 「長さを8で割った余り」がある場合、
// 4つの数値のうち、先頭から「長さを8で割った商 + 1」番目の数値を、「余り」から求める。
int r = length % 8;
switch ( r ) {
case 7:
parts[q] = 254;
break;
case 6:
parts[q] = 252;
break;
case 5:
parts[q] = 248;
break;
case 4:
parts[q] = 240;
break;
case 3:
parts[q] = 224;
break;
case 2:
parts[q] = 192;
break;
case 1:
parts[q] = 128;
break;
}
// 4つの数値を連結して、サブネットマスクを作成する。
return parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3];
}
}
|
・Mask2Bit:
ネットマスク(文字型)を渡すとビット値(数値型)を返却します。
・Bit2Mask:
ビット値(数値型)を渡すとネットマスク(文字型)を返却します。
/* ネットマスクをビット値に変換 */
call Mask2Bit input("input netmask.");
message str(##return);
/* 逆変換 */
call Bit2Mask val(input("input value."));
message $$return;
* パラメータの妥当性はチェックしてません。
* Bin2DecとDec2Binは2進数⇔10進数のサブルーチンです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | Mask2Bit:
##c=0;
##bit=0;
while (strlen($$1)) {
$b = leftstr($$1,1);
$$1 = rightstr($$1, strlen($$1)-1);
if ($b == ".") {
##c = ##c + 1;
} else if (val($b) == 0 && $b != "0") {
break;
} else {
$$aar[##c] = $$aar[##c] + $b;
}
}
while (0 <= ##c) {
call Dec2Bin val($$aar[##c]);
while (strlen($$return)) {
##bit = ##bit + val(leftstr($$return,1));
$$return = rightstr($$return, strlen($$return)-1);
}
##c = ##c - 1;
}
return ##bit;
Bit2Mask:
$$b_str = "";
$$mask = "";
while (strlen($$b_str) < 32) {
if (0<##1) {
$$b_str = $$b_str + "1";
} else {
$$b_str = $$b_str + "0";
}
##1 = ##1 - 1;
}
while (strlen($$b_str)) {
call Bin2Dec leftstr($$b_str, 8);
$$mask = $$mask + str(##return);
$$b_str = rightstr($$b_str, strlen($$b_str)-8);
if (strlen($$b_str)) {
$$mask = $$mask + ".";
}
}
return $$mask;
Bin2Dec:
##dec = 0;
##val = 1;
while (strlen($$1)) {
if (rightstr($$1, 1) == "1") {
##dec = ##dec + ##val;
}
##val = ##val*2;
$$1 = leftstr($$1, strlen($$1)-1);
}
return ##dec;
Dec2Bin:
$$str = "";
if (##1!=0) {
while (##1!=1) {
$$str = str(##1%2)+$$str;
##1 = ##1/2;
}
$$str = "1"+$$str;
} else {
$$str = "0";
}
return $$str;
|
人のを見ないで作ってみた。ドキドキ。
1 2 3 4 5 6 7 | def netMask(addr) {
Long.toString(addr.split(/\./).inject(0) {a,b->a*256+(b as long)},2).chars.grep{it=='1'}.size()
}
assert netMask("255.255.255.0")==24
assert netMask("255.255.255.128")==25
assert netMask("255.255.255.255")==32
|
Groovyでの逆変換。
1 2 3 4 5 6 7 8 9 | def mask2addr(mask){
mask = (-1 << (32-mask))
(3..0).collect{(mask >> it*8) & 0xff}.join('.')
}
assert mask2addr(24)=="255.255.255.0"
assert mask2addr(25)=="255.255.255.128"
assert mask2addr(32)=="255.255.255.255"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | Option Explicit
' 与えられた数値を二進数に変換したときの1の数をカウントして返却する
Function countBinaryBits(num)
Dim result, currentNum
result = 0
currentNum = num
While currentNum > 0
result = result + (currentNum Mod 2)
currentNum = Int(currentNum / 2)
Wend
countBinaryBits = result
End Function
' IPv4のネットマスクアドレスを受け取って、マスクビット数を返却する
Function IPv4NetMaskToBits(strIpv4NetMask)
Dim result, strIpv4NetMaskList, netMask
result = 0
strIpv4NetMaskList = Split(strIpv4NetMask, ".")
For Each netMask In strIpv4NetMaskList
result = result + countBinaryBits(CInt(netMask))
Next
IPv4NetMaskToBits = result
End Function
' マスク数を2進数に変換
Function getMaskFromBits(bits)
Dim result, currentBits, i
result = 0
currentBits = bits
For i = 7 to 0 Step - 1
If currentBits > 0 Then
result = result + 2 ^ i
currentBits = currentBits - 1
End If
Next
getMaskFromBits = result
End Function
' マスクビット数を受け取って、IPv4のネットマスクアドレスを返却する
Function bitsToIPv4NetMask(bits)
Dim currentBits
Dim result
Dim i
currentBits = bits
result = ""
For i = 1 to 4
If result <> "" Then
result = result & "."
End If
If currentBits >= 8 Then
result = result & CStr(getMaskFromBits(8))
currentBits = currentBits - 8
ElseIf currentBits > 0 Then
result = result & CStr(getMaskFromBits(currentBits))
currentBits = 0
Else
result = result & "0"
End If
Next
bitsToIPv4NetMask = result
End Function
msgbox IPv4NetMaskToBits("255.255.255.0")
msgbox IPv4NetMaskToBits("255.255.255.128")
msgbox IPv4NetMaskToBits("255.255.255.255")
msgbox bitsToIPv4NetMask(24)
msgbox bitsToIPv4NetMask(25)
msgbox bitsToIPv4NetMask(32)
|
Lost_dogです。
wordsByとsplitsAtは自分のユーティリティから引っ張ってきました。あると便利です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import Numeric
import Data.List
ipMaskToInt = length.takeWhile (=='1').(>>= f).wordsBy (=='.')
where f xs = showIntAtBase 2 ("01"!!) (read xs) ""
intToIpMask x = intercalate "." $ map f $ splitsAt 8 $ ([1..x]>>"1")++([1..32-x]>>"0")
where f = show.fst.head.readInt 2 (`elem`"01") (read.(:[]))
wordsBy p [] = []
wordsBy p xs = ys : wordsBy p (drop 1 zs) where (ys,zs) = break p xs
splitsAt n [] = []
splitsAt n xs = ys : splitsAt n zs where (ys,zs) = splitAt n xs
|
もっと美しく書ける気がします。 これが今の僕の限界です orz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | namespace Doukakuorg
open System
open System.Text.RegularExpressions
type C253 public () =
member this.MaskToBit ip =
if Regex.IsMatch(ip, @"^\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}$") then
ip.Split('.')
|> Array.map (fun a -> Convert.ToInt32(a))
|> Array.map (fun a -> Convert.ToString(a, 2))
|> Array.fold (fun s n -> s + n) ""
|> (fun a -> a.Replace("0", "").Length)
else
0
member this.BitToMask bit =
if 1 <= bit && bit <= 32 then
(new String('1', bit)).PadRight(32, '0')
|> (fun a -> [for i in 0..8..(a.Length-1) do yield a.[i .. (i+7)]])
|> List.map (fun a -> Convert.ToInt32(a, 2))
|> List.fold (fun s n -> String.Format("{0}.{1}", s, n)) ""
|> (fun a -> a.[1..])
else
"0.0.0.0"
|





znz #9039() Rating3/3=1.00
などのようにネットマスクの数値への変換を作ってみてください。
出来れば逆変換も作ってみてください。
[ reply ]