Wednesday 27 July 2011

Checking Types In Python.

Checking Types in Python


Need to know the type of an object? Let me make a brief argument: No, you don't. Just use the object as if it was whatever you expect it to be, and handle any errors that result.
On the other hand, type-checking is convenient, easy to implement, and can save your life when debugging. Sometimes there isn't an obvious other way to attack a problem. (Look for one first though. Really.)
You have a few choices on how to type-check: the built-in functions type and isinstance and the instance property __class__. To compare the object to known classes, you can compare to the class directly or import the 'types' module (safe to do in your global scope) to get access to some types that don't provide user-accessible classes (like functions).
Before we start, note that Python has two types of classes: 'new-style' classes which inherit from the object class, and 'old-style' classes which don't. Most (all?) native Python types are now new-style classes, but classes you've created probably aren't.
Let's try some experiments, trying to print out the name of a class, and then comparing the type to a known class. Say we have the following classes and instances defined:
# 's
 1from types import *
 2
 3# Instances of new-style classes
 4class NewClass(object): pass
 5
 6new_class_instance = NewClass()
 7
 8def function(): pass
 9
10string = 'This is a string!'
11
12# Instances of old-style classes
13class OldClass: pass
14
15old_class_instance = OldClass()
Let's print out the name of the class using type and __class__. There's no way to do this with isinstance.:
# 's
 1print type(new_class_instance)
 2print new_class_instance.__class__
 3# both print "<class '__main__.new_class'>"
 4
 5print type(function)
 6print function.__class__
 7# both print "<type 'function'>"
 8
 9print type(string)
10print string.__class__
11# both print "<type 'str'>"
12
13print type(old_class)
14# prints "<type 'instance'>"
15
16print old_class.__class__
17# prints something like "class __main__.OldClass at 0x00F7F"
18# Note that this is different than what was printed by type()
It looks like type doesn't work for old-style classes. Now let's try checking against a known class or type, using type, isinstance, and __class__:
# 's
 1type(new_class_instance) == NewClass
 2isinstance(new_class_instance, NewClass)
 3new_class_instance.__class__ == NewClass
 4# All return True
 5
 6type(function) == FunctionType # from 'types' module
 7isinstance(function, FunctionType)
 8function.__class__ == FunctionType
 9# All return True
10
11type(string) == str # we could also use StringType from 'types' module
12isinstance(string, str)
13string.__class__ == str
14# All return True
15
16type(old_class_instance) == OldClass
17# Returns False, even though old_class_instance is an instance of OldClass.
18# 'type' just can't understand these.
19
20isinstance(old_class_instance, OldClass)
21old_class_instance.__class__ == OldClass
22# Both return True as expected
Apparently, type is completely useless for both type printing and type comparisons, as it can't understand old-style classes while __class__ can.
__class__ is messy, but is the only method that both works for both types of classes and will print out the name of the class if desired. It is the best choice for debugging, when you have no idea what type of object you're looking at.
isinstance also works on all objects. It also has another couple of perks: it actually checks to see if your object is an instance of a class or subclass of the class you pass it. Generally if you're type-checking you're interested in the existence of a behavior or method, and all subclasses of the target class will probably have it. So, isinstance is more accurate. Additionally, you can pass it a tuple of classes, and it will check against all of them. These perks make it the best choice for legitimate type-checking in a real program.
Here's a summary of what we found:

Python Decorators.

Decorators modify functions. Beginning with the basics, learn how to use decorators in a variety of ways. Execute code when a function is parsed or called. Conditionally call functions and transform inputs and outputs. Write customizable decorators that accept arbitrary arguments. And, if necessary, easily make sure your decorated function has the same signature as the original.

Decorators. The shear mention of them brings fear to even the seasoned Python programmer.
Okay, maybe not. But decorators, at least in this author's opinion, have a weird syntax and inevitably complicated implementations that are especially foreign (I think) to many who have gotten used to the simplicity of Python.

1   The Basics

Decorators modify functions. More specifically, a decorator is a function that transforms another function.
When you use a decorator, Python passes the decorated function -- we'll call this the target function -- to the decorator function, and replaces it with the result. Without decorators, it would look something like this:

# 's
 1def decorator_function(target):
 2    # Do something with the target function
 3    target.attribute = 1
 4    return target
 5
 6def target(a,b):
 7    return a + b
 8
 9# This is what the decorator actually does
10target = decorator_function(target)
This code has the exact same functionality, but uses decorators. Note that I can name my decorator function whatever I want. Here, I've chosen 'decorator_function':
# 's
1def decorator_function(target):
2    # Do something with the target function
3    target.attribute = 1
4    return target
5
6# Here is the decorator, with the syntax '@function_name'
7@decorator_function
8def target(a,b):
9    return a + b
As you can see, you need to put the decorator function's name, prefaced with a @, on the line before the target function definition. Python internally will transform the target by applying the decorator to it and replacing it with the returned value.
Both of the above examples will have the same results:
# 's
1>>> target(1,2)
23
3>>> target.attribute
41

