/* Copyright (c) 2008, 2009 The Board of Trustees of The Leland Stanford
 * Junior University
 * 
 * We are making the OpenFlow specification and associated documentation
 * (Software) available for public use and benefit with the expectation
 * that others will use, modify and enhance the Software and contribute
 * those enhancements back to the community. However, since we would
 * like to make the Software available for broadest use, with as few
 * restrictions as possible permission is hereby granted, free of
 * charge, to any person obtaining a copy of this Software to deal in
 * the Software under the copyrights without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * The name and trademarks of copyright holder(s) may NOT be used in
 * advertising or publicity pertaining to the Software or any
 * derivatives without specific, written prior permission.
 */

/* Functions for executing OpenFlow actions. */

#include <arpa/inet.h>
#include "csum.h"
#include "packets.h"
#include "dp_act.h"
#include "openflow/nicira-ext.h"

#include "nx_act.h"
// MPLS: start
#include "openflow/ericsson-ext.h"
#include "er_act.h"
// MPLS: end



// MPLS: end
// rewrite the 20-bit label for the label on the top of the label stack.
void set_mpls_label_act(struct ofpbuf *buffer,
                                                const struct sw_flow_key *key,
                                                const uint32_t label_out)
{
        struct eth_header *eth_h;
        mpls_header mpls_h;
        uint32_t value_to_swap;
        uint16_t eth_proto;

        eth_proto = ntohs(key->flow.dl_type);
        eth_h = (struct eth_header *) buffer->l2;

        if (eth_proto == ETH_TYPE_MPLS_UNICAST) {
                mpls_h.value = htonl(*((uint32_t*)buffer->l2_5));
                mpls_h.label = ntohl(label_out);
                value_to_swap = htonl(mpls_h.value);
                *((uint32_t*)buffer->l2_5) = value_to_swap;
        }
}


// rewrite the 3 exp bits for the label on the top of the label stack.
void set_mpls_exp_act(struct ofpbuf *buffer,
                                          const struct sw_flow_key *key,
                                          const uint8_t exp)
{
        struct eth_header *eth_h;
        mpls_header mpls_h;
        uint32_t value_to_swap;
        uint16_t eth_proto;
        
        eth_proto = ntohs(key->flow.dl_type);
        eth_h = (struct eth_header *) buffer->l2;
        
        if (eth_proto == ETH_TYPE_MPLS_UNICAST) {
                mpls_h.value = htonl(*((uint32_t*)buffer->l2_5));
                mpls_h.exp = exp;
                value_to_swap = htonl(mpls_h.value);
                *((uint32_t*)buffer->l2_5) = value_to_swap;
        }
}
// MPLS: end

static uint16_t
validate_output(struct datapath *dp UNUSED, const struct sw_flow_key *key, 
        const struct ofp_action_header *ah) 
{
    struct ofp_action_output *oa = (struct ofp_action_output *)ah;
    /* To prevent loops, make sure there's no action to send to the
     * OFP_TABLE virtual port.
     */

// MPLS: start
     if (htonl(oa->port) < OFPP_VP_START) {	
 	  // port is no 32-bit
  	  if (oa->port == htonl(OFPP_NONE) ||
	  // if (oa->port == htons(OFPP_NONE) || 
        	    (!(key->wildcards & OFPFW_IN_PORT) 
                	    && oa->port == key->flow.in_port)) {
    	   return OFPBAC_BAD_OUT_PORT;
    	   }
      }
// MPLS: end
    return ACT_VALIDATION_OK;
}

static uint16_t
validate_queue(struct datapath *dp UNUSED, const struct sw_flow_key *key UNUSED,
               const struct ofp_action_header *ah)
{
    struct ofp_action_enqueue *ea = (struct ofp_action_enqueue *)ah;
	/* Only physical ports may have queues. */
    if (ntohs(ea->port) > OFPP_MAX && ntohs(ea->port) != OFPP_IN_PORT) {
        return OFPBAC_BAD_OUT_PORT;
    }
    return ACT_VALIDATION_OK;
}

// MPLS: start not static
//static
// MPLS: end
void
do_output(struct datapath *dp, struct ofpbuf *buffer, int in_port,
          size_t max_len, int out_port, uint32_t queue_id,
          bool ignore_no_fwd)
{
    if (out_port != OFPP_CONTROLLER) {
        dp_output_port(dp, buffer, in_port, out_port, queue_id, ignore_no_fwd);
    } else {
        dp_output_control(dp, buffer, in_port, max_len, OFPR_ACTION);
    }
}

