/********************************************************************* PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. See LICENSE and COPYING for usage. Authors: Daniele Lacamera, Jelle De Vleeschouwer *********************************************************************/ /******************************************************************************* * PicoTCP ******************************************************************************/ #include "pico_dev_radiotest.h" #include "pico_6lowpan_ll.h" #include "pico_addressing.h" #include "pico_dev_tap.h" #include "pico_802154.h" #include "pico_device.h" #include "pico_config.h" #include "pico_stack.h" /******************************************************************************* * System sockets ******************************************************************************/ #include #include #include #include #include #include #define LISTENING_PORT 7777 #define MESSAGE_MTU 150 #ifdef RADIO_PCAP #include #endif #ifdef DEBUG_RADIOTEST #define RADIO_DBG dbg #else #define RADIO_DBG(...) do { } while (0) #endif /******************************************************************************* * Constants ******************************************************************************/ /* Uncomment next line to enable Random packet loss (specify percentage) */ //#define PACKET_LOSS 3 #define RFDEV_PANID 0xABCD #define MC_ADDR_BE 0x010101EBU #define LO_ADDR 0x0100007FU #define LOOP_MTU 127 /******************************************************************************* * Type definitions ******************************************************************************/ struct radiotest_radio { struct pico_dev_6lowpan dev; struct pico_6lowpan_info addr; int sock0; int sock1; #ifdef RADIO_PCAP pcap_t *pcap; pcap_dumper_t *pcapd; #endif }; struct radiotest_frame { uint8_t *buf; int len; uint32_t id; union pico_ll_addr src; union pico_ll_addr dst; }; /******************************************************************************* * Global variables ******************************************************************************/ static int connection = 0; static uint32_t tx_id = 0; static uint32_t rx_id = 0; /******************************************************************************* * pcap ******************************************************************************/ #ifdef RADIO_PCAP static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump) { char path[100]; /* Open offline packet capture */ #ifdef PICO_SUPPORT_802154 dev->pcap = pcap_open_dead(DLT_IEEE802_15_4, 65535); #elif defined (PICO_SUPPORT_802154_NO_MAC) dev->pcap = pcap_open_dead(DLT_RAW, 65535); #endif if (!dev->pcap) { perror("LibPCAP"); exit (1); } /* Construct file path */ snprintf(path, 100, dump, dev->addr); /* Open dump */ dev->pcapd = pcap_dump_open(dev->pcap, path); if (dev->pcapd) dbg("PCAP Enabled\n"); else dbg("PCAP Disabled\n"); } static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len) { struct pcap_pkthdr ph; if (!dev || !dev->pcapd) return; ph.caplen = (uint32_t)len; ph.len = (uint32_t)len; gettimeofday(&ph.ts, NULL); pcap_dump((u_char *)dev->pcapd, &ph, buf); pcap_dump_flush(dev->pcapd); } #else static void radiotest_pcap_open(struct radiotest_radio *dev, char *dump) { (void)dev; (void)dump; } static void radiotest_pcap_write(struct radiotest_radio *dev, uint8_t *buf, int len) { (void)dev; (void)buf; (void)len; } #endif static int radiotest_cmp(void *a, void *b) { struct radiotest_frame *fa = (struct radiotest_frame *)a; struct radiotest_frame *fb = (struct radiotest_frame *)b; return (int)(fa->id - fb->id); } PICO_TREE_DECLARE(LoopFrames, radiotest_cmp); static uint8_t *radiotest_nxt_rx(int *len, union pico_ll_addr *src, union pico_ll_addr *dst) { struct radiotest_frame test, *found = NULL; uint8_t *ret = NULL; test.id = rx_id++; found = pico_tree_findKey(&LoopFrames, &test); if (found) { ret = found->buf; *len = found->len; *src = found->src; *dst = found->dst; pico_tree_delete(&LoopFrames, found); PICO_FREE(found); } else { rx_id--; } return ret; } static void radiotest_nxt_tx(uint8_t *buf, int len, union pico_ll_addr src, union pico_ll_addr dst) { struct radiotest_frame *new = PICO_ZALLOC(sizeof(struct radiotest_frame)); if (new) { new->buf = PICO_ZALLOC((uint16_t)len); if (new->buf) { memcpy(new->buf, buf, (size_t)len); new->len = len; new->id = tx_id++; new->src = src; new->dst = dst; if (pico_tree_insert(&LoopFrames, new)) { PICO_FREE(new); tx_id--; } } else { PICO_FREE(new); } } } static int pico_loop_send(struct pico_device *dev, void *buf, int len, union pico_ll_addr src, union pico_ll_addr dst) { IGNORE_PARAMETER(dev); if (len > LOOP_MTU) return 0; RADIO_DBG("Looping back frame of %d bytes.\n", len); radiotest_nxt_tx(buf, len, src, dst); return len; } static int pico_loop_poll(struct pico_device *dev, int loop_score) { union pico_ll_addr src, dst; uint8_t *buf = NULL; int len = 0; if (loop_score <= 0) return 0; buf = radiotest_nxt_rx(&len, &src, &dst); if (buf) { RADIO_DBG("Receiving frame of %d bytes.\n", len); pico_6lowpan_stack_recv(dev, buf, (uint32_t)len, &src, &dst); PICO_FREE(buf); loop_score--; } return loop_score; } /* Generates a simple extended address */ static void radiotest_gen_ex(struct pico_6lowpan_short addr_short, uint8_t *buf) { uint16_t sh = addr_short.addr; buf[0] = 0x00; buf[1] = 0x00; buf[2] = 0x00; buf[3] = 0xaa; buf[4] = 0xab; buf[5] = 0x00; buf[6] = (uint8_t)((uint8_t)(short_be(sh) & 0xFF00) >> 8u); buf[7] = (uint8_t)(short_be(sh) & 0xFFu); } /** * Simulated CRC16-CITT Kermit generation * * @param buf uint8_t *, buffer to generate FCS for. * @param len uint8_t, len of the buffer * * @return CITT Kermit CRC16 of the buffer */ static uint16_t calculate_crc16(uint8_t *buf, uint8_t len) { uint16_t crc = 0x0000; uint16_t q = 0, i = 0; uint8_t c = 0; for (i = 0; i < len; i++) { c = buf[i]; q = (crc ^ c) & 0x0F; crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081)); q = (crc ^ (c >> 4)) & 0xF; crc = (uint16_t)((uint16_t)(crc >> 4) ^ (q * 0x1081)); } return crc; } /* Poll-function for the pico_device-structure */ static int radiotest_poll(struct pico_device *dev, int loop_score) { struct radiotest_radio *radio = (struct radiotest_radio *)dev; union pico_ll_addr src = {0}, dst = {0}; int pollret, ret_len; struct pollfd p; uint8_t buf[128]; uint8_t phy = 0; if (loop_score <= 0) return 0; if (!dev) return loop_score; p.fd = connection; p.events = POLLIN | POLLHUP; /* Poll for data from radio management */ errno = 0; pollret = poll(&p, (nfds_t)1, 1); if (errno == EINTR || pollret == 0) return loop_score; if (pollret < 0) { fprintf(stderr, "Socket error %s!\n", strerror(errno)); exit(5); } if (p.revents & POLLIN) { ret_len = (int)recv(connection, &phy, (size_t)1, 0); if (ret_len != 1) return loop_score; ret_len = (int)recv(connection, buf, (size_t)phy, 0); if (ret_len != (int)phy) return loop_score; else if (!ret_len) { RADIO_DBG("Radio manager detached from network\n"); exit(1); } } if (ret_len < 2) { /* Not valid */ return loop_score; } #ifdef P_LOSS long n = lrand48(); n = n % 100; if (n < P_LOSS) { RADIO_DBG("Packet got lost!\n"); return loop_score; } #endif /* ADDRESS FILTER */ if (buf[ret_len - 1] != 0xFF && buf[ret_len - 1] != (uint8_t)short_be(radio->addr.addr_short.addr)) { RADIO_DBG("Packet is not for me!\n"); return loop_score; } /* Get src and destination address */ dst.pan.addr._ext = radio->addr.addr_ext; src.pan.addr.data[3] = 0xAA; src.pan.addr.data[4] = 0xAB; src.pan.addr.data[7] = buf[ret_len - 1]; src.pan.mode = AM_6LOWPAN_EXT; ret_len -= 2; /* Write the received frame to the pcap-dump */ radiotest_pcap_write(radio, buf, ret_len); /* Hand the frame over to pico */ pico_6lowpan_stack_recv(dev, buf, (uint32_t)(ret_len - 2), &src, &dst); loop_score--; return loop_score; } #define RADIO_OVERHEAD 4 /* Send-function for the pico_device-structure */ static int radiotest_send(struct pico_device *dev, void *_buf, int len, union pico_ll_addr src, union pico_ll_addr dst) { struct radiotest_radio *radio = (struct radiotest_radio *)dev; uint8_t *buf = PICO_ZALLOC((size_t)(len + RADIO_OVERHEAD)); uint8_t phy = 0, did = 0; uint16_t crc = 0; int ret = 0, dlen = 0; IGNORE_PARAMETER(src); if (!buf) return -1; /* Try to get node-ID from address */ if (dev && pico_6lowpan_lls[dev->mode].addr_len) { dlen = pico_6lowpan_lls[dev->mode].addr_len(&dst); if (dlen < 0) return -1; did = dst.pan.addr.data[dlen - 1]; } /* Store the addresses in buffer for management */ memcpy(buf, _buf, (size_t)len); len = (uint16_t)(len + (uint16_t)RADIO_OVERHEAD); // CRC + ID buf[len - 2] = (uint8_t)short_be(radio->addr.addr_short.addr); buf[len - 1] = did; /* Generate FCS, keep pcap happy ... */ crc = calculate_crc16(_buf, (uint8_t)(len - RADIO_OVERHEAD)); memcpy(buf + len - RADIO_OVERHEAD, (void *)&crc, 2); /* Send frame to radio management */ phy = (uint8_t)(len); ret = (int)sendto(connection, &phy, 1, 0, NULL, 0); if (ret != 1) return -1; ret = (int)sendto(connection, buf, (size_t)(len), 0, NULL, 0); RADIO_DBG("Radio '%u' transmitted a frame of %d bytes.\n", buf[len - 2], ret); /* Write the sent frame to the pcap-dump */ radiotest_pcap_write(radio, buf, len - 2); PICO_FREE(buf); return ret; } static int radiotest_hello(int s, uint8_t id, uint8_t area0, uint8_t area1) { uint8_t buf[3] = { id, area0, area1 }; if (sendto(s, buf, (size_t)3, 0, NULL, 0) != 3) { RADIO_DBG("Radio '%u' failed to send hello message\n", id); return -1; } RADIO_DBG("Radio '%u' attached to network\n", id); return s; } static int radiotest_connect(uint8_t id, uint8_t area0, uint8_t area1) { struct sockaddr_in addr; int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); int ret = 0; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(LISTENING_PORT); inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); ret = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); if (ret) { RADIO_DBG("Radio '%u' could not attach to network\n", id); return ret; } return radiotest_hello(s, id, area0, area1); } static void pico_radiotest_quit(int signum) { IGNORE_PARAMETER(signum); dbg("Quitting radiotest\n"); exit(0); } /* Creates a radiotest-device */ struct pico_device *pico_radiotest_create(uint8_t addr, uint8_t area0, uint8_t area1, int loop, char *dump) { struct radiotest_radio *radio = PICO_ZALLOC(sizeof(struct radiotest_radio)); struct pico_dev_6lowpan *lp = (struct pico_dev_6lowpan *)radio; if (!radio) return NULL; if (!addr || (addr && !area0)) { RADIO_DBG("Usage (node): -6 [1-255],[1-255],[0-255] ...\n"); } signal(SIGQUIT, pico_radiotest_quit); radio->addr.pan_id.addr = short_be(RFDEV_PANID); radio->addr.addr_short.addr = short_be((uint16_t)addr); radiotest_gen_ex(radio->addr.addr_short, radio->addr.addr_ext.addr); RADIO_DBG("Radiotest short address: 0x%04X\n", short_be(radio->addr.addr_short.addr)); RADIO_DBG("Radiotest ext address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", radio->addr.addr_ext.addr[0],radio->addr.addr_ext.addr[1], radio->addr.addr_ext.addr[2],radio->addr.addr_ext.addr[3], radio->addr.addr_ext.addr[4],radio->addr.addr_ext.addr[5], radio->addr.addr_ext.addr[6],radio->addr.addr_ext.addr[7]); if (!loop) { if ((connection = radiotest_connect(addr, area0, area1)) <= 0) { return NULL; } if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, radiotest_send, radiotest_poll)) { RADIO_DBG("pico_device_init failed.\n"); pico_device_destroy((struct pico_device *)lp); return NULL; } } else { if (pico_dev_6lowpan_init(lp, "radio", (uint8_t *)&radio->addr, LL_MODE_IEEE802154, MTU_802154_MAC, 0, pico_loop_send, pico_loop_poll)) { RADIO_DBG("pico_device_init failed.\n"); pico_device_destroy((struct pico_device *)lp); return NULL; } } if (dump) { dbg("Dump: %s\n", dump); radiotest_pcap_open(radio, dump); } return (struct pico_device *)lp; }