blob: 083fb19cf5fda0f87a7cb621123404e792e25d72 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "os/mynewt.h"
#if (MYNEWT_VAL(OC_TRANSPORT_IP) == 1) && (MYNEWT_VAL(OC_TRANSPORT_IPV4) == 1)
#include <log/log.h>
#include <mn_socket/mn_socket.h>
#include <stats/stats.h>
#include "oic/oc_log.h"
#include "oic/port/oc_connectivity.h"
#include "oic/port/mynewt/adaptor.h"
#include "oic/port/mynewt/transport.h"
#include "oic/port/mynewt/ip.h"
static void oc_send_buffer_ip4(struct os_mbuf *m);
static void oc_send_buffer_ip4_mcast(struct os_mbuf *m);
static uint8_t oc_ep_ip4_size(const struct oc_endpoint *oe);
static char *oc_log_ep_ip4(char *ptr, int maxlen, const struct oc_endpoint *);
static int oc_connectivity_init_ip4(void);
void oc_connectivity_shutdown_ip4(void);
static void oc_event_ip4(struct os_event *ev);
static const struct oc_transport oc_ip4_transport = {
.ot_flags = 0,
.ot_ep_size = oc_ep_ip4_size,
.ot_tx_ucast = oc_send_buffer_ip4,
.ot_tx_mcast = oc_send_buffer_ip4_mcast,
.ot_get_trans_security = NULL,
.ot_ep_str = oc_log_ep_ip4,
.ot_init = oc_connectivity_init_ip4,
.ot_shutdown = oc_connectivity_shutdown_ip4
};
static struct os_event oc_sock4_read_event = {
.ev_cb = oc_event_ip4,
};
#define COAP_PORT_UNSECURED (5683)
/* 224.0.1.187 */
static const struct mn_in_addr coap_all_nodes_v4 = {
.s_addr = htonl(0xe00001bb)
};
STATS_SECT_START(oc_ip4_stats)
STATS_SECT_ENTRY(iframe)
STATS_SECT_ENTRY(ibytes)
STATS_SECT_ENTRY(ierr)
STATS_SECT_ENTRY(oucast)
STATS_SECT_ENTRY(omcast)
STATS_SECT_ENTRY(obytes)
STATS_SECT_ENTRY(oerr)
STATS_SECT_END
STATS_SECT_DECL(oc_ip4_stats) oc_ip4_stats;
STATS_NAME_START(oc_ip4_stats)
STATS_NAME(oc_ip4_stats, iframe)
STATS_NAME(oc_ip4_stats, ibytes)
STATS_NAME(oc_ip4_stats, ierr)
STATS_NAME(oc_ip4_stats, oucast)
STATS_NAME(oc_ip4_stats, omcast)
STATS_NAME(oc_ip4_stats, obytes)
STATS_NAME(oc_ip4_stats, oerr)
STATS_NAME_END(oc_ip4_stats)
/* sockets to use for coap unicast and multicast */
static struct mn_socket *oc_ucast4;
#if (MYNEWT_VAL(OC_SERVER) == 1)
static struct mn_socket *oc_mcast4;
#endif
#ifdef OC_SECURITY
#error This implementation does not yet support security
#endif
static char *
oc_log_ep_ip4(char *ptr, int maxlen, const struct oc_endpoint *oe)
{
const struct oc_endpoint_ip *oe_ip = (const struct oc_endpoint_ip *)oe;
int len;
mn_inet_ntop(MN_PF_INET, oe_ip->v4.address, ptr, maxlen);
len = strlen(ptr);
snprintf(ptr + len, maxlen - len, "-%u", oe_ip->port);
return ptr;
}
static uint8_t
oc_ep_ip4_size(const struct oc_endpoint *oe)
{
return sizeof(struct oc_endpoint_ip);
}
static void
oc_send_buffer_ip4_int(struct os_mbuf *m, int is_mcast)
{
struct mn_sockaddr_in to;
struct oc_endpoint_ip *oe_ip;
struct mn_itf itf;
uint32_t if2_idx;
struct os_mbuf *n;
int rc;
assert(OS_MBUF_USRHDR_LEN(m) >= sizeof(struct oc_endpoint_ip));
oe_ip = (struct oc_endpoint_ip *)OC_MBUF_ENDPOINT(m);
to.msin_len = sizeof(to);
to.msin_family = MN_AF_INET;
to.msin_port = htons(oe_ip->port);
memcpy(&to.msin_addr, oe_ip->v4.address, sizeof(to.msin_addr));
STATS_INCN(oc_ip4_stats, obytes, OS_MBUF_PKTLEN(m));
if (is_mcast) {
memset(&itf, 0, sizeof(itf));
if2_idx = 0;
while (1) {
rc = mn_itf_getnext(&itf);
if (rc) {
break;
}
if ((itf.mif_flags & (MN_ITF_F_UP | MN_ITF_F_MULTICAST)) !=
(MN_ITF_F_UP | MN_ITF_F_MULTICAST)) {
continue;
}
if (!if2_idx) {
if2_idx = itf.mif_idx;
continue;
}
rc = mn_setsockopt(oc_ucast4, MN_SO_LEVEL, MN_MCAST_IF, &if2_idx);
if (rc) {
STATS_INC(oc_ip4_stats, oerr);
continue;
}
n = os_mbuf_dup(m);
if (!n) {
STATS_INC(oc_ip4_stats, oerr);
break;
}
rc = mn_sendto(oc_ucast4, n, (struct mn_sockaddr *)&to);
if (rc != 0) {
OC_LOG(ERROR, "Failed to send buffer %u on %x\n",
OS_MBUF_PKTHDR(m)->omp_len, if2_idx);
STATS_INC(oc_ip4_stats, oerr);
os_mbuf_free_chain(n);
}
if2_idx = itf.mif_idx;
}
if (if2_idx) {
rc = mn_setsockopt(oc_ucast4, MN_SO_LEVEL, MN_MCAST_IF, &if2_idx);
if (rc) {
STATS_INC(oc_ip4_stats, oerr);
os_mbuf_free_chain(m);
} else {
rc = mn_sendto(oc_ucast4, m, (struct mn_sockaddr *) &to);
if (rc != 0) {
OC_LOG(ERROR, "Failed sending buffer %u on itf %x\n",
OS_MBUF_PKTHDR(m)->omp_len, if2_idx);
STATS_INC(oc_ip4_stats, oerr);
os_mbuf_free_chain(m);
}
}
} else {
os_mbuf_free_chain(m);
}
} else {
rc = mn_sendto(oc_ucast4, m, (struct mn_sockaddr *) &to);
if (rc != 0) {
OC_LOG(ERROR, "Failed to send buffer %u ucast\n",
OS_MBUF_PKTHDR(m)->omp_len);
STATS_INC(oc_ip4_stats, oerr);
os_mbuf_free_chain(m);
}
}
}
void
oc_send_buffer_ip4(struct os_mbuf *m)
{
STATS_INC(oc_ip4_stats, oucast);
oc_send_buffer_ip4_int(m, 0);
}
void
oc_send_buffer_ip4_mcast(struct os_mbuf *m)
{
STATS_INC(oc_ip4_stats, omcast);
oc_send_buffer_ip4_int(m, 1);
}
static struct os_mbuf *
oc_attempt_rx_ip4_sock(struct mn_socket *rxsock)
{
int rc;
struct os_mbuf *m;
struct os_mbuf *n;
struct oc_endpoint_ip *oe_ip;
struct mn_sockaddr_in from;
rc = mn_recvfrom(rxsock, &n, (struct mn_sockaddr *) &from);
if (rc != 0) {
return NULL;
}
assert(OS_MBUF_IS_PKTHDR(n));
STATS_INC(oc_ip4_stats, iframe);
STATS_INCN(oc_ip4_stats, ibytes, OS_MBUF_PKTLEN(n));
m = os_msys_get_pkthdr(0, sizeof(struct oc_endpoint_ip));
if (!m) {
OC_LOG(ERROR, "Could not allocate RX buffer\n");
goto rx_attempt_err;
}
OS_MBUF_PKTHDR(m)->omp_len = OS_MBUF_PKTHDR(n)->omp_len;
SLIST_NEXT(m, om_next) = n;
oe_ip = (struct oc_endpoint_ip *)OC_MBUF_ENDPOINT(m);
oe_ip->ep.oe_type = oc_ip4_transport_id;
oe_ip->ep.oe_flags = 0;
memcpy(&oe_ip->v4.address, &from.msin_addr, sizeof(oe_ip->v4.address));
oe_ip->port = ntohs(from.msin_port);
return m;
/* add the addr info to the message */
rx_attempt_err:
STATS_INC(oc_ip4_stats, ierr);
os_mbuf_free_chain(n);
return NULL;
}
static struct os_mbuf *
oc_attempt_rx_ip4(void)
{
struct os_mbuf *m;
m = oc_attempt_rx_ip4_sock(oc_ucast4);
#if (MYNEWT_VAL(OC_SERVER) == 1)
if (m == NULL) {
m = oc_attempt_rx_ip4_sock(oc_mcast4);
}
#endif
return m;
}
static void oc_socks4_readable(void *cb_arg, int err);
union mn_socket_cb oc_sock4_cbs = {
.socket.readable = oc_socks4_readable,
.socket.writable = NULL
};
void
oc_socks4_readable(void *cb_arg, int err)
{
os_eventq_put(oc_evq_get(), &oc_sock4_read_event);
}
void
oc_connectivity_shutdown_ip4(void)
{
if (oc_ucast4) {
mn_close(oc_ucast4);
}
#if (MYNEWT_VAL(OC_SERVER) == 1)
if (oc_mcast4) {
mn_close(oc_mcast4);
}
#endif
}
static void
oc_event_ip4(struct os_event *ev)
{
struct os_mbuf *m;
while ((m = oc_attempt_rx_ip4()) != NULL) {
oc_recv_message(m);
}
}
int
oc_connectivity_init_ip4(void)
{
int rc;
struct mn_sockaddr_in sin;
struct mn_itf itf;
memset(&itf, 0, sizeof(itf));
rc = mn_socket(&oc_ucast4, MN_PF_INET, MN_SOCK_DGRAM, 0);
if (rc != 0 || !oc_ucast4) {
OC_LOG(ERROR, "Could not create oc unicast v4 socket\n");
return rc;
}
mn_socket_set_cbs(oc_ucast4, oc_ucast4, &oc_sock4_cbs);
#if (MYNEWT_VAL(OC_SERVER) == 1)
rc = mn_socket(&oc_mcast4, MN_PF_INET, MN_SOCK_DGRAM, 0);
if (rc != 0 || !oc_mcast4) {
mn_close(oc_ucast4);
OC_LOG(ERROR, "Could not create oc multicast v4 socket\n");
return rc;
}
mn_socket_set_cbs(oc_mcast4, oc_mcast4, &oc_sock4_cbs);
#endif
sin.msin_len = sizeof(sin);
sin.msin_family = MN_AF_INET;
sin.msin_port = 0;
memset(&sin.msin_addr, 0, sizeof(sin.msin_addr));
rc = mn_bind(oc_ucast4, (struct mn_sockaddr *)&sin);
if (rc != 0) {
OC_LOG(ERROR, "Could not bind oc unicast v4 socket\n");
goto oc_connectivity_init_err;
}
#if (MYNEWT_VAL(OC_SERVER) == 1)
/* Set socket option to join multicast group on all valid interfaces */
while (1) {
struct mn_mreq join;
rc = mn_itf_getnext(&itf);
if (rc) {
break;
}
if ((itf.mif_flags & (MN_ITF_F_UP | MN_ITF_F_MULTICAST)) !=
(MN_ITF_F_UP | MN_ITF_F_MULTICAST)) {
continue;
}
memcpy(&join.mm_addr.v4, &coap_all_nodes_v4, sizeof(coap_all_nodes_v4));
join.mm_idx = itf.mif_idx;
join.mm_family = MN_AF_INET;
rc = mn_setsockopt(oc_mcast4, MN_SO_LEVEL, MN_MCAST_JOIN_GROUP, &join);
if (rc != 0) {
continue;
}
OC_LOG(DEBUG, "Joined Coap v4 mcast group on %s\n", itf.mif_name);
}
sin.msin_port = htons(COAP_PORT_UNSECURED);
rc = mn_bind(oc_mcast4, (struct mn_sockaddr *)&sin);
if (rc != 0) {
OC_LOG(ERROR, "Could not bind oc v4 multicast socket\n");
goto oc_connectivity_init_err;
}
#endif
return 0;
oc_connectivity_init_err:
oc_connectivity_shutdown_ip4();
return rc;
}
#endif
uint8_t oc_ip4_transport_id = -1;
void
oc_register_ip4(void)
{
#if (MYNEWT_VAL(OC_TRANSPORT_IP) == 1) && (MYNEWT_VAL(OC_TRANSPORT_IPV4) == 1)
oc_ip4_transport_id = oc_transport_register(&oc_ip4_transport);
#endif
}