diff --git a/mongoose.c b/mongoose.c index 4aaa238b..2a16b6f5 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5042,6 +5042,7 @@ static void mkpay(struct pkt *pkt, void *p) { mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p)); } +// NOTE(): DOES NOT handle reentries after odd length, use last static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { size_t i; const uint8_t *p = (const uint8_t *) buf; @@ -5192,18 +5193,25 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); udp->csum = 0; cs = csumup(0, udp, sizeof(*udp)); - cs = csumup(cs, buf, len); #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { + uint32_t pseudo[2]; cs = csumup(cs, &ip6->src, sizeof(ip6->src)); cs = csumup(cs, &ip6->dst, sizeof(ip6->dst)); + pseudo[0] = mg_htonl((uint32_t) (sizeof(*udp) + len)); + pseudo[1] = mg_htonl(17); + cs = csumup(cs, pseudo, 2 * sizeof(uint32_t)); } else #endif { + uint16_t pseudo[2]; cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); + pseudo[0] = mg_htons(17); + pseudo[1] = udp->len; + cs = csumup(cs, pseudo, 2 * sizeof(uint16_t)); } - cs += (uint32_t) (17 + sizeof(*udp) + len); + cs = csumup(cs, buf, len); // possibly odd length must be last udp->csum = csumfin(cs); memmove(udp + 1, buf, len); l2_len = mg_l2_footer(ifp->l2type, l2_len, l2p); @@ -5520,6 +5528,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, struct ip6 *ip6; struct icmp6 *icmp6; uint32_t cs; + uint16_t pseudo[2]; ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len); icmp6 = (struct icmp6 *) (ip6 + 1); memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0 @@ -5528,10 +5537,12 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, memcpy(icmp6 + 1, buf, len); // Copy payload icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1 cs = csumup(0, icmp6, sizeof(*icmp6)); - cs = csumup(cs, buf, len); cs = csumup(cs, ip_src, 16); cs = csumup(cs, ip_dst, 16); - cs += (uint32_t) (58 + sizeof(*icmp6) + len); + pseudo[0] = htons(58); + pseudo[1] = htons((uint16_t) (sizeof(*icmp6) + len)); + cs = csumup(cs, pseudo, 4); + cs = csumup(cs, buf, len); // possibly odd length must be last icmp6->csum = csumfin(cs); driver_output( ifp, mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len, l2p)); @@ -5922,24 +5933,25 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, { uint32_t cs = 0; uint16_t n = (uint16_t) (sizeof(*tcp) + len); - cs = csumup(cs, tcp, n); #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { + uint32_t pseudo[2]; cs = csumup(cs, &ip6->src, sizeof(ip6->src)); cs = csumup(cs, &ip6->dst, sizeof(ip6->dst)); - cs += (uint32_t) (6 + n); + pseudo[0] = mg_htonl((uint32_t) n); + pseudo[1] = mg_htonl(6); + cs = csumup(cs, pseudo, 2 * sizeof(uint32_t)); } else #endif { - uint8_t pseudo[4]; - pseudo[0] = 0; - pseudo[1] = ip->proto; - pseudo[2] = (uint8_t) (n >> 8); - pseudo[3] = (uint8_t) (n & 255); + uint16_t pseudo[2]; cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); + pseudo[0] = mg_htons(6); + pseudo[1] = mg_htons(n); + cs = csumup(cs, pseudo, 2 * sizeof(uint16_t)); } + cs = csumup(cs, tcp, n); // possibly odd length must be last tcp->csum = csumfin(cs); } MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src, diff --git a/src/net_builtin.c b/src/net_builtin.c index 2c525625..a8103105 100644 --- a/src/net_builtin.c +++ b/src/net_builtin.c @@ -216,6 +216,7 @@ static void mkpay(struct pkt *pkt, void *p) { mg_str_n((char *) p, (size_t) (&pkt->pay.buf[pkt->pay.len] - (char *) p)); } +// NOTE(): DOES NOT handle reentries after odd length, use last static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { size_t i; const uint8_t *p = (const uint8_t *) buf; @@ -366,18 +367,25 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); udp->csum = 0; cs = csumup(0, udp, sizeof(*udp)); - cs = csumup(cs, buf, len); #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { + uint32_t pseudo[2]; cs = csumup(cs, &ip6->src, sizeof(ip6->src)); cs = csumup(cs, &ip6->dst, sizeof(ip6->dst)); + pseudo[0] = mg_htonl((uint32_t) (sizeof(*udp) + len)); + pseudo[1] = mg_htonl(17); + cs = csumup(cs, pseudo, 2 * sizeof(uint32_t)); } else #endif { + uint16_t pseudo[2]; cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); + pseudo[0] = mg_htons(17); + pseudo[1] = udp->len; + cs = csumup(cs, pseudo, 2 * sizeof(uint16_t)); } - cs += (uint32_t) (17 + sizeof(*udp) + len); + cs = csumup(cs, buf, len); // possibly odd length must be last udp->csum = csumfin(cs); memmove(udp + 1, buf, len); l2_len = mg_l2_footer(ifp->l2type, l2_len, l2p); @@ -694,6 +702,7 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, struct ip6 *ip6; struct icmp6 *icmp6; uint32_t cs; + uint16_t pseudo[2]; ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len); icmp6 = (struct icmp6 *) (ip6 + 1); memset(icmp6, 0, sizeof(*icmp6)); // Set csum to 0 @@ -702,10 +711,12 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src, memcpy(icmp6 + 1, buf, len); // Copy payload icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1 cs = csumup(0, icmp6, sizeof(*icmp6)); - cs = csumup(cs, buf, len); cs = csumup(cs, ip_src, 16); cs = csumup(cs, ip_dst, 16); - cs += (uint32_t) (58 + sizeof(*icmp6) + len); + pseudo[0] = htons(58); + pseudo[1] = htons((uint16_t) (sizeof(*icmp6) + len)); + cs = csumup(cs, pseudo, 4); + cs = csumup(cs, buf, len); // possibly odd length must be last icmp6->csum = csumfin(cs); driver_output( ifp, mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len, l2p)); @@ -1096,24 +1107,25 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst, { uint32_t cs = 0; uint16_t n = (uint16_t) (sizeof(*tcp) + len); - cs = csumup(cs, tcp, n); #if MG_ENABLE_IPV6 if (ip_dst->is_ip6) { + uint32_t pseudo[2]; cs = csumup(cs, &ip6->src, sizeof(ip6->src)); cs = csumup(cs, &ip6->dst, sizeof(ip6->dst)); - cs += (uint32_t) (6 + n); + pseudo[0] = mg_htonl((uint32_t) n); + pseudo[1] = mg_htonl(6); + cs = csumup(cs, pseudo, 2 * sizeof(uint32_t)); } else #endif { - uint8_t pseudo[4]; - pseudo[0] = 0; - pseudo[1] = ip->proto; - pseudo[2] = (uint8_t) (n >> 8); - pseudo[3] = (uint8_t) (n & 255); + uint16_t pseudo[2]; cs = csumup(cs, &ip->src, sizeof(ip->src)); cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); + pseudo[0] = mg_htons(6); + pseudo[1] = mg_htons(n); + cs = csumup(cs, pseudo, 2 * sizeof(uint16_t)); } + cs = csumup(cs, tcp, n); // possibly odd length must be last tcp->csum = csumfin(cs); } MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src, diff --git a/test/Makefile b/test/Makefile index 261f8e23..dc786a5a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -91,6 +91,15 @@ mip_tap_test: mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_mult $(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ +# bridge UDP port <--> TAP interface +port_tap_bridge: port_tap_bridge.c + cc port_tap_bridge.c -o $@ + +# requires port_tap_bridge to be running +mip_port_test: mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server + $(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ + ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ + mip_vc98: mip_test.c mongoose.c mongoose.h packed_fs.c Makefile $(DOCKER) mdashnet/vc98 wine cl mip_test.c packed_fs.c $(VCFLAGS) $(DEFS) $(TFLAGS) /Fe$@.exe $(DOCKER) -e WINEPREFIX=/tmp/wineprefix mdashnet/vc98 wine $@.exe @@ -183,6 +192,19 @@ s390: CC = $(DOCKER) mdashnet/s390 cc s390: RUN = $(DOCKER) --init mdashnet/s390 s390: test +mip_s390: ASAN= +mip_s390: CC = $(DOCKER) mdashnet/s390 cc +mip_s390: RUN = $(DOCKER) --init mdashnet/s390 +mip_s390: mip_test + +mip_port_s390: ASAN= +mip_port_s390: IPV6=0 +# This test requires curl, not present in this image +mip_port_s390: CFLAGS += -DNO_HTTPSERVER_TEST +mip_port_s390: CC = $(DOCKER) mdashnet/s390 cc +mip_port_s390: RUN = $(DOCKER) --init mdashnet/s390 +mip_port_s390: mip_port_test + arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_TCPIP_DRIVER_INIT=0 -DMG_ARCH=MG_ARCH_ARMGCC -DNO_SLEEP_ABORT arm: mongoose.h $(SRCS) $(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0 diff --git a/test/mip_port_test.c b/test/mip_port_test.c new file mode 100644 index 00000000..f7129215 --- /dev/null +++ b/test/mip_port_test.c @@ -0,0 +1,119 @@ +#define MIPPORTTEST_USING_DHCP 1 + +#include +#include + +#include "mip_x_test.c" + +struct port_driver_data { + int sockfd; + struct sockaddr_in port_addr; + struct sockaddr_in tap_addr; +}; + +// MIP Socket driver +static size_t sock_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) { + ssize_t received = recv(((struct port_driver_data *)ifp->driver_data)->sockfd, buf, len, 0); + usleep(1); // This is to avoid 100% CPU + if (received < 0) return 0; + return (size_t) received; +} + +static size_t sock_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { + ssize_t res = send(((struct port_driver_data *)ifp->driver_data)->sockfd, buf, len, 0); + if (res < 0) { + MG_ERROR(("sock_tx failed: %d", errno)); + return 0; + } + return (size_t) res; +} + +static bool sock_poll(struct mg_tcpip_if *ifp, bool s1) { + return s1 && ifp->driver_data ? true : false; +} + + +int main(void) { + bool result; + const char *debug_level = getenv("V"); + // Setup interface + const uint16_t port = 0xAA55; // UDP port + const char *mac = "02:00:01:02:03:78"; // MAC address + + struct port_driver_data pdd; + pdd.sockfd = socket(AF_INET, SOCK_DGRAM, 0); + pdd.port_addr.sin_family = AF_INET; + pdd.port_addr.sin_addr.s_addr = htonl(INADDR_ANY); + pdd.port_addr.sin_port = htons(port); + if (bind(pdd.sockfd, (struct sockaddr *) &pdd.port_addr, sizeof(pdd.port_addr)) < 0) return EXIT_FAILURE; + printf("Opened UDP socket\n"); + + pdd.tap_addr.sin_family = AF_INET; + pdd.tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1 + pdd.tap_addr.sin_port = htons(0x55AA); + if (connect(pdd.sockfd, (struct sockaddr *) &pdd.tap_addr, sizeof(pdd.tap_addr)) < 0) return EXIT_FAILURE; + printf("Connected to TAP UDP socket\n"); + + usleep(200000); // 200 ms + + if (debug_level == NULL) debug_level = "3"; + mg_log_set(atoi(debug_level)); + + struct mg_mgr mgr; // Event manager + mg_mgr_init(&mgr); // Initialise event manager + + // MIP driver + struct mg_tcpip_driver driver; + memset(&driver, 0, sizeof(driver)); + + driver.tx = sock_tx; + driver.poll = sock_poll; + driver.rx = sock_rx; + + struct mg_tcpip_if mif; + memset(&mif, 0, sizeof(mif)); + + mif.driver = &driver; + mif.driver_data = &pdd; + +#if MIPPORTTEST_USING_DHCP == 1 +#else + mif.ip = mg_htonl(MG_U32(192, 168, 32, 2)); // Triggering a network failure + mif.mask = mg_htonl(MG_U32(255, 255, 255, 0)); + mif.gw = mg_htonl(MG_U32(192, 168, 32, 1)); +#endif + + sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], + &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); + + mg_tcpip_init(&mgr, &mif); + MG_INFO(("Init done, starting main loop")); + usleep(200000); // 200 ms + + // Stack initialization, Network configuration (DHCP lease, ...) +#if MIPPORTTEST_USING_DHCP == 0 + MG_INFO(("MIF configuration: Static IP")); + ASSERT(mif.ip != 0); // Check we have a satic IP assigned + mg_mgr_poll(&mgr, 100); // For initialisation +#else + MG_INFO(("MIF configuration: DHCP")); + ASSERT(!mif.ip); // Check we are set for DHCP + int pc = 500; // Timeout on DHCP lease 500 ~ approx 5s (typical delay <1s) + while (((pc--) > 0) && !mif.ip) { + mg_mgr_poll(&mgr, 100); + usleep(10000); // 10 ms + } + if (!mif.ip) MG_ERROR(("No ip assigned (DHCP lease may have failed).\n")); + ASSERT(mif.ip); // We have an IP (lease or static) +#endif + while (mif.state != MG_TCPIP_STATE_READY) { + mg_mgr_poll(&mgr, 100); + usleep(10000); // 10 ms + } + + // RUN TESTS + result = mip_x_test(&mgr); + close(pdd.sockfd); + if (!result) return EXIT_FAILURE; + return EXIT_SUCCESS; +} diff --git a/test/mip_tap_test.c b/test/mip_tap_test.c index 61647b5f..917939b6 100644 --- a/test/mip_tap_test.c +++ b/test/mip_tap_test.c @@ -1,10 +1,5 @@ -#define MG_ENABLE_TCPIP 1 -#define MG_ENABLE_TCPIP_DRIVER_INIT 0 - #define MIPTAPTEST_USING_DHCP 1 -#define FETCH_BUF_SIZE (16 * 1024) - #include #ifndef __OpenBSD__ #include @@ -16,112 +11,8 @@ #endif #include -#include "mongoose.c" +#include "mip_x_test.c" -#include "driver_mock.c" - -#ifdef MQTT_LOCALHOST -#define MQTT_URL "mqtt://127.0.0.1:1883" -#else -#define MQTT_URL "mqtt://broker.hivemq.com:1883" -#endif -#if MG_TLS == MG_TLS_BUILTIN -#define MQTTS_URL "mqtts://mongoose.ws:8883" // test requires TLS 1.3 -#define MQTTS_CA mg_str(s_ca_cert) -static const char *s_ca_cert = - "-----BEGIN CERTIFICATE-----\n" - "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n" - "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" - "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n" - "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n" - "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n" - "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n" - "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n" - "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n" - "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n" - "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n" - "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n" - "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n" - "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n" - "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n" - "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n" - "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n" - "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" - "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n" - "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n" - "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n" - "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n" - "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n" - "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n" - "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n" - "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n" - "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n" - "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n" - "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n" - "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n" - "-----END CERTIFICATE-----\n"; -#elif MG_TLS -#ifdef MQTT_LOCALHOST -// we'll generate MQTTS_URL -#define MQTTS_CA mg_str(s_ca_cert) -static const char *s_ca_cert = - "-----BEGIN CERTIFICATE-----\n" - "MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n" - "b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n" - "TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n" - "fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n" - "epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n" - "us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n" - "-----END CERTIFICATE-----\n"; -#else -#define MQTTS_URL "mqtts://broker.hivemq.com:8883" -#define MQTTS_CA mg_unpacked("/data/ca.pem") -#endif // MQTT_LOCALHOST -#endif - -static char *host_ip; - -static int s_num_tests = 0; -static bool s_error = false; - -#ifdef NO_ABORT -static int s_abort = 0; -#define ABORT() ++s_abort, s_error = true -#else -#ifdef NO_SLEEP_ABORT -#define ABORT() abort() -#else -#define ABORT() \ - sleep(2); /* 2s, GH print reason */ \ - abort(); -#endif -#endif - -#define ASSERT(expr) \ - do { \ - s_num_tests++; \ - if (!(expr)) { \ - printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \ - fflush(stdout); \ - ABORT(); \ - } \ - } while (0) - - -static struct mg_http_message gethm(const char *buf) { - struct mg_http_message hm; - memset(&hm, 0, sizeof(hm)); - mg_http_parse(buf, strlen(buf), &hm); - return hm; -} - -static int cmpbody(const char *buf, const char *str) { - struct mg_str s = mg_str(str); - struct mg_http_message hm = gethm(buf); - size_t len = strlen(buf); - if (hm.body.len > len) hm.body.len = len - (size_t) (hm.body.buf - buf); - return mg_strcmp(hm.body, s); -} // MIP TUNTAP driver static size_t tap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) { @@ -144,313 +35,9 @@ static bool tap_poll(struct mg_tcpip_if *ifp, bool s1) { return s1 && ifp->driver_data ? true : false; } -static void eh1(struct mg_connection *c, int ev, void *ev_data) { - struct mg_tls_opts *topts = (struct mg_tls_opts *) c->fn_data; - if (ev == MG_EV_ACCEPT && topts != NULL) mg_tls_init(c, topts); - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - MG_DEBUG(("[%.*s %.*s] message len %d", (int) hm->method.len, - hm->method.buf, (int) hm->uri.len, hm->uri.buf, - (int) hm->message.len)); - if (mg_match(hm->uri, mg_str("/foo/*"), NULL)) { - mg_http_reply(c, 200, "", "uri: %.*s", hm->uri.len - 5, hm->uri.buf + 5); - } else if (mg_match(hm->uri, mg_str("/ws"), NULL)) { - mg_ws_upgrade(c, hm, NULL); - } else if (mg_match(hm->uri, mg_str("/body"), NULL)) { - mg_http_reply(c, 200, "", "%.*s", (int) hm->body.len, hm->body.buf); - } else { - struct mg_http_serve_opts sopts; - memset(&sopts, 0, sizeof(sopts)); - sopts.root_dir = "./data"; - mg_http_serve_dir(c, hm, &sopts); - } - } else if (ev == MG_EV_WS_OPEN) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - ASSERT(mg_strcmp(hm->uri, mg_str("/ws")) == 0); - mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY); - } else if (ev == MG_EV_WS_MSG) { - struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; - mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_BINARY); - } -} -struct fetch_data { - char *buf; - const char *url; - int code, closed; -}; - -static void fcb(struct mg_connection *c, int ev, void *ev_data) { - struct fetch_data *fd = (struct fetch_data *) c->fn_data; - if (ev == MG_EV_CONNECT) { - MG_DEBUG(("CONNECT")); - if (mg_url_is_ssl(fd->url)) { - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(opts)); // read CA from packed_fs - if (host_ip != NULL && strstr(fd->url, host_ip) != NULL) { - MG_DEBUG(("Local connection, using self-signed certificates")); - opts.name = mg_str_s("localhost"); - opts.ca = mg_unpacked("/certs/ca.crt"); - } else { - opts.name = mg_url_host(fd->url); - opts.ca = mg_unpacked("/data/ca.pem"); -#if MG_TLS == MG_TLS_BUILTIN - // our TLS does not search for the proper CA in a bundle - opts.ca = mg_file_read(&mg_fs_posix, "data/e5.crt"); -#endif - } - mg_tls_init(c, &opts); -#if MG_TLS == MG_TLS_BUILTIN - mg_free((void *) opts.ca.buf); -#endif - } - } else if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len, - hm->message.buf); - fd->code = atoi(hm->uri.buf); - fd->closed = 1; - c->is_closing = 1; - MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len, - hm->message.buf)); - (void) c; - } else if (ev == MG_EV_CLOSE) { - MG_DEBUG(("CLOSE")); - fd->closed = 1; - } else if (ev == MG_EV_READ) { - long bytes = *(long *) ev_data; - MG_DEBUG(("READ %d: %.*s", (int) bytes, (int) bytes, c->recv.buf)); - } -} - -static int fetch(struct mg_mgr *mgr, char *buf, const char *url, - const char *fmt, ...) { - struct fetch_data fd = {buf, url, 0, 0}; - int i; - struct mg_connection *c = NULL; - va_list ap; - mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old - // timestamp (from an ancient call in other test) - c = mg_http_connect(mgr, url, fcb, &fd); - ASSERT(c != NULL); - va_start(ap, fmt); - mg_vprintf(c, fmt, &ap); - va_end(ap); - buf[0] = '\0'; - // - TLS: multiple (small) records: allow enough loops so mg_mgr_poll can - // process buffered records when no more frames are coming in - for (i = 0; i < 500 && buf[0] == '\0' && !fd.closed; i++) { - mg_mgr_poll(mgr, 0); - usleep(5000); // 5 ms. Slow down poll loop to ensure packet transit, but - // allow enough loops to get the ARP response, otherwise, - // given enough traffic, the timer expires before we get a - // chance to see the response - } - if (!fd.closed) c->is_closing = 1; - mg_mgr_poll(mgr, 0); - return fd.code; -} - -static void test_http_client(struct mg_mgr *mgr) { - char buf[FETCH_BUF_SIZE]; - int rc = 0; - const bool ipv6 = MG_ENABLE_IPV6; -#if MG_TLS - if (ipv6) { - rc = fetch(mgr, buf, "https://ipv6.google.com", - "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n"); - } else { - rc = fetch(mgr, buf, "https://cesanta.com", - "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n"); - } - ASSERT(rc == 200); // OK -#else - if (ipv6) { - rc = fetch(mgr, buf, "http://ipv6.google.com", - "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n"); - ASSERT(rc == 200); // OK - } else { - rc = fetch(mgr, buf, "http://cesanta.com", - "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n"); - ASSERT(rc == 301); // OK: Permanently moved (HTTP->HTTPS redirect) - } - -#endif -} - -static struct mg_connection *s_conn; -static char s_topic[16]; - -struct mqtt_data { - char *url; - bool passed; -}; - -static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data) { - struct mqtt_data *data = (struct mqtt_data *) c->fn_data; - if (ev == MG_EV_CONNECT) { - MG_DEBUG(("CONNECT")); -#if MG_TLS - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(opts)); - opts.ca = MQTTS_CA; - opts.name = mg_url_host(data->url); - mg_tls_init(c, &opts); -#endif - } else if (ev == MG_EV_MQTT_OPEN) { - MG_DEBUG(("MQTT CONNECT")); - struct mg_mqtt_opts sub_opts; - memset(&sub_opts, 0, sizeof(sub_opts)); - sub_opts.topic = mg_str(mg_random_str(s_topic, sizeof(s_topic))); - sub_opts.qos = 1; - mg_mqtt_sub(c, &sub_opts); - struct mg_mqtt_opts pub_opts; - memset(&pub_opts, 0, sizeof(pub_opts)); - pub_opts.topic = sub_opts.topic; - pub_opts.message = mg_str("hi"); - pub_opts.qos = 1, pub_opts.retain = false; - mg_mqtt_pub(c, &pub_opts); - } else if (ev == MG_EV_MQTT_MSG) { - struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; - MG_DEBUG(("TOPIC: %.*s, MSG: %.*s", (int) mm->topic.len, mm->topic.buf, - mm->data.len > 10 ? 10 : (int) mm->data.len, mm->data.buf)); - ASSERT(mm->topic.len == strlen(s_topic) && - strncmp(mm->topic.buf, s_topic, mm->topic.len) == 0); - if (mm->data.len == 2 && strncmp(mm->data.buf, "hi", 2) == 0) { - struct mg_mqtt_opts pub_opts; - memset(&pub_opts, 0, sizeof(pub_opts)); - pub_opts.topic = mm->topic; - // send more than 1 record, content is not relevant - pub_opts.message = mg_str_n((char *)(size_t) mqtt_fn, 21098); - pub_opts.qos = 1, pub_opts.retain = false; - mg_mqtt_pub(c, &pub_opts); - } else if (mm->data.len == 8 && strncmp(mm->data.buf, "farewell", 8) == 0) { - // close on farewell - MG_INFO(("%lu CLOSING", c->id)); - mg_mqtt_disconnect(c, NULL); - data->passed = true; - } else if (mm->data.len == 21098) { - struct mg_mqtt_opts pub_opts; - ASSERT(memcmp((const char *) (size_t) mqtt_fn, mm->data.buf, 21098) == 0); - // send farewell after receiving big data - memset(&pub_opts, 0, sizeof(pub_opts)); - pub_opts.topic = mm->topic; - pub_opts.message = mg_str("farewell"); - pub_opts.qos = 1, pub_opts.retain = false; - mg_mqtt_pub(c, &pub_opts); - } - } else if (ev == MG_EV_CLOSE) { - MG_DEBUG(("CLOSE")); - s_conn = NULL; - } else if (ev == MG_EV_ERROR) { - MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data)); - } -} - -static void test_mqtt_connsubpub(struct mg_mgr *mgr) { - struct mqtt_data data; - struct mg_mqtt_opts opts; - memset(&opts, 0, sizeof(opts)); - opts.clean = true, opts.version = 4; - data.passed = false; -#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN - if (host_ip == NULL) { - printf("\nMQTT_LOCALHOST defined but no HOST_IP provided, skipping MQTTS tests\n"); - return; - } - printf("HOST_IP: %s\n", host_ip); -#endif -#if MG_TLS -#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN - data.url = mg_mprintf("mqtts://%s:8883", host_ip); -#else - data.url = strdup(MQTTS_URL); -#endif -#else -#ifdef MQTT_LOCALHOST - data.url = mg_mprintf("mqtt://%s:1883", host_ip); -#else - data.url = strdup(MQTT_URL); -#endif -#endif - s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data); - ASSERT(s_conn != NULL); - for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) { - mg_mgr_poll(mgr, 0); - usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit - } - ASSERT(data.passed); - mg_mgr_poll(mgr, 0); - free(data.url); -} - -#include -static void *poll_thread(void *p) { - struct mg_mgr *mgr = (struct mg_mgr *) p; - int i; - for (i = 0; i < 300; i++) { - mg_mgr_poll(mgr, 0); - usleep(10000); // 10 ms. Slow down poll loop to ensure packet transit - } - return NULL; -} - -static void test_http_server(struct mg_mgr *mgr) { - struct mg_connection *c; - char *cmd; - pthread_t thread_id = (pthread_t) 0; -#if MG_TLS - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(opts)); - // opts.ca = mg_str(s_tls_ca); - opts.cert = mg_unpacked("/certs/server.crt"); - opts.key = mg_unpacked("/certs/server.key"); - c = mg_http_listen(mgr, "https://0.0.0.0:12347", eh1, &opts); - cmd = mg_mprintf("./mip_curl.sh --insecure https://%M:12347", mg_print_ip4, - &mgr->ifp->ip); -#else - c = mg_http_listen(mgr, "http://0.0.0.0:12347", eh1, NULL); - cmd = - mg_mprintf("./mip_curl.sh http://%M:12347", mg_print_ip4, &mgr->ifp->ip); -#endif - ASSERT(c != NULL); - ASSERT (mg_send(c, "NADA", 0)); // check mg_send allows len=0 - pthread_create(&thread_id, NULL, poll_thread, - mgr); // simpler this way, no concurrency anyway - MG_DEBUG(("CURL")); - ASSERT(system(cmd) == 0); // wait for curl - MG_DEBUG(("MONGOOSE")); - pthread_join(thread_id, NULL); // wait for Mongoose - MG_DEBUG(("DONE")); - free(cmd); -} - -static void test_tls(struct mg_mgr *mgr) { -#if MG_TLS - char *url; - char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile - struct mg_str data = mg_unpacked("/Makefile"); - if (host_ip == NULL) { - printf("\nNo HOST_IP provided, skipping TLS tests\n"); - return; - } - printf("HOST_IP: %s\n", host_ip); - // - POST a large file, make sure we drain TLS buffers and read all: done at - // server test, using curl as POSTing client - // - Fire patched server, test multiple TLS records per TCP segment handling - url = mg_mprintf("https://%s:8443", host_ip); // for historic reasons - ASSERT(system("tls_multirec/server -d tls_multirec &") == 0); - sleep(1); - ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200); - ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile - ASSERT(system("killall tls_multirec/server") == 0); - free(url); -#else - (void) cmpbody("", ""); - (void) mgr; -#endif -} int main(void) { + bool result; const char *debug_level = getenv("V"); // Setup interface const char *iface = "tap0"; // Network iface @@ -485,9 +72,6 @@ int main(void) { if (debug_level == NULL) debug_level = "3"; mg_log_set(atoi(debug_level)); - host_ip = getenv("HOST_IP"); - - // Events struct mg_mgr mgr; // Event manager mg_mgr_init(&mgr); // Initialise event manager @@ -540,41 +124,9 @@ int main(void) { usleep(10000); // 10 ms } -#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true"); - // RUN TESTS - usleep(500000); // 500 ms - s_error = false; - test_http_client(&mgr); - DASHBOARD("http_client"); - - usleep(500000); // 500 ms - s_error = false; - test_http_server(&mgr); - DASHBOARD("http_server"); - - usleep(500000); // 500 ms - s_error = false; - test_tls(&mgr); - DASHBOARD("tls"); - - usleep(500000); // 500 ms - s_error = false; - test_mqtt_connsubpub(&mgr); - DASHBOARD("mqtt"); - - // Clear - s_error = false; - mg_mgr_free(&mgr); - ASSERT(mgr.conns == NULL); // Deconstruction OK + result = mip_x_test(&mgr); close(fd); - printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true"); - // last entry with no comma - -#ifdef NO_ABORT - if (s_abort != 0) return EXIT_FAILURE; -#endif - - printf("SUCCESS. Total tests: %d\n", s_num_tests); + if (!result) return EXIT_FAILURE; return EXIT_SUCCESS; } diff --git a/test/mip_test.c b/test/mip_test.c index 2a788180..3781a749 100644 --- a/test/mip_test.c +++ b/test/mip_test.c @@ -41,10 +41,100 @@ struct ipp { static void test_csum(void) { uint8_t ip[20] = {0x45, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x28, 0x11, 0x94, 0xcf, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01}; + uint8_t udp_odd[53] = {0xc0, 0xa8, 0x45, 0x58, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x11, 0x00, 0x29, 0xad, 0x67, 0x00, 0x35, 0x00, 0x29, + 0xaf, 0xf8, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01}; + ASSERT(ipcsum(ip, 20) == 0); + ASSERT(ipcsum(udp_odd, 53) == 0); // UDP and TCP checksum calc funcions use the same basic calls as ipcsum() } +#if defined(__DCC__) +#pragma pack(1) +#else +#pragma pack(push, 1) +#endif + +struct pseudoip { + uint32_t src; // Source IP + uint32_t dst; // Destination IP + uint8_t zero; + uint8_t proto; // Upper level protocol + uint16_t len; // Datagram length +}; + +struct pseudoip6 { + uint64_t src[2]; // Source IP + uint64_t dst[2]; // Destination IP + uint32_t plen; // Payload length + uint8_t zero[3]; + uint8_t next; // Upper level protocol +}; + +#if defined(__DCC__) +#pragma pack(0) +#else +#pragma pack(pop) +#endif + +static bool test_udp_csum(void *d, void *u) { + struct ip *ip = (struct ip *) d; + struct udp *udp = (struct udp *) u; + struct pseudoip pseudo, *pip; + pseudo.src = ip->src; + pseudo.dst = ip->dst; + pseudo.zero = 0; + pseudo.proto = ip->proto; + pseudo.len = udp->len; + pip = (struct pseudoip *)((size_t) u - sizeof(*pip)); + memcpy(pip, &pseudo, sizeof(*pip)); + return (ipcsum(pip, sizeof(*pip) + mg_ntohs(udp->len)) == 0); +} + +static bool test_udp6_csum(void *d, void *u) { + struct ip6 *ip = (struct ip6 *) d; + struct udp *udp = (struct udp *) u; + struct pseudoip6 pseudo, *pip; + pseudo.src[0] = ip->src[0], pseudo.src[1] = ip->src[1]; + pseudo.dst[0] = ip->dst[0], pseudo.dst[1] = ip->dst[1]; + pseudo.zero[0] = 0, pseudo.zero[1] = 0, pseudo.zero[2] = 0; + pseudo.plen = mg_htonl((uint32_t) mg_ntohs(udp->len)); + pseudo.next = ip->next; + pip = (struct pseudoip6 *) ((size_t) u - sizeof(*pip)); + memcpy(pip, &pseudo, sizeof(*pip)); + return (ipcsum(pip, sizeof(*pip) + mg_ntohs(udp->len)) == 0); +} + +static bool test_tcp_csum(void *d, void *t) { + struct ip *ip = (struct ip *) d; + struct pseudoip pseudo, *pip; + pseudo.src = ip->src; + pseudo.dst = ip->dst; + pseudo.zero = 0; + pseudo.proto = ip->proto; + pseudo.len = mg_htons((uint16_t)(mg_ntohs(ip->len) - (ip->ver & 0x0F) * 4)); + pip = (struct pseudoip *)((size_t) t - sizeof(*pip)); + memcpy(pip, &pseudo, sizeof(*pip)); + return (ipcsum(pip, sizeof(*pip) + mg_ntohs(pip->len)) == 0); +} + +static bool test_tcp6_csum(void *d, void *t) { + struct ip6 *ip = (struct ip6 *) d; + struct pseudoip6 pseudo, *pip; + pseudo.src[0] = ip->src[0], pseudo.src[1] = ip->src[1]; + pseudo.dst[0] = ip->dst[0], pseudo.dst[1] = ip->dst[1]; + pseudo.zero[0] = 0, pseudo.zero[1] = 0, pseudo.zero[2] = 0; + pseudo.plen = mg_htonl((uint32_t) mg_ntohs(ip->plen)); + pseudo.next = ip->next; + pip = (struct pseudoip6 *) ((size_t) t - sizeof(*pip)); + memcpy(pip, &pseudo, sizeof(*pip)); + return (ipcsum(pip, sizeof(*pip) + mg_ntohl(pip->plen)) == 0); +} + static bool executed = false; static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { @@ -78,8 +168,8 @@ static void mif6_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { static void test_state6change(void) { struct mg_tcpip_if iface; memset(&iface, 0, sizeof(iface)); - iface.ip6[0] = (uint64_t) mg_htonl(0x01020304); - iface.ip6[1] = (uint64_t) mg_htonl(0x05060708); + iface.ip6[0] = mg_htonll(0x01020304); + iface.ip6[1] = mg_htonll(0x05060708); iface.state6 = MG_TCPIP_STATE_READY; iface.driver = &mg_tcpip_driver_mock; iface.fn = mif6_fn; @@ -145,7 +235,7 @@ static void test_poll(void) { memset(&mif, 0, sizeof(mif)); mif.driver = &mg_tcpip_driver_mock; mg_tcpip_init(&mgr, &mif); - mg_http_listen(&mgr, "http://127.0.0.1:12346", ph, &count); + mg_http_listen(&mgr, "http://0.0.0.0:12346", ph, &count); for (i = 0; i < 10; i++) mg_mgr_poll(&mgr, 0); ASSERT(count == 10); mg_mgr_free(&mgr); @@ -250,18 +340,18 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp, mif->driver_data = &s_driver_data; #if MG_ENABLE_IPV6 if (ipp->ip6 != NULL) { - mif->ip6[0] = 1; + mif->ip6[0] = mg_htonll(1ULL << 56); mif->prefix[0] = 1; mif->prefix_len = 64; - mif->gw6[0] = 1; + mif->gw6[0] = mg_htonll(1ULL << 56); mif->gw6_ready = true; mif->state = MG_TCPIP_STATE_READY; // so DHCP stops mif->state6 = MG_TCPIP_STATE_READY; // so mg_send() works and RS stops } else #endif { - mif->ip = 1; - mif->gw = 1; + mif->ip = mg_htonl(0x1000000); + mif->gw = mg_htonl(0x1000000); mif->gw_ready = true; mif->mask = 255; // use router, to avoid firing an ARP request mif->state = MG_TCPIP_STATE_READY; // so mg_send() works and DHCP stops @@ -281,7 +371,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp, ip->ver = 0x60; ip->next = proto; // must be outside of Mongoose network to avoid firing NS requests - ip->src[0] = 2; + ip->src[0] = mg_htonll(2ULL << 56); ip->dst[0] = mif->ip6[0]; } else #endif @@ -291,7 +381,7 @@ static void init_tests(struct mg_mgr *mgr, struct eth *e, struct ipp *ipp, ip->ver = (4 << 4) | 5; ip->proto = proto; // must be outside of Mongoose network to avoid firing ARP requests - ip->src = 2; + ip->src = mg_htonl(0x2000000); ip->dst = mif->ip; } } @@ -364,10 +454,13 @@ static void test_tcp_basics(bool ipv6) { ASSERT(t->flags == (TH_RST | TH_ACK)); ASSERT(t->seq == mg_htonl(0)); ASSERT(t->ack == mg_htonl(1235)); + mg_hexdump(s_driver_data.buf, s_driver_data.len); if (ipv6) { ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); + ASSERT(test_tcp6_csum(i6, t)); } else { - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); + ASSERT(test_tcp_csum(i, t)); } // send SYN+ACK, expect RST @@ -421,7 +514,7 @@ static void test_tcp_basics(bool ipv6) { if (ipv6) { ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); } else { - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); } // send SYN+ACK, expect RST @@ -459,7 +552,7 @@ static void test_tcp_basics(bool ipv6) { if (ipv6) { ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); } else { - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); } // segment with seq_no way out of window @@ -678,7 +771,7 @@ static void test_tcp_basics(bool ipv6) { if (ipv6) { ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); } else { - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); } // invalid SYN + ACK to connecting client (after SYN...), send ACK != seq @@ -1068,8 +1161,10 @@ static void test_udp(bool ipv6) { ASSERT(*((char *) (u + 1)) == 'P'); if (ipv6) { ASSERT(i6->src[0] == 1 && i6->dst[0] == 2); + ASSERT(test_udp6_csum(i6, u)); } else { - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); + ASSERT(test_udp_csum(i, u)); } s_driver_data.len = 0; @@ -1119,7 +1214,7 @@ static void test_icmp_basics(void) { create_icmp_dat(&e, &ipp, 8, 0, 0); // Echo Request mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); ASSERT(i->proto == 1); ASSERT(i->len == mg_htons(sizeof(*i) + sizeof(*icmp) + 0)); ASSERT(ipcsum(i, sizeof(*i)) == 0); // Bonus, not tested elsewhere @@ -1130,7 +1225,7 @@ static void test_icmp_basics(void) { create_icmp_dat(&e, &ipp, 8, 0, 69); // Echo Request mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); - ASSERT(i->src == 1 && i->dst == 2); + ASSERT(i->src == mg_htonl(0x1000000) && i->dst == mg_htonl(0x2000000)); ASSERT(i->proto == 1); ASSERT(i->len == mg_htons(sizeof(*i) + sizeof(*icmp) + 69)); ASSERT(icmp->type == 0); // Echo Reply @@ -1201,7 +1296,7 @@ static void test_icmp6_basics(void) { create_icmp6_dat(&e, &ipp, 128, 0, NULL, 0); // Echo Request mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); - ASSERT(i->src[0] == 1 && i->src[1] == 0 && i->dst[0] == 2 && i->dst[1] == 0); + ASSERT(i->src[0] == mg_htonll(1ULL << 56) && i->src[1] == 0 && i->dst[0] == mg_htonll(2ULL << 56) && i->dst[1] == 0); ASSERT(i->next == 58); ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 0)); ASSERT(icmp6->type == 129); // Echo Reply @@ -1211,7 +1306,7 @@ static void test_icmp6_basics(void) { create_icmp6_dat(&e, &ipp, 128, 0, NULL, 69); // Echo Request mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); - ASSERT(i->src[0] == 1 && i->src[1] == 0 && i->dst[0] == 2 && i->dst[1] == 0); + ASSERT(i->src[0] == mg_htonll(1ULL << 56) && i->src[1] == 0 && i->dst[0] == mg_htonll(2ULL << 56) && i->dst[1] == 0); ASSERT(i->next == 58); ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 69)); ASSERT(icmp6->type == 129); // Echo Reply @@ -1226,7 +1321,7 @@ static void test_icmp6_basics(void) { create_icmp6_dat(&e, &ipp, 135, 0, payload, 28); mg_mgr_poll(&mgr, 0); // make sure we clean former stuff in buffer while (!received_response(&s_driver_data)) mg_mgr_poll(&mgr, 0); - ASSERT(i->src[0] == 1 && i->src[1] == 0 && i->dst[0] == 2 && i->dst[1] == 0); + ASSERT(i->src[0] == mg_htonll(1ULL << 56) && i->src[1] == 0 && i->dst[0] == mg_htonll(2ULL << 56) && i->dst[1] == 0); ASSERT(i->next == 58); ASSERT(i->plen == mg_htons(sizeof(*icmp6) + 28)); ASSERT(icmp6->type == 136); // Neighbor Advertisement diff --git a/test/mip_x_test.c b/test/mip_x_test.c new file mode 100644 index 00000000..c4a0724a --- /dev/null +++ b/test/mip_x_test.c @@ -0,0 +1,468 @@ +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_TCPIP_DRIVER_INIT 0 + +#define FETCH_BUF_SIZE (16 * 1024) + +#include "mongoose.c" + +bool mip_x_test(struct mg_mgr *); + + +#ifdef MQTT_LOCALHOST +#define MQTT_URL "mqtt://127.0.0.1:1883" +#else +#define MQTT_URL "mqtt://broker.hivemq.com:1883" +#endif +#if MG_TLS == MG_TLS_BUILTIN +#define MQTTS_URL "mqtts://mongoose.ws:8883" // test requires TLS 1.3 +#define MQTTS_CA mg_str(s_ca_cert) +static const char *s_ca_cert = + "-----BEGIN CERTIFICATE-----\n" + "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\n" + "-----END CERTIFICATE-----\n"; +#elif MG_TLS +#ifdef MQTT_LOCALHOST +// we'll generate MQTTS_URL +#define MQTTS_CA mg_str(s_ca_cert) +static const char *s_ca_cert = + "-----BEGIN CERTIFICATE-----\n" + "MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n" + "b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n" + "TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n" + "fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n" + "epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n" + "us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n" + "-----END CERTIFICATE-----\n"; +#else +#define MQTTS_URL "mqtts://broker.hivemq.com:8883" +#define MQTTS_CA mg_unpacked("/data/ca.pem") +#endif // MQTT_LOCALHOST +#endif + +static char *host_ip; + +static int s_num_tests = 0; +static bool s_error = false; + +#ifdef NO_ABORT +static int s_abort = 0; +#define ABORT() ++s_abort, s_error = true +#else +#ifdef NO_SLEEP_ABORT +#define ABORT() abort() +#else +#define ABORT() \ + sleep(2); /* 2s, GH print reason */ \ + abort(); +#endif +#endif + +#define ASSERT(expr) \ + do { \ + s_num_tests++; \ + if (!(expr)) { \ + printf("FAILURE %s:%d: %s\n", __FILE__, __LINE__, #expr); \ + fflush(stdout); \ + ABORT(); \ + } \ + } while (0) + + +static struct mg_http_message gethm(const char *buf) { + struct mg_http_message hm; + memset(&hm, 0, sizeof(hm)); + mg_http_parse(buf, strlen(buf), &hm); + return hm; +} + +static int cmpbody(const char *buf, const char *str) { + struct mg_str s = mg_str(str); + struct mg_http_message hm = gethm(buf); + size_t len = strlen(buf); + if (hm.body.len > len) hm.body.len = len - (size_t) (hm.body.buf - buf); + return mg_strcmp(hm.body, s); +} + +#ifndef NO_HTTPSERVER_TEST +static void eh1(struct mg_connection *c, int ev, void *ev_data) { + struct mg_tls_opts *topts = (struct mg_tls_opts *) c->fn_data; + if (ev == MG_EV_ACCEPT && topts != NULL) mg_tls_init(c, topts); + if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + MG_DEBUG(("[%.*s %.*s] message len %d", (int) hm->method.len, + hm->method.buf, (int) hm->uri.len, hm->uri.buf, + (int) hm->message.len)); + if (mg_match(hm->uri, mg_str("/foo/*"), NULL)) { + mg_http_reply(c, 200, "", "uri: %.*s", hm->uri.len - 5, hm->uri.buf + 5); + } else if (mg_match(hm->uri, mg_str("/ws"), NULL)) { + mg_ws_upgrade(c, hm, NULL); + } else if (mg_match(hm->uri, mg_str("/body"), NULL)) { + mg_http_reply(c, 200, "", "%.*s", (int) hm->body.len, hm->body.buf); + } else { + struct mg_http_serve_opts sopts; + memset(&sopts, 0, sizeof(sopts)); + sopts.root_dir = "./data"; + mg_http_serve_dir(c, hm, &sopts); + } + } else if (ev == MG_EV_WS_OPEN) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + ASSERT(mg_strcmp(hm->uri, mg_str("/ws")) == 0); + mg_ws_send(c, "opened", 6, WEBSOCKET_OP_BINARY); + } else if (ev == MG_EV_WS_MSG) { + struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; + mg_ws_send(c, wm->data.buf, wm->data.len, WEBSOCKET_OP_BINARY); + } +} +#endif + +struct fetch_data { + char *buf; + const char *url; + int code, closed; +}; + +static void fcb(struct mg_connection *c, int ev, void *ev_data) { + struct fetch_data *fd = (struct fetch_data *) c->fn_data; + if (ev == MG_EV_CONNECT) { + MG_DEBUG(("CONNECT")); + if (mg_url_is_ssl(fd->url)) { + struct mg_tls_opts opts; + memset(&opts, 0, sizeof(opts)); // read CA from packed_fs + if (host_ip != NULL && strstr(fd->url, host_ip) != NULL) { + MG_DEBUG(("Local connection, using self-signed certificates")); + opts.name = mg_str_s("localhost"); + opts.ca = mg_unpacked("/certs/ca.crt"); + } else { + opts.name = mg_url_host(fd->url); + opts.ca = mg_unpacked("/data/ca.pem"); +#if MG_TLS == MG_TLS_BUILTIN + // our TLS does not search for the proper CA in a bundle + opts.ca = mg_file_read(&mg_fs_posix, "data/e5.crt"); +#endif + } + mg_tls_init(c, &opts); +#if MG_TLS == MG_TLS_BUILTIN + mg_free((void *) opts.ca.buf); +#endif + } + } else if (ev == MG_EV_HTTP_MSG) { + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + snprintf(fd->buf, FETCH_BUF_SIZE, "%.*s", (int) hm->message.len, + hm->message.buf); + fd->code = atoi(hm->uri.buf); + fd->closed = 1; + c->is_closing = 1; + MG_DEBUG(("CODE: %d, MSG: %.*s", fd->code, (int) hm->message.len, + hm->message.buf)); + (void) c; + } else if (ev == MG_EV_CLOSE) { + MG_DEBUG(("CLOSE")); + fd->closed = 1; + } else if (ev == MG_EV_READ) { + long bytes = *(long *) ev_data; + MG_DEBUG(("READ %d: %.*s", (int) bytes, (int) bytes, c->recv.buf)); + } +} + +static int fetch(struct mg_mgr *mgr, char *buf, const char *url, + const char *fmt, ...) { + struct fetch_data fd = {buf, url, 0, 0}; + int i; + struct mg_connection *c = NULL; + va_list ap; + mg_mgr_poll(mgr, 0); // update ifp->now to avoid ARP lookup using an old + // timestamp (from an ancient call in other test) + c = mg_http_connect(mgr, url, fcb, &fd); + ASSERT(c != NULL); + va_start(ap, fmt); + mg_vprintf(c, fmt, &ap); + va_end(ap); + buf[0] = '\0'; + // - TLS: multiple (small) records: allow enough loops so mg_mgr_poll can + // process buffered records when no more frames are coming in + for (i = 0; i < 500 && buf[0] == '\0' && !fd.closed; i++) { + mg_mgr_poll(mgr, 0); + usleep(5000); // 5 ms. Slow down poll loop to ensure packet transit, but + // allow enough loops to get the ARP response, otherwise, + // given enough traffic, the timer expires before we get a + // chance to see the response + } + if (!fd.closed) c->is_closing = 1; + mg_mgr_poll(mgr, 0); + return fd.code; +} + +static void test_http_client(struct mg_mgr *mgr) { + char buf[FETCH_BUF_SIZE]; + int rc = 0; + const bool ipv6 = MG_ENABLE_IPV6; +#if MG_TLS + if (ipv6) { + rc = fetch(mgr, buf, "https://ipv6.google.com", + "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n"); + } else { + rc = fetch(mgr, buf, "https://cesanta.com", + "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n"); + } + ASSERT(rc == 200); // OK +#else + if (ipv6) { + rc = fetch(mgr, buf, "http://ipv6.google.com", + "GET / HTTP/1.0\r\nHost: ipv6.google.com\r\n\r\n"); + ASSERT(rc == 200); // OK + } else { + rc = fetch(mgr, buf, "http://cesanta.com", + "GET /robots.txt HTTP/1.0\r\nHost: cesanta.com\r\n\r\n"); + ASSERT(rc == 301); // OK: Permanently moved (HTTP->HTTPS redirect) + } + +#endif +} + +static struct mg_connection *s_conn; +static char s_topic[16]; + +struct mqtt_data { + char *url; + bool passed; +}; + +static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data) { + struct mqtt_data *data = (struct mqtt_data *) c->fn_data; + if (ev == MG_EV_CONNECT) { + MG_DEBUG(("CONNECT")); +#if MG_TLS + struct mg_tls_opts opts; + memset(&opts, 0, sizeof(opts)); + opts.ca = MQTTS_CA; + opts.name = mg_url_host(data->url); + mg_tls_init(c, &opts); +#endif + } else if (ev == MG_EV_MQTT_OPEN) { + MG_DEBUG(("MQTT CONNECT")); + struct mg_mqtt_opts sub_opts; + memset(&sub_opts, 0, sizeof(sub_opts)); + sub_opts.topic = mg_str(mg_random_str(s_topic, sizeof(s_topic))); + sub_opts.qos = 1; + mg_mqtt_sub(c, &sub_opts); + struct mg_mqtt_opts pub_opts; + memset(&pub_opts, 0, sizeof(pub_opts)); + pub_opts.topic = sub_opts.topic; + pub_opts.message = mg_str("hi"); + pub_opts.qos = 1, pub_opts.retain = false; + mg_mqtt_pub(c, &pub_opts); + } else if (ev == MG_EV_MQTT_MSG) { + struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data; + MG_DEBUG(("TOPIC: %.*s, MSG: %.*s", (int) mm->topic.len, mm->topic.buf, + mm->data.len > 10 ? 10 : (int) mm->data.len, mm->data.buf)); + ASSERT(mm->topic.len == strlen(s_topic) && + strncmp(mm->topic.buf, s_topic, mm->topic.len) == 0); + if (mm->data.len == 2 && strncmp(mm->data.buf, "hi", 2) == 0) { + struct mg_mqtt_opts pub_opts; + memset(&pub_opts, 0, sizeof(pub_opts)); + pub_opts.topic = mm->topic; + // send more than 1 record, content is not relevant + pub_opts.message = mg_str_n((char *)(size_t) mqtt_fn, 21098); + pub_opts.qos = 1, pub_opts.retain = false; + mg_mqtt_pub(c, &pub_opts); + } else if (mm->data.len == 8 && strncmp(mm->data.buf, "farewell", 8) == 0) { + // close on farewell + MG_INFO(("%lu CLOSING", c->id)); + mg_mqtt_disconnect(c, NULL); + data->passed = true; + } else if (mm->data.len == 21098) { + struct mg_mqtt_opts pub_opts; + ASSERT(memcmp((const char *) (size_t) mqtt_fn, mm->data.buf, 21098) == 0); + // send farewell after receiving big data + memset(&pub_opts, 0, sizeof(pub_opts)); + pub_opts.topic = mm->topic; + pub_opts.message = mg_str("farewell"); + pub_opts.qos = 1, pub_opts.retain = false; + mg_mqtt_pub(c, &pub_opts); + } + } else if (ev == MG_EV_CLOSE) { + MG_DEBUG(("CLOSE")); + s_conn = NULL; + } else if (ev == MG_EV_ERROR) { + MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data)); + } +} + +static void test_mqtt_connsubpub(struct mg_mgr *mgr) { + struct mqtt_data data; + struct mg_mqtt_opts opts; + memset(&opts, 0, sizeof(opts)); + opts.clean = true, opts.version = 4; + data.passed = false; +#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN + if (host_ip == NULL) { + printf("\nMQTT_LOCALHOST defined but no HOST_IP provided, skipping MQTTS tests\n"); + return; + } + printf("HOST_IP: %s\n", host_ip); +#endif +#if MG_TLS +#if defined(MQTT_LOCALHOST) && MG_TLS != MG_TLS_BUILTIN + data.url = mg_mprintf("mqtts://%s:8883", host_ip); +#else + data.url = strdup(MQTTS_URL); +#endif +#else +#ifdef MQTT_LOCALHOST + data.url = mg_mprintf("mqtt://%s:1883", host_ip); +#else + data.url = strdup(MQTT_URL); +#endif +#endif + s_conn = mg_mqtt_connect(mgr, data.url, &opts, mqtt_fn, &data); + ASSERT(s_conn != NULL); + for (int i = 0; i < 1000 && s_conn != NULL && !s_conn->is_closing; i++) { + mg_mgr_poll(mgr, 0); + usleep(10000); // 10 ms. Slow down poll loop to ensure packets transit + } + ASSERT(data.passed); + mg_mgr_poll(mgr, 0); + free(data.url); +} + +#ifndef NO_HTTPSERVER_TEST +#include +static void *poll_thread(void *p) { + struct mg_mgr *mgr = (struct mg_mgr *) p; + int i; + for (i = 0; i < 300; i++) { + mg_mgr_poll(mgr, 0); + usleep(10000); // 10 ms. Slow down poll loop to ensure packet transit + } + return NULL; +} +#endif + +static void test_http_server(struct mg_mgr *mgr) { +#ifdef NO_HTTPSERVER_TEST + (void) mgr; +#else + struct mg_connection *c; + char *cmd; + pthread_t thread_id = (pthread_t) 0; +#if MG_TLS + struct mg_tls_opts opts; + memset(&opts, 0, sizeof(opts)); + // opts.ca = mg_str(s_tls_ca); + opts.cert = mg_unpacked("/certs/server.crt"); + opts.key = mg_unpacked("/certs/server.key"); + c = mg_http_listen(mgr, "https://0.0.0.0:12347", eh1, &opts); + cmd = mg_mprintf("./mip_curl.sh --insecure https://%M:12347", mg_print_ip4, + &mgr->ifp->ip); +#else + c = mg_http_listen(mgr, "http://0.0.0.0:12347", eh1, NULL); + cmd = + mg_mprintf("./mip_curl.sh http://%M:12347", mg_print_ip4, &mgr->ifp->ip); +#endif + ASSERT(c != NULL); + ASSERT (mg_send(c, "NADA", 0)); // check mg_send allows len=0 + pthread_create(&thread_id, NULL, poll_thread, + mgr); // simpler this way, no concurrency anyway + MG_DEBUG(("CURL")); + ASSERT(system(cmd) == 0); // wait for curl + MG_DEBUG(("MONGOOSE")); + pthread_join(thread_id, NULL); // wait for Mongoose + MG_DEBUG(("DONE")); + free(cmd); +#endif +} + +static void test_tls(struct mg_mgr *mgr) { +#if MG_TLS + char *url; + char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile + struct mg_str data = mg_unpacked("/Makefile"); + if (host_ip == NULL) { + printf("\nNo HOST_IP provided, skipping TLS tests\n"); + return; + } + printf("HOST_IP: %s\n", host_ip); + // - POST a large file, make sure we drain TLS buffers and read all: done at + // server test, using curl as POSTing client + // - Fire patched server, test multiple TLS records per TCP segment handling + url = mg_mprintf("https://%s:8443", host_ip); // for historic reasons + ASSERT(system("tls_multirec/server -d tls_multirec &") == 0); + sleep(1); + ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200); + ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile + ASSERT(system("killall tls_multirec/server") == 0); + free(url); +#else + (void) cmpbody("", ""); + (void) mgr; +#endif +} + +bool mip_x_test(struct mg_mgr *mgr) { + + host_ip = getenv("HOST_IP"); + +#define DASHBOARD(x) printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false":"true"); + + usleep(500000); // 500 ms + s_error = false; + test_http_client(mgr); + DASHBOARD("http_client"); + + usleep(500000); // 500 ms + s_error = false; + test_http_server(mgr); + DASHBOARD("http_server"); + + usleep(500000); // 500 ms + s_error = false; + test_tls(mgr); + DASHBOARD("tls"); + + usleep(500000); // 500 ms + s_error = false; + test_mqtt_connsubpub(mgr); + DASHBOARD("mqtt"); + + // Clear + s_error = false; + mg_mgr_free(mgr); + ASSERT(mgr->conns == NULL); // Deconstruction OK + printf("HEALTH_DASHBOARD\t\"cleanup\": %s\n", s_error ? "false":"true"); + // last entry with no comma + +#ifdef NO_ABORT + if (s_abort != 0) return false; +#endif + + printf("SUCCESS. Total tests: %d\n", s_num_tests); + return true; +} diff --git a/test/port_tap_bridge.c b/test/port_tap_bridge.c new file mode 100644 index 00000000..92057ef8 --- /dev/null +++ b/test/port_tap_bridge.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __OpenBSD__ +#include +#include +#else +#include +#include +#include +#endif + +static int s_signo; +void signal_handler(int signo) { + s_signo = signo; +} + +int main(void) { + // Setup interface + const char *iface = "tap0"; // Network iface + const uint16_t port = 0x55AA; // UDP port +#ifndef __OpenBSD__ + const char *tuntap_device = "/dev/net/tun"; +#else + const char *tuntap_device = "/dev/tap0"; +#endif + int fd = open(tuntap_device, O_RDWR); + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, IFNAMSIZ); +#ifndef __OpenBSD__ + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) { +#else + ifr.ifr_flags = (short) (IFF_UP | IFF_BROADCAST | IFF_MULTICAST); + if (ioctl(fd, TUNSIFMODE, (void *) &ifr) < 0) { +#endif + printf("Failed to setup TAP interface: %s", ifr.ifr_name); + return EXIT_FAILURE; + } + printf("Opened TAP interface: %s\n", iface); + + int sockfd; + struct sockaddr_in tap_addr, port_addr; + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + tap_addr.sin_family = AF_INET; + tap_addr.sin_addr.s_addr = htonl(0x7f000001U); // 127.0.0.1 + tap_addr.sin_port = htons(port); + if (bind(sockfd, (struct sockaddr *) &tap_addr, sizeof(tap_addr)) < 0) return EXIT_FAILURE; + printf("Opened UDP socket\n"); + + memset(&port_addr, 0, sizeof(port_addr)); + + while (s_signo == 0) { + struct pollfd pfd[2]; + int result; + do { + pfd[0].fd = fd; + pfd[0].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write + pfd[1].fd = sockfd; + pfd[1].events = POLLIN | POLLPRI; // will wait for input available (read ready), use POLLOUT for write + + // only read ready shown here, for write ready use the 3rd arg, and the 4th for exceptions + result = poll(pfd, 2, 1000); // 2 pfd, TIMEOUT in milliseconds + switch(result) { + case -1: // error + break; + case 0: // timeout (if a timeout struct was passed to select) + break; + default: // the number of fd/sockets with data available (see pfd.revents for each one); + uint8_t buf[1540]; + if (pfd[0].revents & (POLLIN | POLLPRI)) { + ssize_t nread = read(fd, buf, 1540); +// printf("Got at TAP: %d bytes\n", (int) nread); + if (port_addr.sin_family != 0) { // something has been written to this struct (we got a conterpart) + sendto(sockfd, buf, (size_t) nread, 0, (struct sockaddr *) &port_addr, sizeof(port_addr)); +// printf("Sent to %s port %d\n", inet_ntoa(port_addr.sin_addr), ntohs(port_addr.sin_port)); + } + } + if (pfd[1].revents & (POLLIN | POLLPRI)) { + socklen_t len = sizeof(port_addr); + ssize_t nread = recvfrom(sockfd, buf, 1540, 0, (struct sockaddr *) &port_addr, &len); +// printf("Got at UDP: %d bytes from %s port %d\n", nread, inet_ntoa(port_addr.sin_addr), ntohs(port_addr.sin_port)); + write(fd, buf, (size_t) nread); +// printf("Sent to TAP\n"); + } + break; + } + } while (result >= 0); + printf("looks like we got an error here... should I quit ? Waiting for CTRL-C\n"); + } + close(sockfd); + close(fd); +}