/* Modify vlan tag control information (TCI).  Only sets the TCI bits
 * indicated by 'mask'.  If no vlan tag is present, one is added.
 */
static void
modify_vlan_tci(struct ofpbuf *buffer, struct sw_flow_key *key,
        uint16_t tci, uint16_t mask)
{
    struct vlan_eth_header *veh;

    if (key->flow.dl_vlan != htons(OFP_VLAN_NONE)) {
        /* Modify vlan id, but maintain other TCI values */
        veh = buffer->l2;
        veh->veth_tci &= ~htons(mask);
        veh->veth_tci |= htons(tci);
    } else {
        /* Insert new vlan id. */
        struct eth_header *eh = buffer->l2;
        struct vlan_eth_header tmp;
        memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
        memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
        tmp.veth_type = htons(ETH_TYPE_VLAN);
        tmp.veth_tci = htons(tci);
        tmp.veth_next_type = eh->eth_type;

        veh = ofpbuf_push_uninit(buffer, VLAN_HEADER_LEN);
        memcpy(veh, &tmp, sizeof tmp);
        buffer->l2 = (char*)buffer->l2 - VLAN_HEADER_LEN;
    }

    key->flow.dl_vlan = veh->veth_tci & htons(VLAN_VID_MASK);
    key->flow.dl_vlan_pcp = (uint8_t)((ntohs(veh->veth_tci) >> VLAN_PCP_SHIFT)
                                      & VLAN_PCP_BITMASK);
}


/* Remove an existing vlan header if it exists. */
static void
vlan_pull_tag(struct ofpbuf *buffer)
{
    struct vlan_eth_header *veh = buffer->l2;

    if (veh->veth_type == htons(ETH_TYPE_VLAN)) {
        struct eth_header tmp;

        memcpy(tmp.eth_dst, veh->veth_dst, ETH_ADDR_LEN);
        memcpy(tmp.eth_src, veh->veth_src, ETH_ADDR_LEN);
        tmp.eth_type = veh->veth_next_type;

        buffer->size -= VLAN_HEADER_LEN;
        buffer->data = (char*)buffer->data + VLAN_HEADER_LEN;
        buffer->l2 = (char*)buffer->l2 + VLAN_HEADER_LEN;
        memcpy(buffer->data, &tmp, sizeof tmp);
    }
}

static void
set_vlan_vid(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah)
{
    struct ofp_action_vlan_vid *va = (struct ofp_action_vlan_vid *)ah;
    uint16_t tci = ntohs(va->vlan_vid);

    modify_vlan_tci(buffer, key, tci, VLAN_VID_MASK);
}

static void
set_vlan_pcp(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah)
{
    struct ofp_action_vlan_pcp *va = (struct ofp_action_vlan_pcp *)ah;
    uint16_t tci = (uint16_t)va->vlan_pcp << 13;

    modify_vlan_tci(buffer, key, tci, VLAN_PCP_MASK);
}

static void
strip_vlan(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah UNUSED)
{
    vlan_pull_tag(buffer);
    key->flow.dl_vlan = htons(OFP_VLAN_NONE);
}

static void
set_dl_addr(struct ofpbuf *buffer, struct sw_flow_key *key UNUSED, 
        const struct ofp_action_header *ah)
{
    struct ofp_action_dl_addr *da = (struct ofp_action_dl_addr *)ah;
    struct eth_header *eh = buffer->l2;

    if (da->type == htons(OFPAT_SET_DL_SRC)) {
        memcpy(eh->eth_src, da->dl_addr, sizeof eh->eth_src);
    } else {
        memcpy(eh->eth_dst, da->dl_addr, sizeof eh->eth_dst);
    }
}

// MPLS: start
static void
set_mpls_label(struct ofpbuf *buffer, struct sw_flow_key *key,
        const struct ofp_action_header *ah)
{
    struct ofp_action_mpls_label *ml = (struct ofp_action_mpls_label *)ah;
    set_mpls_label_act(buffer, key, ml->label_out);
}

static void
set_mpls_exp(struct ofpbuf *buffer, struct sw_flow_key *key,
        const struct ofp_action_header *ah)
{
    struct ofp_action_mpls_exp *me = (struct ofp_action_mpls_exp *)ah;
    set_mpls_exp_act(buffer, key, me->exp);
}
// MPLS: end