1.1   Does a decorator function have to return a function?

No. The decorator function can return absolutely anything, and Python will replace the target function with that return value. For example, you could do something like this:
# 's
 1def decorator_evil(target):
 2    return False
 3
 4@decorator_evil
 5def target(a,b):
 6    return a + b
 7
 8>>> target
 9False
10
11>>> target(1,2)
12TypeError: 'bool' object is not callable
This is really not something you want to be doing on a regular basis though -- I'm pretty sure that a basic design principal is to not have functions randomly turning into other sorts of things. It makes good sense to at least return some sort of callable.

2   Run-Time Transformations

"But," I hear you saying, "I thought decorators did more than that. I want to do things at run-time, like conditionally calling the function and transforming the arguments and return value."
Can decorators do these things? Yes. Is that really something 'more' than we talked about above? Not really. It's important here not to get bogged down in the details -- you already know all there is to know about decorators. To do one of these more complex things, we're really just adding some plain old Python to the mix.

2.1   The Wrapper Function

Remember, your decorator function can return an arbitrary function. We'll call it the wrapper function, for reasons which will become clear in a second. The trick here is to define the wrapper function inside the decorator function, giving it access to the decorator function's variable scope, including the target function.
# 's
 1def decorator(target):
 2
 3    def wrapper():
 4        print 'Calling function "%s"' % target.__name__
 5        return target()
 6
 7    # Since the wrapper is replacing the target function, assigning an attribute to the target function won't do anything.
 8    # We need to assign it to the *wrapper function*.
 9    wrapper.attribute = 1
10    return wrapper
11
12@decorator
13def target():
14    print 'I am the target function'
15
16>>> target()
17Calling function "target"
18I am the target function
19
20>>> target.attribute
211
As you can see, the wrapper function can do whatever it wants to the target function, including the simple case of returning the target's return value. But what happens to any arguments passed to the target function?

2.2   Getting the Arguments

Since the returned wrapper function replaces the target function, the wrapper function will receive the arguments intended for the target function. Assuming you want your decorator to work for any target function, your wrapper function then should accept arbitrary non-keyword arguments and arbitrary keyword arguments, add, remove, or modify arguments if necessary, and pass the arguments to the target function.
# 's
 1def decorator(target):
 2
 3    def wrapper(*args, **kwargs):
 4        kwargs.update({'debug': True}) # Edit the keyword arguments -- here, enable debug mode no matter what
 5        print 'Calling function "%s" with arguments %s and keyword arguments %s' % (target.__name__, args, kwargs)
 6        return target(*args, **kwargs)
 7
 8    wrapper.attribute = 1
 9    return wrapper
10
11@decorator
12def target(a, b, debug=False):
13    if debug: print '[Debug] I am the target function'
14    return a+b
15
16>>> target(1,2)
17Calling function "target" with arguments (1, 2) and keyword arguments {'debug': True}
18[Debug] I am the target function
193
20
21>>> target.attribute
221
Note
You can also apply a decorator to a class method. If your decorator is always going to be used this way, and you need access to the current instance, your wrapper function can assume the first argument is always self:
# 's
1def wrapper(self, *args, **kwargs):
2    # Do something with 'self'
3    print self
4    return target(self, *args, **kwargs)

2.3   Summing It Up

So, we have a wrapper function that accepts arbitrary arguments defined inside our decorator function. The wrapper function can call the target function if and when it wants, get the result, do something with it, and return whatever it wants.
Say I want certain function calls to require positive confirmation before they are executed, and then stringify the result of the function before returning it. Note that the built-in function raw_input prints a message and then waits for a response from stdin.
# 's
 1def decorator(target):  # Python passes the target function to the decorator
 2
 3    def wrapper(*args, **kwargs):
 4
 5        choice = raw_input('Are you sure you want to call the function "%s"? ' % target.__name__)
 6
 7        if choice and choice[0].lower() == 'y':
 8            # If input starts with a 'y', call the function with the arguments
 9            result = target(*args, **kwargs)
10            return str(result)
11
12        else:
13            print 'Call to %s cancelled' % target.__name__
14
15    return wrapper
16
17@decorator
18def target(a,b):
19    return a+b
20
21>>> test.target(1,2)
22Are you sure you want to call the function "target"? n
23Call to target cancelled
24
25>>> test.target(1,2)
26Are you sure you want to call the function "target"? y
273

3   Dynamic Decorators

Sometimes you might want to customize behavior by passing arbitrary options to your decorator function. A cursory look at decorator syntax suggests there's no way to do that. You could just abandon the decorator idea altogether, but you certainly don't have to.
The solution is define your decorator function inside another function -- call it the options function. Right before the target function definition, where you would normally list the decorator function (prepended with an @), call this options function (prepended with an @) instead. The options function then returns your decorator function, which Python will use as the passes the target function to as before.

