From 5560c56ef05c74da5e0e0825dc1f134019593cad Mon Sep 17 00:00:00 2001 From: Yonit Halperin Date: Sun, 18 Sep 2011 10:31:38 +0300 Subject: server,proto: tell the client to connect to the migration target before migraton starts (1) send SPICE_MSG_MAIN_MIGRATE_BEGIN upon spice_server_migrate_connect (2) wait for SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR), or a timeout, in order to complete client_migrate_info monitor command --- common/messages.h | 2 + server/reds.c | 148 ++++++++++++++++++++++++++++++++++++++++-------------- spice.proto | 5 +- 3 files changed, 115 insertions(+), 40 deletions(-) diff --git a/common/messages.h b/common/messages.h index 6fcd8bea..16ae05b7 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/reds.c b/server/reds.c index 99d52f94..845b0ee3 100644 --- a/server/reds.c +++ b/server/reds.c @@ -271,6 +271,7 @@ typedef struct RedsState { VDIPortState agent_state; InputsState *inputs_state; + int client_semi_mig_cap; int mig_wait_connect; int mig_wait_disconnect; int mig_inprogress; @@ -280,6 +281,7 @@ typedef struct RedsState { IncomingHandler in_handler; RedsOutgoingData outgoing; Channel *channels; + Channel main_channel; int mouse_mode; int is_client_mouse_allowed; int dispatcher_allows_client_mouse; @@ -378,6 +380,7 @@ static uint8_t zero_page[ZERO_BUF_SIZE] = {0}; static void reds_push(); static void reds_out_item_free(RedsOutItem *item); +static void migrate_timeout(void *opaque); static ChannelSecurityOptions *channels_security = NULL; static int default_channel_security = @@ -644,6 +647,12 @@ static void reds_shatdown_channels() static void reds_mig_cleanup() { 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; @@ -1700,7 +1709,7 @@ static void reds_main_handle_message(void *opaque, size_t size, uint32_t type, v reds_send_channels(); break; case SPICE_MSGC_MAIN_MIGRATE_CONNECTED: - red_printf("connected"); + red_printf("client connected to migration target"); if (reds->mig_wait_connect) { reds_mig_cleanup(); } @@ -1891,6 +1900,23 @@ static int sync_write(RedsStream *stream, const void *in_buf, size_t n) return TRUE; } +static void reds_channel_set_caps(Channel *channel, int cap, int active) +{ + int nbefore, n; + + nbefore = channel->num_caps; + n = cap / 32; + channel->num_caps = MAX(channel->num_caps, n + 1); + channel->caps = spice_renew(uint32_t, channel->caps, channel->num_caps); + memset(channel->caps + nbefore, 0, + (channel->num_caps - nbefore) * sizeof(uint32_t)); + if (active) { + channel->caps[n] |= (1 << cap); + } else { + channel->caps[n] &= ~(1 << cap); + } +} + static void reds_channel_set_common_caps(Channel *channel, int cap, int active) { int nbefore, n; @@ -1933,7 +1959,6 @@ static int reds_send_link_ack(RedLinkInfo *link) { SpiceLinkHeader header; SpiceLinkReply ack; - Channel caps = { 0, }; Channel *channel; BUF_MEM *bmBuf; BIO *bio; @@ -1948,7 +1973,8 @@ static int reds_send_link_ack(RedLinkInfo *link) channel = reds_find_channel(link->link_mess->channel_type, 0); if (!channel) { - channel = ∩︀ + ASSERT(link->link_mess->channel_type == SPICE_CHANNEL_MAIN); + channel = &reds->main_channel; } reds_channel_init_auth_caps(channel); /* make sure common caps are set */ @@ -1989,7 +2015,6 @@ static int reds_send_link_ack(RedLinkInfo *link) ret = TRUE; end: - reds_channel_dispose(&caps); BIO_free(bio); return ret; } @@ -2042,28 +2067,41 @@ static void reds_start_net_test() } } +static int test_capability(uint32_t *caps, uint32_t num_caps, uint32_t cap) +{ + uint32_t index = cap / 32; + if (num_caps < index + 1) { + return FALSE; + } + + return (caps[index] & (1 << (cap % 32))) != 0; +} + static void reds_handle_main_link(RedLinkInfo *link) { + SpiceLinkMess *link_mess = link->link_mess; uint32_t connection_id; + uint32_t *channel_caps; + uint32_t num_channel_caps; red_printf(""); reds_disconnect(); - if (!link->link_mess->connection_id) { + if (!link_mess->connection_id) { reds_send_link_result(link, SPICE_LINK_ERR_OK); while((connection_id = rand()) == 0); reds->agent_state.num_tokens = 0; memcpy(&(reds->taTicket), &taTicket, sizeof(reds->taTicket)); reds->mig_target = FALSE; } else { - if (link->link_mess->connection_id != reds->link_id) { + if (link_mess->connection_id != reds->link_id) { reds_send_link_result(link, SPICE_LINK_ERR_BAD_CONNECTION_ID); reds_link_free(link); return; } reds_send_link_result(link, SPICE_LINK_ERR_OK); - connection_id = link->link_mess->connection_id; + connection_id = link_mess->connection_id; reds->mig_target = TRUE; } @@ -2074,6 +2112,17 @@ static void reds_handle_main_link(RedLinkInfo *link) reds->stream = link->stream; reds->in_handler.shut = FALSE; + num_channel_caps = link_mess->num_channel_caps; + channel_caps = num_channel_caps ? (uint32_t *)((uint8_t *)link_mess + + link_mess->caps_offset) + link_mess->num_common_caps : + NULL; + reds->client_semi_mig_cap = test_capability(channel_caps, num_channel_caps, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + if (reds->client_semi_mig_cap && (SPICE_VERSION_MAJOR == 2) && (reds->peer_minor_version < 1)) { + red_printf("warning: client claims to support semi seamless migration," + "but its version is incompatible"); + reds->client_semi_mig_cap = FALSE; + } + reds_show_new_channel(link, connection_id); reds_stream_remove_watch(link->stream); link->stream = NULL; @@ -2534,6 +2583,12 @@ static void openssl_init(RedLinkInfo *link) BN_set_word(link->tiTicketing.bn, f4); } +static void main_init() +{ + reds->main_channel.type = SPICE_CHANNEL_MAIN; + reds_channel_set_caps(&reds->main_channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE, TRUE); +} + static void inputs_init() { Channel *channel; @@ -3926,15 +3981,11 @@ 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 { @@ -3963,15 +4014,20 @@ static void reds_mig_continue(void) RedsOutItem *item; red_printf(""); + ASSERT(reds->client_semi_mig_cap); item = new_out_item(SPICE_MSG_MAIN_MIGRATE_BEGIN); migrate.port = s->port; migrate.sport = s->sport; migrate.host_size = strlen(s->host) + 1; migrate.host_data = (uint8_t *)s->host; - migrate.pub_key_type = s->cert_pub_key_type; - migrate.pub_key_size = s->cert_pub_key_len; - migrate.pub_key_data = s->cert_pub_key; + if (s->cert_subject) { + migrate.cert_subject_size = strlen(s->cert_subject) + 1; + migrate.cert_subject_data = (uint8_t *)s->cert_subject; + } else { + migrate.cert_subject_size = 0; + migrate.cert_subject_data = NULL; + } spice_marshall_msg_main_migrate_begin(item->m, &migrate); reds_push_pipe_item(item); @@ -3985,6 +4041,7 @@ static void reds_mig_continue(void) static void reds_mig_started(void) { red_printf(""); + ASSERT(reds->mig_spice); reds->mig_inprogress = TRUE; @@ -4001,12 +4058,6 @@ static void reds_mig_started(void) 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; @@ -4089,7 +4140,7 @@ static void reds_mig_switch(void) reds_mig_release(); } -static void migrate_timout(void *opaque) +static void migrate_timeout(void *opaque) { red_printf(""); ASSERT(reds->mig_wait_connect || reds->mig_wait_disconnect); @@ -4477,7 +4528,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface) init_vd_agent_resources(); - 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->key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) { @@ -4540,6 +4591,7 @@ static int do_spice_init(SpiceCoreInterface *core_interface) } #endif + main_init(); inputs_init(); reds->mouse_mode = SPICE_MOUSE_MODE_SERVER; @@ -4869,6 +4921,31 @@ 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, @@ -4879,10 +4956,18 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *s, const char* 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 (reds->client_semi_mig_cap) { + reds_mig_started(); + } else { + sif->migrate_connect_complete(migration_interface); + } return 0; } @@ -4890,27 +4975,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/spice.proto b/spice.proto index 6160de1d..d5b954e8 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; -- cgit