shell实现netmask掩码和cidr掩码位转换

在写一个脚本时需要实现掩码位和掩码之间的转换,想简单的通过shell 实现,在openwrt程序上刚好有此脚本,内容如下:



<br />
#!/bin/bash
# code from www.361way.com
mask2cdr ()
{
   # Assumes there's no "255." after a non-255 byte in the mask
   local x={1##*255.}
   set -- 0^^^128^192^224^240^248^252^254^(( ({#1} -{#x})*2 )) {x%%.*}
   x={1%%3*}
   echo(( 2 + ({#x}/4) ))
}
cdr2mask ()
{
   # Number of args to shift, 255..255, first non-255 byte, zeroes
   set -- (( 5 - (1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
   [ $1 -gt 1 ] && shift $1 || shift
   echo ${1-0}.${2-0}.${3-0}.${4-0}
}
# examples:
mask2cdr 255.255.255.0
cdr2mask 24
上面的代码看起来比较玄妙,其具体解释可以看下英文解释。



<br />

mask2cdr()

To get the CIDR prefix from a dot-decimal netmask like this one:



<br />
255.255.192.0
<br />



you first have to convert the four octets to binary and then count the most significant bits (i.e. the number of leading ones):



<br />
11111111.11111111.11000000.00000000 # 18 ones = /18 in CIDR
<br />



This function does that rather creatively. First, we strip off all of the leading&nbsp;255&nbsp;octets (i.e. the octets that are all ones in binary) and store the results in variable&nbsp;x:



<br />
local x=${1##*255.}
<br />



This step uses&nbsp;<a href="http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html">parameter expansion</a>, which the entire script relies on pretty heavily. If we continue with our example netmask of&nbsp;255.255.192.0, we now have the following values:



<br />
$1: 255.255.192.0 $x: 192.0
<br />



Next we set three variables:&nbsp;1,&nbsp;2, and&nbsp;$3. These are called&nbsp;<a href="http://www.gnu.org/software/bash/manual/bashref.html#Positional-Parameters">positional parameters</a>; they are much like ordinary named variables but are typically set when you pass arguments to a script or function. We can set the values directly using&nbsp;set --, for example:



<br />
set -- foo bar # 1 = foo,2 = bar
<br />



I prefer using named variables over positional parameters since it makes scripts easier to read and debug, but the end result is the same. We set&nbsp;$1&nbsp;to:



<br />
0^^^128^192^224^240^248^252^254^
<br />



This is really just a table to convert certain decimal values to binary and count the number of&nbsp;1&nbsp;bits. We'll come back to this later.



We set&nbsp;$2&nbsp;to



<br />
$(( (${#1} - ${#x})*2 ))
<br />



This looks complex, but it is really just counting the number of&nbsp;1&nbsp;bits we stripped off in the first command. It breaks down to this:



<br />
(number of chars in 1 - number of chars inx) * 2
<br />



which in our case works out to



<br />
(13 - 5) * 2 = 16
<br />



We stripped off two octets so we get 16. Makes sense.



We set&nbsp;$3&nbsp;to:



<br />
${x%%.*}
<br />



which is the value of&nbsp;$x&nbsp;with everything after the first&nbsp;.&nbsp;stripped off. In our case, this is&nbsp;192.



We need to convert this number to binary and count the number of&nbsp;1&nbsp;bits in it, so let's go back to our "conversion table." We can divide the table into equal chunks of four characters each:



<br />
0^^^ 128^ 192^ 224^ 240^ 248^ 252^ 254^
<br />



In binary, the above numbers are:



<br />
00000000 10000000 11000000 11100000 11110000 11111000 11111100 11111110
# 0 ones 1 one    2 ones   3 ones   ...
<br />



If we count from the left, each four-character block in the table corresponds to an additional&nbsp;1&nbsp;bit in binary. We're trying to convert&nbsp;192, so let's first lop off the rightmost part of the table, from&nbsp;192&nbsp;on, and store it in&nbsp;x:



<br />
x={1%%3*}
<br />



The value of&nbsp;$x&nbsp;is now



<br />
0^^^128^
<br />



which contains two four-character blocks, or two&nbsp;1&nbsp;bits in binary.



Now we just need to add up the&nbsp;1&nbsp;bits from our leading&nbsp;255&nbsp;octets (16 total, stored in variable&nbsp;$2) and the&nbsp;1&nbsp;bits from the previous step (2 total):



<br />
echo ((2 + (${#x}/4) ))
<br />



where
${#x}/4
is the number of characters in&nbsp;x&nbsp;divided by four, i.e. the number of four-character blocks in&nbsp;x.

Output:

<br />
18
<br />

cdr2mask()

Let's keep running with our previous example, which had a CIDR prefix of&nbsp;18.



We use&nbsp;set --&nbsp;to set positional parameters 1 through9:



<br />
$1: $(( 5 - ($1 / 8) ))  # 5 - (18 / 8) = 3 [integer math]
$2: 255
$3: 255
$4: 255
$5: 255
$6: $(( (255 << (8 - ($1 % 8))) & 255 ))  # (255 << (8 - (18 % 8))) & 255 = 192
$7: 0
$8: 0
$9: 0
<br />



Let's examine the formulas used to set&nbsp;1&nbsp;and&nbsp;6&nbsp;a little closer.&nbsp;$1&nbsp;is set to:



<br />
$(( 5 - ($1 / 8) ))
<br />



The maximum and minimum possible values for a CIDR prefix are 32 for netmask



<br />
11111111.11111111.11111111.11111111
<br />



and 0 for netmask



<br />
00000000.00000000.00000000.00000000
<br />



The above formula uses integer division, so the possible results range from 1 to 5:



<br />
5 - (32 / 8) = 1
5 - ( 0 / 8) = 5
<br />



$6&nbsp;is set to:



<br />
$(( (255 << (8 - ($1 % 8))) & 255 ))
<br />



Let's break this down for our example CIDR prefix of&nbsp;18. First we take the modulus and do some subtraction:



<br />
8 - (18 % 8) = 6
<br />



Next we bitwise shift 255 by this value:



<br />
255 << 6
<br />



This is the same as pushing six&nbsp;0&nbsp;bits onto the end of 255 in binary:



<br />
11111111000000
<br />



Finally, we bitwise AND this value with 255:



<br />
11111111000000 &
00000011111111  # 255
<br />



which gives



<br />
00000011000000
<br />



or simply



<br />
11000000
<br />



Look familiar? This is the third octet in our netmask in binary:



<br />
11111111.11111111.11000000.00000000
                  ^------^
<br />



In decimal, the value is 192.



Next we shift the positional parameters based on the value of&nbsp;$1:



<br />
[ 1 -gt 1 ] && shift1 || shift
<br />



In our case, the value of&nbsp;1&nbsp;is 3, so we shift the positional parameters 3 to the left. The previous value of&nbsp;4&nbsp;becomes the new value of&nbsp;1, the previous value of&nbsp;5&nbsp;becomes the value of&nbsp;$2, and so on:



<br />
$1: 255
$2: 255
$3: 192
$4: 0
$5: 0
$6: 0
<br />



These values should look familiar: they are the decimal octets from our netmask (with a couple of extra zeros tacked on at the end). To get the netmask, we simply print out the first four with dots in between them:



<br />
echo {1-0}.{2-0}.{3-0}.{4-0}
<br />



The&nbsp;-0&nbsp;after each parameter says to use&nbsp;0&nbsp;as the default value if the parameter is not set.

Output:

<br />
255.255.192.0
<br />



<br />



<br />

shell实现netmask掩码和cidr掩码位转换》有1条评论

  1. 引入数组呢?(0 128 192 224 240 248 252 254) 0:0 ,128:1,192:2…….

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注