trema/trema-edge

View on GitHub
src/switch/datapath/action.c

Summary

Maintainability
Test Coverage
/*
 * Copyright (C) 2012-2013 NEC Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include "action.h"
#include "group_table.h"
#include "port_manager.h"
#include "switch_port.h"


static action *
create_action() {
  action *new_action = xmalloc( sizeof( action ) );
  memset( new_action, 0, sizeof( action ) );

  return new_action;
}


action *
create_action_output( const uint32_t port, const uint16_t max_len ) {
  action *action = create_action();

  action->type = OFPAT_OUTPUT;
  action->port = port;
  action->max_len = max_len;

  return action;
}


action *
create_action_group( const uint32_t group_id ) {
  action *action = create_action();

  action->type = OFPAT_GROUP;
  action->group_id = group_id;

  return action;
}


action *
create_action_set_queue( const uint32_t queue_id ) {
  action *action = create_action();

  action->type = OFPAT_SET_QUEUE;
  action->queue_id = queue_id;

  return action;
}


action *
create_action_set_mpls_ttl( const uint8_t mpls_ttl ) {
  action *action = create_action();

  action->type = OFPAT_SET_MPLS_TTL;
  action->mpls_ttl = mpls_ttl;

  return action;
}


action *
create_action_dec_mpls_ttl( void ) {
  action *action = create_action();

  action->type = OFPAT_DEC_MPLS_TTL;

  return action;
}


action *
create_action_set_ipv4_ttl( const uint8_t nw_ttl ) {
  action *action = create_action();

  action->type = OFPAT_SET_NW_TTL;
  action->nw_ttl = nw_ttl;

  return action;
}


action *
create_action_dec_ipv4_ttl() {
  action *action = create_action();

  action->type = OFPAT_DEC_NW_TTL;

  return action;
}


action *
create_action_copy_ttl_out( void ) {
  action *action = create_action();

  action->type = OFPAT_COPY_TTL_OUT;

  return action;
}


action *
create_action_copy_ttl_in() {
  action *action = create_action();

  action->type = OFPAT_COPY_TTL_IN;

  return action;
}


action *
create_action_push_vlan( const uint16_t ethertype ) {
  action *action = create_action();

  action->type = OFPAT_PUSH_VLAN;
  action->ethertype = ethertype;

  return action;
}


action *
create_action_push_mpls( const uint16_t ethertype ) {
  action *action = create_action();

  action->type = OFPAT_PUSH_MPLS;
  action->ethertype = ethertype;

  return action;
}


action *
create_action_push_pbb( const uint16_t ethertype ) {
  action *action = create_action();

  action->type = OFPAT_PUSH_PBB;
  action->ethertype = ethertype;

  return action;
}


action *
create_action_pop_vlan( void ) {
  action *action = create_action();

  action->type = OFPAT_POP_VLAN;

  return action;
}


action *
create_action_pop_mpls( const uint16_t ethertype ) {
  action *action = create_action();

  action->type = OFPAT_POP_MPLS;
  action->ethertype = ethertype;

  return action;
}


action *
create_action_pop_pbb( void ) {
  action *action = create_action();

  action->type = OFPAT_POP_PBB;

  return action;
}


action *
create_action_set_field( match *match ) {
  action *action = create_action();

  action->type = OFPAT_SET_FIELD;
  action->match = match;

  return action;
}


void
delete_action( action *action ) {
  assert( action != NULL );

  if ( action->match != NULL ) {
    delete_match( action->match );
  }
  xfree( action );
}


action_list *
create_action_list() {
  return create_dlist();
}


void
delete_action_list( action_list *list ) {
  assert( list != NULL );

  for ( dlist_element *element = get_first_element( list ); element != NULL; element = element->next ) {
    action *action = element->data;
    if ( action != NULL ) {
      delete_action( action );
    }
  }
  delete_dlist( list );
}


OFDPE
append_action( action_list *list, action *action ) {
  assert( list != NULL );
  assert( action != NULL );

  switch ( action->type ) {
    case OFPAT_OUTPUT:
    case OFPAT_COPY_TTL_OUT:
    case OFPAT_COPY_TTL_IN:
    case OFPAT_SET_MPLS_TTL:
    case OFPAT_DEC_MPLS_TTL:
    case OFPAT_PUSH_VLAN:
    case OFPAT_POP_VLAN:
    case OFPAT_PUSH_MPLS:
    case OFPAT_POP_MPLS:
    case OFPAT_SET_QUEUE:
    case OFPAT_GROUP:
    case OFPAT_SET_NW_TTL:
    case OFPAT_DEC_NW_TTL:
    case OFPAT_SET_FIELD:
    case OFPAT_PUSH_PBB:
    case OFPAT_POP_PBB:
      break;

    default:
      return ERROR_OFDPE_BAD_ACTION_BAD_TYPE;
  }

  list = insert_before_dlist( list, ( void * ) action );
  if ( list == NULL ) {
    return ERROR_APPEND_TO_LIST;
  }

  return OFDPE_SUCCESS;
}


OFDPE
remove_action( action_list *list, action *action ) {
  assert( list != NULL );
  assert( action != NULL );
  
  dlist_element *element = find_element( get_first_element( list ), action );
  if ( element == NULL ) {
    return ERROR_NOT_FOUND;
  }
  delete_action( action );
  delete_dlist_element( element );

  return OFDPE_SUCCESS;
}


action *
duplicate_action( const action *src ) {
  assert( src != NULL );

  action *dst = create_action();
  memcpy( dst, src, sizeof( action ) );
  if ( src->match != NULL ) {
    dst->match = duplicate_match( src->match );
  }

  return dst;
}


action_list *
duplicate_action_list( action_list *list ) {
  if ( list == NULL ) {
    return NULL;
  }

  dlist_element *dst = create_action_list();
  for ( dlist_element *element = get_first_element( list ); element != NULL; element = element->next ) {
    action *src_action = element->data;
    if ( src_action != NULL ) {
      action *dst_action = duplicate_action( src_action );
      insert_before_dlist( dst, dst_action );
    }
  }

  return dst;
}


bool
validate_action_set( action_list *list ) {
  assert( list != NULL );

  bool copy_ttl_inwards = false;
  bool pop = false;
  bool push_mpls = false;
  bool push_pbb = false;
  bool push_vlan = false;
  bool copy_ttl_outwards = false;
  bool decrement_ttl = false;
  bool set = false;
  bool qos = false;
  bool group = false;
  bool output = false;

  bool ret = true;
  for ( dlist_element *element = get_first_element( list ); element != NULL; element = element->next ) {
    action *action = element->data;
    if ( action == NULL ) {
      continue;
    }
    switch( action->type ) {
      case OFPAT_OUTPUT:
      {
        if ( output ) {
          ret = false;
        }
        output = true;
      }
      break;

      case OFPAT_COPY_TTL_OUT:
      {
        if ( copy_ttl_outwards ) {
          ret = false;
        }
        copy_ttl_outwards = true;
      }
      break;

      case OFPAT_COPY_TTL_IN:
      {
        if ( copy_ttl_inwards ) {
          ret = false;
        }
        copy_ttl_inwards = true;
      }
      break;

      case OFPAT_SET_MPLS_TTL:
      case OFPAT_SET_NW_TTL:
      {
        ret = false;
      }
      break;

      case OFPAT_PUSH_VLAN:
      {
        if ( push_vlan ) {
          ret = false;
        }
        push_vlan = true;
      }
      break;

      case OFPAT_PUSH_MPLS:
      {
        if ( push_mpls ) {
          ret = false;
        }
        push_mpls = true;
      }
      break;

      case OFPAT_PUSH_PBB:
      {
        if ( push_pbb ) {
          ret = false;
        }
        push_pbb = true;
      }
      break;

      case OFPAT_POP_VLAN:
      case OFPAT_POP_MPLS:
      case OFPAT_POP_PBB:
      {
        if ( pop ) {
          ret = false;
        }
        pop = true;
      }
      break;

      case OFPAT_DEC_MPLS_TTL:
      case OFPAT_DEC_NW_TTL:
      {
        if ( decrement_ttl ) {
         ret = false;
        }
        decrement_ttl = true;
      }
      break;

      case OFPAT_SET_QUEUE:
      {
        if ( qos ) {
          ret = false;
        }
        qos = true;
      }
      break;

      case OFPAT_SET_FIELD:
      {
        if ( set ) {
          ret = false;
        }
        set = true;
      }
      break;

      case OFPAT_GROUP:
      {
        if ( group ) {
          ret = false;
        }
        group = true;
      }
      break;

      case OFPAT_EXPERIMENTER:
      {
        ret = false;
      }
      break;
    }

    if ( !ret ) {
      break;
    }
  }

  return ret;
}


OFDPE
validate_action_list( action_list *list ) {
  if ( list == NULL ) {
    return true;
  }

  OFDPE ret = OFDPE_SUCCESS;
  for ( dlist_element *element = get_first_element( list ); element != NULL; element = element->next ) {
    if ( element->data == NULL ) {
      continue;
    }
    action *action = element->data;
    if ( action->type == OFPAT_GROUP ) {
      if ( !group_exists( action->group_id ) ) {
        ret = ERROR_OFDPE_BAD_ACTION_BAD_OUT_GROUP;
        break;
      }
    }
    if ( action->type == OFPAT_OUTPUT ) {
      if ( action->port > 0 && action->port <= OFPP_MAX ) {
        if ( !switch_port_exists( action->port ) ) {
          ret = ERROR_OFDPE_BAD_ACTION_BAD_OUT_PORT;
        }
        break;
      }
      else if ( action->port != OFPP_ALL && action->port != OFPP_FLOOD &&
                action->port != OFPP_IN_PORT && action->port != OFPP_CONTROLLER ) {
        ret = ERROR_OFDPE_BAD_ACTION_BAD_OUT_PORT;
        break;
      }
    }
  }

  return ret;
}


void
clear_action_set( action_set *set ) {
  assert( set != NULL );

  set->copy_ttl_in = NULL;
  set->copy_ttl_out = NULL;
  set->dec_mpls_ttl = NULL;
  set->dec_nw_ttl = NULL;
  set->group = NULL;
  set->output = NULL;
  set->pop_mpls = NULL;
  set->pop_pbb = NULL;
  set->pop_vlan = NULL;
  set->push_mpls = NULL;
  set->push_pbb = NULL;
  set->push_vlan = NULL;
  if ( set->set_field != NULL ) {
    delete_action( set->set_field );
  }
  set->set_field = NULL;
  set->set_mpls_ttl = NULL;
  set->set_nw_ttl = NULL;
  set->set_queue = NULL;
}


OFDPE
write_action_set( action_list *list, action_set *set ) {
  assert( list != NULL );
  assert( set != NULL );

  OFDPE ret = OFDPE_SUCCESS;
  for ( dlist_element *element = get_first_element( list ); element != NULL; element = element->next ) {
    if ( element->data == NULL ) {
      continue;
    }
    action *act = element->data;

    debug( "Writing an action ( type = %#x ) to action set ( set = %p ).", act->type, set );

    switch ( act->type ) {
      case OFPAT_OUTPUT:
      {
        set->output = act;
      }
      break;

      case OFPAT_COPY_TTL_OUT:
      {
        set->copy_ttl_out = act;
      }
      break;

      case OFPAT_COPY_TTL_IN:
      {
        set->copy_ttl_in = act;
      }
      break;

      case OFPAT_SET_MPLS_TTL:
      {
        set->set_mpls_ttl = act;
      }
      break;

      case OFPAT_DEC_MPLS_TTL:
      {
        set->dec_mpls_ttl = act;
      }
      break;

      case OFPAT_PUSH_VLAN:
      {
        set->push_vlan = act;
      }
      break;

      case OFPAT_POP_VLAN:
      {
        set->pop_vlan = act;
      }
      break;

      case OFPAT_PUSH_MPLS:
      {
        set->push_mpls = act;
      }
      break;

      case OFPAT_POP_MPLS:
      {
        set->pop_mpls = act;
      }
      break;

      case OFPAT_SET_QUEUE:
      {
        set->set_queue = act;
      }
      break;

      case OFPAT_GROUP:
      {
        set->group = act;
      }
      break;

      case OFPAT_SET_NW_TTL:
      {
        set->set_nw_ttl = act;
      }
      break;

      case OFPAT_DEC_NW_TTL:
      {
        set->dec_nw_ttl = act;
      }
      break;

      case OFPAT_SET_FIELD:
      {
        if ( set->set_field == NULL ) {
          set->set_field = create_action_set_field( duplicate_match( act->match ) );
        }
        else{
          merge_match( set->set_field->match, ( const match * ) act->match );
        }
      }
      break;

      case OFPAT_PUSH_PBB:
      {
        set->push_pbb = act;
      }
      break;

      case OFPAT_POP_PBB:
      {
        set->pop_pbb = act;
      }
      break;

      case OFPAT_EXPERIMENTER:
      {
        error( "OFPAT_EXPERIMENTER is not implemented." );
        ret = ERROR_OFDPE_BAD_ACTION_BAD_TYPE;
      }
      break;

      default:
      {
        error( "Undefined action type ( %#x ).", act->type );
        ret = ERROR_INVALID_PARAMETER;
      }
      break;
    }
  }

  return ret;
}


void
dump_action_capabilities( const action_capabilities capabilities ) {
  print_bitmap( capabilities, ACTION_OUTPUT, "output" );
  print_bitmap( capabilities, ACTION_COPY_TTL_OUT, "copy_ttl_out" );
  print_bitmap( capabilities, ACTION_COPY_TTL_IN, "copy_ttl_in" );
  print_bitmap( capabilities, ACTION_SET_MPLS_TTL, "set_mpls_ttl" );
  print_bitmap( capabilities, ACTION_DEC_MPLS_TTL, "dec_mpls_ttl" );
  print_bitmap( capabilities, ACTION_PUSH_VLAN, "push_vlan" );
  print_bitmap( capabilities, ACTION_POP_VLAN, "pop_vlan" );
  print_bitmap( capabilities, ACTION_PUSH_MPLS, "push_mpls" );
  print_bitmap( capabilities, ACTION_POP_MPLS, "pop_mpls" );
  print_bitmap( capabilities, ACTION_SET_QUEUE, "set_queue" );
  print_bitmap( capabilities, ACTION_GROUP, "group" );
  print_bitmap( capabilities, ACTION_SET_NW_TTL, "set_nw_ttl" );
  print_bitmap( capabilities, ACTION_DEC_NW_TTL, "dec_nw_ttl" );
  print_bitmap( capabilities, ACTION_SET_FIELD, "set_field" );
  print_bitmap( capabilities, ACTION_PUSH_PBB, "push_pbb" );
  print_bitmap( capabilities, ACTION_POP_PBB, "pop_pbb" );
  print_bitmap( capabilities, ACTION_EXPERIMENTER, "experimenter" );
}


void
dump_action( const action *action, void dump_function( const char *format, ... ) ) {
  assert( action != NULL );
  assert( dump_function != NULL );

  switch ( action->type ) {
    case OFPAT_OUTPUT:
    {
      ( *dump_function )( "type: OUTPUT ( %#x )", action->type );
      ( *dump_function )( "port: %u ( %#x )", action->port, action->port );
      ( *dump_function )( "max_len: %u ( %#x )", action->max_len, action->max_len );
    }
    break;

    case OFPAT_COPY_TTL_OUT:
    {
      ( *dump_function )( "type: COPY_TTL_OUT ( %#x )", action->type );
    }
    break;

    case OFPAT_COPY_TTL_IN:
    {
      ( *dump_function )( "type: COPY_TTL_IN ( %#x )", action->type );
    }
    break;

    case OFPAT_SET_MPLS_TTL:
    {
      ( *dump_function )( "type: SET_MPLS_TTL ( %#x )", action->type );
      ( *dump_function )( "mpls_ttl: %u ( %#x )", action->mpls_ttl, action->mpls_ttl );
    }
    break;

    case OFPAT_DEC_MPLS_TTL:
    {
      ( *dump_function )( "type: DEC_MPLS_TTL ( %#x )", action->type );
    }
    break;

    case OFPAT_PUSH_VLAN:
    {
      ( *dump_function )( "type: PUSH_VLAN ( %#x )", action->type );
      ( *dump_function )( "ethertype: %#x", action->ethertype );
    }
    break;

    case OFPAT_POP_VLAN:
    {
      ( *dump_function )( "type: POP_VLAN ( %#x )", action->type );
    }
    break;

    case OFPAT_PUSH_MPLS:
    {
      ( *dump_function )( "type: PUSH_MPLS ( %#x )", action->type );
      ( *dump_function )( "ethertype: %#x", action->ethertype );
    }
    break;

    case OFPAT_POP_MPLS:
    {
      ( *dump_function )( "type: POP_MPLS ( %#x )", action->type );
    }
    break;

    case OFPAT_SET_QUEUE:
    {
      ( *dump_function )( "type: SET_QUEUE ( %#x )", action->type );
      ( *dump_function )( "queue_id: %u ( %#x )", action->queue_id, action->queue_id );
    }
    break;

    case OFPAT_GROUP:
    {
      ( *dump_function )( "type: GROUP ( %#x )", action->type );
      ( *dump_function )( "group_id: %u ( %#x )", action->group_id, action->group_id );
    }
    break;

    case OFPAT_SET_NW_TTL:
    {
      ( *dump_function )( "type: SET_NW_TTL ( %#x )", action->type );
      ( *dump_function )( "nw_ttl: %u ( %#x )", action->nw_ttl, action->nw_ttl );
    }
    break;

    case OFPAT_DEC_NW_TTL:
    {
      ( *dump_function )( "type: DEC_NW_TTL ( %#x )", action->type );
    }
    break;

    case OFPAT_SET_FIELD:
    {
      ( *dump_function )( "type: SET_FIELD ( %#x )", action->type );
      dump_match( action->match, dump_function );
    }
    break;

    case OFPAT_PUSH_PBB:
    {
      ( *dump_function )( "type: PUSH_PBB ( %#x )", action->type );
      ( *dump_function )( "ethertype: %#x", action->ethertype );
    }
    break;

    case OFPAT_POP_PBB:
    {
      ( *dump_function )( "type: POP_PBB ( %#x )", action->type );
    }
    break;

    case OFPAT_EXPERIMENTER:
    {
      ( *dump_function )( "type: EXPERIMENTER ( %#x )", action->type );
    }
    break;

    default:
    {
      ( *dump_function )( "type: UNDEFINED ( %#x )", action->type );
    }
    break;
  }

  ( *dump_function )( "flow entry: %p", action->entry );
}


void
dump_action_list( action_list *list, void dump_function( const char *format, ... ) ) {
  assert( dump_function != NULL );

  for ( dlist_element *e = get_first_element( list ); e != NULL; e = e->next ) {
    if ( e->data == NULL ) {
      continue;
    }
    dump_action( e->data, dump_function );
  }
}


/*
 * Local variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */