static void
handle_flow_mod_mod( const uint32_t transaction_id, const uint64_t cookie, 
                     const uint64_t cookie_mask, const uint8_t table_id,
                     const uint16_t idle_timeout, const uint16_t hard_timeout,
                     const uint16_t priority, const uint32_t buffer_id,
                     const uint16_t flags, const oxm_matches *oxm,
                     const openflow_instructions *instructions,
                     const bool strict, struct protocol *protocol ) {

  match *match = create_match( );
  if ( oxm != NULL && oxm->n_matches > 0 ) {
    for ( list_element *e = oxm->list; e != NULL; e = e->next ) {
      oxm_match_header *hdr = e->data;
      assign_match( match, hdr );
    }
  }

  instruction_set *ins_set = create_instruction_set();
  if ( instructions != NULL ) {
    OFDPE ret = assign_instructions( ins_set, instructions->list );
    if ( ret != OFDPE_SUCCESS ) {
      send_error_message( transaction_id, OFPET_FLOW_MOD_FAILED, OFPBIC_UNSUP_INST );
      delete_instruction_set( ins_set );
      delete_match( match );
      return;
    }
  }

  OFDPE ret = update_or_add_flow_entry( table_id, match, cookie, cookie_mask, priority, idle_timeout, hard_timeout,
                                        flags, strict, ins_set );
  delete_instruction_set( ins_set );
  delete_match( match );
  if ( ret != OFDPE_SUCCESS ) {
    uint16_t type = OFPET_FLOW_MOD_FAILED;
    uint16_t code = OFPFMFC_UNKNOWN;
    get_ofp_error( ret, &type, &code );
    send_error_message( transaction_id, type, code );
    return;
  }

  if ( buffer_id != OFP_NO_BUFFER ) {
    action_list *actions = create_action_list();
    action *action = create_action_output( OFPP_TABLE, UINT16_MAX );
    append_action( actions, action );
    ret = execute_packet_out( buffer_id, 0, actions, NULL );
    delete_action_list( actions );
    if ( ret != OFDPE_SUCCESS ) {
      uint16_t type = OFPET_FLOW_MOD_FAILED;
      uint16_t code = OFPFMFC_UNKNOWN;
      get_ofp_error( ret, &type, &code );
      send_error_message( transaction_id, type, code );
      return;
    }
    wakeup_datapath( protocol );
  }
}
static action_list *
complete_action_list( void ) {
  action_list *ac_list = init_action_list();

//  expect_value( mock_is_valid_port_no, port_no, PORT_NO );
  action *ac_output = create_action_output( PORT_NO, MAX_LEN );
  append_action( ac_list, ac_output );

  const uint32_t group_id = GROUP_ID;
  action *ac_group = create_action_group( group_id );
  append_action( ac_list, ac_group );
  
  const uint32_t queue_id = QUEUE_ID;
  action *ac_set_queue = create_action_set_queue( queue_id );
  append_action( ac_list, ac_set_queue );
  
  const uint8_t mpls_ttl = MPLS_TTL;
  action *ac_set_mpls_ttl = create_action_set_mpls_ttl( mpls_ttl );
  append_action( ac_list, ac_set_mpls_ttl );
  
  action *ac_dec_mpls_ttl = create_action_dec_mpls_ttl();
  append_action( ac_list, ac_dec_mpls_ttl );
  
  const uint8_t nw_ttl = NW_TTL;
  action *ac_set_ipv4_ttl = create_action_set_ipv4_ttl( nw_ttl );
  append_action( ac_list, ac_set_ipv4_ttl );
  

  action *ac_dec_ipv4_ttl = create_action_dec_ipv4_ttl();
  append_action( ac_list, ac_dec_ipv4_ttl );
  
  action *ac_copy_ttl_out = create_action_copy_ttl_out();
  append_action( ac_list, ac_copy_ttl_out );
  
  action *ac_copy_ttl_in = create_action_copy_ttl_in();
  append_action( ac_list, ac_copy_ttl_in );
  
  const uint16_t ether_type = VLAN_ETHERTYPE;
  action *ac_action_push_vlan = create_action_push_vlan( ether_type );
  append_action( ac_list, ac_action_push_vlan );
  

  const uint16_t mpls_type = MPLS_ETHERTYPE;
  action *ac_action_push_mpls = create_action_push_mpls( mpls_type );
  append_action( ac_list, ac_action_push_mpls );
  

  const uint16_t pbb_type = PBB_ETHERTYPE;
  action *ac_action_push_pbb = create_action_push_pbb( pbb_type );
  append_action( ac_list, ac_action_push_pbb );


  action *ac_action_pop_vlan = create_action_pop_vlan();
  append_action( ac_list, ac_action_pop_vlan );
  
  action *ac_action_pop_mpls = create_action_pop_mpls( mpls_type );
  append_action( ac_list, ac_action_pop_mpls );

  action *ac_action_pop_pbb = create_action_pop_pbb();
  append_action( ac_list, ac_action_pop_pbb );
  
  match *match = init_match();
  action *ac_action_set_field = create_action_set_field( match );
  append_action( ac_list, ac_action_set_field );
  return ac_list;
}
static void
handle_flow_mod_add( const uint32_t transaction_id, const uint64_t cookie, 
                     const uint64_t cookie_mask, const uint8_t table_id,
                     const uint16_t idle_timeout, const uint16_t hard_timeout,
                     const uint16_t priority, const uint32_t buffer_id,
                     const uint16_t flags, const oxm_matches *oxm,
                     const openflow_instructions *instructions,
                     struct protocol *protocol ) {
  UNUSED( cookie_mask );
  /*
   * currently if flags set OFPFF_SEND_FLOW_REM and OFPFF_RESET_COUNTS are the only allowed value.
   */
  if ( ( flags & ~( OFPFF_SEND_FLOW_REM | OFPFF_RESET_COUNTS ) ) != 0 ) {
    send_error_message( transaction_id, OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_FLAGS );
    return;
  }
  /*
   * The use of OFPTT_ALL is only valid for delete requests.
   */
  if ( table_id == OFPTT_ALL ) {
    send_error_message( transaction_id, OFPET_FLOW_MOD_FAILED, OFPFMFC_BAD_TABLE_ID );
    return;
  }

  /*
   * If no buffered packet is associated with a flow mod it must be set
   * to OFP_NO_BUFFER otherwise it must be equal to the buffer_id sent to
   * controller by a packet-in message.
   */

  match *match = create_match();
  if ( oxm != NULL && oxm->n_matches > 0 ) {
    
#ifdef DEBUG    
    char oxm_str[ 2048 ];
    match_to_string( oxm, oxm_str, sizeof( oxm_str ) );
    printf( "%s\n", oxm_str );
#endif
    
    for ( list_element *e = oxm->list; e != NULL; e = e->next ) {
      oxm_match_header *hdr = e->data;
      assign_match( match, hdr );
    }
  }

  instruction_set *instruction_set = create_instruction_set();
  if ( instructions != NULL ) {
    OFDPE ret = assign_instructions( instruction_set, instructions->list );
    if ( ret != OFDPE_SUCCESS ) {
      send_error_message( transaction_id, OFPET_FLOW_MOD_FAILED, OFPBIC_UNSUP_INST );
      delete_instruction_set( instruction_set );
      delete_match( match );
      return;
    }
  }

  /*
   * When a flow entry is inserted in a table, its flags field is set with the
   * values from the message.
   * When a flow entry is inserted in a table, its idle_timeout and
   * hard_timeout fields are set with the values from the message.
   * When a flow entry is inserted in a table through an OFPFC_ADD message,
   * its cookie field is set to the provided value
   */
  flow_entry *new_entry = alloc_flow_entry( match, instruction_set, priority,
                                            idle_timeout, hard_timeout, flags, cookie );
  if ( new_entry == NULL ) {
    /*
     * TODO we should send a more appropriate error once we worked out the
     * datapath errors.
     */
    delete_instruction_set( instruction_set );
    delete_match( match );
    send_error_message( transaction_id, OFPET_FLOW_MOD_FAILED, OFPFMFC_UNKNOWN );
    return;
  }

  OFDPE ret = add_flow_entry( table_id, new_entry, flags );
  if ( ret != OFDPE_SUCCESS ) {
    error( "Failed to add a flow entry ( ret = %d ).", ret );
    delete_instruction_set( instruction_set );
    delete_match( match );

    uint16_t type = OFPET_FLOW_MOD_FAILED;
    uint16_t code = OFPFMFC_UNKNOWN;
    get_ofp_error( ret, &type, &code );
    send_error_message( transaction_id, type, code );
    return;
  }

  if ( buffer_id != OFP_NO_BUFFER ) {
    action_list *actions = create_action_list();
    action *action = create_action_output( OFPP_TABLE, UINT16_MAX );
    append_action( actions, action );
    ret = execute_packet_out( buffer_id, 0, actions, NULL );
    delete_action_list( actions );
    if ( ret != OFDPE_SUCCESS ) {
      uint16_t type = OFPET_FLOW_MOD_FAILED;
      uint16_t code = OFPFMFC_UNKNOWN;
      get_ofp_error( ret, &type, &code );
      send_error_message( transaction_id, type, code );
      return;
    }
    wakeup_datapath( protocol );
  }
}