diff options
-rw-r--r-- | common/messages.h | 2 | ||||
-rw-r--r-- | server/main_channel.c | 164 | ||||
-rw-r--r-- | server/main_channel.h | 12 | ||||
-rw-r--r-- | server/red_channel.h | 2 | ||||
-rw-r--r-- | server/reds.c | 129 | ||||
-rw-r--r-- | server/reds.h | 14 | ||||
-rw-r--r-- | spice.proto | 5 |
7 files changed, 176 insertions, 152 deletions
diff --git a/common/messages.h b/common/messages.h index 8151dc00..a54190f3 100644 --- a/common/messages.h +++ b/common/messages.h @@ -66,6 +66,8 @@ typedef struct SpiceMsgMainMigrationBegin { uint16_t pub_key_type; uint32_t pub_key_size; uint8_t *pub_key_data; + uint32_t cert_subject_size; + uint8_t *cert_subject_data; } SpiceMsgMainMigrationBegin; typedef struct SpiceMsgMainMigrationSwitchHost { diff --git a/server/main_channel.c b/server/main_channel.c index 49028b3f..5b16b307 100644 --- a/server/main_channel.c +++ b/server/main_channel.c @@ -110,16 +110,6 @@ typedef struct NotifyPipeItem { int mess_len; } NotifyPipeItem; -typedef struct MigrateBeginPipeItem { - PipeItem base; - int port; - int sport; - char *host; - uint16_t cert_pub_key_type; - uint32_t cert_pub_key_len; - uint8_t *cert_pub_key; -} MigrateBeginPipeItem; - typedef struct MultiMediaTimePipeItem { PipeItem base; int time; @@ -137,6 +127,8 @@ struct MainChannelClient { SpiceTimer *ping_timer; int ping_interval; #endif + int mig_wait_connect; + int mig_connect_ok; }; enum NetTestStage { @@ -283,33 +275,6 @@ static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num return &item->base; } -typedef struct MigrateBeginItemInfo { - int port; - int sport; - char *host; - uint16_t cert_pub_key_type; - uint32_t cert_pub_key_len; - uint8_t *cert_pub_key; -} MigrateBeginItemInfo; - -// TODO: MC: migration is not tested at all with multiclient. -static PipeItem *main_migrate_begin_item_new( - RedChannelClient *rcc, void *data, int num) -{ - MigrateBeginPipeItem *item = spice_malloc(sizeof(MigrateBeginPipeItem)); - MigrateBeginItemInfo *info = data; - - red_channel_pipe_item_init(rcc->channel, &item->base, - SPICE_MSG_MAIN_MIGRATE_BEGIN); - item->port = info->port; - item->sport = info->sport; - item->host = info->host; - item->cert_pub_key_type = info->cert_pub_key_type; - item->cert_pub_key_len = info->cert_pub_key_len; - item->cert_pub_key = info->cert_pub_key; - return &item->base; -} - static PipeItem *main_multi_media_time_item_new( RedChannelClient *rcc, void *data, int num) { @@ -550,35 +515,23 @@ static void main_channel_marshall_notify(SpiceMarshaller *m, NotifyPipeItem *ite spice_marshaller_add(m, item->mess, item->mess_len + 1); } -void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, - char *host, uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, - uint8_t *cert_pub_key) -{ - MigrateBeginItemInfo info = { - .port =port, - .sport = sport, - .host = host, - .cert_pub_key_type = cert_pub_key_type, - .cert_pub_key_len = cert_pub_key_len, - .cert_pub_key = cert_pub_key, - }; - - red_channel_pipes_new_add_push(&main_chan->base, - main_migrate_begin_item_new, &info); -} - -static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, - MigrateBeginPipeItem *item) +static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc) { SpiceMsgMainMigrationBegin migrate; - - migrate.port = item->port; - migrate.sport = item->sport; - migrate.host_size = strlen(item->host) + 1; - migrate.host_data = (uint8_t *)item->host; - migrate.pub_key_type = item->cert_pub_key_type; - migrate.pub_key_size = item->cert_pub_key_len; - migrate.pub_key_data = item->cert_pub_key; + MainChannel *main_ch; + + main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); + migrate.port = main_ch->mig_target.port; + migrate.sport = main_ch->mig_target.sport; + migrate.host_size = strlen(main_ch->mig_target.host) + 1; + migrate.host_data = (uint8_t *)main_ch->mig_target.host; + if (main_ch->mig_target.cert_subject) { + migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1; + migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject; + } else { + migrate.cert_subject_size = 0; + migrate.cert_subject_data = NULL; + } spice_marshall_msg_main_migrate_begin(m, &migrate); } @@ -699,8 +652,7 @@ static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base) main_channel_marshall_migrate(m); break; case SPICE_MSG_MAIN_MIGRATE_BEGIN: - main_channel_marshall_migrate_begin(m, - SPICE_CONTAINEROF(base, MigrateBeginPipeItem, base)); + main_channel_marshall_migrate_begin(m, rcc); break; case SPICE_MSG_MAIN_MULTI_MEDIA_TIME: main_channel_marshall_multi_media_time(m, @@ -741,6 +693,26 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc, free(base); } +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, int success) +{ + red_printf("client %p connected: %d", mcc->base.client, success); + if (mcc->mig_wait_connect) { + MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base); + + mcc->mig_wait_connect = FALSE; + mcc->mig_connect_ok = success; + ASSERT(main_channel->num_clients_mig_wait); + if (!--main_channel->num_clients_mig_wait) { + reds_on_main_migrate_connected(); + } + } else { + if (success) { + red_printf("client %p MIGRATE_CANCEL", mcc->base.client); + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + } + } +} + static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); @@ -764,12 +736,10 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint main_channel_push_channels(mcc); break; case SPICE_MSGC_MAIN_MIGRATE_CONNECTED: - red_printf("connected"); - reds_on_main_migrate_connected(); + main_channel_client_handle_migrate_connected(mcc, TRUE); break; case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR: - red_printf("mig connect error"); - reds_on_main_migrate_connect_error(); + main_channel_client_handle_migrate_connected(mcc, FALSE); break; case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST: reds_on_main_mouse_mode_request(message, size); @@ -1001,6 +971,58 @@ MainChannel* main_channel_init(void) main_channel_handle_parsed, &channel_cbs); ASSERT(channel); - + red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); return (MainChannel *)channel; } + +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc) +{ + ASSERT(mcc); + return &mcc->base; +} + +int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target) +{ + RingItem *client_link; + + ASSERT(mig_target); + free(main_channel->mig_target.host); + main_channel->mig_target.host = strdup(mig_target->host); + free(main_channel->mig_target.cert_subject); + if (mig_target->cert_subject) { + main_channel->mig_target.cert_subject = strdup(mig_target->cert_subject); + } + main_channel->mig_target.port = mig_target->port; + main_channel->mig_target.sport = mig_target->sport; + + main_channel->num_clients_mig_wait = 0; + + RING_FOREACH(client_link, &main_channel->base.clients) { + MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link); + if (red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { + red_channel_client_pipe_add_type(&mcc->base, SPICE_MSG_MAIN_MIGRATE_BEGIN); + mcc->mig_wait_connect = TRUE; + mcc->mig_connect_ok = FALSE; + main_channel->num_clients_mig_wait++; + } + } + return main_channel->num_clients_mig_wait; +} + +void main_channel_migrate_cancel_wait(MainChannel *main_chan) +{ + RingItem *client_link; + + RING_FOREACH(client_link, &main_chan->base.clients) { + MainChannelClient *mcc; + + mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link); + if (mcc->mig_wait_connect) { + red_printf("client %p cancel wait connect", mcc->base.client); + mcc->mig_wait_connect = FALSE; + mcc->mig_connect_ok = FALSE; + } + } + main_chan->num_clients_mig_wait = 0; +} diff --git a/server/main_channel.h b/server/main_channel.h index 2ae05e8b..f3702e76 100644 --- a/server/main_channel.h +++ b/server/main_channel.h @@ -57,6 +57,8 @@ struct MainMigrateData { typedef struct MainChannel { RedChannel base; uint8_t recv_buf[RECEIVE_BUF_SIZE]; + RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?) + int num_clients_mig_wait; } MainChannel; @@ -80,9 +82,6 @@ void main_channel_push_init(MainChannelClient *mcc, int connection_id, int displ int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time, int ram_hint); void main_channel_push_notify(MainChannel *main_chan, uint8_t *mess, const int mess_len); -// TODO: consider exporting RedsMigSpice from reds.c -void main_channel_push_migrate_begin(MainChannel *main_chan, int port, int sport, char *host, - uint16_t cert_pub_key_type, uint32_t cert_pub_key_len, uint8_t *cert_pub_key); void main_channel_push_migrate(MainChannel *main_chan); void main_channel_push_migrate_switch(MainChannel *main_chan); void main_channel_push_migrate_cancel(MainChannel *main_chan); @@ -94,5 +93,12 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc); int main_channel_client_is_low_bandwidth(MainChannelClient *mcc); uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc); int main_channel_is_connected(MainChannel *main_chan); +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc); +/* semi seamless migration */ + +/* returns the number of clients that we are waiting for their connection */ +int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target); +void main_channel_migrate_cancel_wait(MainChannel *main_chan); +void main_channel_migrate_complete(MainChannel *main_chan, int success); #endif diff --git a/server/red_channel.h b/server/red_channel.h index d044253a..e30401c9 100644 --- a/server/red_channel.h +++ b/server/red_channel.h @@ -450,6 +450,7 @@ struct RedClient { pthread_t thread_id; int disconnecting; + }; RedClient *red_client_new(void); @@ -457,7 +458,6 @@ MainChannelClient *red_client_get_main(RedClient *client); // main should be set once before all the other channels are created void red_client_set_main(RedClient *client, MainChannelClient *mcc); - void red_client_migrate(RedClient *client); // disconnects all the client's channels (should be called from the client's thread) void red_client_destroy(RedClient *client); diff --git a/server/reds.c b/server/reds.c index 0c201e73..bc93e4ac 100644 --- a/server/reds.c +++ b/server/reds.c @@ -191,8 +191,6 @@ typedef struct RedsStatValue { #endif -typedef struct RedsMigSpice RedsMigSpice; - typedef struct RedsState { int listen_socket; int secure_listen_socket; @@ -295,6 +293,7 @@ struct ChannelSecurityOptions { ChannelSecurityOptions *next; }; +static void migrate_timeout(void *opaque); static ChannelSecurityOptions *channels_security = NULL; static int default_channel_security = @@ -538,6 +537,12 @@ static RedChannel *reds_find_channel(uint32_t type, uint32_t id) static void reds_mig_cleanup(void) { if (reds->mig_inprogress) { + if (reds->mig_wait_connect) { + SpiceMigrateInterface *sif; + ASSERT(migration_interface); + sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base); + sif->migrate_connect_complete(migration_interface); + } reds->mig_inprogress = FALSE; reds->mig_wait_connect = FALSE; reds->mig_wait_disconnect = FALSE; @@ -1060,13 +1065,6 @@ void reds_on_main_migrate_connected(void) } } -void reds_on_main_migrate_connect_error(void) -{ - if (reds->mig_wait_connect) { - reds_mig_cleanup(); - } -} - void reds_on_main_mouse_mode_request(void *message, size_t size) { switch (((SpiceMsgcMainMouseModeRequest *)message)->mode) { @@ -2976,18 +2974,6 @@ static void set_one_channel_security(int id, uint32_t security) #define REDS_SAVE_VERSION 1 -struct RedsMigSpice { - char pub_key[SPICE_TICKET_PUBKEY_BYTES]; - uint32_t mig_key; - char *host; - char *cert_subject; - int port; - int sport; - uint16_t cert_pub_key_type; - uint32_t cert_pub_key_len; - uint8_t* cert_pub_key; -}; - typedef struct RedsMigSpiceMessage { uint32_t connection_id; } RedsMigSpiceMessage; @@ -3007,24 +2993,12 @@ void reds_mig_release(void) } } -static void reds_mig_continue(void) -{ - RedsMigSpice *s = reds->mig_spice; - - red_printf(""); - main_channel_push_migrate_begin(reds->main_channel, s->port, s->sport, - s->host, s->cert_pub_key_type, s->cert_pub_key_len, s->cert_pub_key); - - reds_mig_release(); - - reds->mig_wait_connect = TRUE; - core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT); -} - static void reds_mig_started(void) { red_printf(""); + ASSERT(reds->mig_spice); + reds->mig_wait_connect = TRUE; reds->mig_inprogress = TRUE; if (reds->listen_watch != NULL) { @@ -3034,24 +3008,7 @@ static void reds_mig_started(void) if (reds->secure_listen_watch != NULL) { core->watch_update_mask(reds->secure_listen_watch, 0); } - - if (!reds_main_channel_connected()) { - red_printf("not connected to peer"); - goto error; - } - - if ((SPICE_VERSION_MAJOR == 1) && (reds->peer_minor_version < 2)) { - red_printf("minor version mismatch client %u server %u", - reds->peer_minor_version, SPICE_VERSION_MINOR); - goto error; - } - - reds_mig_continue(); - return; - -error: - reds_mig_release(); - reds_mig_disconnect(); + core->timer_start(reds->mig_timer, MIGRATE_TIMEOUT); } static void reds_mig_finished(int completed) @@ -3127,11 +3084,16 @@ void reds_fill_mig_switch(SpiceMsgMainMigrationSwitchHost *migrate) } } -static void migrate_timout(void *opaque) +static void migrate_timeout(void *opaque) { red_printf(""); ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect); - reds_mig_disconnect(); + if (reds->mig_wait_connect) { + main_channel_migrate_cancel_wait(reds->main_channel); + reds_mig_cleanup(); + } else { + reds_mig_disconnect(); + } } uint32_t reds_get_mm_time(void) @@ -3492,7 +3454,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface) main_dispatcher_init(core); ring_init(&reds->channels); - if (!(reds->mig_timer = core->timer_add(migrate_timout, NULL))) { + if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) { red_error("migration timer create failed"); } if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL))) @@ -3875,19 +3837,55 @@ SPICE_GNUC_VISIBLE int spice_server_set_agent_copypaste(SpiceServer *s, int enab return 0; } +/* returns FALSE if info is invalid */ +static int reds_set_migration_dest_info(const char* dest, + int port, int secure_port, + const char* cert_subject) +{ + RedsMigSpice *spice_migration = NULL; + + reds_mig_release(); + if ((port == -1 && secure_port == -1) || !dest) { + return FALSE; + } + + spice_migration = spice_new0(RedsMigSpice, 1); + spice_migration->port = port; + spice_migration->sport = secure_port; + spice_migration->host = strdup(dest); + if (cert_subject) { + spice_migration->cert_subject = strdup(cert_subject); + } + + reds->mig_spice = spice_migration; + + return TRUE; +} + /* semi-seamless client migration */ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* dest, int port, int secure_port, const char* cert_subject) { SpiceMigrateInterface *sif; + red_printf(""); ASSERT(migration_interface); ASSERT(reds == s); - red_printf("not implemented yet"); sif = SPICE_CONTAINEROF(migration_interface->base.sif, SpiceMigrateInterface, base); - sif->migrate_connect_complete(migration_interface); + + if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) { + sif->migrate_connect_complete(migration_interface); + return -1; + } + + if (main_channel_migrate_connect(reds->main_channel, reds->mig_spice)) { + reds->mig_wait_connect = TRUE; + reds_mig_started(); + } else { + sif->migrate_connect_complete(migration_interface); + } return 0; } @@ -3896,27 +3894,16 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_info(SpiceServer *s, const char* des int port, int secure_port, const char* cert_subject) { - RedsMigSpice *spice_migration = NULL; - + ASSERT(!migration_interface); ASSERT(reds == s); - if ((port == -1 && secure_port == -1) || !dest) + if (!reds_set_migration_dest_info(dest, port, secure_port, cert_subject)) { return -1; - - spice_migration = spice_new0(RedsMigSpice, 1); - spice_migration->port = port; - spice_migration->sport = secure_port; - spice_migration->host = strdup(dest); - if (cert_subject) { - spice_migration->cert_subject = strdup(cert_subject); } - reds_mig_release(); - reds->mig_spice = spice_migration; return 0; } -/* interface for seamless migration */ SPICE_GNUC_VISIBLE int spice_server_migrate_start(SpiceServer *s) { ASSERT(reds == s); diff --git a/server/reds.h b/server/reds.h index 7720b30e..13fec421 100644 --- a/server/reds.h +++ b/server/reds.h @@ -99,6 +99,13 @@ struct SpiceMigrateState { int dummy; }; +typedef struct RedsMigSpice { + char *host; + char *cert_subject; + int port; + int sport; +} RedsMigSpice; + ssize_t reds_stream_read(RedsStream *s, void *buf, size_t nbyte); ssize_t reds_stream_write(RedsStream *s, const void *buf, size_t nbyte); ssize_t reds_stream_writev(RedsStream *s, const struct iovec *iov, int iovcnt); @@ -134,11 +141,12 @@ int reds_num_of_clients(void); void reds_update_stat_value(uint32_t value); #endif -// callbacks from main channel messages +/* callbacks from main channel messages */ + void reds_on_main_agent_start(void); void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size); -void reds_on_main_migrate_connected(void); -void reds_on_main_migrate_connect_error(void); +void reds_on_main_migrate_connected(void); //should be called when all the clients + // are connected to the target void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end); void reds_on_main_mouse_mode_request(void *message, size_t size); diff --git a/spice.proto b/spice.proto index abf3ec30..78c1fad5 100644 --- a/spice.proto +++ b/spice.proto @@ -167,9 +167,8 @@ channel MainChannel : BaseChannel { uint16 sport; uint32 host_size; uint8 *host_data[host_size] @zero_terminated @marshall @nonnull; - pubkey_type pub_key_type; - uint32 pub_key_size; - uint8 *pub_key_data[pub_key_size] @zero_terminated @marshall @nonnull; + uint32 cert_subject_size; + uint8 *cert_subject_data[cert_subject_size] @zero_terminated @marshall; } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101; Empty migrate_cancel; |