Added checksum calculations for events larger than 2^24 bytes

The checksums are now properly calculated for large events that span multiple
SQL packets.
This commit is contained in:
Markus Makela 2016-02-12 08:31:50 +02:00
parent 3609f97ba0
commit 2b7e2d3043
3 changed files with 94 additions and 74 deletions

View File

@ -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];

View File

@ -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

View File

@ -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)
{