| /** @file |
| @brief Network context API |
| |
| An API for applications to define a network connection. |
| */ |
| |
| /* |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifdef CONFIG_NETWORK_IP_STACK_DEBUG_CONTEXT |
| #define DEBUG 1 |
| #endif |
| #include "contiki/ip/uip-debug.h" |
| |
| #include <nanokernel.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| |
| #include <net/net_ip.h> |
| #include <net/net_socket.h> |
| |
| #include "contiki/ip/simple-udp.h" |
| #include "contiki/ipv6/uip-ds6.h" |
| |
| #include "contiki/os/lib/random.h" |
| #include "contiki/ipv6/uip-ds6.h" |
| |
| #ifdef CONFIG_NETWORKING_WITH_TCP |
| #include "contiki/os/sys/process.h" |
| #include "contiki/ip/psock.h" |
| #endif |
| |
| #if !defined(CONFIG_NETWORK_IP_STACK_DEBUG_CONTEXT) |
| #undef NET_DBG |
| #define NET_DBG(...) |
| #endif |
| |
| int net_context_get_receiver_registered(struct net_context *context); |
| |
| struct net_context { |
| /* Connection tuple identifies the connection */ |
| struct net_tuple tuple; |
| |
| /* Application receives data via this fifo */ |
| struct nano_fifo rx_queue; |
| |
| /* Application connection data */ |
| union { |
| struct simple_udp_connection udp; |
| |
| #ifdef CONFIG_NETWORKING_WITH_TCP |
| struct { |
| /* Proto socket that handles one TCP connection. */ |
| struct psock ps; |
| struct process tcp; |
| enum net_tcp_type tcp_type; |
| }; |
| #endif |
| }; |
| |
| bool receiver_registered; |
| }; |
| |
| /* Override this in makefile if needed */ |
| #ifndef NET_MAX_CONTEXT |
| #define NET_MAX_CONTEXT 5 |
| #endif |
| |
| static struct net_context contexts[NET_MAX_CONTEXT]; |
| static struct nano_sem contexts_lock; |
| |
| static void context_sem_give(struct nano_sem *chan) |
| { |
| switch (sys_execution_context_type_get()) { |
| case NANO_CTX_FIBER: |
| nano_fiber_sem_give(chan); |
| break; |
| case NANO_CTX_TASK: |
| nano_task_sem_give(chan); |
| break; |
| case NANO_CTX_ISR: |
| default: |
| /* Invalid context type */ |
| break; |
| } |
| } |
| |
| static int context_port_used(enum ip_protocol ip_proto, uint16_t local_port, |
| const struct net_addr *local_addr) |
| |
| { |
| int i; |
| |
| for (i = 0; i < NET_MAX_CONTEXT; i++) { |
| if (contexts[i].tuple.ip_proto == ip_proto && |
| contexts[i].tuple.local_port == local_port && |
| !memcmp(&contexts[i].tuple.local_addr, local_addr, |
| sizeof(struct net_addr))) { |
| return -EEXIST; |
| } |
| } |
| |
| return 0; |
| } |
| |
| struct net_context *net_context_get(enum ip_protocol ip_proto, |
| const struct net_addr *remote_addr, |
| uint16_t remote_port, |
| struct net_addr *local_addr, |
| uint16_t local_port) |
| { |
| #ifdef CONFIG_NETWORKING_WITH_IPV6 |
| const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; |
| const uip_ds6_addr_t *uip_addr; |
| uip_ipaddr_t ipaddr; |
| #endif |
| int i; |
| struct net_context *context = NULL; |
| |
| /* User must provide storage for the local address. */ |
| if (!local_addr) { |
| return NULL; |
| } |
| |
| #ifdef CONFIG_NETWORKING_WITH_IPV6 |
| if (memcmp(&local_addr->in6_addr, &in6addr_any, |
| sizeof(in6addr_any)) == 0) { |
| uip_addr = uip_ds6_get_global(-1); |
| if (!uip_addr) { |
| uip_addr = uip_ds6_get_link_local(-1); |
| } |
| if (!uip_addr) { |
| return NULL; |
| } |
| |
| memcpy(&local_addr->in6_addr, &uip_addr->ipaddr, |
| sizeof(struct in6_addr)); |
| } |
| #else |
| if (local_addr->in_addr.s_addr == INADDR_ANY) { |
| uip_gethostaddr((uip_ipaddr_t *)&local_addr->in_addr); |
| } |
| #endif |
| |
| nano_sem_take(&contexts_lock, TICKS_UNLIMITED); |
| |
| if (local_port) { |
| if (context_port_used(ip_proto, local_port, local_addr) < 0) { |
| return NULL; |
| } |
| } else { |
| do { |
| local_port = random_rand() | 0x8000; |
| } while (context_port_used(ip_proto, local_port, |
| local_addr) == -EEXIST); |
| } |
| |
| for (i = 0; i < NET_MAX_CONTEXT; i++) { |
| if (!contexts[i].tuple.ip_proto) { |
| contexts[i].tuple.ip_proto = ip_proto; |
| contexts[i].tuple.remote_addr = (struct net_addr *)remote_addr; |
| contexts[i].tuple.remote_port = remote_port; |
| contexts[i].tuple.local_addr = (struct net_addr *)local_addr; |
| contexts[i].tuple.local_port = local_port; |
| context = &contexts[i]; |
| break; |
| } |
| } |
| |
| context_sem_give(&contexts_lock); |
| |
| /* Set our local address */ |
| #ifdef CONFIG_NETWORKING_WITH_IPV6 |
| memcpy(&ipaddr.u8, local_addr->in6_addr.s6_addr, sizeof(ipaddr.u8)); |
| if (uip_is_addr_mcast(&ipaddr)) { |
| uip_ds6_maddr_add(&ipaddr); |
| } else { |
| uip_ds6_addr_add(&ipaddr, 0, ADDR_MANUAL); |
| } |
| #endif |
| |
| return context; |
| } |
| |
| void net_context_put(struct net_context *context) |
| { |
| if (!context) { |
| return; |
| } |
| |
| nano_sem_take(&contexts_lock, TICKS_UNLIMITED); |
| |
| if (context->tuple.ip_proto == IPPROTO_UDP) { |
| if (net_context_get_receiver_registered(context)) { |
| struct simple_udp_connection *udp = |
| net_context_get_udp_connection(context); |
| simple_udp_unregister(udp); |
| } |
| } |
| |
| #ifdef CONFIG_NETWORKING_WITH_TCP |
| if (context->tcp_type == NET_TCP_TYPE_SERVER) { |
| tcp_unlisten(UIP_HTONS(context->tuple.local_port), |
| &context->tcp); |
| } |
| #endif |
| |
| memset(&context->tuple, 0, sizeof(context->tuple)); |
| memset(&context->udp, 0, sizeof(context->udp)); |
| context->receiver_registered = false; |
| |
| context_sem_give(&contexts_lock); |
| } |
| |
| struct net_tuple *net_context_get_tuple(struct net_context *context) |
| { |
| if (!context) { |
| return NULL; |
| } |
| |
| return &context->tuple; |
| } |
| |
| struct nano_fifo *net_context_get_queue(struct net_context *context) |
| { |
| if (!context) |
| return NULL; |
| |
| return &context->rx_queue; |
| } |
| |
| struct simple_udp_connection * |
| net_context_get_udp_connection(struct net_context *context) |
| { |
| if (!context) { |
| return NULL; |
| } |
| |
| return &context->udp; |
| } |
| |
| #ifdef CONFIG_NETWORKING_WITH_TCP |
| static int handle_tcp_connection(struct psock *p, enum tcp_event_type type, |
| struct net_buf *buf) |
| { |
| PSOCK_BEGIN(p); |
| |
| if (type == TCP_WRITE_EVENT) { |
| NET_DBG("Trying to send %d bytes data\n", uip_appdatalen(buf)); |
| PSOCK_SEND(p, buf); |
| } |
| |
| PSOCK_END(p); |
| } |
| |
| int net_context_tcp_send(struct net_buf *buf) |
| { |
| /* Prepare data to be sent */ |
| process_post_synch(&ip_buf_context(buf)->tcp, |
| tcpip_event, |
| INT_TO_POINTER(TCP_WRITE_EVENT), |
| buf); |
| |
| |
| return ip_buf_sent_status(buf); |
| } |
| |
| /* This is called by contiki/ip/tcpip.c:tcpip_uipcall() when packet |
| * is processed. |
| */ |
| PROCESS_THREAD(tcp, ev, data, buf, user_data) |
| { |
| PROCESS_BEGIN(); |
| |
| while(1) { |
| PROCESS_YIELD_UNTIL(ev == tcpip_event); |
| |
| if (POINTER_TO_INT(data) == TCP_WRITE_EVENT) { |
| /* We want to send data to peer. */ |
| struct net_context *context = user_data; |
| |
| if (!context) { |
| continue; |
| } |
| |
| do { |
| context = user_data; |
| if (!context || !buf) { |
| break; |
| } |
| |
| if (!context->ps.net_buf || |
| context->ps.net_buf != buf) { |
| NET_DBG("psock init %p buf %p\n", |
| &context->ps, buf); |
| PSOCK_INIT(&context->ps, buf); |
| } |
| |
| handle_tcp_connection(&context->ps, |
| POINTER_TO_INT(data), |
| buf); |
| |
| PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); |
| |
| if (POINTER_TO_INT(data) != TCP_WRITE_EVENT) { |
| goto read_data; |
| } |
| } while(!(uip_closed(buf) || |
| uip_aborted(buf) || |
| uip_timedout(buf))); |
| |
| context = user_data; |
| |
| if (context && |
| context->tcp_type == NET_TCP_TYPE_CLIENT) { |
| NET_DBG("\nConnection closed.\n"); |
| ip_buf_sent_status(buf) = -ECONNRESET; |
| } |
| |
| continue; |
| } |
| |
| read_data: |
| /* We are receiving data from peer. */ |
| if (buf && uip_newdata(buf)) { |
| struct net_buf *clone; |
| |
| if (!uip_len(buf)) { |
| continue; |
| } |
| |
| /* Note that uIP stack will reuse the buffer when |
| * sending ACK to peer host. The sending will happen |
| * right after this function returns. Because of this |
| * we cannot use the same buffer to pass data to |
| * application. |
| */ |
| clone = net_buf_clone(buf); |
| if (!clone) { |
| NET_ERR("No enough RX buffers, " |
| "packet %p discarded\n", buf); |
| continue; |
| } |
| |
| ip_buf_appdata(clone) = uip_buf(clone) + |
| (ip_buf_appdata(buf) - (void *)uip_buf(buf)); |
| ip_buf_appdatalen(clone) = uip_len(buf); |
| ip_buf_len(clone) = ip_buf_len(buf); |
| ip_buf_context(clone) = user_data; |
| uip_set_conn(clone) = uip_conn(buf); |
| uip_flags(clone) = uip_flags(buf); |
| uip_flags(clone) |= UIP_CONNECTED; |
| |
| NET_DBG("packet received context %p buf %p len %d " |
| "appdata %p appdatalen %d\n", |
| ip_buf_context(clone), |
| clone, |
| ip_buf_len(clone), |
| ip_buf_appdata(clone), |
| ip_buf_appdatalen(clone)); |
| |
| nano_fifo_put(net_context_get_queue(user_data), clone); |
| |
| /* We let the application to read the data now */ |
| fiber_yield(); |
| } |
| } |
| |
| PROCESS_END(); |
| } |
| |
| int net_context_tcp_init(struct net_context *context, |
| enum net_tcp_type tcp_type) |
| { |
| if (!context || context->tuple.ip_proto != IPPROTO_TCP) { |
| return -EINVAL; |
| } |
| |
| if (context->receiver_registered) { |
| return 0; |
| } |
| |
| context->receiver_registered = true; |
| |
| if (context->tcp_type == NET_TCP_TYPE_UNKNOWN) { |
| /* This is the first call to this init func. |
| * If we are called by net_receive() first, then |
| * we are working as a server, if net_send() called |
| * us first, then we are the client. |
| */ |
| context->tcp_type = tcp_type; |
| } |
| |
| context->tcp.thread = process_thread_tcp; |
| |
| if (context->tcp_type == NET_TCP_TYPE_SERVER) { |
| context->tcp.name = "TCP server"; |
| |
| NET_DBG("Listen to TCP port %d\n", context->tuple.local_port); |
| tcp_listen(UIP_HTONS(context->tuple.local_port), |
| &context->tcp); |
| #if UIP_ACTIVE_OPEN |
| } else { |
| context->tcp.name = "TCP client"; |
| |
| #ifdef CONFIG_NETWORKING_WITH_IPV6 |
| NET_DBG("Connecting to "); |
| PRINT6ADDR((const uip_ipaddr_t *)&context->tuple.remote_addr->in6_addr); |
| PRINTF(" port %d\n", context->tuple.remote_port); |
| |
| tcp_connect((uip_ipaddr_t *) |
| &context->tuple.remote_addr->in6_addr, |
| UIP_HTONS(context->tuple.remote_port), |
| context, &context->tcp); |
| #else /* CONFIG_NETWORKING_WITH_IPV6 */ |
| NET_DBG("Connecting to "); |
| PRINT6ADDR((const uip_ipaddr_t *)&context->tuple.remote_addr->in_addr); |
| PRINTF(" port %d\n", context->tuple.remote_port); |
| |
| tcp_connect((uip_ipaddr_t *) |
| &context->tuple.remote_addr->in_addr, |
| UIP_HTONS(context->tuple.remote_port), |
| context, &context->tcp); |
| #endif /* CONFIG_NETWORKING_WITH_IPV6 */ |
| #endif /* UIP_ACTIVE_OPEN */ |
| } |
| |
| context->tcp.next = NULL; |
| process_start(&context->tcp, NULL, context); |
| |
| return 0; |
| } |
| #endif /* TCP */ |
| |
| void net_context_init(void) |
| { |
| int i; |
| |
| nano_sem_init(&contexts_lock); |
| |
| memset(contexts, 0, sizeof(contexts)); |
| |
| for (i = 0; i < NET_MAX_CONTEXT; i++) { |
| nano_fifo_init(&contexts[i].rx_queue); |
| } |
| |
| context_sem_give(&contexts_lock); |
| } |
| |
| int net_context_get_receiver_registered(struct net_context *context) |
| { |
| if (!context) { |
| return -ENOENT; |
| } |
| |
| if (context->receiver_registered) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void net_context_set_receiver_registered(struct net_context *context) |
| { |
| if (!context) { |
| return; |
| } |
| |
| context->receiver_registered = true; |
| } |