validateMask is a subroutine that takes a dotted-decimal IP mask (a.k.a. netmask) and checks to see if the mask is logical. Returns 1 on success, undef otherwise.

UPDATE: totally rewrote the thing under license from TIMTOWTDI. Instead of doing things in decimal, I convert the mask to the 32-bit string, then look to see if there is a zero followed by a one. Takes care of most identified concerns.

UPDATE 1: okay ... took out the Socket requirement ... THX to whomever pointed this out ...

UPDATE 2: Threw the $mask var back in ... can't remember why I started playing w/ that

sub validateMask { return # return undef if it's not com +plete unless $_[0] =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; my $mask = unpack ( "B32", pack( "C4", split ( /\./, $_[0] ) ) ); return # return undef if we find an i +nvalid if $mask =~ /01/; # mask ( a zero followed by a +one ) return 1; # return 1 if everything is ok +ay }

Replies are listed 'Best First'.
Re: validateMask (regex tweak, 30bit mask, CPAN modules)
by ybiC (Prior) on May 09, 2001 at 02:02 UTC
    Code below shows my minor tweaks, idnopheq, to your neat snippet.

    Line 12 rejects $mask if any octet(s) are other than 1 to 3 digits.   Somewhat redundant, since you already check for > 255 at line 20.

    Line 22y3 allows the sub to pass the valid octet of 252, which happens to not be divisable by 8.   Although you may have left that out on purpose, since a 30bit mask would normally be used only with point-to-point WAN links.

    I've not used them yet myself, but modules like Net::Netmask, Net::IPv4Addr, Network::IPv4Addr, NetAddr::IP, and Tie::NetAddr::IP might be worth looking at.
        cheers,
        Don
        striving toward Perl Adept
        (it's pronounced "why-bick")

    1: # sub validateMask 2: # 3: # takes a dotted-decimal IP mask 4: # 5: # returns 1 if the mask is valid 6: # returns undef if not 7: # 8: 9: sub validateMask { 10: my $mask = shift; 11: my $net = 0; 12: return 12: unless $mask =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; 12y: # accept only 1 to 3 digit octe +ts in above regex 14: foreach ( split ( /\./, $mask ) ) { 15: if ( 16: $net == 1 17: && 18: $_ != 0 19: ) { return; } 20: if ( $_ > 255 ) { return; } 21: if ( $_ == 255 ) { next; } 22: if ( 22y1: $_ % 8 != 0 # if an octet mod 8 isn't 0, + bad mask 22y2: && 22y3: $_ != 252 # 255 is valid octet, but oc +tet mod 8 isn't 0 22y4: ) { return; } 23: $net = 1; 24: next; 25: } 26: return 1; 27: }
      THX for the comments ... I rewrote the validateMask sub, including your

      return unless $mask =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;

      My new version is far simpler than my original post, using Socket, unpack, and join to give a 32 character binary string, then searching for the pattern /01/. If we find it, bad mask. Everything else then must be good ( unless someone finds a fatal flaw here, too! :b ).

      I wasn't initially that excited about requiring Socket, but then I figured this will likely be used (if at all) in a networking script, so Socket.pm is ok.


      --
      idnopheq
      Apply yourself to new problems without preparation, develop confidence in your ability to to meet situations as they arrise.

Re: validateMask
by Petruchio (Vicar) on May 13, 2001 at 07:33 UTC
    Consider the following program which calls your subroutine:

    my $x = '255.0.0.0'; print "$x is a valid mask!\n" if validateMask($x); sub validateMask { return # return undef if it's not com +plete unless $_[0] =~/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; $_[0] = unpack ( "B32", pack( "C4", split ( /\./, $_[0] ) ) ); return # return undef if we find an i +nvalid if $_[0] =~ /01/; # mask ( a zero followed by a +one ) return 1; # return 1 if everything is ok +ay }

    It returns the following:

    11111111000000000000000000000000 is a valid mask!

    Which isn't exactly what one might expect. Similarly, if I say

    print "255.0.0.0 is a valid mask!\n" if validateMask('255.0.0.0');
    I get the error:
    Modification of a read-only value attempted at ./idnopheq.pl line 5.

    The problem is, parameters to subroutines in Perl are passed by alias; which is to say, $_[0] is just another name for $x in the former program, and a string literal in the latter. For this reason, it's a good idea to assign the values of any members of @_ to other variables as soon as you begin your subroutine, unless you really want to modify the original.

    For instance, ybiC says

    my $mask = shift;

    and then works with the variable $mask, which will cease to exist once he leaves validateMask. Another option is to use $_, which can make your statements more succinct (and, sometimes, harder to read).

    sub validateMask { $_ = shift; return unless /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; unpack('B32', pack('C4', split /\./)) =~ /01/ ? undef : 1; }

    Hope this helps.

Re: validateMask
by no_slogan (Deacon) on May 09, 2001 at 02:59 UTC
    Why does octet mod 8 have to be 0? Don't you like /23 subnets? You don't check to make sure that the octets consists of contiguous one bits followed by contiguous zeros - you can do that by checking to see if: (~$octet & -$octet & 255) == 0

    Update:To clarify, the $net business tracks continuous one bits between octets, but not within a single octet.

      Well, about the valid subnets I left out ... I guess I'm just prejudiced. HHOJ.

      But seriously, folks, I was going of the top of my head for masks, and missed some. I thought I was ~so~ crafty! As I reflected later while drooling on a table in a meeting, it would be better to test by powers of 2, maybe ... I still have crusty residue on the corners of my mouth.

      For contiguous ones followed by contiguous zeros, I do test, actually. That's what $net tracks ... if we get an octet other than 255 or 0, set it to one. If any of the subsequent octets have a value other than 0, it's no good.

      I think I like your solution better. I'll play w/ it.

      Thanks to all two of you who responded thus far!

      HTH
      --
      idnopheq
      Apply yourself to new problems without preparation, develop confidence in your ability to to meet situations as they arrise.