3.1   Passing Options to the Decorator

Your options function can accept any arguments you want it to. Since the decorator function is defined inside the options function, the decorator function has access to any of the arguments passed to the options function.
# 's
 1def options(value):
 2
 3    def decorator(target):
 4        # Do something with the target function
 5        target.attribute = value
 6        return target
 7    return decorator
 8
 9@options('value')
10def target(a,b):
11    return a + b
12
13>>> target(1,2)
143
15
16>>> target.attribute
17'value'
As you can see, nothing here about the decorator syntax itself has changed. Our decorator function is just in a dynamic scope instead of a static one.

3.2   Run-Time Tranformations

You can do Run-time Transformations by returning a wrapper function from your decorator function, just like before. For better or worse, though, there now must be three levels of functions:
# 's
 1def options(debug_level):
 2
 3    def decorator(target):
 4
 5        def wrapper(*args, **kwargs):
 6            kwargs.update({'debug_level': debug_level}) # Edit the keyword arguments
 7                                                        # here, set debug level to whatever specified in the options
 8
 9            print 'Calling function "%s" with arguments %s and keyword arguments %s' % (target.__name__, args, kwargs)
10            return target(*args, **kwargs)
11
12        return wrapper
13
14    return decorator
15
16@options(5)
17def target(a, b, debug_level=0):
18    if debug_level: print '[Debug Level %s] I am the target function' % debug_level
19    return a+b
20
21>>> target(1,2)
22Calling function "target" with arguments (1, 2) and keyword arguments {'debug_level': 5}
23[Debug Level 5] I am the target function
243

4   Caveat: Function Signatures

Phew. Understand everything you can do with decorators now? Good :). However, there is one drawback that must be mentioned.
The function returned from the decorator function -- usually a wrapper function -- replaces the target function completely. Any later introspection into what appears to be the target function will actually be into the wrapper function.
Most of the time, this is okay. Generally you just call a function with some options. Your program doesn't check to see what the function's __name__ or what arguments it accepts. So usually this problem won't be a problem.
However sometimes you care if the function you are calling supports a certain option, supports arbitrary options, or, perhaps, what its __name__ is. Or maybe you are interested in one if the function's attributes. If you look at a function that has been decorated, you will actually be looking at the wrapper function.
In the example below, note that the getargspec function of the inspect module gets the names and default values of a function's arguments.
# 's
 1# This function is the same as the function 'target', except for the name
 2def standalone_function(a,b):
 3    return a+b
 4
 5def decorator(target):
 6
 7    def wrapper(*args, **kwargs):
 8        return target()
 9
10    return wrapper
11
12@decorator
13def target(a,b):
14    return a+b
15
16>>> from inspect import getargspec
17
18>>> standalone_function.__name__
19'standalone_function'
20
21>>> getargspec(standalone_function)
22(['a', 'b'], None, None, None)
23
24>>> target.__name__
25'wrapper'
26
27>>> getargspec(target)
28([], 'args', 'kwargs', None)
As you can see, the wrapper function reports that it accepts different arguments than the original target function Its call signature has changed.

4.1   A Solution

This is not an easy problem to solve. The update_wrapper method of the functools module provides a partial solution, copying the __name__ and other attributes from one function to another. But it does not solve what might be the largest problem of all: the changed call signature.
The decorator function of the decorator module provides the best solution: it can wrap your wrapper function in a dynamically-evaluated function with the correct arguments, restoring the original call signature. Similar to the update_wrapper function, it can also update your wrapper function with the __name__ and other attributes from the target function.
Note
For the remainder of this section, when I speak of the decorator function, I mean the one from this module, not one of the decorator functions that we've been using to transform our target functions.
Another way to create decorators
Unfortunately, though, the decorator function wasn't written with this use in mind. Instead it was written to turn standalone wrapper functions into full-fledged decorators, without having to worry about the function nesting described in Run-Time Transformations, above.
While this technique is often useful, it is much less customizable. Everything must be done at run-time, each time the function is executed. You cannot do any work when the target function is defined, including assigning the target or wrapper functions attributes or passing options to the decorator.
Also, in this author's opinion it is a bit of a black box; I'd rather know what my decorators are doing even if it is a little messier.
But we can make it work for us to solve this problem.
Ideally you would just call decorator(wrapper) and be done with it. However, things are never as simple as we'd like. As described above, the decorator function wraps the function passed to it -- our wrapper function -- in a dynamic function to fix the signature. But we still have a few problems:

