Create l2_switching_v4.py

**************
The switch algorithm implemented in the program is quite simple and is as below
1. The switch receives an ICMP packet from host1 destined to host2
2. Initially, just after the switch start up , there are no flow table entries(a match) in the switch.
3. The OpenFlow switches in general forward the packets with no match to the controller.
4. The controller receives the packet from the switch
5. The controller performs a source based MAC learning and updates its srcmacaddrtable. In the current implementation, I have created a dictionary to  store the srcmacaddr and incoming port mapping. As and when a new packet is received, this dictionary is updated.
6. The controller checks if the destination macadrr (destmacaddr) has an entry made in the srcmacaddr table.
    > if no entry is made , the controller in structs the switch to broadcast the packets.
    > if an entry is made, a match is created with the details from the current packet
    > if an entry is made and the destination port is same as src port, the switch is instructed to drop the packet.

*************
point 6 needs a little detailing here:
1. When a packet with no match arrives on the controller, the controller should be sending a ofp_packet_out() message and not a ofp_flow_mod() message. The difference being - when the controller inserts a match stating  - " broadcast all packets with no matches ), the switch stops sending the succeeding packets to the controller. That is because it already has a flow table match stating it to broadcast. So, the switch ends up acting like a Hub and not a switch.
But, when the controller uses a ofp_packet_out() message, no flow table entry is made, rather the switch floods the packets for once. As soon as a reply is received from a host, the controller updates the srcmacaddr {}. Hence, the flood() method calls, ofp_packet_out() and not ofp_flow_mod().

2. Also, incase the destination mac adde has a outgoing port matched to, the controller inserts a 'match' in the flow tables. The match is formed with the information provided in the parsed incoming packet (with help of ofp_match_frompacket(). Once a match is formed, flow table is modified with ofp_flow_mod().

*************
Elaboration of methods used:

1. def _handle_ConnectionUp():  
This is an event handler for any connectionUp packets event from the switch end. the format'_handle_<eventname>' automatically, makes the method '_handle_ConnectionUp' a handler for the event 'ConnectionUp'

2. def _handle_PacketIn():
As an when the controller receives a packet from the switch the PacketIn event is triggered and the method _handle_PacketIn() is the corresponding event handler. In this version of event handler, I am parsing the packet, reading relevant information such as sourcemac address, source port, destination mac address. This information while we process our packet.

There are other open-flow events apart from the PacketIn and ConnectionUp events. A brief listing as below:
a. ConnectionUp
b. PacketIn
c. ConnectionDown
d. PortStatus
e. FlowRemoved
f. Statistics Events
g. ErrorIn
h. BarrierIn

3. def floodPacket():
is a helper function to flood packets

4. def dropPacket():
is again a helper function to drop packets.

5.def UpdateMap():
is a helper function to update the srcmacaddr {} during srcmacaddr and inport mapping

6.def processPacket():
this is the key method which processes the packet and implements the algorithm we had mentioned.
 
7. def sendPacekt():
sends the final open-flow message ( be it ofp_packet_out() or a ofp_flow_mod()) to the switch.

the launch() function simply hooks the l2 module to the core. As a reminder note that, open flow module is by default hooked to the core module.