Newer
Older
NetAddr-IP / tutorial.htm
  1. <HTML>
  2. <HEAD>
  3. <TITLE>A tutorial for NetAddr::IP</TITLE>
  4. </HEAD><BODY>
  5.  
  6. <tt>$Id: tutorial.htm,v 1.3 2003/10/09 00:12:22 lem Exp $</tt>
  7.  
  8. <P><FONT SIZE="small">This tutorial is kept online at <A
  9. HREF="http://mipagina.cantv.net/lem/perl/iptut.htm">this</A>
  10. location. Please see the online version if you can, as it is probably
  11. more up to date...</FONT></P>
  12.  
  13. <a name="about"><h2>What this tutorial is all about...</h2></a>
  14.  
  15. <p>Some of us, monks who love Perl, also have to deal with the
  16. complexities of IP addresses, subnets and such. A while ago, I wrote <a
  17. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  18. to help me work out tedious tasks such as finding out which addresses
  19. fell within a certain subnet or allocating IP space to network
  20. devices.
  21.  
  22. <p>This tutorial discusses many common tasks along with solutions
  23. using <a
  24. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>.
  25. Since Perl lacks a native type to represent either an IP address or an
  26. IP subnet, I feel this module has been quite helpful for fellow monks
  27. who like me, need to work in this area.
  28.  
  29. <p>Note however that neither the module itself nor this tutorials are
  30. intended as replacements to your knowledge about how to work with
  31. chunks of IP space. The module was written as a tool to help with the
  32. boring tasks (after all, we're plentiful with good laziness, aren't
  33. we?) and this tutorial, was written to help answer the most common
  34. questions I get. Both the module and this tutorial expect you to be
  35. fluent in basic networking and somewhat fluent in Perl. You should not
  36. writing Perl code to manage your subnetting otherwise.
  37.  
  38. <hr>
  39.  
  40. <a name="new"><h2>Specifying an IP Address or a subnet</h2></a>
  41.  
  42. <p>A <a
  43. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  44. object represents a subnet. This involves storing an IP address
  45. <b>within</b> the subnet along with the subnet's netmask. Of course,
  46. using a host netmask (/32 or in decimal notation, 255.255.255.255)
  47. allows for the specification of a single IP address.
  48.  
  49. <p>You can create a <a
  50. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  51. object with an incantation like the following:
  52.  
  53. <PRE>
  54. use NetAddr::IP;
  55. my $ip = new NetAddr::IP '127.0.0.1';
  56. </PRE>
  57.  
  58. <p>which will create an object representing the 'address'
  59. <tt>127.0.0.1</tt> or the 'subnet' <tt>127.0.0.1/32</tt>.
  60.  
  61. <p>Creating a subnet is equally easy. Just specify the address and
  62. netmask in almost any common notation, as in the following examples:
  63.  
  64. <PRE>
  65. use NetAddr::IP;
  66. my $loopback = new NetAddr::IP '127.0.0.1', '255.0.0.0';
  67. my $rfc1918 = new NetAddr::IP '10.0.0.0/8';
  68. my $another = new NetAddr::IP '1.2.0.0/255.255.0.0';
  69. my $loopback2 = new NetAddr::IP 'loopback';
  70. </PRE>
  71.  
  72. <p>The following is a list of the acceptable arguments to
  73. <tt>->new()</tt> and their meanings:
  74.  
  75. <ul>
  76. <li><tt>->new('broadcast')</tt>
  77.  
  78. <p>Equivalent to the address <tt>255.255.255.255/32</tt> which is often
  79. used to denote a broadcast address.
  80.  
  81. <li><tt>->new('default')</tt>
  82.  
  83. <p>Synonim to the address <tt>0.0.0.0/0</tt> which is universally used
  84. to represent a default route. This subnet is guaranteed to
  85. <tt>->contains()</tt> any other subnet. More on that later.
  86.  
  87. <p>For the benefit of many Cisco users out there, <tt>any</tt> is
  88. considered a synonim of <tt>default</tt>.
  89.  
  90. <li><tt>->new('loopback')</tt>
  91.  
  92. <p>The same as the address <tt>127.0.0.1/8</tt> which is the standard
  93. <tt>loopback</tt> address.
  94.  
  95. <li><tt>->new('10.10.10.10')</tt> or <tt>->new('foo.bar.com')</tt>
  96.  
  97. <p>This represents a single host. When no netmask is supplied, a netmask
  98. of <tt>/32</tt> is assumed. When supplying a name, the host name will
  99. be looked up using <a
  100. href="http://www.perldoc.com/perl5.6.1/pod/perlfunc.html">gethostbyname()</a>,
  101. which will in turn use whatever name resolution is configured in your
  102. system to obtain the IP address associated with the supplied name.
  103.  
  104. <li><tt>->new('10.10.1')</tt>
  105.  
  106. <p>An ancient notation that allows the <em>middle zeroes</em> to be
  107. skipped. The example is equivalent to <tt>->new('10.10.0.1')</tt>.
  108.  
  109. <li><tt>->new('10.10.1.')</tt>
  110.  
  111. <p>Note the trailing dot. This format allows the omission of the
  112. netmask for classful subnets. The example is equivalent to
  113. <tt>->new('10.10.1.0/24')</tt>.
  114.  
  115. <li><tt>->new('10.10.10.0 - 10.10.10.255')</tt>
  116.  
  117. <p>This is also known as <em>range notation</em>. Both ends of an
  118. address range are specified. Note that this notation is only supported
  119. if the specified subnet can be represented in valid CIDR notation.
  120.  
  121. <li><tt>->new('10.10.10.0-255')</tt>
  122.  
  123. <p>This notation is a shorthand for the <em>range notation</em>
  124. discussed above. It provides for the specification of an address range
  125. where both of its ends share the first octets. This notation is only
  126. supported when the specified range of hosts defined a proper CIDR
  127. subnet.
  128.  
  129. <li><tt>->new(1024)</tt>
  130.  
  131. <p>Whenever the address is specified as a numeric value greater than
  132. 255, it is assumed to contain an IP address encoded as an unsigned int.
  133.  
  134.  
  135. <li><tt>->new()</tt> with two arguments
  136.  
  137. <p>Whenever two arguments are specified to <tt>->new()</tt>, the first
  138. is always going to be interpreted as the IP address and the second
  139. will always be the netmask, in any of the formats discussed so far.
  140.  
  141. <p>Netmasks can be specified in dotted-quad notation, as the number of
  142. one-bits or as the equivalent unsigned int. Also, special keywords
  143. such as <tt>broadcast</tt>, <tt>default</tt> or <tt>host</tt> can be
  144. used as netmasks.
  145.  
  146. </ul>
  147.  
  148. <p>The semantics and notations depicted above, are supposed to comply
  149. strictly with the DWIM approach which is so popular with Perl. The
  150. general idea is that you should be able to stick almost anything
  151. resembling an IP address or a subnet specification into the
  152. <tt>->new()</tt> method to get an equivalent object. However, if you
  153. can find another notation that is not included in the above list,
  154. please by all means let me know.
  155.  
  156. <hr>
  157.  
  158. <a name="overloading"><h2>Simple operations with subnets</h2></a>
  159.  
  160. <P>There is a number of operations that have been simplified along the
  161. different versions of the module. The current version, as of this
  162. writing, provides support for the following operations:
  163.  
  164. <ul>
  165. <li>Scalarization
  166.  
  167. <p>A <a
  168. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  169. object will become its CIDR representation whenever a scalar
  170. representation for it is required. For instance, you can very well do
  171. something like <tt>print "My object contains $ip\n";</tt>.
  172.  
  173. <li>Numerical comparison
  174.  
  175. <p>Two objects can be compared using any of the numerical comparison
  176. operators. Only the address part of the subnet is compared. The
  177. netmask is ignored in the comparison.
  178.  
  179. <li>Increments and decrements
  180.  
  181. <p>Adding or substracting a scalar from an object will change the
  182. address in the subnet, but always keeping it within the subnet. This
  183. is very useful to write loops, like the following:
  184.  
  185. <PRE>
  186. use NetAddr::IP;
  187. my $ip = new NetAddr::IP('10.0.0.0/30');
  188. while ($ip < $ip->broadcast) {
  189. print "ip = $ip\n";
  190. $ip ++;
  191. }
  192. </PRE>
  193.  
  194. <p>which will produce the following output:
  195.  
  196. <PRE>
  197. ip = 10.0.0.0/30
  198. ip = 10.0.0.1/30
  199. ip = 10.0.0.2/30
  200. </PRE>
  201.  
  202. <li>List expansion
  203.  
  204. <p>When required, a <a
  205. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  206. will expand automatically to a list containing all the addresses
  207. within a subnet, conveniently leaving the subnet and the broadcast
  208. addresses out. The following code shows this:
  209.  
  210. <PRE>
  211. use NetAddr::IP;
  212. my $ip = new NetAddr::IP('10.0.0.0/30');
  213. print join(' ', @$ip), "\n";
  214. </PRE>
  215.  
  216. <p>And the output would be
  217.  
  218. <PRE>
  219. 10.0.0.1/32 10.0.0.2/32
  220. </PRE>
  221.  
  222. </ul>
  223.  
  224. <hr>
  225.  
  226. <a name="common"><h2>Common (and not so common) tasks</h2></a>
  227.  
  228. <p>Below I will try to provide an example for each major feature of <a
  229. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>,
  230. along with a description of what is being done, where appropiate.
  231.  
  232. <a name="compact"><h3>Optimising the address space</h3></a>
  233.  
  234. <p>This is one of the reason for writing <a
  235. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  236. in the first place. Let's say you have a few chunks of IP space and
  237. you want to find the <em>optimum</em> CIDR representation for
  238. them. By optimum, I mean the least amount of CIDR subnets that exactly
  239. represent the given IP address space. The code below is an example of
  240. this:
  241.  
  242. <PRE>
  243. use NetAddr::IP;
  244.  
  245. push @addresses, NetAddr::IP->new($_) for &lt;DATA>;
  246. print join(", ", NetAddr::IP::compact(@addresses)), "\n";
  247. __DATA__
  248. 10.0.0.0/18
  249. 10.0.64.0/18
  250. 10.0.192.0/18
  251. 10.0.160.0/19
  252. </PRE>
  253.  
  254. <p>Which will, of course, output <tt>10.0.0.0/17, 10.0.160.0/19,
  255. 10.0.192.0/18</tt>. Let's see how this is done...
  256.  
  257. <p>First, the line starting with <tt>push ...</tt> creates a list of
  258. objects representing all the subnets read in via the
  259. <tt>&lt;DATA&gt;</tt> filehandle. There should be no surprises here.
  260.  
  261. <p>Then, we call <tt>NetAddr::IP::compact</tt> with the list of
  262. subnets build earlier. This function accepts a list of subnets as its
  263. input (actually, an array of objects). It processes them internally
  264. and outputs another array of objects, as summarized as possible.
  265.  
  266. <p>Using <tt>compact()</tt> as in the example is fine when you're
  267. dealing with a few subnets or are writing a throw-away one-liner. If
  268. you think your script will be handling more than a few tens of
  269. subnets, you might find <tt>compactref()</tt> useful. It works exactly
  270. as shown before, but takes (and returns) references to arrays. I've
  271. seen 10x speed improvements when working with huge lists of subnets.
  272.  
  273. <p>Something that gets asked quite frequently is <em>"why not
  274. <tt>@EXPORT</tt> or at least, <tt>@EXPORT_OK</tt> methods such as
  275. <tt>compact()</tt>?"</em>. The answer is that I believe
  276. <tt>compact()</tt> to be a very generic name, for an operation that
  277. is not always used. I think fully qualifying it, adds to the mnemonics
  278. of what's being done while not polluting the namespace innecesarilly.
  279.  
  280. <hr>
  281.  
  282. <a name="split"><h3>Assigning address space</h3></a>
  283.  
  284. <p>This problem can be tought as the complement to the prior
  285. one. Let's say a couple of network segments need to be connected to
  286. your network. You can carve slices out of your address space easily,
  287. such as in the following code:
  288.  
  289. <PRE>
  290. use NetAddr::IP;
  291.  
  292. print "My address space contains the following /24s:\n",
  293. join("\n", NetAddr::IP->new('10.0.0.0/22')->split(24)), "\n";
  294. </PRE>
  295.  
  296. <p>Which will divide your precious address space (the one specified in
  297. the <tt>NetAddr::IP->new()</tt>) in subnets with a netmask of 24
  298. bytes. This magic is accomplished by the <tt>->split()</tt> method,
  299. which takes the number of bits in the mask as its only parameter. It
  300. returns a list of subnets contained in the original object.
  301.  
  302. <p>Again, in situations where the split might return a large number of
  303. subnets, you might prefer the use of <tt>->splitref()</tt>, which
  304. returns a reference to an array instead.
  305.  
  306. <p>Returning to our example, you might assign a /24 to each new
  307. subnet. Ok, perhaps assigning a /24 is not that good an example, as
  308. this falls on an octet boundary but trust me, when you have to split a
  309. /16 in /20s, to be allocated in chunks of /22s in a network spanning
  310. the whole country, it's nice to know your subnetting is well done.
  311.  
  312. <hr>
  313.  
  314. <a name="wildcard"><h3>Cisco's wildcard notation (and other dialects)</h3></a>
  315.  
  316. <p>Those of you who have had to write an ACL in a Cisco router, know
  317. about the joys of this peculiar format in which the netmask works the
  318. opposite of what custom says.
  319.  
  320. <p>An easy way to convert between traditional notation and Cisco's
  321. wildcard notation, is to use the eloquently named
  322. <tt>->wildcard()</tt> method, as this example shows:
  323.  
  324. <PRE>
  325. use NetAddr::IP;
  326.  
  327. print join(' ', NetAddr::IP->new('10.0.0.0/25')->wildcard());
  328. </PRE>
  329.  
  330. <p>As you might have guessed, <tt>->wildcard()</tt> returns an array
  331. whose first element is the address and its second element is the
  332. netmask, in wildcard notation. If scalar context is forced using
  333. <tt>scalar</tt>, only the netmask will be returned, as this is most
  334. likely what you want.
  335.  
  336. <p>In case you wonder, the example outputs <tt>10.0.0.0
  337. 0.0.0.127</tt>.
  338.  
  339. <p>Just for the record, below is a number of outputs from different
  340. methods for the above example:
  341.  
  342. <ul>
  343. <li>Range (The <tt>->range()</tt> method)
  344.  
  345. <p>Outputs <tt>10.0.0.0 - 10.0.0.127</tt>. Note that this range goes
  346. from the <em>network address</em> to the <em>broadcast
  347. address</em>.
  348.  
  349. <li>CIDR notation (The <tt>->cidr()</tt> method)
  350.  
  351. <p>As expected, it outputs <tt>10.0.0.0/25</tt>.
  352.  
  353. <li>Prefix notation (The <tt>->prefix()</tt> method)
  354.  
  355. <p>Similar to <tt>->range()</tt>, this method produces
  356. <tt>10.0.0.1-127</tt>. However, note that the first address is
  357. <b>not</b> the network address but the first host address.
  358.  
  359. <li><em>n</em>-Prefix notation (The <tt>->nprefix()</tt> method)
  360.  
  361. <p>Produces <tt>10.0.0.1-126</tt>. Note how the broadcast address is
  362. not within the range.
  363.  
  364. <li>Numeric (The <tt>->numeric()</tt> method)
  365.  
  366. <p>In scalar context, produces and unsigned int that represents the
  367. address in the subnet. In array context, both the address and netmask
  368. are returned. For the example, the array output is <tt>(167772160,
  369. 4294967168)</tt>. This is very useful when serializing the object for
  370. storage. You can pass those two numbers back to <tt>->new()</tt> and
  371. get your object back.
  372.  
  373. <li>Just the IP address (The <tt>->addr()</tt> method)
  374.  
  375. <li>Just the netmask as a dotted quad (The <tt>->mask()</tt> method)
  376.  
  377. <li>The length in bits of the netmask (The <tt>->masklen()</tt> method)
  378.  
  379. </ul>
  380. <hr>
  381.  
  382. <a name="contains"><h3>Matching against your address space</h3></a>
  383.  
  384. <p>Let's say you have a log full of IP addresses and you want to know
  385. which ones belong to your IP space. A simple way to achieve this is
  386. shown below:
  387.  
  388. <PRE>
  389. use NetAddr::IP;
  390.  
  391. my $space = new NetAddr::IP->new('10.128.0.0/17');
  392.  
  393. for my $ip (map { new NetAddr::IP->new($_) } &lt;DATA>)
  394. {
  395. print $ip, "\n"
  396. if $space->contains($ip);
  397. }
  398.  
  399. __DATA__
  400. 172.16.1.1
  401. 172.16.1.5
  402. 172.16.1.11
  403. 172.16.1.10
  404. 172.16.1.9
  405. 172.16.1.3
  406. 172.16.1.2
  407. 172.16.1.7
  408. 172.16.1.4
  409. 172.16.1.1
  410. 10.128.0.1
  411. 10.128.0.12
  412. 10.128.0.13
  413. 10.128.0.41
  414. 10.128.0.17
  415. 10.128.0.19
  416. </PRE>
  417.  
  418. <p>This code will output only the addresses belonging to your address
  419. space, represented by <tt>$space</tt>. The only interesting thing here
  420. is the use of the <tt>->contains()</tt> method. As used in our
  421. example, it returns a true value if <tt>$ip</tt> is completely contained within
  422. the <tt>$space</tt> subnet.
  423.  
  424. <p>Alternatively, the condition could have been written as
  425. <tt>$ip->within($space)</tt>. Remember that TIMTOWTDI.
  426.  
  427. <hr>
  428.  
  429. <a name="iteration"><h3>Walking through the network without leaving
  430. the office</h3></a>
  431.  
  432. <p>Some of the nicest features of <a
  433. href="http://search.cpan.org/search?mode=module&query=NetAddr%3A%3AIP">NetAddr::IP</a>
  434. can be better put to use when you want to perform actions with your
  435. address space. Some of them are discussed below.
  436.  
  437. <p>One of the most efficient ways to walk your address space is
  438. building a <tt>for</tt> loop, as this example shows:
  439.  
  440. <PRE>
  441. use NetAddr::IP;
  442.  
  443. push @space, new NetAddr::IP->new($_) for &lt;DATA>;
  444.  
  445. for my $netblock (NetAddr::IP::compact @space)
  446. {
  447. for (my $ip = $netblock->first;
  448. $ip <= $netblock->last;
  449. $ip++)
  450. {
  451. # Do something with $ip
  452. }
  453. }
  454. __DATA__
  455. 10.0.0.0/16
  456. 172.16.0.0/24
  457. </PRE>
  458.  
  459. <p>The nicest thing about this way of walking your IP space, is that
  460. even if you are lucky enough to have lots of it, you won't eat all
  461. your memory by generating a huge list of objects. In this example,
  462. only one object is created in every iteration of the loop.
  463.  
  464. <p>Everything up to the inner loop should be pretty clear by now, so
  465. we just ignore it. Since a couple of new friends were introduced in
  466. the inner loop of our example, an explanation is in order.
  467.  
  468. <p>This C-like <tt>for</tt> loop uses the <tt>->first()</tt> function to
  469. find the first subnet address. The first subnet address is defined as
  470. that having all of its <em>host bits</em> but the rightmost set to
  471. zero and the rightmost, set to one.
  472.  
  473. <p>We then use the numerical comparison discussed earlier to see if
  474. the value of <tt>$ip</tt> is less than or equal to whatever
  475. <tt>->last()</tt> returns. <tt>->last()</tt> returns an address with
  476. all of its host bits set to one but the rightmost. If this condition
  477. holds, we execute the loop and post-increment <tt>$ip</tt> to get the
  478. next IP address in the subnet.
  479.  
  480. <p>I started the discussion on this topic with the approach that
  481. insures less wasted resources. However, in the purest Perl tradition,
  482. this is not the only way to do it. There's another way, reserved for
  483. the true lazy (or those with memory to burn, but we all know you never
  484. have enough memory, right?).
  485.  
  486. <p>This other way is invoked with the <tt>->hostenum()</tt> or the
  487. <tt>->hostenumref()</tt> methods. They return either an array or a
  488. reference to an array respectively, containing one object for each
  489. <em>host</em> address in the subnet. Note that only valid host
  490. addresses will be returned (as objects) since the network and
  491. broadcast addresses are seldom useful.
  492.  
  493. <p>With no further preamble, I introduce an example that kids
  494. shouldn't attempt at home, or at least in production code. (If you
  495. find this warning superfluous, try adding <tt>64.0.0.0/8</tt> to the
  496. <tt>__DATA__</tt> section and see if your machine chews through it
  497. all).
  498.  
  499. <PRE>
  500. use NetAddr::IP;
  501.  
  502. push @space, new NetAddr::IP->new($_) for &lt;DATA>;
  503.  
  504. for my $ip (map { $_->hostenum } NetAddr::IP::compact @space)
  505. {
  506. # Do something with $ip
  507. }
  508. __DATA__
  509. 10.0.0.0/16
  510. 172.16.0.0/24
  511. </PRE>
  512.  
  513. <p>If you really have enough memory, you'll see that each host address
  514. in your IP space is generated into a huge array. This is much more
  515. costly (read, slow) than the approach presented earlier, but provides
  516. for more compact one-liners or quickies.
  517.  
  518. <hr>
  519.  
  520. <a name="num"><h3>Finding out how big is your network</h3></a>
  521.  
  522. <p>Have you wondered just how many IP addresses can you use in your
  523. current subnet plan? If the answer to this (or to a similar question)
  524. is <em>yes</em>, then read on.
  525.  
  526. <p>There is a method called <tt>->num()</tt> that will tell you
  527. exactly how many addresses can you use in a given subnet. For the
  528. quick observers out there, you can also use something like <tt>scalar
  529. $subnet->hostenum</tt> but this is a really expensive way of doing it.
  530.  
  531. <p>A more conservative (in resources) approach is depicted below:
  532.  
  533. <PRE>
  534. use NetAddr::IP;
  535.  
  536. my $hosts = 0;
  537. push @space, new NetAddr::IP->new($_) for &lt;DATA>;
  538. $hosts += $_->num for @space;
  539. print "You have $hosts\n";
  540. __DATA__
  541. 10.0.0.0/16
  542. 172.16.0.0/24
  543. </PRE>
  544.  
  545. <p>Sometimes, you will be surprised at how many addresses are lost by
  546. subnetting, but we'll leave that discussion to other tutorials.
  547.  
  548. <hr>
  549. </BODY></HTML>