static void
set_nw_addr(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah)
{
    struct ofp_action_nw_addr *na = (struct ofp_action_nw_addr *)ah;
    uint16_t eth_proto = ntohs(key->flow.dl_type);

    if (eth_proto == ETH_TYPE_IP) {
        struct ip_header *nh = buffer->l3;
        uint8_t nw_proto = key->flow.nw_proto;
        uint32_t new, *field;

        new = na->nw_addr;
        field = na->type == htons(OFPAT_SET_NW_SRC) ? &nh->ip_src : &nh->ip_dst;
        if (nw_proto == IP_TYPE_TCP) {
            struct tcp_header *th = buffer->l4;
            th->tcp_csum = recalc_csum32(th->tcp_csum, *field, new);
        } else if (nw_proto == IP_TYPE_UDP) {
            struct udp_header *th = buffer->l4;
            if (th->udp_csum) {
                th->udp_csum = recalc_csum32(th->udp_csum, *field, new);
                if (!th->udp_csum) {
                    th->udp_csum = 0xffff;
                }
            }
        }
        nh->ip_csum = recalc_csum32(nh->ip_csum, *field, new);
        *field = new;
    }
}

static void
set_nw_tos(struct ofpbuf *buffer, struct sw_flow_key *key, 
           const struct ofp_action_header *ah)
{
    struct ofp_action_nw_tos *nt = (struct ofp_action_nw_tos *)ah;
    uint16_t eth_proto = ntohs(key->flow.dl_type);

   if (eth_proto == ETH_TYPE_IP) {
       struct ip_header *nh = buffer->l3;
       uint8_t new, *field;

       /* JeanII : Set only 6 bits, don't clobber ECN */
       new = (nt->nw_tos & 0xFC) | (nh->ip_tos & 0x03);

       /* Get address of field */
       field = &nh->ip_tos;

        /* jklee : ip tos field is not included in TCP pseudo header.
         * Need magic as update_csum() don't work with 8 bits. */
       nh->ip_csum = recalc_csum32(nh->ip_csum, htons((uint16_t)*field),
                                   htons((uint16_t)new));

       /* Change the IP ToS bits */
       *field = new;
    }
}

static void
set_tp_port(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah)
{
    struct ofp_action_tp_port *ta = (struct ofp_action_tp_port *)ah;
    uint16_t eth_proto = ntohs(key->flow.dl_type);

    if (eth_proto == ETH_TYPE_IP) {
        uint8_t nw_proto = key->flow.nw_proto;
        uint16_t new, *field;

        new = ta->tp_port;
        if (nw_proto == IP_TYPE_TCP) {
            struct tcp_header *th = buffer->l4;
            field = ta->type == htons(OFPAT_SET_TP_SRC) ? &th->tcp_src : &th->tcp_dst;
            th->tcp_csum = recalc_csum16(th->tcp_csum, *field, new);
            *field = new;
        } else if (nw_proto == IP_TYPE_UDP) {
            struct udp_header *th = buffer->l4;
            field = ta->type == htons(OFPAT_SET_TP_SRC) ? &th->udp_src : &th->udp_dst;
            th->udp_csum = recalc_csum16(th->udp_csum, *field, new);
            *field = new;
        }
    }
}

struct openflow_action {
    size_t min_size;
    size_t max_size;
    uint16_t (*validate)(struct datapath *dp, 
            const struct sw_flow_key *key,
            const struct ofp_action_header *ah);
    void (*execute)(struct ofpbuf *buffer,
            struct sw_flow_key *key, 
            const struct ofp_action_header *ah);
};