Problem #1:
The dynamic function calls our wrapper function with (func, *args, **kwargs)
Solution #1:
Make our wrapper function accept (func, *args, **kwargs) instead of just (*args, **kwargs).
Problem #2:
The dynamic function is then wrapped in another function that expects to be used as an actual decorator -- it expects to be called with the target function, and will return the wrapper function.
Solution #2:
Call decorator's return value with the target function to get back to the dynamic function, which has the right signature.
This technique is a bit of a hack, and is a bit hard to explain, but it is easy to implement and works well.
This is the same example as before, but now with the decorator function (and a name change so things don't get too confusing):
# 's
 1from decorator import decorator
 2
 3def my_decorator(target):
 4
 5    def wrapper(target, *args, **kwargs): # the target function has been prepended to the list of arguments
 6        return target(*args, **kwargs)
 7
 8    # We are calling the returned value with the target function to get a 'proper' wrapper function back
 9    return decorator(wrapper)(target)
10
11
12@my_decorator
13def target(a,b):
14    return a+b
15
16>>> from inspect import getargspec
17
18>>> target.__name__
19'target'
20
21>>> getargspec(target)
22(['a', 'b'], None, None, None)

5   Putting it All Together

Sometimes, you really need a customizable decorator that does work both at parse-time and run-time, and has the signature of the original target function.
Here's an example that ties everything together. Expanding on the example from earlier, say you want certain function calls to require positive confirmation before they are executed, and you want to be able to customize the confirmation string for each target function. Furthermore, for some reason [1], you need the decorated function's signature to match the target function.
Here we go:
# 's
 1from decorator import decorator
 2
 3# The 'options' function.  Recieves options and returns a decorator.
 4def confirm(text):
 5    '''
 6    Pass a string to be sent as a confirmation message.  Returns a decorator.
 7    '''
 8
 9    # The actual decorator.  Recieves the target function.
10    def my_decorator(target):
11        # Anything not in the wrapper function is done when the target function is initially parsed
12
13        # This is okay because the decorator function will copy the attribute to the wrapper function
14        target.attribute = 1
15
16        # The wrapper function.  Replaces the target function and receives its arguments
17        def wrapper(target, *args, **kwargs):
18            # You could do something with the args or kwargs here
19
20            choice = raw_input(text)
21
22            if choice and choice[0].lower() == 'y':
23                # If input starts with a 'y', call the function with the arguments
24                result = target(*args, **kwargs)
25                # You could do something with the result here
26                return result
27
28            else:
29                print 'Call to %s cancelled' % target.__name__
30
31        # Fix the wrapper's call signature
32        return decorator(wrapper)(target)
33
34    return my_decorator
35
36@confirm('Are you sure you want to add these numbers? ')
37def target(a,b):
38    return a+b
39
40>>> Are you sure you want to add these numbers? yes
413
42
43>>> target.attribute
441
Hey, what do you know, it actually works.

6   Conclusion

As always, if you have a better way to do anything mentioned here, or if I've left anything out, leave a comment or feel free edit this article to fix the problem.

7   References

Michele Simionato's 'decorator' Module
Python Tips, Tricks, and Hacks - Decorators

Tuesday 26 July 2011

IPV4 Address.

What is an IP address?

Computers communicate over the Internet using the IP protocol (Internet Protocol), which uses numerical addresses, called IP addresses, made up of four whole numbers (4 bytes) between 0 and 255 and written in the format xxx.xxx.xxx.xxx. For example, 194.153.205.26 is an IP address given in technical format.
These addresses are used by networked computers to communicate, so each computer on a network has a unique IP address on that network.
It is ICANN (Internet Corporation for Assigned Names and Numbers, replaced since 1998 by IANA, Internet Assigned Numbers Agency) which is responsible for allocating public IP addresses, i.e. IP addresses for computers directly connected to the public internet network.

Decrypting an IP address

An IP address is a 32 bit address, generally written in the format of 4 whole numbers separated by dots. There are two distinct parts to an IP address:
  • the numbers to the left indicate the network and are called the netID,
  • the numbers to the right indicate the computers on this network and are called the host-ID.
Shown in the example below:
network example
Note the network to the left 194.28.12.0. It contains the following computers:
  • 194.28.12.1 to 194.28.12.4
Note that of the right 178.12.0.0. It includes the following computers:
  • 178.12.77.1 to 178.12.77.6
In the case above, the networks are written 194.28.12 and 178.12.77, then each computer making up the network is numbered incrementally.
Take a network written 58.0.0.0. The computers on this network could have IP addresses going from 58.0.0.1 to 58.255.255.254. So, it is a case of allocating the numbers in such a way that there is a structure in the hierarchy of the computers and servers.
So, the smaller the number of bits reserved on the network, the more computers it can contain.
In fact, a network written 102.0.0.0 can contain computers whose IP address can vary between 102.0.0.1 and 102.255.255.254 (256*256*256-2=16,777,214 possibilities), while a network written 194.24 can only contain computers where the IP address is between 194.26.0.1 and 194.26.255.254 (256*256-2=65,534 possibilities), this is the notion of IP address classes.

Special addresses.

When the host-id is cancelled, i.e. when the bits reserved for the machines on the network are replaced by zeros (for example 194.28.12.0), something called a network address is obtained. This address cannot be allocated to any of the computers on the network.
When the netid is cancelled, i.e. when the bits reserved for the network are replaced by zeros, a machine address is obtained. This address represents the machine specified by the host-ID which is found on the current network.
When all the bits of the host-id are at 1, the address obtained is called the broadcast address. This a specific address, enabling a message to be sent to all the machines on the network specified by the netID.
Conversely, when all the bits of the netid are at 1, the address obtained is called the multicast address.
Finally the address 127.0.0.1 is called the loopback address because it indicates the localhost.

Network classes

IP addresses are divided into classes, according to the number of bytes which represent the network.

Class A

In a class A IP address, the first byte represents the network.
The most significant bit (the first bit, that to the left) is at zero which means that there are 27 (00000000 to 01111111) network possibilities, which is 128 possibilities However, the 0 network (bits valuing 00000000) does not exist and number 127 is reserved to indicate your machine.
The networks available in class A are therefore networks going from 1.0.0.0 to 126.0.0.0 (the last bytes are zeros which indicate that this is indeed a network and not computers!)
The three bytes to the left represent the computers on the network, the network can therefore contain a number of computers equal to:
224-2 = 16,777,214 computers.
A class A IP address, in binary looks like:







Class B

In a class B IP address, the first two bytes represent the network.
The first two bits are 1 and 0, which means that there are 214 (10 000000 00000000 to 10 111111 11111111) network possibilities, which is 16,384 possible networks. The networks available in class B are therefore networks going from 128.0.0.0 to 191.255.0.0.
The two bytes to the left represent the computers on the network. The network can therefore contain a number of computers equal to:
216-21 = 65,534 computers.
A class B IP address, in binary looks like:






Class C

In a class C IP address, the first three bytes represent the network. The first three bits are 1,1 and 0 which means that there are 221 network possibilities, i.e. 2,097,152. The networks available in class C are therefore networks going from 192.0.0.0 to 223.255.255.0.
The byte to the left represents the computers on the network, the network can therefore contain:
28-21 = 254 computers.
In binary, a class C IP address looks like:






Allocation of IP addresses

The aim of dividing IP addresses into three classes A, B and C is to make the search for a computer on the network easier. In fact, with this notation it is possible to firstly search for the network that you want to reach, then search for a computer on this network. So, allocation of IP address is done according to the size of the network.











Class A addresses are used for very large networks, while class C addresses are for example allocated to small company networks.

Reserved IP addresses

It frequently happens that in a company or organisation only one computer is linked to the Internet and it is through this that other computers on the network access the Internet (generally we talk of a proxy or gateway).
In such a case, only the computer linked to the network needs to reserve an IP address with ICANN. However, the other computers still need an IP address to be able to communicate with each other internally.
So, ICANN has reserved a handful of addresses in each class to enable an IP address to be allocated to computers on a local network linked to the Internet without the risk of creating IP address conflicts on the network of networks. These are the following addresses:
  • Private class A IP addresses: 10.0.0.1 to 10.255.255.254, enabling the creation of large private networks comprising of thousands of computers.
  • Private class B IP addresses: 172.16.0.1 to 172.31.255.254, making it possible to create medium sized private networks.
  • Private class C IP addresses: 192.168.0.1 to 192.168.0.254, for putting in place small private networks.

Subnet masks


Subnet masks

In short, a mask is produced containing 1s with the location of bits that you want to keep and 0s for those you want to cancel. Once this mask is created, you simply put a logical AND between the value you want to mask and the mask in order to keep the part you wish to cancel separate from the rest.
So a netmask is presented in the form of 4 bytes separated by dots (like an IP address), it comprises (in its binary notation) zeros at the level of the bits from the IP address that you wish to cancel (and ones at the level of those you want to keep).

Importance of subnet masks

The primary importance of a subnet mask is to enable the simple identification of the network associated to an IP address.
Indeed, the network is determined by a certain number of bytes in the IP address (1 byte for class A addresses, 2 for class B and 3 bytes for class C). However, a network is written by taking the number of bytes which characterise it, then completing it with zeros. For example, the network linked to the address 34.56.123.12 is 34.0.0.0, because it is a class A type IP address.
To find out the network address linked to the IP address 34.56.123.12, you simply need to apply a mask where the first byte is only made up of 1s (which is 255 in decimal), then 0s in the following bytes.
The mask is: 11111111.00000000.00000000.00000000
The mask associated with the IP address 34.208.123.12 is therefore 255.0.0.0.
The binary value of 34.208.123.12 is: 00100010.11010000.01111011.00001100
So an AND logic between the IP address and the mask gives the following result:
00100010.11010000.01111011.00001100
  AND
11111111.00000000.00000000.00000000
  =
00100010.00000000.00000000.00000000
Which is 34.0.0.0. It is the network linked to the address 34.208.123.12
By generalising, it is possible to obtain masks relating to each class of address:
  • For a Class A address, only the first byte must be retained. The mask has the following format 11111111.00000000.00000000.00000000, i.e. 255.0.0.0 in decimal;
  • For a Class B address, the first two bytes must be retained, which gives the following mask 11111111.11111111.00000000.00000000, relating to 255.255.0.0 in decimal;
  • For a Class C address, by the same reasoning, the mask will have the following format 11111111.11111111.11111111.00000000, i.e. 255.255.255.0 in decimal;

Creation of subnets

Let us re-examine the example of the network 34.0.0.0, and assume that we want the first two bits of the second byte to make it possible to indicate the network.
The mask to be applied will then be:
11111111.11000000.00000000.00000000

That is 255.192.0.0
If we apply this mask to the address 34.208.123.12 we get:
34.192.0.0
In reality there are 4 possible scenarios for the result of the masking of an IP address of a computer on the network 34.0.0.0
  • When the first two bits of the second byte are 00, in which case the result of the masking is 34.0.0.0
  • When the first two bits of the second byte are 01, in which case the result of the masking is 34.64.0.0
  • When the first two bits of the second byte are 10, in which case the result of the masking is 34.128.0.0
  • When the first two bits of the second byte are 11, in which case the result of the masking is 34.192.0.0
Therefore, this masking divides a class A network (able to allow 16,777,214 computers) into 4 subnets - from where the name of subnet mask - can allow 222 computers or 4,194,304 computers.
It may be interesting to note that in these two cases, the total number of computers is the same, which is 16,777,214 computers (4 x 4,194,304 - 2 = 16,777,214).
The number of subnets depends on the number of additional bits allocated to the network (here 2). The number of subnets is therefore: 


Concept of IPV4 By Russ White.


IP addresses, both IPv4 and IPv6, appear to be complicated when you first encounter them, but in reality they are simple constructions, and a using a few basic rules will allow you to find the important information for any situation very quickly—and with minimal math. In this article, we review some of the basics of IPv4 address layout, and then consider a technique to make working with IPv4 addresses easier. Although this is not the “conventional” method you might have been taught to work with in IP address space, you will find it is very easy and fast. We conclude with a discussion of applying those techniques to the IPv6 address space.

Basic Addressing

IPv4 addresses are essentially 32-bit binary numbers; computer systems and routers do not see any sorts of divisions within the IPv4 address space. To make IPv4 addresses more human-readable, however, we break them up into four sections divided by dots, or periods, commonly called “octets.” An octet is a set of eight binary digits, sometimes also called a “byte.” We do not use byte here, because the real definition of a byte can vary from computer to computer, whereas an octet remains the same length in all situations. Figure 1 illustrates the IPv4 address structure.
Figure 1: IPv4 Address Structure
Because each octet represents a binary (base 2) number between 0 and 28, each octet will be between 0 and 255. This part of IPv4 addresses is simple—but what about subnet masks? To understand a subnet mask, we need to understand how a device actually uses subnet masks to determine where to send a specific packet, as Figure 2 illustrates.
Figure 2: Subnet Masks
If host A, which has the local IP address 10.1.1.2 with a subnet mask of 255.255.255.0, wants to send a packet to 10.1.3.2, how does it know whether D is connected to the same network (broadcast domain) or not? If D is connected to the same network, then A should look for D’s local Layer 2 address to transmit the packet to. If D is not connected to the same network, then A needs to send any packets destined to D to A’s local default gateway.
To discover whether D is connected or not, A takes its local address and performs a logical AND between this and the subnet mask. A then takes the destination (remote) address and performs the same logical AND (using its local subnet mask). If the two resulting numbers, called the network address or prefix, match, then the destination must be on the local segment, and A can simply look up the destination in the Address Resolution Protocol (ARP) cache, and send the packet locally. If the two numbers do not match, then A needs to send the packet to its default gateway.
Note: ARP is a protocol used to discover the mappings between the IP addresses of devices attached to the same network as the local device and the Layer 2 address of devices attached to the same network as the local device. Essentially, a device sends an ARP broadcast containing the IP address of some other device it believes to be connected, and the device with the specified IP address replies with its Layer 2 address, providing a mapping between these two addresses.
If a subnet mask is a “dotted decimal” version of the binary subnet mask, then what is the prefix length? The prefix length is just a shorthand way of expressing the subnet mask. The prefix length is the number of bits set in the subnet mask; for instance, if the subnet mask is 255.255.255.0, there are 24 1’s in the binary version of the subnet mask, so the prefix length is 24 bits. Figure 3 illustrates network masks and prefix lengths.
Figure 3: Prefix Lengths

Working with IPv4 Addresses

Now that we understand how an IPv4 address is formed and what the subnet length and prefix length are, how do we work with them? The most basic questions we face when working with an IP address follow:
  • What is the network address of the prefix?
  • What is the host address?
There are two ways to find the answers to these questions: the hard way and the easy way. We cover the hard way first, and then show you the easy way.

The Hard Way

The hard way to determine the prefix and host addresses is to convert the address into binary, perform logical AND and NOR operations on the address and the subnet mask, and then convert the resulting numbers back to decimal. Figure 4 illustrates the process of converting a single octet of the IPv4 address into binary; the number converted in this case is 192.
Figure 4: Binary Conversion
The process is simple, but tedious; divide the octet value by 2, take the remainder off, and then divide by 2 again, until you reach 0. The remainders, reversed in direction, are the binary numbers representing the value of the octet. Performing this process for all four octets, we have the binary IP address, and can use logical AND and NOR operations to find the prefix (network address) and the host address, as Figure 5 shows for the address 192.168.100.80/26.
Figure 5: Address Calculation

The Easy Way

All this conversion from binary to decimal and from decimal to binary is tedious— is there an easier way? Yes. First, we start with the observation that we work only with the numbers within one octet at a time, no matter what the prefix length is. We can assume all the octets before this working octet are part of the network address, and octets after this working octet are part of the host address.
The first thing we need to do, then, is to find out which octet is our working octet. This task is actually quite simple: just divide the prefix length by 8, discard the remainder, and add 1. The following table provides some examples.


Note: Another way to look at this task is that you will ignore the octets indicated by the division. For instance, for 192.168.100.80/26, the result of dividing 26 by 8 is 3, so you will ignore the first three octets of the IP address, and work only with the fourth octet. This process has the same result.
When we know the working octet, what do we do with it? Well, we could simply use the procedure outlined, convert the single octet to binary, perform AND and NOR operations on it with the right bits from the subnet mask, and then put it all back together to find the network and host addresses—but there is an easier way to find the network and host parts of the working octet. Start by doing the same math, only this time we want to work with the remainder rather than the result.
192.168.100.80/26
26 ÷ 8 = 3 with a remainder of 2
Take the remainder, and use the following table to find the corresponding jump within the octet; this number is the distance, in decimal form, between the network addresses within the octet.


In this chart, the first column represents the prefix length within this octet, the second column represents the prefix value when this bit is set to 1, the number of hosts in the subnet for this prefix length, and the jump between network addresses with the specified prefix length.
The number 2 corresponds to 64, so the jump is 64—there is a network at 0, 64, 128, 192, and 224 in this octet. Now all we need to do is figure out which one of those networks this address is in. This task is fairly simple: just take the largest network number that fits into the number in the working octet. In this case, the largest number that fits into 80 is 64, so our network address is 192.168.100.64/26.
Now, what about the host address? That is easy when we have the network address—just subtract the network address from the IP address, and you have the host address within the network: 80 – 64 = 16. This process takes a little practice, but it is not hard when you become accustomed to the steps.


In the second and third examples, you see that the working octet is actually the third, rather than the fourth, octet. To find the host address in these examples, you simply find the host address in the third octet, and then “tack on” the fourth octet as part of the host address as well, because part of the third octet—and all of the fourth octet—are actually part of the host address.

Summarization and Subnets

Subnets and supernets are probably the hardest part of IP addressing for most people to understand and handle quickly, but they are both based on a very simple concept—aggregation. Figure 6 shows how aggregation works.
Figure 6: Address Aggregation
The figure shows four hosts with the addresses 10.1.0.1, 10.1.0.2, 10.1.0.3, and 10.1.0.4. Router A advertises 10.1.1.0/24, meaning: “Any host within the address range 10.1.0.0 through 10.1.0.255 is reachable through me.” Note that not all the hosts within this range exist, and that is okay—if a host within that range of addresses is reachable, it is reachable through Router A. In IP, the address that A is advertising is called a network address, and you can conveniently think of it as an address for the wire the hosts and router are attached to, rather than a specific device.
For many people, the confusing part comes next. Router B is also advertising 10.1.1.0/24, which is another network address. Router C can combine—or aggregate—these two advertisements into a single advertisement. Although we have just removed the correspondence between the wire and the network address, we have not changed the fundamental meaning of the advertisement itself. In other words, Router C is saying: “Any host within the range of addresses from 10.1.0.0 through 10.1.1.255 is reachable through me.” There is no wire with this address space, but devices beyond Router C do not know this, so it does not matter.
To better handle aggregated address space, we define two new terms, subnets and supernets. A subnet is a network that is contained entirely within another network; a supernet is a network that entirely contains another network. For instance, 10.1.0.0/24 and 10.1.1.0/24 are both subnets of 10.1.0.0/23, whereas 10.1.0.0/23 is a supernet of 10.1.0.0/24 and 10.1.1.0/24.
Now we consider a binary representation of these three addresses, and try to make more sense out of the concept of aggregation from an addressing perspective; Figure 7 illustrates.
Figure 7: Aggregation Details
By looking at the binary form of 10.1.0.0/24 and 10.1.1.0/24, we can see that only the 24th bit in the network address changes. If we change the prefix length to 23, we have effectively “masked out” this single bit, making the 10.1.0.0/23 address cover the same address range as the 10.1.0.0/24 and 10.1.1.0/24 addresses combined.

The Hardest Subnetting Problem

The hardest subnetting problem most people face is that of trying to decide what the smallest subnet is that will provide a given number of hosts on a specific segment, and yet not waste any address space. The way this sort of problem is normally phrased is something like the following:
You have 5 subnets with the following numbers of hosts on them: 58, 14, 29, 49, and 3, and you are given the address space 10.1.1.0/24. Determine how you could divide the address space given into subnets so these hosts fit into it.
This appears to be a very difficult problem to solve, but the chart we used previously to find the jump within a single octet actually makes this task quite easy. First, we run through the steps, and then we solve the example problem to see how it actually works.
  • Order the networks from the largest to the smallest.
  • Find the smallest number in the chart that fits the number of the largest number of hosts + 2 (you cannot, except on point-to-point links, use the address with all 0’s or all 1’s in the host address; for point-to-point links, you can use a /31, which has no broadcast addresses).
  • Continue through each space needed until you either run out of space or you finish.
This process seems pretty simple, but does it work? Let’s try it with our example.
  • Reorder the numbers 58, 14, 29, 49, 3 to 58, 49, 29, 14, 3.
  • Start with 58.
    • The smallest number larger than (58 + 2) is 64, and 64 is 2 bits.
    • There are 24 bits of prefix length in the address space given; add 2 for 26.
    • The first network is 10.1.1.0/26.
    • The next network is 10.1.1.0 + 64, so we start the next “round” at 10.1.1.64.
  • The next block is 49 hosts.
    • The smallest number larger than (49 + 2) is 64, and 64 is 2 bits.
    • There are 24 bits of prefix length in the address space given; add 2 for 26.
    • We start this block at 10.1.1.64, so the network is 10.1.1.64/26.
    • The next network is 10.1.1.64 + 64, so we start the next “round” at 10.1.1.128.
  • The next block is 29 hosts.
    • The smallest number larger than (29 + 2) is 32, and 32 is 3 bits.
    • There are 24 bits of prefix length in the address space given; add 3 for 27.
    • We start this block at 10.1.1.128, so the network is 10.1.1.128/27.
    • The next network is 10.1.1.128 + 32, so we start the next “round” at 10.1.1.160.
  • The next block is 14 hosts.
    • The smallest number larger than (14 + 2) is 16, and 16 is 4 bits (actually equal, but it still works).
    • There are 24 bits of prefix length in the address space given; add 14 for 28.
    • We start this block at 10.1.1.160, so the network is 10.1.1.160/28.
    • The next network is 10.1.1.160 + 16, so we start the next “round” at 10.1.1.176.
  • The last block is 3 hosts.
    • The smallest number larger than (3 + 2) is 8, and 8 is 5 bits.
    • There are 24 bits of prefix length in the address space given; add 5 for 29.
    • We start this block at 10.1.1.176, so the network is 10.1.1.176/29.
    • This is the last block of hosts, so we are finished.
It is a simple matter of iterating from the largest to the smallest block, and using the simple chart we used before to determine how large of a jump we need to cover the host addresses we need to fit onto the subnet. Figure 8 illustrates the resulting hierarchy of subnets.
Figure 8: Subnet Chart
In this illustration:
  • The first line in each box contains the final octet of the network address in binary and decimal forms.
  • The second line in each box contains the prefix length.
  • The third line indicates the number of hosts the original problem required on that subnet.
  • Gray boxes indicate blocks of address space that are unused at that level.

Working with IPv6 Addresses

IPv6 addresses appear to be much more difficult to work with—but they really are not. Although they are larger, they are still made up of the same fundamental components, and hosts and routers still use the addresses the same way. All we really need to do is realize that each pair of hexadecimal numbers in the IPv6 address is actually an octet of binary address space. The chart, the mechanisms used to find the network and host addresses, and the concepts of super and subnets remain the same.
For example, suppose we have the IPv6 address 2002:FF10:9876: DD0A:9090:4896:AC56:0E01/63 and we want to know what the network number is (host numbers are less useful in IPv6 networks, because they are often the MAC address of the system itself).
  • 63 ÷ 8 = 7, remainder 7.
  • The working octet is the 8th, which is 0A.
  • Remainder 7 on the chart says the jump is 2, so the networks are 00, 02, 04, 06, 08, 0A, 0C, and 0E.
  • The network is 2002:FF10:9876:DD0A::/63.
The numbers are longer, but the principle is the same, as long as you remember that every pair of digits in the IPv6 address is a single octet.