Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers socket_api.c Source File

socket_api.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014-2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 /**
00018  * \file socket_api.c
00019  * \brief Socket API for library model
00020  *
00021  * The socket API functions for library model
00022  */
00023 
00024 #include "nsconfig.h"
00025 #include "ns_types.h"
00026 #include "eventOS_scheduler.h"
00027 #include "platform/arm_hal_phy.h"
00028 #ifndef BUILD_LIBRARY
00029 #define BUILD_LIBRARY
00030 #endif
00031 #include "ns_address.h"
00032 #include "socket_api.h"
00033 #include "ns_trace.h"
00034 #include "string.h"
00035 #include "nsdynmemLIB.h"
00036 #include "Core/include/socket.h"
00037 #include "NWK_INTERFACE/Include/protocol.h"
00038 #include "Common_Protocols/ipv6_constants.h"
00039 #include "Common_Protocols/ipv6_flow.h"
00040 #include "Common_Protocols/tcp.h"
00041 #include "Common_Protocols/udp.h"
00042 #include "common_functions.h"
00043 
00044 #define TRACE_GROUP "sckA"
00045 
00046 const uint8_t ns_in6addr_any[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
00047 
00048 int8_t socket_open(uint8_t protocol, uint16_t identifier, void (*passed_fptr)(void *))
00049 {
00050     int8_t sid = -1;
00051     socket_family_t family;
00052     socket_type_t type;
00053 
00054     switch (protocol) {
00055         case SOCKET_ICMP:
00056             family = SOCKET_FAMILY_IPV6;
00057             type = SOCKET_TYPE_RAW;
00058             protocol = IPV6_NH_ICMPV6;
00059             break;
00060         case SOCKET_UDP:
00061             family = SOCKET_FAMILY_IPV6;
00062             type = SOCKET_TYPE_DGRAM;
00063             protocol = IPV6_NH_UDP;
00064             break;
00065 #ifndef NO_TCP
00066         case SOCKET_TCP:
00067             family = SOCKET_FAMILY_IPV6;
00068             type = SOCKET_TYPE_STREAM;
00069             protocol = IPV6_NH_TCP;
00070             break;
00071 #endif /* NO_TCP */
00072         case SOCKET_RAW:
00073             if (identifier > 0xff) {
00074                 return -1;
00075             }
00076             family = SOCKET_FAMILY_IPV6;
00077             type = SOCKET_TYPE_RAW;
00078             protocol = identifier;
00079             break;
00080         default:
00081             return -1;
00082     }
00083 
00084     switch (socket_create(family, type, protocol, &sid, identifier, passed_fptr, false)) {
00085         case eOK:
00086             return sid;
00087         case eBUSY:
00088             return -2;
00089         case eFALSE:
00090         default:
00091             break;
00092     }
00093 
00094     return sid;
00095 }
00096 
00097 int8_t socket_close(int8_t sid)
00098 {
00099     socket_t *socket = socket_pointer_get(sid);
00100     if (!socket) {
00101         return -1;
00102     }
00103 
00104     socket_id_detach(sid);
00105     return 0;
00106 }
00107 
00108 int8_t socket_listen(int8_t socket, uint8_t backlog)
00109 {
00110 #ifdef NO_TCP
00111     return -1;
00112 #else
00113     socket_t *socket_ptr = socket_pointer_get(socket);
00114     if (!socket_ptr || !socket_is_ipv6(socket_ptr) || socket_ptr->inet_pcb ->local_port  == 0) {
00115         return -1;
00116     }
00117 
00118     switch (socket_ptr->type) {
00119         case SOCKET_TYPE_STREAM:
00120             break;
00121         default:
00122             return -1;
00123     }
00124 
00125     if (socket_ptr->u.live.fptr == NULL) {
00126         return -2;
00127     }
00128 
00129     socket_ptr->flags  |= SOCKET_LISTEN_STATE;
00130     if (backlog < 1) {
00131         backlog = 1;
00132     } else if (backlog > SOCKET_LISTEN_BACKLOG_MAX) {
00133         backlog = SOCKET_LISTEN_BACKLOG_MAX;
00134     }
00135     socket_ptr->listen_backlog  = backlog;
00136 
00137     return 0;
00138 #endif
00139 }
00140 
00141 int8_t socket_accept(int8_t listen_socket_id, ns_address_t *addr, void (*passed_fptr)(void *))
00142 {
00143 #ifdef NO_TCP
00144     return -1;
00145 #else
00146     socket_t *socket_ptr = socket_pointer_get(listen_socket_id);
00147     if (!socket_ptr || !socket_is_ipv6(socket_ptr) || !(socket_ptr->flags  & SOCKET_LISTEN_STATE) || passed_fptr == NULL) {
00148         return -1;
00149     }
00150 
00151     /* Queue contains both partial and complete connections - find a complete one */
00152     socket_t *pending_socket = NULL;
00153     ns_list_foreach(socket_t, so, &socket_ptr->u.live.queue) {
00154         if (so->flags & SOCKET_FLAG_CONNECTED) {
00155             pending_socket = so;
00156             break;
00157         }
00158     }
00159     if (!pending_socket) {
00160         tr_warn("No pending complete connection for socket %d", listen_socket_id);
00161         return NS_EWOULDBLOCK;
00162     }
00163 
00164     int8_t new_sid = socket_id_assign_and_attach(pending_socket);
00165     if (new_sid == -1) {
00166         tr_error("Socket id allocation failed, s=%d", listen_socket_id);
00167         return -1;
00168     }
00169 
00170     // This takes it off the pending queue, and releases one reference count,
00171     // but the ID assignment above took one, so it stays live.
00172     socket_leave_pending_state(pending_socket, passed_fptr);
00173 
00174     // Make a data-received event if anything already pending - some may expect this
00175     if (pending_socket->rcvq.data_bytes != 0) {
00176         socket_data_queued_event_push(pending_socket);
00177     }
00178 
00179     if (addr) {
00180         addr->type = ADDRESS_IPV6;
00181         memcpy(addr->address, pending_socket->inet_pcb ->remote_address , 16);
00182         addr->identifier = pending_socket->inet_pcb ->remote_port ;
00183     }
00184 
00185     return new_sid;
00186 #endif
00187 }
00188 
00189 int8_t socket_shutdown(int8_t socket, uint8_t how)
00190 {
00191 #ifdef NO_TCP
00192     return -1;
00193 #else
00194     socket_t *socket_ptr = socket_pointer_get(socket);
00195     if (!socket_ptr || !socket_is_ipv6(socket_ptr)) {
00196         return -1;
00197     }
00198 
00199     if (socket_ptr->type != SOCKET_TYPE_STREAM) {
00200         return -1;
00201     }
00202 
00203     if (!(socket_ptr->flags  & SOCKET_FLAG_CONNECTED)) {
00204         return -2;
00205     }
00206 
00207     uint8_t flags;
00208 
00209     switch (how) {
00210         case SOCKET_SHUT_WR:
00211             flags = SOCKET_FLAG_SHUT_WR;
00212             break;
00213         case SOCKET_SHUT_RD:
00214             flags = SOCKET_FLAG_CANT_RECV_MORE;
00215             break;
00216         case SOCKET_SHUT_RDWR:
00217             flags = SOCKET_FLAG_CANT_RECV_MORE | SOCKET_FLAG_SHUT_WR;
00218             break;
00219         default:
00220             return -1;
00221     }
00222 
00223     socket_ptr->flags  |= flags;
00224 
00225     // Take care never to put tcp_info in local variable - these calls can delete it
00226     int8_t ret = 0;
00227 
00228     // If we've shutdown writing, signal close to TCP if it exists
00229     if (tcp_info(socket_ptr->inet_pcb ) && (flags & SOCKET_FLAG_SHUT_WR)) {
00230         switch (tcp_session_close(tcp_info(socket_ptr->inet_pcb ))) {
00231             case TCP_ERROR_NO_ERROR:
00232                 break;
00233             default:
00234                 ret = -1;
00235                 break;
00236         }
00237     }
00238 
00239     // Likewise for the read shutdown
00240     if (flags & SOCKET_FLAG_CANT_RECV_MORE) {
00241         if (tcp_info(socket_ptr->inet_pcb )) {
00242             switch (tcp_session_shutdown_read(tcp_info(socket_ptr->inet_pcb ))) {
00243                 case TCP_ERROR_NO_ERROR:
00244                     break;
00245                 default:
00246                     ret = -1;
00247                     break;
00248             }
00249         }
00250         // Note sockbuf flush after tcp shutdown - this allows TCP to see
00251         // that we are binning unread data, and send a RESET to indicate.
00252         sockbuf_flush(&socket_ptr->rcvq);
00253     }
00254 
00255     return ret;
00256 #endif
00257 }
00258 
00259 int8_t socket_getsockname(int8_t socket, ns_address_t *address) {
00260 
00261     const socket_t *socket_ptr = socket_pointer_get(socket);
00262     if (!socket_ptr || !socket_is_ipv6(socket_ptr)) {
00263         return -1;
00264     }
00265 
00266     const inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00267     address->identifier = inet_pcb->local_port ;
00268     address->type = ADDRESS_IPV6;
00269     memcpy(address->address, inet_pcb->local_address , 16);
00270 
00271     return 0;
00272 }
00273 
00274 int8_t socket_getpeername(int8_t socket, ns_address_t *address)
00275 {
00276     const socket_t *socket_ptr = socket_pointer_get(socket);
00277     if (!socket_ptr || !socket_is_ipv6(socket_ptr)) {
00278         return -1;
00279     }
00280 
00281     const inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00282     address->identifier = inet_pcb->remote_port ;
00283     address->type = ADDRESS_IPV6;
00284     memcpy(address->address, inet_pcb->remote_address , 16);
00285 
00286     if (addr_ipv6_equal(inet_pcb->remote_address , ns_in6addr_any)) {
00287         return -2;
00288     }
00289 
00290     return 0;
00291 }
00292 
00293 /* Extracts data from queue, copying to msg->msg_iov.
00294  * Data is removed from queue. msg->msg_iov pointers and lengths not modified.
00295  * msg->flags gets NS_MSG_TRUNC set if a datagram was truncated.
00296  * For non-streams, will return actual datagram length if flags & NS_MSG_TRUNC.
00297  * Otherwise, returns amount read.
00298  */
00299 static uint_fast16_t socket_copy_queue_to_user(sockbuf_t *sb, ns_msghdr_t *msg, int flags, bool stream)
00300 {
00301     uint_fast16_t retVal = 0;
00302 
00303     const ns_iovec_t *iovec_ptr = msg->msg_iov;
00304     uint_fast16_t iovec_cnt = msg->msg_iovlen;
00305 
00306     uint8_t *out_ptr = iovec_ptr->iov_base;
00307     uint_fast16_t out_len = iovec_ptr->iov_len;
00308     buffer_t *buf_src = ns_list_get_first(&sb->bufs);
00309     const uint8_t *in_ptr = buffer_data_pointer(buf_src);
00310     uint_fast16_t in_len = buffer_data_length(buf_src);
00311 
00312     for (;;) {
00313         /* Compute amount to copy in this chunk - smaller of current input and output block */
00314         uint_fast16_t len = in_len;
00315         if (len > out_len) {
00316             len = out_len;
00317         }
00318 
00319         memcpy(out_ptr, in_ptr, len);
00320         retVal += len;
00321 
00322         /* Advance input pointer, consume buffer */
00323         in_ptr += len;
00324         in_len -= len;
00325         if (!(flags & NS_MSG_PEEK)) {
00326             buffer_data_strip_header(buf_src, len);
00327             sb->data_bytes -= len;
00328         }
00329         if (in_len == 0) {
00330             /* Drop this buffer from queue, and advance to next buffer if stream */
00331             if (flags & NS_MSG_PEEK) {
00332                 buf_src = stream ? ns_list_get_next(&sb->bufs, buf_src) : NULL;
00333             } else {
00334                 sockbuf_drop_first(sb);
00335                 buf_src = stream ? ns_list_get_first(&sb->bufs) : NULL;
00336             }
00337             if (!buf_src) {
00338                 break;
00339             }
00340             in_ptr = buffer_data_pointer(buf_src);
00341             in_len = buffer_data_length(buf_src);
00342         }
00343 
00344         /* There's still more input - advance output pointer */
00345         out_ptr += len;
00346         out_len -= len;
00347         if (out_len == 0) {
00348             /* Advance to next vector */
00349             if (--iovec_cnt == 0) {
00350                 break;
00351             }
00352             iovec_ptr++;
00353             out_ptr = iovec_ptr->iov_base;
00354             out_len = iovec_ptr->iov_len;
00355         }
00356     }
00357 
00358     /* If there was anything left in buf_src, for not-stream need to drop it now */
00359     if (buf_src && !stream) {
00360         msg->msg_flags |= NS_MSG_TRUNC;
00361         if (flags & NS_MSG_TRUNC) {
00362             // Return actual length, not read length
00363             retVal += in_len;
00364         }
00365         if (!(flags & NS_MSG_PEEK)) {
00366             sockbuf_drop_first(sb);
00367         }
00368     }
00369     return retVal;
00370 }
00371 
00372 static uint8_t * socket_cmsg_ipv6_header_set(ns_cmsghdr_t *cmsg, uint8_t type, uint16_t length)
00373 {
00374     cmsg->cmsg_level = SOCKET_IPPROTO_IPV6;
00375     cmsg->cmsg_type = type;
00376     cmsg->cmsg_len = NS_CMSG_LEN(length);
00377     return NS_CMSG_DATA(cmsg);
00378 }
00379 
00380 int16_t socket_read(int8_t socket, ns_address_t *src_addr, uint8_t *buffer, uint16_t length)
00381 {
00382     return socket_recvfrom(socket, buffer, length, NS_MSG_LEGACY0, src_addr);
00383 }
00384 
00385 int16_t socket_recv(int8_t socket, void *buffer, uint16_t length, int flags)
00386 {
00387     return socket_recvfrom(socket, buffer, length, flags, NULL);
00388 }
00389 
00390 int16_t socket_recvfrom(int8_t socket, void *buffer, uint16_t length, int flags, ns_address_t *src_addr)
00391 {
00392     ns_iovec_t msg_iov;
00393     ns_msghdr_t msghdr;
00394 
00395     //Init message payload vector
00396     msg_iov.iov_base = buffer;
00397     msg_iov.iov_len = length;
00398 
00399     //Set messages name buffer
00400     msghdr.msg_name = src_addr;
00401     msghdr.msg_namelen = src_addr ? sizeof(ns_address_t) : 0;
00402     msghdr.msg_iov = &msg_iov;
00403     msghdr.msg_iovlen = 1;
00404     msghdr.msg_control = NULL;
00405     msghdr.msg_controllen = 0;
00406 
00407     return socket_recvmsg(socket, &msghdr, flags);
00408 }
00409 
00410 static uint_fast16_t copy_ancillary_to_user(ns_msghdr_t *msg, uint_fast16_t written_data, uint8_t type, void * data_ptr, uint16_t length)
00411 {
00412     if (!msg->msg_control || msg->msg_controllen < (written_data + NS_CMSG_SPACE(length))) {
00413         msg->msg_flags |= NS_MSG_CTRUNC;
00414         return written_data;
00415     }
00416     uint8_t *msg_start = msg->msg_control;
00417     uint8_t *data_start = socket_cmsg_ipv6_header_set((ns_cmsghdr_t*) (msg_start + written_data) , type, length);
00418     memcpy(data_start, data_ptr, length);
00419 
00420     return (written_data + NS_CMSG_SPACE(length));
00421 }
00422 
00423 int16_t socket_recvmsg(int8_t socket, ns_msghdr_t *msg, int flags)
00424 {
00425     if (!msg) {
00426         return -1;
00427     }
00428     msg->msg_flags = 0;
00429     /**
00430      * Validate socket id
00431      */
00432     socket_t *socket_ptr = socket_pointer_get(socket);
00433     if (!socket_ptr) {
00434         return -1;
00435     }
00436 
00437     //Read Buffer
00438     buffer_t *socket_buf = ns_list_get_first(&socket_ptr->rcvq.bufs);
00439     if (!socket_buf) {
00440         if (flags & NS_MSG_LEGACY0) {
00441             return 0;
00442         } else {
00443             return (socket_ptr->flags  & SOCKET_FLAG_CANT_RECV_MORE) ? 0 : NS_EWOULDBLOCK;
00444         }
00445     }
00446 
00447     /**
00448      * Validate message payload buffer size
00449      */
00450     if (!socket_message_validate_iov(msg, NULL)) {
00451         return -1;
00452     }
00453 
00454     /**
00455      * Write Ancillary
00456      */
00457     uint_fast16_t cmsg_written = 0;
00458     if (socket_ptr->inet_pcb ->recvpktinfo) {
00459         ns_in6_pktinfo_t pktinfo;
00460         pktinfo.ipi6_ifindex = socket_buf->interface ->id;
00461         memcpy(pktinfo.ipi6_addr, socket_buf->dst_sa .address , 16);
00462         cmsg_written = copy_ancillary_to_user(msg, cmsg_written, SOCKET_IPV6_PKTINFO, &pktinfo, sizeof pktinfo);
00463     }
00464 
00465     if (socket_ptr->inet_pcb ->recvhoplimit) {
00466         int16_t hoplimit = socket_buf->options .hop_limit ;
00467         cmsg_written = copy_ancillary_to_user(msg, cmsg_written, SOCKET_IPV6_HOPLIMIT, &hoplimit, sizeof hoplimit);
00468     }
00469 
00470     if (socket_ptr->inet_pcb ->recvtclass) {
00471         int16_t tclass = socket_buf->options .traffic_class ;
00472         cmsg_written = copy_ancillary_to_user(msg, cmsg_written, SOCKET_IPV6_TCLASS, &tclass, sizeof tclass);
00473     }
00474     msg->msg_controllen = cmsg_written;
00475 
00476     ns_address_t *src_addr = msg->msg_name;
00477     //Read src address
00478     if (src_addr) {
00479 
00480         if (msg->msg_namelen != sizeof(ns_address_t)) {
00481             return -1;
00482         }
00483 
00484         src_addr->identifier = socket_buf->src_sa .port ;
00485         src_addr->type = ADDRESS_IPV6;
00486         memcpy(src_addr->address, socket_buf->src_sa .address , 16);
00487     }
00488 
00489     //Read Data to Buffer
00490     int16_t len = socket_copy_queue_to_user(&socket_ptr->rcvq, msg, flags, socket_ptr->type == SOCKET_TYPE_STREAM);
00491 
00492     if (len > 0 && !(flags & NS_MSG_PEEK) && socket_is_ipv6(socket_ptr) && tcp_info(socket_ptr->inet_pcb )) {
00493         tcp_session_data_received(tcp_info(socket_ptr->inet_pcb ));
00494     }
00495 
00496     return len;
00497 }
00498 
00499 int16_t socket_sendmsg(int8_t socket, const ns_msghdr_t *msg, int flags)
00500 {
00501     if (!msg) {
00502         return -1;
00503     }
00504 
00505     return socket_buffer_sendmsg(socket, NULL, msg, flags);
00506 }
00507 
00508 int16_t socket_sendto(int8_t socket, const ns_address_t *address, const void *buffer, uint16_t length)
00509 {
00510     ns_iovec_t data_vector;
00511     ns_msghdr_t msghdr;
00512 
00513     //SET IOV vector
00514     data_vector.iov_base = (void *) buffer;
00515     data_vector.iov_len = length;
00516 
00517     //Set message name
00518     msghdr.msg_name = (void *) address;
00519     msghdr.msg_namelen = address ? sizeof(ns_address_t) : 0;
00520     msghdr.msg_iov = &data_vector;
00521     msghdr.msg_iovlen = 1;
00522     //No ancillary data
00523     msghdr.msg_control = NULL;
00524     msghdr.msg_controllen = 0;
00525 
00526     return socket_sendmsg(socket, &msghdr, NS_MSG_LEGACY0);
00527 }
00528 
00529 int8_t socket_connect(int8_t socket, ns_address_t *address, uint8_t randomly_take_src_number)
00530 {
00531     socket_t *socket_ptr = socket_pointer_get(socket);
00532     if (!address || !socket_ptr || !socket_is_ipv6(socket_ptr)) {
00533         return -1;
00534     }
00535 
00536     switch (socket_ptr->type) {
00537         case SOCKET_TYPE_STREAM:
00538         case SOCKET_TYPE_DGRAM:
00539             break;
00540         default:
00541             return -5;
00542     }
00543 
00544     if (socket_ptr->flags  & SOCKET_LISTEN_STATE) {
00545         return -3;
00546     }
00547 
00548     if (socket_ptr->flags  & (SOCKET_FLAG_CONNECTED|SOCKET_FLAG_CONNECTING)) {
00549         return -4;
00550     }
00551 
00552     inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00553 
00554     /* Save hash computation time in IP layer by using a random flow for connected sockets */
00555     if (inet_pcb->flow_label == IPV6_FLOW_AUTOGENERATE ||
00556             (inet_pcb->flow_label == IPV6_FLOW_UNSPECIFIED && ipv6_flow_auto_label)) {
00557         inet_pcb->flow_label = ipv6_flow_random();
00558     }
00559 
00560     // Choose local port if not already bound
00561     (void) randomly_take_src_number;
00562     if (inet_pcb->local_port  == 0) {
00563         inet_pcb->local_port  = socket_generate_random_port(inet_pcb->protocol );
00564         if (inet_pcb->local_port  == 0) {
00565             return -7;
00566         }
00567     }
00568 
00569     // Choose local address if not already bound
00570     int8_t status = socket_bind2addrsel(socket, address);
00571     switch(status) {
00572         case 0: //OK
00573         case -3: // already bound
00574             break;
00575         case -1: // invalid param
00576         case -2: //mem alloc fail
00577             return status;
00578         case -4: // no interface
00579         case -5: // addr selection fails
00580         default:
00581             return -7;
00582     }
00583 
00584     memcpy(inet_pcb->remote_address , address->address, 16);
00585     inet_pcb->remote_port  = address->identifier;
00586     status = 0;
00587 
00588 #ifndef NO_TCP
00589     if (socket_ptr->type == SOCKET_TYPE_STREAM) {
00590         if (tcp_info(socket_ptr->inet_pcb )) {
00591             status = -4; // shouldn't happen - flags should cover it
00592             goto exit;
00593         }
00594 
00595         //Allocate session here
00596         tcp_session_t *tcp_session = tcp_session_ptr_allocate(socket_ptr->inet_pcb , NULL);
00597         if (!tcp_session) {
00598             status = -2;
00599             goto exit;
00600         }
00601 
00602         if (tcp_session_open(tcp_session) != TCP_ERROR_NO_ERROR) {
00603             tcp_session_ptr_free(tcp_session);
00604             status = -2;
00605             goto exit;
00606         }
00607 
00608         socket_ptr->flags  |= SOCKET_FLAG_CONNECTING;
00609     }
00610 #endif
00611 exit:
00612     if (status != 0) {
00613         memcpy(inet_pcb->remote_address , ns_in6addr_any, 16);
00614         inet_pcb->remote_port  = 0;
00615     }
00616     return status;
00617 }
00618 
00619 int8_t socket_bind(int8_t socket, const ns_address_t *address)
00620 {
00621     socket_t *socket_ptr = socket_pointer_get(socket);
00622     if (!address || !socket_ptr || !socket_is_ipv6(socket_ptr)) {
00623         return -1;
00624     }
00625 
00626     switch (socket_ptr->type) {
00627         case SOCKET_TYPE_STREAM:
00628         case SOCKET_TYPE_DGRAM:
00629             break;
00630         default:
00631             return -5;
00632     }
00633 
00634     inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00635 
00636     // are they specifying a new port?
00637     if (address->identifier && address->identifier != inet_pcb->local_port ) {
00638         // don't let them do so if a port was already chosen
00639         if (inet_pcb->local_port  != 0) {
00640             return -4;
00641         }
00642         if (socket_port_validate(address->identifier, inet_pcb->protocol ) == eOK) {
00643             inet_pcb->local_port  = address->identifier;
00644         }
00645         else {
00646             return -2;
00647         }
00648     }
00649     // we don't actually allocate ephemeral ports until later when we know
00650     // the destination (potentially useful to follow RFC 6056 some day)
00651 
00652     // are they specifying a new address?
00653     if (!addr_ipv6_equal(address->address, ns_in6addr_any) && !addr_ipv6_equal(address->address, inet_pcb->local_address )) {
00654         // don't let them do so if an address was already chosen
00655         if (!addr_ipv6_equal(inet_pcb->local_address , ns_in6addr_any)) {
00656             return -4;
00657         }
00658 
00659         if (protcol_interface_address_compare(NULL, address->address) != 0) {
00660             return -3;
00661         }
00662 
00663         memcpy(inet_pcb->local_address , address->address, 16);
00664     }
00665 
00666     return 0;
00667 }
00668 
00669 int8_t socket_bind2addrsel(int8_t socket, const ns_address_t *dst_address)
00670 {
00671     protocol_interface_info_entry_t *if_info;
00672 
00673     socket_t *socket_ptr = socket_pointer_get(socket);
00674     if (!dst_address || !socket_ptr || !socket_is_ipv6(socket_ptr)) {
00675         return -1;
00676     }
00677 
00678     switch (socket_ptr->type) {
00679         case SOCKET_TYPE_STREAM:
00680         case SOCKET_TYPE_DGRAM:
00681             break;
00682         default:
00683             return -6;
00684     }
00685 
00686     inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00687 
00688     if (!addr_ipv6_equal(inet_pcb->local_address , ns_in6addr_any)) {
00689         return -3;
00690     }
00691 
00692     // select source address based on destination
00693     buffer_t *buf = buffer_get(20);
00694     if (!buf) {
00695         return -2;
00696     }
00697 
00698     memcpy(&buf->dst_sa .address , dst_address->address, 16);
00699     buf->dst_sa .port  = dst_address->identifier;
00700     if_info = socket_interface_determine(socket_ptr, buf);
00701 
00702     if (!if_info) {
00703         tr_error("socket_bind2addrsel: No interface");
00704         buffer_free(buf);
00705         return -4;
00706     }
00707 
00708     if (addr_interface_select_source(if_info, inet_pcb->local_address , dst_address->address, inet_pcb->addr_preferences) < 0) {
00709         tr_error("src_addr selection failed");
00710         buffer_free(buf);
00711         return -5;
00712     }
00713 
00714     buffer_free(buf);
00715 
00716     /* Could choose an ephemeral port here, but no point - see bind() */
00717 
00718     return 0;
00719 }
00720 
00721 int16_t socket_send(int8_t socket, const void *buffer, uint16_t length)
00722 {
00723     return socket_sendto(socket, NULL, buffer, length);
00724 }
00725 
00726 static int8_t ipv6_setsockopt(socket_t *socket_ptr, uint8_t opt_name, const void *opt_value, uint16_t opt_len)
00727 {
00728     inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
00729 
00730     switch (opt_name) {
00731 
00732         /* These first few aren't really IPv6, but hey... */
00733         case SOCKET_INTERFACE_SELECT: {
00734             if (opt_len != sizeof(int8_t)) {
00735                 return -3;
00736             }
00737             int8_t interface_id = *(const int8_t *) opt_value;
00738             socket_ptr->default_interface_id = interface_id;
00739 
00740             return 0;
00741         }
00742         case SOCKET_LINK_LAYER_SECURITY: {
00743             if (opt_len != sizeof(int8_t)) {
00744                 return -3;
00745             }
00746             int8_t securityEnabled = *(const int8_t *) opt_value;
00747             inet_pcb->link_layer_security = securityEnabled;
00748 
00749             return 0;
00750         }
00751         case SOCKET_BROADCAST_PAN: {
00752             if (opt_len != sizeof(int8_t)) {
00753                 return -3;
00754             }
00755             socket_ptr->broadcast_pan = *(const int8_t *) opt_value;
00756 
00757             return 0;
00758         }
00759         case SOCKET_IPV6_TCLASS: {
00760             /* Supporting TCP properly is fiddly, and not yet needed */
00761             if (socket_ptr->type == SOCKET_TYPE_STREAM) {
00762                 return -2;
00763             }
00764 
00765             if (opt_len != sizeof(int16_t)) {
00766                 return -3;
00767             }
00768 
00769             int16_t tclass = *(const int16_t *) opt_value;
00770             if (tclass == -1) {
00771                 inet_pcb->tclass = SOCKET_IPV6_TCLASS_DEFAULT;
00772             } else if (tclass >= 0 && tclass <= 255) {
00773                 inet_pcb->tclass = tclass;
00774             } else {
00775                 return -3;
00776             }
00777 
00778             return 0;
00779         }
00780         case SOCKET_IPV6_FLOW_LABEL: {
00781             if (opt_len != sizeof(int32_t)) {
00782                 return -3;
00783             }
00784 
00785             int32_t flow = *(const int32_t *) opt_value;
00786             if (flow >= -2 && flow <= 0xfffff) {
00787                 inet_pcb->flow_label = flow;
00788             } else {
00789                 return -3;
00790             }
00791 
00792             return 0;
00793         }
00794         case SOCKET_IPV6_UNICAST_HOPS: {
00795             if (opt_len != sizeof(int16_t)) {
00796                 return -3;
00797             }
00798 
00799             int16_t hops = *(const int16_t *) opt_value;
00800             if (hops >= -1 && hops <= 255) {
00801                 inet_pcb->unicast_hop_limit  = hops;
00802             } else {
00803                 return -3;
00804             }
00805 
00806             return 0;
00807         }
00808         case SOCKET_IPV6_MULTICAST_HOPS: {
00809             if (opt_len != sizeof(int16_t)) {
00810                 return -3;
00811             }
00812 
00813             int16_t hops = *(const int16_t *) opt_value;
00814             if (hops == -1) {
00815                 inet_pcb->multicast_hop_limit = 1;
00816             } else if (hops >= 0 && hops <= 255) {
00817                 inet_pcb->multicast_hop_limit = hops;
00818             } else {
00819                 return -3;
00820             }
00821 
00822             return 0;
00823         }
00824         case SOCKET_IPV6_MULTICAST_IF: {
00825             if (opt_len != sizeof(int8_t)) {
00826                 return -3;
00827             }
00828             int8_t interface_id = *(const int8_t *) opt_value;
00829             if (interface_id != 0 && !protocol_stack_interface_info_get_by_id(interface_id)) {
00830                 return -3;
00831             }
00832             inet_pcb->multicast_if = interface_id;
00833 
00834             return 0;
00835         }
00836         case SOCKET_IPV6_MULTICAST_LOOP: {
00837             if (opt_len != sizeof(bool)) {
00838                 return -3;
00839             }
00840             inet_pcb->multicast_loop = *(const bool *) opt_value;
00841 
00842             return 0;
00843         }
00844         case SOCKET_IPV6_JOIN_GROUP:
00845         case SOCKET_IPV6_LEAVE_GROUP: {
00846             if (opt_len != sizeof(ns_ipv6_mreq_t)) {
00847                 return -3;
00848             }
00849             const ns_ipv6_mreq_t *mreq = opt_value;
00850             if (!addr_is_ipv6_multicast(mreq->ipv6mr_multiaddr)) {
00851                 return -3;
00852             }
00853             if (opt_name == SOCKET_IPV6_JOIN_GROUP) {
00854                 return socket_inet_pcb_join_group(inet_pcb, mreq->ipv6mr_interface, mreq->ipv6mr_multiaddr);
00855             } else {
00856                 return socket_inet_pcb_leave_group(inet_pcb, mreq->ipv6mr_interface, mreq->ipv6mr_multiaddr);
00857             }
00858         }
00859         case SOCKET_IPV6_ADDR_PREFERENCES: {
00860             if (opt_len != sizeof(uint32_t)) {
00861                 return -3;
00862             }
00863 
00864             uint32_t flags = *(const uint32_t *) opt_value;
00865 
00866             /* Fault contradictory flags */
00867             if (flags & (flags >> 16)) {
00868                 return -3;
00869             }
00870 
00871             /* Set specified flags, clear opposite flags */
00872             inet_pcb->addr_preferences |= flags;
00873             inet_pcb->addr_preferences &= ~((flags >> 16) | (flags << 16));
00874 
00875             return 0;
00876         }
00877 #ifndef NO_IPV6_PMTUD
00878         case SOCKET_IPV6_USE_MIN_MTU: {
00879             if (opt_len != sizeof(int8_t)) {
00880                 return -3;
00881             }
00882 
00883             int8_t mode = *(const int8_t *) opt_value;
00884             if (mode < -1 || mode > 1) {
00885                 return -3;
00886             }
00887 
00888             inet_pcb->use_min_mtu  = mode;
00889 
00890             return 0;
00891         }
00892 #endif
00893 #ifndef NO_IP_FRAGMENT_TX
00894         case SOCKET_IPV6_DONTFRAG: {
00895             if (opt_len != sizeof(int8_t)) {
00896                 return -3;
00897             }
00898 
00899             int8_t mode = *(const int8_t *) opt_value;
00900             if (mode < 0 || mode > 1) {
00901                 return -3;
00902             }
00903 
00904             inet_pcb->dontfrag = mode;
00905 
00906             return 0;
00907         }
00908 #endif
00909         /* Are there any users of this yet? Can we remove it? */
00910         case SOCKET_IPV6_ADDRESS_SELECT: {
00911             if (opt_len != sizeof(int16_t)) {
00912                 return -3;
00913             }
00914 
00915             int16_t address_mode = *(const int16_t *) opt_value;
00916 
00917             if (address_mode == SOCKET_SRC_ADDRESS_MODE_PRIMARY) {
00918                 inet_pcb->addr_preferences |= SOCKET_IPV6_PREFER_SRC_6LOWPAN_SHORT;
00919                 inet_pcb->addr_preferences &= ~(uint32_t) SOCKET_IPV6_PREFER_SRC_6LOWPAN_LONG;
00920             } else if (address_mode == SOCKET_SRC_ADDRESS_MODE_SECONDARY) {
00921                 inet_pcb->addr_preferences |= SOCKET_IPV6_PREFER_SRC_6LOWPAN_LONG;
00922                 inet_pcb->addr_preferences &= ~(uint32_t) SOCKET_IPV6_PREFER_SRC_6LOWPAN_SHORT;
00923             } else {
00924                 return -3;
00925             }
00926 
00927             return 0;
00928         }
00929 
00930         case SOCKET_IPV6_RECVPKTINFO: {
00931             if (opt_len != sizeof(bool)) {
00932                 return -3;
00933             }
00934             inet_pcb->recvpktinfo = *(const bool *) opt_value;
00935             return 0;
00936         }
00937         case SOCKET_IPV6_RECVHOPLIMIT: {
00938             if (opt_len != sizeof(bool)) {
00939                 return -3;
00940             }
00941             inet_pcb->recvhoplimit = *(const bool *) opt_value;
00942             return 0;
00943         }
00944 
00945         case SOCKET_IPV6_RECVTCLASS: {
00946             if (opt_len != sizeof(bool)) {
00947                 return -3;
00948             }
00949             inet_pcb->recvtclass = *(const bool *) opt_value;
00950             return 0;
00951         }
00952         default:
00953             return -2;
00954     }
00955 }
00956 
00957 int8_t socket_setsockopt(int8_t socket, uint8_t level, uint8_t opt_name, const void *opt_value, uint16_t opt_len)
00958 {
00959     socket_t *socket_ptr = socket_pointer_get(socket);
00960     if (!opt_value || !socket_ptr) {
00961         return -1;
00962     }
00963 
00964     if (level == SOCKET_IPPROTO_IPV6 && socket_is_ipv6(socket_ptr)) {
00965         return ipv6_setsockopt(socket_ptr, opt_name, opt_value, opt_len);
00966     }
00967 
00968     if (level != SOCKET_SOL_SOCKET) {
00969         return -2;
00970     }
00971 
00972     switch (opt_name) {
00973         case SOCKET_SO_SNDBUF:
00974         case SOCKET_SO_RCVBUF: {
00975             if (opt_len != sizeof(int32_t)) {
00976                 return -3;
00977             }
00978             int32_t value = *(const int32_t *) opt_value;
00979             if (value < 0) {
00980                 return -3;
00981             }
00982             sockbuf_t *sb = opt_name == SOCKET_SO_SNDBUF ? &socket_ptr->sndq
00983                                                          : &socket_ptr->rcvq;
00984             return sockbuf_reserve(sb, value) ? 0 : -3;
00985         }
00986         case SOCKET_SO_SNDLOWAT: {
00987             if (opt_len != sizeof(int32_t)) {
00988                 return -3;
00989             }
00990             int32_t value = *(const int32_t *) opt_value;
00991             if (value < 1) {
00992                 return -3;
00993             }
00994             sockbuf_t *sb = &socket_ptr->sndq;
00995             if ((uint32_t) value > sb->data_byte_limit) {
00996                 value = sb->data_byte_limit;
00997             }
00998             sb->low_water_mark = value;
00999             return 0;
01000         }
01001         default:
01002             return -2;
01003     }
01004 }
01005 
01006 static union {
01007     int8_t s8;
01008     uint16_t u16;
01009     int16_t s16;
01010     uint32_t u32;
01011     int32_t s32;
01012     bool boolean;
01013 } opt_temp;
01014 
01015 static int8_t ipv6_getsockopt(const socket_t *socket_ptr, uint8_t opt_name, const void **value, uint16_t *len)
01016 {
01017     const inet_pcb_t *inet_pcb = socket_ptr->inet_pcb ;
01018 
01019     switch (opt_name) {
01020         case SOCKET_INTERFACE_SELECT: {
01021             const int8_t *p = &socket_ptr->default_interface_id;
01022             *value = p;
01023             *len = sizeof *p;
01024             break;
01025         }
01026         case SOCKET_LINK_LAYER_SECURITY: {
01027             const int8_t *p = &inet_pcb->link_layer_security;
01028             *value = p;
01029             *len = sizeof *p;
01030             break;
01031         }
01032         case SOCKET_BROADCAST_PAN: {
01033             const int8_t *p = &socket_ptr->broadcast_pan;
01034             *value = p;
01035             *len = sizeof *p;
01036             break;
01037         }
01038         case SOCKET_IPV6_TCLASS: {
01039             opt_temp.s16 = inet_pcb->tclass;
01040             *value = &opt_temp.s16;
01041             *len = sizeof(int16_t);
01042             break;
01043         }
01044         case SOCKET_IPV6_FLOW_LABEL: {
01045             opt_temp.s32 = inet_pcb->flow_label;
01046             *value = &opt_temp.s32;
01047             *len = sizeof(int32_t);
01048             break;
01049         }
01050         case SOCKET_IPV6_UNICAST_HOPS: {
01051             opt_temp.s16 = inet_pcb->unicast_hop_limit ;
01052             if (opt_temp.s16 == -1) {
01053                 /* Hop limit is determined dynamically on TX - try to work out
01054                  * a sensible number for the caller; we can't just say -1.
01055                  */
01056                 opt_temp.s16 = UNICAST_HOP_LIMIT_DEFAULT;
01057 
01058                 /* Try to look at interface for connected sockets */
01059                 if (socket_ptr->type == SOCKET_TYPE_STREAM) {
01060                     tcp_session_t *info = tcp_info(socket_ptr->inet_pcb );
01061                     if (info) {
01062                         opt_temp.s16 = info->interface->cur_hop_limit;
01063                     }
01064                 }
01065             }
01066             *value = &opt_temp.s16;
01067             *len = sizeof(int16_t);
01068             break;
01069         }
01070         case SOCKET_IPV6_MULTICAST_HOPS: {
01071             opt_temp.s16 = inet_pcb->multicast_hop_limit;
01072             /* Unlike unicast hop limit, this can't be -1 */
01073             *value = &opt_temp.s16;
01074             *len = sizeof(int16_t);
01075             break;
01076         }
01077         case SOCKET_IPV6_MULTICAST_IF: {
01078             opt_temp.s8 = inet_pcb->multicast_if;
01079             *value = &opt_temp.s8;
01080             *len = sizeof(int8_t);
01081             break;
01082         }
01083         case SOCKET_IPV6_MULTICAST_LOOP: {
01084             opt_temp.boolean = inet_pcb->multicast_loop;
01085             *value = &opt_temp.boolean;
01086             *len = sizeof(bool);
01087             break;
01088         }
01089         case SOCKET_IPV6_ADDR_PREFERENCES: {
01090             const uint32_t *p = &inet_pcb->addr_preferences;
01091             *value = p;
01092             *len = sizeof *p;
01093             break;
01094         }
01095 #ifndef NO_IPV6_PMTUD
01096         case SOCKET_IPV6_USE_MIN_MTU: {
01097             const int8_t *p = &inet_pcb->use_min_mtu ;
01098             *value = p;
01099             *len = sizeof *p;
01100             break;
01101         }
01102 #endif
01103 #ifndef NO_IP_FRAGMENT_TX
01104         case SOCKET_IPV6_DONTFRAG: {
01105             const int8_t *p = &inet_pcb->dontfrag;
01106             *value = p;
01107             *len = sizeof *p;
01108             break;
01109         }
01110 #endif
01111         case SOCKET_IPV6_RECVPKTINFO:
01112             opt_temp.boolean = inet_pcb->recvpktinfo;
01113             *value = &opt_temp.boolean;
01114             *len = sizeof(bool);
01115             break;
01116         case SOCKET_IPV6_RECVHOPLIMIT:
01117             opt_temp.boolean = inet_pcb->recvhoplimit;
01118             *value = &opt_temp.boolean;
01119             *len = sizeof(bool);
01120             break;
01121         case SOCKET_IPV6_RECVTCLASS:
01122             opt_temp.boolean = inet_pcb->recvtclass;
01123             *value = &opt_temp.boolean;
01124             *len = sizeof(bool);
01125             break;
01126 
01127         default:
01128             return -2;
01129     }
01130 
01131     return 0;
01132 }
01133 
01134 int8_t socket_getsockopt(int8_t socket, uint8_t level, uint8_t opt_name, void *opt_value, uint16_t *opt_len)
01135 {
01136     const socket_t *socket_ptr = socket_pointer_get(socket);
01137     if (!opt_value || !opt_len || !socket_ptr) {
01138         return -1;
01139     }
01140 
01141     const void *value;
01142     uint16_t len;
01143 
01144     if (level == SOCKET_IPPROTO_IPV6 && socket_is_ipv6(socket_ptr)) {
01145         int8_t ret = ipv6_getsockopt(socket_ptr, opt_name, &value, &len);
01146         if (ret != 0) {
01147             return ret;
01148         }
01149     } else if (level == SOCKET_SOL_SOCKET) {
01150         switch (opt_name) {
01151             case SOCKET_SO_SNDBUF:
01152                 opt_temp.s32 = socket_ptr->sndq.data_byte_limit;
01153                 value = &opt_temp.s32;
01154                 len = sizeof(int32_t);
01155                 break;
01156             case SOCKET_SO_RCVBUF:
01157                 opt_temp.s32 = socket_ptr->rcvq.data_byte_limit;
01158                 value = &opt_temp.s32;
01159                 len = sizeof(int32_t);
01160                 break;
01161             case SOCKET_SO_SNDLOWAT:
01162                 opt_temp.s32 = socket_ptr->rcvq.low_water_mark;
01163                 value = &opt_temp.s32;
01164                 len = sizeof(int32_t);
01165                 break;
01166             default:
01167                 return -2;
01168         }
01169     } else {
01170         return -2;
01171     }
01172 
01173     if (*opt_len > len) {
01174         *opt_len = len;
01175     }
01176 
01177     memcpy(opt_value, value, *opt_len);
01178 
01179     return 0;
01180 }
01181 
01182 ns_cmsghdr_t *NS_CMSG_NXTHDR(const ns_msghdr_t *msgh, const ns_cmsghdr_t *cmsg)
01183 {
01184     if (!cmsg) {
01185         return NS_CMSG_FIRSTHDR(msgh);
01186     }
01187     uint8_t *start_of_next_header = (uint8_t *) (cmsg) + NS_ALIGN_SIZE(cmsg->cmsg_len, CMSG_HEADER_ALIGN);
01188     uint8_t *end_of_next_header = start_of_next_header + NS_ALIGN_SIZE(sizeof(ns_cmsghdr_t), CMSG_DATA_ALIGN);
01189     if (end_of_next_header > (uint8_t *) (msgh)->msg_control + (msgh)->msg_controllen) {
01190         return NULL;
01191     }
01192     return (ns_cmsghdr_t *) start_of_next_header;
01193 }
01194