/*---------------------------------------------------------------------------*/ void ip64_dhcpc_init(const void *mac_addr, int mac_len) { /* Although this is DHCPv4, we explicitly bind the socket to an IPv6 address so that it can operate over the ip64 bridge. */ uip_ip6addr_t v6addr; uip_ip4addr_t v4addr; struct uip_udp_conn *conn2; s.mac_addr = mac_addr; s.mac_len = mac_len; s.state = STATE_INITIAL; uip_ipaddr(&v4addr, 255,255,255,255); ip64_addr_4to6(&v4addr, &v6addr); s.conn = udp_new(&v6addr, UIP_HTONS(IP64_DHCPC_SERVER_PORT), NULL); conn2 = udp_new(NULL, UIP_HTONS(IP64_DHCPC_SERVER_PORT), NULL); if(s.conn != NULL) { udp_bind(s.conn, UIP_HTONS(IP64_DHCPC_CLIENT_PORT)); } if(conn2 != NULL) { udp_bind(conn2, UIP_HTONS(IP64_DHCPC_CLIENT_PORT)); } PT_INIT(&s.pt); }
/** * Connect to MQTT broker. * * N.B. None-blocking call. */ mqtt_status_t mqtt_connect(struct mqtt_connection* conn, const char* host, uint16_t port, uint16_t keep_alive) { uip_ip6addr_t ip6addr; uip_ip4addr_t ip4addr; uip_ipaddr_t *ipaddr; ipaddr = &ip6addr; /* Sanity check */ assert(conn != NULL && host != NULL); /* Check if we are already trying to connect */ if(conn->state > MQTT_CONN_STATE_NOT_CONNECTED) { return MQTT_STATUS_OK; } conn->server_host = host; conn->keep_alive = keep_alive; conn->server_port = port; conn->out_buffer_ptr = conn->out_buffer; conn->out_packet.qos_state = MQTT_QOS_STATE_NO_ACK; /* First check if the host is an IP address. If not, try to look it up. */ if(uiplib_ip6addrconv(host, &ip6addr) == 0) { if(uiplib_ip4addrconv(host, &ip4addr) != 0) { ip64_addr_4to6(&ip4addr, &ip6addr); } else { #if UIP_UDP ipaddr = mdns_lookup(host); if(ipaddr == NULL) { printf("MQTT - Resolving host...\r\n"); mdns_query(host); conn->state = MQTT_CONN_STATE_DNS_LOOKUP; return MQTT_STATUS_OK; } #else /* UIP_UDP */ DBG("Error looking up hostname when mDNS is not used due to UIP_UDP = 0."); conn->state = MQTT_CONN_STATE_ERROR; return MQTT_STATUS_DNS_ERROR; #endif /* UIP_UDP */ } } uip_ipaddr_copy(&(conn->server_ip), ipaddr); /* Initiate the connection if the IP could be resolved. Otherwise the * connection will be initiated when the DNS lookup is finished, in the main * event loop. */ process_post(&mqtt_process, mqtt_do_connect_tcp_event, conn); return MQTT_STATUS_OK; }
/*---------------------------------------------------------------------------*/ void ip64_dhcpc_configured(const struct ip64_dhcpc_state *s) { uip_ip6addr_t ip6dnsaddr; printf("DHCP Configured with %d.%d.%d.%d\n", s->ipaddr.u8[0], s->ipaddr.u8[1], s->ipaddr.u8[2], s->ipaddr.u8[3]); ip64_set_hostaddr((uip_ip4addr_t *)&s->ipaddr); ip64_set_netmask((uip_ip4addr_t *)&s->netmask); ip64_set_draddr((uip_ip4addr_t *)&s->default_router); ip64_addr_4to6((uip_ip4addr_t *)&s->dnsaddr, &ip6dnsaddr); // mdns_conf(&ip6dnsaddr); }
/*---------------------------------------------------------------------------*/ static int start_request(struct http_socket *s) { uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; uip_ip6addr_t *addr; char host[MAX_HOSTLEN]; char path[MAX_PATHLEN]; uint16_t port; int ret; if(parse_url(s->url, host, &port, path)) { printf("url %s host %s port %d path %s\n", s->url, host, port, path); /* Check if we are to route the request through a proxy. */ if(s->proxy_port != 0) { /* The proxy address should be an IPv6 address. */ uip_ip6addr_copy(&ip6addr, &s->proxy_addr); port = s->proxy_port; } else if(uiplib_ip6addrconv(host, &ip6addr) == 0) { /* First check if the host is an IP address. */ if(uiplib_ip4addrconv(host, &ip4addr) != 0) { ip64_addr_4to6(&ip4addr, &ip6addr); } else { /* Try to lookup the hostname. If it fails, we initiate a hostname lookup. */ ret = resolv_lookup(host, &addr); if(ret == RESOLV_STATUS_UNCACHED || ret == RESOLV_STATUS_EXPIRED) { resolv_query(host); puts("Resolving host..."); return HTTP_SOCKET_OK; } if(addr != NULL) { s->did_tcp_connect = 1; tcp_socket_connect(&s->s, addr, port); return HTTP_SOCKET_OK; } else { return HTTP_SOCKET_ERR; } } } tcp_socket_connect(&s->s, &ip6addr, port); return HTTP_SOCKET_OK; } else { return HTTP_SOCKET_ERR; } }
/*-----------------------------------------------------------------------------------*/ unsigned char websocket_http_client_get(struct websocket_http_client_state *s, const char *host, uint16_t port, const char *file, const char *subprotocol) { struct uip_conn *conn; uip_ip6addr_t ip6addr; uip_ip4addr_t ip4addr; uip_ipaddr_t *ipaddr; ipaddr = &ip6addr; /* First check if the host is an IP address. */ if(uiplib_ip6addrconv(host, &ip6addr) == 0) { if(uiplib_ip4addrconv(host, &ip4addr) != 0) { ip64_addr_4to6(&ip4addr, &ip6addr); } else { #if UIP_UDP ipaddr = mdns_lookup(host); if(ipaddr == NULL) { return 0; } #else /* UIP_UDP */ return 0; #endif /* UIP_UDP */ } } conn = tcp_connect(ipaddr, uip_htons(port), NULL); if(conn == NULL) { return 0; } tcp_markconn(conn, s); s->conn = conn; s->port = port; strncpy(s->file, file, sizeof(s->file)); strncpy(s->host, host, sizeof(s->host)); strncpy(s->subprotocol, subprotocol, sizeof(s->subprotocol)); init_connection(s); return 1; }
/*---------------------------------------------------------------------------*/ static int start_request(struct http_socket *s) { uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; uip_ip6addr_t *addr; char host[MAX_HOSTLEN]; char path[MAX_PATHLEN]; uint16_t port; if(parse_url(s->url, host, &port, path)) { printf("url %s host %s port %d path %s\n\r", s->url, host, port, path); /* First check if the host is an IP address. */ if(uiplib_ip6addrconv(host, &ip6addr) == 0) { if(uiplib_ip4addrconv(host, &ip4addr) != 0) { ip64_addr_4to6(&ip4addr, &ip6addr); } else { /* Try to lookup the hostname. If it fails, we initiate a hostname lookup. */ addr = mdns_lookup(host); if(addr == NULL) { mdns_query(host); puts("Resolving host..."); return HTTP_SOCKET_OK; } tcp_socket_connect(&s->s, addr, port); return HTTP_SOCKET_OK; } } tcp_socket_connect(&s->s, &ip6addr, port); return HTTP_SOCKET_OK; } else { return HTTP_SOCKET_ERR; } }
/*---------------------------------------------------------------------------*/ PROCESS_THREAD(http_example_process, ev, data) { static struct etimer et; uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; PROCESS_BEGIN(); uip_ipaddr(&ip4addr, 8, 8, 8, 8); ip64_addr_4to6(&ip4addr, &ip6addr); uip_nameserver_update(&ip6addr, UIP_NAMESERVER_INFINITE_LIFETIME); etimer_set(&et, CLOCK_SECOND * 20); PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); memset(url_buffer, 0, HTTP_CLIENT_BUFFER_LEN); snprintf(url_buffer, HTTP_CLIENT_BUFFER_LEN, "http://maker.ifttt.com/trigger/%s/with/key/%s", IFTTT_EVENT, IFTTT_KEY); http_socket_init(&s); restarts = 0; while(1) { PROCESS_YIELD(); if((ev == sensors_event) && (data == &button_sensor)) { if(button_sensor.value(BUTTON_SENSOR_VALUE_TYPE_LEVEL) == BUTTON_SENSOR_PRESSED_LEVEL) { leds_on(LEDS_GREEN); printf("Button pressed! sending a POST to IFTTT\n"); http_socket_post(&s, url_buffer, NULL, 0, NULL, callback, NULL); } } } PROCESS_END(); }
/*---------------------------------------------------------------------------*/ static int start_request(struct http_socket *s) { uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; uip_ipaddr_t *addr; char host[MAX_HOSTLEN]; char path[MAX_PATHLEN]; uint16_t port; if(parse_url(s->url, host, &port, path)) { PRINTF("%s url %s host %s port %d path %s\n", HTTP_METHOD_STR(s->method), s->url, host, port, path); /* First check if the host is an IP address. */ if(uiplib_ip6addrconv(host, &ip6addr) == 0) { if(uiplib_ip4addrconv(host, &ip4addr) != 0) { ip64_addr_4to6(&ip4addr, &ip6addr); } else { /* Try to lookup the hostname. If it fails, we initiate a hostname lookup. */ if(resolv_lookup(host, &addr) != RESOLV_STATUS_CACHED) { resolv_query(host); PRINTF("Resolving host...\n"); return HTTP_SOCKET_OK; } tcp_socket_connect(&s->s, addr, port); return HTTP_SOCKET_OK; } } tcp_socket_connect(&s->s, (uip_ipaddr_t *)&ip6addr, port); return HTTP_SOCKET_OK; } else { return HTTP_SOCKET_ERR; } }
/*---------------------------------------------------------------------------*/ void ip64_dhcpc_configured(const struct ip64_dhcpc_state *s) { uip_ip6addr_t ip6dnsaddr; PRINTF("DHCP Configured with %d.%d.%d.%d\n", s->ipaddr.u8[0], s->ipaddr.u8[1], s->ipaddr.u8[2], s->ipaddr.u8[3]); ip64_set_hostaddr((uip_ip4addr_t *)&s->ipaddr); ip64_set_netmask((uip_ip4addr_t *)&s->netmask); ip64_set_draddr((uip_ip4addr_t *)&s->default_router); if(!uip_ip4addr_cmp((uip_ip4addr_t *)&s->dnsaddr, &uip_all_zeroes_addr)) { //Note: Currently we assume only one DNS server uip_ipaddr_t * dns = uip_nameserver_get(0); //Only update DNS entry if it is empty or already IPv4 if(uip_is_addr_unspecified(dns) || ip64_addr_is_ip64(dns)) { ip64_addr_4to6((uip_ip4addr_t *)&s->dnsaddr, &ip6dnsaddr); uip_nameserver_update(&ip6dnsaddr, uip_ntohs(s->lease_time[0])*65536ul + uip_ntohs(s->lease_time[1])); } } #if CETIC_6LBR cetic_6lbr_ip64_dhcpc_configured(s); #endif }
/*---------------------------------------------------------------------------*/ void contiki_init(void) { int i; uint8_t addr[sizeof(uip_lladdr.addr)]; uip_ipaddr_t ipaddr; uip_ds6_addr_t *lladdr; uip_ip4addr_t ipv4addr, netmask; /* Start process handler */ process_init(); /* Start Contiki processes */ process_start(&etimer_process, NULL); process_start(&sensors_process, NULL); ctimer_init(); /* Print startup information */ printf(CONTIKI_VERSION_STRING " started. "); if(node_id > 0) { printf("Node id is set to %u.\n", node_id); } else { printf("Node id is not set.\n"); } set_mac_addr(); queuebuf_init(); /* Initialize communication stack */ netstack_init(); printf("%s/%s/%s, channel check rate %lu Hz\n", NETSTACK_NETWORK.name, NETSTACK_MAC.name, NETSTACK_RDC.name, CLOCK_SECOND / (NETSTACK_RDC.channel_check_interval() == 0 ? 1: NETSTACK_RDC.channel_check_interval())); /* IPv6 CONFIGURATION */ for(i = 0; i < sizeof(uip_lladdr.addr); i += 2) { addr[i + 1] = node_id & 0xff; addr[i + 0] = node_id >> 8; } linkaddr_copy(addr, &linkaddr_node_addr); memcpy(&uip_lladdr.addr, addr, sizeof(uip_lladdr.addr)); process_start(&tcpip_process, NULL); printf("Tentative link-local IPv6 address "); lladdr = uip_ds6_get_link_local(-1); for(i = 0; i < 7; ++i) { printf("%02x%02x:", lladdr->ipaddr.u8[i * 2], lladdr->ipaddr.u8[i * 2 + 1]); } printf("%02x%02x\n", lladdr->ipaddr.u8[14], lladdr->ipaddr.u8[15]); uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); uip_ds6_addr_add(&ipaddr, 0, ADDR_TENTATIVE); printf("Tentative global IPv6 address "); for(i = 0; i < 7; ++i) { printf("%02x%02x:", ipaddr.u8[i * 2], ipaddr.u8[i * 2 + 1]); } printf("%02x%02x\n", ipaddr.u8[7 * 2], ipaddr.u8[7 * 2 + 1]); /* Start serial process */ serial_line_init(); /* Start autostart processes (defined in Contiki application) */ print_processes(autostart_processes); autostart_start(autostart_processes); /* Start the SLIP */ printf("Initiating SLIP with IP address is 172.16.0.2.\n"); uip_ipaddr(&ipv4addr, 172, 16, 0, 2); uip_ipaddr(&netmask, 255, 255, 255, 0); ip64_set_ipv4_address(&ipv4addr, &netmask); rs232_set_input(slip_input_byte); log_set_putchar_with_slip(1); uip_ip4addr_t ip4addr; uip_ip6addr_t ip6addr; uip_ipaddr(&ip4addr, 8,8,8,8); ip64_addr_4to6(&ip4addr, &ip6addr); uip_nameserver_update((uip_ipaddr_t *)&ip6addr, UIP_NAMESERVER_INFINITE_LIFETIME); }
/*---------------------------------------------------------------------------*/ int ip64_4to6(const uint8_t *ipv4packet, const uint16_t ipv4packet_len, uint8_t *resultpacket) { struct ipv4_hdr *v4hdr; struct ipv6_hdr *v6hdr; struct udp_hdr *udphdr; struct tcp_hdr *tcphdr; struct icmpv4_hdr *icmpv4hdr; struct icmpv6_hdr *icmpv6hdr; uint16_t ipv4len, ipv6len, ipv6_packet_len; struct ip64_addrmap_entry *m; v6hdr = (struct ipv6_hdr *)resultpacket; v4hdr = (struct ipv4_hdr *)ipv4packet; if((v4hdr->len[0] << 8) + v4hdr->len[1] <= ipv4packet_len) { ipv4len = (v4hdr->len[0] << 8) + v4hdr->len[1]; } else { PRINTF("ip64_4to6: packet smaller than reported in IPv4 header, dropping\n"); return 0; } if(ipv4len <= IPV4_HDRLEN) { return 0; } /* Make sure that the resulting packet fits in the ip64 packet buffer. If not, we drop it. */ if(ipv4len - IPV4_HDRLEN + IPV6_HDRLEN > BUFSIZE) { PRINTF("ip64_4to6: packet too big to fit in buffer, dropping\n"); return 0; } /* We copy the data from the IPv4 packet into the IPv6 packet. */ memcpy(&resultpacket[IPV6_HDRLEN], &ipv4packet[IPV4_HDRLEN], ipv4len - IPV4_HDRLEN); udphdr = (struct udp_hdr *)&resultpacket[IPV6_HDRLEN]; tcphdr = (struct tcp_hdr *)&resultpacket[IPV6_HDRLEN]; icmpv4hdr = (struct icmpv4_hdr *)&ipv4packet[IPV4_HDRLEN]; icmpv6hdr = (struct icmpv6_hdr *)&resultpacket[IPV6_HDRLEN]; ipv6len = ipv4len - IPV4_HDRLEN + IPV6_HDRLEN; ipv6_packet_len = ipv6len - IPV6_HDRLEN; /* Translate the IPv4 header into an IPv6 header. */ /* We first fill in the simple fields: IP header version, traffic class and flow label, and length fields. */ v6hdr->vtc = 0x60; v6hdr->tcflow = 0; v6hdr->flow = 0; v6hdr->len[0] = ipv6_packet_len >> 8; v6hdr->len[1] = ipv6_packet_len & 0xff; /* We use the IPv4 TTL field as the IPv6 hop limit field. */ v6hdr->hoplim = v4hdr->ttl; /* We now translate the IPv4 source and destination addresses to IPv6 source and destination addresses. We translate the IPv4 source address into an IPv6-encoded IPv4 address. The IPv4 destination address will be the address with which we have previously been configured, through the ip64_set_ipv4_address() function. We use the mapping table to look up the new IPv6 destination address. As we assume that the IPv4 packet is a response to a previously sent IPv6 packet, we should have a mapping between the (protocol, destport, srcport, srcaddress) tuple. If not, we'll return 0 to indicate that we failed to translate the packet. */ if(ip64_addr_4to6(&v4hdr->srcipaddr, &v6hdr->srcipaddr) == 0) { PRINTF("ip64_packet_4to6: failed to convert source IP address\n"); return 0; } /* For the next header field, we simply use the IPv4 protocol field. We only support UDP and TCP packets. */ switch(v4hdr->proto) { case IP_PROTO_UDP: v6hdr->nxthdr = IP_PROTO_UDP; break; case IP_PROTO_TCP: v6hdr->nxthdr = IP_PROTO_TCP; break; case IP_PROTO_ICMPV4: /* Allow only ICMPv4 ECHO_REQUESTS (ping packets) through to the local IPv6 host. */ if(icmpv4hdr->type == ICMP_ECHO) { PRINTF("ip64_4to6: translating ICMPv4 ECHO packet\n"); v6hdr->nxthdr = IP_PROTO_ICMPV6; icmpv6hdr->type = ICMP6_ECHO; ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address); } else { PRINTF("ip64_packet_4to6: ICMPv4 packet type %d not supported\n", icmpv4hdr->type); return 0; } break; default: /* For protocol types that we do not support, we return 0 to indicate that we failed to translate the packet to an IPv6 packet. */ PRINTF("ip64_packet_4to6: protocol type %d not supported\n", v4hdr->proto); return 0; } /* Translate IPv4 broadcasts to IPv6 all-nodes multicasts. */ if(uip_ip4addr_cmp(&v4hdr->destipaddr, &ipv4_broadcast_addr) || (uip_ipaddr_maskcmp(&v4hdr->destipaddr, &ip64_hostaddr, &ip64_netmask) && ((v4hdr->destipaddr.u16[0] & (~ip64_netmask.u16[0])) == (ipv4_broadcast_addr.u16[0] & (~ip64_netmask.u16[0]))) && ((v4hdr->destipaddr.u16[1] & (~ip64_netmask.u16[1])) == (ipv4_broadcast_addr.u16[1] & (~ip64_netmask.u16[1]))))) { uip_create_linklocal_allnodes_mcast(&v6hdr->destipaddr); } else { if(!ip64_hostaddr_configured) { PRINTF("ip64_packet_4to6: no local IPv4 address configured, dropping incoming packet.\n"); return 0; } if(!uip_ip4addr_cmp(&v4hdr->destipaddr, &ip64_hostaddr)) { PRINTF("ip64_packet_4to6: the IPv4 destination address %d.%d.%d.%d did not match our IPv4 address %d.%d.%d.%d\n", uip_ipaddr_to_quad(&v4hdr->destipaddr), uip_ipaddr_to_quad(&ip64_hostaddr)); return 0; } /* Now we translate the transport layer port numbers. We assume that the IPv4 packet is a response to a packet that has previously been translated from IPv6 to IPv4. If this is the case, the tuple (protocol, destport, srcport, srcaddress) corresponds to an address/port pair in our mapping table. If we do not find a mapping, we return 0 to indicate that we could not translate the IPv4 packet to an IPv6 packet. */ /* XXX treat a few ports differently: those ports should be let through to the local host. For those ports, we set up an address mapping that ensures that the local port number is retained. */ if((v4hdr->proto == IP_PROTO_TCP || v4hdr->proto == IP_PROTO_UDP)) { if(uip_htons(tcphdr->destport) < EPHEMERAL_PORTRANGE) { /* This packet should go to the local host. */ PRINTF("Port is in the non-ephemeral port range %d (%d)\n", tcphdr->destport, uip_htons(tcphdr->destport)); ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address); } else if(ip64_special_ports_incoming_is_special(uip_htons(tcphdr->destport))) { uip_ip6addr_t newip6addr; uint16_t newport; PRINTF("ip64 port %d (%d) is special, treating it differently\n", tcphdr->destport, uip_htons(tcphdr->destport)); if(ip64_special_ports_translate_incoming(uip_htons(tcphdr->destport), &newip6addr, &newport)) { ip64_addr_copy6(&v6hdr->destipaddr, &newip6addr); tcphdr->destport = uip_htons(newport); PRINTF("New port %d (%d)\n", tcphdr->destport, uip_htons(tcphdr->destport)); } else { ip64_addr_copy6(&v6hdr->destipaddr, &ipv6_local_address); PRINTF("No new port\n"); } } else { /* The TCP or UDP port numbers were not non-ephemeral and not special, so we map the port number according to the address mapping table. */ m = ip64_addrmap_lookup_port(uip_ntohs(udphdr->destport), v4hdr->proto); if(m == NULL) { PRINTF("Inbound lookup failed\n"); return 0; } else { PRINTF("Inbound lookup did not fail\n"); } ip64_addr_copy6(&v6hdr->destipaddr, &m->ip6addr); udphdr->destport = uip_htons(m->ip6port); } } } /* The checksum is in different places in the different protocol headers, so we need to be sure that we update the correct field. */ switch(v6hdr->nxthdr) { case IP_PROTO_TCP: tcphdr->tcpchksum = 0; tcphdr->tcpchksum = ~(ipv6_transport_checksum(resultpacket, ipv6len, IP_PROTO_TCP)); break; case IP_PROTO_UDP: udphdr->udpchksum = 0; udphdr->udpchksum = ~(ipv6_transport_checksum(resultpacket, ipv6len, IP_PROTO_UDP)); if(udphdr->udpchksum == 0) { udphdr->udpchksum = 0xffff; } break; case IP_PROTO_ICMPV6: icmpv6hdr->icmpchksum = 0; icmpv6hdr->icmpchksum = ~(ipv6_transport_checksum(resultpacket, ipv6len, IP_PROTO_ICMPV6)); break; default: PRINTF("ip64_4to6: transport protocol %d not implemented\n", v4hdr->proto); return 0; } /* Finally, we return the length of the resulting IPv6 packet. */ PRINTF("ip64_4to6: ipv6len %d\n", ipv6len); return ipv6len; }