Author Message

logic:
2. and an address [(a) 192.9.111.1 OR (b) 192.9.111.50]
3. determine if (a) belongs to a subnet specified in 1. [whereas (b)
does NOT]

Say if I have a file subnets.txt
192.9.111.0|255.255.255.224
192.9.111.0|255.255.255.192

(a) will be validated by the FIRST record/row
(b) will be validated by the SECOND record/row

Anyone have any leads/tips or snippets I could elaborate on?!

Sat, 07 May 2005 20:51:33 GMT

Quote:
> logic:
>    1. given a subnet address and a mask [192.9.111.0 and 255.255.255.224]
>    2. and an address [(a) 192.9.111.1 OR (b) 192.9.111.50]
>    3. determine if (a) belongs to a subnet specified in 1. [whereas (b)
>           does NOT]

> Say if I have a file subnets.txt
> 192.9.111.0|255.255.255.224
> 192.9.111.0|255.255.255.192

> (a) will be validated by the FIRST record/row
> (b) will be validated by the SECOND record/row

> Anyone have any leads/tips or snippets I could elaborate on?!

Life is easier with gawk and bitwise boolean operators.  Do you have gawk?
Otherwise, seach Google groups for comp.lang.awk "bitwise" for some
solutions to that sub-problem.

You could use FS = "|" and split(ip,ipQuads,".") to break your data lines.

You could use
/^[ \t]*#/ {next}
to skip your "#" comment lines.

- Dan

Sat, 07 May 2005 21:36:28 GMT

Quote:

>logic:
>   1. given a subnet address and a mask [192.9.111.0 and 255.255.255.224]
>   2. and an address [(a) 192.9.111.1 OR (b) 192.9.111.50]
>   3. determine if (a) belongs to a subnet specified in 1. [whereas (b)
>          does NOT]

>Say if I have a file subnets.txt
>192.9.111.0|255.255.255.224
>192.9.111.0|255.255.255.192

>(a) will be validated by the FIRST record/row
>(b) will be validated by the SECOND record/row

>Anyone have any leads/tips or snippets I could elaborate on?!

I usually do this by converting addresses to numbers:

function iptonum(ip,
ipn, n, i) {
split(ip, ipn, ".")
n = 0
for(i = 1; i <= 4; i++) n = n * 256 + ipn[i]
return n
}

The converse (although not needed for this job) is:

function numtoip(n,
ip, i) {
ip = ""
for(i = 4; i >= 1; i--) {
ip = n % 256 "." ip
n = int(n / 256)
}
return substr(ip, 1, length(ip) - 1)
}

The next trick is that the number of hosts specified by as given mask is
2^32 - mask value, so the number of hosts specified by 255.255.255.224 is:

2^32 - iptonum("255.255.255.224")

From there it's a simple matter to implement what you want:

BEGIN {
ips = 0
oldFS = FS
FS="|"                                # Field sep id |
while((getline < "subnets.txt") > 0) {
if(!NF) continue        # ignore blank lines
ips++
iplo[ips] = iptonum(\$1)
iphi[ips] = iplo[ips] + (2^32 = iptonum(\$2))
}
close("subnets.txt")
FS = oldFS
}

{
n = iptonum(\$1)
for(i = 1; i <= ips; i++) {
if(n >= iplo[i] && n < iphi[i]) {
print \$1, "valid"
break
}
}
if(i > ips) print \$1, "invalid"
}

Who the heck needs bitwise ops?

-- don

Sun, 08 May 2005 15:32:04 GMT

Quote:

> >logic:
> >   1. given a subnet address and a mask [192.9.111.0 and 255.255.255.224]
> >   2. and an address [(a) 192.9.111.1 OR (b) 192.9.111.50]
> >   3. determine if (a) belongs to a subnet specified in 1. [whereas (b)
> >          does NOT]

> >Say if I have a file subnets.txt
> >192.9.111.0|255.255.255.224
> >192.9.111.0|255.255.255.192

> >(a) will be validated by the FIRST record/row
> >(b) will be validated by the SECOND record/row

> >Anyone have any leads/tips or snippets I could elaborate on?!

> I usually do this by converting addresses to numbers:

> function iptonum(ip,
> ipn, n, i) {
> split(ip, ipn, ".")
> n = 0
> for(i = 1; i <= 4; i++) n = n * 256 + ipn[i]
> return n
> }

> The converse (although not needed for this job) is:

> function numtoip(n,
> ip, i) {
> ip = ""
> for(i = 4; i >= 1; i--) {
> ip = n % 256 "." ip
> n = int(n / 256)
> }
> return substr(ip, 1, length(ip) - 1)
> }

> The next trick is that the number of hosts specified by as given mask is
> 2^32 - mask value, so the number of hosts specified by 255.255.255.224 is:

> 2^32 - iptonum("255.255.255.224")

> From there it's a simple matter to implement what you want:

