From 2b7e2d30436cba656c52e6c19a7c3d5d1437055a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 12 Feb 2016 08:31:50 +0200 Subject: [PATCH] Added checksum calculations for events larger than 2^24 bytes The checksums are now properly calculated for large events that span multiple SQL packets. --- server/modules/include/blr.h | 1 + .../include/mysql_client_server_protocol.h | 1 + server/modules/routing/binlog/blr_master.c | 166 ++++++++++-------- 3 files changed, 94 insertions(+), 74 deletions(-) diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 0596b02ea..e8c827b14 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -424,6 +424,7 @@ typedef struct router_instance { int trx_safe; /*< Detect and handle partial transactions */ int pending_transaction; /*< Pending transaction */ enum blr_event_state master_event_state; /*< Packet read state */ + uint32_t stored_checksum; /*< The current value of the checksum */ REP_HEADER stored_header; /*< Relication header of the event the master is sending */ uint64_t last_safe_pos; /* last committed transaction */ char binlog_name[BINLOG_FNAMELEN+1]; diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index 39e3f9d6b..bf7b8bee1 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -70,6 +70,7 @@ #define GW_MYSQL_READ 0 #define GW_MYSQL_WRITE 1 #define MYSQL_HEADER_LEN 4L +#define MYSQL_CHECKSUM_LEN 4L #define GW_MYSQL_PROTOCOL_VERSION 10 // version is 10 #define GW_MYSQL_HANDSHAKE_FILLER 0x00 diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index aac5f8a4f..f11371bb9 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -1002,84 +1002,102 @@ uint32_t partialpos = 0; #ifdef SHOW_EVENTS printf("blr: len %lu, event type 0x%02x, flags 0x%04x, event size %d, event timestamp %lu\n", (unsigned long)len-4, hdr.event_type, hdr.flags, hdr.event_size, (unsigned long)hdr.timestamp); #endif - /* - * First check that the checksum we calculate matches the - * checksum in the packet we received. - */ - if (router->master_chksum && - router->master_event_state == BLR_EVENT_DONE) - { - uint32_t chksum, pktsum; - - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, ptr + 5, hdr.event_size - 4); - pktsum = EXTRACT32(ptr + hdr.event_size + 1); - if (pktsum != chksum) - { - router->stats.n_badcrc++; - if (msg) - { - free(msg); - msg = NULL; - } - MXS_ERROR("%s: Checksum error in event " - "from master, " - "binlog %s @ %lu. " - "Closing master connection.", - router->service->name, - router->binlog_name, - router->current_pos); - blr_master_close(router); - blr_master_delayed_connect(router); - return; - } - } } - } + } - /* pending large event */ - if (router->master_event_state != BLR_EVENT_DONE) - { - if (len - MYSQL_HEADER_LEN < MYSQL_PACKET_LENGTH_MAX) - { - /** This is the last packet, we can now proceed to distribute - * the event */ - ss_dassert(router->master_event_state != BLR_EVENT_COMPLETE); - router->master_event_state = BLR_EVENT_COMPLETE; - memcpy(&hdr, &router->stored_header, sizeof(hdr)); - } - else - { - /* pending 16 mb */ - /* current partial event is being written to disk file */ - uint32_t offset = 4; + /* pending large event */ + if (router->master_event_state != BLR_EVENT_DONE) + { + if (len - MYSQL_HEADER_LEN < MYSQL_PACKET_LENGTH_MAX) + { + /** This is the last packet, we can now proceed to distribute + * the event */ + ss_dassert(router->master_event_state != BLR_EVENT_COMPLETE); + router->master_event_state = BLR_EVENT_COMPLETE; + memcpy(&hdr, &router->stored_header, sizeof(hdr)); + } + else + { + /* current partial event is being written to disk file */ + uint32_t offset = MYSQL_HEADER_LEN; + uint32_t extra_bytes = MYSQL_HEADER_LEN; - /** Don't write the OK byte into the binlog */ - if (router->master_event_state == BLR_EVENT_STARTED) - { - offset++; - router->master_event_state = BLR_EVENT_ONGOING; - } + /** Don't write the OK byte into the binlog */ + if (router->master_event_state == BLR_EVENT_STARTED) + { + offset = MYSQL_HEADER_LEN + 1; + router->master_event_state = BLR_EVENT_ONGOING; - if (blr_write_data_into_binlog(router, len - offset, ptr + offset) == 0) - { - /* - * Failed to write to the - * binlog file, destroy the - * buffer chain and close the - * connection with the master - */ - while ((pkt = gwbuf_consume(pkt, - GWBUF_LENGTH(pkt))) != NULL); - blr_master_close(router); - blr_master_delayed_connect(router); - return; - } - pkt = gwbuf_consume(pkt, len); - pkt_length -= len; - continue; - } - } + /** Initialize the checksum and calculate it for + * the first packet */ + if (router->master_chksum) + { + router->stored_checksum = crc32(0L, NULL, 0); + extra_bytes = MYSQL_HEADER_LEN + 1; + } + } + + if (router->master_chksum) + { + router->stored_checksum = crc32(router->stored_checksum, + ptr + offset, + len - extra_bytes); + } + + if (blr_write_data_into_binlog(router, len - offset, ptr + offset) == 0) + { + /** Failed to write to the binlog file, destroy the buffer + * chain and close the connection with the master */ + while (pkt) + { + pkt = GWBUF_CONSUME_ALL(pkt); + } + blr_master_close(router); + blr_master_delayed_connect(router); + return; + } + pkt = gwbuf_consume(pkt, len); + pkt_length -= len; + continue; + } + } + + /* + * First check that the checksum we calculate matches the + * checksum in the packet we received. + */ + if (router->master_chksum) + { + uint32_t pktsum, offset = MYSQL_HEADER_LEN; + uint32_t size = len - MYSQL_HEADER_LEN - MYSQL_CHECKSUM_LEN; + + if (router->master_event_state == BLR_EVENT_DONE) + { + /** Initialize the checksum and set the pointer offset to + * the first byte after the header and OK byte */ + router->stored_checksum = crc32(0L, NULL, 0); + offset = MYSQL_HEADER_LEN + 1; + size = len - MYSQL_HEADER_LEN - MYSQL_CHECKSUM_LEN - 1; + } + + router->stored_checksum = crc32(router->stored_checksum, + ptr + offset, size); + + pktsum = EXTRACT32(ptr + len - MYSQL_CHECKSUM_LEN); + if (pktsum != router->stored_checksum) + { + router->stats.n_badcrc++; + free(msg); + msg = NULL; + MXS_ERROR("%s: Checksum error in event from master, " + "binlog %s @ %lu. Closing master connection.", + router->service->name, router->binlog_name, + router->current_pos); + blr_master_close(router); + blr_master_delayed_connect(router); + return; + } + } if (hdr.ok == 0) {