diff --git a/mongoose.c b/mongoose.c index 457df2da..633b1c5a 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5948,30 +5948,14 @@ uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto, s_id, src, dst, frame)); } -// Calculate FCS/CRC for PPP frames. Could be implemented faster using lookup -// tables. -static uint16_t fcs_do(uint8_t *frame, size_t len) { - uint32_t fcs = 0xffff; - unsigned int j; - for (j = 0; j < len; j++) { - unsigned int i; - uint8_t x = frame[j]; - for (i = 0; i < 8; i++) { - fcs = ((fcs ^ x) & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1; - x >>= 1; - } - } - return (uint16_t) (fcs & 0xffff); -} - size_t mg_l2_ppp_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) { uint16_t crc; uint8_t *frame; len += sizeof(struct ppp) + sizeof(struct hdlc_); frame = cur - len; - crc = fcs_do(frame, len); - *cur++ = (uint8_t) ~crc; // add CRC, note the byte order - *cur++ = (uint8_t) (~crc >> 8); + crc = mg_crc16(0, (const char *) frame, len); + *cur++ = (uint8_t) crc; // add CRC, note the byte order + *cur++ = (uint8_t) (crc >> 8); // there is no len field in PPP (void) ifp; return len + 2; @@ -26075,6 +26059,20 @@ uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) { return ~crc; } +uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) { + static const uint16_t crclut[16] = { + // table for polynomial 0x8408 (reflected) + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F}; + unsigned int c = (unsigned int) ~crc & 0xffff; + while (len--) { + uint8_t b = *(uint8_t *) buf++; + c = crclut[(c ^ b) & 0x0F] ^ (c >> 4); + c = crclut[(c ^ (b >> 4)) & 0x0F] ^ (c >> 4); + } + return (uint16_t) ~c; +} + static int isbyte(int n) { return n >= 0 && n <= 255; } diff --git a/mongoose.h b/mongoose.h index 543cefd7..e4af7fa2 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1960,6 +1960,10 @@ char *mg_random_str(char *buf, size_t len); // a new checksum; pass the result of a prior call to extend over more data. uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); +// Computes CRC16 (HDLC, polynomial 0x8408) over buf/len. Pass crc=0 to start +// a new checksum; pass the result of a prior call to extend over more data. +uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len); + // Returns true if path is safe to serve from the filesystem. Rejects paths // that start with '~' or '..', or contain a '/../' component, to prevent // directory traversal attacks. diff --git a/src/l2_ppp.c b/src/l2_ppp.c index 130d4f16..73d2bf67 100644 --- a/src/l2_ppp.c +++ b/src/l2_ppp.c @@ -169,30 +169,14 @@ uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto, s_id, src, dst, frame)); } -// Calculate FCS/CRC for PPP frames. Could be implemented faster using lookup -// tables. -static uint16_t fcs_do(uint8_t *frame, size_t len) { - uint32_t fcs = 0xffff; - unsigned int j; - for (j = 0; j < len; j++) { - unsigned int i; - uint8_t x = frame[j]; - for (i = 0; i < 8; i++) { - fcs = ((fcs ^ x) & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1; - x >>= 1; - } - } - return (uint16_t) (fcs & 0xffff); -} - size_t mg_l2_ppp_trailer(struct mg_tcpip_if *ifp, size_t len, uint8_t *cur) { uint16_t crc; uint8_t *frame; len += sizeof(struct ppp) + sizeof(struct hdlc_); frame = cur - len; - crc = fcs_do(frame, len); - *cur++ = (uint8_t) ~crc; // add CRC, note the byte order - *cur++ = (uint8_t) (~crc >> 8); + crc = mg_crc16(0, (const char *) frame, len); + *cur++ = (uint8_t) crc; // add CRC, note the byte order + *cur++ = (uint8_t) (crc >> 8); // there is no len field in PPP (void) ifp; return len + 2; diff --git a/src/util.c b/src/util.c index 9b55acb9..7539f09f 100644 --- a/src/util.c +++ b/src/util.c @@ -128,6 +128,20 @@ uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) { return ~crc; } +uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len) { + static const uint16_t crclut[16] = { + // table for polynomial 0x8408 (reflected) + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F}; + unsigned int c = (unsigned int) ~crc & 0xffff; + while (len--) { + uint8_t b = *(uint8_t *) buf++; + c = crclut[(c ^ b) & 0x0F] ^ (c >> 4); + c = crclut[(c ^ (b >> 4)) & 0x0F] ^ (c >> 4); + } + return (uint16_t) ~c; +} + static int isbyte(int n) { return n >= 0 && n <= 255; } diff --git a/src/util.h b/src/util.h index 8d4795c3..d6ef816b 100644 --- a/src/util.h +++ b/src/util.h @@ -40,6 +40,10 @@ char *mg_random_str(char *buf, size_t len); // a new checksum; pass the result of a prior call to extend over more data. uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); +// Computes CRC16 (HDLC, polynomial 0x8408) over buf/len. Pass crc=0 to start +// a new checksum; pass the result of a prior call to extend over more data. +uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len); + // Returns true if path is safe to serve from the filesystem. Rejects paths // that start with '~' or '..', or contain a '/../' component, to prevent // directory traversal attacks. diff --git a/test/ppp_test.c b/test/ppp_test.c index e5cb638e..f08f846b 100644 --- a/test/ppp_test.c +++ b/test/ppp_test.c @@ -61,6 +61,17 @@ static void *ppp_payload(uint8_t *frame) { return (struct ppp *) ((struct hdlc_ *) frame + 1) + 1; } +static void test_fcs(void) { + struct mg_tcpip_if ifp; + uint8_t frame[] = {0xff, 0x03, 0x00, 0x21, 1, 2, 3, 4, 0, 0}; + uint8_t fcs[] = {0xae, 0x77}; + init_if(&ifp, MG_TCPIP_L2_PPP); + // PPP trailer stores the FCS bytes in the proper order + ASSERT(mg_l2_ppp_trailer(&ifp, 4, frame + sizeof(frame) - sizeof(fcs)) == + sizeof(frame)); + ASSERT(memcmp(frame + sizeof(frame) - sizeof(fcs), fcs, sizeof(fcs)) == 0); +} + static size_t mk_pppoe_disc(uint8_t *buf, uint8_t code, uint16_t id, const void *payload, size_t len) { struct eth *eth = (struct eth *) buf; @@ -385,6 +396,10 @@ static void test_pppoe(void) { printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true") int main(void) { + s_error = false; + test_fcs(); + DASHBOARD("fcs"); + s_error = false; test_lcp(); DASHBOARD("lcp"); diff --git a/test/unit_test.c b/test/unit_test.c index f35ccae9..fe364cb6 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -3039,6 +3039,13 @@ static void test_crc32(void) { ASSERT(mg_crc32(mg_crc32(0, "ab", 2), "c", 1) == 891568578); } +static void test_crc16(void) { + ASSERT(mg_crc16(0, 0, 0) == 0); + ASSERT(mg_crc16(0, "a", 1) == 0x82f7); + ASSERT(mg_crc16(0, "abc", 3) == 0x9e25); + ASSERT(mg_crc16(mg_crc16(0, "ab", 2), "c", 1) == 0x9e25); +} + static void us(struct mg_connection *c, int ev, void *ev_data) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (ev == MG_EV_HTTP_MSG && mg_match(hm->uri, mg_str("/upload"), NULL)) { @@ -5512,6 +5519,7 @@ int main(void) { test_str(); test_match(); test_crc32(); + test_crc16(); DASHBOARD("misc"); s_error = false;