static const struct openflow_action of_actions[] = {
    [OFPAT_OUTPUT] = {
        sizeof(struct ofp_action_output),
        sizeof(struct ofp_action_output),
        validate_output,
        NULL                   /* This is optimized into execute_actions */
    },
    [OFPAT_ENQUEUE] = {
        sizeof(struct ofp_action_enqueue),
        sizeof(struct ofp_action_enqueue),
        validate_queue,
        NULL         /* This is optimized into execute_actions */
    },
    [OFPAT_SET_VLAN_VID] = {
        sizeof(struct ofp_action_vlan_vid),
        sizeof(struct ofp_action_vlan_vid),
        NULL,
        set_vlan_vid
    },
    [OFPAT_SET_VLAN_PCP] = {
        sizeof(struct ofp_action_vlan_pcp),
        sizeof(struct ofp_action_vlan_pcp),
        NULL,
        set_vlan_pcp
    },
    [OFPAT_STRIP_VLAN] = {
        sizeof(struct ofp_action_header),
        sizeof(struct ofp_action_header),
        NULL,
        strip_vlan
    },
    [OFPAT_SET_DL_SRC] = {
        sizeof(struct ofp_action_dl_addr),
        sizeof(struct ofp_action_dl_addr),
        NULL,
        set_dl_addr
    },
    [OFPAT_SET_DL_DST] = {
        sizeof(struct ofp_action_dl_addr),
        sizeof(struct ofp_action_dl_addr),
        NULL,
        set_dl_addr
    },
    [OFPAT_SET_NW_SRC] = {
        sizeof(struct ofp_action_nw_addr),
        sizeof(struct ofp_action_nw_addr),
        NULL,
        set_nw_addr
    },
    [OFPAT_SET_NW_DST] = {
        sizeof(struct ofp_action_nw_addr),
        sizeof(struct ofp_action_nw_addr),
        NULL,
        set_nw_addr
    },
    [OFPAT_SET_NW_TOS] = {
        sizeof(struct ofp_action_nw_tos),
        sizeof(struct ofp_action_nw_tos),
        NULL,
        set_nw_tos
    },
    [OFPAT_SET_TP_SRC] = {
        sizeof(struct ofp_action_tp_port),
        sizeof(struct ofp_action_tp_port),
        NULL,
        set_tp_port
    },
    [OFPAT_SET_TP_DST] = {
        sizeof(struct ofp_action_tp_port),
        sizeof(struct ofp_action_tp_port),
        NULL,
        set_tp_port
    },
    // MPLS: start
    [OFPAT_SET_MPLS_LABEL] = {
        sizeof(struct ofp_action_mpls_label),
        sizeof(struct ofp_action_mpls_label),
        NULL,
        set_mpls_label
    },
    [OFPAT_SET_MPLS_EXP] = {
                sizeof(struct ofp_action_mpls_exp),
                sizeof(struct ofp_action_mpls_exp),
                NULL,
                set_mpls_exp
        }
    // MPLS: end
    /* OFPAT_VENDOR is not here, since it would blow up the array size. */
};

/* Validate built-in OpenFlow actions.  Either returns ACT_VALIDATION_OK
 * or an OFPET_BAD_ACTION error code. */
static uint16_t 
validate_ofpat(struct datapath *dp, const struct sw_flow_key *key, 
        const struct ofp_action_header *ah, uint16_t type, uint16_t len)
{
    uint16_t ret = ACT_VALIDATION_OK;
    const struct openflow_action *act = &of_actions[type];

    if ((len < act->min_size) || (len > act->max_size)) {
        return OFPBAC_BAD_LEN;
    }

    if (act->validate) {
        ret = act->validate(dp, key, ah);
    }

    return ret;
}

/* Validate vendor-defined actions.  Either returns ACT_VALIDATION_OK
 * or an OFPET_BAD_ACTION error code. */
static uint16_t 
validate_vendor(struct datapath *dp UNUSED, const struct sw_flow_key *key UNUSED, 
        const struct ofp_action_header *ah, uint16_t len)
{
    struct ofp_action_vendor_header *avh;
    int ret = ACT_VALIDATION_OK;

    if (len < sizeof(struct ofp_action_vendor_header)) {
        return OFPBAC_BAD_LEN;
    }

    avh = (struct ofp_action_vendor_header *)ah;

    switch(ntohl(avh->vendor)) {

    // MPLS: start
    case ER_VENDOR_ID:
        ret = er_validate_act(dp, key, avh, len);
        break;
    // MPLS: end

    default:
        return OFPBAC_BAD_VENDOR;
    }

    return ret;
}

/* Validates a list of actions.  If a problem is found, a code for the
 * OFPET_BAD_ACTION error type is returned.  If the action list validates, 
 * ACT_VALIDATION_OK is returned. */
uint16_t 
validate_actions(struct datapath *dp, const struct sw_flow_key *key,
        const struct ofp_action_header *actions, size_t actions_len)
{
    uint8_t *p = (uint8_t *)actions;
    int err;

    while (actions_len >= sizeof(struct ofp_action_header)) {
        struct ofp_action_header *ah = (struct ofp_action_header *)p;
        size_t len = ntohs(ah->len);
        uint16_t type;

        /* Make there's enough remaining data for the specified length
         * and that the action length is a multiple of 64 bits. */
        if ((actions_len < len) || (len % 8) != 0) {
            return OFPBAC_BAD_LEN;
        }

        type = ntohs(ah->type);
        if (type < ARRAY_SIZE(of_actions)) {
            err = validate_ofpat(dp, key, ah, type, len);
            if (err != ACT_VALIDATION_OK) {
                return err;
            }
        } else if (type == OFPAT_VENDOR) {
            err = validate_vendor(dp, key, ah, len);
            if (err != ACT_VALIDATION_OK) {
                return err;
            }
        } else {
            return OFPBAC_BAD_TYPE;
        }

        p += len;
        actions_len -= len;
    }

    /* Check if there's any trailing garbage. */
    if (actions_len != 0) {
        return OFPBAC_BAD_LEN;
    }

    return ACT_VALIDATION_OK;
}

