import sys
import pdb
from socket import IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP
import logging
from ipaddr import IPv4Address
from struct import pack as spack
from pytricia import PyTricia
import socket
from fslib.node import Node, PortInfo
from fslib.link import NullLink
from fslib.common import fscore, get_logger
from fslib.flowlet import Flowlet, FlowIdent
from fslib.util import default_ip_to_macaddr
from fslib.configurator import FsConfigurator
from fslib.openflow import load_pox_component, load_odl_component
from pox.openflow import libopenflow_01 as oflib
import pox.core
from pox.lib.addresses import *
import pox.lib.packet as pktlib
from pox.lib.util import dpid_to_str
import pox.openflow.of_01 as ofcore
from pox.datapaths.switch import SoftwareSwitch
class UnhandledPoxPacketFlowletTranslation(Exception):
    pass
class PoxFlowlet(Flowlet):
    __slots__ = ['origpkt']
    def __init__(self, ident):
        Flowlet.__init__(self, ident)
        self.origpkt = None
class OpenflowMessage(Flowlet):
    __slots__ = ['ofmsg']
    def __init__(self, ident, ofmsg):
        Flowlet.__init__(self, ident)
        self.ofmsg = ofmsg
        self.bytes = len(ofmsg)
def flowlet_to_packet(flowlet):
    if hasattr(flowlet, "origpkt"):
        return getattr(flowlet, "origpkt")
    ident = flowlet.ident.key
    etherhdr = pktlib.ethernet()
    etherhdr.src = EthAddr(flowlet.srcmac)
    etherhdr.dst = EthAddr(flowlet.dstmac)
    etherhdr.type = pktlib.ethernet.IP_TYPE
    ipv4 = pktlib.ipv4() 
    ipv4.srcip = IPAddr(ident.srcip)
    ipv4.dstip = IPAddr(ident.dstip)
    ipv4.protocol = ident.ipproto
    ipv4.tos = flowlet.iptos
    iplen = flowlet.bytes / flowlet.pkts
    ipv4.iplen = iplen
    payloadlen = 0
    etherhdr.payload = ipv4
    if ident.ipproto == IPPROTO_ICMP:
        layer4 = pktlib.icmp()
        layer4.type = ident.dport >> 8
        layer4.code = ident.dport & 0x00FF
        payloadlen = max(iplen-28,0)
    elif ident.ipproto == IPPROTO_UDP:
        layer4 = pktlib.udp()
        layer4.srcport = ident.sport 
        layer4.dstport = ident.dport 
    elif ident.ipproto == IPPROTO_TCP:
        layer4 = pktlib.tcp()
        layer4.srcport = ident.sport 
        layer4.dstport = ident.dport 
        layer4.flags = flowlet.tcpflags
        layer4.off = 5
        payloadlen = max(iplen-40,0)
        layer4.tcplen = payloadlen
        layer4.payload = spack('{}x'.format(payloadlen))
    else:
        raise UnhandledPoxPacketFlowletTranslation("Can't translate IP protocol {} from flowlet to POX packet".format(fident.ipproto))
    ipv4.payload = layer4
    etherhdr.origflet = flowlet
    return etherhdr
def packet_to_flowlet(pkt):
    try:
        return getattr(pkt, "origflet")
    except AttributeError,e:
        log = get_logger()
        flet = None
        ip = pkt.find('ipv4')
        if ip is None:
            flet = PoxFlowlet(FlowIdent())
            log.debug("Received non-IP packet {} from POX: there's no direct translation to fs".format(str(pkt.payload)))
        else:
            dport = sport = tcpflags = 0
            if ip.protocol == IPPROTO_TCP:
                tcp = ip.payload
                sport = tcp.srcport
                dport = tcp.dstport
                tcpflags = tcp.flags
                log.debug("Translating POX TCP packet to fs {}".format(tcp))
            elif ip.protocol == IPPROTO_UDP:
                udp = ip.payload
                sport = udp.srcport
                dport = udp.dstport
                log.debug("Translating POX UDP packet to fs {}".format(udp))
            elif ip.protocol == IPPROTO_ICMP:
                icmp = ip.payload
                dport = (icmp.type << 8) | icmp.code
                log.debug("Translating POX ICMP packet to fs {}".format(icmp))
            else:
                log.warn("Received unhandled IPv4 packet {} from POX: can't translate to fs".format(str(ip.payload)))
            flet = PoxFlowlet(FlowIdent(srcip=ip.srcip, dstip=ip.dstip, ipproto=ip.protocol, sport=sport, dport=dport))
            flet.tcpflags = tcpflags
            flet.iptos = ip.tos
        flet.srcmac = pkt.src
        flet.dstmac = pkt.dst
        flet.pkts = 1
        flet.bytes = len(pkt)
        flet.origpkt = pkt
        return flet
