# vim: set expandtab ts=4 sw=4:
#
# Copyright (c) 2011, EPFL (Ecole Politechnique Federale de Lausanne)
# All rights reserved.
#
# Created by Marco Canini, Daniele Venzano, Dejan Kostic, Jennifer Rexford
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   -  Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#   -  Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#   -  Neither the names of the contributors, nor their associated universities or
#      organizations may be used to endorse or promote products derived from this
#      software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

import cPickle

from mac_address import MacAddress
from packet_base import packet_base

ETHER_ANY            = MacAddress((0, 0, 0, 0, 0, 0))
ETHER_BROADCAST      = MacAddress((0xff, 0xff, 0xff, 0xff, 0xff, 0xff))
BRIDGE_GROUP_ADDRESS = MacAddress((0x01, 0x80, 0xC2, 0x00, 0x00, 0x00))
LLDP_MULTICAST       = MacAddress((0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e))
PAE_MULTICAST        = MacAddress((0x01, 0x80, 0xc2, 0x00, 0x00, 0x03)) # 802.1x Port Access Entity
NDP_MULTICAST        = MacAddress((0x01, 0x23, 0x20, 0x00, 0x00, 0x01)) # Nicira discovery multicast

PKTI_VN = -1

class ethernet(packet_base):
    # Ethernet types
    IP_TYPE = 2048
    ARP_TYPE = 2054
    RARP_TYPE = 32821
    VLAN_TYPE = 33024
    LLDP_TYPE = 35020
    PAE_TYPE = 34958

# XX-CHANGE
    def __init__(self, name="noname", packet_id = None):
    # def __init__(self, name="noname", packet_id = None):

        """ name is the name of ethernet packet """
        self.name = name
        self.src = MacAddress()
        self.dst = MacAddress()
        self.type = 0
        self.arr = None

# X-CHANGE
        self.packet_id = packet_id # used by invariants

        self.annotation = [] # used by invariants
        self.fault_injection = [] # just for debugging, to help tracking packets around

# XX-CHANGE
        self.next_entry_attr = {}
        self.next_entry_vn = PKTI_VN

# XX-CHANGE
    def set_preflow_field(self, next_entry_attr = {}, next_entry_vn = PKTI_VN):
        self.next_entry_attr = next_entry_attr
        self.next_entry_vn = next_entry_vn
    def clr_preflow_field(self):
        self.next_entry_attr = {}
        self.next_entry_vn = PKTI_VN

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        if self.next != None:
            payload = " <- " + repr(self.next)
        else:
            payload = ""
# X-CHANGE
        return "eth: " + str((self.name, self.packet_id, self.src, self.dst, self.type,
        self.fault_injection, self.next_entry_attr, self.next_entry_vn)) + payload

    def __ne__(self, other):
        if not isinstance(other, ethernet):
            return True
        else:
# X-CHANGE
            return not(self.parsed == other.parsed and self.src == other.src and self.dst == other.dst
            and self.type == other.type and self.arr == other.arr and self.next == other.next
            and self.next_entry_attr == other.next_entry_attr and self.next_entry_vn == other.next_entry_vn)

    def __eq__(self, other):
        if not isinstance(other, ethernet):
            return False
        else:
# X-CHANGE
            return (self.parsed == other.parsed and self.src == other.src and self.dst == other.dst
            and self.type == other.type and self.arr == other.arr and self.next == other.next
            and self.next_entry_attr == other.next_entry_attr and self.next_entry_vn == other.next_entry_vn)

    def __hash__(self):
        ser = cPickle.dumps(self)
        return hash(ser)

    def __getstate__(self): # This is not really needed, the default would work. But it does sanity checks, so I leave it.
        filtered_dict = {}
        filtered_dict["name"] = self.name
        filtered_dict["annotation"] = sorted(self.annotation)
        filtered_dict["packet_id"] = None
        filtered_dict["fault_injection"] = False
# X-CHANGE
        filtered_dict["next_entry_attr"] = self.next_entry_attr
        filtered_dict["next_entry_vn"] = self.next_entry_vn

        try:
            filtered_dict["src"] = self.src.data
            filtered_dict["dst"] = self.dst.data
            filtered_dict["type"] = self.type
        except AttributeError:
            import sys; sys.stdin = file("/dev/stdin", "r")
            import pdb; pdb.set_trace()
        if self.next != None:
            filtered_dict["next"] = self.next
        return filtered_dict

    def tostring(self):
        return self

    def copy(self):
        cpy = ethernet()
        cpy.name = self.name
        cpy.type = self.type
        cpy.arr = self.arr
        cpy.src = self.src.copy()
        cpy.dst = self.dst.copy()
        cpy.packet_id = self.packet_id
        cpy.annotation = list(self.annotation)
        cpy.fault_injection = self.fault_injection[:]
# X-CHANGE
        cpy.next_entry_attr = self.next_entry_attr
        cpy.next_entry_vn = self.next_entry_vn
        
        if self.next != None:
            cpy.set_payload(self.next.copy())
        return cpy