/* Execute a built-in OpenFlow action against 'buffer'. */
static void
execute_ofpat(struct ofpbuf *buffer, struct sw_flow_key *key, 
        const struct ofp_action_header *ah, uint16_t type)
{
    const struct openflow_action *act = &of_actions[type];

    if (act->execute) {
        act->execute(buffer, key, ah);
    }
}

/* Execute a vendor-defined action against 'buffer'. */
// MPLS: start
// include a reference to the datapath object for vendor actions
static void execute_vendor(struct datapath *dp, struct ofpbuf *buffer,
                const struct sw_flow_key *key, const struct ofp_action_header *ah)
//static void
//execute_vendor(struct ofpbuf *buffer, const struct sw_flow_key *key,
//        const struct ofp_action_header *ah)
// MPLS: end

//static void
//execute_vendor(struct ofpbuf *buffer UNUSED, const struct sw_flow_key *key UNUSED, 
//        const struct ofp_action_header *ah)
{
    struct ofp_action_vendor_header *avh 
            = (struct ofp_action_vendor_header *)ah;

    switch(ntohl(avh->vendor)) {

    // MPLS: start
    // add case for Ericsson Vendor ID
    case ER_VENDOR_ID:
        er_execute_act(dp, buffer, key, avh);
        break;
    // MPLS: end

    default:
        /* This should not be possible due to prior validation. */
        printf("attempt to execute action with unknown vendor: %#x\n", 
                ntohl(avh->vendor));
        break;
    }
}

/* Execute a list of actions against 'buffer'. */
void execute_actions(struct datapath *dp, struct ofpbuf *buffer,
             struct sw_flow_key *key,
             const struct ofp_action_header *actions, size_t actions_len,
             int ignore_no_fwd)
{
    /* Every output action needs a separate clone of 'buffer', but the common
     * case is just a single output action, so that doing a clone and then
     * freeing the original buffer is wasteful.  So the following code is
     * slightly obscure just to avoid that. */
    int prev_port;
    uint32_t prev_queue;
    size_t max_len = UINT16_MAX;
    uint16_t in_port = ntohs(key->flow.in_port);
    uint8_t *p = (uint8_t *)actions;

    prev_port = -1;
    prev_queue = 0;


    /* The action list was already validated, so we can be a bit looser
     * in our sanity-checking. */
    while (actions_len > 0) {
        struct ofp_action_header *ah = (struct ofp_action_header *)p;
        size_t len = htons(ah->len);

        if (prev_port != -1) {
            do_output(dp, ofpbuf_clone(buffer), in_port, max_len,
                      prev_port, prev_queue, ignore_no_fwd);
            prev_port = -1;
        }

        if (ah->type == htons(OFPAT_OUTPUT)) {
            struct ofp_action_output *oa = (struct ofp_action_output *)p;
            // MPLS: start
            // port is now 32-bits
            prev_port = ntohl(oa->port);
            //prev_port = ntohs(oa->port);
            // MPLS: end
	    prev_queue = 0; /* using the default best-effort queue */
	    max_len = ntohs(oa->max_len);
        } else if (ah->type == htons(OFPAT_ENQUEUE)) {
            struct ofp_action_enqueue *ea = (struct ofp_action_enqueue *)p;
	    // MPLS: start
   	    // port is now 32-bits
            // prev_port = ntohs(ea->port);
            prev_port = ntohl(ea->port);
            // MPLS: end
            prev_queue = ntohl(ea->queue_id);
            max_len = 0; /* we will not send to the controller anyways - useless */
        } else {
            uint16_t type = ntohs(ah->type);

            if (type < ARRAY_SIZE(of_actions)) {
                execute_ofpat(buffer, key, ah, type);
            } else if (type == OFPAT_VENDOR) {
		// MPLS: start
		execute_vendor(dp, buffer, key, ah);
                // execute_vendor(buffer, key, ah);
		// MPLS: end
            }
        }

        p += len;
        actions_len -= len;
    }
    if (prev_port != -1) {
        do_output(dp, buffer, in_port, max_len, prev_port, prev_queue, ignore_no_fwd);
    } else {
        ofpbuf_delete(buffer);
    }
}