class PoxBridgeSoftwareSwitch(SoftwareSwitch):
    def __init__(self, *args, **kwargs):
        SoftwareSwitch.__init__(self, *args, **kwargs)
    def _output_packet_physical(self, packet, port_num):
        self.forward(packet, port_num)
        SoftwareSwitch._output_packet_physical(self, packet, port_num)
    def set_output_packet_callback(self, fn):
        self.forward = fn
    # start here
    '''
    def _get_table_entry(self, dpid):
        print self.pox_switch
    '''
class OpenflowSwitch(Node):
    __slots__ = ['dpid', 'pox_switch', 'controller_name', 'controller_links', 'ipdests', 
                 'interface_to_port_map', 'trafgen_ip', 'autoack', 'trafgen_mac', 'dstmac_cache',
                 'trace','tracePkt']
    def __init__(self, name, measurement_config, **kwargs):
        Node.__init__(self, name, measurement_config, **kwargs)
        self.dpid = abs(hash(name))
        self.dstmac_cache = {}
        self.pox_switch = PoxBridgeSoftwareSwitch(self.dpid, name=name, 
            ports=0, miss_send_len=2**16, max_buffers=2**8, features=None)
        self.pox_switch.set_connection(self)
        self.pox_switch.set_output_packet_callback(self. send_packet)
        self.controller_name = kwargs.get('controller', 'controller')
        self.autoack = bool(eval(kwargs.get('autoack', 'False')))
        self.controller_links = {}
        self.interface_to_port_map = {}
        self.trace = bool(eval(kwargs.get('trace', 'False')))
        self.tracePkt = bool(eval(kwargs.get('tracePkt','False')))
        self.ipdests = PyTricia()
        for prefix in kwargs.get('ipdests','').split():
            self.ipdests[prefix] = True
        # explicitly add a localhost link/interface
        ipa,ipb = [ ip for ip in next(FsConfigurator.link_subnetter).iterhosts() ]
        remotemac = default_ip_to_macaddr(ipb)
        self.add_link(NullLink, ipa, ipb, 'remote', remotemac=remotemac)
        self.trafgen_ip = str(ipa)
        self.trafgen_mac = remotemac
        self.dstmac_cache[self.name] = remotemac
    @property
    def remote_macaddr(self):
        return self.trafgen_mac
    def send_packet(self, packet, port_num):
        '''Forward a data plane packet out a given port'''
        flet = packet_to_flowlet(packet)
        # has packet reached destination?
        if flet is None or self.ipdests.get(flet.dstaddr, None):
            return
        pinfo = self.ports[port_num]
        # self.logger.debug("Switch sending translated packet {}->{} from {}->{} on port {} to {}".format(packet, flet, flet.srcmac, flet.dstmac, port_num, pinfo.link.egress_name))
        pinfo.link.flowlet_arrival(flet, self.name, pinfo.remoteip)
    def send(self, ofmessage):
        '''Callback function for POX SoftwareSwitch to send an outgoing OF message
        to controller.'''
        if not self.started:
            # self.logger.debug("OF switch-to-controller deferred message {}".format(ofmessage))
            evid = 'deferred switch->controller send'
            fscore().after(0.0, evid, self.send, ofmessage)
        else:
            # self.logger.debug("OF switch-to-controller {} - {}".format(str(self.controller_links[self.controller_name]), ofmessage))
            clink = self.controller_links[self.controller_name]
            self.controller_links[self.controller_name].flowlet_arrival(OpenflowMessage(FlowIdent(), ofmessage), self.name, self.controller_name)
    def set_message_handler(self, *args):
        '''Dummy callback function for POX SoftwareSwitchBase'''
        pass
    def process_packet(self, poxpkt, inputport):
        '''Process an incoming POX packet.  Mainly want to check whether
        it's an ARP and update our ARP "table" state'''
        # self.logger.debug("Switch {} processing packet: {}".format(self.name, str(poxpkt)))
        if poxpkt.type == poxpkt.ARP_TYPE:
            if poxpkt.payload.opcode == pktlib.arp.REQUEST:
                self.logger.debug("Got ARP request: {}".format(str(poxpkt.payload)))
                arp = poxpkt.payload
                dstip = str(IPv4Address(arp.protodst))
                srcip = str(IPv4Address(arp.protosrc))
                if dstip in self.interface_to_port_map:
                    portnum = self.interface_to_port_map[dstip]


Answer:                     pinfo = self.ports[portnum]