> BEGIN {
> ips = 0
> oldFS = FS
> FS="|" # Field sep id |
> while((getline < "subnets.txt") > 0) {
> gsub(/#.*/, "") # remove comments
> if(!NF) continue # ignore blank lines
> ips++
> iplo[ips] = iptonum(\$1)
> iphi[ips] = iplo[ips] + (2^32 = iptonum(\$2))
> }
> close("subnets.txt")
> FS = oldFS
> }

> {
> n = iptonum(\$1)
> for(i = 1; i <= ips; i++) {
> if(n >= iplo[i] && n < iphi[i]) {
> print \$1, "valid"
> break
> }
> }
> if(i > ips) print \$1, "invalid"
> }

> Who the heck needs bitwise ops?

> -- don

Hi, Don -

I was wondering if you tested your code.  In particular, I'm wondering about
the clause
(2^32 = iptonum(\$2))
which looks like an assignment into an expression to me.  (And I sure hate
it when news clients strip code indentation!)

Quote:
> Who the heck needs bitwise ops?

Well, as you've illustrated, not many people NEED bitwise ops.  But here is
an example of how you could use them to your advantage.  Like you, I convert
the dotted-quads to a four-byte integer.

(BTW, your code and mine both fail to duplicate a common "bug" in most IP
code:  that is, we both convert ".010." to 10.  Microsoft's Win2K IP code
(which undoubtedly traces its heritage to Berkely or before) uses a C
library function that sees the leading zero as an indicator that the rest of
the string is to be interpretted as octal.  So the machine at 192.168.1.8
replies to 'ping 192.168.001.010'.)

The CIDR conversion uses the lshift() operation, and the IP comparison
relies on a simple validation for IP-on-subnet, basically
v = I & M == N

Finally, vgersh99 (the OP) seemed to imply that the validation was all that
was required.  Your 'break' does this.  I opted to list all of the subnets a
particular IP could belong to, because it occurred to me that vgersh99's
sample, 192.9.111.1, is valid on both the subnets he presented, and if I
were scanning a network, I might want to know about multiple subnet
membership.

- Dan

validate.awk
----------
BEGIN {
FS = "[.|]"
while (getline < "subnets.txt" > 0) {
if (/^[ \t]*#/) continue
net[++c] = \$4+256*(\$3+256*(\$2+256*\$1))
for (cidr = 0; m > 0; m = lshift(m,1)) cidr++
subnet[c] = gensub(/\|.*\$/,"/" cidr,1)
}
}
{
err = " invalid"
printf "%s:", \$0
for (i = 1; i <= c; i++)
printf " " subnet[i]
err = ""
}
print err
}

subnets.txt
----------
192.9.111.0|255.255.255.224
192.9.111.0|255.255.255.192
172.16.0.0|255.240.0.0
172.22.0.0|255.255.0.0

----------
192.9.111.1
192.9.111.50
192.12.111.50
192.8.121.12
172.16.4.2
172.18.0.1
172.48.4.4
172.22.75.251
172.23.75.251

(output) (gawk 3.1.0/Win98)
----------
192.9.111.1: 192.9.111.0/27 192.9.111.0/26
192.9.111.50: 192.9.111.0/26
192.12.111.50: invalid
192.8.121.12: invalid
172.16.4.2: 172.16.0.0/12
172.18.0.1: 172.16.0.0/12
172.48.4.4: invalid
172.22.75.251: 172.16.0.0/12 172.22.0.0/16
172.23.75.251: 172.16.0.0/12

Mon, 09 May 2005 14:20:55 GMT

Quote:

>I was wondering if you tested your code.  In particular, I'm wondering about
>the clause
>    (2^32 = iptonum(\$2))
>which looks like an assignment into an expression to me.  (And I sure hate
>it when news clients strip code indentation!)

Whaddaya want for \$0?  Tested code?

(I tested the bits, the final edit probably skipped a bit.)

Quote:
>Well, as you've illustrated, not many people NEED bitwise ops.  But here is
>an example of how you could use them to your advantage.  Like you, I convert
>the dotted-quads to a four-byte integer.

I guess I was demonstrating a more general approach; the 2^32 - mask
technique allows you to do things like:

# ping all addresses in a subnet
for(ip = addr + 1; ip < addr + size - 1; ip++) # skip net & broadcast
system("ping -c 1 " numtoip(ip))

Quote:
>(BTW, your code and mine both fail to duplicate a common "bug" in most IP
>code:  that is, we both convert ".010." to 10.  Microsoft's Win2K IP code
>(which undoubtedly traces its heritage to Berkely or before) uses a C
>library function that sees the leading zero as an indicator that the rest of
>the string is to be interpretted as octal.  So the machine at 192.168.1.8
>replies to 'ping 192.168.001.010'.)

Yeah.  The Unix inet_addr() function suffers this flaw, which means
basically everything that can take an IP address.  It's easy enough to
duplicate using the strtonum() function in GAWK 3.1:

function iptonum(ip,
ipn, n, i) {
split(ip, ipn, ".")
n = 0
for(i = 1; i <= 4; i++)
n = n * 256 + strtonum(ipn[i])
return n
}

In fact there are a few subtleties in inet_addr(), e.g:

1               -> 0.0.0.1
1.2             -> 1.0.0.2
1.2.3           -> 1.2.0.3
1000000         -> 0.15.66.64
0xa.013.12.13   -> 10.11.12.13

I think this is horrible unnecessary bug food, but who am I to argue
against 20 years of IP development on Unix boxen?  Again, it's not that
hard to implement using strtonum(), but I don't feel like it right now.

-- don

Mon, 09 May 2005 17:30:19 GMT
Hi,

Quote:

> Whaddaya want for \$0?  Tested code?

the whole record, of course.

Stepan

Mon, 09 May 2005 19:31:45 GMT

 Page 1 of 1 [ 6 post ]

Relevant Pages