From 2e8264010ba5635182a3d415b0d5cc45d6b96c64 Mon Sep 17 00:00:00 2001 From: julienabeille Date: Tue, 14 Oct 2008 09:42:33 +0000 Subject: [PATCH] Main uIPv6 files addition - more --- core/net/sicslowpan.c | 1423 ++++++++++++++++++++++++++++ core/net/sicslowpan.h | 259 +++++ core/net/uip-icmp6.c | 185 ++++ core/net/uip-icmp6.h | 127 +++ core/net/uip-nd6-io.c | 919 ++++++++++++++++++ core/net/uip-nd6.c | 471 +++++++++ core/net/uip-nd6.h | 688 ++++++++++++++ core/net/uip-netif.c | 445 +++++++++ core/net/uip-netif.h | 207 ++++ core/net/uip6.c | 2108 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 6832 insertions(+) create mode 100644 core/net/sicslowpan.c create mode 100644 core/net/sicslowpan.h create mode 100644 core/net/uip-icmp6.c create mode 100644 core/net/uip-icmp6.h create mode 100644 core/net/uip-nd6-io.c create mode 100644 core/net/uip-nd6.c create mode 100644 core/net/uip-nd6.h create mode 100644 core/net/uip-netif.c create mode 100644 core/net/uip-netif.h create mode 100644 core/net/uip6.c diff --git a/core/net/sicslowpan.c b/core/net/sicslowpan.c new file mode 100644 index 000000000..6c2ca0b76 --- /dev/null +++ b/core/net/sicslowpan.c @@ -0,0 +1,1423 @@ +/** + * \addtogroup sicslowpan + * @{ + */ +/* + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: sicslowpan.c,v 1.1 2008/10/14 09:42:33 julienabeille Exp $ + */ +/** + * \file + * 6lowpan implementation (RFC4944 and draft-hui-6lowpan-hc-01) + * + * \author Adam Dunkels + * \author Nicolas Tsiftes + * \author Niclas Finne + * \author Mathilde Durvy + * \author Julien Abeille + */ + +#include + +#include "net/tcpip.h" +#include "net/uip.h" +#include "net/uip-netif.h" +#include "net/rime.h" +#include "net/sicslowpan.h" + +#include + + +#define DEBUG 0 +#if DEBUG +u8_t p; +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5],lladdr->addr[6], lladdr->addr[7]) +#define PRINTRIMEBUF() PRINTF("RIME buffer: "); for(p = 0; p < rimebuf_datalen(); p++){PRINTF("%.2X", *(rime_ptr + p));} PRINTF("\n") +#define PRINTUIPBUF() PRINTF("UIP buffer: "); for(p = 0; p < uip_len; p++){PRINTF("%.2X", uip_buf[p]);}PRINTF("\n") +#define PRINTSICSLOWPANBUF() PRINTF("SICSLOWPAN buffer: "); for(p = 0; p < sicslowpan_len; p++){PRINTF("%.2X", sicslowpan_buf[p]);}PRINTF("\n") +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(lladdr) +#define PRINTRIMEBUF() +#define PRINTUIPBUF() +#define PRINTSICSLOWPANBUF() +#endif /* DEBUG == 1*/ + +#if UIP_LOGGING +#include +void uip_log(char *msg); +#define UIP_LOG(m) uip_log(m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +/** \name Pointers in the rime buffer + * @{ + */ +#define RIME_FRAG_BUF ((struct sicslowpan_frag_hdr *)rime_ptr) +#define RIME_HC1_BUF ((struct sicslowpan_hc1_hdr *)(rime_ptr + rime_hdr_len)) +#define RIME_HC1_HC_UDP_BUF ((struct sicslowpan_hc1_hc_udp_hdr *)(rime_ptr + rime_hdr_len)) +#define RIME_IP_BUF ((struct uip_ip_hdr *)(rime_ptr + rime_hdr_len)) +#define RIME_IPHC_BUF ((struct sicslowpan_iphc_hdr *)(rime_ptr + rime_hdr_len)) +/** @} */ + +/** \name Pointers in the sicslowpan and uip buffer + * @{ + */ +#define SICSLOWPAN_IP_BUF ((struct uip_ip_hdr *)&sicslowpan_buf[UIP_LLH_LEN]) +#define SICSLOWPAN_UDP_BUF ((struct uip_udp_hdr *)&sicslowpan_buf[UIP_LLIPH_LEN]) + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN]) +/** @} */ + + +/** \brief Size of the 802.15.4 payload (127byte - 25 for MAC header) */ +#define MAC_MAX_PAYLOAD 102 + +/** \name General variables + * @{ + */ +/** A pointer to the mac driver */ +static const struct mac_driver *mac; + +/** + * A pointer to the rime buffer. + * We initialize it to the beginning of the rime buffer, then + * access different fields by updating the offset rime_hdr_len. + */ +static u8_t *rime_ptr; + +/** + * rime_hdr_len is the total length of (the processed) 6lowpan headers + * (fragment headers, IPV6 or HC1, HC2, and HC1 and HC2 non compressed + * fields). + */ +static u8_t rime_hdr_len; + +/** + * The length of the payload in the Rime buffer. + * The payload is what comes after the compressed or uncompressed + * headers (can be the IP payload if the IP header only is compressed + * or the UDP payload if the UDP header is also compressed) + */ +static u8_t rime_payload_len; + +/** + * uncomp_hdr_len is the length of the headers before compression (if HC2 + * is used this includes the UDP header in addition to the IP header). + */ +static u8_t uncomp_hdr_len; +/** @} */ + +#if SICSLOWPAN_CONF_FRAG +/** \name Fragmentation related variables + * @{ + */ +/** + * The buffer used for the 6lowpan reassembly. + * This buffer contains only the IPv6 packet (no MAC header, 6lowpan, etc). + * It has a fix size as we do not use dynamic memory allocation. + */ +static u8_t sicslowpan_buf[UIP_BUFSIZE]; + +/** The total length of the IPv6 packet in the sicslowpan_buf. */ +static u16_t sicslowpan_len; + +/** + * length of the ip packet already sent / received. + * It includes IP and transport headers. + */ +static u16_t processed_ip_len; + +/** Datagram tag to be put in the fragments I send. */ +static u16_t my_tag; + +/** When reassembling, the tag in the fragments being merged. */ +static u16_t reass_tag; + +/** When reassembling, the source address of the fragments being merged */ +rimeaddr_t frag_sender; + +/** Reassembly %process %timer. */ +static struct timer reass_timer; + + +/** @} */ +#else /* SICSLOWPAN_CONF_FRAG */ +/** The buffer used for the 6lowpan processing is uip_buf. + We do not use any additional buffer.*/ +#define sicslowpan_buf uip_buf +#define sicslowpan_len uip_len +#endif /* SICSLOWPAN_CONF_FRAG */ + + +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01 +/** \name HC01 specific variables + * @{ + */ +/** Addresses contexts for IPHC. */ +static struct sicslowpan_addr_context +addr_contexts[SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS]; + +/** pointer to an address context. */ +static struct sicslowpan_addr_context *context; + +/** pointer to the byte where to write next inline field. */ +static u8_t *hc01_ptr; + +/** Index for loops. */ +static u8_t i; +/** @} */ + + +/*--------------------------------------------------------------------*/ +/** \name HC01 related functions + * @{ */ +/*--------------------------------------------------------------------*/ +/** \brief find the context corresponding to prefix ipaddr */ +static struct sicslowpan_addr_context* +addr_context_lookup_by_prefix(uip_ipaddr_t *ipaddr) { + for(i = 0; i < SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS; i++) { + if((addr_contexts[i].used == 1) && + uip_ipaddr_prefixcmp(&addr_contexts[i].prefix, ipaddr, 64)) { + return &addr_contexts[i]; + } + } + return NULL; +} +/*--------------------------------------------------------------------*/ +/** \brief find the context with the given number */ +static struct sicslowpan_addr_context* +addr_context_lookup_by_number(u8_t number) { + for(i = 0; i < SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS; i++) { + if((addr_contexts[i].used == 1) && + addr_contexts[i].number == number) { + return &addr_contexts[i]; + } + } + return NULL; +} +/*--------------------------------------------------------------------*/ +/** + * \brief Compress IP/UDP header + * + * This function is called by the 6lowpan code to create a compressed + * 6lowpan packet in the rimebuf buffer from a full IPv6 packet in the + * uip_buf buffer. + * + * + * HC01 (draft-hui-6lowpan-hc, version 1)\n + * + * \note We do not support ISA100_UDP header compression + * + * For LOWPAN_UDP compression, we either compress both ports or none. + * General format with LOWPAN_UDP compression is + * \verbatim + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | HC01 Dsp | HC01 encoding | non | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | compressed IPv6 fields ..... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | LOWPAN_UDP | non compressed UDP fields ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | L4 data ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endverbatim + * \note The context number 00 is reserved for the link local prefix. + * For unicast addresses, if we cannot compress the prefix, we neither + * compress the IID. + * \param rime_destaddr L2 destination address, needed to compress IP + * dest + */ +/* + * Address compression logic (multicast only applies to dest): + * If multicast: + * If flags (see RFC4291 section 2.7) are all 0 AND + * the 112-bit group id is mapable to a 9-bit group + * (for now all nodes and all routers groups are + * mapable), + * we compress to 16 bits + * Else unicast: + * If we have a context for the prefix, + * we elide 64 bits prefix. + * If the IID can be inferred from lower layers, + * we elide 64 bits IID + * else + * if first 49 bits of IID are 0, + * we compress IID to 16 bits (with first = 0 + * to differentiate from multicast) + * + */ +static void +compress_hdr_hc01(rimeaddr_t *rime_destaddr) +{ + hc01_ptr = rime_ptr + 3; + /* + * As we copy some bit-length fields, in the IPHC encoding bytes, + * we sometimes use |= + * If the field is 0, and the current bit value in memory is 1, + * this does not work. We therefore reset the IPHC encoding here + */ + memset(RIME_IPHC_BUF->encoding, 0, 2); + + RIME_IPHC_BUF->dispatch = SICSLOWPAN_DISPATCH_IPHC; + /* + * Version, traffic class, flow label + * If flow label is 0, compress it. If traffic class is 0, compress it + * We have to process both in the same time as the offset of traffic class + * depends on the presence of version and flow label + */ + if(((UIP_IP_BUF->tcflow & 0x0F) == 0) && + (UIP_IP_BUF->flow == 0)) { + /* version and flow label can be compressed */ + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_VF_C; + if(((UIP_IP_BUF->vtc & 0x0F) == 0) && + ((UIP_IP_BUF->tcflow & 0xF0) == 0)) { + /* compress (elide) all */ + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_TC_C; + } else { + /* compress only version and flow label */ + *hc01_ptr = (UIP_IP_BUF->vtc << 4) | + (UIP_IP_BUF->tcflow >> 4); + hc01_ptr += 1; + } + } else { + /* version and flow label cannot be compressed */ + if(((UIP_IP_BUF->vtc & 0x0F) == 0) && + ((UIP_IP_BUF->tcflow & 0xF0) == 0)) { + /* compress only traffic class */ + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_TC_C; + *hc01_ptr = (UIP_IP_BUF->vtc & 0xF0) | + (UIP_IP_BUF->tcflow & 0x0F); + memcpy(hc01_ptr + 1, &UIP_IP_BUF->flow, 2); + hc01_ptr += 3; + } else { + /* compress nothing */ + memcpy(hc01_ptr, &UIP_IP_BUF->vtc, 4); + hc01_ptr += 4; + } + } + + /* Note that the payload length is always compressed */ + + /* Next header. We compress it if UDP */ +#if UIP_CONF_UDP + if(UIP_IP_BUF->proto == UIP_PROTO_UDP) { + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_NH_C; + } else { +#endif /*UIP_CONF_UDP*/ + *hc01_ptr = UIP_IP_BUF->proto; + hc01_ptr += 1; +#if UIP_CONF_UDP + } +#endif /*UIP_CONF_UDP*/ + + /* + * Hop limit + * if 1: compress, encoding is 01 + * if 64: compress, encoding is 10 + * if 255: compress, encoding is 11 + * else do not compress + */ + switch(UIP_IP_BUF->ttl) { + case 1: + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_TTL_1; + break; + case 64: + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_TTL_64; + break; + case 255: + RIME_IPHC_BUF->encoding[0] |= SICSLOWPAN_IPHC_TTL_255; + break; + default: + *hc01_ptr = UIP_IP_BUF->ttl; + hc01_ptr += 1; + break; + } + + /* source address - cannot be multicast */ + if((context = addr_context_lookup_by_prefix(&UIP_IP_BUF->srcipaddr)) + != NULL) { + /* elide the prefix */ + RIME_IPHC_BUF->encoding[1] |= context->number << 4; + if(uip_is_addr_mac_addr_based(&UIP_IP_BUF->srcipaddr, &uip_lladdr)){ + /* elide the IID */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_SAM_0; + } else { + if(sicslowpan_is_iid_16_bit_compressable(&UIP_IP_BUF->srcipaddr)){ + /* compress IID to 16 bits */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_SAM_16; + memcpy(hc01_ptr, &UIP_IP_BUF->srcipaddr.u16[7], 2); + hc01_ptr += 2; + } else { + /* do not compress IID */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_SAM_64; + memcpy(hc01_ptr, &UIP_IP_BUF->srcipaddr.u16[4], 8); + hc01_ptr += 8; + } + } + } else { + /* send the full address */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_SAM_I; + memcpy(hc01_ptr, &UIP_IP_BUF->srcipaddr.u16[0], 16); + hc01_ptr += 16; + } + + /* dest address*/ + if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) { + /* Address is multicast, try to compress */ + if(sicslowpan_is_mcast_addr_compressable(&UIP_IP_BUF->destipaddr)) { + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_16; + /* 3 first bits = 101 */ + *hc01_ptr = SICSLOWPAN_IPHC_MCAST_RANGE; + /* bits 3-6 = scope = bits 8-11 in 128 bits address */ + *hc01_ptr |= (UIP_IP_BUF->destipaddr.u8[1] & 0x0F) << 1; + /* + * bits 7 - 15 = 9-bit group + * We just copy the last byte because it works + * with currently supported groups + */ + *(hc01_ptr + 1) = UIP_IP_BUF->destipaddr.u8[15]; + hc01_ptr += 2; + } else { + /* send the full address */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_I; + memcpy(hc01_ptr, &UIP_IP_BUF->destipaddr.u16[0], 16); + hc01_ptr += 16; + } + } else { + /* Address is unicast, try to compress */ + if((context = addr_context_lookup_by_prefix(&UIP_IP_BUF->destipaddr)) != NULL) { + /* elide the prefix */ + RIME_IPHC_BUF->encoding[1] |= context->number; + if(uip_is_addr_mac_addr_based(&UIP_IP_BUF->destipaddr, (uip_lladdr_t *)rime_destaddr)) { + /* elide the IID */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_0; + } else { + if(sicslowpan_is_iid_16_bit_compressable(&UIP_IP_BUF->destipaddr)) { + /* compress IID to 16 bits */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_16; + memcpy(hc01_ptr, &UIP_IP_BUF->destipaddr.u16[7], 2); + hc01_ptr += 2; + } else { + /* do not compress IID */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_64; + memcpy(hc01_ptr, &UIP_IP_BUF->destipaddr.u16[4], 8); + hc01_ptr += 8; + } + } + } else { + /* send the full address */ + RIME_IPHC_BUF->encoding[1] |= SICSLOWPAN_IPHC_DAM_I; + memcpy(hc01_ptr, &UIP_IP_BUF->destipaddr.u16[0], 16); + hc01_ptr += 16; + } + } + uncomp_hdr_len = UIP_IPH_LEN; + +#if UIP_CONF_UDP + /* UDP header compression */ + if(UIP_IP_BUF->proto == UIP_PROTO_UDP) { + if(HTONS(UIP_UDP_BUF->srcport) >= SICSLOWPAN_UDP_PORT_MIN && + HTONS(UIP_UDP_BUF->srcport) < SICSLOWPAN_UDP_PORT_MAX && + HTONS(UIP_UDP_BUF->destport) >= SICSLOWPAN_UDP_PORT_MIN && + HTONS(UIP_UDP_BUF->destport) < SICSLOWPAN_UDP_PORT_MAX) { + /* we can compress. Copy compressed ports, full chcksum */ + *hc01_ptr = SICSLOWPAN_NHC_UDP_C; + *(hc01_ptr + 1) = + (u8_t)((HTONS(UIP_UDP_BUF->srcport) - + SICSLOWPAN_UDP_PORT_MIN) << 4) + + (u8_t)((HTONS(UIP_UDP_BUF->destport) - + SICSLOWPAN_UDP_PORT_MIN)); + memcpy(hc01_ptr + 2, &UIP_UDP_BUF->udpchksum, 2); + hc01_ptr += 4; + } else { + /* we cannot compress. Copy uncompressed ports, full chcksum */ + *hc01_ptr = SICSLOWPAN_NHC_UDP_I; + memcpy(hc01_ptr + 1, &UIP_UDP_BUF->srcport, 4); + memcpy(hc01_ptr + 5, &UIP_UDP_BUF->udpchksum, 2); + hc01_ptr += 7; + } + uncomp_hdr_len += UIP_UDPH_LEN; + } +#endif /*UIP_CONF_UDP*/ + rime_hdr_len = hc01_ptr - rime_ptr; + return; +} + +/*--------------------------------------------------------------------*/ +/** + * \brief Uncompress HC01 (i.e., IPHC and LOWPAN_UDP) headers and put + * them in sicslowpan_buf + * + * This function is called by the input function when the dispatch is + * HC01. + * We %process the packet in the rime buffer, uncompress the header + * fields, and copy the result in the sicslowpan buffer. + * At the end of the decompression, rime_hdr_len and uncompressed_hdr_len + * are set to the appropriate values + * + * \param ip_len Equal to 0 if the packet is not a fragment (IP length + * is then inferred from the L2 length), non 0 if the packet is a 1st + * fragment. + */ + +/* Processing Details + * - IP header + * We process the fields in their order of appearance in the normal + * IP header, with two exceptions: + * - next header: field when it is compressed: we need to reach the + * NHC encoding to know which is the next header + * - length: we need to know the length of headers in rime buffer + * (i.e. the final value of rime_hdr_len) + * - Addresses processing + * We do the same for src and dest, even though a multicast source + * address is wrong. IP layer will handle this. The logic is: + * + * Switch(compression) + * case no compression: copy full address + * case 64bit compressed address: + * find the context, copy prefix from context, + * copy IID from packet + * case all 128 bit of the address are ellided: + * find the context, copy prefix from context, + * infer IID from L2 address + * case 16 bit compressed address: + * if 1st bit = 0 (unicast case) + * copy prefix from context, then 48 zeros + * then 16 last bits from packet + * else (multicast case) + * first byte = FF, flags = 0, copy scope + * from packet, infer 112 bits group ID from + * 9 bit group id + * + * - UDP header, for LOWPAN_UDP compression + * The only trick is that we fill the length field at the end of + * the function. + */ + +static void +uncompress_hdr_hc01(u16_t ip_len) { + hc01_ptr = rime_ptr + rime_hdr_len + 3; + + /* Version and flow label */ + if((RIME_IPHC_BUF->encoding[0] & 0x40) == 0) { + /* Version and flow label are carried inline */ + if((RIME_IPHC_BUF->encoding[0] & 0x80) == 0) { + /* Traffic class is carried inline */ + memcpy(&SICSLOWPAN_IP_BUF->vtc, hc01_ptr, 4); + hc01_ptr += 4; + } else { + /* Traffic class is compressed */ + SICSLOWPAN_IP_BUF->vtc = 0x60; + SICSLOWPAN_IP_BUF->tcflow = *hc01_ptr & 0x0F; + memcpy(&SICSLOWPAN_IP_BUF->flow, hc01_ptr + 1, 2); + hc01_ptr += 3; + } + } else { + /* Version and flow label are compressed */ + if((RIME_IPHC_BUF->encoding[0] & 0x80) == 0) { + /* Traffic class is inline */ + SICSLOWPAN_IP_BUF->vtc = 0x60 | (*hc01_ptr >> 4); + SICSLOWPAN_IP_BUF->tcflow = *hc01_ptr << 4; + hc01_ptr += 1; + } else { + /* Traffic class is compressed */ + SICSLOWPAN_IP_BUF->vtc = 0x60; + SICSLOWPAN_IP_BUF->tcflow = 0; + } + SICSLOWPAN_IP_BUF->flow = 0; + } + /* Next Header */ + if((RIME_IPHC_BUF->encoding[0] & 0x20) == 0) { + /* Next header is carried inline */ + SICSLOWPAN_IP_BUF->proto = *hc01_ptr; + hc01_ptr += 1; + } + + /* Hop limit */ + switch(RIME_IPHC_BUF->encoding[0] & 0x18) { + case SICSLOWPAN_IPHC_TTL_1: + SICSLOWPAN_IP_BUF->ttl = 1; + break; + case SICSLOWPAN_IPHC_TTL_64: + SICSLOWPAN_IP_BUF->ttl = 64; + break; + case SICSLOWPAN_IPHC_TTL_255: + SICSLOWPAN_IP_BUF->ttl = 255; + break; + case SICSLOWPAN_IPHC_TTL_I: + SICSLOWPAN_IP_BUF->ttl = *hc01_ptr; + hc01_ptr += 1; + break; + } + + /* Source address */ + context = + addr_context_lookup_by_number((RIME_IPHC_BUF->encoding[1] & 0x30) >> 4); + + switch(RIME_IPHC_BUF->encoding[1] & 0xC0) { + case SICSLOWPAN_IPHC_SAM_0: + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + /* copy prefix from context */ + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr, context->prefix, 8); + /* infer IID from L2 address */ + uip_netif_addr_autoconf_set(&SICSLOWPAN_IP_BUF->srcipaddr, + (uip_lladdr_t *)rimebuf_addr(RIMEBUF_ADDR_SENDER)); + break; + case SICSLOWPAN_IPHC_SAM_16: + if((*hc01_ptr & 0x80) == 0) { + /* unicast address */ + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr, context->prefix, 8); + /* copy 6 NULL bytes then 2 last bytes of IID */ + memset(&SICSLOWPAN_IP_BUF->srcipaddr.u8[8], 0, 6); + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr.u8[14], hc01_ptr, 2); + hc01_ptr += 2; + } else { + /* multicast address check the 9-bit group-id is known */ + if(sicslowpan_is_mcast_addr_decompressable(hc01_ptr)) { + SICSLOWPAN_IP_BUF->srcipaddr.u8[0] = 0xFF; + SICSLOWPAN_IP_BUF->srcipaddr.u8[1] = (*hc01_ptr >> 1) & 0x0F; + memset(&SICSLOWPAN_IP_BUF->srcipaddr.u8[2], 0, 13); + SICSLOWPAN_IP_BUF->srcipaddr.u8[15] = *(hc01_ptr + 1); + hc01_ptr += 2; + } else { + PRINTF("sicslowpan uncompress_hdr: error unknown compressed mcast address\n"); + return; + } + } + break; + case SICSLOWPAN_IPHC_SAM_64: + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + /* copy prefix from context */ + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr, context->prefix, 8); + /* copy IID from packet */ + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr.u8[8], hc01_ptr, 8); + hc01_ptr += 8; + break; + case SICSLOWPAN_IPHC_SAM_I: + /* copy whole address from packet */ + memcpy(&SICSLOWPAN_IP_BUF->srcipaddr.u8[0], hc01_ptr, 16); + hc01_ptr += 16; + break; + } + + /* Destination address */ + context = addr_context_lookup_by_number(RIME_IPHC_BUF->encoding[1] & 0x03); + + switch(RIME_IPHC_BUF->encoding[1] & 0x0C) { + case SICSLOWPAN_IPHC_DAM_0: + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + /* copy prefix from context */ + memcpy(&SICSLOWPAN_IP_BUF->destipaddr, context->prefix, 8); + /* infer IID from L2 address */ + uip_netif_addr_autoconf_set(&SICSLOWPAN_IP_BUF->destipaddr, + (uip_lladdr_t *)rimebuf_addr(RIMEBUF_ADDR_RECEIVER)); + break; + case SICSLOWPAN_IPHC_DAM_16: + if((*hc01_ptr & 0x80) == 0) { + /* unicast address */ + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + memcpy(&SICSLOWPAN_IP_BUF->destipaddr, context->prefix, 8); + /* copy 6 NULL bytes then 2 last bytes of IID */ + memset(&SICSLOWPAN_IP_BUF->destipaddr.u8[8], 0, 6); + memcpy(&SICSLOWPAN_IP_BUF->destipaddr.u8[14], hc01_ptr, 2); + hc01_ptr += 2; + } else { + /* multicast address check the 9-bit group-id is known */ + if(sicslowpan_is_mcast_addr_decompressable(hc01_ptr)) { + SICSLOWPAN_IP_BUF->destipaddr.u8[0] = 0xFF; + SICSLOWPAN_IP_BUF->destipaddr.u8[1] = (*hc01_ptr >> 1) & 0x0F; + memset(&SICSLOWPAN_IP_BUF->destipaddr.u8[2], 0, 13); + SICSLOWPAN_IP_BUF->destipaddr.u8[15] = *(hc01_ptr + 1); + hc01_ptr += 2; + } else { + PRINTF("sicslowpan uncompress_hdr: error unknown compressed mcast address\n"); + return; + } + } + break; + case SICSLOWPAN_IPHC_DAM_64: + if(context == NULL) { + PRINTF("sicslowpan uncompress_hdr: error context not found\n"); + return; + } + memcpy(&SICSLOWPAN_IP_BUF->destipaddr, context->prefix, 8); + memcpy(&SICSLOWPAN_IP_BUF->destipaddr.u8[8], hc01_ptr, 8); + hc01_ptr += 8; + break; + case SICSLOWPAN_IPHC_DAM_I: + /* copy whole address from packet */ + memcpy(&SICSLOWPAN_IP_BUF->destipaddr.u8[0], hc01_ptr, 16); + hc01_ptr += 16; + break; + } + uncomp_hdr_len += UIP_IPH_LEN; + + /* Next header processing - continued */ + if((RIME_IPHC_BUF->encoding[0] & 0x20) != 0) { + /* The next header is compressed, NHC is following */ + if((*hc01_ptr & 0xFC) == SICSLOWPAN_NHC_UDP_ID) { + SICSLOWPAN_IP_BUF->proto = UIP_PROTO_UDP; + switch(*hc01_ptr) { + case SICSLOWPAN_NHC_UDP_C: + /* 1 byte for NHC, 1 byte for ports, 2 bytes chksum */ + SICSLOWPAN_UDP_BUF->srcport = HTONS(SICSLOWPAN_UDP_PORT_MIN + + (*(hc01_ptr + 1) >> 4)); + SICSLOWPAN_UDP_BUF->destport = HTONS(SICSLOWPAN_UDP_PORT_MIN + + ((*(hc01_ptr + 1)) & 0x0F)); + memcpy(&SICSLOWPAN_UDP_BUF->udpchksum, hc01_ptr + 2, 2); + hc01_ptr += 4; + break; + case SICSLOWPAN_NHC_UDP_I: + /* 1 byte for NHC, 4 byte for ports, 2 bytes chksum */ + memcpy(&SICSLOWPAN_UDP_BUF->srcport, hc01_ptr + 1, 2); + memcpy(&SICSLOWPAN_UDP_BUF->destport, hc01_ptr + 3, 2); + memcpy(&SICSLOWPAN_UDP_BUF->udpchksum, hc01_ptr + 5, 2); + hc01_ptr += 7; + break; + default: + PRINTF("sicslowpan uncompress_hdr: error unsupported UDP compression\n"); + return; + } + uncomp_hdr_len += UIP_UDPH_LEN; + } + } + + rime_hdr_len = hc01_ptr - rime_ptr; + + /* IP length field. */ + if(ip_len == 0) { + /* This is not a fragmented packet */ + SICSLOWPAN_IP_BUF->len[0] = 0; + SICSLOWPAN_IP_BUF->len[1] = rimebuf_datalen() - rime_hdr_len + uncomp_hdr_len - UIP_IPH_LEN; + } else { + /* This is a 1st fragment */ + SICSLOWPAN_IP_BUF->len[0] = (ip_len - UIP_IPH_LEN) >> 8; + SICSLOWPAN_IP_BUF->len[1] = (ip_len - UIP_IPH_LEN) & 0x00FF; + } + + /* length field in UDP header */ + if(SICSLOWPAN_IP_BUF->proto == UIP_PROTO_UDP) { + memcpy(&SICSLOWPAN_UDP_BUF->udplen, &SICSLOWPAN_IP_BUF->len[0], 2); + } + return; +} +/** @} */ +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01*/ + + +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1 +/*--------------------------------------------------------------------*/ +/** \name HC1 compression and uncompression functions + * @{ */ +/*--------------------------------------------------------------------*/ +/** + * \brief Compress IP/UDP header using HC1 and HC_UDP + * + * This function is called by the 6lowpan code to create a compressed + * 6lowpan packet in the rimebuf buffer from a full IPv6 packet in the + * uip_buf buffer. + * + * + * If we can compress everything, we use HC1 dispatch, if not we use + * IPv6 dispatch.\n + * We can compress everything if: + * - IP version is + * - Flow label and traffic class are 0 + * - Both src and dest ip addresses are link local + * - Both src and dest interface ID are recoverable from lower layer + * header + * - Next header is either ICMP, UDP or TCP + * Moreover, if next header is UDP, we try to compress it using HC_UDP. + * This is feasible is both ports are between F0B0 and F0B0 + 15\n\n + * + * Resulting header structure: + * - For ICMP, TCP, non compressed UDP\n + * HC1 encoding = 11111010 (UDP) 11111110 (TCP) 11111100 (ICMP)\n + * \verbatim + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | LoWPAN HC1 Dsp | HC1 encoding | IPv6 Hop limit| L4 hdr + data| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endverbatim + * + * - For compressed UDP + * HC1 encoding = 11111011, HC_UDP encoding = 11100000\n + * \verbatim + * 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | LoWPAN HC1 Dsp| HC1 encoding | HC_UDP encod.| IPv6 Hop limit| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | src p.| dst p.| UDP checksum | L4 data... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endverbatim + * + * \param rime_destaddr L2 destination address, needed to compress the + * IP destination field + */ +static void +compress_hdr_hc1(rimeaddr_t *rime_destaddr) +{ + /* + * Check if all the assumptions for full compression + * are valid : + */ + if(UIP_IP_BUF->vtc != 0x60 || + UIP_IP_BUF->tcflow != 0 || + UIP_IP_BUF->flow != 0 || + !uip_is_addr_link_local(&UIP_IP_BUF->srcipaddr) || + !uip_is_addr_mac_addr_based(&UIP_IP_BUF->srcipaddr, &uip_lladdr) || + !uip_is_addr_link_local(&UIP_IP_BUF->destipaddr) || + !uip_is_addr_mac_addr_based(&UIP_IP_BUF->destipaddr, + (uip_lladdr_t *)rime_destaddr) || + (UIP_IP_BUF->proto != UIP_PROTO_ICMP6 && + UIP_IP_BUF->proto != UIP_PROTO_UDP && + UIP_IP_BUF->proto != UIP_PROTO_TCP)) + { + /* + * IPV6 DISPATCH + * Something cannot be compressed, use IPV6 DISPATCH, + * compress nothing, copy IPv6 header in rime buffer + */ + *rime_ptr = SICSLOWPAN_DISPATCH_IPV6; + rime_hdr_len += SICSLOWPAN_IPV6_HDR_LEN; + memcpy(rime_ptr + rime_hdr_len, UIP_IP_BUF, UIP_IPH_LEN); + rime_hdr_len += UIP_IPH_LEN; + uncomp_hdr_len += UIP_IPH_LEN; + } else { + /* + * HC1 DISPATCH + * maximum compresssion: + * All fields in the IP header but Hop Limit are elided + * If next header is UDP, we compress UDP header using HC2 + */ + RIME_HC1_BUF->dispatch = SICSLOWPAN_DISPATCH_HC1; + uncomp_hdr_len += UIP_IPH_LEN; + switch(UIP_IP_BUF->proto) { + case UIP_PROTO_ICMP6: + /* HC1 encoding and ttl */ + RIME_HC1_BUF->encoding = 0xFC; + RIME_HC1_BUF->ttl = UIP_IP_BUF->ttl; + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + break; +#if UIP_CONF_TCP + case UIP_PROTO_TCP: + /* HC1 encoding and ttl */ + RIME_HC1_BUF->encoding = 0xFE; + RIME_HC1_BUF->ttl = UIP_IP_BUF->ttl; + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + break; +#endif /* UIP_CONF_TCP */ +#if UIP_CONF_UDP + case UIP_PROTO_UDP: + /* + * try to compress UDP header (we do only full compression). + * This is feasible if both src and dest ports are between + * SICSLOWPAN_UDP_PORT_MIN and SICSLOWPAN_UDP_PORT_MIN + 15 + */ + PRINTF("local/remote port %u/%u\n",UIP_UDP_BUF->srcport,UIP_UDP_BUF->destport); + if(HTONS(UIP_UDP_BUF->srcport) >= SICSLOWPAN_UDP_PORT_MIN && + HTONS(UIP_UDP_BUF->srcport) < SICSLOWPAN_UDP_PORT_MAX && + HTONS(UIP_UDP_BUF->destport) >= SICSLOWPAN_UDP_PORT_MIN && + HTONS(UIP_UDP_BUF->destport) < SICSLOWPAN_UDP_PORT_MAX) { + /* HC1 encoding */ + RIME_HC1_HC_UDP_BUF->hc1_encoding = 0xFB; + + /* HC_UDP encoding, ttl, src and dest ports, checksum */ + RIME_HC1_HC_UDP_BUF->hc_udp_encoding = 0xE0; + RIME_HC1_HC_UDP_BUF->ttl = UIP_IP_BUF->ttl; + RIME_HC1_HC_UDP_BUF->ports = (u8_t)((HTONS(UIP_UDP_BUF->srcport) - + SICSLOWPAN_UDP_PORT_MIN) << 4) + + (u8_t)((HTONS(UIP_UDP_BUF->destport) - SICSLOWPAN_UDP_PORT_MIN)); + RIME_HC1_HC_UDP_BUF->udpchksum = UIP_UDP_BUF->udpchksum; + rime_hdr_len += SICSLOWPAN_HC1_HC_UDP_HDR_LEN; + uncomp_hdr_len += UIP_UDPH_LEN; + } else { + /* HC1 encoding and ttl */ + RIME_HC1_BUF->encoding = 0xFA; + RIME_HC1_BUF->ttl = UIP_IP_BUF->ttl; + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + } + break; +#endif /*UIP_CONF_UDP*/ + } + } + return; +} + +/*--------------------------------------------------------------------*/ +/** + * \brief Uncompress HC1 (and HC_UDP) headers and put them in + * sicslowpan_buf + * + * This function is called by the input function when the dispatch is + * HC1. + * We %process the packet in the rime buffer, uncompress the header + * fields, and copy the result in the sicslowpan buffer. + * At the end of the decompression, rime_hdr_len and uncompressed_hdr_len + * are set to the appropriate values + * + * \param ip_len Equal to 0 if the packet is not a fragment (IP length + * is then inferred from the L2 length), non 0 if the packet is a 1st + * fragment. + */ +static void +uncompress_hdr_hc1(u16_t ip_len) { + /* version, traffic class, flow label */ + SICSLOWPAN_IP_BUF->vtc = 0x60; + SICSLOWPAN_IP_BUF->tcflow = 0; + SICSLOWPAN_IP_BUF->flow = 0; + + /* src and dest ip addresses */ + uip_ip6addr(&SICSLOWPAN_IP_BUF->srcipaddr, 0xfe80, 0, 0, 0, 0, 0, 0, 0); + uip_netif_addr_autoconf_set(&SICSLOWPAN_IP_BUF->srcipaddr, + (uip_lladdr_t *)rimebuf_addr(RIMEBUF_ADDR_SENDER)); + uip_ip6addr(&SICSLOWPAN_IP_BUF->destipaddr, 0xfe80, 0, 0, 0, 0, 0, 0, 0); + uip_netif_addr_autoconf_set(&SICSLOWPAN_IP_BUF->destipaddr, + (uip_lladdr_t *)rimebuf_addr(RIMEBUF_ADDR_RECEIVER)); + + uncomp_hdr_len += UIP_IPH_LEN; + + /* Next header field */ + switch(RIME_HC1_BUF->encoding & 0x06) { + case SICSLOWPAN_HC1_NH_ICMP6: + SICSLOWPAN_IP_BUF->proto = UIP_PROTO_ICMP6; + SICSLOWPAN_IP_BUF->ttl = RIME_HC1_BUF->ttl; + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + break; +#if UIP_CONF_TCP + case SICSLOWPAN_HC1_NH_TCP: + SICSLOWPAN_IP_BUF->proto = UIP_PROTO_TCP; + SICSLOWPAN_IP_BUF->ttl = RIME_HC1_BUF->ttl; + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + break; +#endif/* UIP_CONF_TCP */ +#if UIP_CONF_UDP + case SICSLOWPAN_HC1_NH_UDP: + SICSLOWPAN_IP_BUF->proto = UIP_PROTO_UDP; + if(RIME_HC1_HC_UDP_BUF->hc1_encoding & 0x01) { + /* UDP header is compressed with HC_UDP */ + if(RIME_HC1_HC_UDP_BUF->hc_udp_encoding != + SICSLOWPAN_HC_UDP_ALL_C) { + PRINTF("sicslowpan (uncompress_hdr), packet not supported"); + return; + } + /* IP TTL */ + SICSLOWPAN_IP_BUF->ttl = RIME_HC1_HC_UDP_BUF->ttl; + /* UDP ports, len, checksum */ + SICSLOWPAN_UDP_BUF->srcport = HTONS(SICSLOWPAN_UDP_PORT_MIN + + (RIME_HC1_HC_UDP_BUF->ports >> 4)); + SICSLOWPAN_UDP_BUF->destport = HTONS(SICSLOWPAN_UDP_PORT_MIN + + (RIME_HC1_HC_UDP_BUF->ports & 0x0F)); + SICSLOWPAN_UDP_BUF->udpchksum = RIME_HC1_HC_UDP_BUF->udpchksum; + uncomp_hdr_len += UIP_UDPH_LEN; + rime_hdr_len += SICSLOWPAN_HC1_HC_UDP_HDR_LEN; + } else { + rime_hdr_len += SICSLOWPAN_HC1_HDR_LEN; + } + break; +#endif/* UIP_CONF_UDP */ + default: + /* this shouldn't happen, drop */ + return; + } + + /* IP length field. */ + if(ip_len == 0) { + /* This is not a fragmented packet */ + SICSLOWPAN_IP_BUF->len[0] = 0; + SICSLOWPAN_IP_BUF->len[1] = rimebuf_datalen() - rime_hdr_len + uncomp_hdr_len - UIP_IPH_LEN; + } else { + /* This is a 1st fragment */ + SICSLOWPAN_IP_BUF->len[0] = (ip_len - UIP_IPH_LEN) >> 8; + SICSLOWPAN_IP_BUF->len[1] = (ip_len - UIP_IPH_LEN) & 0x00FF; + } + /* length field in UDP header */ + if(SICSLOWPAN_IP_BUF->proto == UIP_PROTO_UDP) { + memcpy(&SICSLOWPAN_UDP_BUF->udplen, &SICSLOWPAN_IP_BUF->len[0], 2); + } + return; +} +/** @} */ +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1*/ + + +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_IPV6 +/*--------------------------------------------------------------------*/ +/** \name IPv6 dispatch "compression" function + * @{ */ +/*--------------------------------------------------------------------*/ +/* \brief Packets "Compression" when only IPv6 dispatch is used + * + * There is no compression in this case, all fields are sent + * inline. We just add the IPv6 dispatch byte before the packet. + * \verbatim + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | IPv6 Dsp | IPv6 header and payload ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * \endverbatim + */ +static void +compress_hdr_ipv6(rimeaddr_t *rime_destaddr) { + *rime_ptr = SICSLOWPAN_DISPATCH_IPV6; + rime_hdr_len += SICSLOWPAN_IPV6_HDR_LEN; + memcpy(rime_ptr + rime_hdr_len, UIP_IP_BUF, UIP_IPH_LEN); + rime_hdr_len += UIP_IPH_LEN; + uncomp_hdr_len += UIP_IPH_LEN; + return; +} +/** @} */ +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_IPV6*/ + + + +/*--------------------------------------------------------------------*/ +/** \name Input/output functions common to all compression schemes + * @{ */ +/*--------------------------------------------------------------------*/ +/** + * \brief This function is called by the 6lowpan code to send out a + * packet. + * \param dest the link layer destination address of the packet + */ +static void +send_packet(rimeaddr_t *dest) +{ + + /* Set the link layer destination address for the packet as a + * rimebuf attribute. The MAC layer can access the destination + * address with the function "rimebuf_addr(RIMEBUF_ADDR_RECEIVER); + */ + rimebuf_set_addr(RIMEBUF_ADDR_RECEIVER, dest); + + if(mac != NULL) { + /** \todo: Fix sending delays so they aren't blocking, or even better would + * be to figure out how to get rid of delays entirely */ + + +#if DEBUG + #warning "*************************************************************" + #warning "Debug code enabled in sicslowpan.c, you MUST enable debug " + #warning "code in sicslowmac.c as well, and ALL nodes on network must " + #warning "have debug code in sicslowmac.c enabled. See documentation " + #warning "*************************************************************" +#endif + mac->send(); + } +} + +/** \brief Take an IP packet and format it to be sent on an 802.15.4 + * network using 6lowpan. + * \param localdest The MAC address of the destination + * + * The IP packet is initially in uip_buf. Its header is compressed + * and if necessary it is fragmented. The resulting + * packet/fragments are put in rimebuf and delivered to the 802.15.4 + * MAC. + */ +static u8_t +output(uip_lladdr_t *localdest) +{ + /* The MAC address of the destination of the packet */ + rimeaddr_t dest; + + + /* init */ + uncomp_hdr_len = 0; + rime_hdr_len = 0; + + /* reset rime buffer */ + rimebuf_clear(); + rime_ptr = rimebuf_dataptr(); + + /* + * The destination address will be tagged to each outbound + * packet. If the argument localdest is NULL, we are sending a + * broadcast packet. + */ + if(localdest == NULL) { + rimeaddr_copy(&dest, &rimeaddr_null); + } else { + rimeaddr_copy(&dest, (const rimeaddr_t *)localdest); + } + + PRINTF("sicslowpan output: sending packet len %d\n", uip_len); + + /* Try to compress the headers */ +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1 + compress_hdr_hc1(&dest); +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1*/ +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_IPV6 + compress_hdr_ipv6(&dest); +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_IPV6*/ +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01 + compress_hdr_hc01(&dest); +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01*/ + PRINTF("sicslowpan output: header of len %d\n", rime_hdr_len); + + if(uip_len - uncomp_hdr_len > MAC_MAX_PAYLOAD - rime_hdr_len) { +#if SICSLOWPAN_CONF_FRAG + /* + * The outbound IPv6 packet is too large to fit into a single 15.4 + * packet, so we fragment it into multiple packets and send them. + * The first fragment contains frag1 dispatch, then + * IPv6/HC1/HC01/HC_UDP dispatchs/headers. + * The following fragments contain only the fragn dispatch. + */ + + /* Create 1st Fragment */ + PRINTF("sicslowpan output: 1rst fragment "); + + /* move HC1/HC01/IPv6 header */ + memmove(rime_ptr + SICSLOWPAN_FRAG1_HDR_LEN, rime_ptr, rime_hdr_len); + + /* + * FRAG1 dispatch + header + * Note that the length is in units of 8 bytes + */ + RIME_FRAG_BUF->dispatch_size = + htons((SICSLOWPAN_DISPATCH_FRAG1 << 8) | uip_len); + RIME_FRAG_BUF->tag = htons(my_tag); + + /* Copy payload and send */ + rime_hdr_len += SICSLOWPAN_FRAG1_HDR_LEN; + rime_payload_len = (MAC_MAX_PAYLOAD - rime_hdr_len) & 0xf8; + PRINTF("(len %d, tag %d)\n", rime_payload_len, my_tag); + memcpy(rime_ptr + rime_hdr_len, + (void *)UIP_IP_BUF + uncomp_hdr_len, rime_payload_len); + rimebuf_set_datalen(rime_payload_len + rime_hdr_len); + send_packet(&dest); + + /* set processed_ip_len to what we already sent from the IP payload*/ + processed_ip_len = rime_payload_len + uncomp_hdr_len; + + /* + * Create following fragments + * Datagram tag is already in the buffer, we need to set the + * FRAGN dispatch and for each fragment, the offset + */ + rime_hdr_len = SICSLOWPAN_FRAGN_HDR_LEN; + RIME_FRAG_BUF->dispatch_size = + htons((SICSLOWPAN_DISPATCH_FRAGN << 8) | uip_len); + rime_payload_len = (MAC_MAX_PAYLOAD - rime_hdr_len) & 0xf8; + while(processed_ip_len < uip_len){ + PRINTF("sicslowpan output: fragment "); + RIME_FRAG_BUF->offset = processed_ip_len >> 3; + + /* Copy payload and send */ + if(uip_len - processed_ip_len < rime_payload_len){ + /* last fragment */ + rime_payload_len = uip_len - processed_ip_len; + } + PRINTF("(offset %d, len %d, tag %d)\n", + processed_ip_len >> 3, rime_payload_len, my_tag); + memcpy(rime_ptr + rime_hdr_len, + (void *)UIP_IP_BUF + processed_ip_len, rime_payload_len); + rimebuf_set_datalen(rime_payload_len + rime_hdr_len); + send_packet(&dest); + processed_ip_len += rime_payload_len; + } + + /* end: reset global variables */ + my_tag++; + processed_ip_len = 0; +#else /* SICSLOWPAN_CONF_FRAG */ + PRINTF("sicslowpan output: Packet too large to be sent without fragmentation support; dropping packet\n"); + return 0; +#endif /* SICSLOWPAN_CONF_FRAG */ + } else { + /* + * The packet does not need to be fragmented + * copy "payload" and send + */ + memcpy(rime_ptr + rime_hdr_len, (void *)UIP_IP_BUF + uncomp_hdr_len, + uip_len - uncomp_hdr_len); + rimebuf_set_datalen(uip_len - uncomp_hdr_len + rime_hdr_len); + send_packet(&dest); + } + return 1; +} + +/*--------------------------------------------------------------------*/ +/** \brief Process a received 6lowpan packet. + * \param r The MAC layer + * + * The 6lowpan packet is put in rimebuf by the MAC. If its a frag1 or + * a non-fragmented packet we first uncompress the IP header. The + * 6lowpan payload and possibly the uncompressed IP header are then + * copied in siclowpan_buf. If the IP packet is complete it is copied + * to uip_buf and the IP layer is called. + * + * \note We do not check for overlapping sicslowpan fragments + * (it is a SHALL in the RFC 4944 and should never happen) + */ +static void +input(const struct mac_driver *r) +{ + /* size of the IP packet (read from fragment) */ + u16_t frag_size = 0; + /* offset of the fragment in the IP packet */ + u8_t frag_offset = 0; +#if SICSLOWPAN_CONF_FRAG + /* tag of the fragment */ + u16_t frag_tag = 0; +#endif /*SICSLOWPAN_CONF_FRAG*/ + + /* init */ + uncomp_hdr_len = 0; + rime_hdr_len = 0; + + /* The MAC puts the 15.4 payload inside the RIME data buffer */ + rime_ptr = rimebuf_dataptr(); + +#if SICSLOWPAN_CONF_FRAG + /* if reassembly timed out, cancel it */ + if(timer_expired(&reass_timer)){ + sicslowpan_len = 0; + processed_ip_len = 0; + } + /* + * Since we don't support the mesh and broadcast header, the first header + * we look for is the fragmentation header + */ + switch((ntohs(RIME_FRAG_BUF->dispatch_size) & 0xf800) >> 8) { + case SICSLOWPAN_DISPATCH_FRAG1: + PRINTF("sicslowpan input: FRAG1 "); + frag_offset = 0; + frag_size = (ntohs(RIME_FRAG_BUF->dispatch_size) & 0x07ff); + frag_tag = ntohs(RIME_FRAG_BUF->tag); + PRINTF("size %d, tag %d, offset %d)\n", + frag_size, frag_tag, frag_offset); + rime_hdr_len += SICSLOWPAN_FRAG1_HDR_LEN; + + break; + case SICSLOWPAN_DISPATCH_FRAGN: + /* + * set offset, tag, size + * Offset is in units of 8 bytes + */ + PRINTF("sicslowpan input: FRAGN "); + frag_offset = RIME_FRAG_BUF->offset; + frag_tag = ntohs(RIME_FRAG_BUF->tag); + frag_size = (ntohs(RIME_FRAG_BUF->dispatch_size) & 0x07ff); + PRINTF("size %d, tag %d, offset %d)\n", + frag_size, frag_tag, frag_offset); + rime_hdr_len += SICSLOWPAN_FRAGN_HDR_LEN; + break; + default: + break; + } + + if(processed_ip_len > 0) { + /* reassembly is ongoing */ + if((frag_size > 0 && + (frag_size != sicslowpan_len || + reass_tag != frag_tag || + !rimeaddr_cmp(&frag_sender, rimebuf_addr(RIMEBUF_ADDR_SENDER)))) || + frag_size == 0) { + /* + * the packet is a fragment that does not belong to the packet + * being reassembled or the packet is not a fragment. + */ + PRINTF("sicslowpan input: Dropping 6lowpan packet\n"); + return; + } + } else { + /* + * reassembly is off + * start it if we received a fragment + */ + if(frag_size > 0){ + sicslowpan_len = frag_size; + reass_tag = frag_tag; + timer_set(&reass_timer, SICSLOWPAN_REASS_MAXAGE*CLOCK_SECOND); + PRINTF("sicslowpan input: INIT FRAGMENTATION (len %d, tag %d)\n", + sicslowpan_len, reass_tag); + rimeaddr_copy(&frag_sender, rimebuf_addr(RIMEBUF_ADDR_SENDER)); + } + } + + if(rime_hdr_len == SICSLOWPAN_FRAGN_HDR_LEN) { + /* this is a FRAGN, skip the header compression dispatch section */ + goto copypayload; + } +#endif /* SICSLOWPAN_CONF_FRAG */ + + /* Process next dispatch and headers */ + switch(RIME_HC1_BUF->dispatch) { +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1 + case SICSLOWPAN_DISPATCH_HC1: + PRINTF("sicslowpan input: HC1\n"); + uncompress_hdr_hc1(frag_size); + break; +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC1*/ +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01 + case SICSLOWPAN_DISPATCH_IPHC: + PRINTF("sicslowpan input: IPHC\n"); + uncompress_hdr_hc01(frag_size); + break; +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01*/ + case SICSLOWPAN_DISPATCH_IPV6: + PRINTF("sicslowpan input: IPV6\n"); + rime_hdr_len += SICSLOWPAN_IPV6_HDR_LEN; + + /* Put uncompressed IP header in sicslowpan_buf. */ + memcpy(SICSLOWPAN_IP_BUF, rime_ptr + rime_hdr_len, UIP_IPH_LEN); + + /* Update uncomp_hdr_len and rime_hdr_len. */ + rime_hdr_len += UIP_IPH_LEN; + uncomp_hdr_len += UIP_IPH_LEN; + break; + default: + /* unknown header */ + PRINTF("sicslowpan input: unknown dispatch\n"); + return; + } + + +#if SICSLOWPAN_CONF_FRAG + copypayload: +#endif /*SICSLOWPAN_CONF_FRAG*/ + /* + * copy "payload" from the rime buffer to the sicslowpan_buf + * if this is a first fragment or not fragmented packet, + * we have already copied the compressed headers, uncomp_hdr_len + * and rime_hdr_len are non 0, frag_offset is. + * If this is a subsequent fragment, this is the contrary. + */ + rime_payload_len = rimebuf_datalen() - rime_hdr_len; + memcpy((void *)SICSLOWPAN_IP_BUF + uncomp_hdr_len + (u16_t)(frag_offset << 3), rime_ptr + rime_hdr_len, rime_payload_len); + + /* update processed_ip_len if fragment, sicslowpan_len otherwise */ + +#if SICSLOWPAN_CONF_FRAG + if(frag_size > 0){ + if(processed_ip_len == 0) { + processed_ip_len += uncomp_hdr_len; + } + processed_ip_len += rime_payload_len; + } else { +#endif /* SICSLOWPAN_CONF_FRAG */ + sicslowpan_len = rime_payload_len + uncomp_hdr_len; +#if SICSLOWPAN_CONF_FRAG + } + + /* + * If we have a full IP packet in sicslowpan_buf, deliver it to + * the IP stack + */ + if(processed_ip_len == 0 || (processed_ip_len == sicslowpan_len)){ + PRINTF("sicslowpan input: IP packet ready (length %d)\n", + sicslowpan_len); + memcpy((void *)UIP_IP_BUF, (void *)SICSLOWPAN_IP_BUF, sicslowpan_len); + uip_len = sicslowpan_len; + sicslowpan_len = 0; + processed_ip_len = 0; +#endif /* SICSLOWPAN_CONF_FRAG */ + tcpip_input(); +#if SICSLOWPAN_CONF_FRAG + } +#endif /* SICSLOWPAN_CONF_FRAG */ + return; +} +/** @} */ + +/*--------------------------------------------------------------------*/ +/* \brief 6lowpan init function (called by the MAC layer) */ +/*--------------------------------------------------------------------*/ +void +sicslowpan_init(const struct mac_driver *m) +{ + /* remember the mac driver */ + mac = m; + + /* Set our input function as the receive function of the MAC. */ + mac->set_receive_function(input); + + /* + * Set out output function as the function to be called from uIP to + * send a packet. + */ + tcpip_set_outputfunc(output); + +#if SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01 + /* + * Initialize the address contexts + * Context 00 is link local context + * Other contexts are NULL at init + */ + addr_contexts[0].used = 1; + addr_contexts[0].number = SICSLOWPAN_IPHC_ADDR_CONTEXT_LL; + addr_contexts[0].prefix[0] = 0xfe; + addr_contexts[0].prefix[1] = 0x80; + + addr_contexts[1].used = 1; + addr_contexts[1].number = 1; + addr_contexts[1].prefix[0] = 0xaa; + addr_contexts[1].prefix[1] = 0xaa; + + for(i = 2; i < SICSLOWPAN_CONF_MAX_ADDR_CONTEXTS; i++) { + addr_contexts[i].used = 0; + } +#endif /*SICSLOWPAN_CONF_COMPRESSION == SICSLOWPAN_CONF_COMPRESSION_HC01*/ +} +/*--------------------------------------------------------------------*/ +/** @} */ diff --git a/core/net/sicslowpan.h b/core/net/sicslowpan.h new file mode 100644 index 000000000..5af176b25 --- /dev/null +++ b/core/net/sicslowpan.h @@ -0,0 +1,259 @@ +/** + * \addtogroup sicslowpan + * @{ + */ + +/* + * Copyright (c) 2008, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: sicslowpan.h,v 1.1 2008/10/14 09:42:33 julienabeille Exp $ + */ +/** + * \file + * Header file for the 6lowpan implementation + * (RFC4944 and draft-hui-6lowpan-hc-01) + * \author Adam Dunkels + * \author Nicolas Tsiftes + * \author Niclas Finne + * \author Mathilde Durvy + * \author Julien Abeille + */ + +#ifndef __SICSLOWPAN_H__ +#define __SICSLOWPAN_H__ +#include "net/uip.h" +#include "sicslowmac.h" + +/** + * \name General sicslowpan defines + * @{ + */ +/* Min and Max compressable UDP ports */ +#define SICSLOWPAN_UDP_PORT_MIN 0xF0B0 +#define SICSLOWPAN_UDP_PORT_MAX 0xF0BF /* F0B0 + 15 */ +/** @} */ + + +/** + * \name 6lowpan dispatchs + * @{ + */ +#define SICSLOWPAN_DISPATCH_IPV6 0x41 /* 01000001 = 65 */ +#define SICSLOWPAN_DISPATCH_HC1 0x42 /* 01000010 = 66 */ +#define SICSLOWPAN_DISPATCH_IPHC 0x03 /* 00000011 = 3 */ +#define SICSLOWPAN_DISPATCH_FRAG1 0xc0 /* 11000xxx */ +#define SICSLOWPAN_DISPATCH_FRAGN 0xe0 /* 11100xxx */ +/** @} */ + +/** \name HC1 encoding + * @{ + */ +#define SICSLOWPAN_HC1_NH_UDP 0x02 +#define SICSLOWPAN_HC1_NH_TCP 0x06 +#define SICSLOWPAN_HC1_NH_ICMP6 0x04 +/** @} */ + +/** \name HC_UDP encoding (works together with HC1) + * @{ + */ +#define SICSLOWPAN_HC_UDP_ALL_C 0xE0 +/** @} */ + +/** + * \name IPHC encoding + * @{ + */ +/* + * Values of fields within the IPHC encoding first byte + * (C stands for compressed and I for inline) + */ +#define SICSLOWPAN_IPHC_TC_C 0x80 +#define SICSLOWPAN_IPHC_VF_C 0x40 +#define SICSLOWPAN_IPHC_NH_C 0x20 +#define SICSLOWPAN_IPHC_TTL_1 0x08 +#define SICSLOWPAN_IPHC_TTL_64 0x10 +#define SICSLOWPAN_IPHC_TTL_255 0x18 +#define SICSLOWPAN_IPHC_TTL_I 0x00 + +/* Values of fields within the IPHC encoding second byte */ +#define SICSLOWPAN_IPHC_SAM_I 0x00 +#define SICSLOWPAN_IPHC_SAM_64 0x40 +#define SICSLOWPAN_IPHC_SAM_16 0x80 +#define SICSLOWPAN_IPHC_SAM_0 0xC0 +#define SICSLOWPAN_IPHC_DAM_I 0x00 +#define SICSLOWPAN_IPHC_DAM_64 0x04 +#define SICSLOWPAN_IPHC_DAM_16 0x08 +#define SICSLOWPAN_IPHC_DAM_0 0x0C + +/* Link local context number */ +#define SICSLOWPAN_IPHC_ADDR_CONTEXT_LL 0 +/* 16-bit multicast addresses compression */ +#define SICSLOWPAN_IPHC_MCAST_RANGE 0xA0 +/** @} */ + + +/** + * \name LOWPAN_UDP encoding (works together with IPHC) + * @{ + */ +#define SICSLOWPAN_NHC_UDP_ID 0xF8 +#define SICSLOWPAN_NHC_UDP_C 0xFB +#define SICSLOWPAN_NHC_UDP_I 0xF8 +/** @} */ + + +/** + * \name The 6lowpan "headers" length + * @{ + */ + +#define SICSLOWPAN_IPV6_HDR_LEN 1 /*one byte*/ +#define SICSLOWPAN_HC1_HDR_LEN 3 +#define SICSLOWPAN_HC1_HC_UDP_HDR_LEN 7 +#define SICSLOWPAN_FRAG1_HDR_LEN 4 +#define SICSLOWPAN_FRAGN_HDR_LEN 5 +/** @} */ + +/** + * \brief The header for fragments + * \note We do not define different structuresfor FRAG1 + * and FRAGN headers, which are different. For FRAG1, the + * offset field is just not used + */ +struct sicslowpan_frag_hdr { + u16_t dispatch_size; + u16_t tag; + u8_t offset; +}; + +/** + * \brief The HC1 header when HC_UDP is not used + * + * When all fields are compressed and HC_UDP is not used, + * we use this structure. If HC_UDP is used, the ttl is + * in another spot, and we use the sicslowpan_hc1_hc_udp + * structure + */ +struct sicslowpan_hc1_hdr { + u8_t dispatch; + u8_t encoding; + u8_t ttl; +}; + +/** + * \brief HC1 followed by HC_UDP + */ +struct sicslowpan_hc1_hc_udp_hdr { + u8_t dispatch; + u8_t hc1_encoding; + u8_t hc_udp_encoding; + u8_t ttl; + u8_t ports; + u16_t udpchksum; +}; + +/** + * \brief IPHC dispatch and encoding + * the rest (uncompressed fields) is variable + */ +struct sicslowpan_iphc_hdr { + u8_t dispatch; + u8_t encoding[2]; +}; + +struct sicslowpan_nhc_udp_comp_hdr { + u8_t nhcid; + u8_t ports; + u16_t udpchksum; +}; + +/** + * \brief An address context for IPHC address compression + */ +struct sicslowpan_addr_context { + u8_t used; + u8_t number; + u8_t prefix[8]; +}; + +/** + * \name Address compressibility test functions + * @{ + */ + +/** + * \brief check whether we can compress the IID in + * address 'a' to 16 bits. + * This is used for unicast addresses only, and is true + * if first 49 bits of IID are 0 + */ +#define sicslowpan_is_iid_16_bit_compressable(a) \ + ((((a)->u16[4]) == 0) && \ + (((a)->u16[5]) == 0) && \ + (((a)->u16[6]) == 0) && \ + ((((a)->u8[14]) & 0x80) == 0)) + +/** + * \brief check whether the 9-bit group-id of the + * compressed multicast address is known. It is true + * if the 9-bit group is the all nodes or all routers + * group. + * \param a is typed u8_t * + */ +#define sicslowpan_is_mcast_addr_decompressable(a) \ + (((*a & 0x01) == 0) && \ + ((*(a + 1) == 0x01) || (*(a + 1) == 0x02))) + +/** + * \brief check whether the 112-bit group-id of the + * multicast address is mapable to a 9-bit group-id + * It is true if the group is the all nodes or all + * routers group. +*/ +#define sicslowpan_is_mcast_addr_compressable(a) \ + ((((a)->u16[1]) == 0) && \ + (((a)->u16[2]) == 0) && \ + (((a)->u16[3]) == 0) && \ + (((a)->u16[4]) == 0) && \ + (((a)->u16[5]) == 0) && \ + (((a)->u16[6]) == 0) && \ + (((a)->u8[14]) == 0) && \ + ((((a)->u8[15]) == 1) || (((a)->u8[15]) == 2))) + +/** @} */ + +/** + * \brief 6lowpan init function + * \param m is the MAC layer "driver" + */ +void sicslowpan_init(const struct mac_driver *m); + +#endif /* __SICSLOWPAN_H__ */ +/** @} */ diff --git a/core/net/uip-icmp6.c b/core/net/uip-icmp6.c new file mode 100644 index 000000000..d9cb95582 --- /dev/null +++ b/core/net/uip-icmp6.c @@ -0,0 +1,185 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * ICMPv6 echo request and error messages (RFC 4443) + * \author Julien Abeille + * \author Mathilde Durvy + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + */ + +#include +#include "net/uip-netif.h" +#include "net/uip-icmp6.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#endif + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)&uip_buf[uip_l2_l3_icmp_hdr_len]) + +/** \brief temporary IP address */ +static uip_ipaddr_t tmp_ipaddr; + +void +uip_icmp6_echo_request_input(void) +{ + /* + * we send an echo reply. It is trivial if there was no extension + * headers in the request otherwise we need to remove the extension + * headers and change a few fields + */ + PRINTF("Received Echo Request from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("\n"); + + /* IP header */ + UIP_IP_BUF->ttl = uip_netif_physical_if.cur_hop_limit; + + if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)){ + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + } else { + uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->srcipaddr); + uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &tmp_ipaddr); + } + + if(uip_ext_len > 0) { + /* If there were extension headers*/ + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + uip_len -= uip_ext_len; + UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); + /* move the echo request payload (starting after the icmp header) + * to the new location in the reply. + * The shift is equal to the length of the extension headers present + * Note: UIP_ICMP_BUF still points to the echo request at this stage + */ + memmove((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN - uip_ext_len, + (void *)UIP_ICMP_BUF + UIP_ICMPH_LEN, + (uip_len - UIP_IPH_LEN - UIP_ICMPH_LEN)); + } + /* Below is important for the correctness of UIP_ICMP_BUF and the + * checksum + */ + uip_ext_len = 0; + /* Note: now UIP_ICMP_BUF points to the beginning of the echo reply */ + UIP_ICMP_BUF->type = ICMP6_ECHO_REPLY; + UIP_ICMP_BUF->icode = 0; + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + PRINTF("Sending Echo Reply to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + UIP_STAT(++uip_stat.icmp.sent); + return; +} + +void +uip_icmp6_error_output(u8_t type, u8_t code, u32_t param) { + uip_ext_len = 0; + /* remember data of original packet before shifting */ + uip_ipaddr_copy(&tmp_ipaddr, &UIP_IP_BUF->destipaddr); + + uip_len += UIP_IPICMPH_LEN + UIP_ICMP6_ERROR_LEN; + + if(uip_len > UIP_LINK_MTU) + uip_len = UIP_LINK_MTU; + + memmove((void *)UIP_ICMP6_ERROR_BUF + UIP_ICMP6_ERROR_LEN, + (void *)UIP_IP_BUF, uip_len - UIP_IPICMPH_LEN - UIP_ICMP6_ERROR_LEN); + + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = uip_netif_physical_if.cur_hop_limit; + + /* the source should not be unspecified nor multicast, the check for + multicast is done in uip_process */ + if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){ + uip_len = 0; + return; + } + + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); + + if(uip_is_addr_mcast(&tmp_ipaddr)){ + if(type == ICMP6_PARAM_PROB && code == ICMP6_PARAMPROB_OPTION){ + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr); + } else { + uip_len = 0; + return; + } + } else { + uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &tmp_ipaddr); + } + + UIP_ICMP_BUF->type = type; + UIP_ICMP_BUF->icode = code; + UIP_ICMP6_ERROR_BUF->param = htonl(param); + UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + UIP_STAT(++uip_stat.icmp.sent); + + PRINTF("Sending ICMPv6 ERROR message to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + return; +} + +/** @} */ diff --git a/core/net/uip-icmp6.h b/core/net/uip-icmp6.h new file mode 100644 index 000000000..d3ee3b133 --- /dev/null +++ b/core/net/uip-icmp6.h @@ -0,0 +1,127 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * ICMPv6 echo request and error messages (RFC 4443) + * \author Julien Abeille + * \author Mathilde Durvy + */ + +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + + +#ifndef __ICMP6_H__ +#define __ICMP6_H__ + +#include "net/uip.h" + + +/** \name ICMPv6 message types */ +/** @{ */ +#define ICMP6_DST_UNREACH 1 /**< dest unreachable */ +#define ICMP6_PACKET_TOO_BIG 2 /**< packet too big */ +#define ICMP6_TIME_EXCEEDED 3 /**< time exceeded */ +#define ICMP6_PARAM_PROB 4 /**< ip6 header bad */ +#define ICMP6_ECHO_REQUEST 128 /**< Echo request */ +#define ICMP6_ECHO_REPLY 129 /**< Echo reply */ + +#define ICMP6_RS 133 /**< Router Solicitation */ +#define ICMP6_RA 134 /**< Router Advertisement */ +#define ICMP6_NS 135 /**< Neighbor Solicitation */ +#define ICMP6_NA 136 /**< Neighbor advertisement */ +#define ICMP6_REDIRECT 137 /**< Redirect */ +/** @} */ + + +/** \name ICMPv6 Destination Unreachable message codes*/ +/** @{ */ +#define ICMP6_DST_UNREACH_NOROUTE 0 /**< no route to destination */ +#define ICMP6_DST_UNREACH_ADMIN 1 /**< administratively prohibited */ +#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /**< not a neighbor(obsolete) */ +#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /**< beyond scope of source address */ +#define ICMP6_DST_UNREACH_ADDR 3 /**< address unreachable */ +#define ICMP6_DST_UNREACH_NOPORT 4 /**< port unreachable */ +/** @} */ + +/** \name ICMPv6 Time Exceeded message codes*/ +/** @{ */ +#define ICMP6_TIME_EXCEED_TRANSIT 0 /**< ttl==0 in transit */ +#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /**< ttl==0 in reass */ +/** @} */ + +/** \name ICMPv6 Parameter Problem message codes*/ +/** @{ */ +#define ICMP6_PARAMPROB_HEADER 0 /**< erroneous header field */ +#define ICMP6_PARAMPROB_NEXTHEADER 1 /**< unrecognized next header */ +#define ICMP6_PARAMPROB_OPTION 2 /**< unrecognized option */ +/** @} */ + +/** \brief Echo Request constant part length */ +#define UIP_ICMP6_ECHO_REQUEST_LEN 4 + +/** \brief ICMPv6 Error message constant part length */ +#define UIP_ICMP6_ERROR_LEN 4 + +/** \brief ICMPv6 Error message constant part */ +struct uip_icmp6_error{ + u32_t param; +}; + +/** \name ICMPv6 RFC4443 Message processing and sending */ +/** @{ */ +/** \ + * brief Process an echo request + * + * Perform a few checks, then send an Echop reply. The reply is + * built here. + */ +void +uip_icmp6_echo_request_input(void); + +/** + * \brief Send an icmpv6 error message + * \param type type of the Error message + * \param code of the error message + * \param type 32 bit parameter of the error message, semantic depends on error + */ +void +uip_icmp6_error_output(u8_t type, u8_t code, u32_t param); + +/** @} */ + +#endif /*__ICMP6_H__*/ +/** @} */ + diff --git a/core/net/uip-nd6-io.c b/core/net/uip-nd6-io.c new file mode 100644 index 000000000..3c1667858 --- /dev/null +++ b/core/net/uip-nd6-io.c @@ -0,0 +1,919 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * Neighbor discovery Input-Output (RFC 4861) + * \author Julien Abeille + * \author Mathilde Durvy + */ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#include "net/uip-icmp6.h" +#include "net/uip-nd6.h" +#include "net/uip-netif.h" + + + +/*------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(lladdr) +#endif + +#if UIP_LOGGING +#include +void uip_log(char *msg); +#define UIP_LOG(m) uip_log(m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + + +/*------------------------------------------------------------------*/ +/** @{ */ +/** \name Pointers to the header structures. + * All pointers except UIP_IP_BUF depend on uip_ext_len, which at + * packet reception, is the total length of the extension headers. + * + * The pointer to ND6 options header also depends on nd6_opt_offset, + * which we set in each function. + * + * Care should be taken when manipulating these buffers about the + * value of these length variables + */ + +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +/**< Pointer to IP header */ +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +/**< Pointer to ICMP header*/ +/**@{ Pointers to messages just after icmp header */ +#define UIP_ND6_RS_BUF ((struct uip_nd6_rs *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +#define UIP_ND6_RA_BUF ((struct uip_nd6_ra *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +#define UIP_ND6_NS_BUF ((struct uip_nd6_ns *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +#define UIP_ND6_NA_BUF ((struct uip_nd6_na *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +#define UIP_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +/** @} */ +/** Pointer to ND option */ +#define UIP_ND6_OPT_HDR_BUF ((struct uip_nd6_opt_hdr *)&uip_buf[uip_l2_l3_icmp_hdr_len + nd6_opt_offset]) +/** @} */ + +/** \brief Offset from the end of the icmpv6 header to the option in uip_buf*/ +static u8_t nd6_opt_offset; +/** \brief Pointer to llao option in uip_buf */ +static struct uip_nd6_opt_llao *nd6_opt_llao; +/** \brief Pointer to mtu option in uip_buf */ +static struct uip_nd6_opt_mtu *nd6_opt_mtu; +/** \brief Pointer to prefix information option in uip_buf */ +static struct uip_nd6_opt_prefix_info *nd6_opt_prefix_info[UIP_CONF_ND6_MAX_PREFIXES]; +/** \brief Pointer to a neighbor cache entry*/ +static struct uip_nd6_neighbor *neighbor; +/** \brief Pointer to a prefix list entry */ +static struct uip_nd6_prefix *prefix; +/** \brief Pointer to a router list entry */ +static struct uip_nd6_defrouter *router; +/** \brief Pointer to an interface address */ +static struct uip_netif_addr *ifaddr; +/** \brief Index used in loops */ +static u8_t i; + +/*------------------------------------------------------------------*/ +void +uip_nd6_io_ns_input(void) +{ + PRINTF("Received NS from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("with target address"); + PRINT6ADDR((uip_ipaddr_t *)(&UIP_ND6_NS_BUF->tgtipaddr)); + PRINTF("\n"); + UIP_STAT(++uip_stat.nd6.recv); + + u8_t flags; + +#if UIP_CONF_IPV6_CHECKS + if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) || + (uip_is_addr_mcast(&UIP_ND6_NS_BUF->tgtipaddr)) || + (UIP_ICMP_BUF->icode != 0)) + { + goto badpkt; + } +#endif /* UIP_CONF_IPV6_CHECKS */ + + /* Options reading: we handle only SLLAO for now */ + nd6_opt_llao = NULL; + nd6_opt_offset = UIP_ND6_NS_LEN; + while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) { +#if UIP_CONF_IPV6_CHECKS + if(UIP_ND6_OPT_HDR_BUF->len == 0) { + goto badpkt; + } +#endif /* UIP_CONF_IPV6_CHECKS */ + switch(UIP_ND6_OPT_HDR_BUF->type) { + case UIP_ND6_OPT_SLLAO: + nd6_opt_llao = (struct uip_nd6_opt_llao *)UIP_ND6_OPT_HDR_BUF; + break; + default: + UIP_LOG("ND option not supported in NS"); + break; + } + nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3); + } + + /* Options processing: only SLLAO */ + if(nd6_opt_llao != NULL) { +#if UIP_CONF_IPV6_CHECKS + /* There must be NO option in a DAD NS */ + if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) { + goto badpkt; + } else { +#endif /*UIP_CONF_IPV6_CHECKS*/ + neighbor = uip_nd6_nbrcache_lookup(&UIP_IP_BUF->srcipaddr); + if(neighbor == NULL) { + /* we need to add the neighbor*/ + uip_nd6_nbrcache_add(&UIP_IP_BUF->srcipaddr, + &nd6_opt_llao->addr, 0, STALE); + } else { + /* If LL address changed, set neighbor state to stale */ + if(memcmp(&nd6_opt_llao->addr, &neighbor->lladdr, UIP_LLADDR_LEN) != 0) { + memcpy(&neighbor->lladdr, &nd6_opt_llao->addr, UIP_LLADDR_LEN); + neighbor->state = STALE; + } else { + /* If neighbor state is INCOMPLETE, set to STALE */ + if(neighbor->state == INCOMPLETE) { + neighbor->state = STALE; + } + } + } +#if UIP_CONF_IPV6_CHECKS + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + } + + /* + * Rest of NS processing: Depends on the purpose of the NS: NUD or DAD or + * Address Resolution + */ + /** \note we use ifaddr to remember the target address */ + ifaddr = uip_netif_addr_lookup(&UIP_ND6_NS_BUF->tgtipaddr, 128, 0); + if(ifaddr != NULL) { + if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)){ + /* DAD CASE */ +#if UIP_CONF_IPV6_CHECKS + /* Dst address must be solicited node mcast address */ + if(!uip_netif_is_addr_my_solicited(&UIP_IP_BUF->destipaddr)){ + goto badpkt; + } +#endif /* UIP_CONF_IPV6_CHECKS */ + /* + * If my address is not tentative, then send a NA to all nodes with + * TLLAO flags are: override = yes. + */ + if(ifaddr->state!=TENTATIVE) { + /* + * we need to send a NA, we set the src, dest, flags. tgt remains the + * same and the rest is done at "create_na" + */ + uip_create_linklocal_allnodes_mcast(&UIP_IP_BUF->destipaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + flags = UIP_ND6_NA_FLAG_OVERRIDE; + goto create_na; + } else { + /** \todo if I sent a NS before him, I win */ + uip_netif_dad_failed(&UIP_ND6_NS_BUF->tgtipaddr); + goto discard; + } + } + + +#if UIP_CONF_IPV6_CHECKS + /* Duplicate check */ + if(uip_netif_is_addr_my_unicast(&UIP_IP_BUF->srcipaddr)) { + /** + * \NOTE do we do something here? we both are using the same address. + * If we are doing dad, we could cancel it, though we should receive a + * NA in response of DAD NS we sent, hence DAD will fail anyway. If we + * were not doing DAD, it means there is a duplicate in the network! + */ + goto badpkt; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + + /* Address resolution case */ + if(uip_netif_is_addr_my_solicited(&UIP_IP_BUF->destipaddr)){ + /* + * we need to send a NA, we set the src, dest, flags. The rest is + * set at the "create_na" label. + */ + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); + uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr); + flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE; + goto create_na; + } + + /* + * NUD CASE. at this point the packet must be for us! we check this, + * and at the same time if target == dest + */ + if(uip_netif_addr_lookup(&UIP_IP_BUF->destipaddr, 128, 0) == ifaddr){ + /* + * we need to send a NA, we set the src, dest, flags. The rest is set + * at the "create_na" label. + */ + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); + uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, &UIP_ND6_NS_BUF->tgtipaddr); + flags = UIP_ND6_NA_FLAG_SOLICITED | UIP_ND6_NA_FLAG_OVERRIDE; + goto create_na; + } else { +#if UIP_CONF_IPV6_CHECKS + goto badpkt; +#endif /* UIP_CONF_IPV6_CHECKS */ + } + } else { + goto discard; + } + + + create_na: + /* + * Fill the part of the NA which is common to all NAs. If the NS contained + * extension headers, we must set the target address properly + */ + uip_ext_len = 0; + + /* IP header */ + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->len[0] = 0; /* length will not be more than 255 */ + UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT; + + /* ICMP header */ + UIP_ICMP_BUF->type = ICMP6_NA; + UIP_ICMP_BUF->icode = 0; + + /* NA static part */ + UIP_ND6_NA_BUF->flagsreserved = flags; + memcpy(&UIP_ND6_NA_BUF->tgtipaddr, &ifaddr->ipaddr, sizeof(uip_ipaddr_t)); + + /* NA option: TLLAO. note that length field is in unit of 8 bytes */ + uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NA_LEN + UIP_ND6_OPT_LLAO_LEN; + nd6_opt_llao = (struct uip_nd6_opt_llao *)&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NA_LEN]; + nd6_opt_llao->type = UIP_ND6_OPT_TLLAO; + nd6_opt_llao->len = UIP_ND6_OPT_LLAO_LEN >> 3; + memcpy(&(nd6_opt_llao->addr), &uip_lladdr, UIP_LLADDR_LEN); + /* padding if needed */ + memset((void *)(&nd6_opt_llao->addr) + UIP_LLADDR_LEN, 0, UIP_ND6_OPT_LLAO_LEN - 2 - UIP_LLADDR_LEN); + + /*ICMP checksum*/ + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + + UIP_STAT(++uip_stat.nd6.sent); + PRINTF("Sending NA to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("with target address"); + PRINT6ADDR(&UIP_ND6_NA_BUF->tgtipaddr); + PRINTF("\n"); + return; + +#if UIP_CONF_IPV6_CHECKS + badpkt: + UIP_STAT(++uip_stat.nd6.drop); + UIP_LOG("NS received is bad"); +#endif /* UIP_CONF_IPV6_CHECKS */ + + discard: + uip_len = 0; + return; +} + + + +/*------------------------------------------------------------------*/ +void +uip_nd6_io_ns_output(uip_ipaddr_t *src, uip_ipaddr_t *dest, uip_ipaddr_t *tgt) +{ + /* IP header fields */ + uip_ext_len = 0; + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT; + + /* + * The caller must put a valid tgt address. + * For dest, if the caller is doing DAD or Address resolution, he sets + * dest to NULL and we forge dest as the sollicited node mcast address + * for tgt. + * If the caller is sending the NS for NUD, dest is unicast and the caller + * specifies it in the arguments + */ + if(dest == NULL) { + uip_create_solicited_node(tgt, &UIP_IP_BUF->destipaddr); + } else { + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, dest); + } + UIP_ICMP_BUF->type = ICMP6_NS; + UIP_ICMP_BUF->icode = 0; + UIP_ND6_NS_BUF->reserved = 0; + + /* Copy the tgt address */ + uip_ipaddr_copy((uip_ipaddr_t *)&UIP_ND6_NS_BUF->tgtipaddr, tgt); + + UIP_IP_BUF->len[0] = 0; /* length will not be more than 255 */ + /* + * check if we add a SLLAO option: for DAD, MUST NOT, for NUD, MAY + * (here yes), for Address resolution , MUST + * i.e. if and only if tgt is our address (in this case it is DAD), we do + * not add it + */ + if(!(uip_netif_is_addr_my_unicast(tgt))) { + if(src != NULL) { + uip_ipaddr_copy(&UIP_IP_BUF->srcipaddr, src); + } else { + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + } + UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_NS_LEN + UIP_ND6_OPT_LLAO_LEN; + + uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NS_LEN + UIP_ND6_OPT_LLAO_LEN; + nd6_opt_llao = (struct uip_nd6_opt_llao *)&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_NS_LEN]; + nd6_opt_llao->type = UIP_ND6_OPT_SLLAO; /* type of the option */ + /* + * length of the option: 2 bytes for type, length, plus the length of + * the L2 address. It must be in units of 8 bytes + */ + nd6_opt_llao->len = UIP_ND6_OPT_LLAO_LEN >> 3; + memcpy(&nd6_opt_llao->addr, &uip_lladdr, UIP_LLADDR_LEN); + /* padding if needed */ + memset((void *)(&nd6_opt_llao->addr) + UIP_LLADDR_LEN, 0, UIP_ND6_OPT_LLAO_LEN - 2 - UIP_LLADDR_LEN); + } + else { + uip_create_unspecified(&UIP_IP_BUF->srcipaddr); + UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_NS_LEN; + uip_len = UIP_IPH_LEN + UIP_ICMPH_LEN + UIP_ND6_NS_LEN; + } + + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + UIP_STAT(++uip_stat.nd6.sent); + PRINTF("Sending NS to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("with target address"); + PRINT6ADDR(tgt); + PRINTF("\n"); + return; +} + + + +/*------------------------------------------------------------------*/ +void +uip_nd6_io_na_input(void) +{ + + PRINTF("Received NA from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("with target address"); + PRINT6ADDR((uip_ipaddr_t *)(&UIP_ND6_NA_BUF->tgtipaddr)); + PRINTF("\n"); + UIP_STAT(++uip_stat.nd6.recv); + + /* + * booleans. the three last one are not 0 or 1 but 0 or 0x80, 0x40, 0x20 + * but it works. Be careful though, do not use tests such as is_router == 1 + */ + u8_t is_llchange = 0; + u8_t is_router = ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_ROUTER)); + u8_t is_solicited = ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_SOLICITED)); + u8_t is_override = ((UIP_ND6_NA_BUF->flagsreserved & UIP_ND6_NA_FLAG_OVERRIDE)); + + +#if UIP_CONF_IPV6_CHECKS + /* + * Check hop limit / icmp code + * target address must not be multicast + * if the NA is solicited, dest must not be multicast + */ + if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) || + (UIP_ICMP_BUF->icode != 0) || + (uip_is_addr_mcast(&UIP_ND6_NA_BUF->tgtipaddr)) || + (is_solicited && uip_is_addr_mcast(&UIP_IP_BUF->destipaddr))) { + goto badpkt; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + + /* Options reading: we can handle TLLAO, and must ignore othehrs */ + nd6_opt_offset = UIP_ND6_NA_LEN; + nd6_opt_llao = NULL; + while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) { +#if UIP_CONF_IPV6_CHECKS + if(UIP_ND6_OPT_HDR_BUF->len == 0) { + goto badpkt; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + switch(UIP_ND6_OPT_HDR_BUF->type) { + case UIP_ND6_OPT_TLLAO: + nd6_opt_llao = (struct uip_nd6_opt_llao *)UIP_ND6_OPT_HDR_BUF; + break; + default: + UIP_LOG("ND option not supported in NA"); + break; + } + nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3); + } + ifaddr = uip_netif_addr_lookup(&UIP_ND6_NA_BUF->tgtipaddr, 128, 0); + /* Message processing, including TLLAO if any */ + if(ifaddr != NULL) { + if(ifaddr->state == TENTATIVE) { + /*It means DAD failed*/ + uip_netif_dad_failed(&UIP_ND6_NA_BUF->tgtipaddr); + } + goto discard; + } else { + neighbor = uip_nd6_nbrcache_lookup(&UIP_ND6_NA_BUF->tgtipaddr); + /* if the neighbor entry does not exist, silently discard */ + if(neighbor == NULL) { + goto discard; + } + if(nd6_opt_llao != 0) { + is_llchange = memcmp((void *)&nd6_opt_llao->addr, (void *)(&neighbor->lladdr), UIP_LLADDR_LEN); + } + if(neighbor->state == INCOMPLETE){ + if(nd6_opt_llao == 0) { + goto discard; + } + memcpy(&neighbor->lladdr, &nd6_opt_llao->addr, UIP_LLADDR_LEN); + if(is_solicited) { + neighbor->state = REACHABLE; + /* reachable time is stored in ms*/ + timer_set(&(neighbor->reachable), + uip_netif_physical_if.reachable_time * CLOCK_SECOND / 1000); + + } else { + neighbor->state = STALE; + } + neighbor->isrouter = is_router; + } else { + if(!is_override && is_llchange) { + if(neighbor->state == REACHABLE) { + neighbor->state = STALE; + } + goto discard; + } else { + if(is_override + || (!is_override && nd6_opt_llao != 0 && !is_llchange) + || nd6_opt_llao == 0) { + + /* update LL address if any */ + if(nd6_opt_llao != 0) { + memcpy(&neighbor->lladdr, &nd6_opt_llao->addr, UIP_LLADDR_LEN); + } + + /* + * if the NA was solicited, change the state of the neighbor to + * reachabe. Otherwise and if the neighbor LL changed, set the + * state to STALE + */ + if(is_solicited) { + neighbor->state = REACHABLE; + /* reachable time is stored in ms*/ + timer_set(&(neighbor->reachable), + uip_netif_physical_if.reachable_time*CLOCK_SECOND/1000); + + } else { + if(nd6_opt_llao != 0 && is_llchange) { + neighbor->state = STALE; + } + } + } + } + if(neighbor->isrouter && !is_router){ + /* the neighbor stopped being a router, we remove it from the list */ + router = uip_nd6_defrouter_lookup(neighbor); + if(router != NULL){ + uip_nd6_defrouter_rm(router); + } + } + neighbor->isrouter = is_router; + } + } + +#if UIP_CONF_IPV6_QUEUE_PKT + /* The neighbor is now reachable, check if we had buffered a pkt for it */ + if(neighbor->queue_buf_len != 0) { + uip_len = neighbor->queue_buf_len; + memcpy(UIP_IP_BUF, neighbor->queue_buf, uip_len); + neighbor->queue_buf_len = 0; + return; + } +#endif /*UIP_CONF_IPV6_QUEUE_PKT*/ + uip_len = 0; + return; + +#if UIP_CONF_IPV6_CHECKS + badpkt: + UIP_STAT(++uip_stat.nd6.drop); + UIP_LOG("NA received is bad"); +#endif /* UIP_CONF_IPV6_CHECKS */ + + discard: + uip_len = 0; + return; +} + + + +/*---------------------------------------------------------------------------*/ +void +uip_nd6_io_rs_output(void) { + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0; + UIP_IP_BUF->flow = 0; + UIP_IP_BUF->proto = UIP_PROTO_ICMP6; + UIP_IP_BUF->ttl = UIP_ND6_HOP_LIMIT; + uip_create_linklocal_allrouters_mcast(&UIP_IP_BUF->destipaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + UIP_ICMP_BUF->type = ICMP6_RS; + UIP_ICMP_BUF->icode = 0; + UIP_ND6_RS_BUF->reserved = 0; + + UIP_IP_BUF->len[0] = 0; /* length will not be more than 255 */ + + /* we add the SLLAO option only if src is not unspecified */ + if(uip_is_addr_unspecified(&UIP_IP_BUF->srcipaddr)) { + UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_RS_LEN; + uip_len = uip_l3_icmp_hdr_len + UIP_ND6_RS_LEN; + } else { + uip_len = uip_l3_icmp_hdr_len + UIP_ND6_RS_LEN + UIP_ND6_OPT_LLAO_LEN; + UIP_IP_BUF->len[1] = UIP_ICMPH_LEN + UIP_ND6_RS_LEN + UIP_ND6_OPT_LLAO_LEN; + nd6_opt_llao = (struct uip_nd6_opt_llao *)&uip_buf[uip_l2_l3_icmp_hdr_len + UIP_ND6_RS_LEN]; + nd6_opt_llao->type = UIP_ND6_OPT_SLLAO; /* type of the option */ + nd6_opt_llao->len = UIP_ND6_OPT_LLAO_LEN >> 3; + /* length of the option in units of 8 bytes */ + memcpy(&nd6_opt_llao->addr, &uip_lladdr, UIP_LLADDR_LEN); + /* padding if needed */ + memset((void *)(&nd6_opt_llao->addr) + UIP_LLADDR_LEN, 0, UIP_ND6_OPT_LLAO_LEN - 2 - UIP_LLADDR_LEN); + } + + + UIP_ICMP_BUF->icmpchksum = 0; + UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum(); + + UIP_STAT(++uip_stat.nd6.sent); + + PRINTF("Sendin RS to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + + return; +} + + + +/*---------------------------------------------------------------------------*/ +void +uip_nd6_io_ra_input(void) { + PRINTF("Received RA from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("\n"); + UIP_STAT(++uip_stat.nd6.recv); + + +#if UIP_CONF_IPV6_CHECKS + /* a few validity checks */ + if((UIP_IP_BUF->ttl != UIP_ND6_HOP_LIMIT) || + (!uip_is_addr_link_local(&UIP_IP_BUF->srcipaddr)) || + (UIP_ICMP_BUF->icode != 0)) { + goto badpkt; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + + /* + * process the fields of the constant part + * As per RFC 4861, for reachable time and retrans timer, a value of 0 means + * unspecified by this router. + * If router lifetime is 0, the router SHOULD NOT be placed in default + * router list + */ + if(UIP_ND6_RA_BUF->cur_ttl != 0) { + uip_netif_physical_if.cur_hop_limit = UIP_ND6_RA_BUF->cur_ttl; + PRINTF("uip_netif_physical_if.cur_hop_limit %d\n", uip_netif_physical_if.cur_hop_limit); + } + /* + * As per RFC4861 section 6.3.4, if reachable time field is non zero + * and defers from the current base reachable time for the interface, + * set base reachable time to the new value and recompute the reachable time + * as a random value distributed between 0.5 and 1.5 x Base reachable time + */ + if(UIP_ND6_RA_BUF->reachable_time != 0) { + if(uip_netif_physical_if.base_reachable_time != ntohl(UIP_ND6_RA_BUF->reachable_time)) { + uip_netif_physical_if.base_reachable_time = ntohl(UIP_ND6_RA_BUF->reachable_time); + uip_netif_physical_if.reachable_time = uip_netif_compute_reachable_time(); + } + } + if(UIP_ND6_RA_BUF->retrans_timer !=0) { + uip_netif_physical_if.retrans_timer = ntohl(UIP_ND6_RA_BUF->retrans_timer); + } + + /* + * Note: in our implementation, an entry in the default router list contains + * does not contain the IP address of the router. The entry has a pointer + * to the corresponding neighbor cache entry, where the IP address is stored + */ + /* Options reading: possible options are MTU, SLLAO, prefix */ + nd6_opt_llao = NULL; + nd6_opt_mtu = NULL; + for(i = 0; i < UIP_CONF_ND6_MAX_PREFIXES; i++) { + nd6_opt_prefix_info[i] = NULL; + } + nd6_opt_offset = UIP_ND6_RA_LEN; + while(uip_l3_icmp_hdr_len + nd6_opt_offset < uip_len) { + if(UIP_ND6_OPT_HDR_BUF->len == 0) { + goto badpkt; + } + switch(UIP_ND6_OPT_HDR_BUF->type) { + case UIP_ND6_OPT_SLLAO: + nd6_opt_llao = (struct uip_nd6_opt_llao *)UIP_ND6_OPT_HDR_BUF; + break; + case UIP_ND6_OPT_MTU: + nd6_opt_mtu = (struct uip_nd6_opt_mtu *)UIP_ND6_OPT_HDR_BUF; + break; + case UIP_ND6_OPT_PREFIX_INFO: + i = 0; + while((i < UIP_CONF_ND6_MAX_PREFIXES) && (nd6_opt_prefix_info[i] != NULL)) { + i++; + } + if(i < UIP_CONF_ND6_MAX_PREFIXES) { + nd6_opt_prefix_info[i] = (struct uip_nd6_opt_prefix_info *)UIP_ND6_OPT_HDR_BUF; + } + break; + default: + UIP_LOG("ND option not supported in RA"); + break; + } + nd6_opt_offset += (UIP_ND6_OPT_HDR_BUF->len << 3); + } + + /* Process the MTU option if any */ + if(nd6_opt_mtu != NULL) { + /* just set the link MTU to the value specified. Note that this option */ + PRINTF("Processing MTU option in RA\n"); + uip_netif_physical_if.link_mtu = ntohl(nd6_opt_mtu->mtu); + } + + /* Prefix information options processing */ + i = 0; + while((i < UIP_CONF_ND6_MAX_PREFIXES) && (nd6_opt_prefix_info[i] != NULL)) { + PRINTF("Processing PREFIX option in RA\n"); + + if((ntohl((nd6_opt_prefix_info[i])->validlt) >= + ntohl((nd6_opt_prefix_info[i])->preferredlt)) && + (!uip_is_addr_link_local(&nd6_opt_prefix_info[i]->prefix))) { + /* on-link flag related processing*/ + if(((nd6_opt_prefix_info[i])->flagsreserved1 & 0x80) == 0x80) { + prefix = uip_nd6_prefix_lookup(&(nd6_opt_prefix_info[i])->prefix); + if(prefix == NULL) { + if((nd6_opt_prefix_info[i])->validlt != 0){ + if((nd6_opt_prefix_info[i])->validlt != UIP_ND6_INFINITE_LIFETIME){ + prefix = uip_nd6_prefix_add(&(nd6_opt_prefix_info[i])->prefix, + (nd6_opt_prefix_info[i])->preflen, + ntohl((nd6_opt_prefix_info[i])->validlt)*CLOCK_SECOND); + } else { + prefix = uip_nd6_prefix_add(&(nd6_opt_prefix_info[i])->prefix, + (nd6_opt_prefix_info[i])->preflen, + 0); + } + } + } else { + /* we update or remove an existing prefix */ + switch((nd6_opt_prefix_info[i])->validlt) { + case 0: + uip_nd6_prefix_rm(prefix); + break; + case UIP_ND6_INFINITE_LIFETIME: + prefix->is_infinite = 1; + break; + default: + timer_set(&prefix->vlifetime, ntohl((nd6_opt_prefix_info[i])->validlt)*CLOCK_SECOND); + /*in case the prefix lifetime was previously infinite */ + prefix->is_infinite = 0; + break; + } + } + } + /* End of on-link flag related processing */ + /* autonomous flag related processing*/ + if((((nd6_opt_prefix_info[i])->flagsreserved1 & 0x40) == 0x40) && + ((nd6_opt_prefix_info[i])->validlt != 0)){ + ifaddr = uip_netif_addr_lookup(&(nd6_opt_prefix_info[i])->prefix, + (nd6_opt_prefix_info[i])->preflen, + AUTOCONF); + if(ifaddr != NULL) { + if((nd6_opt_prefix_info[i])->validlt != UIP_ND6_INFINITE_LIFETIME) { + /* The processing below is defined in RFC4862 section 5.5.3 e */ + if((ntohl((nd6_opt_prefix_info[i])->validlt) > 2 * 60 * 60) || + (ntohl((nd6_opt_prefix_info[i])->validlt) * CLOCK_SECOND > timer_remaining(&ifaddr->vlifetime))) { + PRINTF("Updating timer of address"); + PRINT6ADDR(&ifaddr->ipaddr); + PRINTF("new value %d\n", ntohl((nd6_opt_prefix_info[i])->validlt)); + timer_set(&ifaddr->vlifetime, ntohl((nd6_opt_prefix_info[i])->validlt) * CLOCK_SECOND); + } else { + /** \TODO below overflows on raven, i.e. when times are on + 16 bits */ + timer_set(&ifaddr->vlifetime, 2 * 60 * 60 * CLOCK_SECOND); + PRINTF("Updating timer of address "); + PRINT6ADDR(&ifaddr->ipaddr); + PRINTF("new value %d\n", 2 * 60 * 60); + } + /*in case the address lifetime was previously infinite */ + ifaddr->is_infinite = 0; + } else { + ifaddr->is_infinite = 1; + } + } else { + /* Autoconfigure an address*/ + if((nd6_opt_prefix_info[i])->validlt != UIP_ND6_INFINITE_LIFETIME) { + /* Add an address with FINITE lifetime */ + uip_netif_addr_add(&(nd6_opt_prefix_info[i])->prefix, + (nd6_opt_prefix_info[i])->preflen, + ntohl((nd6_opt_prefix_info[i])->validlt)*CLOCK_SECOND, + AUTOCONF); + } else { + /* Add an address with INFINITE lifetime */ + uip_netif_addr_add(&(nd6_opt_prefix_info[i])->prefix, + (nd6_opt_prefix_info[i])->preflen, + 0, + AUTOCONF); + } + } + } + /* End of autonomous flag related processing */ + } + i++; + } + /* End of Prefix Information Option processing*/ + + + /* SLLAO option processing */ + neighbor = uip_nd6_nbrcache_lookup(&UIP_IP_BUF->srcipaddr); + if(nd6_opt_llao != NULL) { + PRINTF("Processing SLLAO option in RA\n"); + if(neighbor == NULL) { + neighbor = uip_nd6_nbrcache_add(&UIP_IP_BUF->srcipaddr, + &nd6_opt_llao->addr, 1, STALE); + } else { + if(neighbor->state == INCOMPLETE) { + neighbor->state = STALE; + } + /* check if there was an address change. it applies to the case + * where if the router ND entry was in state NO_STATE */ + if(memcmp(&nd6_opt_llao->addr, &neighbor->lladdr, UIP_LLADDR_LEN) != 0) { + memcpy(&neighbor->lladdr, &nd6_opt_llao->addr, UIP_LLADDR_LEN); + neighbor->state = STALE; + } + /*in case the neighbor was not a router before */ + neighbor->isrouter = 1; + } + } + + + /* + * As per RFC4861, section 6.3.4, and considering that in our + * implementation, a router entry contains a pointer to an existing + * neighbor entry: + * if router lifetime != 0, if entry present in def router list + * (and therefore neighbor cache), update timer, and update neighbor entry + * if needed + * else add entry to both router list (and neighbor cache if absent) + * else delete entry in router list if it exists + */ + if(UIP_ND6_RA_BUF->router_lifetime != 0) { + /* Add router or update timer, we need a neighbor entry for this */ + if(neighbor == NULL) { + neighbor = uip_nd6_nbrcache_add(&UIP_IP_BUF->srcipaddr, NULL, 1, NO_STATE); + } else { + /* in case the neighbor was not a router before */ + neighbor->isrouter = 1; + } + if((router = uip_nd6_defrouter_lookup(neighbor)) == NULL) { + uip_nd6_defrouter_add(neighbor, ntohs(UIP_ND6_RA_BUF->router_lifetime)*CLOCK_SECOND); + } else { + timer_set(&(router->lifetime), ntohs(UIP_ND6_RA_BUF->router_lifetime)*CLOCK_SECOND); + } + } else { + /* delete router entry*/ + if(neighbor != NULL) { + router = uip_nd6_defrouter_lookup(neighbor); + if(router != NULL) { + uip_nd6_defrouter_rm(router); + } + } + } + +#if UIP_CONF_IPV6_QUEUE_PKT + /* + * If the neighbor just became reachable (e.g. it was in INCOMPLETE state + * and we got a SLLAO), check if we had buffered a pkt for it + */ + if((neighbor != NULL) && (neighbor->queue_buf_len != 0)) { + uip_len = neighbor->queue_buf_len; + memcpy(UIP_IP_BUF, neighbor->queue_buf, uip_len); + neighbor->queue_buf_len = 0; + return; + } +#endif /*UIP_CONF_IPV6_QUEUE_PKT*/ + uip_len = 0; + return; + + badpkt: + UIP_STAT(++uip_stat.nd6.drop); + UIP_LOG("RA received is bad"); + uip_len = 0; + return; +} + + +/** @} */ diff --git a/core/net/uip-nd6.c b/core/net/uip-nd6.c new file mode 100644 index 000000000..1ba0cada1 --- /dev/null +++ b/core/net/uip-nd6.c @@ -0,0 +1,471 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * Neighbor discovery (RFC 4861) + * \author Mathilde Durvy + * \author Julien Abeille + */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "net/uip-nd6.h" +#include "net/uip-netif.h" +#include "lib/random.h" + +#include + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +#if UIP_LOGGING +#include +void uip_log(char *msg); +#define UIP_LOG(m) uip_log(m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +/** Remove a neighbor cache entry */ +#define uip_nd6_nbrcache_rm(neighbor) do { \ + PRINTF("Removing neighbor with ip addr"); \ + PRINT6ADDR(&neighbor->ipaddr); \ + PRINTF("\n"); \ + neighbor->used = 0; \ + } while(0) + + +/** + * \brief Timer for maintenance of neighbor cache, prefix list, and default + * router lists + */ +struct etimer uip_nd6_timer_periodic; +/** \brief Neighor cache */ +static struct uip_nd6_neighbor uip_nd6_nbrcache_list[UIP_CONF_ND6_MAX_NEIGHBORS]; +/** \brief Default router list */ +static struct uip_nd6_defrouter uip_nd6_defrouter_list[UIP_CONF_ND6_MAX_DEFROUTERS]; +/** \brief Prefix list */ +static struct uip_nd6_prefix uip_nd6_prefix_list[UIP_CONF_ND6_MAX_PREFIXES]; + +/** \name Temp variables*/ +/** @{*/ +static u8_t i; +static struct uip_nd6_prefix *prefix; +static struct uip_nd6_neighbor *neighbor; +static struct uip_nd6_defrouter *router; +/** @}*/ + + +void +uip_nd6_init(void) +{ + /* INITIALIZE NEIGHBOR DISCOVERY*/ + for(i = 0; i < UIP_CONF_ND6_MAX_NEIGHBORS; i ++) { + uip_nd6_nbrcache_list[i].used = 0; + } + for(i = 0; i < UIP_CONF_ND6_MAX_DEFROUTERS; i ++) { + uip_nd6_defrouter_list[i].used = 0; + } + for(i = 0; i < UIP_CONF_ND6_MAX_PREFIXES; i ++) { + uip_nd6_prefix_list[i].used = 0; + } + + /* create link local prefix */ + uip_ip6addr(&(uip_nd6_prefix_list[0].ipaddr), 0xfe80, 0, 0, 0, 0, 0, 0, 0); + uip_nd6_prefix_list[0].length = UIP_DEFAULT_PREFIX_LEN; + uip_nd6_prefix_list[0].used = 1; + uip_nd6_prefix_list[0].is_infinite = 1; + + /* we check the ND structures every 100ms */ + etimer_set(&uip_nd6_timer_periodic, 0.1 * CLOCK_SECOND); +} + +struct uip_nd6_neighbor * +uip_nd6_nbrcache_lookup(uip_ipaddr_t *ipaddr) +{ + neighbor = NULL; + + for(i = 0; i < UIP_CONF_ND6_MAX_NEIGHBORS; i ++) { + if(uip_nd6_nbrcache_list[i].used == 0) { + continue; + } + if(uip_ipaddr_cmp(&uip_nd6_nbrcache_list[i].ipaddr, ipaddr)) { + neighbor = &uip_nd6_nbrcache_list[i]; + break; + } + } + return neighbor; +} + +struct uip_nd6_neighbor * +uip_nd6_nbrcache_add(uip_ipaddr_t *ipaddr, uip_lladdr_t *lladdr, + u8_t isrouter, uip_neighbor_state state) +{ + neighbor = NULL; + + for(i = 0; i < UIP_CONF_ND6_MAX_NEIGHBORS; ++i) { + if(uip_nd6_nbrcache_list[i].used == 0) { + break; + } + } + if(i == UIP_CONF_ND6_MAX_NEIGHBORS){ + i = (u8_t)(random_rand()%UIP_CONF_ND6_MAX_NEIGHBORS); + UIP_LOG("CACHE FULL"); + } + + neighbor = &(uip_nd6_nbrcache_list[i]); + uip_ipaddr_copy(&(neighbor->ipaddr), ipaddr); + if(lladdr != NULL){ + memcpy(&(neighbor->lladdr), lladdr, UIP_LLADDR_LEN); + } else { + memset(&(neighbor->lladdr), 0, UIP_LLADDR_LEN); + } + PRINTF("Adding neighbor with ip addr"); + PRINT6ADDR(ipaddr); + PRINTF("link addr"); + PRINTLLADDR((&(neighbor->lladdr))); + PRINTF("state %d\n", state); + + neighbor->isrouter = isrouter; + neighbor->state = state; + /* timers are set separately, for now we put them in expired state */ + timer_set(&(neighbor->reachable),0); + timer_set(&(neighbor->last_send),0); + neighbor->count_send = 0; + neighbor->used = 1; + return neighbor; +} + + +struct uip_nd6_defrouter * +uip_nd6_defrouter_lookup(struct uip_nd6_neighbor *neighbor) +{ + router = NULL; + + for(i = 0; i < UIP_CONF_ND6_MAX_DEFROUTERS; i ++) { + if(uip_nd6_defrouter_list[i].used == 0) { + continue; + } + if(uip_nd6_defrouter_list[i].nb == neighbor) { + router = &uip_nd6_defrouter_list[i]; + break; + } + } + return router; +} + +struct uip_nd6_defrouter * +uip_nd6_choose_defrouter(void) +{ + router = NULL; + + for(i = 0; i < UIP_CONF_ND6_MAX_DEFROUTERS; i ++) { + if(uip_nd6_defrouter_list[i].used == 0) { + continue; + } + if(uip_nd6_defrouter_list[i].nb == NULL){ + continue; + } + if(uip_nd6_defrouter_list[i].nb->state == INCOMPLETE || + uip_nd6_defrouter_list[i].nb->state == NO_STATE){ + if(router == NULL) + router = &(uip_nd6_defrouter_list[i]); + } else { + router = &(uip_nd6_defrouter_list[i]); + break; + } + } + return router; +} + +void +uip_nd6_defrouter_rm(struct uip_nd6_defrouter *router) +{ + PRINTF("Removing defrouter with ip addr"); + PRINT6ADDR(&((router->nb)->ipaddr)); + PRINTF("\n"); + router->used = 0; + if(((router->nb)->state) == NO_STATE){ + /* Also remove the corresponding neighbor cache entry */ + uip_nd6_nbrcache_rm(router->nb); + } +} + +struct uip_nd6_defrouter * +uip_nd6_defrouter_add(struct uip_nd6_neighbor *neighbor, clock_time_t interval) +{ + router = NULL; + + for(i = 0; i < UIP_CONF_ND6_MAX_DEFROUTERS; ++i) { + if(uip_nd6_defrouter_list[i].used == 0) { + break; + } + } + if(i == UIP_CONF_ND6_MAX_DEFROUTERS){ + UIP_LOG("Defrouter list full"); + i = (u8_t)(random_rand()%UIP_CONF_ND6_MAX_DEFROUTERS); + } + + router = &(uip_nd6_defrouter_list[i]); + router->nb = neighbor; + + PRINTF("Adding defrouter with ip addr"); + PRINT6ADDR(&neighbor->ipaddr); + PRINTF("\n"); + + timer_set(&(router->lifetime),interval); + uip_nd6_defrouter_list[i].used = 1; + + return router; +} + +u8_t +uip_nd6_is_addr_onlink(uip_ipaddr_t *ipaddr) +{ + + for(i = 0; i < UIP_CONF_ND6_MAX_PREFIXES; i ++) { + if(uip_nd6_prefix_list[i].used == 0){ + continue; + } + if(uip_ipaddr_prefixcmp(&uip_nd6_prefix_list[i].ipaddr, + ipaddr, uip_nd6_prefix_list[i].length)){ + return 1; + } + } + return 0; +} + +struct uip_nd6_prefix * +uip_nd6_prefix_lookup(uip_ipaddr_t *ipaddr) +{ + prefix = NULL; + + for (i = 0; i < UIP_CONF_ND6_MAX_PREFIXES; i ++) { + if (uip_nd6_prefix_list[i].used == 0){ + continue; + } + if (uip_ipaddr_cmp(&uip_nd6_prefix_list[i].ipaddr, ipaddr)) { + prefix = &uip_nd6_prefix_list[i]; + break; + } + } + return prefix; +} + +struct uip_nd6_prefix * +uip_nd6_prefix_add(uip_ipaddr_t *ipaddr, u8_t length, clock_time_t interval){ + + prefix = NULL; + + /* + * we start at 1: we do not want to overwrite the link local + * prefix + */ + for(i = 1; i < UIP_CONF_ND6_MAX_PREFIXES; ++i) { + if(uip_nd6_prefix_list[i].used == 0) { + break; + } + } + if(i == UIP_CONF_ND6_MAX_PREFIXES){ + UIP_LOG("Prefix list full"); + /* + * we do not want it to pick the first entry, which + * is the link local prefix + */ + i = (u8_t)(random_rand()%(UIP_CONF_ND6_MAX_PREFIXES -1) + 1); + PRINTF("Prefix list full, picking index %x\n\n", i); + } + prefix = &(uip_nd6_prefix_list[i]); + uip_ipaddr_copy(&(prefix->ipaddr), ipaddr); + prefix->length = length; + + PRINTF("Adding prefix "); + PRINT6ADDR(&prefix->ipaddr); + PRINTF("length %d, vlifetime * CLOCK_SECOND %d\n", length, (u16_t)interval); + + if(interval != 0){ + timer_set(&(prefix->vlifetime),interval); + prefix->is_infinite = 0; + } else { + prefix->is_infinite = 1; + } + + uip_nd6_prefix_list[i].used = 1; + return prefix; +} + + +void uip_nd6_prefix_rm(struct uip_nd6_prefix *prefix) { + PRINTF("Removing prefix "); + PRINT6ADDR(&prefix->ipaddr); + PRINTF("length %d\n", prefix->length); + prefix->used = 0; +} + + +void +uip_nd6_periodic(void) +{ + + etimer_reset(&uip_nd6_timer_periodic); + + /*PERIODIC PROCESSING FOR DEFAULT ROUTER LIST*/ + router = NULL; + for(i = 0; i < UIP_CONF_ND6_MAX_DEFROUTERS; i ++) { + if(uip_nd6_defrouter_list[i].used == 1) { + router = &(uip_nd6_defrouter_list[i]); + if (timer_expired(&(router->lifetime))) { + uip_nd6_defrouter_rm(router); + } + } + } + /*PERIODIC PROCESSING FOR NEIGHBOR CACHE*/ + neighbor = NULL; + for(i = 0; i < UIP_CONF_ND6_MAX_NEIGHBORS; i ++) { + if(uip_nd6_nbrcache_list[i].used == 1) { + neighbor = &(uip_nd6_nbrcache_list[i]); + switch (neighbor->state) { + case INCOMPLETE: + if(neighbor->count_send >= UIP_ND6_MAX_MULTICAST_SOLICIT) { + uip_nd6_nbrcache_list[i].used = 0; + } + else if(timer_expired(&(neighbor->last_send))) { + PRINTF("INCOMPLETE: NS %d\n",neighbor->count_send+1); + uip_nd6_io_ns_output(NULL, NULL, &neighbor->ipaddr); + timer_set(&(neighbor->last_send), + uip_netif_physical_if.retrans_timer/1000*CLOCK_SECOND); + neighbor->count_send++; + } + break; + case REACHABLE: + if(timer_expired(&(neighbor->reachable))) { + PRINTF("REACHABLE: moving to STALE ("); + PRINT6ADDR(&neighbor->ipaddr); + PRINTF(")\n"); + + neighbor->state = STALE; + neighbor->count_send = 0; + } + break; + case DELAY: + if(timer_expired(&(neighbor->reachable))) { + neighbor->state = PROBE; + neighbor->count_send = 0; + PRINTF("DELAY: moving to PROBE + NS %d\n", neighbor->count_send+1); + uip_nd6_io_ns_output(NULL, &neighbor->ipaddr, &neighbor->ipaddr); + timer_set(&(neighbor->last_send), + uip_netif_physical_if.retrans_timer/1000*CLOCK_SECOND); + neighbor->count_send++; + } + break; + case PROBE: + if(neighbor->count_send >= UIP_ND6_MAX_UNICAST_SOLICIT) { + PRINTF("PROBE END \n"); + neighbor->count_send = 0; + /* remove any corresponding default router (if any)*/ + router = uip_nd6_defrouter_lookup(neighbor); + if(router != NULL) + uip_nd6_defrouter_rm(router); + /* remove neighbor cache entry */ + uip_nd6_nbrcache_rm(neighbor); + continue; + } + if(timer_expired(&(neighbor->last_send))){ + PRINTF("PROBE: NS %d\n",neighbor->count_send+1); + uip_nd6_io_ns_output(NULL, &neighbor->ipaddr, &neighbor->ipaddr); + timer_set(&(neighbor->last_send), + uip_netif_physical_if.retrans_timer/1000*CLOCK_SECOND); + neighbor->count_send++; + } + break; + default: + break; + } + } + } + /*PERIODIC PROCESSING FOR PREFIX LIST*/ + prefix = NULL; + /* + * we start with i=1 because the first prefix is the link-local prefix + * and therefore has INFINITE an lifetime + */ + for(i = 1; i < UIP_CONF_ND6_MAX_PREFIXES; i ++) { + if(uip_nd6_prefix_list[i].used == 1) { + prefix = &(uip_nd6_prefix_list[i]); + if((prefix->is_infinite == 0) && + (timer_expired(&(prefix->vlifetime)))) { + /* remove prefix */ + uip_nd6_prefix_rm(prefix); + } + } + } +} +/** @} */ diff --git a/core/net/uip-nd6.h b/core/net/uip-nd6.h new file mode 100644 index 000000000..fcd5e58ef --- /dev/null +++ b/core/net/uip-nd6.h @@ -0,0 +1,688 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * Neighbor discovery (RFC 4861) + * \author Julien Abeille + * \author Mathilde Durvy + */ + +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ + +#ifndef __UIP_ND6_H__ +#define __UIP_ND6_H__ + +#include "net/uip.h" + +/** + * \name General + * @{ + */ +/** \brief HOP LIMIT to be used when sending ND messages (255) */ +#define UIP_ND6_HOP_LIMIT 255 +/** \brief INFINITE lifetime */ +#define UIP_ND6_INFINITE_LIFETIME 0xFFFFFFFF +/** @} */ + + +/** \name Configuration options */ +/** @{ */ +#ifndef UIP_CONF_ND6_MAX_NEIGHBORS +/** \brief max number of entries in the neighbor cache */ +#define UIP_CONF_ND6_MAX_NEIGHBORS 4 +#endif /*UIP_CONF_ND6_MAX_NEIGHBORS*/ +#ifndef UIP_CONF_ND6_MAX_DEFROUTERS +/** \brief max number of entries in the default router cache */ +#define UIP_CONF_ND6_MAX_DEFROUTERS 2 +#endif /*UIP_CONF_ND6_MAX_DEFROUTERS*/ +#ifndef UIP_CONF_ND6_MAX_PREFIXES +/** \brief max number of entries in the prefix list */ +#define UIP_CONF_ND6_MAX_PREFIXES 2 +#endif /*UIP_CONF_ND6_MAX_PREFIXES*/ +/** @} */ + + +/** \name RFC 4861 Host constant */ +/** @{ */ +#define UIP_ND6_MAX_RTR_SOLICITATION_DELAY 1 +#define UIP_ND6_RTR_SOLICITATION_INTERVAL 4 +#define UIP_ND6_MAX_RTR_SOLICITATIONS 3 +/** @} */ + + +/** \name RFC 4861 Node constant */ +#define UIP_ND6_MAX_MULTICAST_SOLICIT 3 +#define UIP_ND6_MAX_UNICAST_SOLICIT 3 +#define UIP_ND6_REACHABLE_TIME 30000 +#define UIP_ND6_RETRANS_TIMER 1000 +#define UIP_ND6_DELAY_FIRST_PROBE_TIME 5 +#define UIP_ND6_MIN_RANDOM_FACTOR 0.5 +#define UIP_ND6_MAX_RANDOM_FACTOR 1.5 +/** @} */ + + +/** \name ND6 option types */ +/** @{ */ +#define UIP_ND6_OPT_SLLAO 1 +#define UIP_ND6_OPT_TLLAO 2 +#define UIP_ND6_OPT_PREFIX_INFO 3 +#define UIP_ND6_OPT_REDIRECTED_HDR 4 +#define UIP_ND6_OPT_MTU 5 +/** @} */ + + +/** \name ND6 message length (excluding options) */ +/** @{ */ +#define UIP_ND6_NA_LEN 20 +#define UIP_ND6_NS_LEN 20 +#define UIP_ND6_RA_LEN 12 +#define UIP_ND6_RS_LEN 4 +/** @} */ + + +/** \name ND6 option length in bytes */ +/** @{ */ +#define UIP_ND6_OPT_HDR_LEN 2 +#define UIP_ND6_OPT_PREFIX_INFO_LEN 32 +#define UIP_ND6_OPT_MTU_LEN 8 + + +/* Length of TLLAO and SLLAO options, it is L2 dependant */ +#if UIP_CONF_LL_802154 +/* If the interface is 802.15.4. For now we use only long addresses */ +#define UIP_ND6_OPT_SHORT_LLAO_LEN 8 +#define UIP_ND6_OPT_LONG_LLAO_LEN 16 +/** \brief length of a ND6 LLAO option for 802.15.4 */ +#define UIP_ND6_OPT_LLAO_LEN UIP_ND6_OPT_LONG_LLAO_LEN +#else /*UIP_CONF_LL_802154*/ +#if UIP_CONF_LL_80211 +/* If the interface is 802.11 */ +/** \brief length of a ND6 LLAO option for 802.11 */ +#define UIP_ND6_OPT_LLAO_LEN 8 +#else /*UIP_CONF_LL_80211*/ +/** \brief length of a ND6 LLAO option for default L2 type (e.g. Ethernet) */ +#define UIP_ND6_OPT_LLAO_LEN 8 +#endif /*UIP_CONF_LL_80211*/ +#endif /*UIP_CONF_LL_802154*/ +/** @} */ + + +/** \name Neighbor Advertisement flags masks */ +/** @{ */ +#define UIP_ND6_NA_FLAG_ROUTER 0x80 +#define UIP_ND6_NA_FLAG_SOLICITED 0x40 +#define UIP_ND6_NA_FLAG_OVERRIDE 0x20 +/** @} */ + + +/** + * \brief Possible states for the neighbor cache entries + * + * NO_STATE is for implementation purposes: a router entry contains a pointer + * to a neighbor entry, which holds its ip address. If we do not know the LL + * address of the router, we do not have to create a neighbor entry as per + * RFC 4861. However, we still need to have the IP of the router stored in a + * neighbor entry, hence we create an entry in the NO_STATE state + */ +typedef enum { + INCOMPLETE = 0, + REACHABLE = 1, + STALE = 2, + DELAY = 3, + PROBE = 4, + NO_STATE = 5 +} uip_neighbor_state; + +/** + * \name ND structures + * @{ + */ +/** \brief An entry in the neighbor cache */ +struct uip_nd6_neighbor{ + uip_ipaddr_t ipaddr; + uip_lladdr_t lladdr; + u8_t isrouter; + uip_neighbor_state state; + struct timer reachable; + struct timer last_send; /**< last time a ND message was sent */ + u8_t count_send; /**< how many ND message were already sent */ + u8_t used; /**< brief is this neighbor currently used */ +#if UIP_CONF_IPV6_QUEUE_PKT + u8_t queue_buf[UIP_BUFSIZE - UIP_LLH_LEN]; + /**< buffer to hold one packet during address resolution */ + u8_t queue_buf_len; + /**< length of the pkt in buffer, used as "boolean" as well*/ +#endif /*UIP_CONF_QUEUE_PKT*/ +}; + + +/** \brief An entry in the default router list */ +struct uip_nd6_defrouter { + struct uip_nd6_neighbor *nb; + struct timer lifetime; + /**< the lifetime contained in RA corresponds to the interval field + * of the timer + */ + u8_t used; /**< Is this default router entry currently used */ +}; + +/** \brief A prefix list entry */ +struct uip_nd6_prefix { + uip_ipaddr_t ipaddr; + u8_t length; + /**< we do not use preferred lifetime, which is always smaller than + * valid lifetime (for addr, preferred->deprecated) + */ + struct timer vlifetime; + u8_t is_infinite; /**< Is the prefix lifetime INFINITE */ + u8_t used; /**< Is this prefix entry currently used */ +}; +/** @} */ + +extern struct etimer uip_nd6_timer_periodic; + +/** + * \note + * We do not use a destination cache, do next-hop determination each time + * a packet needs to be sent. (info such as rtt, path mtu could be stored + * in uip_conn) + * + */ + +/** + * \name ND message structures + * @{ + */ + +/** + * \brief A neighbor solicitation constant part + * + * Possible option is: SLLAO + */ +struct uip_nd6_ns { + u32_t reserved; + uip_ipaddr_t tgtipaddr; +}; + +/** + * \brief A neighbor advertisement constant part. + * + * Possible option is: TLLAO + */ +struct uip_nd6_na { + u8_t flagsreserved; + u8_t reserved[3]; + uip_ipaddr_t tgtipaddr; +}; + +/** + * \brief A router solicitation constant part + * + * Possible option is: SLLAO + */ +struct uip_nd6_rs { + u32_t reserved; +}; + +/** + * \brief A router advertisement constant part + * + * Possible options are: SLLAO, MTU, Prefix Information + */ +struct uip_nd6_ra { + u8_t cur_ttl; + u8_t flags_reserved; + u16_t router_lifetime; + u32_t reachable_time; + u32_t retrans_timer; +}; + +/** + * \brief A redirect message constant part + * + * Possible options are: TLLAO, redirected header + */ +struct uip_nd6_redirect { + u32_t reserved; + uip_ipaddr_t tgtipaddress; + uip_ipaddr_t destipaddress; +}; +/** @} */ + +/** + * \name ND Option structures + * @{ + */ + +/** \brief ND option header */ +struct uip_nd6_opt_hdr { + u8_t type; + u8_t len; +}; + +/** \brief ND option prefix information */ +struct uip_nd6_opt_prefix_info { + u8_t type; + u8_t len; + u8_t preflen; + u8_t flagsreserved1; + u32_t validlt; + u32_t preferredlt; + u32_t reserved2; + uip_ipaddr_t prefix; +}; + +/** \brief ND option MTU */ +struct uip_nd6_opt_mtu { + u8_t type; + u8_t len; + u16_t reserved; + u32_t mtu; +}; + +/** \brief ND option: both TLLAO and SLLAO */ +struct uip_nd6_opt_llao { + u8_t type; + u8_t len; + uip_lladdr_t addr; +}; + +/** \struct Redirected header option */ +struct uip_nd6_opt_redirected_hdr { + u8_t type; + u8_t len; + u8_t reserved[6]; +}; +/** @} */ + +/** + * \name ND Neighbor Cache, Router List and Prefix List handling functions + * @{ + */ +/** + * \brief Initialize Neighbor Discovery structures + */ +void uip_nd6_init(void); + +/** + * \brief Periodic processing of Neighbor Discovery Structures + */ +void uip_nd6_periodic(void); + +/** + * \brief Look for a neighbor cache entry corresponding to a specific IP + * address + * \param ipaddr the specific IP address + * \return the corresponding neighbor cache entry + */ +struct uip_nd6_neighbor *uip_nd6_nbrcache_lookup(uip_ipaddr_t *ipaddr); + +/** + * \brief Add a neighbor cache entry + * \param ipaddr the IP address of the entry + * \param lladdr the layer 2 address of the entry + * \param isrouter true is the entry is a router + * \param state the state of the entry + * \return the new neighbor or updated cache entry + */ +struct uip_nd6_neighbor * uip_nd6_nbrcache_add(uip_ipaddr_t *ipaddr, + uip_lladdr_t *lladdr, + u8_t isrouter, + uip_neighbor_state state); +/** + * \brief Returns a default router + */ +struct uip_nd6_defrouter * uip_nd6_choose_defrouter(void); + +/** + * \brief Find a default router corresponding to a given neighbor cache entry + * \param neighbor the neighbor cache entry + * \return the corresponding router if any + */ +struct uip_nd6_defrouter * +uip_nd6_defrouter_lookup(struct uip_nd6_neighbor *neighbor); + +/** + * \brief Remove a default router + * \param router to be removed + */ +void uip_nd6_defrouter_rm(struct uip_nd6_defrouter *router); + +/** + * \brief Add a default router + * \param neighbor the corresponding neighbor cache entry + * \param interval the lifetime of the router + * \return the new or updated defrouter entry + */ +struct uip_nd6_defrouter * +uip_nd6_defrouter_add(struct uip_nd6_neighbor *neighbor, clock_time_t interval); + +/** + * \brief Check if an IP address in on-link by looking at prefix list + * \param ipaddr an IP address + * \return true if on-link + */ +u8_t uip_nd6_is_addr_onlink(uip_ipaddr_t *ipaddr); + +/** + * \brief Find a given prefix + * \param ipaddr an IP address + * \return the corresponding prefix if any + */ +struct uip_nd6_prefix * +uip_nd6_prefix_lookup(uip_ipaddr_t *ipaddr); + +/** + * \brief Add a prefix + * \param ipaddr the IP address of the prefix + * \param length the length of the prefix + * \param interval the lifetime of the prefix + * \return the new or updated prefix entry + */ +struct uip_nd6_prefix * +uip_nd6_prefix_add(uip_ipaddr_t *ipaddr, u8_t length, clock_time_t interval); + +/** + * \brief Remove a prefix from th eprefix list + * \param prefix pointer to the prefix to be removed + */ +void + uip_nd6_prefix_rm(struct uip_nd6_prefix *prefix); +/** @} */ + + +/** + * \name ND Messages Processing and Generation + * @{ + */ + /** + * \brief Process a neighbor solicitation + * + * The NS can be received in 3 cases (procedures): + * - sender is performing DAD (ip src = unspecified, no SLLAO option) + * - sender is performing NUD (ip dst = unicast) + * - sender is performing address resolution (ip dest = solicited node mcast + * address) + * + * We do: + * - if the tgt belongs to me, reply, otherwise ignore + * - if i was performing DAD for the same address, two cases: + * -- I already sent a NS, hence I win + * -- I did not send a NS yet, hence I lose + * + * If we need to send a NA in response (i.e. the NS was done for NUD, or + * address resolution, or DAD and there is a conflict), we do it in this + * function: set src, dst, tgt address in the three cases, then for all cases + * set the rest, including SLLAO + * + */ +void +uip_nd6_io_ns_input(void); + +/** + * \brief Send a neighbor solicitation, send a Neighbor Advertisement + * \param src pointer to the src of the NS if known + * \param dest pointer to ip address to send the NS, for DAD or ADDR Resol, + * MUST be NULL, for NUD, must be correct unicast dest + * \param tgt pointer to ip address to fill the target address field, must + * not be NULL + * + * - RFC 4861, 7.2.2 : + * "If the source address of the packet prompting the solicitation is the + * same as one of the addresses assigned to the outgoing interface, that + * address SHOULD be placed in the IP Source Address of the outgoing + * solicitation. Otherwise, any one of the addresses assigned to the + * interface should be used." + * This is why we have a src ip address as argument. If NULL, we will do + * src address selection, otherwise we use the argument. + * + * - we check if it is a NS for Address resolution or NUD, if yes we include + * a SLLAO option, otherwise no. + */ +void +uip_nd6_io_ns_output(uip_ipaddr_t *src, uip_ipaddr_t *dest, uip_ipaddr_t *tgt); + +/** + * \brief Process a Neighbor Advertisement + * + * we might have to send a pkt that had been buffered while address + * resolution was performed (if we support buffering, see UIP_CONF_QUEUE_PKT) + * + * As per RFC 4861, on link layer that have addresses, TLLAO options MUST be + * included when responding to multicast solicitations, SHOULD be included in + * response to unicast (here we assume it is for now) + * + * NA can be received after sending NS for DAD, Address resolution or NUD. Can + * be unsolicited as well. + * It can trigger update of the state of the neighbor in the neighbor cache, + * router in the router list. + * If the NS was for DAD, it means DAD failed + * + */ +void +uip_nd6_io_na_input(void); + +/** + * \brief Send a Router Solicitation + * + * src is chosen through the uip_netif_select_src function. If src is + * unspecified (i.e. we do not have a preferred address yet), then we do not + * put a SLLAO option (MUST NOT in RFC 4861). Otherwise we do. + * + * RS message format, + * possible option is SLLAO, MUST NOT be included if source = unspecified + * SHOULD be included otherwise + */ +void uip_nd6_io_rs_output(void); + +/** + * + * \brief process a Router Advertisement + * + * - Possible actions when receiving a RA: add router to router list, + * recalculate reachable time, update link hop limit, update retrans timer. + * - If MTU option: update MTU. + * - If SLLAO option: update entry in neighbor cache + * - If prefix option: start autoconf, add prefix to prefix list + */ +void +uip_nd6_io_ra_input(void); +/** @} */ + + +void +uip_appserver_addr_get(uip_ipaddr_t *ipaddr); +/*--------------------------------------*/ +/******* ANNEX - message formats ********/ +/*--------------------------------------*/ + +/* + * RS format. possible option is SLLAO + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Options ... + * +-+-+-+-+-+-+-+-+-+-+-+- + * + * + * RA format. possible options: prefix information, MTU, SLLAO + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Cur Hop Limit |M|O| Reserved | Router Lifetime | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reachable Time | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Retrans Timer | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Options ... + * +-+-+-+-+-+-+-+-+-+-+-+- + * + * + * NS format: options should be SLLAO + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Target Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Options ... + * +-+-+-+-+-+-+-+-+-+-+-+- + * + * + * NA message format. possible options is TLLAO + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |R|S|O| Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Target Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Options ... + * +-+-+-+-+-+-+-+-+-+-+-+- + * + * + * Redirect message format. Possible options are TLLAO and Redirected header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Code | Checksum | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Target Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Destination Address + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Options ... + * +-+-+-+-+-+-+-+-+-+-+-+- + * + * + * SLLAO/TLLAO option: + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Length | Link-Layer Address ... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * Prefix information option + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Length | Prefix Length |L|A| Reserved1 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Valid Lifetime | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Preferred Lifetime | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved2 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * + + + * | | + * + Prefix + + * | | + * + + + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * MTU option + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Length | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MTU | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * + * Redirected header option + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Length | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * ~ IP header + data ~ + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ +#endif /* __UIP_ND6_H__ */ + +/** @} */ diff --git a/core/net/uip-netif.c b/core/net/uip-netif.c new file mode 100644 index 000000000..a832a3753 --- /dev/null +++ b/core/net/uip-netif.c @@ -0,0 +1,445 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * Network interface and stateless autoconfiguration (RFC 4862) + * \author Mathilde Durvy + * \author Julien Abeille + * + */ + +/* + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + */ + +#include "net/uip-nd6.h" +#include "net/uip-netif.h" +#include "lib/random.h" + +#include +#include + + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(lladdr) +#endif + +#if UIP_LOGGING +#include +void uip_log(char *msg); +#define UIP_LOG(m) uip_log(m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +/*---------------------------------------------------------------------------*/ +/** \brief The single physical interface */ +struct uip_netif uip_netif_physical_if; +/** \brief DAD timer */ +struct etimer uip_netif_timer_dad; +/** \brief The interface address on which dad is being performed */ +static struct uip_netif_addr *dad_ifaddr; +/** \brief Number of ns already sent for DAD*/ +static u8_t dad_ns; +/** \brief RS timer, to schedule RS sending */ +struct etimer uip_netif_timer_rs; +/** \brief number of rs already sent */ +static u8_t rs_count; +/** \brief index for loops */ +static u8_t i; +/** \brief timer to check the address states */ +struct etimer uip_netif_timer_periodic; + + +/** \brief remove an interface address, argument type is uip_netif_addr* */ +#define uip_netif_addr_rm(addr) do { \ + PRINTF("Removing address"); \ + PRINT6ADDR(&addr->ipaddr); \ + PRINTF("\n"); \ + addr->state = NOT_USED; \ + } while(0) +/*---------------------------------------------------------------------------*/ +void +uip_netif_init(void) +{ + /* INITIALIZE INTERFACE (default values for now) */ + uip_netif_physical_if.link_mtu = UIP_LINK_MTU; + uip_netif_physical_if.cur_hop_limit = UIP_TTL; + uip_netif_physical_if.base_reachable_time = UIP_ND6_REACHABLE_TIME; + uip_netif_physical_if.reachable_time = uip_netif_compute_reachable_time(); + uip_netif_physical_if.retrans_timer = UIP_ND6_RETRANS_TIMER; + uip_netif_physical_if.dup_addr_detect_transmit = 1; + + /* + * STATELESS AUTOCONFIGURATION of the link local address. We set it to + * infinite (this will become really true once DAD succeeds) + */ + uip_ip6addr(&(uip_netif_physical_if.addresses[0].ipaddr), + 0xfe80,0,0,0,0,0,0,0); + uip_netif_addr_autoconf_set(&(uip_netif_physical_if.addresses[0].ipaddr), &uip_lladdr); + uip_netif_physical_if.addresses[0].state = TENTATIVE; + uip_netif_physical_if.addresses[0].type = MANUAL; + uip_netif_physical_if.addresses[0].is_infinite = 1; + + /* set all other addresses to NOT_USED initialy */ + for(i = 1; i < UIP_CONF_NETIF_MAX_ADDRESSES; i ++) + uip_netif_physical_if.addresses[i].state = NOT_USED; + + uip_ip6addr_u8(&(uip_netif_physical_if.solicited_node_mcastaddr), + 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, + uip_lladdr.addr[UIP_LLADDR_LEN - 3], + uip_lladdr.addr[UIP_LLADDR_LEN - 2], + uip_lladdr.addr[UIP_LLADDR_LEN - 1]); + /* Start DAD */ + uip_netif_sched_dad(&(uip_netif_physical_if.addresses[0])); + + /* Find router (send rs to all-routers multicast group)) */ + uip_netif_sched_send_rs(); + + /* Reset the timer */ + etimer_set(&uip_netif_timer_periodic, CLOCK_SECOND); +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_periodic(void) { + for(i = 1; i < UIP_CONF_NETIF_MAX_ADDRESSES; i++) { + if((uip_netif_physical_if.addresses[i].state != NOT_USED) && + (uip_netif_physical_if.addresses[i].is_infinite != 1) && + (timer_expired(&uip_netif_physical_if.addresses[i].vlifetime))) { + uip_netif_addr_rm((&uip_netif_physical_if.addresses[i])); + } + } + etimer_reset(&uip_netif_timer_periodic); + return; +} + +/*---------------------------------------------------------------------------*/ +u32_t +uip_netif_compute_reachable_time(void) +{ + return (u32_t)(uip_netif_physical_if.base_reachable_time * UIP_ND6_MIN_RANDOM_FACTOR) + ((u16_t)(random_rand() << 8) + (u16_t)random_rand()) % (u32_t)(uip_netif_physical_if.base_reachable_time *(UIP_ND6_MAX_RANDOM_FACTOR - UIP_ND6_MIN_RANDOM_FACTOR)); +} + + +/*---------------------------------------------------------------------------*/ +u8_t +uip_netif_is_addr_my_solicited(uip_ipaddr_t *ipaddr) +{ + if(uip_ipaddr_cmp(ipaddr, &uip_netif_physical_if.solicited_node_mcastaddr)) + return 1; + return 0; +} + +/*---------------------------------------------------------------------------*/ +struct uip_netif_addr * +uip_netif_addr_lookup(uip_ipaddr_t *ipaddr, u8_t length, uip_netif_type type) { + for(i = 0; i < UIP_CONF_NETIF_MAX_ADDRESSES; i ++) { + if((uip_netif_physical_if.addresses[i].state != NOT_USED) && + (uip_netif_physical_if.addresses[i].type == type || type == 0) && + (uip_ipaddr_prefixcmp(&(uip_netif_physical_if.addresses[i].ipaddr), ipaddr, length))) { + return &uip_netif_physical_if.addresses[i]; + } + } + return NULL; +} + +/*---------------------------------------------------------------------------*/ +void +uip_netif_addr_add(uip_ipaddr_t *ipaddr, u8_t length, clock_time_t vlifetime, uip_netif_type type) { + + /* check prefix has the right length if we are doing autoconf */ + if((type == AUTOCONF) && (length != UIP_DEFAULT_PREFIX_LEN)) { + UIP_LOG("Error: UNSUPPORTED PREFIX LENGTH"); + return; + } + + /* check if addr does not already exist and find a free entry */ + for(i = 0; i < UIP_CONF_NETIF_MAX_ADDRESSES; ++i) { + if(uip_netif_physical_if.addresses[i].state == NOT_USED){ + /* + * Copying address + * If we are doing autoconf, ipaddr is a prefix, we copy the 128 bits + * of it, then overwrite the last 64 bits with the interface ID at + * next if statement. + * Otherwise ipaddr is an address, we just copy it + */ + uip_ipaddr_copy(&uip_netif_physical_if.addresses[i].ipaddr, ipaddr); + if(type == AUTOCONF) { + /* construct address from prefix and layer2 id */ + uip_netif_addr_autoconf_set(&uip_netif_physical_if.addresses[i].ipaddr, &uip_lladdr); + } + /* setting state, type */ + uip_netif_physical_if.addresses[i].state = TENTATIVE; + uip_netif_physical_if.addresses[i].type = type; + /* setting lifetime timer if lieftime is not infinite */ + if(vlifetime != 0) { + timer_set(&(uip_netif_physical_if.addresses[i].vlifetime), vlifetime); + uip_netif_physical_if.addresses[i].is_infinite = 0; + } else { + uip_netif_physical_if.addresses[i].is_infinite = 1; + } + PRINTF("Created new address"); + PRINT6ADDR(&uip_netif_physical_if.addresses[i].ipaddr); + PRINTF("for interface\n"); + + /* schedule DAD */ + uip_netif_sched_dad(&uip_netif_physical_if.addresses[i]); + + return; + } + } + + /* If we did not find space, log */ + UIP_LOG("ADDRESS LIST FULL"); + return; +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_addr_autoconf_set(uip_ipaddr_t *ipaddr, uip_lladdr_t *lladdr) +{ + /* We consider only links with IEEE EUI-64 identifier or + IEEE 48-bit MAC addresses */ +#if (UIP_LLADDR_LEN == 8) + memcpy(ipaddr->u8 + 8, lladdr, UIP_LLADDR_LEN); + ipaddr->u8[8] ^= 0x02; +#elif (UIP_LLADDR_LEN == 6) + memcpy(ipaddr->u8 + 8, lladdr, 3); + ipaddr->u8[11] = 0xff; + ipaddr->u8[12] = 0xfe; + memcpy(ipaddr->u8 + 13, lladdr + 3, 3); + ipaddr->u8[8] ^= 0x02; +#else + UIP_LOG("CAN NOT BUIL INTERFACE IDENTIFIER"); + UIP_LOG("THE STACK IS GOING TO SHUT DOWN"); + UIP_LOG("THE HOST WILL BE UNREACHABLE"); + exit(-1); +#endif +} + + +/*---------------------------------------------------------------------------*/ +u8_t +get_match_length(uip_ipaddr_t *src, uip_ipaddr_t *dst) +{ + u8_t j, k, x_or; + u8_t len = 0; + for(j = 0; j < 16; j ++) { + if(src->u8[j] == dst->u8[j]) { + len += 8; + } else { + x_or = src->u8[j] ^ dst->u8[j]; + for(k = 0; k < 8; k ++) { + if((x_or & 0x80) == 0){ + len ++; + x_or <<= 1; + } + else { + break; + } + } + break; + } + } + return len; +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_select_src(uip_ipaddr_t *src, uip_ipaddr_t *dst) +{ + u8_t best = 0; /* number of bit in common with best match*/ + u8_t n = 0; + u8_t index = 0; + + if(!uip_is_addr_link_local(dst) && !uip_is_addr_mcast(dst)) { + for(i = 1; i < UIP_CONF_NETIF_MAX_ADDRESSES; ++i) { + if(uip_netif_physical_if.addresses[i].state == PREFERRED){ + n = get_match_length(dst, &(uip_netif_physical_if.addresses[i].ipaddr)); + if(n >= best){ + best = n; + index = i; + } + } + } + } + + uip_ipaddr_copy(src, &(uip_netif_physical_if.addresses[index].ipaddr)); + return; +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_sched_dad(struct uip_netif_addr *ifaddr) +{ + if(ifaddr->state != TENTATIVE){ + UIP_LOG("DAD called with non tentative address"); + return; + } + /* + * check dad is not running + */ + if(dad_ifaddr != NULL){ + UIP_LOG("DAD already running"); + return; + } + /* + * Set the interface address that is going through DAD + */ + dad_ifaddr = ifaddr; + + PRINTF("Scheduling DAD for ipaddr:"); + PRINT6ADDR(&dad_ifaddr->ipaddr); + PRINTF("\n"); + + etimer_set(&uip_netif_timer_dad, random_rand()%(UIP_ND6_MAX_RTR_SOLICITATION_DELAY*CLOCK_SECOND)); +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_dad(void) +{ + /* + * check if dad is running + */ + if(dad_ifaddr == NULL){ + PRINTF("uip_netif_dad: DAD not running\n"); + return; + } + /* + * send dup_addr_detect_transmit NS for DAD + */ + if(dad_ns < uip_netif_physical_if.dup_addr_detect_transmit) { + uip_nd6_io_ns_output(NULL, NULL, &dad_ifaddr->ipaddr); + dad_ns++; + etimer_set(&uip_netif_timer_dad, uip_netif_physical_if.retrans_timer/1000*CLOCK_SECOND); + return; + } + /* + * If we arrive here it means DAD succeeded, otherwise the dad process + * would have been interrupted in nd6_dad_ns/na_input + */ + PRINTF("DAD succeeded for ipaddr:"); + PRINT6ADDR(&(dad_ifaddr->ipaddr)); + PRINTF("\n"); + + etimer_stop(&uip_netif_timer_dad); + dad_ifaddr->state = PREFERRED; + dad_ifaddr = NULL; + dad_ns = 0; + /* + * check if we need to run DAD on another address + * This is necessary because if you receive a RA, + * you might want to run DAD for several addresses + * Considering that we have structures to do DAD + * for one address only, we start DAD for the subsequent + * addresses here + */ + PRINTF("END of DAD\n"); + for(i = 0; i < UIP_CONF_NETIF_MAX_ADDRESSES; i ++){ + if(uip_netif_physical_if.addresses[i].state != NOT_USED){ + PRINTF("address %d : ",i); + PRINT6ADDR(&(uip_netif_physical_if.addresses[i].ipaddr)); + PRINTF("\n"); + } + if(uip_netif_physical_if.addresses[i].state == TENTATIVE){ + uip_netif_sched_dad(&uip_netif_physical_if.addresses[i]); + return; + } + } +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_dad_failed(uip_ipaddr_t *ipaddr) +{ + + UIP_LOG("DAD FAILED"); + UIP_LOG("THE STACK IS GOING TO SHUT DOWN"); + UIP_LOG("THE HOST WILL BE UNREACHABLE"); + + if(uip_ipaddr_cmp(&dad_ifaddr->ipaddr, ipaddr)){ + etimer_stop(&uip_netif_timer_dad); + dad_ifaddr->state = NOT_USED; + dad_ifaddr = NULL; + dad_ns = 0; + } + + exit(-1); +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_sched_send_rs(void) +{ + /* before a host sends an initial solicitation, it SHOULD delay the + transmission for a random amount of time between 0 and + UIP_ND6_MAX_RTR_SOLICITATION_DELAY. */ + if(rs_count == 0){ + etimer_set(&uip_netif_timer_rs, random_rand()%(UIP_ND6_MAX_RTR_SOLICITATION_DELAY*CLOCK_SECOND)); + PRINTF("Scheduling RS\n"); + } +} + + +/*---------------------------------------------------------------------------*/ +void +uip_netif_send_rs(void) +{ + if((uip_nd6_choose_defrouter() == NULL) && (rs_count < UIP_ND6_MAX_RTR_SOLICITATIONS)){ + //PRINTF("Sending RS %d\n", rs_count); + uip_nd6_io_rs_output(); + rs_count++; + etimer_set(&uip_netif_timer_rs, UIP_ND6_RTR_SOLICITATION_INTERVAL * CLOCK_SECOND); + } else { + PRINTF("Router found ? (boolean): %d\n", (uip_nd6_choose_defrouter() != NULL)); + etimer_stop(&uip_netif_timer_rs); + rs_count = 0; + } +} + +/** @} */ diff --git a/core/net/uip-netif.h b/core/net/uip-netif.h new file mode 100644 index 000000000..c2673a418 --- /dev/null +++ b/core/net/uip-netif.h @@ -0,0 +1,207 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * Network interface and stateless autoconfiguration (RFC 4862) + * \author Mathilde Durvy + * \author Julien Abeille + * + */ +/* + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + */ + +#ifndef __UIP_NETIF_H__ +#define __UIP_NETIF_H__ + +#include "net/uip-nd6.h" + +#ifndef UIP_CONF_NETIF_MAX_ADDRESSES +#define UIP_CONF_NETIF_MAX_ADDRESSES 2 +#endif /*UIP_CONF_NETIF_MAX_ADDRESSES*/ + +/** + * \brief Possible states for the address of an interface (RFC 4862 autoconf + + * NOT_USED + INFINITE) + */ +typedef enum { + NOT_USED = -1, + TENTATIVE = 0, + PREFERRED = 1, + DEPRECATED = 2, /* not needed if we don't use pliffetime in prefix struct */ +} uip_netif_state; + +/** + * \brief How the address was acquired: Autoconf, DHCP or manually + * + */ +typedef enum { + AUTOCONF = 1, + DHCP = 2, + MANUAL = 3 +} uip_netif_type; + +/** + * \brief An address structure for an interface + * + * Contains an ip address assigned to the interface, and its state. + */ +struct uip_netif_addr { + uip_ipaddr_t ipaddr; + uip_netif_state state; + struct timer vlifetime; + u8_t is_infinite; + uip_netif_type type; +}; + +/** \brief Interface structure (contains all the interface variables) */ +struct uip_netif { + u32_t link_mtu; + u8_t cur_hop_limit; + u32_t base_reachable_time; /* in msec */ + u32_t reachable_time; /* in msec */ + u32_t retrans_timer; /* in msec */ + u8_t dup_addr_detect_transmit; + /** Note: the link-local address is at position 0 */ + struct uip_netif_addr addresses[UIP_CONF_NETIF_MAX_ADDRESSES]; + uip_ipaddr_t solicited_node_mcastaddr; +}; + + +/*---------------------------------------------------------------------------*/ +extern struct uip_netif uip_netif_physical_if; +extern struct etimer uip_netif_timer_dad; +extern struct etimer uip_netif_timer_rs; +extern struct etimer uip_netif_timer_periodic; + +/*---------------------------------------------------------------------------*/ +/** \brief Initialize the network interfac and run stateless autoconf */ +void uip_netif_init(void); + + +/** + * \brief periodically check the state of the addresses. + */ +void uip_netif_periodic(void); + +/** + * \brief recompute random reachable timer + * \return the new value for timer + */ +u32_t uip_netif_compute_reachable_time(void); + +/** + * \brief Check if an unicast address is attached to my interface + * \param ipaddr an IP address to be checked + * \return 1 if address is attached to my interface (otherwise false) + */ +#define uip_netif_is_addr_my_unicast(a) (uip_netif_addr_lookup(a, 128, 0) != NULL) + +/** + * \brief Check if this is my solicited-node multicast address + * \param ipaddr an IP address to be checked + * \return 1 if the address is my solicited-node (otherwise false) + */ +u8_t uip_netif_is_addr_my_solicited(uip_ipaddr_t *ipaddr); + +/** + * \brief Autoconfigure and add an address corresponding to a specific prefix + * \param ipaddr the prefix if we are doing autoconf, the address for DHCP and manual + * \param length the prefix length if autoconf, 0 for DHCP and manual + * \param vlifetime valid lifetime of the address, 0 if the address has INFINITE lifetime, + * non 0 otherwise + * \param type AUTOCONF or MANUAL or DHCP + */ +void uip_netif_addr_add(uip_ipaddr_t *ipaddr, u8_t length, clock_time_t vlifetime, uip_netif_type type); + +/** + * \brief Set the 8 last bytes of the IP address + * based on the L2 identifier using autoconf + * \param *ipaddr the IP address to be completed with layer 2 info + * \param *lladdr the L2 address + */ +void uip_netif_addr_autoconf_set(uip_ipaddr_t *ipaddr, uip_lladdr_t *lladdr); + +/** + * \brief Lookup an address + * \param ipaddr the prefix if we are looking for an autoconf address, the address otherwise + * \param length the prefix length if we are looking for autoconf address, 128 otherwise + * \param type AUTOCONF or MANUAL or DHCP or 0 + * + * + * If we are looking for an AUTOCONFIGURED address, ipaddr is a prefix + * length is its length, type is AUTOCONF. + * Otherwise ipaddr is a full address, length must be 128, type is MANUAL + * or DHCP. + * Note: if we do not care about the type, type MUST be 0 + */ +struct uip_netif_addr * +uip_netif_addr_lookup(uip_ipaddr_t *ipaddr, u8_t length, uip_netif_type type); + +/** + * \brief Select the appropriate source address for a packet + * \param ipaddr the selected address (returned value) + * \param ipaddr the destination of the packet + */ +void uip_netif_select_src(uip_ipaddr_t *src, uip_ipaddr_t *dst); + +/** + * \brief Schedule DAD for a given address + * \param ifaddr the address for which schedule DAD + */ +void uip_netif_sched_dad(struct uip_netif_addr *ifaddr); + +/** + * \brief Perform DAD (i.e. Duplicate Address Detection) + */ +void uip_netif_dad(void); + +/** + * \brief DAD failed, should never happen! + * \param ipaddr the address for which DAD failed + */ +void uip_netif_dad_failed(uip_ipaddr_t *ipaddr); + +/** + * \brief Schedule the sending of RS + */ +void uip_netif_sched_send_rs(void); + +/** + * \brief Send up to MAX_RTR_SOLICITATION_DELAY RS separated by a delay of + * RTR_SOLICITATION_INTERVAL + */ +void uip_netif_send_rs(void); + +#endif + +/** @} */ + diff --git a/core/net/uip6.c b/core/net/uip6.c new file mode 100644 index 000000000..47f49cb75 --- /dev/null +++ b/core/net/uip6.c @@ -0,0 +1,2108 @@ +/** + * \addtogroup uip6 + * @{ + */ + +/** + * \file + * The uIP TCP/IPv6 stack code. + * + * \author Adam Dunkels + * \author Julien Abeille (IPv6 related code) + * \author Mathilde Durvy (IPv6 related code) + */ +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * $Id: uip6.c,v 1.1 2008/10/14 09:42:33 julienabeille Exp $ + * + */ + +/* + * uIP is a small implementation of the IP, UDP and TCP protocols (as + * well as some basic ICMP stuff). The implementation couples the IP, + * UDP, TCP and the application layers very tightly. To keep the size + * of the compiled code down, this code frequently uses the goto + * statement. While it would be possible to break the uip_process() + * function into many smaller functions, this would increase the code + * size because of the overhead of parameter passing and the fact that + * the optimier would not be as efficient. + * + * The principle is that we have a small buffer, called the uip_buf, + * in which the device driver puts an incoming packet. The TCP/IP + * stack parses the headers in the packet, and calls the + * application. If the remote host has sent data to the application, + * this data is present in the uip_buf and the application read the + * data from there. It is up to the application to put this data into + * a byte stream if needed. The application will not be fed with data + * that is out of sequence. + * + * If the application whishes to send data to the peer, it should put + * its data into the uip_buf. The uip_appdata pointer points to the + * first available byte. The TCP/IP stack will calculate the + * checksums, and fill in the necessary header fields and finally send + * the packet back to the peer. + */ + +//#include "net/uip.h" +//#include "net/uipopt.h" +#include "net/uip-icmp6.h" +#include "net/uip-nd6.h" +#include "net/uip-netif.h" +#include "raven-lcd.h" + +#include + +/*---------------------------------------------------------------------------*/ +/* For Debug, logging, statistics */ +/*---------------------------------------------------------------------------*/ + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",lladdr->addr[0], lladdr->addr[1], lladdr->addr[2], lladdr->addr[3],lladdr->addr[4], lladdr->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#endif + +#if UIP_LOGGING == 1 +#include +void uip_log(char *msg); +#define UIP_LOG(m) uip_log(m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#endif /* UIP_STATISTICS == 1 */ + + +/*---------------------------------------------------------------------------*/ +/** @{ \name Layer 2 variables */ +/*---------------------------------------------------------------------------*/ +/** Host L2 address */ +#if UIP_CONF_LL_802154 +uip_lladdr_t uip_lladdr; +#else /*UIP_CONF_LL_802154*/ +uip_lladdr_t uip_lladdr = {{0x00,0x06,0x98,0x00,0x02,0x32}}; +#endif /*UIP_CONF_LL_802154*/ +/** @} */ + +/*---------------------------------------------------------------------------*/ +/** @{ \name Layer 3 variables */ +/*---------------------------------------------------------------------------*/ +/** + * \brief Type of the next header in IPv6 header or extension headers + * + * Can be the next header field in the IPv6 header or in an extension header. + * When doing fragment reassembly, we must change the value of the next header + * field in the header before the fragmentation header, hence we need a pointer + * to this field. + */ +u8_t *uip_next_hdr; +/** \brief bitmap we use to record which IPv6 headers we have already seen */ +u8_t uip_ext_bitmap = 0; +/** + * \brief length of the extension headers read. updated each time we process + * a header + */ +u8_t uip_ext_len = 0; +/** \brief length of the header options read */ +u8_t uip_ext_opt_offset = 0; +/** @} */ + +/*---------------------------------------------------------------------------*/ +/* Buffers */ +/*---------------------------------------------------------------------------*/ +/** \name Buffer defines + * @{ + */ +#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0]) +#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN]) +#define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_TCP_BUF ((struct uip_tcp_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_EXT_BUF ((struct uip_ext_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_ROUTING_BUF ((struct uip_routing_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_FRAG_BUF ((struct uip_frag_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_HBHO_BUF ((struct uip_hbho_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_DESTO_BUF ((struct uip_desto_hdr *)&uip_buf[uip_l2_l3_hdr_len]) +#define UIP_EXT_HDR_OPT_BUF ((struct uip_ext_hdr_opt *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset]) +#define UIP_EXT_HDR_OPT_PADN_BUF ((struct uip_ext_hdr_opt_padn *)&uip_buf[uip_l2_l3_hdr_len + uip_ext_opt_offset]) +#define UIP_ICMP6_ERROR_BUF ((struct uip_icmp6_error *)&uip_buf[uip_l2_l3_icmp_hdr_len]) +/** @} */ +/** \name Buffer variables + * @{ + */ +/** Packet buffer for incoming and outgoing packets */ +#ifndef UIP_CONF_EXTERNAL_BUFFER +u8_t uip_buf[UIP_BUFSIZE + 2]; +#endif /* UIP_CONF_EXTERNAL_BUFFER */ + +/* The uip_appdata pointer points to application data. */ +void *uip_appdata; +/* The uip_appdata pointer points to the application data which is to be sent*/ +void *uip_sappdata; + +#if UIP_URGDATA > 0 +/* The uip_urgdata pointer points to urgent data (out-of-band data), if present */ +void *uip_urgdata; +u16_t uip_urglen, uip_surglen; +#endif /* UIP_URGDATA > 0 */ + +/* The uip_len is either 8 or 16 bits, depending on the maximum packet size.*/ +u16_t uip_len, uip_slen; +/** @} */ + +/*---------------------------------------------------------------------------*/ +/** @{ \name General variables */ +/*---------------------------------------------------------------------------*/ + +/* The uip_flags variable is used for communication between the TCP/IP stack +and the application program. */ +u8_t uip_flags; + +/* uip_conn always points to the current connection (set to NULL for UDP). */ +struct uip_conn *uip_conn; + +/* Temporary variables. */ +#if (UIP_TCP || UIP_UDP) +static u8_t c; +#endif + +#if UIP_ACTIVE_OPEN +/* Keeps track of the last port used for a new connection. */ +static u16_t lastport; +#endif /* UIP_ACTIVE_OPEN */ +/** @} */ + +/*---------------------------------------------------------------------------*/ +/* TCP */ +/*---------------------------------------------------------------------------*/ +/** \name TCP defines + *@{ + */ +/* Structures and definitions. */ +#define TCP_FIN 0x01 +#define TCP_SYN 0x02 +#define TCP_RST 0x04 +#define TCP_PSH 0x08 +#define TCP_ACK 0x10 +#define TCP_URG 0x20 +#define TCP_CTL 0x3f + +#define TCP_OPT_END 0 /* End of TCP options list */ +#define TCP_OPT_NOOP 1 /* "No-operation" TCP option */ +#define TCP_OPT_MSS 2 /* Maximum segment size TCP option */ + +#define TCP_OPT_MSS_LEN 4 /* Length of TCP MSS option. */ +/** @} */ +/** \name TCP variables + *@{ + */ +#if UIP_TCP +/* The uip_conns array holds all TCP connections. */ +struct uip_conn uip_conns[UIP_CONNS]; + +/* The uip_listenports list all currently listning ports. */ +u16_t uip_listenports[UIP_LISTENPORTS]; + +/* The iss variable is used for the TCP initial sequence number. */ +static u8_t iss[4]; + +/* Temporary variables. */ +u8_t uip_acc32[4]; +static u8_t opt; +static u16_t tmp16; +#endif /* UIP_TCP */ +/** @} */ + +/*---------------------------------------------------------------------------*/ +/** @{ \name UDP variables */ +/*---------------------------------------------------------------------------*/ +#if UIP_UDP +struct uip_udp_conn *uip_udp_conn; +struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS]; +#endif /* UIP_UDP */ +/** @} */ + + +/*---------------------------------------------------------------------------*/ +/** @{ \name ICMPv6 variables */ +/*---------------------------------------------------------------------------*/ +#if UIP_CONF_ICMP6 +/** single possible icmpv6 "connection" */ +struct uip_icmp6_conn uip_icmp6_conns; +#endif /*UIP_CONF_ICMP6*/ + +/*---------------------------------------------------------------------------*/ +/* Functions */ +/*---------------------------------------------------------------------------*/ +#if (!UIP_ARCH_ADD32 && UIP_TCP) +void +uip_add32(u8_t *op32, u16_t op16) +{ + uip_acc32[3] = op32[3] + (op16 & 0xff); + uip_acc32[2] = op32[2] + (op16 >> 8); + uip_acc32[1] = op32[1]; + uip_acc32[0] = op32[0]; + + if(uip_acc32[2] < (op16 >> 8)) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + + + if(uip_acc32[3] < (op16 & 0xff)) { + ++uip_acc32[2]; + if(uip_acc32[2] == 0) { + ++uip_acc32[1]; + if(uip_acc32[1] == 0) { + ++uip_acc32[0]; + } + } + } +} + +#endif /* UIP_ARCH_ADD32 && UIP_TCP */ + +#if ! UIP_ARCH_CHKSUM +/*---------------------------------------------------------------------------*/ +static u16_t +chksum(u16_t sum, const u8_t *data, u16_t len) +{ + u16_t t; + const u8_t *dataptr; + const u8_t *last_byte; + + dataptr = data; + last_byte = data + len - 1; + + while(dataptr < last_byte) { /* At least two more bytes */ + t = (dataptr[0] << 8) + dataptr[1]; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + dataptr += 2; + } + + if(dataptr == last_byte) { + t = (dataptr[0] << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + /* Return sum in host byte order. */ + return sum; +} +/*---------------------------------------------------------------------------*/ +u16_t +uip_chksum(u16_t *data, u16_t len) +{ + return htons(chksum(0, (u8_t *)data, len)); +} +/*---------------------------------------------------------------------------*/ +#ifndef UIP_ARCH_IPCHKSUM +u16_t +uip_ipchksum(void) +{ + u16_t sum; + + sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN); + PRINTF("uip_ipchksum: sum 0x%04x\n", sum); + return (sum == 0) ? 0xffff : htons(sum); +} +#endif +/*---------------------------------------------------------------------------*/ +static u16_t +upper_layer_chksum(u8_t proto) +{ + u16_t upper_layer_len; + u16_t sum; + + upper_layer_len = (((u16_t)(UIP_IP_BUF->len[0]) << 8) + UIP_IP_BUF->len[1] - uip_ext_len) ; + + /* First sum pseudoheader. */ + /* IP protocol and length fields. This addition cannot carry. */ + sum = upper_layer_len + proto; + /* Sum IP source and destination addresses. */ + sum = chksum(sum, (u8_t *)&UIP_IP_BUF->srcipaddr, 2 * sizeof(uip_ipaddr_t)); + + /* Sum TCP header and data. */ + sum = chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN + uip_ext_len], + upper_layer_len); + + return (sum == 0) ? 0xffff : htons(sum); +} +/*---------------------------------------------------------------------------*/ +u16_t +uip_icmp6chksum(void) +{ + return upper_layer_chksum(UIP_PROTO_ICMP6); + +} +/*---------------------------------------------------------------------------*/ +#if UIP_TCP +u16_t +uip_tcpchksum(void) +{ + return upper_layer_chksum(UIP_PROTO_TCP); +} +#endif /* UIP_TCP */ +/*---------------------------------------------------------------------------*/ +#if UIP_UDP && UIP_UDP_CHECKSUMS +u16_t +uip_udpchksum(void) +{ + return upper_layer_chksum(UIP_PROTO_UDP); +} +#endif /* UIP_UDP && UIP_UDP_CHECKSUMS */ +#endif /* UIP_ARCH_CHKSUM */ +/*---------------------------------------------------------------------------*/ +void +uip_init(void) +{ + + uip_netif_init(); + uip_nd6_init(); + +#if UIP_TCP + for(c = 0; c < UIP_LISTENPORTS; ++c) { + uip_listenports[c] = 0; + } + for(c = 0; c < UIP_CONNS; ++c) { + uip_conns[c].tcpstateflags = UIP_CLOSED; + } +#endif /* UIP_TCP */ + +#if UIP_ACTIVE_OPEN + lastport = 1024; +#endif /* UIP_ACTIVE_OPEN */ + +#if UIP_UDP + for(c = 0; c < UIP_UDP_CONNS; ++c) { + uip_udp_conns[c].lport = 0; + } +#endif /* UIP_UDP */ +} + + +/*---------------------------------------------------------------------------*/ +#if UIP_TCP && UIP_ACTIVE_OPEN +struct uip_conn * +uip_connect(uip_ipaddr_t *ripaddr, u16_t rport) +{ + register struct uip_conn *conn, *cconn; + + /* Find an unused local port. */ + again: + ++lastport; + + if(lastport >= 32000) { + lastport = 4096; + } + + /* Check if this port is already in use, and if so try to find + another one. */ + for(c = 0; c < UIP_CONNS; ++c) { + conn = &uip_conns[c]; + if(conn->tcpstateflags != UIP_CLOSED && + conn->lport == htons(lastport)) { + goto again; + } + } + + conn = 0; + for(c = 0; c < UIP_CONNS; ++c) { + cconn = &uip_conns[c]; + if(cconn->tcpstateflags == UIP_CLOSED) { + conn = cconn; + break; + } + if(cconn->tcpstateflags == UIP_TIME_WAIT) { + if(conn == 0 || + cconn->timer > conn->timer) { + conn = cconn; + } + } + } + + if(conn == 0) { + return 0; + } + + conn->tcpstateflags = UIP_SYN_SENT; + + conn->snd_nxt[0] = iss[0]; + conn->snd_nxt[1] = iss[1]; + conn->snd_nxt[2] = iss[2]; + conn->snd_nxt[3] = iss[3]; + + conn->initialmss = conn->mss = UIP_TCP_MSS; + + conn->len = 1; /* TCP length of the SYN is one. */ + conn->nrtx = 0; + conn->timer = 1; /* Send the SYN next time around. */ + conn->rto = UIP_RTO; + conn->sa = 0; + conn->sv = 16; /* Initial value of the RTT variance. */ + conn->lport = htons(lastport); + conn->rport = rport; + uip_ipaddr_copy(&conn->ripaddr, ripaddr); + + return conn; +} +#endif /* UIP_TCP && UIP_ACTIVE_OPEN */ + + +/*---------------------------------------------------------------------------*/ +#if UIP_UDP +struct uip_udp_conn * +uip_udp_new(const uip_ipaddr_t *ripaddr, u16_t rport) +{ + register struct uip_udp_conn *conn; + + /* Find an unused local port. */ + again: + ++lastport; + + if(lastport >= 32000) { + lastport = 4096; + } + + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(uip_udp_conns[c].lport == htons(lastport)) { + goto again; + } + } + + + conn = 0; + for(c = 0; c < UIP_UDP_CONNS; ++c) { + if(uip_udp_conns[c].lport == 0) { + conn = &uip_udp_conns[c]; + break; + } + } + + if(conn == 0) { + return 0; + } + + conn->lport = HTONS(lastport); + conn->rport = rport; + if(ripaddr == NULL) { + memset(&conn->ripaddr, 0, sizeof(uip_ipaddr_t)); + } else { + uip_ipaddr_copy(&conn->ripaddr, ripaddr); + } + conn->ttl = uip_netif_physical_if.cur_hop_limit; + + return conn; +} +#endif /* UIP_UDP */ + + +/*---------------------------------------------------------------------------*/ +#if UIP_TCP +void +uip_unlisten(u16_t port) +{ + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(uip_listenports[c] == port) { + uip_listenports[c] = 0; + return; + } + } +} + + +/*---------------------------------------------------------------------------*/ +void +uip_listen(u16_t port) +{ + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(uip_listenports[c] == 0) { + uip_listenports[c] = port; + return; + } + } +} +#endif +/*---------------------------------------------------------------------------*/ + + +#if UIP_CONF_IPV6_REASSEMBLY +#define UIP_REASS_BUFSIZE (UIP_BUFSIZE - UIP_LLH_LEN) + +static u8_t uip_reassbuf[UIP_REASS_BUFSIZE]; + +static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; +/*the first byte of an IP fragment is aligned on an 8-byte boundary */ + +static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01}; +static u16_t uip_reasslen; +static u8_t uip_reassflags; + +#define UIP_REASS_FLAG_LASTFRAG 0x01 +#define UIP_REASS_FLAG_FIRSTFRAG 0x02 +#define UIP_REASS_FLAG_ERROR_MSG 0x04 + + +/* + * See RFC 2460 for a description of fragmentation in IPv6 + * A typical Ipv6 fragment + * +------------------+--------+--------------+ + * | Unfragmentable |Fragment| first | + * | Part | Header | fragment | + * +------------------+--------+--------------+ + */ + + +struct etimer uip_reass_timer; /* timer for reassembly */ +u8_t uip_reass_on; /* equal to 1 if we are currently reassembling a packet */ + +static u32_t uip_id; /* For every packet that is to be fragmented, the source + node generates an Identification value that is present + in all the fragments */ +#define IP_MF 0x0001 + +static u16_t +uip_reass(void) +{ + u16_t offset=0; + u16_t len; + u16_t i; + + /* If ip_reasstmr is zero, no packet is present in the buffer */ + /* We first write the unfragmentable part of IP header into the reassembly + buffer. The reset the other reassembly variables. */ + if(uip_reass_on == 0) { + PRINTF("Starting reassembly\n"); + memcpy(FBUF, UIP_IP_BUF, uip_ext_len + UIP_IPH_LEN); + /* temporary in case we do not receive the fragment with offset 0 first */ + etimer_set(&uip_reass_timer, UIP_REASS_MAXAGE*CLOCK_SECOND); + uip_reass_on = 1; + uip_reassflags = 0; + uip_id = UIP_FRAG_BUF->id; + /* Clear the bitmap. */ + memset(uip_reassbitmap, 0, sizeof(uip_reassbitmap)); + } + /* + * Check if the incoming fragment matches the one currently present + * in the reasembly buffer. If so, we proceed with copying the fragment + * into the buffer. + */ + if(uip_ipaddr_cmp(&FBUF->srcipaddr, &UIP_IP_BUF->srcipaddr) && + uip_ipaddr_cmp(&FBUF->destipaddr, &UIP_IP_BUF->destipaddr) && + UIP_FRAG_BUF->id == uip_id) { + len = uip_len - uip_ext_len - UIP_IPH_LEN - UIP_FRAGH_LEN; + offset = (ntohs(UIP_FRAG_BUF->offsetresmore) & 0xfff8); + /* in byte, originaly in multiple of 8 bytes*/ + PRINTF("len %d\n", len); + PRINTF("offset %d\n", offset); + if(offset == 0){ + uip_reassflags |= UIP_REASS_FLAG_FIRSTFRAG; + /* + * The Next Header field of the last header of the Unfragmentable + * Part is obtained from the Next Header field of the first + * fragment's Fragment header. + */ + *uip_next_hdr = UIP_FRAG_BUF->next; + memcpy(FBUF, UIP_IP_BUF, uip_ext_len + UIP_IPH_LEN); + PRINTF("src "); + PRINT6ADDR(&FBUF->srcipaddr); + PRINTF("dest "); + PRINT6ADDR(&FBUF->destipaddr); + PRINTF("next %d\n", UIP_IP_BUF->proto); + + } + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if(offset > UIP_REASS_BUFSIZE || + offset + len > UIP_REASS_BUFSIZE) { + uip_reass_on = 0; + etimer_stop(&uip_reass_timer); + return 0; + } + + /* If this fragment has the More Fragments flag set to zero, it is the + last fragment*/ + if((ntohs(UIP_FRAG_BUF->offsetresmore) & IP_MF) == 0) { + uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; + /*calculate the size of the entire packet*/ + uip_reasslen = offset + len; + PRINTF("LAST FRAGMENT reasslen %d\n", uip_reasslen); + } else { + /* If len is not a multiple of 8 octets and the M flag of that fragment + is 1, then that fragment must be discarded and an ICMP Parameter + Problem, Code 0, message should be sent to the source of the fragment, + pointing to the Payload Length field of the fragment packet. */ + if(len % 8 != 0){ + uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, 4); + uip_reassflags |= UIP_REASS_FLAG_ERROR_MSG; + /* not clear if we should interrupt reassembly, but it seems so from + the conformance tests */ + uip_reass_on = 0; + etimer_stop(&uip_reass_timer); + return uip_len; + } + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + memcpy((void *)FBUF + UIP_IPH_LEN + uip_ext_len + offset, + (void *)UIP_FRAG_BUF + UIP_FRAGH_LEN, len); + + /* Update the bitmap. */ + if(offset >> 6 == (offset + len) >> 6) { + uip_reassbitmap[offset >> 6] |= + bitmap_bits[(offset >> 3) & 7] & + ~bitmap_bits[((offset + len) >> 3) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + uip_reassbitmap[offset >> 6] |= bitmap_bits[(offset >> 3) & 7]; + + for(i = (1 + (offset >> 6)); i < ((offset + len) >> 6); ++i) { + uip_reassbitmap[i] = 0xff; + } + uip_reassbitmap[(offset + len) >> 6] |= + ~bitmap_bits[((offset + len) >> 3) & 7]; + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + + if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + for(i = 0; i < (uip_reasslen >> 6); ++i) { + if(uip_reassbitmap[i] != 0xff) { + return 0; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + if(uip_reassbitmap[uip_reasslen >> 6] != + (u8_t)~bitmap_bits[(uip_reasslen >> 3) & 7]) { + return 0; + } + + /* If we have come this far, we have a full packet in the + buffer, so we copy it to uip_buf. We also reset the timer. */ + uip_reass_on = 0; + etimer_stop(&uip_reass_timer); + + uip_reasslen += UIP_IPH_LEN + uip_ext_len; + memcpy(UIP_IP_BUF, FBUF, uip_reasslen); + UIP_IP_BUF->len[0] = ((uip_reasslen - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_reasslen - UIP_IPH_LEN) & 0xff); + PRINTF("REASSEMBLED PAQUET %d (%d)\n", uip_reasslen, + (UIP_IP_BUF->len[0] << 8) | UIP_IP_BUF->len[1]); + + return uip_reasslen; + + } + } else { + PRINTF("Already reassembling another paquet\n"); + } + return 0; +} + +void +uip_reass_over(void) +{ + /* to late, we abandon the reassembly of the packet */ + + uip_reass_on = 0; + etimer_stop(&uip_reass_timer); + + if(uip_reassflags & UIP_REASS_FLAG_FIRSTFRAG){ + PRINTF("FRAG INTERRUPTED TOO LATE\n"); + /* If the first fragment has been received, an ICMP Time Exceeded + -- Fragment Reassembly Time Exceeded message should be sent to the + source of that fragment. */ + /** \note + * We don't have a complete packet to put in the error message. + * We could include the first fragment but since its not mandated by + * any RFC, we decided not to include it as it reduces the size of + * the packet. + */ + uip_len = 0; + uip_ext_len = 0; + memcpy(UIP_IP_BUF, FBUF, UIP_IPH_LEN); /* copy the header for src + and dest address*/ + uip_icmp6_error_output(ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY, 0); + + UIP_STAT(++uip_stat.ip.sent); + uip_flags = 0; + } +} + +#endif /* UIP_CONF_IPV6_REASSEMBLY */ + +/*---------------------------------------------------------------------------*/ +#if UIP_TCP +static void +uip_add_rcv_nxt(u16_t n) +{ + uip_add32(uip_conn->rcv_nxt, n); + uip_conn->rcv_nxt[0] = uip_acc32[0]; + uip_conn->rcv_nxt[1] = uip_acc32[1]; + uip_conn->rcv_nxt[2] = uip_acc32[2]; + uip_conn->rcv_nxt[3] = uip_acc32[3]; +} +#endif +/*---------------------------------------------------------------------------*/ + +/** + * \brief Process the options in Destination and Hop By Hop extension headers + */ +static u8_t +ext_hdr_options_process() { + /* + * Length field in the extension header: length of th eheader in units of + * 8 bytes, excluding the first 8 bytes + * length field in an option : the length of data in the option + */ + uip_ext_opt_offset = 2; + while(uip_ext_opt_offset < ((UIP_EXT_BUF->len << 3) + 8)) { + switch (UIP_EXT_HDR_OPT_BUF->type) { + /* + * for now we do not support any options except padding ones + * PAD1 does not make sense as the header must be 8bytes aligned, + * hence we can only have + */ + case UIP_EXT_HDR_OPT_PAD1: + PRINTF("Processing PAD1 option\n"); + uip_ext_opt_offset += 1; + break; + case UIP_EXT_HDR_OPT_PADN: + PRINTF("Processing PADN option\n"); + uip_ext_opt_offset += UIP_EXT_HDR_OPT_PADN_BUF->opt_len + 2; + break; + default: + /* + * check the two highest order bits of the option + * - 00 skip over this option and continue processing the header. + * - 01 discard the packet. + * - 10 discard the packet and, regardless of whether or not the + * packet's Destination Address was a multicast address, send an + * ICMP Parameter Problem, Code 2, message to the packet's + * Source Address, pointing to the unrecognized Option Type. + * - 11 discard the packet and, only if the packet's Destination + * Address was not a multicast address, send an ICMP Parameter + * Problem, Code 2, message to the packet's Source Address, + * pointing to the unrecognized Option Type. + */ + PRINTF("MSB %x\n", UIP_EXT_HDR_OPT_BUF->type); + switch(UIP_EXT_HDR_OPT_BUF->type & 0xC0) { + case 0: + break; + case 0x40: + return 1; + case 0xC0: + if(uip_is_addr_mcast(&UIP_IP_BUF->destipaddr)) { + return 1; + } + case 0x80: + uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_OPTION, + (u32_t)UIP_IPH_LEN + uip_ext_len + uip_ext_opt_offset); + return 2; + } + /* in the cases were we did not discard, update ext_opt* */ + uip_ext_opt_offset += UIP_EXT_HDR_OPT_BUF->len + 2; + break; + } + } + return 0; +} + + +/*---------------------------------------------------------------------------*/ +void +uip_process(u8_t flag) +{ +#if UIP_TCP + register struct uip_conn *uip_connr = uip_conn; +#endif /* UIP_TCP */ +#if UIP_UDP + if(flag == UIP_UDP_SEND_CONN) { + goto udp_send; + } +#endif /* UIP_UDP */ + uip_sappdata = uip_appdata = &uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN]; + + /* Check if we were invoked because of a poll request for a + particular connection. */ + if(flag == UIP_POLL_REQUEST) { +#if UIP_TCP + if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED && + !uip_outstanding(uip_connr)) { + uip_flags = UIP_POLL; + UIP_APPCALL(); + goto appsend; + } + goto drop; +#endif /* UIP_TCP */ + /* Check if we were invoked because of the perodic timer fireing. */ + } else if(flag == UIP_TIMER) { + /* Reset the length variables. */ +#if UIP_TCP + uip_len = 0; + uip_slen = 0; + + /* Increase the initial sequence number. */ + if(++iss[3] == 0) { + if(++iss[2] == 0) { + if(++iss[1] == 0) { + ++iss[0]; + } + } + } + + /* + * Check if the connection is in a state in which we simply wait + * for the connection to time out. If so, we increase the + * connection's timer and remove the connection if it times + * out. + */ + if(uip_connr->tcpstateflags == UIP_TIME_WAIT || + uip_connr->tcpstateflags == UIP_FIN_WAIT_2) { + ++(uip_connr->timer); + if(uip_connr->timer == UIP_TIME_WAIT_TIMEOUT) { + uip_connr->tcpstateflags = UIP_CLOSED; + } + } else if(uip_connr->tcpstateflags != UIP_CLOSED) { + /* + * If the connection has outstanding data, we increase the + * connection's timer and see if it has reached the RTO value + * in which case we retransmit. + */ + if(uip_outstanding(uip_connr)) { + if(uip_connr->timer-- == 0) { + if(uip_connr->nrtx == UIP_MAXRTX || + ((uip_connr->tcpstateflags == UIP_SYN_SENT || + uip_connr->tcpstateflags == UIP_SYN_RCVD) && + uip_connr->nrtx == UIP_MAXSYNRTX)) { + uip_connr->tcpstateflags = UIP_CLOSED; + + /* + * We call UIP_APPCALL() with uip_flags set to + * UIP_TIMEDOUT to inform the application that the + * connection has timed out. + */ + uip_flags = UIP_TIMEDOUT; + UIP_APPCALL(); + + /* We also send a reset packet to the remote host. */ + UIP_TCP_BUF->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + /* Exponential backoff. */ + uip_connr->timer = UIP_RTO << (uip_connr->nrtx > 4? + 4: + uip_connr->nrtx); + ++(uip_connr->nrtx); + + /* + * Ok, so we need to retransmit. We do this differently + * depending on which state we are in. In ESTABLISHED, we + * call upon the application so that it may prepare the + * data for the retransmit. In SYN_RCVD, we resend the + * SYNACK that we sent earlier and in LAST_ACK we have to + * retransmit our FINACK. + */ + UIP_STAT(++uip_stat.tcp.rexmit); + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + case UIP_SYN_RCVD: + /* In the SYN_RCVD state, we should retransmit our SYNACK. */ + goto tcp_send_synack; + +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In the SYN_SENT state, we retransmit out SYN. */ + UIP_TCP_BUF->flags = 0; + goto tcp_send_syn; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* + * In the ESTABLISHED state, we call upon the application + * to do the actual retransmit after which we jump into + * the code for sending out the packet (the apprexmit + * label). + */ + uip_flags = UIP_REXMIT; + UIP_APPCALL(); + goto apprexmit; + + case UIP_FIN_WAIT_1: + case UIP_CLOSING: + case UIP_LAST_ACK: + /* In all these states we should retransmit a FINACK. */ + goto tcp_send_finack; + } + } + } else if((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) { + /* + * If there was no need for a retransmission, we poll the + * application for new data. + */ + uip_flags = UIP_POLL; + UIP_APPCALL(); + goto appsend; + } + } + goto drop; +#endif /* UIP_TCP */ + } +#if UIP_UDP + if(flag == UIP_UDP_TIMER) { + if(uip_udp_conn->lport != 0) { + uip_conn = NULL; + uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + uip_len = uip_slen = 0; + uip_flags = UIP_POLL; + UIP_UDP_APPCALL(); + goto udp_send; + } else { + goto drop; + } + } +#endif /* UIP_UDP */ + + /* This is where the input processing starts. */ + UIP_STAT(++uip_stat.ip.recv); + + /* Start of IP input header processing code. */ + + /* Check validity of the IP header. */ + if((UIP_IP_BUF->vtc & 0xf0) != 0x60) { /* IP version and header length. */ + UIP_STAT(++uip_stat.ip.drop); + UIP_STAT(++uip_stat.ip.vhlerr); + UIP_LOG("ipv6: invalid version."); + goto drop; + } + + /* + * Check the size of the packet. If the size reported to us in + * uip_len is smaller the size reported in the IP header, we assume + * that the packet has been corrupted in transit. If the size of + * uip_len is larger than the size reported in the IP packet header, + * the packet has been padded and we set uip_len to the correct + * value.. + */ + + if((UIP_IP_BUF->len[0] << 8) + UIP_IP_BUF->len[1] <= uip_len) { + uip_len = (UIP_IP_BUF->len[0] << 8) + UIP_IP_BUF->len[1] + UIP_IPH_LEN; + /* + * The length reported in the IPv6 header is the + * length of the payload that follows the + * header. However, uIP uses the uip_len variable + * for holding the size of the entire packet, + * including the IP header. For IPv4 this is not a + * problem as the length field in the IPv4 header + * contains the length of the entire packet. But + * for IPv6 we need to add the size of the IPv6 + * header (40 bytes). + */ + } else { + UIP_LOG("ip: packet shorter than reported in IP header."); + goto drop; + } + + + if(uip_is_addr_mcast(&UIP_IP_BUF->srcipaddr)){ + UIP_STAT(++uip_stat.ip.drop); + goto drop; + } + + if(!uip_netif_is_addr_my_unicast(&UIP_IP_BUF->destipaddr) && + !uip_netif_is_addr_my_solicited(&UIP_IP_BUF->destipaddr) && + !uip_is_addr_linklocal_allnodes_mcast(&UIP_IP_BUF->destipaddr)){ + UIP_STAT(++uip_stat.ip.drop); + goto drop; + } + + + /* + * Next header field processing. In IPv6, we can have extension headers, + * they are processed here + */ + uip_next_hdr = &UIP_IP_BUF->proto; + uip_ext_len = 0; + uip_ext_bitmap = 0; + while(1) { + switch(*uip_next_hdr){ +#if UIP_TCP + case UIP_PROTO_TCP: + /* TCP, for both IPv4 and IPv6 */ + goto tcp_input; +#endif /* UIP_TCP */ +#if UIP_UDP + case UIP_PROTO_UDP: + /* UDP, for both IPv4 and IPv6 */ + goto udp_input; +#endif /* UIP_UDP */ + case UIP_PROTO_ICMP6: + /* ICMPv6 */ + goto icmp6_input; + case UIP_PROTO_HBHO: + PRINTF("Processing hbh header\n"); + /* Hop by hop option header */ +#if UIP_CONF_IPV6_CHECKS + /* Hop by hop option header. If we saw one HBH already, drop */ + if(uip_ext_bitmap & UIP_EXT_HDR_BITMAP_HBHO) { + goto bad_hdr; + } else { + uip_ext_bitmap |= UIP_EXT_HDR_BITMAP_HBHO; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + switch(ext_hdr_options_process()) { + case 0: + /*continue*/ + uip_next_hdr = &UIP_EXT_BUF->next; + uip_ext_len += (UIP_EXT_BUF->len << 3) + 8; + break; + case 1: + /*silently discard*/ + goto drop; + case 2: + /* send icmp error message (created in ext_hdr_options_process) + * and discard*/ + goto send; + } + break; + case UIP_PROTO_DESTO: +#if UIP_CONF_IPV6_CHECKS + /* Destination option header. if we saw two already, drop */ + PRINTF("Processing desto header\n"); + if(uip_ext_bitmap & UIP_EXT_HDR_BITMAP_DESTO1) { + if(uip_ext_bitmap & UIP_EXT_HDR_BITMAP_DESTO2) { + goto bad_hdr; + } else{ + uip_ext_bitmap |= UIP_EXT_HDR_BITMAP_DESTO2; + } + } else { + uip_ext_bitmap |= UIP_EXT_HDR_BITMAP_DESTO1; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + switch(ext_hdr_options_process()) { + case 0: + /*continue*/ + uip_next_hdr = &UIP_EXT_BUF->next; + uip_ext_len += (UIP_EXT_BUF->len << 3) + 8; + break; + case 1: + /*silently discard*/ + goto drop; + case 2: + /* send icmp error message (created in ext_hdr_options_process) + * and discard*/ + goto send; + } + break; + case UIP_PROTO_ROUTING: +#if UIP_CONF_IPV6_CHECKS + /* Routing header. If we saw one already, drop */ + if(uip_ext_bitmap & UIP_EXT_HDR_BITMAP_ROUTING) { + goto bad_hdr; + } else { + uip_ext_bitmap |= UIP_EXT_HDR_BITMAP_ROUTING; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + /* + * Routing Header length field is in units of 8 bytes, excluding + * As per RFC2460 section 4.4, if routing type is unrecognized: + * if segments left = 0, ignore the header + * if segments left > 0, discard packet and send icmp error pointing + * to the routing type + */ + + PRINTF("Processing Routing header\n"); + if(UIP_ROUTING_BUF->seg_left > 0) { + uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, UIP_IPH_LEN + uip_ext_len + 2); + UIP_STAT(++uip_stat.ip.drop); + UIP_LOG("ip6: unrecognized routing type"); + goto send; + } + uip_next_hdr = &UIP_EXT_BUF->next; + uip_ext_len += (UIP_EXT_BUF->len << 3) + 8; + break; + case UIP_PROTO_FRAG: + /* Fragmentation header:call the reassembly function, then leave */ +#if UIP_CONF_IPV6_REASSEMBLY + PRINTF("Processing frag header\n"); + uip_len = uip_reass(); + if(uip_len == 0) { + goto drop; + } + if(uip_reassflags & UIP_REASS_FLAG_ERROR_MSG){ + /* we are not done with reassembly, this is an error message */ + goto send; + } + /*packet is reassembled, reset the next hdr to the beginning + of the IP header and restart the parsing of the reassembled pkt*/ + PRINTF("Processing reassembled packet\n"); + uip_ext_len = 0; + uip_ext_bitmap = 0; + uip_next_hdr = &UIP_IP_BUF->proto; + break; +#else /* UIP_CONF_IPV6_REASSEMBLY */ + UIP_STAT(++uip_stat.ip.drop); + UIP_STAT(++uip_stat.ip.fragerr); + UIP_LOG("ip: fragment dropped."); + goto drop; +#endif /* UIP_CONF_IPV6_REASSEMBLY */ + case UIP_PROTO_NONE: + goto drop; + default: + goto bad_hdr; + } + } + bad_hdr: + /* + * RFC 2460 send error message parameterr problem, code unrecognized + * next header, pointing to the next header field + */ + uip_icmp6_error_output(ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER, (u32_t)((void *)uip_next_hdr - (void *)UIP_IP_BUF)); + UIP_STAT(++uip_stat.ip.drop); + UIP_STAT(++uip_stat.ip.protoerr); + UIP_LOG("ip6: unrecognized header"); + goto send; + /* End of headers processing */ + + icmp6_input: + /* This is IPv6 ICMPv6 processing code. */ + PRINTF("icmp6_input: length %d\n", uip_len); + +#if UIP_CONF_IPV6_CHECKS + /* Compute and check the ICMP header checksum */ + if(uip_icmp6chksum() != 0xffff) { + UIP_STAT(++uip_stat.icmp.drop); + UIP_STAT(++uip_stat.icmp.chkerr); + UIP_LOG("icmpv6: bad checksum."); + goto drop; + } +#endif /*UIP_CONF_IPV6_CHECKS*/ + + UIP_STAT(++uip_stat.icmp.recv); + /* + * Here we process incoming ICMPv6 packets + * For echo request, we send echo reply + * For ND pkts, we call the appropriate function in uip-nd6-io.c + * We do not treat Error messages for now + * If no pkt is to be sent as an answer to the incoming one, we + * "goto drop". Else we just break; then at the after the "switch" + * we "goto send" + */ +#if UIP_CONF_ICMP6 + UIP_ICMP6_APPCALL(UIP_ICMP_BUF->type); +#endif /*UIP_CONF_ICMP6*/ + + switch(UIP_ICMP_BUF->type) { + case ICMP6_NS: + uip_nd6_io_ns_input(); + break; + case ICMP6_NA: + uip_nd6_io_na_input(); + break; + case ICMP6_RA: + uip_nd6_io_ra_input(); + break; + case ICMP6_ECHO_REQUEST: + uip_icmp6_echo_request_input(); + break; + case ICMP6_ECHO_REPLY: + /** \note We don't implement any application callback for now */ + PRINTF("Received an icmp6 echo reply\n"); + UIP_STAT(++uip_stat.icmp.recv); + uip_len = 0; + break; + default: + PRINTF("Unknown icmp6 message type %d\n", UIP_ICMP_BUF->type); + UIP_STAT(++uip_stat.icmp.drop); + UIP_STAT(++uip_stat.icmp.typeerr); + UIP_LOG("icmp6: unknown ICMP message."); + uip_len = 0; + break; + } + + if(uip_len > 0) { + goto send; + } else { + goto drop; + } + /* End of IPv6 ICMP processing. */ + + +#if UIP_UDP + /* UDP input processing. */ + udp_input: + PRINTF("Receiving UDP packet\n"); + UIP_STAT(++uip_stat.udp.recv); + + /* UDP processing is really just a hack. We don't do anything to the + UDP/IP headers, but let the UDP application do all the hard + work. If the application sets uip_slen, it has a packet to + send. */ +#if UIP_UDP_CHECKSUMS + uip_len = uip_len - UIP_IPUDPH_LEN; + uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + if(UIP_UDP_BUF->udpchksum != 0 && uip_udpchksum() != 0xffff) { + UIP_STAT(++uip_stat.udp.drop); + UIP_STAT(++uip_stat.udp.chkerr); + UIP_LOG("udp: bad checksum."); + goto drop; + } +#else /* UIP_UDP_CHECKSUMS */ + uip_len = uip_len - UIP_IPUDPH_LEN; +#endif /* UIP_UDP_CHECKSUMS */ + + /* Demultiplex this UDP packet between the UDP "connections". */ + for(uip_udp_conn = &uip_udp_conns[0]; + uip_udp_conn < &uip_udp_conns[UIP_UDP_CONNS]; + ++uip_udp_conn) { + /* If the local UDP port is non-zero, the connection is considered + to be used. If so, the local port number is checked against the + destination port number in the received packet. If the two port + numbers match, the remote port number is checked if the + connection is bound to a remote port. Finally, if the + connection is bound to a remote IP address, the source IP + address of the packet is checked. */ + if(uip_udp_conn->lport != 0 && + UIP_UDP_BUF->destport == uip_udp_conn->lport && + (uip_udp_conn->rport == 0 || + UIP_UDP_BUF->srcport == uip_udp_conn->rport) && + (uip_is_addr_unspecified(&uip_udp_conn->ripaddr) || + uip_ipaddr_cmp(&UIP_IP_BUF->srcipaddr, &uip_udp_conn->ripaddr))) { + goto udp_found; + } + } + PRINTF("udp: no matching connection found\n"); + goto drop; + + udp_found: + PRINTF("In udp_found\n"); + + uip_conn = NULL; + uip_flags = UIP_NEWDATA; + uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN]; + uip_slen = 0; + UIP_UDP_APPCALL(); + + udp_send: + PRINTF("In udp_send\n"); + + if(uip_slen == 0) { + goto drop; + } + uip_len = uip_slen + UIP_IPUDPH_LEN; + + /* For IPv6, the IP length field does not include the IPv6 IP header + length. */ + UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); + + UIP_IP_BUF->ttl = uip_udp_conn->ttl; + UIP_IP_BUF->proto = UIP_PROTO_UDP; + + UIP_UDP_BUF->udplen = HTONS(uip_slen + UIP_UDPH_LEN); + UIP_UDP_BUF->udpchksum = 0; + + UIP_TCP_BUF->srcport = uip_udp_conn->lport; + UIP_TCP_BUF->destport = uip_udp_conn->rport; + + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &uip_udp_conn->ripaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + + uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPTCPH_LEN]; + +#if UIP_UDP_CHECKSUMS + /* Calculate UDP checksum. */ + UIP_UDP_BUF->udpchksum = ~(uip_udpchksum()); + if(UIP_UDP_BUF->udpchksum == 0) { + UIP_UDP_BUF->udpchksum = 0xffff; + } +#endif /* UIP_UDP_CHECKSUMS */ + UIP_STAT(++uip_stat.udp.sent); + goto ip_send_nolen; +#endif /* UIP_UDP */ + +#if UIP_TCP + /* TCP input processing. */ + tcp_input: + + UIP_STAT(++uip_stat.tcp.recv); + PRINTF("Receiving TCP packet\n"); + /* Start of TCP input header processing code. */ + + if(uip_tcpchksum() != 0xffff) { /* Compute and check the TCP + checksum. */ + UIP_STAT(++uip_stat.tcp.drop); + UIP_STAT(++uip_stat.tcp.chkerr); + UIP_LOG("tcp: bad checksum."); + goto drop; + } + + /* Demultiplex this segment. */ + /* First check any active connections. */ + for(uip_connr = &uip_conns[0]; uip_connr <= &uip_conns[UIP_CONNS - 1]; + ++uip_connr) { + if(uip_connr->tcpstateflags != UIP_CLOSED && + UIP_TCP_BUF->destport == uip_connr->lport && + UIP_TCP_BUF->srcport == uip_connr->rport && + uip_ipaddr_cmp(&UIP_IP_BUF->srcipaddr, &uip_connr->ripaddr)) { + goto found; + } + } + + /* If we didn't find and active connection that expected the packet, + either this packet is an old duplicate, or this is a SYN packet + destined for a connection in LISTEN. If the SYN flag isn't set, + it is an old packet and we send a RST. */ + if((UIP_TCP_BUF->flags & TCP_CTL) != TCP_SYN) { + goto reset; + } + + tmp16 = UIP_TCP_BUF->destport; + /* Next, check listening connections. */ + for(c = 0; c < UIP_LISTENPORTS; ++c) { + if(tmp16 == uip_listenports[c]) { + goto found_listen; + } + } + + /* No matching connection found, so we send a RST packet. */ + UIP_STAT(++uip_stat.tcp.synrst); + + reset: + PRINTF("In reset\n"); + /* We do not send resets in response to resets. */ + if(UIP_TCP_BUF->flags & TCP_RST) { + goto drop; + } + + UIP_STAT(++uip_stat.tcp.rst); + + UIP_TCP_BUF->flags = TCP_RST | TCP_ACK; + uip_len = UIP_IPTCPH_LEN; + UIP_TCP_BUF->tcpoffset = 5 << 4; + + /* Flip the seqno and ackno fields in the TCP header. */ + c = UIP_TCP_BUF->seqno[3]; + UIP_TCP_BUF->seqno[3] = UIP_TCP_BUF->ackno[3]; + UIP_TCP_BUF->ackno[3] = c; + + c = UIP_TCP_BUF->seqno[2]; + UIP_TCP_BUF->seqno[2] = UIP_TCP_BUF->ackno[2]; + UIP_TCP_BUF->ackno[2] = c; + + c = UIP_TCP_BUF->seqno[1]; + UIP_TCP_BUF->seqno[1] = UIP_TCP_BUF->ackno[1]; + UIP_TCP_BUF->ackno[1] = c; + + c = UIP_TCP_BUF->seqno[0]; + UIP_TCP_BUF->seqno[0] = UIP_TCP_BUF->ackno[0]; + UIP_TCP_BUF->ackno[0] = c; + + /* We also have to increase the sequence number we are + acknowledging. If the least significant byte overflowed, we need + to propagate the carry to the other bytes as well. */ + if(++UIP_TCP_BUF->ackno[3] == 0) { + if(++UIP_TCP_BUF->ackno[2] == 0) { + if(++UIP_TCP_BUF->ackno[1] == 0) { + ++UIP_TCP_BUF->ackno[0]; + } + } + } + + /* Swap port numbers. */ + tmp16 = UIP_TCP_BUF->srcport; + UIP_TCP_BUF->srcport = UIP_TCP_BUF->destport; + UIP_TCP_BUF->destport = tmp16; + + /* Swap IP addresses. */ + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &UIP_IP_BUF->srcipaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr); + /* And send out the RST packet! */ + goto tcp_send_noconn; + + /* This label will be jumped to if we matched the incoming packet + with a connection in LISTEN. In that case, we should create a new + connection and send a SYNACK in return. */ + found_listen: + PRINTF("In found listen\n"); + /* First we check if there are any connections avaliable. Unused + connections are kept in the same table as used connections, but + unused ones have the tcpstate set to CLOSED. Also, connections in + TIME_WAIT are kept track of and we'll use the oldest one if no + CLOSED connections are found. Thanks to Eddie C. Dost for a very + nice algorithm for the TIME_WAIT search. */ + uip_connr = 0; + for(c = 0; c < UIP_CONNS; ++c) { + if(uip_conns[c].tcpstateflags == UIP_CLOSED) { + uip_connr = &uip_conns[c]; + break; + } + if(uip_conns[c].tcpstateflags == UIP_TIME_WAIT) { + if(uip_connr == 0 || + uip_conns[c].timer > uip_connr->timer) { + uip_connr = &uip_conns[c]; + } + } + } + + if(uip_connr == 0) { + /* All connections are used already, we drop packet and hope that + the remote end will retransmit the packet at a time when we + have more spare connections. */ + UIP_STAT(++uip_stat.tcp.syndrop); + UIP_LOG("tcp: found no unused connections."); + goto drop; + } + uip_conn = uip_connr; + + /* Fill in the necessary fields for the new connection. */ + uip_connr->rto = uip_connr->timer = UIP_RTO; + uip_connr->sa = 0; + uip_connr->sv = 4; + uip_connr->nrtx = 0; + uip_connr->lport = UIP_TCP_BUF->destport; + uip_connr->rport = UIP_TCP_BUF->srcport; + uip_ipaddr_copy(&uip_connr->ripaddr, &UIP_IP_BUF->srcipaddr); + uip_connr->tcpstateflags = UIP_SYN_RCVD; + + uip_connr->snd_nxt[0] = iss[0]; + uip_connr->snd_nxt[1] = iss[1]; + uip_connr->snd_nxt[2] = iss[2]; + uip_connr->snd_nxt[3] = iss[3]; + uip_connr->len = 1; + + /* rcv_nxt should be the seqno from the incoming packet + 1. */ + uip_connr->rcv_nxt[3] = UIP_TCP_BUF->seqno[3]; + uip_connr->rcv_nxt[2] = UIP_TCP_BUF->seqno[2]; + uip_connr->rcv_nxt[1] = UIP_TCP_BUF->seqno[1]; + uip_connr->rcv_nxt[0] = UIP_TCP_BUF->seqno[0]; + uip_add_rcv_nxt(1); + + /* Parse the TCP MSS option, if present. */ + if((UIP_TCP_BUF->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((UIP_TCP_BUF->tcpoffset >> 4) - 5) << 2 ;) { + opt = uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + c]; + if(opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(opt == TCP_OPT_MSS && + uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = ((u16_t)uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | + (u16_t)uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = uip_connr->mss = + tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; + } + } + } + + /* Our response will be a SYNACK. */ +#if UIP_ACTIVE_OPEN + tcp_send_synack: + UIP_TCP_BUF->flags = TCP_ACK; + + tcp_send_syn: + UIP_TCP_BUF->flags |= TCP_SYN; +#else /* UIP_ACTIVE_OPEN */ + tcp_send_synack: + UIP_TCP_BUF->flags = TCP_SYN | TCP_ACK; +#endif /* UIP_ACTIVE_OPEN */ + + /* We send out the TCP Maximum Segment Size option with our + SYNACK. */ + UIP_TCP_BUF->optdata[0] = TCP_OPT_MSS; + UIP_TCP_BUF->optdata[1] = TCP_OPT_MSS_LEN; + UIP_TCP_BUF->optdata[2] = (UIP_TCP_MSS) / 256; + UIP_TCP_BUF->optdata[3] = (UIP_TCP_MSS) & 255; + uip_len = UIP_IPTCPH_LEN + TCP_OPT_MSS_LEN; + UIP_TCP_BUF->tcpoffset = ((UIP_TCPH_LEN + TCP_OPT_MSS_LEN) / 4) << 4; + goto tcp_send; + + /* This label will be jumped to if we found an active connection. */ + found: + PRINTF("In found\n"); + uip_conn = uip_connr; + uip_flags = 0; + /* We do a very naive form of TCP reset processing; we just accept + any RST and kill our connection. We should in fact check if the + sequence number of this reset is wihtin our advertised window + before we accept the reset. */ + if(UIP_TCP_BUF->flags & TCP_RST) { + uip_connr->tcpstateflags = UIP_CLOSED; + UIP_LOG("tcp: got reset, aborting connection."); + uip_flags = UIP_ABORT; + UIP_APPCALL(); + goto drop; + } + /* Calculate the length of the data, if the application has sent + any data to us. */ + c = (UIP_TCP_BUF->tcpoffset >> 4) << 2; + /* uip_len will contain the length of the actual TCP data. This is + calculated by subtracing the length of the TCP header (in + c) and the length of the IP header (20 bytes). */ + uip_len = uip_len - c - UIP_IPH_LEN; + + /* First, check if the sequence number of the incoming packet is + what we're expecting next. If not, we send out an ACK with the + correct numbers in. */ + if(!(((uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_SYN_SENT) && + ((UIP_TCP_BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)))) { + if((uip_len > 0 || ((UIP_TCP_BUF->flags & (TCP_SYN | TCP_FIN)) != 0)) && + (UIP_TCP_BUF->seqno[0] != uip_connr->rcv_nxt[0] || + UIP_TCP_BUF->seqno[1] != uip_connr->rcv_nxt[1] || + UIP_TCP_BUF->seqno[2] != uip_connr->rcv_nxt[2] || + UIP_TCP_BUF->seqno[3] != uip_connr->rcv_nxt[3])) { + goto tcp_send_ack; + } + } + + /* Next, check if the incoming segment acknowledges any outstanding + data. If so, we update the sequence number, reset the length of + the outstanding data, calculate RTT estimations, and reset the + retransmission timer. */ + if((UIP_TCP_BUF->flags & TCP_ACK) && uip_outstanding(uip_connr)) { + uip_add32(uip_connr->snd_nxt, uip_connr->len); + + if(UIP_TCP_BUF->ackno[0] == uip_acc32[0] && + UIP_TCP_BUF->ackno[1] == uip_acc32[1] && + UIP_TCP_BUF->ackno[2] == uip_acc32[2] && + UIP_TCP_BUF->ackno[3] == uip_acc32[3]) { + /* Update sequence number. */ + uip_connr->snd_nxt[0] = uip_acc32[0]; + uip_connr->snd_nxt[1] = uip_acc32[1]; + uip_connr->snd_nxt[2] = uip_acc32[2]; + uip_connr->snd_nxt[3] = uip_acc32[3]; + + /* Do RTT estimation, unless we have done retransmissions. */ + if(uip_connr->nrtx == 0) { + signed char m; + m = uip_connr->rto - uip_connr->timer; + /* This is taken directly from VJs original code in his paper */ + m = m - (uip_connr->sa >> 3); + uip_connr->sa += m; + if(m < 0) { + m = -m; + } + m = m - (uip_connr->sv >> 2); + uip_connr->sv += m; + uip_connr->rto = (uip_connr->sa >> 3) + uip_connr->sv; + + } + /* Set the acknowledged flag. */ + uip_flags = UIP_ACKDATA; + /* Reset the retransmission timer. */ + uip_connr->timer = uip_connr->rto; + + /* Reset length of outstanding data. */ + uip_connr->len = 0; + } + + } + + /* Do different things depending on in what state the connection is. */ + switch(uip_connr->tcpstateflags & UIP_TS_MASK) { + /* CLOSED and LISTEN are not handled here. CLOSE_WAIT is not + implemented, since we force the application to close when the + peer sends a FIN (hence the application goes directly from + ESTABLISHED to LAST_ACK). */ + case UIP_SYN_RCVD: + /* In SYN_RCVD we have sent out a SYNACK in response to a SYN, and + we are waiting for an ACK that acknowledges the data we sent + out the last time. Therefore, we want to have the UIP_ACKDATA + flag set. If so, we enter the ESTABLISHED state. */ + if(uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_ESTABLISHED; + uip_flags = UIP_CONNECTED; + uip_connr->len = 0; + if(uip_len > 0) { + uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(uip_len); + } + uip_slen = 0; + UIP_APPCALL(); + goto appsend; + } + goto drop; +#if UIP_ACTIVE_OPEN + case UIP_SYN_SENT: + /* In SYN_SENT, we wait for a SYNACK that is sent in response to + our SYN. The rcv_nxt is set to sequence number in the SYNACK + plus one, and we send an ACK. We move into the ESTABLISHED + state. */ + if((uip_flags & UIP_ACKDATA) && + (UIP_TCP_BUF->flags & TCP_CTL) == (TCP_SYN | TCP_ACK)) { + + /* Parse the TCP MSS option, if present. */ + if((UIP_TCP_BUF->tcpoffset & 0xf0) > 0x50) { + for(c = 0; c < ((UIP_TCP_BUF->tcpoffset >> 4) - 5) << 2 ;) { + opt = uip_buf[UIP_IPTCPH_LEN + UIP_LLH_LEN + c]; + if(opt == TCP_OPT_END) { + /* End of options. */ + break; + } else if(opt == TCP_OPT_NOOP) { + ++c; + /* NOP option. */ + } else if(opt == TCP_OPT_MSS && + uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == TCP_OPT_MSS_LEN) { + /* An MSS option with the right option length. */ + tmp16 = (uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 2 + c] << 8) | + uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 3 + c]; + uip_connr->initialmss = + uip_connr->mss = tmp16 > UIP_TCP_MSS? UIP_TCP_MSS: tmp16; + + /* And we are done processing options. */ + break; + } else { + /* All other options have a length field, so that we easily + can skip past them. */ + if(uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + c += uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN + 1 + c]; + } + } + } + uip_connr->tcpstateflags = UIP_ESTABLISHED; + uip_connr->rcv_nxt[0] = UIP_TCP_BUF->seqno[0]; + uip_connr->rcv_nxt[1] = UIP_TCP_BUF->seqno[1]; + uip_connr->rcv_nxt[2] = UIP_TCP_BUF->seqno[2]; + uip_connr->rcv_nxt[3] = UIP_TCP_BUF->seqno[3]; + uip_add_rcv_nxt(1); + uip_flags = UIP_CONNECTED | UIP_NEWDATA; + uip_connr->len = 0; + uip_len = 0; + uip_slen = 0; + UIP_APPCALL(); + goto appsend; + } + /* Inform the application that the connection failed */ + uip_flags = UIP_ABORT; + UIP_APPCALL(); + /* The connection is closed after we send the RST */ + uip_conn->tcpstateflags = UIP_CLOSED; + goto reset; +#endif /* UIP_ACTIVE_OPEN */ + + case UIP_ESTABLISHED: + /* In the ESTABLISHED state, we call upon the application to feed + data into the uip_buf. If the UIP_ACKDATA flag is set, the + application should put new data into the buffer, otherwise we are + retransmitting an old segment, and the application should put that + data into the buffer. + + If the incoming packet is a FIN, we should close the connection on + this side as well, and we send out a FIN and enter the LAST_ACK + state. We require that there is no outstanding data; otherwise the + sequence numbers will be screwed up. */ + + if(UIP_TCP_BUF->flags & TCP_FIN && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + if(uip_outstanding(uip_connr)) { + goto drop; + } + uip_add_rcv_nxt(1 + uip_len); + uip_flags |= UIP_CLOSE; + if(uip_len > 0) { + uip_flags |= UIP_NEWDATA; + } + UIP_APPCALL(); + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_LAST_ACK; + uip_connr->nrtx = 0; + tcp_send_finack: + UIP_TCP_BUF->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* Check the URG flag. If this is set, the segment carries urgent + data that we must pass to the application. */ + if((UIP_TCP_BUF->flags & TCP_URG) != 0) { +#if UIP_URGDATA > 0 + uip_urglen = (UIP_TCP_BUF->urgp[0] << 8) | UIP_TCP_BUF->urgp[1]; + if(uip_urglen > uip_len) { + /* There is more urgent data in the next segment to come. */ + uip_urglen = uip_len; + } + uip_add_rcv_nxt(uip_urglen); + uip_len -= uip_urglen; + uip_urgdata = uip_appdata; + uip_appdata += uip_urglen; + } else { + uip_urglen = 0; +#else /* UIP_URGDATA > 0 */ + uip_appdata = ((char *)uip_appdata) + ((UIP_TCP_BUF->urgp[0] << 8) | UIP_TCP_BUF->urgp[1]); + uip_len -= (UIP_TCP_BUF->urgp[0] << 8) | UIP_TCP_BUF->urgp[1]; +#endif /* UIP_URGDATA > 0 */ + } + + /* If uip_len > 0 we have TCP data in the packet, and we flag this + by setting the UIP_NEWDATA flag and update the sequence number + we acknowledge. If the application has stopped the dataflow + using uip_stop(), we must not accept any data packets from the + remote host. */ + if(uip_len > 0 && !(uip_connr->tcpstateflags & UIP_STOPPED)) { + uip_flags |= UIP_NEWDATA; + uip_add_rcv_nxt(uip_len); + } + + /* Check if the available buffer space advertised by the other end + is smaller than the initial MSS for this connection. If so, we + set the current MSS to the window size to ensure that the + application does not send more data than the other end can + handle. + + If the remote host advertises a zero window, we set the MSS to + the initial MSS so that the application will send an entire MSS + of data. This data will not be acknowledged by the receiver, + and the application will retransmit it. This is called the + "persistent timer" and uses the retransmission mechanim. + */ + tmp16 = ((u16_t)UIP_TCP_BUF->wnd[0] << 8) + (u16_t)UIP_TCP_BUF->wnd[1]; + if(tmp16 > uip_connr->initialmss || + tmp16 == 0) { + tmp16 = uip_connr->initialmss; + } + uip_connr->mss = tmp16; + + /* If this packet constitutes an ACK for outstanding data (flagged + by the UIP_ACKDATA flag, we should call the application since it + might want to send more data. If the incoming packet had data + from the peer (as flagged by the UIP_NEWDATA flag), the + application must also be notified. + + When the application is called, the global variable uip_len + contains the length of the incoming data. The application can + access the incoming data through the global pointer + uip_appdata, which usually points UIP_IPTCPH_LEN + UIP_LLH_LEN + bytes into the uip_buf array. + + If the application wishes to send any data, this data should be + put into the uip_appdata and the length of the data should be + put into uip_len. If the application don't have any data to + send, uip_len must be set to 0. */ + if(uip_flags & (UIP_NEWDATA | UIP_ACKDATA)) { + uip_slen = 0; + UIP_APPCALL(); + + appsend: + + if(uip_flags & UIP_ABORT) { + uip_slen = 0; + uip_connr->tcpstateflags = UIP_CLOSED; + UIP_TCP_BUF->flags = TCP_RST | TCP_ACK; + goto tcp_send_nodata; + } + + if(uip_flags & UIP_CLOSE) { + uip_slen = 0; + uip_connr->len = 1; + uip_connr->tcpstateflags = UIP_FIN_WAIT_1; + uip_connr->nrtx = 0; + UIP_TCP_BUF->flags = TCP_FIN | TCP_ACK; + goto tcp_send_nodata; + } + + /* If uip_slen > 0, the application has data to be sent. */ + if(uip_slen > 0) { + + /* If the connection has acknowledged data, the contents of + the ->len variable should be discarded. */ + if((uip_flags & UIP_ACKDATA) != 0) { + uip_connr->len = 0; + } + + /* If the ->len variable is non-zero the connection has + already data in transit and cannot send anymore right + now. */ + if(uip_connr->len == 0) { + + /* The application cannot send more than what is allowed by + the mss (the minumum of the MSS and the available + window). */ + if(uip_slen > uip_connr->mss) { + uip_slen = uip_connr->mss; + } + + /* Remember how much data we send out now so that we know + when everything has been acknowledged. */ + uip_connr->len = uip_slen; + } else { + + /* If the application already had unacknowledged data, we + make sure that the application does not send (i.e., + retransmit) out more than it previously sent out. */ + uip_slen = uip_connr->len; + } + } + uip_connr->nrtx = 0; + apprexmit: + uip_appdata = uip_sappdata; + + /* If the application has data to be sent, or if the incoming + packet had new data in it, we must send out a packet. */ + if(uip_slen > 0 && uip_connr->len > 0) { + /* Add the length of the IP and TCP headers. */ + uip_len = uip_connr->len + UIP_TCPIP_HLEN; + /* We always set the ACK flag in response packets. */ + UIP_TCP_BUF->flags = TCP_ACK | TCP_PSH; + /* Send the packet. */ + goto tcp_send_noopts; + } + /* If there is no data to send, just send out a pure ACK if + there is newdata. */ + if(uip_flags & UIP_NEWDATA) { + uip_len = UIP_TCPIP_HLEN; + UIP_TCP_BUF->flags = TCP_ACK; + goto tcp_send_noopts; + } + } + goto drop; + case UIP_LAST_ACK: + /* We can close this connection if the peer has acknowledged our + FIN. This is indicated by the UIP_ACKDATA flag. */ + if(uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_CLOSED; + uip_flags = UIP_CLOSE; + UIP_APPCALL(); + } + break; + + case UIP_FIN_WAIT_1: + /* The application has closed the connection, but the remote host + hasn't closed its end yet. Thus we do nothing but wait for a + FIN from the other side. */ + if(uip_len > 0) { + uip_add_rcv_nxt(uip_len); + } + if(UIP_TCP_BUF->flags & TCP_FIN) { + if(uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_connr->len = 0; + } else { + uip_connr->tcpstateflags = UIP_CLOSING; + } + uip_add_rcv_nxt(1); + uip_flags = UIP_CLOSE; + UIP_APPCALL(); + goto tcp_send_ack; + } else if(uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_FIN_WAIT_2; + uip_connr->len = 0; + goto drop; + } + if(uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_FIN_WAIT_2: + if(uip_len > 0) { + uip_add_rcv_nxt(uip_len); + } + if(UIP_TCP_BUF->flags & TCP_FIN) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + uip_add_rcv_nxt(1); + uip_flags = UIP_CLOSE; + UIP_APPCALL(); + goto tcp_send_ack; + } + if(uip_len > 0) { + goto tcp_send_ack; + } + goto drop; + + case UIP_TIME_WAIT: + goto tcp_send_ack; + + case UIP_CLOSING: + if(uip_flags & UIP_ACKDATA) { + uip_connr->tcpstateflags = UIP_TIME_WAIT; + uip_connr->timer = 0; + } + } + goto drop; + + /* We jump here when we are ready to send the packet, and just want + to set the appropriate TCP sequence numbers in the TCP header. */ + tcp_send_ack: + UIP_TCP_BUF->flags = TCP_ACK; + + tcp_send_nodata: + uip_len = UIP_IPTCPH_LEN; + + tcp_send_noopts: + UIP_TCP_BUF->tcpoffset = (UIP_TCPH_LEN / 4) << 4; + + /* We're done with the input processing. We are now ready to send a + reply. Our job is to fill in all the fields of the TCP and IP + headers before calculating the checksum and finally send the + packet. */ + tcp_send: + PRINTF("In tcp_send\n"); + + UIP_TCP_BUF->ackno[0] = uip_connr->rcv_nxt[0]; + UIP_TCP_BUF->ackno[1] = uip_connr->rcv_nxt[1]; + UIP_TCP_BUF->ackno[2] = uip_connr->rcv_nxt[2]; + UIP_TCP_BUF->ackno[3] = uip_connr->rcv_nxt[3]; + + UIP_TCP_BUF->seqno[0] = uip_connr->snd_nxt[0]; + UIP_TCP_BUF->seqno[1] = uip_connr->snd_nxt[1]; + UIP_TCP_BUF->seqno[2] = uip_connr->snd_nxt[2]; + UIP_TCP_BUF->seqno[3] = uip_connr->snd_nxt[3]; + + UIP_IP_BUF->proto = UIP_PROTO_TCP; + + UIP_TCP_BUF->srcport = uip_connr->lport; + UIP_TCP_BUF->destport = uip_connr->rport; + + + uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, &uip_connr->ripaddr); + uip_netif_select_src(&UIP_IP_BUF->srcipaddr,&UIP_IP_BUF->destipaddr); + PRINTF("Sending TCP packet to"); + PRINT6ADDR(&UIP_IP_BUF->destipaddr); + PRINTF("from"); + PRINT6ADDR(&UIP_IP_BUF->srcipaddr); + PRINTF("\n"); + + if(uip_connr->tcpstateflags & UIP_STOPPED) { + /* If the connection has issued uip_stop(), we advertise a zero + window so that the remote host will stop sending data. */ + UIP_TCP_BUF->wnd[0] = UIP_TCP_BUF->wnd[1] = 0; + } else { + UIP_TCP_BUF->wnd[0] = ((UIP_RECEIVE_WINDOW) >> 8); + UIP_TCP_BUF->wnd[1] = ((UIP_RECEIVE_WINDOW) & 0xff); + } + + tcp_send_noconn: + UIP_IP_BUF->ttl = uip_netif_physical_if.cur_hop_limit; + UIP_IP_BUF->len[0] = ((uip_len - UIP_IPH_LEN) >> 8); + UIP_IP_BUF->len[1] = ((uip_len - UIP_IPH_LEN) & 0xff); + + UIP_TCP_BUF->urgp[0] = UIP_TCP_BUF->urgp[1] = 0; + + /* Calculate TCP checksum. */ + UIP_TCP_BUF->tcpchksum = 0; + UIP_TCP_BUF->tcpchksum = ~(uip_tcpchksum()); + UIP_STAT(++uip_stat.tcp.sent); + +#endif /* UIP_TCP */ +#if UIP_UDP + ip_send_nolen: +#endif + UIP_IP_BUF->vtc = 0x60; + UIP_IP_BUF->tcflow = 0x00; + UIP_IP_BUF->flow = 0x00; + send: + PRINTF("Sending packet with length %d (%d)\n", uip_len, + (UIP_IP_BUF->len[0] << 8) | UIP_IP_BUF->len[1]); + + UIP_STAT(++uip_stat.ip.sent); + /* Return and let the caller do the actual transmission. */ + uip_flags = 0; + return; + + drop: + uip_len = 0; + uip_ext_len = 0; + uip_ext_bitmap = 0; + uip_flags = 0; + return; +} +/*---------------------------------------------------------------------------*/ +u16_t +htons(u16_t val) +{ + return HTONS(val); +} + +u32_t +htonl(u32_t val) +{ + return HTONL(val); +} +/*---------------------------------------------------------------------------*/ +void +uip_send(const void *data, int len) +{ + int copylen; +#define MIN(a,b) ((a) < (b)? (a): (b)) + copylen = MIN(len, UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN - + (int)((char *)uip_sappdata - (char *)&uip_buf[UIP_LLH_LEN + UIP_TCPIP_HLEN])); + if(copylen > 0) { + uip_slen = copylen; + if(data != uip_sappdata) { + memcpy(uip_sappdata, (data), uip_slen); + } + } +} +/*---------------------------------------------------------------------------*/ +/** @} */