1*97dc5e69SMatthias Ringwald /**
2*97dc5e69SMatthias Ringwald * @file
3*97dc5e69SMatthias Ringwald * Sockets BSD-Like API module
4*97dc5e69SMatthias Ringwald */
5*97dc5e69SMatthias Ringwald
6*97dc5e69SMatthias Ringwald /*
7*97dc5e69SMatthias Ringwald * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
8*97dc5e69SMatthias Ringwald * All rights reserved.
9*97dc5e69SMatthias Ringwald *
10*97dc5e69SMatthias Ringwald * Redistribution and use in source and binary forms, with or without modification,
11*97dc5e69SMatthias Ringwald * are permitted provided that the following conditions are met:
12*97dc5e69SMatthias Ringwald *
13*97dc5e69SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright notice,
14*97dc5e69SMatthias Ringwald * this list of conditions and the following disclaimer.
15*97dc5e69SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright notice,
16*97dc5e69SMatthias Ringwald * this list of conditions and the following disclaimer in the documentation
17*97dc5e69SMatthias Ringwald * and/or other materials provided with the distribution.
18*97dc5e69SMatthias Ringwald * 3. The name of the author may not be used to endorse or promote products
19*97dc5e69SMatthias Ringwald * derived from this software without specific prior written permission.
20*97dc5e69SMatthias Ringwald *
21*97dc5e69SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22*97dc5e69SMatthias Ringwald * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23*97dc5e69SMatthias Ringwald * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24*97dc5e69SMatthias Ringwald * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25*97dc5e69SMatthias Ringwald * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26*97dc5e69SMatthias Ringwald * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27*97dc5e69SMatthias Ringwald * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28*97dc5e69SMatthias Ringwald * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29*97dc5e69SMatthias Ringwald * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30*97dc5e69SMatthias Ringwald * OF SUCH DAMAGE.
31*97dc5e69SMatthias Ringwald *
32*97dc5e69SMatthias Ringwald * This file is part of the lwIP TCP/IP stack.
33*97dc5e69SMatthias Ringwald *
34*97dc5e69SMatthias Ringwald * Author: Adam Dunkels <[email protected]>
35*97dc5e69SMatthias Ringwald *
36*97dc5e69SMatthias Ringwald * Improved by Marc Boucher <[email protected]> and David Haas <[email protected]>
37*97dc5e69SMatthias Ringwald *
38*97dc5e69SMatthias Ringwald */
39*97dc5e69SMatthias Ringwald
40*97dc5e69SMatthias Ringwald #include "lwip/opt.h"
41*97dc5e69SMatthias Ringwald
42*97dc5e69SMatthias Ringwald #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
43*97dc5e69SMatthias Ringwald
44*97dc5e69SMatthias Ringwald #include "lwip/sockets.h"
45*97dc5e69SMatthias Ringwald #include "lwip/priv/sockets_priv.h"
46*97dc5e69SMatthias Ringwald #include "lwip/api.h"
47*97dc5e69SMatthias Ringwald #include "lwip/igmp.h"
48*97dc5e69SMatthias Ringwald #include "lwip/inet.h"
49*97dc5e69SMatthias Ringwald #include "lwip/tcp.h"
50*97dc5e69SMatthias Ringwald #include "lwip/raw.h"
51*97dc5e69SMatthias Ringwald #include "lwip/udp.h"
52*97dc5e69SMatthias Ringwald #include "lwip/memp.h"
53*97dc5e69SMatthias Ringwald #include "lwip/pbuf.h"
54*97dc5e69SMatthias Ringwald #include "lwip/netif.h"
55*97dc5e69SMatthias Ringwald #include "lwip/priv/tcpip_priv.h"
56*97dc5e69SMatthias Ringwald #include "lwip/mld6.h"
57*97dc5e69SMatthias Ringwald #if LWIP_CHECKSUM_ON_COPY
58*97dc5e69SMatthias Ringwald #include "lwip/inet_chksum.h"
59*97dc5e69SMatthias Ringwald #endif
60*97dc5e69SMatthias Ringwald
61*97dc5e69SMatthias Ringwald #if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
62*97dc5e69SMatthias Ringwald #include <stdarg.h>
63*97dc5e69SMatthias Ringwald #endif
64*97dc5e69SMatthias Ringwald
65*97dc5e69SMatthias Ringwald #include <string.h>
66*97dc5e69SMatthias Ringwald
67*97dc5e69SMatthias Ringwald #ifdef LWIP_HOOK_FILENAME
68*97dc5e69SMatthias Ringwald #include LWIP_HOOK_FILENAME
69*97dc5e69SMatthias Ringwald #endif
70*97dc5e69SMatthias Ringwald
71*97dc5e69SMatthias Ringwald /* If the netconn API is not required publicly, then we include the necessary
72*97dc5e69SMatthias Ringwald files here to get the implementation */
73*97dc5e69SMatthias Ringwald #if !LWIP_NETCONN
74*97dc5e69SMatthias Ringwald #undef LWIP_NETCONN
75*97dc5e69SMatthias Ringwald #define LWIP_NETCONN 1
76*97dc5e69SMatthias Ringwald #include "api_msg.c"
77*97dc5e69SMatthias Ringwald #include "api_lib.c"
78*97dc5e69SMatthias Ringwald #include "netbuf.c"
79*97dc5e69SMatthias Ringwald #undef LWIP_NETCONN
80*97dc5e69SMatthias Ringwald #define LWIP_NETCONN 0
81*97dc5e69SMatthias Ringwald #endif
82*97dc5e69SMatthias Ringwald
83*97dc5e69SMatthias Ringwald #define API_SELECT_CB_VAR_REF(name) API_VAR_REF(name)
84*97dc5e69SMatthias Ringwald #define API_SELECT_CB_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_select_cb, name)
85*97dc5e69SMatthias Ringwald #define API_SELECT_CB_VAR_ALLOC(name, retblock) API_VAR_ALLOC_EXT(struct lwip_select_cb, MEMP_SELECT_CB, name, retblock)
86*97dc5e69SMatthias Ringwald #define API_SELECT_CB_VAR_FREE(name) API_VAR_FREE(MEMP_SELECT_CB, name)
87*97dc5e69SMatthias Ringwald
88*97dc5e69SMatthias Ringwald #if LWIP_IPV4
89*97dc5e69SMatthias Ringwald #define IP4ADDR_PORT_TO_SOCKADDR(sin, ipaddr, port) do { \
90*97dc5e69SMatthias Ringwald (sin)->sin_len = sizeof(struct sockaddr_in); \
91*97dc5e69SMatthias Ringwald (sin)->sin_family = AF_INET; \
92*97dc5e69SMatthias Ringwald (sin)->sin_port = lwip_htons((port)); \
93*97dc5e69SMatthias Ringwald inet_addr_from_ip4addr(&(sin)->sin_addr, ipaddr); \
94*97dc5e69SMatthias Ringwald memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
95*97dc5e69SMatthias Ringwald #define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
96*97dc5e69SMatthias Ringwald inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
97*97dc5e69SMatthias Ringwald (port) = lwip_ntohs((sin)->sin_port); }while(0)
98*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 */
99*97dc5e69SMatthias Ringwald
100*97dc5e69SMatthias Ringwald #if LWIP_IPV6
101*97dc5e69SMatthias Ringwald #define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipaddr, port) do { \
102*97dc5e69SMatthias Ringwald (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
103*97dc5e69SMatthias Ringwald (sin6)->sin6_family = AF_INET6; \
104*97dc5e69SMatthias Ringwald (sin6)->sin6_port = lwip_htons((port)); \
105*97dc5e69SMatthias Ringwald (sin6)->sin6_flowinfo = 0; \
106*97dc5e69SMatthias Ringwald inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipaddr); \
107*97dc5e69SMatthias Ringwald (sin6)->sin6_scope_id = ip6_addr_zone(ipaddr); }while(0)
108*97dc5e69SMatthias Ringwald #define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
109*97dc5e69SMatthias Ringwald inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
110*97dc5e69SMatthias Ringwald if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
111*97dc5e69SMatthias Ringwald ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
112*97dc5e69SMatthias Ringwald } \
113*97dc5e69SMatthias Ringwald (port) = lwip_ntohs((sin6)->sin6_port); }while(0)
114*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 */
115*97dc5e69SMatthias Ringwald
116*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
117*97dc5e69SMatthias Ringwald static void sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port);
118*97dc5e69SMatthias Ringwald
119*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
120*97dc5e69SMatthias Ringwald ((namelen) == sizeof(struct sockaddr_in6)))
121*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
122*97dc5e69SMatthias Ringwald ((name)->sa_family == AF_INET6))
123*97dc5e69SMatthias Ringwald #define SOCK_ADDR_TYPE_MATCH(name, sock) \
124*97dc5e69SMatthias Ringwald ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
125*97dc5e69SMatthias Ringwald (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
126*97dc5e69SMatthias Ringwald #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
127*97dc5e69SMatthias Ringwald if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*ipaddr)) { \
128*97dc5e69SMatthias Ringwald IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port); \
129*97dc5e69SMatthias Ringwald } else { \
130*97dc5e69SMatthias Ringwald IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port); \
131*97dc5e69SMatthias Ringwald } } while(0)
132*97dc5e69SMatthias Ringwald #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
133*97dc5e69SMatthias Ringwald #define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
134*97dc5e69SMatthias Ringwald (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
135*97dc5e69SMatthias Ringwald #elif LWIP_IPV6 /* LWIP_IPV4 && LWIP_IPV6 */
136*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in6))
137*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET6)
138*97dc5e69SMatthias Ringwald #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
139*97dc5e69SMatthias Ringwald #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
140*97dc5e69SMatthias Ringwald IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ip_2_ip6(ipaddr), port)
141*97dc5e69SMatthias Ringwald #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
142*97dc5e69SMatthias Ringwald SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
143*97dc5e69SMatthias Ringwald #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
144*97dc5e69SMatthias Ringwald #else /*-> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
145*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
146*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
147*97dc5e69SMatthias Ringwald #define SOCK_ADDR_TYPE_MATCH(name, sock) 1
148*97dc5e69SMatthias Ringwald #define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) \
149*97dc5e69SMatthias Ringwald IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ip_2_ip4(ipaddr), port)
150*97dc5e69SMatthias Ringwald #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
151*97dc5e69SMatthias Ringwald SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
152*97dc5e69SMatthias Ringwald #define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
153*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 */
154*97dc5e69SMatthias Ringwald
155*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \
156*97dc5e69SMatthias Ringwald IS_SOCK_ADDR_TYPE_VALID(name))
157*97dc5e69SMatthias Ringwald #define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
158*97dc5e69SMatthias Ringwald SOCK_ADDR_TYPE_MATCH(name, sock))
159*97dc5e69SMatthias Ringwald #define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
160*97dc5e69SMatthias Ringwald
161*97dc5e69SMatthias Ringwald
162*97dc5e69SMatthias Ringwald #define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
163*97dc5e69SMatthias Ringwald #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
164*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
165*97dc5e69SMatthias Ringwald if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
166*97dc5e69SMatthias Ringwald #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
167*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
168*97dc5e69SMatthias Ringwald if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); return EINVAL; } }while(0)
169*97dc5e69SMatthias Ringwald #define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \
170*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \
171*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { done_socket(sock); return ENOPROTOOPT; } }while(0)
172*97dc5e69SMatthias Ringwald
173*97dc5e69SMatthias Ringwald
174*97dc5e69SMatthias Ringwald #define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name)
175*97dc5e69SMatthias Ringwald #define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name)
176*97dc5e69SMatthias Ringwald #define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name)
177*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
178*97dc5e69SMatthias Ringwald #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \
179*97dc5e69SMatthias Ringwald name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \
180*97dc5e69SMatthias Ringwald if (name == NULL) { \
181*97dc5e69SMatthias Ringwald set_errno(ENOMEM); \
182*97dc5e69SMatthias Ringwald done_socket(sock); \
183*97dc5e69SMatthias Ringwald return -1; \
184*97dc5e69SMatthias Ringwald } }while(0)
185*97dc5e69SMatthias Ringwald #else /* LWIP_MPU_COMPATIBLE */
186*97dc5e69SMatthias Ringwald #define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock)
187*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
188*97dc5e69SMatthias Ringwald
189*97dc5e69SMatthias Ringwald #if LWIP_SO_SNDRCVTIMEO_NONSTANDARD
190*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_OPTTYPE int
191*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val))
192*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((long)*(const int*)(optval))
193*97dc5e69SMatthias Ringwald #else
194*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
195*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
196*97dc5e69SMatthias Ringwald u32_t loc = (val); \
197*97dc5e69SMatthias Ringwald ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
198*97dc5e69SMatthias Ringwald ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
199*97dc5e69SMatthias Ringwald #define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
200*97dc5e69SMatthias Ringwald #endif
201*97dc5e69SMatthias Ringwald
202*97dc5e69SMatthias Ringwald
203*97dc5e69SMatthias Ringwald /** A struct sockaddr replacement that has the same alignment as sockaddr_in/
204*97dc5e69SMatthias Ringwald * sockaddr_in6 if instantiated.
205*97dc5e69SMatthias Ringwald */
206*97dc5e69SMatthias Ringwald union sockaddr_aligned {
207*97dc5e69SMatthias Ringwald struct sockaddr sa;
208*97dc5e69SMatthias Ringwald #if LWIP_IPV6
209*97dc5e69SMatthias Ringwald struct sockaddr_in6 sin6;
210*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 */
211*97dc5e69SMatthias Ringwald #if LWIP_IPV4
212*97dc5e69SMatthias Ringwald struct sockaddr_in sin;
213*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 */
214*97dc5e69SMatthias Ringwald };
215*97dc5e69SMatthias Ringwald
216*97dc5e69SMatthias Ringwald /* Define the number of IPv4 multicast memberships, default is one per socket */
217*97dc5e69SMatthias Ringwald #ifndef LWIP_SOCKET_MAX_MEMBERSHIPS
218*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_MAX_MEMBERSHIPS NUM_SOCKETS
219*97dc5e69SMatthias Ringwald #endif
220*97dc5e69SMatthias Ringwald
221*97dc5e69SMatthias Ringwald #if LWIP_IGMP
222*97dc5e69SMatthias Ringwald /* This is to keep track of IP_ADD_MEMBERSHIP calls to drop the membership when
223*97dc5e69SMatthias Ringwald a socket is closed */
224*97dc5e69SMatthias Ringwald struct lwip_socket_multicast_pair {
225*97dc5e69SMatthias Ringwald /** the socket */
226*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
227*97dc5e69SMatthias Ringwald /** the interface address */
228*97dc5e69SMatthias Ringwald ip4_addr_t if_addr;
229*97dc5e69SMatthias Ringwald /** the group address */
230*97dc5e69SMatthias Ringwald ip4_addr_t multi_addr;
231*97dc5e69SMatthias Ringwald };
232*97dc5e69SMatthias Ringwald
233*97dc5e69SMatthias Ringwald static struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
234*97dc5e69SMatthias Ringwald
235*97dc5e69SMatthias Ringwald static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
236*97dc5e69SMatthias Ringwald static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr);
237*97dc5e69SMatthias Ringwald static void lwip_socket_drop_registered_memberships(int s);
238*97dc5e69SMatthias Ringwald #endif /* LWIP_IGMP */
239*97dc5e69SMatthias Ringwald
240*97dc5e69SMatthias Ringwald #if LWIP_IPV6_MLD
241*97dc5e69SMatthias Ringwald /* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
242*97dc5e69SMatthias Ringwald a socket is closed */
243*97dc5e69SMatthias Ringwald struct lwip_socket_multicast_mld6_pair {
244*97dc5e69SMatthias Ringwald /** the socket */
245*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
246*97dc5e69SMatthias Ringwald /** the interface index */
247*97dc5e69SMatthias Ringwald u8_t if_idx;
248*97dc5e69SMatthias Ringwald /** the group address */
249*97dc5e69SMatthias Ringwald ip6_addr_t multi_addr;
250*97dc5e69SMatthias Ringwald };
251*97dc5e69SMatthias Ringwald
252*97dc5e69SMatthias Ringwald static struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
253*97dc5e69SMatthias Ringwald
254*97dc5e69SMatthias Ringwald static int lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
255*97dc5e69SMatthias Ringwald static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
256*97dc5e69SMatthias Ringwald static void lwip_socket_drop_registered_mld6_memberships(int s);
257*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6_MLD */
258*97dc5e69SMatthias Ringwald
259*97dc5e69SMatthias Ringwald /** The global array of available sockets */
260*97dc5e69SMatthias Ringwald static struct lwip_sock sockets[NUM_SOCKETS];
261*97dc5e69SMatthias Ringwald
262*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
263*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
264*97dc5e69SMatthias Ringwald /* protect the select_cb_list using core lock */
265*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
266*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_PROTECT(lev) LOCK_TCPIP_CORE()
267*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
268*97dc5e69SMatthias Ringwald #else /* LWIP_TCPIP_CORE_LOCKING */
269*97dc5e69SMatthias Ringwald /* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
270*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
271*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_PROTECT(lev) SYS_ARCH_PROTECT(lev)
272*97dc5e69SMatthias Ringwald #define LWIP_SOCKET_SELECT_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
273*97dc5e69SMatthias Ringwald /** This counter is increased from lwip_select when the list is changed
274*97dc5e69SMatthias Ringwald and checked in select_check_waiters to see if it has changed. */
275*97dc5e69SMatthias Ringwald static volatile int select_cb_ctr;
276*97dc5e69SMatthias Ringwald #endif /* LWIP_TCPIP_CORE_LOCKING */
277*97dc5e69SMatthias Ringwald /** The global list of tasks waiting for select */
278*97dc5e69SMatthias Ringwald static struct lwip_select_cb *select_cb_list;
279*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
280*97dc5e69SMatthias Ringwald
281*97dc5e69SMatthias Ringwald /* Forward declaration of some functions */
282*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
283*97dc5e69SMatthias Ringwald static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
284*97dc5e69SMatthias Ringwald #define DEFAULT_SOCKET_EVENTCB event_callback
285*97dc5e69SMatthias Ringwald static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
286*97dc5e69SMatthias Ringwald #else
287*97dc5e69SMatthias Ringwald #define DEFAULT_SOCKET_EVENTCB NULL
288*97dc5e69SMatthias Ringwald #endif
289*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
290*97dc5e69SMatthias Ringwald static void lwip_getsockopt_callback(void *arg);
291*97dc5e69SMatthias Ringwald static void lwip_setsockopt_callback(void *arg);
292*97dc5e69SMatthias Ringwald #endif
293*97dc5e69SMatthias Ringwald static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
294*97dc5e69SMatthias Ringwald static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
295*97dc5e69SMatthias Ringwald static int free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
296*97dc5e69SMatthias Ringwald union lwip_sock_lastdata *lastdata);
297*97dc5e69SMatthias Ringwald static void free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata);
298*97dc5e69SMatthias Ringwald
299*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
300*97dc5e69SMatthias Ringwald static void
sockaddr_to_ipaddr_port(const struct sockaddr * sockaddr,ip_addr_t * ipaddr,u16_t * port)301*97dc5e69SMatthias Ringwald sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port)
302*97dc5e69SMatthias Ringwald {
303*97dc5e69SMatthias Ringwald if ((sockaddr->sa_family) == AF_INET6) {
304*97dc5e69SMatthias Ringwald SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6 *)(const void *)(sockaddr), ipaddr, *port);
305*97dc5e69SMatthias Ringwald ipaddr->type = IPADDR_TYPE_V6;
306*97dc5e69SMatthias Ringwald } else {
307*97dc5e69SMatthias Ringwald SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in *)(const void *)(sockaddr), ipaddr, *port);
308*97dc5e69SMatthias Ringwald ipaddr->type = IPADDR_TYPE_V4;
309*97dc5e69SMatthias Ringwald }
310*97dc5e69SMatthias Ringwald }
311*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
312*97dc5e69SMatthias Ringwald
313*97dc5e69SMatthias Ringwald /** LWIP_NETCONN_SEM_PER_THREAD==1: initialize thread-local semaphore */
314*97dc5e69SMatthias Ringwald void
lwip_socket_thread_init(void)315*97dc5e69SMatthias Ringwald lwip_socket_thread_init(void)
316*97dc5e69SMatthias Ringwald {
317*97dc5e69SMatthias Ringwald netconn_thread_init();
318*97dc5e69SMatthias Ringwald }
319*97dc5e69SMatthias Ringwald
320*97dc5e69SMatthias Ringwald /** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
321*97dc5e69SMatthias Ringwald void
lwip_socket_thread_cleanup(void)322*97dc5e69SMatthias Ringwald lwip_socket_thread_cleanup(void)
323*97dc5e69SMatthias Ringwald {
324*97dc5e69SMatthias Ringwald netconn_thread_cleanup();
325*97dc5e69SMatthias Ringwald }
326*97dc5e69SMatthias Ringwald
327*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
328*97dc5e69SMatthias Ringwald /* Thread-safe increment of sock->fd_used, with overflow check */
329*97dc5e69SMatthias Ringwald static int
sock_inc_used(struct lwip_sock * sock)330*97dc5e69SMatthias Ringwald sock_inc_used(struct lwip_sock *sock)
331*97dc5e69SMatthias Ringwald {
332*97dc5e69SMatthias Ringwald int ret;
333*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
334*97dc5e69SMatthias Ringwald
335*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock != NULL", sock != NULL);
336*97dc5e69SMatthias Ringwald
337*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
338*97dc5e69SMatthias Ringwald if (sock->fd_free_pending) {
339*97dc5e69SMatthias Ringwald /* prevent new usage of this socket if free is pending */
340*97dc5e69SMatthias Ringwald ret = 0;
341*97dc5e69SMatthias Ringwald } else {
342*97dc5e69SMatthias Ringwald ++sock->fd_used;
343*97dc5e69SMatthias Ringwald ret = 1;
344*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
345*97dc5e69SMatthias Ringwald }
346*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
347*97dc5e69SMatthias Ringwald return ret;
348*97dc5e69SMatthias Ringwald }
349*97dc5e69SMatthias Ringwald
350*97dc5e69SMatthias Ringwald /* Like sock_inc_used(), but called under SYS_ARCH_PROTECT lock. */
351*97dc5e69SMatthias Ringwald static int
sock_inc_used_locked(struct lwip_sock * sock)352*97dc5e69SMatthias Ringwald sock_inc_used_locked(struct lwip_sock *sock)
353*97dc5e69SMatthias Ringwald {
354*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock != NULL", sock != NULL);
355*97dc5e69SMatthias Ringwald
356*97dc5e69SMatthias Ringwald if (sock->fd_free_pending) {
357*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
358*97dc5e69SMatthias Ringwald return 0;
359*97dc5e69SMatthias Ringwald }
360*97dc5e69SMatthias Ringwald
361*97dc5e69SMatthias Ringwald ++sock->fd_used;
362*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
363*97dc5e69SMatthias Ringwald return 1;
364*97dc5e69SMatthias Ringwald }
365*97dc5e69SMatthias Ringwald
366*97dc5e69SMatthias Ringwald /* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
367*97dc5e69SMatthias Ringwald * released (and possibly reused) when used from more than one thread
368*97dc5e69SMatthias Ringwald * (e.g. read-while-write or close-while-write, etc)
369*97dc5e69SMatthias Ringwald * This function is called at the end of functions using (try)get_socket*().
370*97dc5e69SMatthias Ringwald */
371*97dc5e69SMatthias Ringwald static void
done_socket(struct lwip_sock * sock)372*97dc5e69SMatthias Ringwald done_socket(struct lwip_sock *sock)
373*97dc5e69SMatthias Ringwald {
374*97dc5e69SMatthias Ringwald int freed = 0;
375*97dc5e69SMatthias Ringwald int is_tcp = 0;
376*97dc5e69SMatthias Ringwald struct netconn *conn = NULL;
377*97dc5e69SMatthias Ringwald union lwip_sock_lastdata lastdata;
378*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
379*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock != NULL", sock != NULL);
380*97dc5e69SMatthias Ringwald
381*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
382*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
383*97dc5e69SMatthias Ringwald if (--sock->fd_used == 0) {
384*97dc5e69SMatthias Ringwald if (sock->fd_free_pending) {
385*97dc5e69SMatthias Ringwald /* free the socket */
386*97dc5e69SMatthias Ringwald sock->fd_used = 1;
387*97dc5e69SMatthias Ringwald is_tcp = sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP;
388*97dc5e69SMatthias Ringwald freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
389*97dc5e69SMatthias Ringwald }
390*97dc5e69SMatthias Ringwald }
391*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
392*97dc5e69SMatthias Ringwald
393*97dc5e69SMatthias Ringwald if (freed) {
394*97dc5e69SMatthias Ringwald free_socket_free_elements(is_tcp, conn, &lastdata);
395*97dc5e69SMatthias Ringwald }
396*97dc5e69SMatthias Ringwald }
397*97dc5e69SMatthias Ringwald
398*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_FULLDUPLEX */
399*97dc5e69SMatthias Ringwald #define sock_inc_used(sock) 1
400*97dc5e69SMatthias Ringwald #define sock_inc_used_locked(sock) 1
401*97dc5e69SMatthias Ringwald #define done_socket(sock)
402*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_FULLDUPLEX */
403*97dc5e69SMatthias Ringwald
404*97dc5e69SMatthias Ringwald /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
405*97dc5e69SMatthias Ringwald static struct lwip_sock *
tryget_socket_unconn_nouse(int fd)406*97dc5e69SMatthias Ringwald tryget_socket_unconn_nouse(int fd)
407*97dc5e69SMatthias Ringwald {
408*97dc5e69SMatthias Ringwald int s = fd - LWIP_SOCKET_OFFSET;
409*97dc5e69SMatthias Ringwald if ((s < 0) || (s >= NUM_SOCKETS)) {
410*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
411*97dc5e69SMatthias Ringwald return NULL;
412*97dc5e69SMatthias Ringwald }
413*97dc5e69SMatthias Ringwald return &sockets[s];
414*97dc5e69SMatthias Ringwald }
415*97dc5e69SMatthias Ringwald
416*97dc5e69SMatthias Ringwald struct lwip_sock *
lwip_socket_dbg_get_socket(int fd)417*97dc5e69SMatthias Ringwald lwip_socket_dbg_get_socket(int fd)
418*97dc5e69SMatthias Ringwald {
419*97dc5e69SMatthias Ringwald return tryget_socket_unconn_nouse(fd);
420*97dc5e69SMatthias Ringwald }
421*97dc5e69SMatthias Ringwald
422*97dc5e69SMatthias Ringwald /* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
423*97dc5e69SMatthias Ringwald static struct lwip_sock *
tryget_socket_unconn(int fd)424*97dc5e69SMatthias Ringwald tryget_socket_unconn(int fd)
425*97dc5e69SMatthias Ringwald {
426*97dc5e69SMatthias Ringwald struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
427*97dc5e69SMatthias Ringwald if (ret != NULL) {
428*97dc5e69SMatthias Ringwald if (!sock_inc_used(ret)) {
429*97dc5e69SMatthias Ringwald return NULL;
430*97dc5e69SMatthias Ringwald }
431*97dc5e69SMatthias Ringwald }
432*97dc5e69SMatthias Ringwald return ret;
433*97dc5e69SMatthias Ringwald }
434*97dc5e69SMatthias Ringwald
435*97dc5e69SMatthias Ringwald /* Like tryget_socket_unconn(), but called under SYS_ARCH_PROTECT lock. */
436*97dc5e69SMatthias Ringwald static struct lwip_sock *
tryget_socket_unconn_locked(int fd)437*97dc5e69SMatthias Ringwald tryget_socket_unconn_locked(int fd)
438*97dc5e69SMatthias Ringwald {
439*97dc5e69SMatthias Ringwald struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
440*97dc5e69SMatthias Ringwald if (ret != NULL) {
441*97dc5e69SMatthias Ringwald if (!sock_inc_used_locked(ret)) {
442*97dc5e69SMatthias Ringwald return NULL;
443*97dc5e69SMatthias Ringwald }
444*97dc5e69SMatthias Ringwald }
445*97dc5e69SMatthias Ringwald return ret;
446*97dc5e69SMatthias Ringwald }
447*97dc5e69SMatthias Ringwald
448*97dc5e69SMatthias Ringwald /**
449*97dc5e69SMatthias Ringwald * Same as get_socket but doesn't set errno
450*97dc5e69SMatthias Ringwald *
451*97dc5e69SMatthias Ringwald * @param fd externally used socket index
452*97dc5e69SMatthias Ringwald * @return struct lwip_sock for the socket or NULL if not found
453*97dc5e69SMatthias Ringwald */
454*97dc5e69SMatthias Ringwald static struct lwip_sock *
tryget_socket(int fd)455*97dc5e69SMatthias Ringwald tryget_socket(int fd)
456*97dc5e69SMatthias Ringwald {
457*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket_unconn(fd);
458*97dc5e69SMatthias Ringwald if (sock != NULL) {
459*97dc5e69SMatthias Ringwald if (sock->conn) {
460*97dc5e69SMatthias Ringwald return sock;
461*97dc5e69SMatthias Ringwald }
462*97dc5e69SMatthias Ringwald done_socket(sock);
463*97dc5e69SMatthias Ringwald }
464*97dc5e69SMatthias Ringwald return NULL;
465*97dc5e69SMatthias Ringwald }
466*97dc5e69SMatthias Ringwald
467*97dc5e69SMatthias Ringwald /**
468*97dc5e69SMatthias Ringwald * Map a externally used socket index to the internal socket representation.
469*97dc5e69SMatthias Ringwald *
470*97dc5e69SMatthias Ringwald * @param fd externally used socket index
471*97dc5e69SMatthias Ringwald * @return struct lwip_sock for the socket or NULL if not found
472*97dc5e69SMatthias Ringwald */
473*97dc5e69SMatthias Ringwald static struct lwip_sock *
get_socket(int fd)474*97dc5e69SMatthias Ringwald get_socket(int fd)
475*97dc5e69SMatthias Ringwald {
476*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket(fd);
477*97dc5e69SMatthias Ringwald if (!sock) {
478*97dc5e69SMatthias Ringwald if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
479*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
480*97dc5e69SMatthias Ringwald }
481*97dc5e69SMatthias Ringwald set_errno(EBADF);
482*97dc5e69SMatthias Ringwald return NULL;
483*97dc5e69SMatthias Ringwald }
484*97dc5e69SMatthias Ringwald return sock;
485*97dc5e69SMatthias Ringwald }
486*97dc5e69SMatthias Ringwald
487*97dc5e69SMatthias Ringwald /**
488*97dc5e69SMatthias Ringwald * Allocate a new socket for a given netconn.
489*97dc5e69SMatthias Ringwald *
490*97dc5e69SMatthias Ringwald * @param newconn the netconn for which to allocate a socket
491*97dc5e69SMatthias Ringwald * @param accepted 1 if socket has been created by accept(),
492*97dc5e69SMatthias Ringwald * 0 if socket has been created by socket()
493*97dc5e69SMatthias Ringwald * @return the index of the new socket; -1 on error
494*97dc5e69SMatthias Ringwald */
495*97dc5e69SMatthias Ringwald static int
alloc_socket(struct netconn * newconn,int accepted)496*97dc5e69SMatthias Ringwald alloc_socket(struct netconn *newconn, int accepted)
497*97dc5e69SMatthias Ringwald {
498*97dc5e69SMatthias Ringwald int i;
499*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
500*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(accepted);
501*97dc5e69SMatthias Ringwald
502*97dc5e69SMatthias Ringwald /* allocate a new socket identifier */
503*97dc5e69SMatthias Ringwald for (i = 0; i < NUM_SOCKETS; ++i) {
504*97dc5e69SMatthias Ringwald /* Protect socket array */
505*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
506*97dc5e69SMatthias Ringwald if (!sockets[i].conn) {
507*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
508*97dc5e69SMatthias Ringwald if (sockets[i].fd_used) {
509*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
510*97dc5e69SMatthias Ringwald continue;
511*97dc5e69SMatthias Ringwald }
512*97dc5e69SMatthias Ringwald sockets[i].fd_used = 1;
513*97dc5e69SMatthias Ringwald sockets[i].fd_free_pending = 0;
514*97dc5e69SMatthias Ringwald #endif
515*97dc5e69SMatthias Ringwald sockets[i].conn = newconn;
516*97dc5e69SMatthias Ringwald /* The socket is not yet known to anyone, so no need to protect
517*97dc5e69SMatthias Ringwald after having marked it as used. */
518*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
519*97dc5e69SMatthias Ringwald sockets[i].lastdata.pbuf = NULL;
520*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
521*97dc5e69SMatthias Ringwald LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
522*97dc5e69SMatthias Ringwald sockets[i].rcvevent = 0;
523*97dc5e69SMatthias Ringwald /* TCP sendbuf is empty, but the socket is not yet writable until connected
524*97dc5e69SMatthias Ringwald * (unless it has been created by accept()). */
525*97dc5e69SMatthias Ringwald sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
526*97dc5e69SMatthias Ringwald sockets[i].errevent = 0;
527*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
528*97dc5e69SMatthias Ringwald return i + LWIP_SOCKET_OFFSET;
529*97dc5e69SMatthias Ringwald }
530*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
531*97dc5e69SMatthias Ringwald }
532*97dc5e69SMatthias Ringwald return -1;
533*97dc5e69SMatthias Ringwald }
534*97dc5e69SMatthias Ringwald
535*97dc5e69SMatthias Ringwald /** Free a socket (under lock)
536*97dc5e69SMatthias Ringwald *
537*97dc5e69SMatthias Ringwald * @param sock the socket to free
538*97dc5e69SMatthias Ringwald * @param is_tcp != 0 for TCP sockets, used to free lastdata
539*97dc5e69SMatthias Ringwald * @param conn the socekt's netconn is stored here, must be freed externally
540*97dc5e69SMatthias Ringwald * @param lastdata lastdata is stored here, must be freed externally
541*97dc5e69SMatthias Ringwald */
542*97dc5e69SMatthias Ringwald static int
free_socket_locked(struct lwip_sock * sock,int is_tcp,struct netconn ** conn,union lwip_sock_lastdata * lastdata)543*97dc5e69SMatthias Ringwald free_socket_locked(struct lwip_sock *sock, int is_tcp, struct netconn **conn,
544*97dc5e69SMatthias Ringwald union lwip_sock_lastdata *lastdata)
545*97dc5e69SMatthias Ringwald {
546*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
547*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
548*97dc5e69SMatthias Ringwald sock->fd_used--;
549*97dc5e69SMatthias Ringwald if (sock->fd_used > 0) {
550*97dc5e69SMatthias Ringwald sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | (is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0);
551*97dc5e69SMatthias Ringwald return 0;
552*97dc5e69SMatthias Ringwald }
553*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_FULLDUPLEX */
554*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(is_tcp);
555*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_FULLDUPLEX */
556*97dc5e69SMatthias Ringwald
557*97dc5e69SMatthias Ringwald *lastdata = sock->lastdata;
558*97dc5e69SMatthias Ringwald sock->lastdata.pbuf = NULL;
559*97dc5e69SMatthias Ringwald *conn = sock->conn;
560*97dc5e69SMatthias Ringwald sock->conn = NULL;
561*97dc5e69SMatthias Ringwald return 1;
562*97dc5e69SMatthias Ringwald }
563*97dc5e69SMatthias Ringwald
564*97dc5e69SMatthias Ringwald /** Free a socket's leftover members.
565*97dc5e69SMatthias Ringwald */
566*97dc5e69SMatthias Ringwald static void
free_socket_free_elements(int is_tcp,struct netconn * conn,union lwip_sock_lastdata * lastdata)567*97dc5e69SMatthias Ringwald free_socket_free_elements(int is_tcp, struct netconn *conn, union lwip_sock_lastdata *lastdata)
568*97dc5e69SMatthias Ringwald {
569*97dc5e69SMatthias Ringwald if (lastdata->pbuf != NULL) {
570*97dc5e69SMatthias Ringwald if (is_tcp) {
571*97dc5e69SMatthias Ringwald pbuf_free(lastdata->pbuf);
572*97dc5e69SMatthias Ringwald } else {
573*97dc5e69SMatthias Ringwald netbuf_delete(lastdata->netbuf);
574*97dc5e69SMatthias Ringwald }
575*97dc5e69SMatthias Ringwald }
576*97dc5e69SMatthias Ringwald if (conn != NULL) {
577*97dc5e69SMatthias Ringwald /* netconn_prepare_delete() has already been called, here we only free the conn */
578*97dc5e69SMatthias Ringwald netconn_delete(conn);
579*97dc5e69SMatthias Ringwald }
580*97dc5e69SMatthias Ringwald }
581*97dc5e69SMatthias Ringwald
582*97dc5e69SMatthias Ringwald /** Free a socket. The socket's netconn must have been
583*97dc5e69SMatthias Ringwald * delete before!
584*97dc5e69SMatthias Ringwald *
585*97dc5e69SMatthias Ringwald * @param sock the socket to free
586*97dc5e69SMatthias Ringwald * @param is_tcp != 0 for TCP sockets, used to free lastdata
587*97dc5e69SMatthias Ringwald */
588*97dc5e69SMatthias Ringwald static void
free_socket(struct lwip_sock * sock,int is_tcp)589*97dc5e69SMatthias Ringwald free_socket(struct lwip_sock *sock, int is_tcp)
590*97dc5e69SMatthias Ringwald {
591*97dc5e69SMatthias Ringwald int freed;
592*97dc5e69SMatthias Ringwald struct netconn *conn;
593*97dc5e69SMatthias Ringwald union lwip_sock_lastdata lastdata;
594*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
595*97dc5e69SMatthias Ringwald
596*97dc5e69SMatthias Ringwald /* Protect socket array */
597*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
598*97dc5e69SMatthias Ringwald
599*97dc5e69SMatthias Ringwald freed = free_socket_locked(sock, is_tcp, &conn, &lastdata);
600*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
601*97dc5e69SMatthias Ringwald /* don't use 'sock' after this line, as another task might have allocated it */
602*97dc5e69SMatthias Ringwald
603*97dc5e69SMatthias Ringwald if (freed) {
604*97dc5e69SMatthias Ringwald free_socket_free_elements(is_tcp, conn, &lastdata);
605*97dc5e69SMatthias Ringwald }
606*97dc5e69SMatthias Ringwald }
607*97dc5e69SMatthias Ringwald
608*97dc5e69SMatthias Ringwald /* Below this, the well-known socket functions are implemented.
609*97dc5e69SMatthias Ringwald * Use google.com or opengroup.org to get a good description :-)
610*97dc5e69SMatthias Ringwald *
611*97dc5e69SMatthias Ringwald * Exceptions are documented!
612*97dc5e69SMatthias Ringwald */
613*97dc5e69SMatthias Ringwald
614*97dc5e69SMatthias Ringwald int
lwip_accept(int s,struct sockaddr * addr,socklen_t * addrlen)615*97dc5e69SMatthias Ringwald lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
616*97dc5e69SMatthias Ringwald {
617*97dc5e69SMatthias Ringwald struct lwip_sock *sock, *nsock;
618*97dc5e69SMatthias Ringwald struct netconn *newconn;
619*97dc5e69SMatthias Ringwald ip_addr_t naddr;
620*97dc5e69SMatthias Ringwald u16_t port = 0;
621*97dc5e69SMatthias Ringwald int newsock;
622*97dc5e69SMatthias Ringwald err_t err;
623*97dc5e69SMatthias Ringwald int recvevent;
624*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
625*97dc5e69SMatthias Ringwald
626*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
627*97dc5e69SMatthias Ringwald sock = get_socket(s);
628*97dc5e69SMatthias Ringwald if (!sock) {
629*97dc5e69SMatthias Ringwald return -1;
630*97dc5e69SMatthias Ringwald }
631*97dc5e69SMatthias Ringwald
632*97dc5e69SMatthias Ringwald /* wait for a new connection */
633*97dc5e69SMatthias Ringwald err = netconn_accept(sock->conn, &newconn);
634*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
635*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
636*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
637*97dc5e69SMatthias Ringwald set_errno(EOPNOTSUPP);
638*97dc5e69SMatthias Ringwald } else if (err == ERR_CLSD) {
639*97dc5e69SMatthias Ringwald set_errno(EINVAL);
640*97dc5e69SMatthias Ringwald } else {
641*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
642*97dc5e69SMatthias Ringwald }
643*97dc5e69SMatthias Ringwald done_socket(sock);
644*97dc5e69SMatthias Ringwald return -1;
645*97dc5e69SMatthias Ringwald }
646*97dc5e69SMatthias Ringwald LWIP_ASSERT("newconn != NULL", newconn != NULL);
647*97dc5e69SMatthias Ringwald
648*97dc5e69SMatthias Ringwald newsock = alloc_socket(newconn, 1);
649*97dc5e69SMatthias Ringwald if (newsock == -1) {
650*97dc5e69SMatthias Ringwald netconn_delete(newconn);
651*97dc5e69SMatthias Ringwald set_errno(ENFILE);
652*97dc5e69SMatthias Ringwald done_socket(sock);
653*97dc5e69SMatthias Ringwald return -1;
654*97dc5e69SMatthias Ringwald }
655*97dc5e69SMatthias Ringwald LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
656*97dc5e69SMatthias Ringwald nsock = &sockets[newsock - LWIP_SOCKET_OFFSET];
657*97dc5e69SMatthias Ringwald
658*97dc5e69SMatthias Ringwald /* See event_callback: If data comes in right away after an accept, even
659*97dc5e69SMatthias Ringwald * though the server task might not have created a new socket yet.
660*97dc5e69SMatthias Ringwald * In that case, newconn->socket is counted down (newconn->socket--),
661*97dc5e69SMatthias Ringwald * so nsock->rcvevent is >= 1 here!
662*97dc5e69SMatthias Ringwald */
663*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
664*97dc5e69SMatthias Ringwald recvevent = (s16_t)(-1 - newconn->socket);
665*97dc5e69SMatthias Ringwald newconn->socket = newsock;
666*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
667*97dc5e69SMatthias Ringwald
668*97dc5e69SMatthias Ringwald if (newconn->callback) {
669*97dc5e69SMatthias Ringwald LOCK_TCPIP_CORE();
670*97dc5e69SMatthias Ringwald while (recvevent > 0) {
671*97dc5e69SMatthias Ringwald recvevent--;
672*97dc5e69SMatthias Ringwald newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
673*97dc5e69SMatthias Ringwald }
674*97dc5e69SMatthias Ringwald UNLOCK_TCPIP_CORE();
675*97dc5e69SMatthias Ringwald }
676*97dc5e69SMatthias Ringwald
677*97dc5e69SMatthias Ringwald /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
678*97dc5e69SMatthias Ringwald * not be NULL if addr is valid.
679*97dc5e69SMatthias Ringwald */
680*97dc5e69SMatthias Ringwald if ((addr != NULL) && (addrlen != NULL)) {
681*97dc5e69SMatthias Ringwald union sockaddr_aligned tempaddr;
682*97dc5e69SMatthias Ringwald /* get the IP address and port of the remote host */
683*97dc5e69SMatthias Ringwald err = netconn_peer(newconn, &naddr, &port);
684*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
685*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
686*97dc5e69SMatthias Ringwald free_socket(nsock, 1);
687*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
688*97dc5e69SMatthias Ringwald done_socket(sock);
689*97dc5e69SMatthias Ringwald return -1;
690*97dc5e69SMatthias Ringwald }
691*97dc5e69SMatthias Ringwald
692*97dc5e69SMatthias Ringwald IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
693*97dc5e69SMatthias Ringwald if (*addrlen > tempaddr.sa.sa_len) {
694*97dc5e69SMatthias Ringwald *addrlen = tempaddr.sa.sa_len;
695*97dc5e69SMatthias Ringwald }
696*97dc5e69SMatthias Ringwald MEMCPY(addr, &tempaddr, *addrlen);
697*97dc5e69SMatthias Ringwald
698*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock));
699*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
700*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
701*97dc5e69SMatthias Ringwald } else {
702*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
703*97dc5e69SMatthias Ringwald }
704*97dc5e69SMatthias Ringwald
705*97dc5e69SMatthias Ringwald set_errno(0);
706*97dc5e69SMatthias Ringwald done_socket(sock);
707*97dc5e69SMatthias Ringwald done_socket(nsock);
708*97dc5e69SMatthias Ringwald return newsock;
709*97dc5e69SMatthias Ringwald }
710*97dc5e69SMatthias Ringwald
711*97dc5e69SMatthias Ringwald int
lwip_bind(int s,const struct sockaddr * name,socklen_t namelen)712*97dc5e69SMatthias Ringwald lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
713*97dc5e69SMatthias Ringwald {
714*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
715*97dc5e69SMatthias Ringwald ip_addr_t local_addr;
716*97dc5e69SMatthias Ringwald u16_t local_port;
717*97dc5e69SMatthias Ringwald err_t err;
718*97dc5e69SMatthias Ringwald
719*97dc5e69SMatthias Ringwald sock = get_socket(s);
720*97dc5e69SMatthias Ringwald if (!sock) {
721*97dc5e69SMatthias Ringwald return -1;
722*97dc5e69SMatthias Ringwald }
723*97dc5e69SMatthias Ringwald
724*97dc5e69SMatthias Ringwald if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
725*97dc5e69SMatthias Ringwald /* sockaddr does not match socket type (IPv4/IPv6) */
726*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_VAL));
727*97dc5e69SMatthias Ringwald done_socket(sock);
728*97dc5e69SMatthias Ringwald return -1;
729*97dc5e69SMatthias Ringwald }
730*97dc5e69SMatthias Ringwald
731*97dc5e69SMatthias Ringwald /* check size, family and alignment of 'name' */
732*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
733*97dc5e69SMatthias Ringwald IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
734*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
735*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(namelen);
736*97dc5e69SMatthias Ringwald
737*97dc5e69SMatthias Ringwald SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
738*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
739*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
740*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
741*97dc5e69SMatthias Ringwald
742*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
743*97dc5e69SMatthias Ringwald /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
744*97dc5e69SMatthias Ringwald if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {
745*97dc5e69SMatthias Ringwald unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));
746*97dc5e69SMatthias Ringwald IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);
747*97dc5e69SMatthias Ringwald }
748*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
749*97dc5e69SMatthias Ringwald
750*97dc5e69SMatthias Ringwald err = netconn_bind(sock->conn, &local_addr, local_port);
751*97dc5e69SMatthias Ringwald
752*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
753*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
754*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
755*97dc5e69SMatthias Ringwald done_socket(sock);
756*97dc5e69SMatthias Ringwald return -1;
757*97dc5e69SMatthias Ringwald }
758*97dc5e69SMatthias Ringwald
759*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
760*97dc5e69SMatthias Ringwald set_errno(0);
761*97dc5e69SMatthias Ringwald done_socket(sock);
762*97dc5e69SMatthias Ringwald return 0;
763*97dc5e69SMatthias Ringwald }
764*97dc5e69SMatthias Ringwald
765*97dc5e69SMatthias Ringwald int
lwip_close(int s)766*97dc5e69SMatthias Ringwald lwip_close(int s)
767*97dc5e69SMatthias Ringwald {
768*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
769*97dc5e69SMatthias Ringwald int is_tcp = 0;
770*97dc5e69SMatthias Ringwald err_t err;
771*97dc5e69SMatthias Ringwald
772*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
773*97dc5e69SMatthias Ringwald
774*97dc5e69SMatthias Ringwald sock = get_socket(s);
775*97dc5e69SMatthias Ringwald if (!sock) {
776*97dc5e69SMatthias Ringwald return -1;
777*97dc5e69SMatthias Ringwald }
778*97dc5e69SMatthias Ringwald
779*97dc5e69SMatthias Ringwald if (sock->conn != NULL) {
780*97dc5e69SMatthias Ringwald is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
781*97dc5e69SMatthias Ringwald } else {
782*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
783*97dc5e69SMatthias Ringwald }
784*97dc5e69SMatthias Ringwald
785*97dc5e69SMatthias Ringwald #if LWIP_IGMP
786*97dc5e69SMatthias Ringwald /* drop all possibly joined IGMP memberships */
787*97dc5e69SMatthias Ringwald lwip_socket_drop_registered_memberships(s);
788*97dc5e69SMatthias Ringwald #endif /* LWIP_IGMP */
789*97dc5e69SMatthias Ringwald #if LWIP_IPV6_MLD
790*97dc5e69SMatthias Ringwald /* drop all possibly joined MLD6 memberships */
791*97dc5e69SMatthias Ringwald lwip_socket_drop_registered_mld6_memberships(s);
792*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6_MLD */
793*97dc5e69SMatthias Ringwald
794*97dc5e69SMatthias Ringwald err = netconn_prepare_delete(sock->conn);
795*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
796*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
797*97dc5e69SMatthias Ringwald done_socket(sock);
798*97dc5e69SMatthias Ringwald return -1;
799*97dc5e69SMatthias Ringwald }
800*97dc5e69SMatthias Ringwald
801*97dc5e69SMatthias Ringwald free_socket(sock, is_tcp);
802*97dc5e69SMatthias Ringwald set_errno(0);
803*97dc5e69SMatthias Ringwald return 0;
804*97dc5e69SMatthias Ringwald }
805*97dc5e69SMatthias Ringwald
806*97dc5e69SMatthias Ringwald int
lwip_connect(int s,const struct sockaddr * name,socklen_t namelen)807*97dc5e69SMatthias Ringwald lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
808*97dc5e69SMatthias Ringwald {
809*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
810*97dc5e69SMatthias Ringwald err_t err;
811*97dc5e69SMatthias Ringwald
812*97dc5e69SMatthias Ringwald sock = get_socket(s);
813*97dc5e69SMatthias Ringwald if (!sock) {
814*97dc5e69SMatthias Ringwald return -1;
815*97dc5e69SMatthias Ringwald }
816*97dc5e69SMatthias Ringwald
817*97dc5e69SMatthias Ringwald if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
818*97dc5e69SMatthias Ringwald /* sockaddr does not match socket type (IPv4/IPv6) */
819*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_VAL));
820*97dc5e69SMatthias Ringwald done_socket(sock);
821*97dc5e69SMatthias Ringwald return -1;
822*97dc5e69SMatthias Ringwald }
823*97dc5e69SMatthias Ringwald
824*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(namelen);
825*97dc5e69SMatthias Ringwald if (name->sa_family == AF_UNSPEC) {
826*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
827*97dc5e69SMatthias Ringwald err = netconn_disconnect(sock->conn);
828*97dc5e69SMatthias Ringwald } else {
829*97dc5e69SMatthias Ringwald ip_addr_t remote_addr;
830*97dc5e69SMatthias Ringwald u16_t remote_port;
831*97dc5e69SMatthias Ringwald
832*97dc5e69SMatthias Ringwald /* check size, family and alignment of 'name' */
833*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
834*97dc5e69SMatthias Ringwald IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
835*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
836*97dc5e69SMatthias Ringwald
837*97dc5e69SMatthias Ringwald SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
838*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
839*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
840*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
841*97dc5e69SMatthias Ringwald
842*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
843*97dc5e69SMatthias Ringwald /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
844*97dc5e69SMatthias Ringwald if (IP_IS_V6_VAL(remote_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&remote_addr))) {
845*97dc5e69SMatthias Ringwald unmap_ipv4_mapped_ipv6(ip_2_ip4(&remote_addr), ip_2_ip6(&remote_addr));
846*97dc5e69SMatthias Ringwald IP_SET_TYPE_VAL(remote_addr, IPADDR_TYPE_V4);
847*97dc5e69SMatthias Ringwald }
848*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
849*97dc5e69SMatthias Ringwald
850*97dc5e69SMatthias Ringwald err = netconn_connect(sock->conn, &remote_addr, remote_port);
851*97dc5e69SMatthias Ringwald }
852*97dc5e69SMatthias Ringwald
853*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
854*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
855*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
856*97dc5e69SMatthias Ringwald done_socket(sock);
857*97dc5e69SMatthias Ringwald return -1;
858*97dc5e69SMatthias Ringwald }
859*97dc5e69SMatthias Ringwald
860*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
861*97dc5e69SMatthias Ringwald set_errno(0);
862*97dc5e69SMatthias Ringwald done_socket(sock);
863*97dc5e69SMatthias Ringwald return 0;
864*97dc5e69SMatthias Ringwald }
865*97dc5e69SMatthias Ringwald
866*97dc5e69SMatthias Ringwald /**
867*97dc5e69SMatthias Ringwald * Set a socket into listen mode.
868*97dc5e69SMatthias Ringwald * The socket may not have been used for another connection previously.
869*97dc5e69SMatthias Ringwald *
870*97dc5e69SMatthias Ringwald * @param s the socket to set to listening mode
871*97dc5e69SMatthias Ringwald * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
872*97dc5e69SMatthias Ringwald * @return 0 on success, non-zero on failure
873*97dc5e69SMatthias Ringwald */
874*97dc5e69SMatthias Ringwald int
lwip_listen(int s,int backlog)875*97dc5e69SMatthias Ringwald lwip_listen(int s, int backlog)
876*97dc5e69SMatthias Ringwald {
877*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
878*97dc5e69SMatthias Ringwald err_t err;
879*97dc5e69SMatthias Ringwald
880*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
881*97dc5e69SMatthias Ringwald
882*97dc5e69SMatthias Ringwald sock = get_socket(s);
883*97dc5e69SMatthias Ringwald if (!sock) {
884*97dc5e69SMatthias Ringwald return -1;
885*97dc5e69SMatthias Ringwald }
886*97dc5e69SMatthias Ringwald
887*97dc5e69SMatthias Ringwald /* limit the "backlog" parameter to fit in an u8_t */
888*97dc5e69SMatthias Ringwald backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
889*97dc5e69SMatthias Ringwald
890*97dc5e69SMatthias Ringwald err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
891*97dc5e69SMatthias Ringwald
892*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
893*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
894*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
895*97dc5e69SMatthias Ringwald set_errno(EOPNOTSUPP);
896*97dc5e69SMatthias Ringwald } else {
897*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
898*97dc5e69SMatthias Ringwald }
899*97dc5e69SMatthias Ringwald done_socket(sock);
900*97dc5e69SMatthias Ringwald return -1;
901*97dc5e69SMatthias Ringwald }
902*97dc5e69SMatthias Ringwald
903*97dc5e69SMatthias Ringwald set_errno(0);
904*97dc5e69SMatthias Ringwald done_socket(sock);
905*97dc5e69SMatthias Ringwald return 0;
906*97dc5e69SMatthias Ringwald }
907*97dc5e69SMatthias Ringwald
908*97dc5e69SMatthias Ringwald #if LWIP_TCP
909*97dc5e69SMatthias Ringwald /* Helper function to loop over receiving pbufs from netconn
910*97dc5e69SMatthias Ringwald * until "len" bytes are received or we're otherwise done.
911*97dc5e69SMatthias Ringwald * Keeps sock->lastdata for peeking or partly copying.
912*97dc5e69SMatthias Ringwald */
913*97dc5e69SMatthias Ringwald static ssize_t
lwip_recv_tcp(struct lwip_sock * sock,void * mem,size_t len,int flags)914*97dc5e69SMatthias Ringwald lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
915*97dc5e69SMatthias Ringwald {
916*97dc5e69SMatthias Ringwald u8_t apiflags = NETCONN_NOAUTORCVD;
917*97dc5e69SMatthias Ringwald ssize_t recvd = 0;
918*97dc5e69SMatthias Ringwald ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
919*97dc5e69SMatthias Ringwald
920*97dc5e69SMatthias Ringwald LWIP_ASSERT("no socket given", sock != NULL);
921*97dc5e69SMatthias Ringwald LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
922*97dc5e69SMatthias Ringwald
923*97dc5e69SMatthias Ringwald if (flags & MSG_DONTWAIT) {
924*97dc5e69SMatthias Ringwald apiflags |= NETCONN_DONTBLOCK;
925*97dc5e69SMatthias Ringwald }
926*97dc5e69SMatthias Ringwald
927*97dc5e69SMatthias Ringwald do {
928*97dc5e69SMatthias Ringwald struct pbuf *p;
929*97dc5e69SMatthias Ringwald err_t err;
930*97dc5e69SMatthias Ringwald u16_t copylen;
931*97dc5e69SMatthias Ringwald
932*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void *)sock->lastdata.pbuf));
933*97dc5e69SMatthias Ringwald /* Check if there is data left from the last recv operation. */
934*97dc5e69SMatthias Ringwald if (sock->lastdata.pbuf) {
935*97dc5e69SMatthias Ringwald p = sock->lastdata.pbuf;
936*97dc5e69SMatthias Ringwald } else {
937*97dc5e69SMatthias Ringwald /* No data was left from the previous operation, so we try to get
938*97dc5e69SMatthias Ringwald some from the network. */
939*97dc5e69SMatthias Ringwald err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
940*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
941*97dc5e69SMatthias Ringwald err, (void *)p));
942*97dc5e69SMatthias Ringwald
943*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
944*97dc5e69SMatthias Ringwald if (recvd > 0) {
945*97dc5e69SMatthias Ringwald /* already received data, return that (this trusts in getting the same error from
946*97dc5e69SMatthias Ringwald netconn layer again next time netconn_recv is called) */
947*97dc5e69SMatthias Ringwald goto lwip_recv_tcp_done;
948*97dc5e69SMatthias Ringwald }
949*97dc5e69SMatthias Ringwald /* We should really do some error checking here. */
950*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
951*97dc5e69SMatthias Ringwald lwip_strerr(err)));
952*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
953*97dc5e69SMatthias Ringwald if (err == ERR_CLSD) {
954*97dc5e69SMatthias Ringwald return 0;
955*97dc5e69SMatthias Ringwald } else {
956*97dc5e69SMatthias Ringwald return -1;
957*97dc5e69SMatthias Ringwald }
958*97dc5e69SMatthias Ringwald }
959*97dc5e69SMatthias Ringwald LWIP_ASSERT("p != NULL", p != NULL);
960*97dc5e69SMatthias Ringwald sock->lastdata.pbuf = p;
961*97dc5e69SMatthias Ringwald }
962*97dc5e69SMatthias Ringwald
963*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
964*97dc5e69SMatthias Ringwald p->tot_len, (int)recv_left, (int)recvd));
965*97dc5e69SMatthias Ringwald
966*97dc5e69SMatthias Ringwald if (recv_left > p->tot_len) {
967*97dc5e69SMatthias Ringwald copylen = p->tot_len;
968*97dc5e69SMatthias Ringwald } else {
969*97dc5e69SMatthias Ringwald copylen = (u16_t)recv_left;
970*97dc5e69SMatthias Ringwald }
971*97dc5e69SMatthias Ringwald if (recvd + copylen < recvd) {
972*97dc5e69SMatthias Ringwald /* overflow */
973*97dc5e69SMatthias Ringwald copylen = (u16_t)(SSIZE_MAX - recvd);
974*97dc5e69SMatthias Ringwald }
975*97dc5e69SMatthias Ringwald
976*97dc5e69SMatthias Ringwald /* copy the contents of the received buffer into
977*97dc5e69SMatthias Ringwald the supplied memory pointer mem */
978*97dc5e69SMatthias Ringwald pbuf_copy_partial(p, (u8_t *)mem + recvd, copylen, 0);
979*97dc5e69SMatthias Ringwald
980*97dc5e69SMatthias Ringwald recvd += copylen;
981*97dc5e69SMatthias Ringwald
982*97dc5e69SMatthias Ringwald /* TCP combines multiple pbufs for one recv */
983*97dc5e69SMatthias Ringwald LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
984*97dc5e69SMatthias Ringwald recv_left -= copylen;
985*97dc5e69SMatthias Ringwald
986*97dc5e69SMatthias Ringwald /* Unless we peek the incoming message... */
987*97dc5e69SMatthias Ringwald if ((flags & MSG_PEEK) == 0) {
988*97dc5e69SMatthias Ringwald /* ... check if there is data left in the pbuf */
989*97dc5e69SMatthias Ringwald LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
990*97dc5e69SMatthias Ringwald if (p->tot_len - copylen > 0) {
991*97dc5e69SMatthias Ringwald /* If so, it should be saved in the sock structure for the next recv call.
992*97dc5e69SMatthias Ringwald We store the pbuf but hide/free the consumed data: */
993*97dc5e69SMatthias Ringwald sock->lastdata.pbuf = pbuf_free_header(p, copylen);
994*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void *)sock->lastdata.pbuf));
995*97dc5e69SMatthias Ringwald } else {
996*97dc5e69SMatthias Ringwald sock->lastdata.pbuf = NULL;
997*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void *)p));
998*97dc5e69SMatthias Ringwald pbuf_free(p);
999*97dc5e69SMatthias Ringwald }
1000*97dc5e69SMatthias Ringwald }
1001*97dc5e69SMatthias Ringwald /* once we have some data to return, only add more if we don't need to wait */
1002*97dc5e69SMatthias Ringwald apiflags |= NETCONN_DONTBLOCK | NETCONN_NOFIN;
1003*97dc5e69SMatthias Ringwald /* @todo: do we need to support peeking more than one pbuf? */
1004*97dc5e69SMatthias Ringwald } while ((recv_left > 0) && !(flags & MSG_PEEK));
1005*97dc5e69SMatthias Ringwald lwip_recv_tcp_done:
1006*97dc5e69SMatthias Ringwald if ((recvd > 0) && !(flags & MSG_PEEK)) {
1007*97dc5e69SMatthias Ringwald /* ensure window update after copying all data */
1008*97dc5e69SMatthias Ringwald netconn_tcp_recvd(sock->conn, (size_t)recvd);
1009*97dc5e69SMatthias Ringwald }
1010*97dc5e69SMatthias Ringwald set_errno(0);
1011*97dc5e69SMatthias Ringwald return recvd;
1012*97dc5e69SMatthias Ringwald }
1013*97dc5e69SMatthias Ringwald #endif
1014*97dc5e69SMatthias Ringwald
1015*97dc5e69SMatthias Ringwald /* Convert a netbuf's address data to struct sockaddr */
1016*97dc5e69SMatthias Ringwald static int
lwip_sock_make_addr(struct netconn * conn,ip_addr_t * fromaddr,u16_t port,struct sockaddr * from,socklen_t * fromlen)1017*97dc5e69SMatthias Ringwald lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
1018*97dc5e69SMatthias Ringwald struct sockaddr *from, socklen_t *fromlen)
1019*97dc5e69SMatthias Ringwald {
1020*97dc5e69SMatthias Ringwald int truncated = 0;
1021*97dc5e69SMatthias Ringwald union sockaddr_aligned saddr;
1022*97dc5e69SMatthias Ringwald
1023*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(conn);
1024*97dc5e69SMatthias Ringwald
1025*97dc5e69SMatthias Ringwald LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
1026*97dc5e69SMatthias Ringwald LWIP_ASSERT("from != NULL", from != NULL);
1027*97dc5e69SMatthias Ringwald LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
1028*97dc5e69SMatthias Ringwald
1029*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
1030*97dc5e69SMatthias Ringwald /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
1031*97dc5e69SMatthias Ringwald if (NETCONNTYPE_ISIPV6(netconn_type(conn)) && IP_IS_V4(fromaddr)) {
1032*97dc5e69SMatthias Ringwald ip4_2_ipv4_mapped_ipv6(ip_2_ip6(fromaddr), ip_2_ip4(fromaddr));
1033*97dc5e69SMatthias Ringwald IP_SET_TYPE(fromaddr, IPADDR_TYPE_V6);
1034*97dc5e69SMatthias Ringwald }
1035*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
1036*97dc5e69SMatthias Ringwald
1037*97dc5e69SMatthias Ringwald IPADDR_PORT_TO_SOCKADDR(&saddr, fromaddr, port);
1038*97dc5e69SMatthias Ringwald if (*fromlen < saddr.sa.sa_len) {
1039*97dc5e69SMatthias Ringwald truncated = 1;
1040*97dc5e69SMatthias Ringwald } else if (*fromlen > saddr.sa.sa_len) {
1041*97dc5e69SMatthias Ringwald *fromlen = saddr.sa.sa_len;
1042*97dc5e69SMatthias Ringwald }
1043*97dc5e69SMatthias Ringwald MEMCPY(from, &saddr, *fromlen);
1044*97dc5e69SMatthias Ringwald return truncated;
1045*97dc5e69SMatthias Ringwald }
1046*97dc5e69SMatthias Ringwald
1047*97dc5e69SMatthias Ringwald #if LWIP_TCP
1048*97dc5e69SMatthias Ringwald /* Helper function to get a tcp socket's remote address info */
1049*97dc5e69SMatthias Ringwald static int
lwip_recv_tcp_from(struct lwip_sock * sock,struct sockaddr * from,socklen_t * fromlen,const char * dbg_fn,int dbg_s,ssize_t dbg_ret)1050*97dc5e69SMatthias Ringwald lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
1051*97dc5e69SMatthias Ringwald {
1052*97dc5e69SMatthias Ringwald if (sock == NULL) {
1053*97dc5e69SMatthias Ringwald return 0;
1054*97dc5e69SMatthias Ringwald }
1055*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(dbg_fn);
1056*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(dbg_s);
1057*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(dbg_ret);
1058*97dc5e69SMatthias Ringwald
1059*97dc5e69SMatthias Ringwald #if !SOCKETS_DEBUG
1060*97dc5e69SMatthias Ringwald if (from && fromlen)
1061*97dc5e69SMatthias Ringwald #endif /* !SOCKETS_DEBUG */
1062*97dc5e69SMatthias Ringwald {
1063*97dc5e69SMatthias Ringwald /* get remote addr/port from tcp_pcb */
1064*97dc5e69SMatthias Ringwald u16_t port;
1065*97dc5e69SMatthias Ringwald ip_addr_t tmpaddr;
1066*97dc5e69SMatthias Ringwald netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
1067*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d): addr=", dbg_fn, dbg_s));
1068*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
1069*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
1070*97dc5e69SMatthias Ringwald if (from && fromlen) {
1071*97dc5e69SMatthias Ringwald return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
1072*97dc5e69SMatthias Ringwald }
1073*97dc5e69SMatthias Ringwald }
1074*97dc5e69SMatthias Ringwald return 0;
1075*97dc5e69SMatthias Ringwald }
1076*97dc5e69SMatthias Ringwald #endif
1077*97dc5e69SMatthias Ringwald
1078*97dc5e69SMatthias Ringwald /* Helper function to receive a netbuf from a udp or raw netconn.
1079*97dc5e69SMatthias Ringwald * Keeps sock->lastdata for peeking.
1080*97dc5e69SMatthias Ringwald */
1081*97dc5e69SMatthias Ringwald static err_t
lwip_recvfrom_udp_raw(struct lwip_sock * sock,int flags,struct msghdr * msg,u16_t * datagram_len,int dbg_s)1082*97dc5e69SMatthias Ringwald lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
1083*97dc5e69SMatthias Ringwald {
1084*97dc5e69SMatthias Ringwald struct netbuf *buf;
1085*97dc5e69SMatthias Ringwald u8_t apiflags;
1086*97dc5e69SMatthias Ringwald err_t err;
1087*97dc5e69SMatthias Ringwald u16_t buflen, copylen, copied;
1088*97dc5e69SMatthias Ringwald int i;
1089*97dc5e69SMatthias Ringwald
1090*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(dbg_s);
1091*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
1092*97dc5e69SMatthias Ringwald
1093*97dc5e69SMatthias Ringwald if (flags & MSG_DONTWAIT) {
1094*97dc5e69SMatthias Ringwald apiflags = NETCONN_DONTBLOCK;
1095*97dc5e69SMatthias Ringwald } else {
1096*97dc5e69SMatthias Ringwald apiflags = 0;
1097*97dc5e69SMatthias Ringwald }
1098*97dc5e69SMatthias Ringwald
1099*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void *)sock->lastdata.netbuf));
1100*97dc5e69SMatthias Ringwald /* Check if there is data left from the last recv operation. */
1101*97dc5e69SMatthias Ringwald buf = sock->lastdata.netbuf;
1102*97dc5e69SMatthias Ringwald if (buf == NULL) {
1103*97dc5e69SMatthias Ringwald /* No data was left from the previous operation, so we try to get
1104*97dc5e69SMatthias Ringwald some from the network. */
1105*97dc5e69SMatthias Ringwald err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
1106*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
1107*97dc5e69SMatthias Ringwald err, (void *)buf));
1108*97dc5e69SMatthias Ringwald
1109*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
1110*97dc5e69SMatthias Ringwald return err;
1111*97dc5e69SMatthias Ringwald }
1112*97dc5e69SMatthias Ringwald LWIP_ASSERT("buf != NULL", buf != NULL);
1113*97dc5e69SMatthias Ringwald sock->lastdata.netbuf = buf;
1114*97dc5e69SMatthias Ringwald }
1115*97dc5e69SMatthias Ringwald buflen = buf->p->tot_len;
1116*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
1117*97dc5e69SMatthias Ringwald
1118*97dc5e69SMatthias Ringwald copied = 0;
1119*97dc5e69SMatthias Ringwald /* copy the pbuf payload into the iovs */
1120*97dc5e69SMatthias Ringwald for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
1121*97dc5e69SMatthias Ringwald u16_t len_left = (u16_t)(buflen - copied);
1122*97dc5e69SMatthias Ringwald if (msg->msg_iov[i].iov_len > len_left) {
1123*97dc5e69SMatthias Ringwald copylen = len_left;
1124*97dc5e69SMatthias Ringwald } else {
1125*97dc5e69SMatthias Ringwald copylen = (u16_t)msg->msg_iov[i].iov_len;
1126*97dc5e69SMatthias Ringwald }
1127*97dc5e69SMatthias Ringwald
1128*97dc5e69SMatthias Ringwald /* copy the contents of the received buffer into
1129*97dc5e69SMatthias Ringwald the supplied memory buffer */
1130*97dc5e69SMatthias Ringwald pbuf_copy_partial(buf->p, (u8_t *)msg->msg_iov[i].iov_base, copylen, copied);
1131*97dc5e69SMatthias Ringwald copied = (u16_t)(copied + copylen);
1132*97dc5e69SMatthias Ringwald }
1133*97dc5e69SMatthias Ringwald
1134*97dc5e69SMatthias Ringwald /* Check to see from where the data was.*/
1135*97dc5e69SMatthias Ringwald #if !SOCKETS_DEBUG
1136*97dc5e69SMatthias Ringwald if (msg->msg_name && msg->msg_namelen)
1137*97dc5e69SMatthias Ringwald #endif /* !SOCKETS_DEBUG */
1138*97dc5e69SMatthias Ringwald {
1139*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d): addr=", dbg_s));
1140*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
1141*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
1142*97dc5e69SMatthias Ringwald if (msg->msg_name && msg->msg_namelen) {
1143*97dc5e69SMatthias Ringwald lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
1144*97dc5e69SMatthias Ringwald (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
1145*97dc5e69SMatthias Ringwald }
1146*97dc5e69SMatthias Ringwald }
1147*97dc5e69SMatthias Ringwald
1148*97dc5e69SMatthias Ringwald /* Initialize flag output */
1149*97dc5e69SMatthias Ringwald msg->msg_flags = 0;
1150*97dc5e69SMatthias Ringwald
1151*97dc5e69SMatthias Ringwald if (msg->msg_control) {
1152*97dc5e69SMatthias Ringwald u8_t wrote_msg = 0;
1153*97dc5e69SMatthias Ringwald #if LWIP_NETBUF_RECVINFO
1154*97dc5e69SMatthias Ringwald /* Check if packet info was recorded */
1155*97dc5e69SMatthias Ringwald if (buf->flags & NETBUF_FLAG_DESTADDR) {
1156*97dc5e69SMatthias Ringwald if (IP_IS_V4(&buf->toaddr)) {
1157*97dc5e69SMatthias Ringwald #if LWIP_IPV4
1158*97dc5e69SMatthias Ringwald if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
1159*97dc5e69SMatthias Ringwald struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
1160*97dc5e69SMatthias Ringwald struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
1161*97dc5e69SMatthias Ringwald chdr->cmsg_level = IPPROTO_IP;
1162*97dc5e69SMatthias Ringwald chdr->cmsg_type = IP_PKTINFO;
1163*97dc5e69SMatthias Ringwald chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
1164*97dc5e69SMatthias Ringwald pkti->ipi_ifindex = buf->p->if_idx;
1165*97dc5e69SMatthias Ringwald inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
1166*97dc5e69SMatthias Ringwald msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
1167*97dc5e69SMatthias Ringwald wrote_msg = 1;
1168*97dc5e69SMatthias Ringwald } else {
1169*97dc5e69SMatthias Ringwald msg->msg_flags |= MSG_CTRUNC;
1170*97dc5e69SMatthias Ringwald }
1171*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 */
1172*97dc5e69SMatthias Ringwald }
1173*97dc5e69SMatthias Ringwald }
1174*97dc5e69SMatthias Ringwald #endif /* LWIP_NETBUF_RECVINFO */
1175*97dc5e69SMatthias Ringwald
1176*97dc5e69SMatthias Ringwald if (!wrote_msg) {
1177*97dc5e69SMatthias Ringwald msg->msg_controllen = 0;
1178*97dc5e69SMatthias Ringwald }
1179*97dc5e69SMatthias Ringwald }
1180*97dc5e69SMatthias Ringwald
1181*97dc5e69SMatthias Ringwald /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
1182*97dc5e69SMatthias Ringwald if ((flags & MSG_PEEK) == 0) {
1183*97dc5e69SMatthias Ringwald sock->lastdata.netbuf = NULL;
1184*97dc5e69SMatthias Ringwald netbuf_delete(buf);
1185*97dc5e69SMatthias Ringwald }
1186*97dc5e69SMatthias Ringwald if (datagram_len) {
1187*97dc5e69SMatthias Ringwald *datagram_len = buflen;
1188*97dc5e69SMatthias Ringwald }
1189*97dc5e69SMatthias Ringwald return ERR_OK;
1190*97dc5e69SMatthias Ringwald }
1191*97dc5e69SMatthias Ringwald
1192*97dc5e69SMatthias Ringwald ssize_t
lwip_recvfrom(int s,void * mem,size_t len,int flags,struct sockaddr * from,socklen_t * fromlen)1193*97dc5e69SMatthias Ringwald lwip_recvfrom(int s, void *mem, size_t len, int flags,
1194*97dc5e69SMatthias Ringwald struct sockaddr *from, socklen_t *fromlen)
1195*97dc5e69SMatthias Ringwald {
1196*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1197*97dc5e69SMatthias Ringwald ssize_t ret;
1198*97dc5e69SMatthias Ringwald
1199*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
1200*97dc5e69SMatthias Ringwald sock = get_socket(s);
1201*97dc5e69SMatthias Ringwald if (!sock) {
1202*97dc5e69SMatthias Ringwald return -1;
1203*97dc5e69SMatthias Ringwald }
1204*97dc5e69SMatthias Ringwald #if LWIP_TCP
1205*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1206*97dc5e69SMatthias Ringwald ret = lwip_recv_tcp(sock, mem, len, flags);
1207*97dc5e69SMatthias Ringwald lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
1208*97dc5e69SMatthias Ringwald done_socket(sock);
1209*97dc5e69SMatthias Ringwald return ret;
1210*97dc5e69SMatthias Ringwald } else
1211*97dc5e69SMatthias Ringwald #endif
1212*97dc5e69SMatthias Ringwald {
1213*97dc5e69SMatthias Ringwald u16_t datagram_len = 0;
1214*97dc5e69SMatthias Ringwald struct iovec vec;
1215*97dc5e69SMatthias Ringwald struct msghdr msg;
1216*97dc5e69SMatthias Ringwald err_t err;
1217*97dc5e69SMatthias Ringwald vec.iov_base = mem;
1218*97dc5e69SMatthias Ringwald vec.iov_len = len;
1219*97dc5e69SMatthias Ringwald msg.msg_control = NULL;
1220*97dc5e69SMatthias Ringwald msg.msg_controllen = 0;
1221*97dc5e69SMatthias Ringwald msg.msg_flags = 0;
1222*97dc5e69SMatthias Ringwald msg.msg_iov = &vec;
1223*97dc5e69SMatthias Ringwald msg.msg_iovlen = 1;
1224*97dc5e69SMatthias Ringwald msg.msg_name = from;
1225*97dc5e69SMatthias Ringwald msg.msg_namelen = (fromlen ? *fromlen : 0);
1226*97dc5e69SMatthias Ringwald err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
1227*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
1228*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
1229*97dc5e69SMatthias Ringwald s, lwip_strerr(err)));
1230*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1231*97dc5e69SMatthias Ringwald done_socket(sock);
1232*97dc5e69SMatthias Ringwald return -1;
1233*97dc5e69SMatthias Ringwald }
1234*97dc5e69SMatthias Ringwald ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
1235*97dc5e69SMatthias Ringwald if (fromlen) {
1236*97dc5e69SMatthias Ringwald *fromlen = msg.msg_namelen;
1237*97dc5e69SMatthias Ringwald }
1238*97dc5e69SMatthias Ringwald }
1239*97dc5e69SMatthias Ringwald
1240*97dc5e69SMatthias Ringwald set_errno(0);
1241*97dc5e69SMatthias Ringwald done_socket(sock);
1242*97dc5e69SMatthias Ringwald return ret;
1243*97dc5e69SMatthias Ringwald }
1244*97dc5e69SMatthias Ringwald
1245*97dc5e69SMatthias Ringwald ssize_t
lwip_read(int s,void * mem,size_t len)1246*97dc5e69SMatthias Ringwald lwip_read(int s, void *mem, size_t len)
1247*97dc5e69SMatthias Ringwald {
1248*97dc5e69SMatthias Ringwald return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
1249*97dc5e69SMatthias Ringwald }
1250*97dc5e69SMatthias Ringwald
1251*97dc5e69SMatthias Ringwald ssize_t
lwip_readv(int s,const struct iovec * iov,int iovcnt)1252*97dc5e69SMatthias Ringwald lwip_readv(int s, const struct iovec *iov, int iovcnt)
1253*97dc5e69SMatthias Ringwald {
1254*97dc5e69SMatthias Ringwald struct msghdr msg;
1255*97dc5e69SMatthias Ringwald
1256*97dc5e69SMatthias Ringwald msg.msg_name = NULL;
1257*97dc5e69SMatthias Ringwald msg.msg_namelen = 0;
1258*97dc5e69SMatthias Ringwald /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
1259*97dc5e69SMatthias Ringwald Blame the opengroup standard for this inconsistency. */
1260*97dc5e69SMatthias Ringwald msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
1261*97dc5e69SMatthias Ringwald msg.msg_iovlen = iovcnt;
1262*97dc5e69SMatthias Ringwald msg.msg_control = NULL;
1263*97dc5e69SMatthias Ringwald msg.msg_controllen = 0;
1264*97dc5e69SMatthias Ringwald msg.msg_flags = 0;
1265*97dc5e69SMatthias Ringwald return lwip_recvmsg(s, &msg, 0);
1266*97dc5e69SMatthias Ringwald }
1267*97dc5e69SMatthias Ringwald
1268*97dc5e69SMatthias Ringwald ssize_t
lwip_recv(int s,void * mem,size_t len,int flags)1269*97dc5e69SMatthias Ringwald lwip_recv(int s, void *mem, size_t len, int flags)
1270*97dc5e69SMatthias Ringwald {
1271*97dc5e69SMatthias Ringwald return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
1272*97dc5e69SMatthias Ringwald }
1273*97dc5e69SMatthias Ringwald
1274*97dc5e69SMatthias Ringwald ssize_t
lwip_recvmsg(int s,struct msghdr * message,int flags)1275*97dc5e69SMatthias Ringwald lwip_recvmsg(int s, struct msghdr *message, int flags)
1276*97dc5e69SMatthias Ringwald {
1277*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1278*97dc5e69SMatthias Ringwald int i;
1279*97dc5e69SMatthias Ringwald ssize_t buflen;
1280*97dc5e69SMatthias Ringwald
1281*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
1282*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
1283*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_recvmsg: unsupported flags", (flags & ~(MSG_PEEK|MSG_DONTWAIT)) == 0,
1284*97dc5e69SMatthias Ringwald set_errno(EOPNOTSUPP); return -1;);
1285*97dc5e69SMatthias Ringwald
1286*97dc5e69SMatthias Ringwald if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
1287*97dc5e69SMatthias Ringwald set_errno(EMSGSIZE);
1288*97dc5e69SMatthias Ringwald return -1;
1289*97dc5e69SMatthias Ringwald }
1290*97dc5e69SMatthias Ringwald
1291*97dc5e69SMatthias Ringwald sock = get_socket(s);
1292*97dc5e69SMatthias Ringwald if (!sock) {
1293*97dc5e69SMatthias Ringwald return -1;
1294*97dc5e69SMatthias Ringwald }
1295*97dc5e69SMatthias Ringwald
1296*97dc5e69SMatthias Ringwald /* check for valid vectors */
1297*97dc5e69SMatthias Ringwald buflen = 0;
1298*97dc5e69SMatthias Ringwald for (i = 0; i < message->msg_iovlen; i++) {
1299*97dc5e69SMatthias Ringwald if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
1300*97dc5e69SMatthias Ringwald ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
1301*97dc5e69SMatthias Ringwald ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
1302*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_VAL));
1303*97dc5e69SMatthias Ringwald done_socket(sock);
1304*97dc5e69SMatthias Ringwald return -1;
1305*97dc5e69SMatthias Ringwald }
1306*97dc5e69SMatthias Ringwald buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
1307*97dc5e69SMatthias Ringwald }
1308*97dc5e69SMatthias Ringwald
1309*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1310*97dc5e69SMatthias Ringwald #if LWIP_TCP
1311*97dc5e69SMatthias Ringwald int recv_flags = flags;
1312*97dc5e69SMatthias Ringwald message->msg_flags = 0;
1313*97dc5e69SMatthias Ringwald /* recv the data */
1314*97dc5e69SMatthias Ringwald buflen = 0;
1315*97dc5e69SMatthias Ringwald for (i = 0; i < message->msg_iovlen; i++) {
1316*97dc5e69SMatthias Ringwald /* try to receive into this vector's buffer */
1317*97dc5e69SMatthias Ringwald ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
1318*97dc5e69SMatthias Ringwald if (recvd_local > 0) {
1319*97dc5e69SMatthias Ringwald /* sum up received bytes */
1320*97dc5e69SMatthias Ringwald buflen += recvd_local;
1321*97dc5e69SMatthias Ringwald }
1322*97dc5e69SMatthias Ringwald if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
1323*97dc5e69SMatthias Ringwald (flags & MSG_PEEK)) {
1324*97dc5e69SMatthias Ringwald /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
1325*97dc5e69SMatthias Ringwald if (buflen <= 0) {
1326*97dc5e69SMatthias Ringwald /* nothing received at all, propagate the error */
1327*97dc5e69SMatthias Ringwald buflen = recvd_local;
1328*97dc5e69SMatthias Ringwald }
1329*97dc5e69SMatthias Ringwald break;
1330*97dc5e69SMatthias Ringwald }
1331*97dc5e69SMatthias Ringwald /* pass MSG_DONTWAIT to lwip_recv_tcp() to prevent waiting for more data */
1332*97dc5e69SMatthias Ringwald recv_flags |= MSG_DONTWAIT;
1333*97dc5e69SMatthias Ringwald }
1334*97dc5e69SMatthias Ringwald if (buflen > 0) {
1335*97dc5e69SMatthias Ringwald /* reset socket error since we have received something */
1336*97dc5e69SMatthias Ringwald set_errno(0);
1337*97dc5e69SMatthias Ringwald }
1338*97dc5e69SMatthias Ringwald /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
1339*97dc5e69SMatthias Ringwald done_socket(sock);
1340*97dc5e69SMatthias Ringwald return buflen;
1341*97dc5e69SMatthias Ringwald #else /* LWIP_TCP */
1342*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1343*97dc5e69SMatthias Ringwald done_socket(sock);
1344*97dc5e69SMatthias Ringwald return -1;
1345*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP */
1346*97dc5e69SMatthias Ringwald }
1347*97dc5e69SMatthias Ringwald /* else, UDP and RAW NETCONNs */
1348*97dc5e69SMatthias Ringwald #if LWIP_UDP || LWIP_RAW
1349*97dc5e69SMatthias Ringwald {
1350*97dc5e69SMatthias Ringwald u16_t datagram_len = 0;
1351*97dc5e69SMatthias Ringwald err_t err;
1352*97dc5e69SMatthias Ringwald err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
1353*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
1354*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
1355*97dc5e69SMatthias Ringwald s, lwip_strerr(err)));
1356*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1357*97dc5e69SMatthias Ringwald done_socket(sock);
1358*97dc5e69SMatthias Ringwald return -1;
1359*97dc5e69SMatthias Ringwald }
1360*97dc5e69SMatthias Ringwald if (datagram_len > buflen) {
1361*97dc5e69SMatthias Ringwald message->msg_flags |= MSG_TRUNC;
1362*97dc5e69SMatthias Ringwald }
1363*97dc5e69SMatthias Ringwald
1364*97dc5e69SMatthias Ringwald set_errno(0);
1365*97dc5e69SMatthias Ringwald done_socket(sock);
1366*97dc5e69SMatthias Ringwald return (int)datagram_len;
1367*97dc5e69SMatthias Ringwald }
1368*97dc5e69SMatthias Ringwald #else /* LWIP_UDP || LWIP_RAW */
1369*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1370*97dc5e69SMatthias Ringwald done_socket(sock);
1371*97dc5e69SMatthias Ringwald return -1;
1372*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP || LWIP_RAW */
1373*97dc5e69SMatthias Ringwald }
1374*97dc5e69SMatthias Ringwald
1375*97dc5e69SMatthias Ringwald ssize_t
lwip_send(int s,const void * data,size_t size,int flags)1376*97dc5e69SMatthias Ringwald lwip_send(int s, const void *data, size_t size, int flags)
1377*97dc5e69SMatthias Ringwald {
1378*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1379*97dc5e69SMatthias Ringwald err_t err;
1380*97dc5e69SMatthias Ringwald u8_t write_flags;
1381*97dc5e69SMatthias Ringwald size_t written;
1382*97dc5e69SMatthias Ringwald
1383*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
1384*97dc5e69SMatthias Ringwald s, data, size, flags));
1385*97dc5e69SMatthias Ringwald
1386*97dc5e69SMatthias Ringwald sock = get_socket(s);
1387*97dc5e69SMatthias Ringwald if (!sock) {
1388*97dc5e69SMatthias Ringwald return -1;
1389*97dc5e69SMatthias Ringwald }
1390*97dc5e69SMatthias Ringwald
1391*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
1392*97dc5e69SMatthias Ringwald #if (LWIP_UDP || LWIP_RAW)
1393*97dc5e69SMatthias Ringwald done_socket(sock);
1394*97dc5e69SMatthias Ringwald return lwip_sendto(s, data, size, flags, NULL, 0);
1395*97dc5e69SMatthias Ringwald #else /* (LWIP_UDP || LWIP_RAW) */
1396*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1397*97dc5e69SMatthias Ringwald done_socket(sock);
1398*97dc5e69SMatthias Ringwald return -1;
1399*97dc5e69SMatthias Ringwald #endif /* (LWIP_UDP || LWIP_RAW) */
1400*97dc5e69SMatthias Ringwald }
1401*97dc5e69SMatthias Ringwald
1402*97dc5e69SMatthias Ringwald write_flags = (u8_t)(NETCONN_COPY |
1403*97dc5e69SMatthias Ringwald ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
1404*97dc5e69SMatthias Ringwald ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
1405*97dc5e69SMatthias Ringwald written = 0;
1406*97dc5e69SMatthias Ringwald err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
1407*97dc5e69SMatthias Ringwald
1408*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
1409*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1410*97dc5e69SMatthias Ringwald done_socket(sock);
1411*97dc5e69SMatthias Ringwald /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
1412*97dc5e69SMatthias Ringwald return (err == ERR_OK ? (ssize_t)written : -1);
1413*97dc5e69SMatthias Ringwald }
1414*97dc5e69SMatthias Ringwald
1415*97dc5e69SMatthias Ringwald ssize_t
lwip_sendmsg(int s,const struct msghdr * msg,int flags)1416*97dc5e69SMatthias Ringwald lwip_sendmsg(int s, const struct msghdr *msg, int flags)
1417*97dc5e69SMatthias Ringwald {
1418*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1419*97dc5e69SMatthias Ringwald #if LWIP_TCP
1420*97dc5e69SMatthias Ringwald u8_t write_flags;
1421*97dc5e69SMatthias Ringwald size_t written;
1422*97dc5e69SMatthias Ringwald #endif
1423*97dc5e69SMatthias Ringwald err_t err = ERR_OK;
1424*97dc5e69SMatthias Ringwald
1425*97dc5e69SMatthias Ringwald sock = get_socket(s);
1426*97dc5e69SMatthias Ringwald if (!sock) {
1427*97dc5e69SMatthias Ringwald return -1;
1428*97dc5e69SMatthias Ringwald }
1429*97dc5e69SMatthias Ringwald
1430*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendmsg: invalid msghdr", msg != NULL,
1431*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1432*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
1433*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1434*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
1435*97dc5e69SMatthias Ringwald set_errno(EMSGSIZE); done_socket(sock); return -1;);
1436*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT | MSG_MORE)) == 0,
1437*97dc5e69SMatthias Ringwald set_errno(EOPNOTSUPP); done_socket(sock); return -1;);
1438*97dc5e69SMatthias Ringwald
1439*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(msg->msg_control);
1440*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(msg->msg_controllen);
1441*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(msg->msg_flags);
1442*97dc5e69SMatthias Ringwald
1443*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1444*97dc5e69SMatthias Ringwald #if LWIP_TCP
1445*97dc5e69SMatthias Ringwald write_flags = (u8_t)(NETCONN_COPY |
1446*97dc5e69SMatthias Ringwald ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
1447*97dc5e69SMatthias Ringwald ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
1448*97dc5e69SMatthias Ringwald
1449*97dc5e69SMatthias Ringwald written = 0;
1450*97dc5e69SMatthias Ringwald err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
1451*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1452*97dc5e69SMatthias Ringwald done_socket(sock);
1453*97dc5e69SMatthias Ringwald /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
1454*97dc5e69SMatthias Ringwald return (err == ERR_OK ? (ssize_t)written : -1);
1455*97dc5e69SMatthias Ringwald #else /* LWIP_TCP */
1456*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1457*97dc5e69SMatthias Ringwald done_socket(sock);
1458*97dc5e69SMatthias Ringwald return -1;
1459*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP */
1460*97dc5e69SMatthias Ringwald }
1461*97dc5e69SMatthias Ringwald /* else, UDP and RAW NETCONNs */
1462*97dc5e69SMatthias Ringwald #if LWIP_UDP || LWIP_RAW
1463*97dc5e69SMatthias Ringwald {
1464*97dc5e69SMatthias Ringwald struct netbuf chain_buf;
1465*97dc5e69SMatthias Ringwald int i;
1466*97dc5e69SMatthias Ringwald ssize_t size = 0;
1467*97dc5e69SMatthias Ringwald
1468*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(flags);
1469*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendmsg: invalid msghdr name", (((msg->msg_name == NULL) && (msg->msg_namelen == 0)) ||
1470*97dc5e69SMatthias Ringwald IS_SOCK_ADDR_LEN_VALID(msg->msg_namelen)),
1471*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1472*97dc5e69SMatthias Ringwald
1473*97dc5e69SMatthias Ringwald /* initialize chain buffer with destination */
1474*97dc5e69SMatthias Ringwald memset(&chain_buf, 0, sizeof(struct netbuf));
1475*97dc5e69SMatthias Ringwald if (msg->msg_name) {
1476*97dc5e69SMatthias Ringwald u16_t remote_port;
1477*97dc5e69SMatthias Ringwald SOCKADDR_TO_IPADDR_PORT((const struct sockaddr *)msg->msg_name, &chain_buf.addr, remote_port);
1478*97dc5e69SMatthias Ringwald netbuf_fromport(&chain_buf) = remote_port;
1479*97dc5e69SMatthias Ringwald }
1480*97dc5e69SMatthias Ringwald #if LWIP_NETIF_TX_SINGLE_PBUF
1481*97dc5e69SMatthias Ringwald for (i = 0; i < msg->msg_iovlen; i++) {
1482*97dc5e69SMatthias Ringwald size += msg->msg_iov[i].iov_len;
1483*97dc5e69SMatthias Ringwald if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
1484*97dc5e69SMatthias Ringwald /* overflow */
1485*97dc5e69SMatthias Ringwald goto sendmsg_emsgsize;
1486*97dc5e69SMatthias Ringwald }
1487*97dc5e69SMatthias Ringwald }
1488*97dc5e69SMatthias Ringwald if (size > 0xFFFF) {
1489*97dc5e69SMatthias Ringwald /* overflow */
1490*97dc5e69SMatthias Ringwald goto sendmsg_emsgsize;
1491*97dc5e69SMatthias Ringwald }
1492*97dc5e69SMatthias Ringwald /* Allocate a new netbuf and copy the data into it. */
1493*97dc5e69SMatthias Ringwald if (netbuf_alloc(&chain_buf, (u16_t)size) == NULL) {
1494*97dc5e69SMatthias Ringwald err = ERR_MEM;
1495*97dc5e69SMatthias Ringwald } else {
1496*97dc5e69SMatthias Ringwald /* flatten the IO vectors */
1497*97dc5e69SMatthias Ringwald size_t offset = 0;
1498*97dc5e69SMatthias Ringwald for (i = 0; i < msg->msg_iovlen; i++) {
1499*97dc5e69SMatthias Ringwald MEMCPY(&((u8_t *)chain_buf.p->payload)[offset], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
1500*97dc5e69SMatthias Ringwald offset += msg->msg_iov[i].iov_len;
1501*97dc5e69SMatthias Ringwald }
1502*97dc5e69SMatthias Ringwald #if LWIP_CHECKSUM_ON_COPY
1503*97dc5e69SMatthias Ringwald {
1504*97dc5e69SMatthias Ringwald /* This can be improved by using LWIP_CHKSUM_COPY() and aggregating the checksum for each IO vector */
1505*97dc5e69SMatthias Ringwald u16_t chksum = ~inet_chksum_pbuf(chain_buf.p);
1506*97dc5e69SMatthias Ringwald netbuf_set_chksum(&chain_buf, chksum);
1507*97dc5e69SMatthias Ringwald }
1508*97dc5e69SMatthias Ringwald #endif /* LWIP_CHECKSUM_ON_COPY */
1509*97dc5e69SMatthias Ringwald err = ERR_OK;
1510*97dc5e69SMatthias Ringwald }
1511*97dc5e69SMatthias Ringwald #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1512*97dc5e69SMatthias Ringwald /* create a chained netbuf from the IO vectors. NOTE: we assemble a pbuf chain
1513*97dc5e69SMatthias Ringwald manually to avoid having to allocate, chain, and delete a netbuf for each iov */
1514*97dc5e69SMatthias Ringwald for (i = 0; i < msg->msg_iovlen; i++) {
1515*97dc5e69SMatthias Ringwald struct pbuf *p;
1516*97dc5e69SMatthias Ringwald if (msg->msg_iov[i].iov_len > 0xFFFF) {
1517*97dc5e69SMatthias Ringwald /* overflow */
1518*97dc5e69SMatthias Ringwald goto sendmsg_emsgsize;
1519*97dc5e69SMatthias Ringwald }
1520*97dc5e69SMatthias Ringwald p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
1521*97dc5e69SMatthias Ringwald if (p == NULL) {
1522*97dc5e69SMatthias Ringwald err = ERR_MEM; /* let netbuf_delete() cleanup chain_buf */
1523*97dc5e69SMatthias Ringwald break;
1524*97dc5e69SMatthias Ringwald }
1525*97dc5e69SMatthias Ringwald p->payload = msg->msg_iov[i].iov_base;
1526*97dc5e69SMatthias Ringwald p->len = p->tot_len = (u16_t)msg->msg_iov[i].iov_len;
1527*97dc5e69SMatthias Ringwald /* netbuf empty, add new pbuf */
1528*97dc5e69SMatthias Ringwald if (chain_buf.p == NULL) {
1529*97dc5e69SMatthias Ringwald chain_buf.p = chain_buf.ptr = p;
1530*97dc5e69SMatthias Ringwald /* add pbuf to existing pbuf chain */
1531*97dc5e69SMatthias Ringwald } else {
1532*97dc5e69SMatthias Ringwald if (chain_buf.p->tot_len + p->len > 0xffff) {
1533*97dc5e69SMatthias Ringwald /* overflow */
1534*97dc5e69SMatthias Ringwald pbuf_free(p);
1535*97dc5e69SMatthias Ringwald goto sendmsg_emsgsize;
1536*97dc5e69SMatthias Ringwald }
1537*97dc5e69SMatthias Ringwald pbuf_cat(chain_buf.p, p);
1538*97dc5e69SMatthias Ringwald }
1539*97dc5e69SMatthias Ringwald }
1540*97dc5e69SMatthias Ringwald /* save size of total chain */
1541*97dc5e69SMatthias Ringwald if (err == ERR_OK) {
1542*97dc5e69SMatthias Ringwald size = netbuf_len(&chain_buf);
1543*97dc5e69SMatthias Ringwald }
1544*97dc5e69SMatthias Ringwald #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1545*97dc5e69SMatthias Ringwald
1546*97dc5e69SMatthias Ringwald if (err == ERR_OK) {
1547*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
1548*97dc5e69SMatthias Ringwald /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1549*97dc5e69SMatthias Ringwald if (IP_IS_V6_VAL(chain_buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&chain_buf.addr))) {
1550*97dc5e69SMatthias Ringwald unmap_ipv4_mapped_ipv6(ip_2_ip4(&chain_buf.addr), ip_2_ip6(&chain_buf.addr));
1551*97dc5e69SMatthias Ringwald IP_SET_TYPE_VAL(chain_buf.addr, IPADDR_TYPE_V4);
1552*97dc5e69SMatthias Ringwald }
1553*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
1554*97dc5e69SMatthias Ringwald
1555*97dc5e69SMatthias Ringwald /* send the data */
1556*97dc5e69SMatthias Ringwald err = netconn_send(sock->conn, &chain_buf);
1557*97dc5e69SMatthias Ringwald }
1558*97dc5e69SMatthias Ringwald
1559*97dc5e69SMatthias Ringwald /* deallocated the buffer */
1560*97dc5e69SMatthias Ringwald netbuf_free(&chain_buf);
1561*97dc5e69SMatthias Ringwald
1562*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1563*97dc5e69SMatthias Ringwald done_socket(sock);
1564*97dc5e69SMatthias Ringwald return (err == ERR_OK ? size : -1);
1565*97dc5e69SMatthias Ringwald sendmsg_emsgsize:
1566*97dc5e69SMatthias Ringwald set_errno(EMSGSIZE);
1567*97dc5e69SMatthias Ringwald netbuf_free(&chain_buf);
1568*97dc5e69SMatthias Ringwald done_socket(sock);
1569*97dc5e69SMatthias Ringwald return -1;
1570*97dc5e69SMatthias Ringwald }
1571*97dc5e69SMatthias Ringwald #else /* LWIP_UDP || LWIP_RAW */
1572*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1573*97dc5e69SMatthias Ringwald done_socket(sock);
1574*97dc5e69SMatthias Ringwald return -1;
1575*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP || LWIP_RAW */
1576*97dc5e69SMatthias Ringwald }
1577*97dc5e69SMatthias Ringwald
1578*97dc5e69SMatthias Ringwald ssize_t
lwip_sendto(int s,const void * data,size_t size,int flags,const struct sockaddr * to,socklen_t tolen)1579*97dc5e69SMatthias Ringwald lwip_sendto(int s, const void *data, size_t size, int flags,
1580*97dc5e69SMatthias Ringwald const struct sockaddr *to, socklen_t tolen)
1581*97dc5e69SMatthias Ringwald {
1582*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1583*97dc5e69SMatthias Ringwald err_t err;
1584*97dc5e69SMatthias Ringwald u16_t short_size;
1585*97dc5e69SMatthias Ringwald u16_t remote_port;
1586*97dc5e69SMatthias Ringwald struct netbuf buf;
1587*97dc5e69SMatthias Ringwald
1588*97dc5e69SMatthias Ringwald sock = get_socket(s);
1589*97dc5e69SMatthias Ringwald if (!sock) {
1590*97dc5e69SMatthias Ringwald return -1;
1591*97dc5e69SMatthias Ringwald }
1592*97dc5e69SMatthias Ringwald
1593*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
1594*97dc5e69SMatthias Ringwald #if LWIP_TCP
1595*97dc5e69SMatthias Ringwald done_socket(sock);
1596*97dc5e69SMatthias Ringwald return lwip_send(s, data, size, flags);
1597*97dc5e69SMatthias Ringwald #else /* LWIP_TCP */
1598*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(flags);
1599*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG));
1600*97dc5e69SMatthias Ringwald done_socket(sock);
1601*97dc5e69SMatthias Ringwald return -1;
1602*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP */
1603*97dc5e69SMatthias Ringwald }
1604*97dc5e69SMatthias Ringwald
1605*97dc5e69SMatthias Ringwald if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
1606*97dc5e69SMatthias Ringwald /* cannot fit into one datagram (at least for us) */
1607*97dc5e69SMatthias Ringwald set_errno(EMSGSIZE);
1608*97dc5e69SMatthias Ringwald done_socket(sock);
1609*97dc5e69SMatthias Ringwald return -1;
1610*97dc5e69SMatthias Ringwald }
1611*97dc5e69SMatthias Ringwald short_size = (u16_t)size;
1612*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
1613*97dc5e69SMatthias Ringwald (IS_SOCK_ADDR_LEN_VALID(tolen) &&
1614*97dc5e69SMatthias Ringwald ((to != NULL) && (IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))))),
1615*97dc5e69SMatthias Ringwald set_errno(err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
1616*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(tolen);
1617*97dc5e69SMatthias Ringwald
1618*97dc5e69SMatthias Ringwald /* initialize a buffer */
1619*97dc5e69SMatthias Ringwald buf.p = buf.ptr = NULL;
1620*97dc5e69SMatthias Ringwald #if LWIP_CHECKSUM_ON_COPY
1621*97dc5e69SMatthias Ringwald buf.flags = 0;
1622*97dc5e69SMatthias Ringwald #endif /* LWIP_CHECKSUM_ON_COPY */
1623*97dc5e69SMatthias Ringwald if (to) {
1624*97dc5e69SMatthias Ringwald SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
1625*97dc5e69SMatthias Ringwald } else {
1626*97dc5e69SMatthias Ringwald remote_port = 0;
1627*97dc5e69SMatthias Ringwald ip_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
1628*97dc5e69SMatthias Ringwald }
1629*97dc5e69SMatthias Ringwald netbuf_fromport(&buf) = remote_port;
1630*97dc5e69SMatthias Ringwald
1631*97dc5e69SMatthias Ringwald
1632*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
1633*97dc5e69SMatthias Ringwald s, data, short_size, flags));
1634*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, buf.addr);
1635*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
1636*97dc5e69SMatthias Ringwald
1637*97dc5e69SMatthias Ringwald /* make the buffer point to the data that should be sent */
1638*97dc5e69SMatthias Ringwald #if LWIP_NETIF_TX_SINGLE_PBUF
1639*97dc5e69SMatthias Ringwald /* Allocate a new netbuf and copy the data into it. */
1640*97dc5e69SMatthias Ringwald if (netbuf_alloc(&buf, short_size) == NULL) {
1641*97dc5e69SMatthias Ringwald err = ERR_MEM;
1642*97dc5e69SMatthias Ringwald } else {
1643*97dc5e69SMatthias Ringwald #if LWIP_CHECKSUM_ON_COPY
1644*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
1645*97dc5e69SMatthias Ringwald u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
1646*97dc5e69SMatthias Ringwald netbuf_set_chksum(&buf, chksum);
1647*97dc5e69SMatthias Ringwald } else
1648*97dc5e69SMatthias Ringwald #endif /* LWIP_CHECKSUM_ON_COPY */
1649*97dc5e69SMatthias Ringwald {
1650*97dc5e69SMatthias Ringwald MEMCPY(buf.p->payload, data, short_size);
1651*97dc5e69SMatthias Ringwald }
1652*97dc5e69SMatthias Ringwald err = ERR_OK;
1653*97dc5e69SMatthias Ringwald }
1654*97dc5e69SMatthias Ringwald #else /* LWIP_NETIF_TX_SINGLE_PBUF */
1655*97dc5e69SMatthias Ringwald err = netbuf_ref(&buf, data, short_size);
1656*97dc5e69SMatthias Ringwald #endif /* LWIP_NETIF_TX_SINGLE_PBUF */
1657*97dc5e69SMatthias Ringwald if (err == ERR_OK) {
1658*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
1659*97dc5e69SMatthias Ringwald /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */
1660*97dc5e69SMatthias Ringwald if (IP_IS_V6_VAL(buf.addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&buf.addr))) {
1661*97dc5e69SMatthias Ringwald unmap_ipv4_mapped_ipv6(ip_2_ip4(&buf.addr), ip_2_ip6(&buf.addr));
1662*97dc5e69SMatthias Ringwald IP_SET_TYPE_VAL(buf.addr, IPADDR_TYPE_V4);
1663*97dc5e69SMatthias Ringwald }
1664*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
1665*97dc5e69SMatthias Ringwald
1666*97dc5e69SMatthias Ringwald /* send the data */
1667*97dc5e69SMatthias Ringwald err = netconn_send(sock->conn, &buf);
1668*97dc5e69SMatthias Ringwald }
1669*97dc5e69SMatthias Ringwald
1670*97dc5e69SMatthias Ringwald /* deallocated the buffer */
1671*97dc5e69SMatthias Ringwald netbuf_free(&buf);
1672*97dc5e69SMatthias Ringwald
1673*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
1674*97dc5e69SMatthias Ringwald done_socket(sock);
1675*97dc5e69SMatthias Ringwald return (err == ERR_OK ? short_size : -1);
1676*97dc5e69SMatthias Ringwald }
1677*97dc5e69SMatthias Ringwald
1678*97dc5e69SMatthias Ringwald int
lwip_socket(int domain,int type,int protocol)1679*97dc5e69SMatthias Ringwald lwip_socket(int domain, int type, int protocol)
1680*97dc5e69SMatthias Ringwald {
1681*97dc5e69SMatthias Ringwald struct netconn *conn;
1682*97dc5e69SMatthias Ringwald int i;
1683*97dc5e69SMatthias Ringwald
1684*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(domain); /* @todo: check this */
1685*97dc5e69SMatthias Ringwald
1686*97dc5e69SMatthias Ringwald /* create a netconn */
1687*97dc5e69SMatthias Ringwald switch (type) {
1688*97dc5e69SMatthias Ringwald case SOCK_RAW:
1689*97dc5e69SMatthias Ringwald conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
1690*97dc5e69SMatthias Ringwald (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
1691*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
1692*97dc5e69SMatthias Ringwald domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1693*97dc5e69SMatthias Ringwald break;
1694*97dc5e69SMatthias Ringwald case SOCK_DGRAM:
1695*97dc5e69SMatthias Ringwald conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
1696*97dc5e69SMatthias Ringwald ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)),
1697*97dc5e69SMatthias Ringwald DEFAULT_SOCKET_EVENTCB);
1698*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
1699*97dc5e69SMatthias Ringwald domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1700*97dc5e69SMatthias Ringwald #if LWIP_NETBUF_RECVINFO
1701*97dc5e69SMatthias Ringwald if (conn) {
1702*97dc5e69SMatthias Ringwald /* netconn layer enables pktinfo by default, sockets default to off */
1703*97dc5e69SMatthias Ringwald conn->flags &= ~NETCONN_FLAG_PKTINFO;
1704*97dc5e69SMatthias Ringwald }
1705*97dc5e69SMatthias Ringwald #endif /* LWIP_NETBUF_RECVINFO */
1706*97dc5e69SMatthias Ringwald break;
1707*97dc5e69SMatthias Ringwald case SOCK_STREAM:
1708*97dc5e69SMatthias Ringwald conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
1709*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
1710*97dc5e69SMatthias Ringwald domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
1711*97dc5e69SMatthias Ringwald break;
1712*97dc5e69SMatthias Ringwald default:
1713*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
1714*97dc5e69SMatthias Ringwald domain, type, protocol));
1715*97dc5e69SMatthias Ringwald set_errno(EINVAL);
1716*97dc5e69SMatthias Ringwald return -1;
1717*97dc5e69SMatthias Ringwald }
1718*97dc5e69SMatthias Ringwald
1719*97dc5e69SMatthias Ringwald if (!conn) {
1720*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
1721*97dc5e69SMatthias Ringwald set_errno(ENOBUFS);
1722*97dc5e69SMatthias Ringwald return -1;
1723*97dc5e69SMatthias Ringwald }
1724*97dc5e69SMatthias Ringwald
1725*97dc5e69SMatthias Ringwald i = alloc_socket(conn, 0);
1726*97dc5e69SMatthias Ringwald
1727*97dc5e69SMatthias Ringwald if (i == -1) {
1728*97dc5e69SMatthias Ringwald netconn_delete(conn);
1729*97dc5e69SMatthias Ringwald set_errno(ENFILE);
1730*97dc5e69SMatthias Ringwald return -1;
1731*97dc5e69SMatthias Ringwald }
1732*97dc5e69SMatthias Ringwald conn->socket = i;
1733*97dc5e69SMatthias Ringwald done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
1734*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
1735*97dc5e69SMatthias Ringwald set_errno(0);
1736*97dc5e69SMatthias Ringwald return i;
1737*97dc5e69SMatthias Ringwald }
1738*97dc5e69SMatthias Ringwald
1739*97dc5e69SMatthias Ringwald ssize_t
lwip_write(int s,const void * data,size_t size)1740*97dc5e69SMatthias Ringwald lwip_write(int s, const void *data, size_t size)
1741*97dc5e69SMatthias Ringwald {
1742*97dc5e69SMatthias Ringwald return lwip_send(s, data, size, 0);
1743*97dc5e69SMatthias Ringwald }
1744*97dc5e69SMatthias Ringwald
1745*97dc5e69SMatthias Ringwald ssize_t
lwip_writev(int s,const struct iovec * iov,int iovcnt)1746*97dc5e69SMatthias Ringwald lwip_writev(int s, const struct iovec *iov, int iovcnt)
1747*97dc5e69SMatthias Ringwald {
1748*97dc5e69SMatthias Ringwald struct msghdr msg;
1749*97dc5e69SMatthias Ringwald
1750*97dc5e69SMatthias Ringwald msg.msg_name = NULL;
1751*97dc5e69SMatthias Ringwald msg.msg_namelen = 0;
1752*97dc5e69SMatthias Ringwald /* Hack: we have to cast via number to cast from 'const' pointer to non-const.
1753*97dc5e69SMatthias Ringwald Blame the opengroup standard for this inconsistency. */
1754*97dc5e69SMatthias Ringwald msg.msg_iov = LWIP_CONST_CAST(struct iovec *, iov);
1755*97dc5e69SMatthias Ringwald msg.msg_iovlen = iovcnt;
1756*97dc5e69SMatthias Ringwald msg.msg_control = NULL;
1757*97dc5e69SMatthias Ringwald msg.msg_controllen = 0;
1758*97dc5e69SMatthias Ringwald msg.msg_flags = 0;
1759*97dc5e69SMatthias Ringwald return lwip_sendmsg(s, &msg, 0);
1760*97dc5e69SMatthias Ringwald }
1761*97dc5e69SMatthias Ringwald
1762*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
1763*97dc5e69SMatthias Ringwald /* Add select_cb to select_cb_list. */
1764*97dc5e69SMatthias Ringwald static void
lwip_link_select_cb(struct lwip_select_cb * select_cb)1765*97dc5e69SMatthias Ringwald lwip_link_select_cb(struct lwip_select_cb *select_cb)
1766*97dc5e69SMatthias Ringwald {
1767*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
1768*97dc5e69SMatthias Ringwald
1769*97dc5e69SMatthias Ringwald /* Protect the select_cb_list */
1770*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_PROTECT(lev);
1771*97dc5e69SMatthias Ringwald
1772*97dc5e69SMatthias Ringwald /* Put this select_cb on top of list */
1773*97dc5e69SMatthias Ringwald select_cb->next = select_cb_list;
1774*97dc5e69SMatthias Ringwald if (select_cb_list != NULL) {
1775*97dc5e69SMatthias Ringwald select_cb_list->prev = select_cb;
1776*97dc5e69SMatthias Ringwald }
1777*97dc5e69SMatthias Ringwald select_cb_list = select_cb;
1778*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
1779*97dc5e69SMatthias Ringwald /* Increasing this counter tells select_check_waiters that the list has changed. */
1780*97dc5e69SMatthias Ringwald select_cb_ctr++;
1781*97dc5e69SMatthias Ringwald #endif
1782*97dc5e69SMatthias Ringwald
1783*97dc5e69SMatthias Ringwald /* Now we can safely unprotect */
1784*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_UNPROTECT(lev);
1785*97dc5e69SMatthias Ringwald }
1786*97dc5e69SMatthias Ringwald
1787*97dc5e69SMatthias Ringwald /* Remove select_cb from select_cb_list. */
1788*97dc5e69SMatthias Ringwald static void
lwip_unlink_select_cb(struct lwip_select_cb * select_cb)1789*97dc5e69SMatthias Ringwald lwip_unlink_select_cb(struct lwip_select_cb *select_cb)
1790*97dc5e69SMatthias Ringwald {
1791*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_DECL_PROTECT(lev);
1792*97dc5e69SMatthias Ringwald
1793*97dc5e69SMatthias Ringwald /* Take us off the list */
1794*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_PROTECT(lev);
1795*97dc5e69SMatthias Ringwald if (select_cb->next != NULL) {
1796*97dc5e69SMatthias Ringwald select_cb->next->prev = select_cb->prev;
1797*97dc5e69SMatthias Ringwald }
1798*97dc5e69SMatthias Ringwald if (select_cb_list == select_cb) {
1799*97dc5e69SMatthias Ringwald LWIP_ASSERT("select_cb->prev == NULL", select_cb->prev == NULL);
1800*97dc5e69SMatthias Ringwald select_cb_list = select_cb->next;
1801*97dc5e69SMatthias Ringwald } else {
1802*97dc5e69SMatthias Ringwald LWIP_ASSERT("select_cb->prev != NULL", select_cb->prev != NULL);
1803*97dc5e69SMatthias Ringwald select_cb->prev->next = select_cb->next;
1804*97dc5e69SMatthias Ringwald }
1805*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
1806*97dc5e69SMatthias Ringwald /* Increasing this counter tells select_check_waiters that the list has changed. */
1807*97dc5e69SMatthias Ringwald select_cb_ctr++;
1808*97dc5e69SMatthias Ringwald #endif
1809*97dc5e69SMatthias Ringwald LWIP_SOCKET_SELECT_UNPROTECT(lev);
1810*97dc5e69SMatthias Ringwald }
1811*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
1812*97dc5e69SMatthias Ringwald
1813*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT
1814*97dc5e69SMatthias Ringwald /**
1815*97dc5e69SMatthias Ringwald * Go through the readset and writeset lists and see which socket of the sockets
1816*97dc5e69SMatthias Ringwald * set in the sets has events. On return, readset, writeset and exceptset have
1817*97dc5e69SMatthias Ringwald * the sockets enabled that had events.
1818*97dc5e69SMatthias Ringwald *
1819*97dc5e69SMatthias Ringwald * @param maxfdp1 the highest socket index in the sets
1820*97dc5e69SMatthias Ringwald * @param readset_in set of sockets to check for read events
1821*97dc5e69SMatthias Ringwald * @param writeset_in set of sockets to check for write events
1822*97dc5e69SMatthias Ringwald * @param exceptset_in set of sockets to check for error events
1823*97dc5e69SMatthias Ringwald * @param readset_out set of sockets that had read events
1824*97dc5e69SMatthias Ringwald * @param writeset_out set of sockets that had write events
1825*97dc5e69SMatthias Ringwald * @param exceptset_out set os sockets that had error events
1826*97dc5e69SMatthias Ringwald * @return number of sockets that had events (read/write/exception) (>= 0)
1827*97dc5e69SMatthias Ringwald */
1828*97dc5e69SMatthias Ringwald static int
lwip_selscan(int maxfdp1,fd_set * readset_in,fd_set * writeset_in,fd_set * exceptset_in,fd_set * readset_out,fd_set * writeset_out,fd_set * exceptset_out)1829*97dc5e69SMatthias Ringwald lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
1830*97dc5e69SMatthias Ringwald fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
1831*97dc5e69SMatthias Ringwald {
1832*97dc5e69SMatthias Ringwald int i, nready = 0;
1833*97dc5e69SMatthias Ringwald fd_set lreadset, lwriteset, lexceptset;
1834*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1835*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
1836*97dc5e69SMatthias Ringwald
1837*97dc5e69SMatthias Ringwald FD_ZERO(&lreadset);
1838*97dc5e69SMatthias Ringwald FD_ZERO(&lwriteset);
1839*97dc5e69SMatthias Ringwald FD_ZERO(&lexceptset);
1840*97dc5e69SMatthias Ringwald
1841*97dc5e69SMatthias Ringwald /* Go through each socket in each list to count number of sockets which
1842*97dc5e69SMatthias Ringwald currently match */
1843*97dc5e69SMatthias Ringwald for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
1844*97dc5e69SMatthias Ringwald /* if this FD is not in the set, continue */
1845*97dc5e69SMatthias Ringwald if (!(readset_in && FD_ISSET(i, readset_in)) &&
1846*97dc5e69SMatthias Ringwald !(writeset_in && FD_ISSET(i, writeset_in)) &&
1847*97dc5e69SMatthias Ringwald !(exceptset_in && FD_ISSET(i, exceptset_in))) {
1848*97dc5e69SMatthias Ringwald continue;
1849*97dc5e69SMatthias Ringwald }
1850*97dc5e69SMatthias Ringwald /* First get the socket's status (protected)... */
1851*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
1852*97dc5e69SMatthias Ringwald sock = tryget_socket_unconn_locked(i);
1853*97dc5e69SMatthias Ringwald if (sock != NULL) {
1854*97dc5e69SMatthias Ringwald void *lastdata = sock->lastdata.pbuf;
1855*97dc5e69SMatthias Ringwald s16_t rcvevent = sock->rcvevent;
1856*97dc5e69SMatthias Ringwald u16_t sendevent = sock->sendevent;
1857*97dc5e69SMatthias Ringwald u16_t errevent = sock->errevent;
1858*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
1859*97dc5e69SMatthias Ringwald
1860*97dc5e69SMatthias Ringwald /* ... then examine it: */
1861*97dc5e69SMatthias Ringwald /* See if netconn of this socket is ready for read */
1862*97dc5e69SMatthias Ringwald if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
1863*97dc5e69SMatthias Ringwald FD_SET(i, &lreadset);
1864*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
1865*97dc5e69SMatthias Ringwald nready++;
1866*97dc5e69SMatthias Ringwald }
1867*97dc5e69SMatthias Ringwald /* See if netconn of this socket is ready for write */
1868*97dc5e69SMatthias Ringwald if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
1869*97dc5e69SMatthias Ringwald FD_SET(i, &lwriteset);
1870*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
1871*97dc5e69SMatthias Ringwald nready++;
1872*97dc5e69SMatthias Ringwald }
1873*97dc5e69SMatthias Ringwald /* See if netconn of this socket had an error */
1874*97dc5e69SMatthias Ringwald if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
1875*97dc5e69SMatthias Ringwald FD_SET(i, &lexceptset);
1876*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
1877*97dc5e69SMatthias Ringwald nready++;
1878*97dc5e69SMatthias Ringwald }
1879*97dc5e69SMatthias Ringwald done_socket(sock);
1880*97dc5e69SMatthias Ringwald } else {
1881*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
1882*97dc5e69SMatthias Ringwald /* no a valid open socket */
1883*97dc5e69SMatthias Ringwald return -1;
1884*97dc5e69SMatthias Ringwald }
1885*97dc5e69SMatthias Ringwald }
1886*97dc5e69SMatthias Ringwald /* copy local sets to the ones provided as arguments */
1887*97dc5e69SMatthias Ringwald *readset_out = lreadset;
1888*97dc5e69SMatthias Ringwald *writeset_out = lwriteset;
1889*97dc5e69SMatthias Ringwald *exceptset_out = lexceptset;
1890*97dc5e69SMatthias Ringwald
1891*97dc5e69SMatthias Ringwald LWIP_ASSERT("nready >= 0", nready >= 0);
1892*97dc5e69SMatthias Ringwald return nready;
1893*97dc5e69SMatthias Ringwald }
1894*97dc5e69SMatthias Ringwald
1895*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
1896*97dc5e69SMatthias Ringwald /* Mark all of the set sockets in one of the three fdsets passed to select as used.
1897*97dc5e69SMatthias Ringwald * All sockets are marked (and later unmarked), whether they are open or not.
1898*97dc5e69SMatthias Ringwald * This is OK as lwip_selscan aborts select when non-open sockets are found.
1899*97dc5e69SMatthias Ringwald */
1900*97dc5e69SMatthias Ringwald static void
lwip_select_inc_sockets_used_set(int maxfdp,fd_set * fdset,fd_set * used_sockets)1901*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
1902*97dc5e69SMatthias Ringwald {
1903*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
1904*97dc5e69SMatthias Ringwald if (fdset) {
1905*97dc5e69SMatthias Ringwald int i;
1906*97dc5e69SMatthias Ringwald for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1907*97dc5e69SMatthias Ringwald /* if this FD is in the set, lock it (unless already done) */
1908*97dc5e69SMatthias Ringwald if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
1909*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
1910*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
1911*97dc5e69SMatthias Ringwald sock = tryget_socket_unconn_locked(i);
1912*97dc5e69SMatthias Ringwald if (sock != NULL) {
1913*97dc5e69SMatthias Ringwald /* leave the socket used until released by lwip_select_dec_sockets_used */
1914*97dc5e69SMatthias Ringwald FD_SET(i, used_sockets);
1915*97dc5e69SMatthias Ringwald }
1916*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
1917*97dc5e69SMatthias Ringwald }
1918*97dc5e69SMatthias Ringwald }
1919*97dc5e69SMatthias Ringwald }
1920*97dc5e69SMatthias Ringwald }
1921*97dc5e69SMatthias Ringwald
1922*97dc5e69SMatthias Ringwald /* Mark all sockets passed to select as used to prevent them from being freed
1923*97dc5e69SMatthias Ringwald * from other threads while select is running.
1924*97dc5e69SMatthias Ringwald * Marked sockets are added to 'used_sockets' to mark them only once an be able
1925*97dc5e69SMatthias Ringwald * to unmark them correctly.
1926*97dc5e69SMatthias Ringwald */
1927*97dc5e69SMatthias Ringwald static void
lwip_select_inc_sockets_used(int maxfdp,fd_set * fdset1,fd_set * fdset2,fd_set * fdset3,fd_set * used_sockets)1928*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
1929*97dc5e69SMatthias Ringwald {
1930*97dc5e69SMatthias Ringwald FD_ZERO(used_sockets);
1931*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
1932*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
1933*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
1934*97dc5e69SMatthias Ringwald }
1935*97dc5e69SMatthias Ringwald
1936*97dc5e69SMatthias Ringwald /* Let go all sockets that were marked as used when starting select */
1937*97dc5e69SMatthias Ringwald static void
lwip_select_dec_sockets_used(int maxfdp,fd_set * used_sockets)1938*97dc5e69SMatthias Ringwald lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
1939*97dc5e69SMatthias Ringwald {
1940*97dc5e69SMatthias Ringwald int i;
1941*97dc5e69SMatthias Ringwald for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
1942*97dc5e69SMatthias Ringwald /* if this FD is not in the set, continue */
1943*97dc5e69SMatthias Ringwald if (FD_ISSET(i, used_sockets)) {
1944*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
1945*97dc5e69SMatthias Ringwald LWIP_ASSERT("socket gone at the end of select", sock != NULL);
1946*97dc5e69SMatthias Ringwald if (sock != NULL) {
1947*97dc5e69SMatthias Ringwald done_socket(sock);
1948*97dc5e69SMatthias Ringwald }
1949*97dc5e69SMatthias Ringwald }
1950*97dc5e69SMatthias Ringwald }
1951*97dc5e69SMatthias Ringwald }
1952*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_FULLDUPLEX */
1953*97dc5e69SMatthias Ringwald #define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
1954*97dc5e69SMatthias Ringwald #define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
1955*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_FULLDUPLEX */
1956*97dc5e69SMatthias Ringwald
1957*97dc5e69SMatthias Ringwald int
lwip_select(int maxfdp1,fd_set * readset,fd_set * writeset,fd_set * exceptset,struct timeval * timeout)1958*97dc5e69SMatthias Ringwald lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
1959*97dc5e69SMatthias Ringwald struct timeval *timeout)
1960*97dc5e69SMatthias Ringwald {
1961*97dc5e69SMatthias Ringwald u32_t waitres = 0;
1962*97dc5e69SMatthias Ringwald int nready;
1963*97dc5e69SMatthias Ringwald fd_set lreadset, lwriteset, lexceptset;
1964*97dc5e69SMatthias Ringwald u32_t msectimeout;
1965*97dc5e69SMatthias Ringwald int i;
1966*97dc5e69SMatthias Ringwald int maxfdp2;
1967*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
1968*97dc5e69SMatthias Ringwald int waited = 0;
1969*97dc5e69SMatthias Ringwald #endif
1970*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
1971*97dc5e69SMatthias Ringwald fd_set used_sockets;
1972*97dc5e69SMatthias Ringwald #endif
1973*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
1974*97dc5e69SMatthias Ringwald
1975*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
1976*97dc5e69SMatthias Ringwald maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
1977*97dc5e69SMatthias Ringwald timeout ? (s32_t)timeout->tv_sec : (s32_t) - 1,
1978*97dc5e69SMatthias Ringwald timeout ? (s32_t)timeout->tv_usec : (s32_t) - 1));
1979*97dc5e69SMatthias Ringwald
1980*97dc5e69SMatthias Ringwald if ((maxfdp1 < 0) || (maxfdp1 > LWIP_SELECT_MAXNFDS)) {
1981*97dc5e69SMatthias Ringwald set_errno(EINVAL);
1982*97dc5e69SMatthias Ringwald return -1;
1983*97dc5e69SMatthias Ringwald }
1984*97dc5e69SMatthias Ringwald
1985*97dc5e69SMatthias Ringwald lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
1986*97dc5e69SMatthias Ringwald
1987*97dc5e69SMatthias Ringwald /* Go through each socket in each list to count number of sockets which
1988*97dc5e69SMatthias Ringwald currently match */
1989*97dc5e69SMatthias Ringwald nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
1990*97dc5e69SMatthias Ringwald
1991*97dc5e69SMatthias Ringwald if (nready < 0) {
1992*97dc5e69SMatthias Ringwald /* one of the sockets in one of the fd_sets was invalid */
1993*97dc5e69SMatthias Ringwald set_errno(EBADF);
1994*97dc5e69SMatthias Ringwald lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
1995*97dc5e69SMatthias Ringwald return -1;
1996*97dc5e69SMatthias Ringwald } else if (nready > 0) {
1997*97dc5e69SMatthias Ringwald /* one or more sockets are set, no need to wait */
1998*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
1999*97dc5e69SMatthias Ringwald } else {
2000*97dc5e69SMatthias Ringwald /* If we don't have any current events, then suspend if we are supposed to */
2001*97dc5e69SMatthias Ringwald if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
2002*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
2003*97dc5e69SMatthias Ringwald /* This is OK as the local fdsets are empty and nready is zero,
2004*97dc5e69SMatthias Ringwald or we would have returned earlier. */
2005*97dc5e69SMatthias Ringwald } else {
2006*97dc5e69SMatthias Ringwald /* None ready: add our semaphore to list:
2007*97dc5e69SMatthias Ringwald We don't actually need any dynamic memory. Our entry on the
2008*97dc5e69SMatthias Ringwald list is only valid while we are in this function, so it's ok
2009*97dc5e69SMatthias Ringwald to use local variables (unless we're running in MPU compatible
2010*97dc5e69SMatthias Ringwald mode). */
2011*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_DECLARE(select_cb);
2012*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); lwip_select_dec_sockets_used(maxfdp1, &used_sockets); return -1);
2013*97dc5e69SMatthias Ringwald memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
2014*97dc5e69SMatthias Ringwald
2015*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).readset = readset;
2016*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
2017*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
2018*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2019*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
2020*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_SEM_PER_THREAD */
2021*97dc5e69SMatthias Ringwald if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
2022*97dc5e69SMatthias Ringwald /* failed to create semaphore */
2023*97dc5e69SMatthias Ringwald set_errno(ENOMEM);
2024*97dc5e69SMatthias Ringwald lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2025*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_FREE(select_cb);
2026*97dc5e69SMatthias Ringwald return -1;
2027*97dc5e69SMatthias Ringwald }
2028*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2029*97dc5e69SMatthias Ringwald
2030*97dc5e69SMatthias Ringwald lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2031*97dc5e69SMatthias Ringwald
2032*97dc5e69SMatthias Ringwald /* Increase select_waiting for each socket we are interested in */
2033*97dc5e69SMatthias Ringwald maxfdp2 = maxfdp1;
2034*97dc5e69SMatthias Ringwald for (i = LWIP_SOCKET_OFFSET; i < maxfdp1; i++) {
2035*97dc5e69SMatthias Ringwald if ((readset && FD_ISSET(i, readset)) ||
2036*97dc5e69SMatthias Ringwald (writeset && FD_ISSET(i, writeset)) ||
2037*97dc5e69SMatthias Ringwald (exceptset && FD_ISSET(i, exceptset))) {
2038*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2039*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2040*97dc5e69SMatthias Ringwald sock = tryget_socket_unconn_locked(i);
2041*97dc5e69SMatthias Ringwald if (sock != NULL) {
2042*97dc5e69SMatthias Ringwald sock->select_waiting++;
2043*97dc5e69SMatthias Ringwald if (sock->select_waiting == 0) {
2044*97dc5e69SMatthias Ringwald /* overflow - too many threads waiting */
2045*97dc5e69SMatthias Ringwald sock->select_waiting--;
2046*97dc5e69SMatthias Ringwald nready = -1;
2047*97dc5e69SMatthias Ringwald maxfdp2 = i;
2048*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2049*97dc5e69SMatthias Ringwald done_socket(sock);
2050*97dc5e69SMatthias Ringwald set_errno(EBUSY);
2051*97dc5e69SMatthias Ringwald break;
2052*97dc5e69SMatthias Ringwald }
2053*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2054*97dc5e69SMatthias Ringwald done_socket(sock);
2055*97dc5e69SMatthias Ringwald } else {
2056*97dc5e69SMatthias Ringwald /* Not a valid socket */
2057*97dc5e69SMatthias Ringwald nready = -1;
2058*97dc5e69SMatthias Ringwald maxfdp2 = i;
2059*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2060*97dc5e69SMatthias Ringwald set_errno(EBADF);
2061*97dc5e69SMatthias Ringwald break;
2062*97dc5e69SMatthias Ringwald }
2063*97dc5e69SMatthias Ringwald }
2064*97dc5e69SMatthias Ringwald }
2065*97dc5e69SMatthias Ringwald
2066*97dc5e69SMatthias Ringwald if (nready >= 0) {
2067*97dc5e69SMatthias Ringwald /* Call lwip_selscan again: there could have been events between
2068*97dc5e69SMatthias Ringwald the last scan (without us on the list) and putting us on the list! */
2069*97dc5e69SMatthias Ringwald nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
2070*97dc5e69SMatthias Ringwald if (!nready) {
2071*97dc5e69SMatthias Ringwald /* Still none ready, just wait to be woken */
2072*97dc5e69SMatthias Ringwald if (timeout == 0) {
2073*97dc5e69SMatthias Ringwald /* Wait forever */
2074*97dc5e69SMatthias Ringwald msectimeout = 0;
2075*97dc5e69SMatthias Ringwald } else {
2076*97dc5e69SMatthias Ringwald long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
2077*97dc5e69SMatthias Ringwald if (msecs_long <= 0) {
2078*97dc5e69SMatthias Ringwald /* Wait 1ms at least (0 means wait forever) */
2079*97dc5e69SMatthias Ringwald msectimeout = 1;
2080*97dc5e69SMatthias Ringwald } else {
2081*97dc5e69SMatthias Ringwald msectimeout = (u32_t)msecs_long;
2082*97dc5e69SMatthias Ringwald }
2083*97dc5e69SMatthias Ringwald }
2084*97dc5e69SMatthias Ringwald
2085*97dc5e69SMatthias Ringwald waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
2086*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2087*97dc5e69SMatthias Ringwald waited = 1;
2088*97dc5e69SMatthias Ringwald #endif
2089*97dc5e69SMatthias Ringwald }
2090*97dc5e69SMatthias Ringwald }
2091*97dc5e69SMatthias Ringwald
2092*97dc5e69SMatthias Ringwald /* Decrease select_waiting for each socket we are interested in */
2093*97dc5e69SMatthias Ringwald for (i = LWIP_SOCKET_OFFSET; i < maxfdp2; i++) {
2094*97dc5e69SMatthias Ringwald if ((readset && FD_ISSET(i, readset)) ||
2095*97dc5e69SMatthias Ringwald (writeset && FD_ISSET(i, writeset)) ||
2096*97dc5e69SMatthias Ringwald (exceptset && FD_ISSET(i, exceptset))) {
2097*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2098*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2099*97dc5e69SMatthias Ringwald sock = tryget_socket_unconn_locked(i);
2100*97dc5e69SMatthias Ringwald if (sock != NULL) {
2101*97dc5e69SMatthias Ringwald /* for now, handle select_waiting==0... */
2102*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
2103*97dc5e69SMatthias Ringwald if (sock->select_waiting > 0) {
2104*97dc5e69SMatthias Ringwald sock->select_waiting--;
2105*97dc5e69SMatthias Ringwald }
2106*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2107*97dc5e69SMatthias Ringwald done_socket(sock);
2108*97dc5e69SMatthias Ringwald } else {
2109*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2110*97dc5e69SMatthias Ringwald /* Not a valid socket */
2111*97dc5e69SMatthias Ringwald nready = -1;
2112*97dc5e69SMatthias Ringwald set_errno(EBADF);
2113*97dc5e69SMatthias Ringwald }
2114*97dc5e69SMatthias Ringwald }
2115*97dc5e69SMatthias Ringwald }
2116*97dc5e69SMatthias Ringwald
2117*97dc5e69SMatthias Ringwald lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2118*97dc5e69SMatthias Ringwald
2119*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2120*97dc5e69SMatthias Ringwald if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
2121*97dc5e69SMatthias Ringwald /* don't leave the thread-local semaphore signalled */
2122*97dc5e69SMatthias Ringwald sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
2123*97dc5e69SMatthias Ringwald }
2124*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_SEM_PER_THREAD */
2125*97dc5e69SMatthias Ringwald sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
2126*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2127*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_FREE(select_cb);
2128*97dc5e69SMatthias Ringwald
2129*97dc5e69SMatthias Ringwald if (nready < 0) {
2130*97dc5e69SMatthias Ringwald /* This happens when a socket got closed while waiting */
2131*97dc5e69SMatthias Ringwald lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2132*97dc5e69SMatthias Ringwald return -1;
2133*97dc5e69SMatthias Ringwald }
2134*97dc5e69SMatthias Ringwald
2135*97dc5e69SMatthias Ringwald if (waitres == SYS_ARCH_TIMEOUT) {
2136*97dc5e69SMatthias Ringwald /* Timeout */
2137*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
2138*97dc5e69SMatthias Ringwald /* This is OK as the local fdsets are empty and nready is zero,
2139*97dc5e69SMatthias Ringwald or we would have returned earlier. */
2140*97dc5e69SMatthias Ringwald } else {
2141*97dc5e69SMatthias Ringwald /* See what's set now after waiting */
2142*97dc5e69SMatthias Ringwald nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
2143*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
2144*97dc5e69SMatthias Ringwald }
2145*97dc5e69SMatthias Ringwald }
2146*97dc5e69SMatthias Ringwald }
2147*97dc5e69SMatthias Ringwald
2148*97dc5e69SMatthias Ringwald lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
2149*97dc5e69SMatthias Ringwald set_errno(0);
2150*97dc5e69SMatthias Ringwald if (readset) {
2151*97dc5e69SMatthias Ringwald *readset = lreadset;
2152*97dc5e69SMatthias Ringwald }
2153*97dc5e69SMatthias Ringwald if (writeset) {
2154*97dc5e69SMatthias Ringwald *writeset = lwriteset;
2155*97dc5e69SMatthias Ringwald }
2156*97dc5e69SMatthias Ringwald if (exceptset) {
2157*97dc5e69SMatthias Ringwald *exceptset = lexceptset;
2158*97dc5e69SMatthias Ringwald }
2159*97dc5e69SMatthias Ringwald return nready;
2160*97dc5e69SMatthias Ringwald }
2161*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT */
2162*97dc5e69SMatthias Ringwald
2163*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_POLL
2164*97dc5e69SMatthias Ringwald /** Options for the lwip_pollscan function. */
2165*97dc5e69SMatthias Ringwald enum lwip_pollscan_opts
2166*97dc5e69SMatthias Ringwald {
2167*97dc5e69SMatthias Ringwald /** Clear revents in each struct pollfd. */
2168*97dc5e69SMatthias Ringwald LWIP_POLLSCAN_CLEAR = 1,
2169*97dc5e69SMatthias Ringwald
2170*97dc5e69SMatthias Ringwald /** Increment select_waiting in each struct lwip_sock. */
2171*97dc5e69SMatthias Ringwald LWIP_POLLSCAN_INC_WAIT = 2,
2172*97dc5e69SMatthias Ringwald
2173*97dc5e69SMatthias Ringwald /** Decrement select_waiting in each struct lwip_sock. */
2174*97dc5e69SMatthias Ringwald LWIP_POLLSCAN_DEC_WAIT = 4
2175*97dc5e69SMatthias Ringwald };
2176*97dc5e69SMatthias Ringwald
2177*97dc5e69SMatthias Ringwald /**
2178*97dc5e69SMatthias Ringwald * Update revents in each struct pollfd.
2179*97dc5e69SMatthias Ringwald * Optionally update select_waiting in struct lwip_sock.
2180*97dc5e69SMatthias Ringwald *
2181*97dc5e69SMatthias Ringwald * @param fds array of structures to update
2182*97dc5e69SMatthias Ringwald * @param nfds number of structures in fds
2183*97dc5e69SMatthias Ringwald * @param opts what to update and how
2184*97dc5e69SMatthias Ringwald * @return number of structures that have revents != 0
2185*97dc5e69SMatthias Ringwald */
2186*97dc5e69SMatthias Ringwald static int
lwip_pollscan(struct pollfd * fds,nfds_t nfds,enum lwip_pollscan_opts opts)2187*97dc5e69SMatthias Ringwald lwip_pollscan(struct pollfd *fds, nfds_t nfds, enum lwip_pollscan_opts opts)
2188*97dc5e69SMatthias Ringwald {
2189*97dc5e69SMatthias Ringwald int nready = 0;
2190*97dc5e69SMatthias Ringwald nfds_t fdi;
2191*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2192*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
2193*97dc5e69SMatthias Ringwald
2194*97dc5e69SMatthias Ringwald /* Go through each struct pollfd in the array. */
2195*97dc5e69SMatthias Ringwald for (fdi = 0; fdi < nfds; fdi++) {
2196*97dc5e69SMatthias Ringwald if ((opts & LWIP_POLLSCAN_CLEAR) != 0) {
2197*97dc5e69SMatthias Ringwald fds[fdi].revents = 0;
2198*97dc5e69SMatthias Ringwald }
2199*97dc5e69SMatthias Ringwald
2200*97dc5e69SMatthias Ringwald /* Negative fd means the caller wants us to ignore this struct.
2201*97dc5e69SMatthias Ringwald POLLNVAL means we already detected that the fd is invalid;
2202*97dc5e69SMatthias Ringwald if another thread has since opened a new socket with that fd,
2203*97dc5e69SMatthias Ringwald we must not use that socket. */
2204*97dc5e69SMatthias Ringwald if (fds[fdi].fd >= 0 && (fds[fdi].revents & POLLNVAL) == 0) {
2205*97dc5e69SMatthias Ringwald /* First get the socket's status (protected)... */
2206*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2207*97dc5e69SMatthias Ringwald sock = tryget_socket_unconn_locked(fds[fdi].fd);
2208*97dc5e69SMatthias Ringwald if (sock != NULL) {
2209*97dc5e69SMatthias Ringwald void* lastdata = sock->lastdata.pbuf;
2210*97dc5e69SMatthias Ringwald s16_t rcvevent = sock->rcvevent;
2211*97dc5e69SMatthias Ringwald u16_t sendevent = sock->sendevent;
2212*97dc5e69SMatthias Ringwald u16_t errevent = sock->errevent;
2213*97dc5e69SMatthias Ringwald
2214*97dc5e69SMatthias Ringwald if ((opts & LWIP_POLLSCAN_INC_WAIT) != 0) {
2215*97dc5e69SMatthias Ringwald sock->select_waiting++;
2216*97dc5e69SMatthias Ringwald if (sock->select_waiting == 0) {
2217*97dc5e69SMatthias Ringwald /* overflow - too many threads waiting */
2218*97dc5e69SMatthias Ringwald sock->select_waiting--;
2219*97dc5e69SMatthias Ringwald nready = -1;
2220*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2221*97dc5e69SMatthias Ringwald done_socket(sock);
2222*97dc5e69SMatthias Ringwald break;
2223*97dc5e69SMatthias Ringwald }
2224*97dc5e69SMatthias Ringwald } else if ((opts & LWIP_POLLSCAN_DEC_WAIT) != 0) {
2225*97dc5e69SMatthias Ringwald /* for now, handle select_waiting==0... */
2226*97dc5e69SMatthias Ringwald LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
2227*97dc5e69SMatthias Ringwald if (sock->select_waiting > 0) {
2228*97dc5e69SMatthias Ringwald sock->select_waiting--;
2229*97dc5e69SMatthias Ringwald }
2230*97dc5e69SMatthias Ringwald }
2231*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2232*97dc5e69SMatthias Ringwald done_socket(sock);
2233*97dc5e69SMatthias Ringwald
2234*97dc5e69SMatthias Ringwald /* ... then examine it: */
2235*97dc5e69SMatthias Ringwald /* See if netconn of this socket is ready for read */
2236*97dc5e69SMatthias Ringwald if ((fds[fdi].events & POLLIN) != 0 && ((lastdata != NULL) || (rcvevent > 0))) {
2237*97dc5e69SMatthias Ringwald fds[fdi].revents |= POLLIN;
2238*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for reading\n", fds[fdi].fd));
2239*97dc5e69SMatthias Ringwald }
2240*97dc5e69SMatthias Ringwald /* See if netconn of this socket is ready for write */
2241*97dc5e69SMatthias Ringwald if ((fds[fdi].events & POLLOUT) != 0 && (sendevent != 0)) {
2242*97dc5e69SMatthias Ringwald fds[fdi].revents |= POLLOUT;
2243*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for writing\n", fds[fdi].fd));
2244*97dc5e69SMatthias Ringwald }
2245*97dc5e69SMatthias Ringwald /* See if netconn of this socket had an error */
2246*97dc5e69SMatthias Ringwald if (errevent != 0) {
2247*97dc5e69SMatthias Ringwald /* POLLERR is output only. */
2248*97dc5e69SMatthias Ringwald fds[fdi].revents |= POLLERR;
2249*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_pollscan: fd=%d ready for exception\n", fds[fdi].fd));
2250*97dc5e69SMatthias Ringwald }
2251*97dc5e69SMatthias Ringwald } else {
2252*97dc5e69SMatthias Ringwald /* Not a valid socket */
2253*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2254*97dc5e69SMatthias Ringwald /* POLLNVAL is output only. */
2255*97dc5e69SMatthias Ringwald fds[fdi].revents |= POLLNVAL;
2256*97dc5e69SMatthias Ringwald return -1;
2257*97dc5e69SMatthias Ringwald }
2258*97dc5e69SMatthias Ringwald }
2259*97dc5e69SMatthias Ringwald
2260*97dc5e69SMatthias Ringwald /* Will return the number of structures that have events,
2261*97dc5e69SMatthias Ringwald not the number of events. */
2262*97dc5e69SMatthias Ringwald if (fds[fdi].revents != 0) {
2263*97dc5e69SMatthias Ringwald nready++;
2264*97dc5e69SMatthias Ringwald }
2265*97dc5e69SMatthias Ringwald }
2266*97dc5e69SMatthias Ringwald
2267*97dc5e69SMatthias Ringwald LWIP_ASSERT("nready >= 0", nready >= 0);
2268*97dc5e69SMatthias Ringwald return nready;
2269*97dc5e69SMatthias Ringwald }
2270*97dc5e69SMatthias Ringwald
2271*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_FULLDUPLEX
2272*97dc5e69SMatthias Ringwald /* Mark all sockets as used.
2273*97dc5e69SMatthias Ringwald *
2274*97dc5e69SMatthias Ringwald * All sockets are marked (and later unmarked), whether they are open or not.
2275*97dc5e69SMatthias Ringwald * This is OK as lwip_pollscan aborts select when non-open sockets are found.
2276*97dc5e69SMatthias Ringwald */
2277*97dc5e69SMatthias Ringwald static void
lwip_poll_inc_sockets_used(struct pollfd * fds,nfds_t nfds)2278*97dc5e69SMatthias Ringwald lwip_poll_inc_sockets_used(struct pollfd *fds, nfds_t nfds)
2279*97dc5e69SMatthias Ringwald {
2280*97dc5e69SMatthias Ringwald nfds_t fdi;
2281*97dc5e69SMatthias Ringwald
2282*97dc5e69SMatthias Ringwald if(fds) {
2283*97dc5e69SMatthias Ringwald /* Go through each struct pollfd in the array. */
2284*97dc5e69SMatthias Ringwald for (fdi = 0; fdi < nfds; fdi++) {
2285*97dc5e69SMatthias Ringwald /* Increase the reference counter */
2286*97dc5e69SMatthias Ringwald tryget_socket_unconn(fds[fdi].fd);
2287*97dc5e69SMatthias Ringwald }
2288*97dc5e69SMatthias Ringwald }
2289*97dc5e69SMatthias Ringwald }
2290*97dc5e69SMatthias Ringwald
2291*97dc5e69SMatthias Ringwald /* Let go all sockets that were marked as used when starting poll */
2292*97dc5e69SMatthias Ringwald static void
lwip_poll_dec_sockets_used(struct pollfd * fds,nfds_t nfds)2293*97dc5e69SMatthias Ringwald lwip_poll_dec_sockets_used(struct pollfd *fds, nfds_t nfds)
2294*97dc5e69SMatthias Ringwald {
2295*97dc5e69SMatthias Ringwald nfds_t fdi;
2296*97dc5e69SMatthias Ringwald
2297*97dc5e69SMatthias Ringwald if(fds) {
2298*97dc5e69SMatthias Ringwald /* Go through each struct pollfd in the array. */
2299*97dc5e69SMatthias Ringwald for (fdi = 0; fdi < nfds; fdi++) {
2300*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket_unconn_nouse(fds[fdi].fd);
2301*97dc5e69SMatthias Ringwald if (sock != NULL) {
2302*97dc5e69SMatthias Ringwald done_socket(sock);
2303*97dc5e69SMatthias Ringwald }
2304*97dc5e69SMatthias Ringwald }
2305*97dc5e69SMatthias Ringwald }
2306*97dc5e69SMatthias Ringwald }
2307*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_FULLDUPLEX */
2308*97dc5e69SMatthias Ringwald #define lwip_poll_inc_sockets_used(fds, nfds)
2309*97dc5e69SMatthias Ringwald #define lwip_poll_dec_sockets_used(fds, nfds)
2310*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_FULLDUPLEX */
2311*97dc5e69SMatthias Ringwald
2312*97dc5e69SMatthias Ringwald int
lwip_poll(struct pollfd * fds,nfds_t nfds,int timeout)2313*97dc5e69SMatthias Ringwald lwip_poll(struct pollfd *fds, nfds_t nfds, int timeout)
2314*97dc5e69SMatthias Ringwald {
2315*97dc5e69SMatthias Ringwald u32_t waitres = 0;
2316*97dc5e69SMatthias Ringwald int nready;
2317*97dc5e69SMatthias Ringwald u32_t msectimeout;
2318*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2319*97dc5e69SMatthias Ringwald int waited = 0;
2320*97dc5e69SMatthias Ringwald #endif
2321*97dc5e69SMatthias Ringwald
2322*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll(%p, %d, %d)\n",
2323*97dc5e69SMatthias Ringwald (void*)fds, (int)nfds, timeout));
2324*97dc5e69SMatthias Ringwald LWIP_ERROR("lwip_poll: invalid fds", ((fds != NULL && nfds > 0) || (fds == NULL && nfds == 0)),
2325*97dc5e69SMatthias Ringwald set_errno(EINVAL); return -1;);
2326*97dc5e69SMatthias Ringwald
2327*97dc5e69SMatthias Ringwald lwip_poll_inc_sockets_used(fds, nfds);
2328*97dc5e69SMatthias Ringwald
2329*97dc5e69SMatthias Ringwald /* Go through each struct pollfd to count number of structures
2330*97dc5e69SMatthias Ringwald which currently match */
2331*97dc5e69SMatthias Ringwald nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_CLEAR);
2332*97dc5e69SMatthias Ringwald
2333*97dc5e69SMatthias Ringwald if (nready < 0) {
2334*97dc5e69SMatthias Ringwald lwip_poll_dec_sockets_used(fds, nfds);
2335*97dc5e69SMatthias Ringwald return -1;
2336*97dc5e69SMatthias Ringwald }
2337*97dc5e69SMatthias Ringwald
2338*97dc5e69SMatthias Ringwald /* If we don't have any current events, then suspend if we are supposed to */
2339*97dc5e69SMatthias Ringwald if (!nready) {
2340*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_DECLARE(select_cb);
2341*97dc5e69SMatthias Ringwald
2342*97dc5e69SMatthias Ringwald if (timeout == 0) {
2343*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: no timeout, returning 0\n"));
2344*97dc5e69SMatthias Ringwald goto return_success;
2345*97dc5e69SMatthias Ringwald }
2346*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(EAGAIN); lwip_poll_dec_sockets_used(fds, nfds); return -1);
2347*97dc5e69SMatthias Ringwald memset(&API_SELECT_CB_VAR_REF(select_cb), 0, sizeof(struct lwip_select_cb));
2348*97dc5e69SMatthias Ringwald
2349*97dc5e69SMatthias Ringwald /* None ready: add our semaphore to list:
2350*97dc5e69SMatthias Ringwald We don't actually need any dynamic memory. Our entry on the
2351*97dc5e69SMatthias Ringwald list is only valid while we are in this function, so it's ok
2352*97dc5e69SMatthias Ringwald to use local variables. */
2353*97dc5e69SMatthias Ringwald
2354*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).poll_fds = fds;
2355*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).poll_nfds = nfds;
2356*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2357*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
2358*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_SEM_PER_THREAD */
2359*97dc5e69SMatthias Ringwald if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
2360*97dc5e69SMatthias Ringwald /* failed to create semaphore */
2361*97dc5e69SMatthias Ringwald set_errno(EAGAIN);
2362*97dc5e69SMatthias Ringwald lwip_poll_dec_sockets_used(fds, nfds);
2363*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_FREE(select_cb);
2364*97dc5e69SMatthias Ringwald return -1;
2365*97dc5e69SMatthias Ringwald }
2366*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2367*97dc5e69SMatthias Ringwald
2368*97dc5e69SMatthias Ringwald lwip_link_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2369*97dc5e69SMatthias Ringwald
2370*97dc5e69SMatthias Ringwald /* Increase select_waiting for each socket we are interested in.
2371*97dc5e69SMatthias Ringwald Also, check for events again: there could have been events between
2372*97dc5e69SMatthias Ringwald the last scan (without us on the list) and putting us on the list! */
2373*97dc5e69SMatthias Ringwald nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_INC_WAIT);
2374*97dc5e69SMatthias Ringwald
2375*97dc5e69SMatthias Ringwald if (!nready) {
2376*97dc5e69SMatthias Ringwald /* Still none ready, just wait to be woken */
2377*97dc5e69SMatthias Ringwald if (timeout < 0) {
2378*97dc5e69SMatthias Ringwald /* Wait forever */
2379*97dc5e69SMatthias Ringwald msectimeout = 0;
2380*97dc5e69SMatthias Ringwald } else {
2381*97dc5e69SMatthias Ringwald /* timeout == 0 would have been handled earlier. */
2382*97dc5e69SMatthias Ringwald LWIP_ASSERT("timeout > 0", timeout > 0);
2383*97dc5e69SMatthias Ringwald msectimeout = timeout;
2384*97dc5e69SMatthias Ringwald }
2385*97dc5e69SMatthias Ringwald waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
2386*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2387*97dc5e69SMatthias Ringwald waited = 1;
2388*97dc5e69SMatthias Ringwald #endif
2389*97dc5e69SMatthias Ringwald }
2390*97dc5e69SMatthias Ringwald
2391*97dc5e69SMatthias Ringwald /* Decrease select_waiting for each socket we are interested in,
2392*97dc5e69SMatthias Ringwald and check which events occurred while we waited. */
2393*97dc5e69SMatthias Ringwald nready = lwip_pollscan(fds, nfds, LWIP_POLLSCAN_DEC_WAIT);
2394*97dc5e69SMatthias Ringwald
2395*97dc5e69SMatthias Ringwald lwip_unlink_select_cb(&API_SELECT_CB_VAR_REF(select_cb));
2396*97dc5e69SMatthias Ringwald
2397*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2398*97dc5e69SMatthias Ringwald if (select_cb.sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
2399*97dc5e69SMatthias Ringwald /* don't leave the thread-local semaphore signalled */
2400*97dc5e69SMatthias Ringwald sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
2401*97dc5e69SMatthias Ringwald }
2402*97dc5e69SMatthias Ringwald #else /* LWIP_NETCONN_SEM_PER_THREAD */
2403*97dc5e69SMatthias Ringwald sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
2404*97dc5e69SMatthias Ringwald #endif /* LWIP_NETCONN_SEM_PER_THREAD */
2405*97dc5e69SMatthias Ringwald API_SELECT_CB_VAR_FREE(select_cb);
2406*97dc5e69SMatthias Ringwald
2407*97dc5e69SMatthias Ringwald if (nready < 0) {
2408*97dc5e69SMatthias Ringwald /* This happens when a socket got closed while waiting */
2409*97dc5e69SMatthias Ringwald lwip_poll_dec_sockets_used(fds, nfds);
2410*97dc5e69SMatthias Ringwald return -1;
2411*97dc5e69SMatthias Ringwald }
2412*97dc5e69SMatthias Ringwald
2413*97dc5e69SMatthias Ringwald if (waitres == SYS_ARCH_TIMEOUT) {
2414*97dc5e69SMatthias Ringwald /* Timeout */
2415*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: timeout expired\n"));
2416*97dc5e69SMatthias Ringwald goto return_success;
2417*97dc5e69SMatthias Ringwald }
2418*97dc5e69SMatthias Ringwald }
2419*97dc5e69SMatthias Ringwald
2420*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_poll: nready=%d\n", nready));
2421*97dc5e69SMatthias Ringwald return_success:
2422*97dc5e69SMatthias Ringwald lwip_poll_dec_sockets_used(fds, nfds);
2423*97dc5e69SMatthias Ringwald set_errno(0);
2424*97dc5e69SMatthias Ringwald return nready;
2425*97dc5e69SMatthias Ringwald }
2426*97dc5e69SMatthias Ringwald
2427*97dc5e69SMatthias Ringwald /**
2428*97dc5e69SMatthias Ringwald * Check whether event_callback should wake up a thread waiting in
2429*97dc5e69SMatthias Ringwald * lwip_poll.
2430*97dc5e69SMatthias Ringwald */
2431*97dc5e69SMatthias Ringwald static int
lwip_poll_should_wake(const struct lwip_select_cb * scb,int fd,int has_recvevent,int has_sendevent,int has_errevent)2432*97dc5e69SMatthias Ringwald lwip_poll_should_wake(const struct lwip_select_cb *scb, int fd, int has_recvevent, int has_sendevent, int has_errevent)
2433*97dc5e69SMatthias Ringwald {
2434*97dc5e69SMatthias Ringwald nfds_t fdi;
2435*97dc5e69SMatthias Ringwald for (fdi = 0; fdi < scb->poll_nfds; fdi++) {
2436*97dc5e69SMatthias Ringwald const struct pollfd *pollfd = &scb->poll_fds[fdi];
2437*97dc5e69SMatthias Ringwald if (pollfd->fd == fd) {
2438*97dc5e69SMatthias Ringwald /* Do not update pollfd->revents right here;
2439*97dc5e69SMatthias Ringwald that would be a data race because lwip_pollscan
2440*97dc5e69SMatthias Ringwald accesses revents without protecting. */
2441*97dc5e69SMatthias Ringwald if (has_recvevent && (pollfd->events & POLLIN) != 0) {
2442*97dc5e69SMatthias Ringwald return 1;
2443*97dc5e69SMatthias Ringwald }
2444*97dc5e69SMatthias Ringwald if (has_sendevent && (pollfd->events & POLLOUT) != 0) {
2445*97dc5e69SMatthias Ringwald return 1;
2446*97dc5e69SMatthias Ringwald }
2447*97dc5e69SMatthias Ringwald if (has_errevent) {
2448*97dc5e69SMatthias Ringwald /* POLLERR is output only. */
2449*97dc5e69SMatthias Ringwald return 1;
2450*97dc5e69SMatthias Ringwald }
2451*97dc5e69SMatthias Ringwald }
2452*97dc5e69SMatthias Ringwald }
2453*97dc5e69SMatthias Ringwald return 0;
2454*97dc5e69SMatthias Ringwald }
2455*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_POLL */
2456*97dc5e69SMatthias Ringwald
2457*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL
2458*97dc5e69SMatthias Ringwald /**
2459*97dc5e69SMatthias Ringwald * Callback registered in the netconn layer for each socket-netconn.
2460*97dc5e69SMatthias Ringwald * Processes recvevent (data available) and wakes up tasks waiting for select.
2461*97dc5e69SMatthias Ringwald *
2462*97dc5e69SMatthias Ringwald * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
2463*97dc5e69SMatthias Ringwald * must have the core lock held when signaling the following events
2464*97dc5e69SMatthias Ringwald * as they might cause select_list_cb to be checked:
2465*97dc5e69SMatthias Ringwald * NETCONN_EVT_RCVPLUS
2466*97dc5e69SMatthias Ringwald * NETCONN_EVT_SENDPLUS
2467*97dc5e69SMatthias Ringwald * NETCONN_EVT_ERROR
2468*97dc5e69SMatthias Ringwald * This requirement will be asserted in select_check_waiters()
2469*97dc5e69SMatthias Ringwald */
2470*97dc5e69SMatthias Ringwald static void
event_callback(struct netconn * conn,enum netconn_evt evt,u16_t len)2471*97dc5e69SMatthias Ringwald event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
2472*97dc5e69SMatthias Ringwald {
2473*97dc5e69SMatthias Ringwald int s, check_waiters;
2474*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2475*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
2476*97dc5e69SMatthias Ringwald
2477*97dc5e69SMatthias Ringwald LWIP_UNUSED_ARG(len);
2478*97dc5e69SMatthias Ringwald
2479*97dc5e69SMatthias Ringwald /* Get socket */
2480*97dc5e69SMatthias Ringwald if (conn) {
2481*97dc5e69SMatthias Ringwald s = conn->socket;
2482*97dc5e69SMatthias Ringwald if (s < 0) {
2483*97dc5e69SMatthias Ringwald /* Data comes in right away after an accept, even though
2484*97dc5e69SMatthias Ringwald * the server task might not have created a new socket yet.
2485*97dc5e69SMatthias Ringwald * Just count down (or up) if that's the case and we
2486*97dc5e69SMatthias Ringwald * will use the data later. Note that only receive events
2487*97dc5e69SMatthias Ringwald * can happen before the new socket is set up. */
2488*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2489*97dc5e69SMatthias Ringwald if (conn->socket < 0) {
2490*97dc5e69SMatthias Ringwald if (evt == NETCONN_EVT_RCVPLUS) {
2491*97dc5e69SMatthias Ringwald /* conn->socket is -1 on initialization
2492*97dc5e69SMatthias Ringwald lwip_accept adjusts sock->recvevent if conn->socket < -1 */
2493*97dc5e69SMatthias Ringwald conn->socket--;
2494*97dc5e69SMatthias Ringwald }
2495*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2496*97dc5e69SMatthias Ringwald return;
2497*97dc5e69SMatthias Ringwald }
2498*97dc5e69SMatthias Ringwald s = conn->socket;
2499*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2500*97dc5e69SMatthias Ringwald }
2501*97dc5e69SMatthias Ringwald
2502*97dc5e69SMatthias Ringwald sock = get_socket(s);
2503*97dc5e69SMatthias Ringwald if (!sock) {
2504*97dc5e69SMatthias Ringwald return;
2505*97dc5e69SMatthias Ringwald }
2506*97dc5e69SMatthias Ringwald } else {
2507*97dc5e69SMatthias Ringwald return;
2508*97dc5e69SMatthias Ringwald }
2509*97dc5e69SMatthias Ringwald
2510*97dc5e69SMatthias Ringwald check_waiters = 1;
2511*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2512*97dc5e69SMatthias Ringwald /* Set event as required */
2513*97dc5e69SMatthias Ringwald switch (evt) {
2514*97dc5e69SMatthias Ringwald case NETCONN_EVT_RCVPLUS:
2515*97dc5e69SMatthias Ringwald sock->rcvevent++;
2516*97dc5e69SMatthias Ringwald if (sock->rcvevent > 1) {
2517*97dc5e69SMatthias Ringwald check_waiters = 0;
2518*97dc5e69SMatthias Ringwald }
2519*97dc5e69SMatthias Ringwald break;
2520*97dc5e69SMatthias Ringwald case NETCONN_EVT_RCVMINUS:
2521*97dc5e69SMatthias Ringwald sock->rcvevent--;
2522*97dc5e69SMatthias Ringwald check_waiters = 0;
2523*97dc5e69SMatthias Ringwald break;
2524*97dc5e69SMatthias Ringwald case NETCONN_EVT_SENDPLUS:
2525*97dc5e69SMatthias Ringwald if (sock->sendevent) {
2526*97dc5e69SMatthias Ringwald check_waiters = 0;
2527*97dc5e69SMatthias Ringwald }
2528*97dc5e69SMatthias Ringwald sock->sendevent = 1;
2529*97dc5e69SMatthias Ringwald break;
2530*97dc5e69SMatthias Ringwald case NETCONN_EVT_SENDMINUS:
2531*97dc5e69SMatthias Ringwald sock->sendevent = 0;
2532*97dc5e69SMatthias Ringwald check_waiters = 0;
2533*97dc5e69SMatthias Ringwald break;
2534*97dc5e69SMatthias Ringwald case NETCONN_EVT_ERROR:
2535*97dc5e69SMatthias Ringwald sock->errevent = 1;
2536*97dc5e69SMatthias Ringwald break;
2537*97dc5e69SMatthias Ringwald default:
2538*97dc5e69SMatthias Ringwald LWIP_ASSERT("unknown event", 0);
2539*97dc5e69SMatthias Ringwald break;
2540*97dc5e69SMatthias Ringwald }
2541*97dc5e69SMatthias Ringwald
2542*97dc5e69SMatthias Ringwald if (sock->select_waiting && check_waiters) {
2543*97dc5e69SMatthias Ringwald /* Save which events are active */
2544*97dc5e69SMatthias Ringwald int has_recvevent, has_sendevent, has_errevent;
2545*97dc5e69SMatthias Ringwald has_recvevent = sock->rcvevent > 0;
2546*97dc5e69SMatthias Ringwald has_sendevent = sock->sendevent != 0;
2547*97dc5e69SMatthias Ringwald has_errevent = sock->errevent != 0;
2548*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2549*97dc5e69SMatthias Ringwald /* Check any select calls waiting on this socket */
2550*97dc5e69SMatthias Ringwald select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
2551*97dc5e69SMatthias Ringwald } else {
2552*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2553*97dc5e69SMatthias Ringwald }
2554*97dc5e69SMatthias Ringwald done_socket(sock);
2555*97dc5e69SMatthias Ringwald }
2556*97dc5e69SMatthias Ringwald
2557*97dc5e69SMatthias Ringwald /**
2558*97dc5e69SMatthias Ringwald * Check if any select waiters are waiting on this socket and its events
2559*97dc5e69SMatthias Ringwald *
2560*97dc5e69SMatthias Ringwald * @note on synchronization of select_cb_list:
2561*97dc5e69SMatthias Ringwald * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
2562*97dc5e69SMatthias Ringwald * the core lock. We do a single pass through the list and signal any waiters.
2563*97dc5e69SMatthias Ringwald * Core lock should already be held when calling here!!!!
2564*97dc5e69SMatthias Ringwald
2565*97dc5e69SMatthias Ringwald * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
2566*97dc5e69SMatthias Ringwald * of the loop, thus creating a possibility where a thread could modify the
2567*97dc5e69SMatthias Ringwald * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
2568*97dc5e69SMatthias Ringwald * detect this change and restart the list walk. The list is expected to be small
2569*97dc5e69SMatthias Ringwald */
select_check_waiters(int s,int has_recvevent,int has_sendevent,int has_errevent)2570*97dc5e69SMatthias Ringwald static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
2571*97dc5e69SMatthias Ringwald {
2572*97dc5e69SMatthias Ringwald struct lwip_select_cb *scb;
2573*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
2574*97dc5e69SMatthias Ringwald int last_select_cb_ctr;
2575*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
2576*97dc5e69SMatthias Ringwald #endif /* !LWIP_TCPIP_CORE_LOCKING */
2577*97dc5e69SMatthias Ringwald
2578*97dc5e69SMatthias Ringwald LWIP_ASSERT_CORE_LOCKED();
2579*97dc5e69SMatthias Ringwald
2580*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
2581*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2582*97dc5e69SMatthias Ringwald again:
2583*97dc5e69SMatthias Ringwald /* remember the state of select_cb_list to detect changes */
2584*97dc5e69SMatthias Ringwald last_select_cb_ctr = select_cb_ctr;
2585*97dc5e69SMatthias Ringwald #endif /* !LWIP_TCPIP_CORE_LOCKING */
2586*97dc5e69SMatthias Ringwald for (scb = select_cb_list; scb != NULL; scb = scb->next) {
2587*97dc5e69SMatthias Ringwald if (scb->sem_signalled == 0) {
2588*97dc5e69SMatthias Ringwald /* semaphore not signalled yet */
2589*97dc5e69SMatthias Ringwald int do_signal = 0;
2590*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_POLL
2591*97dc5e69SMatthias Ringwald if (scb->poll_fds != NULL) {
2592*97dc5e69SMatthias Ringwald do_signal = lwip_poll_should_wake(scb, s, has_recvevent, has_sendevent, has_errevent);
2593*97dc5e69SMatthias Ringwald }
2594*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_POLL */
2595*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL
2596*97dc5e69SMatthias Ringwald else
2597*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT && LWIP_SOCKET_POLL */
2598*97dc5e69SMatthias Ringwald #if LWIP_SOCKET_SELECT
2599*97dc5e69SMatthias Ringwald {
2600*97dc5e69SMatthias Ringwald /* Test this select call for our socket */
2601*97dc5e69SMatthias Ringwald if (has_recvevent) {
2602*97dc5e69SMatthias Ringwald if (scb->readset && FD_ISSET(s, scb->readset)) {
2603*97dc5e69SMatthias Ringwald do_signal = 1;
2604*97dc5e69SMatthias Ringwald }
2605*97dc5e69SMatthias Ringwald }
2606*97dc5e69SMatthias Ringwald if (has_sendevent) {
2607*97dc5e69SMatthias Ringwald if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
2608*97dc5e69SMatthias Ringwald do_signal = 1;
2609*97dc5e69SMatthias Ringwald }
2610*97dc5e69SMatthias Ringwald }
2611*97dc5e69SMatthias Ringwald if (has_errevent) {
2612*97dc5e69SMatthias Ringwald if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
2613*97dc5e69SMatthias Ringwald do_signal = 1;
2614*97dc5e69SMatthias Ringwald }
2615*97dc5e69SMatthias Ringwald }
2616*97dc5e69SMatthias Ringwald }
2617*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT */
2618*97dc5e69SMatthias Ringwald if (do_signal) {
2619*97dc5e69SMatthias Ringwald scb->sem_signalled = 1;
2620*97dc5e69SMatthias Ringwald /* For !LWIP_TCPIP_CORE_LOCKING, we don't call SYS_ARCH_UNPROTECT() before signaling
2621*97dc5e69SMatthias Ringwald the semaphore, as this might lead to the select thread taking itself off the list,
2622*97dc5e69SMatthias Ringwald invalidating the semaphore. */
2623*97dc5e69SMatthias Ringwald sys_sem_signal(SELECT_SEM_PTR(scb->sem));
2624*97dc5e69SMatthias Ringwald }
2625*97dc5e69SMatthias Ringwald }
2626*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
2627*97dc5e69SMatthias Ringwald }
2628*97dc5e69SMatthias Ringwald #else
2629*97dc5e69SMatthias Ringwald /* unlock interrupts with each step */
2630*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2631*97dc5e69SMatthias Ringwald /* this makes sure interrupt protection time is short */
2632*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
2633*97dc5e69SMatthias Ringwald if (last_select_cb_ctr != select_cb_ctr) {
2634*97dc5e69SMatthias Ringwald /* someone has changed select_cb_list, restart at the beginning */
2635*97dc5e69SMatthias Ringwald goto again;
2636*97dc5e69SMatthias Ringwald }
2637*97dc5e69SMatthias Ringwald /* remember the state of select_cb_list to detect changes */
2638*97dc5e69SMatthias Ringwald last_select_cb_ctr = select_cb_ctr;
2639*97dc5e69SMatthias Ringwald }
2640*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
2641*97dc5e69SMatthias Ringwald #endif
2642*97dc5e69SMatthias Ringwald }
2643*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET_SELECT || LWIP_SOCKET_POLL */
2644*97dc5e69SMatthias Ringwald
2645*97dc5e69SMatthias Ringwald /**
2646*97dc5e69SMatthias Ringwald * Close one end of a full-duplex connection.
2647*97dc5e69SMatthias Ringwald */
2648*97dc5e69SMatthias Ringwald int
lwip_shutdown(int s,int how)2649*97dc5e69SMatthias Ringwald lwip_shutdown(int s, int how)
2650*97dc5e69SMatthias Ringwald {
2651*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2652*97dc5e69SMatthias Ringwald err_t err;
2653*97dc5e69SMatthias Ringwald u8_t shut_rx = 0, shut_tx = 0;
2654*97dc5e69SMatthias Ringwald
2655*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
2656*97dc5e69SMatthias Ringwald
2657*97dc5e69SMatthias Ringwald sock = get_socket(s);
2658*97dc5e69SMatthias Ringwald if (!sock) {
2659*97dc5e69SMatthias Ringwald return -1;
2660*97dc5e69SMatthias Ringwald }
2661*97dc5e69SMatthias Ringwald
2662*97dc5e69SMatthias Ringwald if (sock->conn != NULL) {
2663*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
2664*97dc5e69SMatthias Ringwald set_errno(EOPNOTSUPP);
2665*97dc5e69SMatthias Ringwald done_socket(sock);
2666*97dc5e69SMatthias Ringwald return -1;
2667*97dc5e69SMatthias Ringwald }
2668*97dc5e69SMatthias Ringwald } else {
2669*97dc5e69SMatthias Ringwald set_errno(ENOTCONN);
2670*97dc5e69SMatthias Ringwald done_socket(sock);
2671*97dc5e69SMatthias Ringwald return -1;
2672*97dc5e69SMatthias Ringwald }
2673*97dc5e69SMatthias Ringwald
2674*97dc5e69SMatthias Ringwald if (how == SHUT_RD) {
2675*97dc5e69SMatthias Ringwald shut_rx = 1;
2676*97dc5e69SMatthias Ringwald } else if (how == SHUT_WR) {
2677*97dc5e69SMatthias Ringwald shut_tx = 1;
2678*97dc5e69SMatthias Ringwald } else if (how == SHUT_RDWR) {
2679*97dc5e69SMatthias Ringwald shut_rx = 1;
2680*97dc5e69SMatthias Ringwald shut_tx = 1;
2681*97dc5e69SMatthias Ringwald } else {
2682*97dc5e69SMatthias Ringwald set_errno(EINVAL);
2683*97dc5e69SMatthias Ringwald done_socket(sock);
2684*97dc5e69SMatthias Ringwald return -1;
2685*97dc5e69SMatthias Ringwald }
2686*97dc5e69SMatthias Ringwald err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
2687*97dc5e69SMatthias Ringwald
2688*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
2689*97dc5e69SMatthias Ringwald done_socket(sock);
2690*97dc5e69SMatthias Ringwald return (err == ERR_OK ? 0 : -1);
2691*97dc5e69SMatthias Ringwald }
2692*97dc5e69SMatthias Ringwald
2693*97dc5e69SMatthias Ringwald static int
lwip_getaddrname(int s,struct sockaddr * name,socklen_t * namelen,u8_t local)2694*97dc5e69SMatthias Ringwald lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
2695*97dc5e69SMatthias Ringwald {
2696*97dc5e69SMatthias Ringwald struct lwip_sock *sock;
2697*97dc5e69SMatthias Ringwald union sockaddr_aligned saddr;
2698*97dc5e69SMatthias Ringwald ip_addr_t naddr;
2699*97dc5e69SMatthias Ringwald u16_t port;
2700*97dc5e69SMatthias Ringwald err_t err;
2701*97dc5e69SMatthias Ringwald
2702*97dc5e69SMatthias Ringwald sock = get_socket(s);
2703*97dc5e69SMatthias Ringwald if (!sock) {
2704*97dc5e69SMatthias Ringwald return -1;
2705*97dc5e69SMatthias Ringwald }
2706*97dc5e69SMatthias Ringwald
2707*97dc5e69SMatthias Ringwald /* get the IP address and port */
2708*97dc5e69SMatthias Ringwald err = netconn_getaddr(sock->conn, &naddr, &port, local);
2709*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
2710*97dc5e69SMatthias Ringwald set_errno(err_to_errno(err));
2711*97dc5e69SMatthias Ringwald done_socket(sock);
2712*97dc5e69SMatthias Ringwald return -1;
2713*97dc5e69SMatthias Ringwald }
2714*97dc5e69SMatthias Ringwald
2715*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_IPV6
2716*97dc5e69SMatthias Ringwald /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
2717*97dc5e69SMatthias Ringwald if (NETCONNTYPE_ISIPV6(netconn_type(sock->conn)) &&
2718*97dc5e69SMatthias Ringwald IP_IS_V4_VAL(naddr)) {
2719*97dc5e69SMatthias Ringwald ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&naddr), ip_2_ip4(&naddr));
2720*97dc5e69SMatthias Ringwald IP_SET_TYPE_VAL(naddr, IPADDR_TYPE_V6);
2721*97dc5e69SMatthias Ringwald }
2722*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_IPV6 */
2723*97dc5e69SMatthias Ringwald
2724*97dc5e69SMatthias Ringwald IPADDR_PORT_TO_SOCKADDR(&saddr, &naddr, port);
2725*97dc5e69SMatthias Ringwald
2726*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
2727*97dc5e69SMatthias Ringwald ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
2728*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
2729*97dc5e69SMatthias Ringwald
2730*97dc5e69SMatthias Ringwald if (*namelen > saddr.sa.sa_len) {
2731*97dc5e69SMatthias Ringwald *namelen = saddr.sa.sa_len;
2732*97dc5e69SMatthias Ringwald }
2733*97dc5e69SMatthias Ringwald MEMCPY(name, &saddr, *namelen);
2734*97dc5e69SMatthias Ringwald
2735*97dc5e69SMatthias Ringwald set_errno(0);
2736*97dc5e69SMatthias Ringwald done_socket(sock);
2737*97dc5e69SMatthias Ringwald return 0;
2738*97dc5e69SMatthias Ringwald }
2739*97dc5e69SMatthias Ringwald
2740*97dc5e69SMatthias Ringwald int
lwip_getpeername(int s,struct sockaddr * name,socklen_t * namelen)2741*97dc5e69SMatthias Ringwald lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
2742*97dc5e69SMatthias Ringwald {
2743*97dc5e69SMatthias Ringwald return lwip_getaddrname(s, name, namelen, 0);
2744*97dc5e69SMatthias Ringwald }
2745*97dc5e69SMatthias Ringwald
2746*97dc5e69SMatthias Ringwald int
lwip_getsockname(int s,struct sockaddr * name,socklen_t * namelen)2747*97dc5e69SMatthias Ringwald lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
2748*97dc5e69SMatthias Ringwald {
2749*97dc5e69SMatthias Ringwald return lwip_getaddrname(s, name, namelen, 1);
2750*97dc5e69SMatthias Ringwald }
2751*97dc5e69SMatthias Ringwald
2752*97dc5e69SMatthias Ringwald int
lwip_getsockopt(int s,int level,int optname,void * optval,socklen_t * optlen)2753*97dc5e69SMatthias Ringwald lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
2754*97dc5e69SMatthias Ringwald {
2755*97dc5e69SMatthias Ringwald int err;
2756*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
2757*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
2758*97dc5e69SMatthias Ringwald err_t cberr;
2759*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
2760*97dc5e69SMatthias Ringwald #endif /* !LWIP_TCPIP_CORE_LOCKING */
2761*97dc5e69SMatthias Ringwald
2762*97dc5e69SMatthias Ringwald if (!sock) {
2763*97dc5e69SMatthias Ringwald return -1;
2764*97dc5e69SMatthias Ringwald }
2765*97dc5e69SMatthias Ringwald
2766*97dc5e69SMatthias Ringwald if ((NULL == optval) || (NULL == optlen)) {
2767*97dc5e69SMatthias Ringwald set_errno(EFAULT);
2768*97dc5e69SMatthias Ringwald done_socket(sock);
2769*97dc5e69SMatthias Ringwald return -1;
2770*97dc5e69SMatthias Ringwald }
2771*97dc5e69SMatthias Ringwald
2772*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
2773*97dc5e69SMatthias Ringwald /* core-locking can just call the -impl function */
2774*97dc5e69SMatthias Ringwald LOCK_TCPIP_CORE();
2775*97dc5e69SMatthias Ringwald err = lwip_getsockopt_impl(s, level, optname, optval, optlen);
2776*97dc5e69SMatthias Ringwald UNLOCK_TCPIP_CORE();
2777*97dc5e69SMatthias Ringwald
2778*97dc5e69SMatthias Ringwald #else /* LWIP_TCPIP_CORE_LOCKING */
2779*97dc5e69SMatthias Ringwald
2780*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
2781*97dc5e69SMatthias Ringwald /* MPU_COMPATIBLE copies the optval data, so check for max size here */
2782*97dc5e69SMatthias Ringwald if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
2783*97dc5e69SMatthias Ringwald set_errno(ENOBUFS);
2784*97dc5e69SMatthias Ringwald done_socket(sock);
2785*97dc5e69SMatthias Ringwald return -1;
2786*97dc5e69SMatthias Ringwald }
2787*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
2788*97dc5e69SMatthias Ringwald
2789*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
2790*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
2791*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
2792*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
2793*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen;
2794*97dc5e69SMatthias Ringwald #if !LWIP_MPU_COMPATIBLE
2795*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.p = optval;
2796*97dc5e69SMatthias Ringwald #endif /* !LWIP_MPU_COMPATIBLE */
2797*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
2798*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
2799*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
2800*97dc5e69SMatthias Ringwald #else
2801*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
2802*97dc5e69SMatthias Ringwald #endif
2803*97dc5e69SMatthias Ringwald cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
2804*97dc5e69SMatthias Ringwald if (cberr != ERR_OK) {
2805*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2806*97dc5e69SMatthias Ringwald set_errno(err_to_errno(cberr));
2807*97dc5e69SMatthias Ringwald done_socket(sock);
2808*97dc5e69SMatthias Ringwald return -1;
2809*97dc5e69SMatthias Ringwald }
2810*97dc5e69SMatthias Ringwald sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
2811*97dc5e69SMatthias Ringwald
2812*97dc5e69SMatthias Ringwald /* write back optlen and optval */
2813*97dc5e69SMatthias Ringwald *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen;
2814*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
2815*97dc5e69SMatthias Ringwald MEMCPY(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval,
2816*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen);
2817*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
2818*97dc5e69SMatthias Ringwald
2819*97dc5e69SMatthias Ringwald /* maybe lwip_getsockopt_impl has changed err */
2820*97dc5e69SMatthias Ringwald err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
2821*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
2822*97dc5e69SMatthias Ringwald #endif /* LWIP_TCPIP_CORE_LOCKING */
2823*97dc5e69SMatthias Ringwald
2824*97dc5e69SMatthias Ringwald set_errno(err);
2825*97dc5e69SMatthias Ringwald done_socket(sock);
2826*97dc5e69SMatthias Ringwald return err ? -1 : 0;
2827*97dc5e69SMatthias Ringwald }
2828*97dc5e69SMatthias Ringwald
2829*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
2830*97dc5e69SMatthias Ringwald /** lwip_getsockopt_callback: only used without CORE_LOCKING
2831*97dc5e69SMatthias Ringwald * to get into the tcpip_thread
2832*97dc5e69SMatthias Ringwald */
2833*97dc5e69SMatthias Ringwald static void
lwip_getsockopt_callback(void * arg)2834*97dc5e69SMatthias Ringwald lwip_getsockopt_callback(void *arg)
2835*97dc5e69SMatthias Ringwald {
2836*97dc5e69SMatthias Ringwald struct lwip_setgetsockopt_data *data;
2837*97dc5e69SMatthias Ringwald LWIP_ASSERT("arg != NULL", arg != NULL);
2838*97dc5e69SMatthias Ringwald data = (struct lwip_setgetsockopt_data *)arg;
2839*97dc5e69SMatthias Ringwald
2840*97dc5e69SMatthias Ringwald data->err = lwip_getsockopt_impl(data->s, data->level, data->optname,
2841*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
2842*97dc5e69SMatthias Ringwald data->optval,
2843*97dc5e69SMatthias Ringwald #else /* LWIP_MPU_COMPATIBLE */
2844*97dc5e69SMatthias Ringwald data->optval.p,
2845*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
2846*97dc5e69SMatthias Ringwald &data->optlen);
2847*97dc5e69SMatthias Ringwald
2848*97dc5e69SMatthias Ringwald sys_sem_signal((sys_sem_t *)(data->completed_sem));
2849*97dc5e69SMatthias Ringwald }
2850*97dc5e69SMatthias Ringwald #endif /* LWIP_TCPIP_CORE_LOCKING */
2851*97dc5e69SMatthias Ringwald
2852*97dc5e69SMatthias Ringwald static int
lwip_sockopt_to_ipopt(int optname)2853*97dc5e69SMatthias Ringwald lwip_sockopt_to_ipopt(int optname)
2854*97dc5e69SMatthias Ringwald {
2855*97dc5e69SMatthias Ringwald /* Map SO_* values to our internal SOF_* values
2856*97dc5e69SMatthias Ringwald * We should not rely on #defines in socket.h
2857*97dc5e69SMatthias Ringwald * being in sync with ip.h.
2858*97dc5e69SMatthias Ringwald */
2859*97dc5e69SMatthias Ringwald switch (optname) {
2860*97dc5e69SMatthias Ringwald case SO_BROADCAST:
2861*97dc5e69SMatthias Ringwald return SOF_BROADCAST;
2862*97dc5e69SMatthias Ringwald case SO_KEEPALIVE:
2863*97dc5e69SMatthias Ringwald return SOF_KEEPALIVE;
2864*97dc5e69SMatthias Ringwald case SO_REUSEADDR:
2865*97dc5e69SMatthias Ringwald return SOF_REUSEADDR;
2866*97dc5e69SMatthias Ringwald default:
2867*97dc5e69SMatthias Ringwald LWIP_ASSERT("Unknown socket option", 0);
2868*97dc5e69SMatthias Ringwald return 0;
2869*97dc5e69SMatthias Ringwald }
2870*97dc5e69SMatthias Ringwald }
2871*97dc5e69SMatthias Ringwald
2872*97dc5e69SMatthias Ringwald /** lwip_getsockopt_impl: the actual implementation of getsockopt:
2873*97dc5e69SMatthias Ringwald * same argument as lwip_getsockopt, either called directly or through callback
2874*97dc5e69SMatthias Ringwald */
2875*97dc5e69SMatthias Ringwald static int
lwip_getsockopt_impl(int s,int level,int optname,void * optval,socklen_t * optlen)2876*97dc5e69SMatthias Ringwald lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
2877*97dc5e69SMatthias Ringwald {
2878*97dc5e69SMatthias Ringwald int err = 0;
2879*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket(s);
2880*97dc5e69SMatthias Ringwald if (!sock) {
2881*97dc5e69SMatthias Ringwald return EBADF;
2882*97dc5e69SMatthias Ringwald }
2883*97dc5e69SMatthias Ringwald
2884*97dc5e69SMatthias Ringwald #ifdef LWIP_HOOK_SOCKETS_GETSOCKOPT
2885*97dc5e69SMatthias Ringwald if (LWIP_HOOK_SOCKETS_GETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
2886*97dc5e69SMatthias Ringwald return err;
2887*97dc5e69SMatthias Ringwald }
2888*97dc5e69SMatthias Ringwald #endif
2889*97dc5e69SMatthias Ringwald
2890*97dc5e69SMatthias Ringwald switch (level) {
2891*97dc5e69SMatthias Ringwald
2892*97dc5e69SMatthias Ringwald /* Level: SOL_SOCKET */
2893*97dc5e69SMatthias Ringwald case SOL_SOCKET:
2894*97dc5e69SMatthias Ringwald switch (optname) {
2895*97dc5e69SMatthias Ringwald
2896*97dc5e69SMatthias Ringwald #if LWIP_TCP
2897*97dc5e69SMatthias Ringwald case SO_ACCEPTCONN:
2898*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2899*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
2900*97dc5e69SMatthias Ringwald done_socket(sock);
2901*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
2902*97dc5e69SMatthias Ringwald }
2903*97dc5e69SMatthias Ringwald if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) {
2904*97dc5e69SMatthias Ringwald *(int *)optval = 1;
2905*97dc5e69SMatthias Ringwald } else {
2906*97dc5e69SMatthias Ringwald *(int *)optval = 0;
2907*97dc5e69SMatthias Ringwald }
2908*97dc5e69SMatthias Ringwald break;
2909*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP */
2910*97dc5e69SMatthias Ringwald
2911*97dc5e69SMatthias Ringwald /* The option flags */
2912*97dc5e69SMatthias Ringwald case SO_BROADCAST:
2913*97dc5e69SMatthias Ringwald case SO_KEEPALIVE:
2914*97dc5e69SMatthias Ringwald #if SO_REUSE
2915*97dc5e69SMatthias Ringwald case SO_REUSEADDR:
2916*97dc5e69SMatthias Ringwald #endif /* SO_REUSE */
2917*97dc5e69SMatthias Ringwald if ((optname == SO_BROADCAST) &&
2918*97dc5e69SMatthias Ringwald (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
2919*97dc5e69SMatthias Ringwald done_socket(sock);
2920*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
2921*97dc5e69SMatthias Ringwald }
2922*97dc5e69SMatthias Ringwald
2923*97dc5e69SMatthias Ringwald optname = lwip_sockopt_to_ipopt(optname);
2924*97dc5e69SMatthias Ringwald
2925*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
2926*97dc5e69SMatthias Ringwald *(int *)optval = ip_get_option(sock->conn->pcb.ip, optname);
2927*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
2928*97dc5e69SMatthias Ringwald s, optname, (*(int *)optval ? "on" : "off")));
2929*97dc5e69SMatthias Ringwald break;
2930*97dc5e69SMatthias Ringwald
2931*97dc5e69SMatthias Ringwald case SO_TYPE:
2932*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2933*97dc5e69SMatthias Ringwald switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
2934*97dc5e69SMatthias Ringwald case NETCONN_RAW:
2935*97dc5e69SMatthias Ringwald *(int *)optval = SOCK_RAW;
2936*97dc5e69SMatthias Ringwald break;
2937*97dc5e69SMatthias Ringwald case NETCONN_TCP:
2938*97dc5e69SMatthias Ringwald *(int *)optval = SOCK_STREAM;
2939*97dc5e69SMatthias Ringwald break;
2940*97dc5e69SMatthias Ringwald case NETCONN_UDP:
2941*97dc5e69SMatthias Ringwald *(int *)optval = SOCK_DGRAM;
2942*97dc5e69SMatthias Ringwald break;
2943*97dc5e69SMatthias Ringwald default: /* unrecognized socket type */
2944*97dc5e69SMatthias Ringwald *(int *)optval = netconn_type(sock->conn);
2945*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG,
2946*97dc5e69SMatthias Ringwald ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
2947*97dc5e69SMatthias Ringwald s, *(int *)optval));
2948*97dc5e69SMatthias Ringwald } /* switch (netconn_type(sock->conn)) */
2949*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
2950*97dc5e69SMatthias Ringwald s, *(int *)optval));
2951*97dc5e69SMatthias Ringwald break;
2952*97dc5e69SMatthias Ringwald
2953*97dc5e69SMatthias Ringwald case SO_ERROR:
2954*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN(sock, *optlen, int);
2955*97dc5e69SMatthias Ringwald *(int *)optval = err_to_errno(netconn_err(sock->conn));
2956*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
2957*97dc5e69SMatthias Ringwald s, *(int *)optval));
2958*97dc5e69SMatthias Ringwald break;
2959*97dc5e69SMatthias Ringwald
2960*97dc5e69SMatthias Ringwald #if LWIP_SO_SNDTIMEO
2961*97dc5e69SMatthias Ringwald case SO_SNDTIMEO:
2962*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2963*97dc5e69SMatthias Ringwald LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn));
2964*97dc5e69SMatthias Ringwald break;
2965*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_SNDTIMEO */
2966*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVTIMEO
2967*97dc5e69SMatthias Ringwald case SO_RCVTIMEO:
2968*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
2969*97dc5e69SMatthias Ringwald LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn));
2970*97dc5e69SMatthias Ringwald break;
2971*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVTIMEO */
2972*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVBUF
2973*97dc5e69SMatthias Ringwald case SO_RCVBUF:
2974*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
2975*97dc5e69SMatthias Ringwald *(int *)optval = netconn_get_recvbufsize(sock->conn);
2976*97dc5e69SMatthias Ringwald break;
2977*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVBUF */
2978*97dc5e69SMatthias Ringwald #if LWIP_SO_LINGER
2979*97dc5e69SMatthias Ringwald case SO_LINGER: {
2980*97dc5e69SMatthias Ringwald s16_t conn_linger;
2981*97dc5e69SMatthias Ringwald struct linger *linger = (struct linger *)optval;
2982*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, struct linger);
2983*97dc5e69SMatthias Ringwald conn_linger = sock->conn->linger;
2984*97dc5e69SMatthias Ringwald if (conn_linger >= 0) {
2985*97dc5e69SMatthias Ringwald linger->l_onoff = 1;
2986*97dc5e69SMatthias Ringwald linger->l_linger = (int)conn_linger;
2987*97dc5e69SMatthias Ringwald } else {
2988*97dc5e69SMatthias Ringwald linger->l_onoff = 0;
2989*97dc5e69SMatthias Ringwald linger->l_linger = 0;
2990*97dc5e69SMatthias Ringwald }
2991*97dc5e69SMatthias Ringwald }
2992*97dc5e69SMatthias Ringwald break;
2993*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_LINGER */
2994*97dc5e69SMatthias Ringwald #if LWIP_UDP
2995*97dc5e69SMatthias Ringwald case SO_NO_CHECK:
2996*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
2997*97dc5e69SMatthias Ringwald #if LWIP_UDPLITE
2998*97dc5e69SMatthias Ringwald if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
2999*97dc5e69SMatthias Ringwald /* this flag is only available for UDP, not for UDP lite */
3000*97dc5e69SMatthias Ringwald done_socket(sock);
3001*97dc5e69SMatthias Ringwald return EAFNOSUPPORT;
3002*97dc5e69SMatthias Ringwald }
3003*97dc5e69SMatthias Ringwald #endif /* LWIP_UDPLITE */
3004*97dc5e69SMatthias Ringwald *(int *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM) ? 1 : 0;
3005*97dc5e69SMatthias Ringwald break;
3006*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP*/
3007*97dc5e69SMatthias Ringwald default:
3008*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
3009*97dc5e69SMatthias Ringwald s, optname));
3010*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3011*97dc5e69SMatthias Ringwald break;
3012*97dc5e69SMatthias Ringwald } /* switch (optname) */
3013*97dc5e69SMatthias Ringwald break;
3014*97dc5e69SMatthias Ringwald
3015*97dc5e69SMatthias Ringwald /* Level: IPPROTO_IP */
3016*97dc5e69SMatthias Ringwald case IPPROTO_IP:
3017*97dc5e69SMatthias Ringwald switch (optname) {
3018*97dc5e69SMatthias Ringwald case IP_TTL:
3019*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3020*97dc5e69SMatthias Ringwald *(int *)optval = sock->conn->pcb.ip->ttl;
3021*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
3022*97dc5e69SMatthias Ringwald s, *(int *)optval));
3023*97dc5e69SMatthias Ringwald break;
3024*97dc5e69SMatthias Ringwald case IP_TOS:
3025*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3026*97dc5e69SMatthias Ringwald *(int *)optval = sock->conn->pcb.ip->tos;
3027*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
3028*97dc5e69SMatthias Ringwald s, *(int *)optval));
3029*97dc5e69SMatthias Ringwald break;
3030*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
3031*97dc5e69SMatthias Ringwald case IP_MULTICAST_TTL:
3032*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
3033*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
3034*97dc5e69SMatthias Ringwald done_socket(sock);
3035*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
3036*97dc5e69SMatthias Ringwald }
3037*97dc5e69SMatthias Ringwald *(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
3038*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
3039*97dc5e69SMatthias Ringwald s, *(int *)optval));
3040*97dc5e69SMatthias Ringwald break;
3041*97dc5e69SMatthias Ringwald case IP_MULTICAST_IF:
3042*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
3043*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
3044*97dc5e69SMatthias Ringwald done_socket(sock);
3045*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
3046*97dc5e69SMatthias Ringwald }
3047*97dc5e69SMatthias Ringwald inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
3048*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
3049*97dc5e69SMatthias Ringwald s, *(u32_t *)optval));
3050*97dc5e69SMatthias Ringwald break;
3051*97dc5e69SMatthias Ringwald case IP_MULTICAST_LOOP:
3052*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
3053*97dc5e69SMatthias Ringwald if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
3054*97dc5e69SMatthias Ringwald *(u8_t *)optval = 1;
3055*97dc5e69SMatthias Ringwald } else {
3056*97dc5e69SMatthias Ringwald *(u8_t *)optval = 0;
3057*97dc5e69SMatthias Ringwald }
3058*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
3059*97dc5e69SMatthias Ringwald s, *(int *)optval));
3060*97dc5e69SMatthias Ringwald break;
3061*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
3062*97dc5e69SMatthias Ringwald default:
3063*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
3064*97dc5e69SMatthias Ringwald s, optname));
3065*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3066*97dc5e69SMatthias Ringwald break;
3067*97dc5e69SMatthias Ringwald } /* switch (optname) */
3068*97dc5e69SMatthias Ringwald break;
3069*97dc5e69SMatthias Ringwald
3070*97dc5e69SMatthias Ringwald #if LWIP_TCP
3071*97dc5e69SMatthias Ringwald /* Level: IPPROTO_TCP */
3072*97dc5e69SMatthias Ringwald case IPPROTO_TCP:
3073*97dc5e69SMatthias Ringwald /* Special case: all IPPROTO_TCP option take an int */
3074*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_TCP);
3075*97dc5e69SMatthias Ringwald if (sock->conn->pcb.tcp->state == LISTEN) {
3076*97dc5e69SMatthias Ringwald done_socket(sock);
3077*97dc5e69SMatthias Ringwald return EINVAL;
3078*97dc5e69SMatthias Ringwald }
3079*97dc5e69SMatthias Ringwald switch (optname) {
3080*97dc5e69SMatthias Ringwald case TCP_NODELAY:
3081*97dc5e69SMatthias Ringwald *(int *)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
3082*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
3083*97dc5e69SMatthias Ringwald s, (*(int *)optval) ? "on" : "off") );
3084*97dc5e69SMatthias Ringwald break;
3085*97dc5e69SMatthias Ringwald case TCP_KEEPALIVE:
3086*97dc5e69SMatthias Ringwald *(int *)optval = (int)sock->conn->pcb.tcp->keep_idle;
3087*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) = %d\n",
3088*97dc5e69SMatthias Ringwald s, *(int *)optval));
3089*97dc5e69SMatthias Ringwald break;
3090*97dc5e69SMatthias Ringwald
3091*97dc5e69SMatthias Ringwald #if LWIP_TCP_KEEPALIVE
3092*97dc5e69SMatthias Ringwald case TCP_KEEPIDLE:
3093*97dc5e69SMatthias Ringwald *(int *)optval = (int)(sock->conn->pcb.tcp->keep_idle / 1000);
3094*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) = %d\n",
3095*97dc5e69SMatthias Ringwald s, *(int *)optval));
3096*97dc5e69SMatthias Ringwald break;
3097*97dc5e69SMatthias Ringwald case TCP_KEEPINTVL:
3098*97dc5e69SMatthias Ringwald *(int *)optval = (int)(sock->conn->pcb.tcp->keep_intvl / 1000);
3099*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) = %d\n",
3100*97dc5e69SMatthias Ringwald s, *(int *)optval));
3101*97dc5e69SMatthias Ringwald break;
3102*97dc5e69SMatthias Ringwald case TCP_KEEPCNT:
3103*97dc5e69SMatthias Ringwald *(int *)optval = (int)sock->conn->pcb.tcp->keep_cnt;
3104*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) = %d\n",
3105*97dc5e69SMatthias Ringwald s, *(int *)optval));
3106*97dc5e69SMatthias Ringwald break;
3107*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP_KEEPALIVE */
3108*97dc5e69SMatthias Ringwald default:
3109*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
3110*97dc5e69SMatthias Ringwald s, optname));
3111*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3112*97dc5e69SMatthias Ringwald break;
3113*97dc5e69SMatthias Ringwald } /* switch (optname) */
3114*97dc5e69SMatthias Ringwald break;
3115*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP */
3116*97dc5e69SMatthias Ringwald
3117*97dc5e69SMatthias Ringwald #if LWIP_IPV6
3118*97dc5e69SMatthias Ringwald /* Level: IPPROTO_IPV6 */
3119*97dc5e69SMatthias Ringwald case IPPROTO_IPV6:
3120*97dc5e69SMatthias Ringwald switch (optname) {
3121*97dc5e69SMatthias Ringwald case IPV6_V6ONLY:
3122*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
3123*97dc5e69SMatthias Ringwald *(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
3124*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
3125*97dc5e69SMatthias Ringwald s, *(int *)optval));
3126*97dc5e69SMatthias Ringwald break;
3127*97dc5e69SMatthias Ringwald default:
3128*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
3129*97dc5e69SMatthias Ringwald s, optname));
3130*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3131*97dc5e69SMatthias Ringwald break;
3132*97dc5e69SMatthias Ringwald } /* switch (optname) */
3133*97dc5e69SMatthias Ringwald break;
3134*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 */
3135*97dc5e69SMatthias Ringwald
3136*97dc5e69SMatthias Ringwald #if LWIP_UDP && LWIP_UDPLITE
3137*97dc5e69SMatthias Ringwald /* Level: IPPROTO_UDPLITE */
3138*97dc5e69SMatthias Ringwald case IPPROTO_UDPLITE:
3139*97dc5e69SMatthias Ringwald /* Special case: all IPPROTO_UDPLITE option take an int */
3140*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
3141*97dc5e69SMatthias Ringwald /* If this is no UDP lite socket, ignore any options. */
3142*97dc5e69SMatthias Ringwald if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
3143*97dc5e69SMatthias Ringwald done_socket(sock);
3144*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
3145*97dc5e69SMatthias Ringwald }
3146*97dc5e69SMatthias Ringwald switch (optname) {
3147*97dc5e69SMatthias Ringwald case UDPLITE_SEND_CSCOV:
3148*97dc5e69SMatthias Ringwald *(int *)optval = sock->conn->pcb.udp->chksum_len_tx;
3149*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
3150*97dc5e69SMatthias Ringwald s, (*(int *)optval)) );
3151*97dc5e69SMatthias Ringwald break;
3152*97dc5e69SMatthias Ringwald case UDPLITE_RECV_CSCOV:
3153*97dc5e69SMatthias Ringwald *(int *)optval = sock->conn->pcb.udp->chksum_len_rx;
3154*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
3155*97dc5e69SMatthias Ringwald s, (*(int *)optval)) );
3156*97dc5e69SMatthias Ringwald break;
3157*97dc5e69SMatthias Ringwald default:
3158*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
3159*97dc5e69SMatthias Ringwald s, optname));
3160*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3161*97dc5e69SMatthias Ringwald break;
3162*97dc5e69SMatthias Ringwald } /* switch (optname) */
3163*97dc5e69SMatthias Ringwald break;
3164*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP */
3165*97dc5e69SMatthias Ringwald /* Level: IPPROTO_RAW */
3166*97dc5e69SMatthias Ringwald case IPPROTO_RAW:
3167*97dc5e69SMatthias Ringwald switch (optname) {
3168*97dc5e69SMatthias Ringwald #if LWIP_IPV6 && LWIP_RAW
3169*97dc5e69SMatthias Ringwald case IPV6_CHECKSUM:
3170*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW);
3171*97dc5e69SMatthias Ringwald if (sock->conn->pcb.raw->chksum_reqd == 0) {
3172*97dc5e69SMatthias Ringwald *(int *)optval = -1;
3173*97dc5e69SMatthias Ringwald } else {
3174*97dc5e69SMatthias Ringwald *(int *)optval = sock->conn->pcb.raw->chksum_offset;
3175*97dc5e69SMatthias Ringwald }
3176*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n",
3177*97dc5e69SMatthias Ringwald s, (*(int *)optval)) );
3178*97dc5e69SMatthias Ringwald break;
3179*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 && LWIP_RAW */
3180*97dc5e69SMatthias Ringwald default:
3181*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
3182*97dc5e69SMatthias Ringwald s, optname));
3183*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3184*97dc5e69SMatthias Ringwald break;
3185*97dc5e69SMatthias Ringwald } /* switch (optname) */
3186*97dc5e69SMatthias Ringwald break;
3187*97dc5e69SMatthias Ringwald default:
3188*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
3189*97dc5e69SMatthias Ringwald s, level, optname));
3190*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3191*97dc5e69SMatthias Ringwald break;
3192*97dc5e69SMatthias Ringwald } /* switch (level) */
3193*97dc5e69SMatthias Ringwald
3194*97dc5e69SMatthias Ringwald done_socket(sock);
3195*97dc5e69SMatthias Ringwald return err;
3196*97dc5e69SMatthias Ringwald }
3197*97dc5e69SMatthias Ringwald
3198*97dc5e69SMatthias Ringwald int
lwip_setsockopt(int s,int level,int optname,const void * optval,socklen_t optlen)3199*97dc5e69SMatthias Ringwald lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
3200*97dc5e69SMatthias Ringwald {
3201*97dc5e69SMatthias Ringwald int err = 0;
3202*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
3203*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
3204*97dc5e69SMatthias Ringwald err_t cberr;
3205*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
3206*97dc5e69SMatthias Ringwald #endif /* !LWIP_TCPIP_CORE_LOCKING */
3207*97dc5e69SMatthias Ringwald
3208*97dc5e69SMatthias Ringwald if (!sock) {
3209*97dc5e69SMatthias Ringwald return -1;
3210*97dc5e69SMatthias Ringwald }
3211*97dc5e69SMatthias Ringwald
3212*97dc5e69SMatthias Ringwald if (NULL == optval) {
3213*97dc5e69SMatthias Ringwald set_errno(EFAULT);
3214*97dc5e69SMatthias Ringwald done_socket(sock);
3215*97dc5e69SMatthias Ringwald return -1;
3216*97dc5e69SMatthias Ringwald }
3217*97dc5e69SMatthias Ringwald
3218*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
3219*97dc5e69SMatthias Ringwald /* core-locking can just call the -impl function */
3220*97dc5e69SMatthias Ringwald LOCK_TCPIP_CORE();
3221*97dc5e69SMatthias Ringwald err = lwip_setsockopt_impl(s, level, optname, optval, optlen);
3222*97dc5e69SMatthias Ringwald UNLOCK_TCPIP_CORE();
3223*97dc5e69SMatthias Ringwald
3224*97dc5e69SMatthias Ringwald #else /* LWIP_TCPIP_CORE_LOCKING */
3225*97dc5e69SMatthias Ringwald
3226*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
3227*97dc5e69SMatthias Ringwald /* MPU_COMPATIBLE copies the optval data, so check for max size here */
3228*97dc5e69SMatthias Ringwald if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) {
3229*97dc5e69SMatthias Ringwald set_errno(ENOBUFS);
3230*97dc5e69SMatthias Ringwald done_socket(sock);
3231*97dc5e69SMatthias Ringwald return -1;
3232*97dc5e69SMatthias Ringwald }
3233*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
3234*97dc5e69SMatthias Ringwald
3235*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock);
3236*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s;
3237*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level;
3238*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname;
3239*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen;
3240*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
3241*97dc5e69SMatthias Ringwald MEMCPY(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen);
3242*97dc5e69SMatthias Ringwald #else /* LWIP_MPU_COMPATIBLE */
3243*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval.pc = (const void *)optval;
3244*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
3245*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0;
3246*97dc5e69SMatthias Ringwald #if LWIP_NETCONN_SEM_PER_THREAD
3247*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET();
3248*97dc5e69SMatthias Ringwald #else
3249*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed;
3250*97dc5e69SMatthias Ringwald #endif
3251*97dc5e69SMatthias Ringwald cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
3252*97dc5e69SMatthias Ringwald if (cberr != ERR_OK) {
3253*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
3254*97dc5e69SMatthias Ringwald set_errno(err_to_errno(cberr));
3255*97dc5e69SMatthias Ringwald done_socket(sock);
3256*97dc5e69SMatthias Ringwald return -1;
3257*97dc5e69SMatthias Ringwald }
3258*97dc5e69SMatthias Ringwald sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
3259*97dc5e69SMatthias Ringwald
3260*97dc5e69SMatthias Ringwald /* maybe lwip_setsockopt_impl has changed err */
3261*97dc5e69SMatthias Ringwald err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
3262*97dc5e69SMatthias Ringwald LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
3263*97dc5e69SMatthias Ringwald #endif /* LWIP_TCPIP_CORE_LOCKING */
3264*97dc5e69SMatthias Ringwald
3265*97dc5e69SMatthias Ringwald set_errno(err);
3266*97dc5e69SMatthias Ringwald done_socket(sock);
3267*97dc5e69SMatthias Ringwald return err ? -1 : 0;
3268*97dc5e69SMatthias Ringwald }
3269*97dc5e69SMatthias Ringwald
3270*97dc5e69SMatthias Ringwald #if !LWIP_TCPIP_CORE_LOCKING
3271*97dc5e69SMatthias Ringwald /** lwip_setsockopt_callback: only used without CORE_LOCKING
3272*97dc5e69SMatthias Ringwald * to get into the tcpip_thread
3273*97dc5e69SMatthias Ringwald */
3274*97dc5e69SMatthias Ringwald static void
lwip_setsockopt_callback(void * arg)3275*97dc5e69SMatthias Ringwald lwip_setsockopt_callback(void *arg)
3276*97dc5e69SMatthias Ringwald {
3277*97dc5e69SMatthias Ringwald struct lwip_setgetsockopt_data *data;
3278*97dc5e69SMatthias Ringwald LWIP_ASSERT("arg != NULL", arg != NULL);
3279*97dc5e69SMatthias Ringwald data = (struct lwip_setgetsockopt_data *)arg;
3280*97dc5e69SMatthias Ringwald
3281*97dc5e69SMatthias Ringwald data->err = lwip_setsockopt_impl(data->s, data->level, data->optname,
3282*97dc5e69SMatthias Ringwald #if LWIP_MPU_COMPATIBLE
3283*97dc5e69SMatthias Ringwald data->optval,
3284*97dc5e69SMatthias Ringwald #else /* LWIP_MPU_COMPATIBLE */
3285*97dc5e69SMatthias Ringwald data->optval.pc,
3286*97dc5e69SMatthias Ringwald #endif /* LWIP_MPU_COMPATIBLE */
3287*97dc5e69SMatthias Ringwald data->optlen);
3288*97dc5e69SMatthias Ringwald
3289*97dc5e69SMatthias Ringwald sys_sem_signal((sys_sem_t *)(data->completed_sem));
3290*97dc5e69SMatthias Ringwald }
3291*97dc5e69SMatthias Ringwald #endif /* LWIP_TCPIP_CORE_LOCKING */
3292*97dc5e69SMatthias Ringwald
3293*97dc5e69SMatthias Ringwald /** lwip_setsockopt_impl: the actual implementation of setsockopt:
3294*97dc5e69SMatthias Ringwald * same argument as lwip_setsockopt, either called directly or through callback
3295*97dc5e69SMatthias Ringwald */
3296*97dc5e69SMatthias Ringwald static int
lwip_setsockopt_impl(int s,int level,int optname,const void * optval,socklen_t optlen)3297*97dc5e69SMatthias Ringwald lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
3298*97dc5e69SMatthias Ringwald {
3299*97dc5e69SMatthias Ringwald int err = 0;
3300*97dc5e69SMatthias Ringwald struct lwip_sock *sock = tryget_socket(s);
3301*97dc5e69SMatthias Ringwald if (!sock) {
3302*97dc5e69SMatthias Ringwald return EBADF;
3303*97dc5e69SMatthias Ringwald }
3304*97dc5e69SMatthias Ringwald
3305*97dc5e69SMatthias Ringwald #ifdef LWIP_HOOK_SOCKETS_SETSOCKOPT
3306*97dc5e69SMatthias Ringwald if (LWIP_HOOK_SOCKETS_SETSOCKOPT(s, sock, level, optname, optval, optlen, &err)) {
3307*97dc5e69SMatthias Ringwald return err;
3308*97dc5e69SMatthias Ringwald }
3309*97dc5e69SMatthias Ringwald #endif
3310*97dc5e69SMatthias Ringwald
3311*97dc5e69SMatthias Ringwald switch (level) {
3312*97dc5e69SMatthias Ringwald
3313*97dc5e69SMatthias Ringwald /* Level: SOL_SOCKET */
3314*97dc5e69SMatthias Ringwald case SOL_SOCKET:
3315*97dc5e69SMatthias Ringwald switch (optname) {
3316*97dc5e69SMatthias Ringwald
3317*97dc5e69SMatthias Ringwald /* SO_ACCEPTCONN is get-only */
3318*97dc5e69SMatthias Ringwald
3319*97dc5e69SMatthias Ringwald /* The option flags */
3320*97dc5e69SMatthias Ringwald case SO_BROADCAST:
3321*97dc5e69SMatthias Ringwald case SO_KEEPALIVE:
3322*97dc5e69SMatthias Ringwald #if SO_REUSE
3323*97dc5e69SMatthias Ringwald case SO_REUSEADDR:
3324*97dc5e69SMatthias Ringwald #endif /* SO_REUSE */
3325*97dc5e69SMatthias Ringwald if ((optname == SO_BROADCAST) &&
3326*97dc5e69SMatthias Ringwald (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
3327*97dc5e69SMatthias Ringwald done_socket(sock);
3328*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
3329*97dc5e69SMatthias Ringwald }
3330*97dc5e69SMatthias Ringwald
3331*97dc5e69SMatthias Ringwald optname = lwip_sockopt_to_ipopt(optname);
3332*97dc5e69SMatthias Ringwald
3333*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3334*97dc5e69SMatthias Ringwald if (*(const int *)optval) {
3335*97dc5e69SMatthias Ringwald ip_set_option(sock->conn->pcb.ip, optname);
3336*97dc5e69SMatthias Ringwald } else {
3337*97dc5e69SMatthias Ringwald ip_reset_option(sock->conn->pcb.ip, optname);
3338*97dc5e69SMatthias Ringwald }
3339*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
3340*97dc5e69SMatthias Ringwald s, optname, (*(const int *)optval ? "on" : "off")));
3341*97dc5e69SMatthias Ringwald break;
3342*97dc5e69SMatthias Ringwald
3343*97dc5e69SMatthias Ringwald /* SO_TYPE is get-only */
3344*97dc5e69SMatthias Ringwald /* SO_ERROR is get-only */
3345*97dc5e69SMatthias Ringwald
3346*97dc5e69SMatthias Ringwald #if LWIP_SO_SNDTIMEO
3347*97dc5e69SMatthias Ringwald case SO_SNDTIMEO: {
3348*97dc5e69SMatthias Ringwald long ms_long;
3349*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
3350*97dc5e69SMatthias Ringwald ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
3351*97dc5e69SMatthias Ringwald if (ms_long < 0) {
3352*97dc5e69SMatthias Ringwald done_socket(sock);
3353*97dc5e69SMatthias Ringwald return EINVAL;
3354*97dc5e69SMatthias Ringwald }
3355*97dc5e69SMatthias Ringwald netconn_set_sendtimeout(sock->conn, ms_long);
3356*97dc5e69SMatthias Ringwald break;
3357*97dc5e69SMatthias Ringwald }
3358*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_SNDTIMEO */
3359*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVTIMEO
3360*97dc5e69SMatthias Ringwald case SO_RCVTIMEO: {
3361*97dc5e69SMatthias Ringwald long ms_long;
3362*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
3363*97dc5e69SMatthias Ringwald ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
3364*97dc5e69SMatthias Ringwald if (ms_long < 0) {
3365*97dc5e69SMatthias Ringwald done_socket(sock);
3366*97dc5e69SMatthias Ringwald return EINVAL;
3367*97dc5e69SMatthias Ringwald }
3368*97dc5e69SMatthias Ringwald netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
3369*97dc5e69SMatthias Ringwald break;
3370*97dc5e69SMatthias Ringwald }
3371*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVTIMEO */
3372*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVBUF
3373*97dc5e69SMatthias Ringwald case SO_RCVBUF:
3374*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int);
3375*97dc5e69SMatthias Ringwald netconn_set_recvbufsize(sock->conn, *(const int *)optval);
3376*97dc5e69SMatthias Ringwald break;
3377*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVBUF */
3378*97dc5e69SMatthias Ringwald #if LWIP_SO_LINGER
3379*97dc5e69SMatthias Ringwald case SO_LINGER: {
3380*97dc5e69SMatthias Ringwald const struct linger *linger = (const struct linger *)optval;
3381*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct linger);
3382*97dc5e69SMatthias Ringwald if (linger->l_onoff) {
3383*97dc5e69SMatthias Ringwald int lingersec = linger->l_linger;
3384*97dc5e69SMatthias Ringwald if (lingersec < 0) {
3385*97dc5e69SMatthias Ringwald done_socket(sock);
3386*97dc5e69SMatthias Ringwald return EINVAL;
3387*97dc5e69SMatthias Ringwald }
3388*97dc5e69SMatthias Ringwald if (lingersec > 0xFFFF) {
3389*97dc5e69SMatthias Ringwald lingersec = 0xFFFF;
3390*97dc5e69SMatthias Ringwald }
3391*97dc5e69SMatthias Ringwald sock->conn->linger = (s16_t)lingersec;
3392*97dc5e69SMatthias Ringwald } else {
3393*97dc5e69SMatthias Ringwald sock->conn->linger = -1;
3394*97dc5e69SMatthias Ringwald }
3395*97dc5e69SMatthias Ringwald }
3396*97dc5e69SMatthias Ringwald break;
3397*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_LINGER */
3398*97dc5e69SMatthias Ringwald #if LWIP_UDP
3399*97dc5e69SMatthias Ringwald case SO_NO_CHECK:
3400*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
3401*97dc5e69SMatthias Ringwald #if LWIP_UDPLITE
3402*97dc5e69SMatthias Ringwald if (udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_UDPLITE)) {
3403*97dc5e69SMatthias Ringwald /* this flag is only available for UDP, not for UDP lite */
3404*97dc5e69SMatthias Ringwald done_socket(sock);
3405*97dc5e69SMatthias Ringwald return EAFNOSUPPORT;
3406*97dc5e69SMatthias Ringwald }
3407*97dc5e69SMatthias Ringwald #endif /* LWIP_UDPLITE */
3408*97dc5e69SMatthias Ringwald if (*(const int *)optval) {
3409*97dc5e69SMatthias Ringwald udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
3410*97dc5e69SMatthias Ringwald } else {
3411*97dc5e69SMatthias Ringwald udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
3412*97dc5e69SMatthias Ringwald }
3413*97dc5e69SMatthias Ringwald break;
3414*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP */
3415*97dc5e69SMatthias Ringwald case SO_BINDTODEVICE: {
3416*97dc5e69SMatthias Ringwald const struct ifreq *iface;
3417*97dc5e69SMatthias Ringwald struct netif *n = NULL;
3418*97dc5e69SMatthias Ringwald
3419*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
3420*97dc5e69SMatthias Ringwald
3421*97dc5e69SMatthias Ringwald iface = (const struct ifreq *)optval;
3422*97dc5e69SMatthias Ringwald if (iface->ifr_name[0] != 0) {
3423*97dc5e69SMatthias Ringwald n = netif_find(iface->ifr_name);
3424*97dc5e69SMatthias Ringwald if (n == NULL) {
3425*97dc5e69SMatthias Ringwald done_socket(sock);
3426*97dc5e69SMatthias Ringwald return ENODEV;
3427*97dc5e69SMatthias Ringwald }
3428*97dc5e69SMatthias Ringwald }
3429*97dc5e69SMatthias Ringwald
3430*97dc5e69SMatthias Ringwald switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
3431*97dc5e69SMatthias Ringwald #if LWIP_TCP
3432*97dc5e69SMatthias Ringwald case NETCONN_TCP:
3433*97dc5e69SMatthias Ringwald tcp_bind_netif(sock->conn->pcb.tcp, n);
3434*97dc5e69SMatthias Ringwald break;
3435*97dc5e69SMatthias Ringwald #endif
3436*97dc5e69SMatthias Ringwald #if LWIP_UDP
3437*97dc5e69SMatthias Ringwald case NETCONN_UDP:
3438*97dc5e69SMatthias Ringwald udp_bind_netif(sock->conn->pcb.udp, n);
3439*97dc5e69SMatthias Ringwald break;
3440*97dc5e69SMatthias Ringwald #endif
3441*97dc5e69SMatthias Ringwald #if LWIP_RAW
3442*97dc5e69SMatthias Ringwald case NETCONN_RAW:
3443*97dc5e69SMatthias Ringwald raw_bind_netif(sock->conn->pcb.raw, n);
3444*97dc5e69SMatthias Ringwald break;
3445*97dc5e69SMatthias Ringwald #endif
3446*97dc5e69SMatthias Ringwald default:
3447*97dc5e69SMatthias Ringwald LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
3448*97dc5e69SMatthias Ringwald break;
3449*97dc5e69SMatthias Ringwald }
3450*97dc5e69SMatthias Ringwald }
3451*97dc5e69SMatthias Ringwald break;
3452*97dc5e69SMatthias Ringwald default:
3453*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
3454*97dc5e69SMatthias Ringwald s, optname));
3455*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3456*97dc5e69SMatthias Ringwald break;
3457*97dc5e69SMatthias Ringwald } /* switch (optname) */
3458*97dc5e69SMatthias Ringwald break;
3459*97dc5e69SMatthias Ringwald
3460*97dc5e69SMatthias Ringwald /* Level: IPPROTO_IP */
3461*97dc5e69SMatthias Ringwald case IPPROTO_IP:
3462*97dc5e69SMatthias Ringwald switch (optname) {
3463*97dc5e69SMatthias Ringwald case IP_TTL:
3464*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3465*97dc5e69SMatthias Ringwald sock->conn->pcb.ip->ttl = (u8_t)(*(const int *)optval);
3466*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
3467*97dc5e69SMatthias Ringwald s, sock->conn->pcb.ip->ttl));
3468*97dc5e69SMatthias Ringwald break;
3469*97dc5e69SMatthias Ringwald case IP_TOS:
3470*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3471*97dc5e69SMatthias Ringwald sock->conn->pcb.ip->tos = (u8_t)(*(const int *)optval);
3472*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
3473*97dc5e69SMatthias Ringwald s, sock->conn->pcb.ip->tos));
3474*97dc5e69SMatthias Ringwald break;
3475*97dc5e69SMatthias Ringwald #if LWIP_NETBUF_RECVINFO
3476*97dc5e69SMatthias Ringwald case IP_PKTINFO:
3477*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
3478*97dc5e69SMatthias Ringwald if (*(const int *)optval) {
3479*97dc5e69SMatthias Ringwald sock->conn->flags |= NETCONN_FLAG_PKTINFO;
3480*97dc5e69SMatthias Ringwald } else {
3481*97dc5e69SMatthias Ringwald sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
3482*97dc5e69SMatthias Ringwald }
3483*97dc5e69SMatthias Ringwald break;
3484*97dc5e69SMatthias Ringwald #endif /* LWIP_NETBUF_RECVINFO */
3485*97dc5e69SMatthias Ringwald #if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
3486*97dc5e69SMatthias Ringwald case IP_MULTICAST_TTL:
3487*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
3488*97dc5e69SMatthias Ringwald udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
3489*97dc5e69SMatthias Ringwald break;
3490*97dc5e69SMatthias Ringwald case IP_MULTICAST_IF: {
3491*97dc5e69SMatthias Ringwald ip4_addr_t if_addr;
3492*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP);
3493*97dc5e69SMatthias Ringwald inet_addr_to_ip4addr(&if_addr, (const struct in_addr *)optval);
3494*97dc5e69SMatthias Ringwald udp_set_multicast_netif_addr(sock->conn->pcb.udp, &if_addr);
3495*97dc5e69SMatthias Ringwald }
3496*97dc5e69SMatthias Ringwald break;
3497*97dc5e69SMatthias Ringwald case IP_MULTICAST_LOOP:
3498*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
3499*97dc5e69SMatthias Ringwald if (*(const u8_t *)optval) {
3500*97dc5e69SMatthias Ringwald udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
3501*97dc5e69SMatthias Ringwald } else {
3502*97dc5e69SMatthias Ringwald udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
3503*97dc5e69SMatthias Ringwald }
3504*97dc5e69SMatthias Ringwald break;
3505*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
3506*97dc5e69SMatthias Ringwald #if LWIP_IGMP
3507*97dc5e69SMatthias Ringwald case IP_ADD_MEMBERSHIP:
3508*97dc5e69SMatthias Ringwald case IP_DROP_MEMBERSHIP: {
3509*97dc5e69SMatthias Ringwald /* If this is a TCP or a RAW socket, ignore these options. */
3510*97dc5e69SMatthias Ringwald err_t igmp_err;
3511*97dc5e69SMatthias Ringwald const struct ip_mreq *imr = (const struct ip_mreq *)optval;
3512*97dc5e69SMatthias Ringwald ip4_addr_t if_addr;
3513*97dc5e69SMatthias Ringwald ip4_addr_t multi_addr;
3514*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP);
3515*97dc5e69SMatthias Ringwald inet_addr_to_ip4addr(&if_addr, &imr->imr_interface);
3516*97dc5e69SMatthias Ringwald inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr);
3517*97dc5e69SMatthias Ringwald if (optname == IP_ADD_MEMBERSHIP) {
3518*97dc5e69SMatthias Ringwald if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) {
3519*97dc5e69SMatthias Ringwald /* cannot track membership (out of memory) */
3520*97dc5e69SMatthias Ringwald err = ENOMEM;
3521*97dc5e69SMatthias Ringwald igmp_err = ERR_OK;
3522*97dc5e69SMatthias Ringwald } else {
3523*97dc5e69SMatthias Ringwald igmp_err = igmp_joingroup(&if_addr, &multi_addr);
3524*97dc5e69SMatthias Ringwald }
3525*97dc5e69SMatthias Ringwald } else {
3526*97dc5e69SMatthias Ringwald igmp_err = igmp_leavegroup(&if_addr, &multi_addr);
3527*97dc5e69SMatthias Ringwald lwip_socket_unregister_membership(s, &if_addr, &multi_addr);
3528*97dc5e69SMatthias Ringwald }
3529*97dc5e69SMatthias Ringwald if (igmp_err != ERR_OK) {
3530*97dc5e69SMatthias Ringwald err = EADDRNOTAVAIL;
3531*97dc5e69SMatthias Ringwald }
3532*97dc5e69SMatthias Ringwald }
3533*97dc5e69SMatthias Ringwald break;
3534*97dc5e69SMatthias Ringwald #endif /* LWIP_IGMP */
3535*97dc5e69SMatthias Ringwald default:
3536*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
3537*97dc5e69SMatthias Ringwald s, optname));
3538*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3539*97dc5e69SMatthias Ringwald break;
3540*97dc5e69SMatthias Ringwald } /* switch (optname) */
3541*97dc5e69SMatthias Ringwald break;
3542*97dc5e69SMatthias Ringwald
3543*97dc5e69SMatthias Ringwald #if LWIP_TCP
3544*97dc5e69SMatthias Ringwald /* Level: IPPROTO_TCP */
3545*97dc5e69SMatthias Ringwald case IPPROTO_TCP:
3546*97dc5e69SMatthias Ringwald /* Special case: all IPPROTO_TCP option take an int */
3547*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP);
3548*97dc5e69SMatthias Ringwald if (sock->conn->pcb.tcp->state == LISTEN) {
3549*97dc5e69SMatthias Ringwald done_socket(sock);
3550*97dc5e69SMatthias Ringwald return EINVAL;
3551*97dc5e69SMatthias Ringwald }
3552*97dc5e69SMatthias Ringwald switch (optname) {
3553*97dc5e69SMatthias Ringwald case TCP_NODELAY:
3554*97dc5e69SMatthias Ringwald if (*(const int *)optval) {
3555*97dc5e69SMatthias Ringwald tcp_nagle_disable(sock->conn->pcb.tcp);
3556*97dc5e69SMatthias Ringwald } else {
3557*97dc5e69SMatthias Ringwald tcp_nagle_enable(sock->conn->pcb.tcp);
3558*97dc5e69SMatthias Ringwald }
3559*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
3560*97dc5e69SMatthias Ringwald s, (*(const int *)optval) ? "on" : "off") );
3561*97dc5e69SMatthias Ringwald break;
3562*97dc5e69SMatthias Ringwald case TCP_KEEPALIVE:
3563*97dc5e69SMatthias Ringwald sock->conn->pcb.tcp->keep_idle = (u32_t)(*(const int *)optval);
3564*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
3565*97dc5e69SMatthias Ringwald s, sock->conn->pcb.tcp->keep_idle));
3566*97dc5e69SMatthias Ringwald break;
3567*97dc5e69SMatthias Ringwald
3568*97dc5e69SMatthias Ringwald #if LWIP_TCP_KEEPALIVE
3569*97dc5e69SMatthias Ringwald case TCP_KEEPIDLE:
3570*97dc5e69SMatthias Ringwald sock->conn->pcb.tcp->keep_idle = 1000 * (u32_t)(*(const int *)optval);
3571*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
3572*97dc5e69SMatthias Ringwald s, sock->conn->pcb.tcp->keep_idle));
3573*97dc5e69SMatthias Ringwald break;
3574*97dc5e69SMatthias Ringwald case TCP_KEEPINTVL:
3575*97dc5e69SMatthias Ringwald sock->conn->pcb.tcp->keep_intvl = 1000 * (u32_t)(*(const int *)optval);
3576*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
3577*97dc5e69SMatthias Ringwald s, sock->conn->pcb.tcp->keep_intvl));
3578*97dc5e69SMatthias Ringwald break;
3579*97dc5e69SMatthias Ringwald case TCP_KEEPCNT:
3580*97dc5e69SMatthias Ringwald sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(const int *)optval);
3581*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
3582*97dc5e69SMatthias Ringwald s, sock->conn->pcb.tcp->keep_cnt));
3583*97dc5e69SMatthias Ringwald break;
3584*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP_KEEPALIVE */
3585*97dc5e69SMatthias Ringwald default:
3586*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
3587*97dc5e69SMatthias Ringwald s, optname));
3588*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3589*97dc5e69SMatthias Ringwald break;
3590*97dc5e69SMatthias Ringwald } /* switch (optname) */
3591*97dc5e69SMatthias Ringwald break;
3592*97dc5e69SMatthias Ringwald #endif /* LWIP_TCP*/
3593*97dc5e69SMatthias Ringwald
3594*97dc5e69SMatthias Ringwald #if LWIP_IPV6
3595*97dc5e69SMatthias Ringwald /* Level: IPPROTO_IPV6 */
3596*97dc5e69SMatthias Ringwald case IPPROTO_IPV6:
3597*97dc5e69SMatthias Ringwald switch (optname) {
3598*97dc5e69SMatthias Ringwald case IPV6_V6ONLY:
3599*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3600*97dc5e69SMatthias Ringwald if (*(const int *)optval) {
3601*97dc5e69SMatthias Ringwald netconn_set_ipv6only(sock->conn, 1);
3602*97dc5e69SMatthias Ringwald } else {
3603*97dc5e69SMatthias Ringwald netconn_set_ipv6only(sock->conn, 0);
3604*97dc5e69SMatthias Ringwald }
3605*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
3606*97dc5e69SMatthias Ringwald s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
3607*97dc5e69SMatthias Ringwald break;
3608*97dc5e69SMatthias Ringwald #if LWIP_IPV6_MLD
3609*97dc5e69SMatthias Ringwald case IPV6_JOIN_GROUP:
3610*97dc5e69SMatthias Ringwald case IPV6_LEAVE_GROUP: {
3611*97dc5e69SMatthias Ringwald /* If this is a TCP or a RAW socket, ignore these options. */
3612*97dc5e69SMatthias Ringwald err_t mld6_err;
3613*97dc5e69SMatthias Ringwald struct netif *netif;
3614*97dc5e69SMatthias Ringwald ip6_addr_t multi_addr;
3615*97dc5e69SMatthias Ringwald const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
3616*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
3617*97dc5e69SMatthias Ringwald inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
3618*97dc5e69SMatthias Ringwald LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
3619*97dc5e69SMatthias Ringwald netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
3620*97dc5e69SMatthias Ringwald if (netif == NULL) {
3621*97dc5e69SMatthias Ringwald err = EADDRNOTAVAIL;
3622*97dc5e69SMatthias Ringwald break;
3623*97dc5e69SMatthias Ringwald }
3624*97dc5e69SMatthias Ringwald
3625*97dc5e69SMatthias Ringwald if (optname == IPV6_JOIN_GROUP) {
3626*97dc5e69SMatthias Ringwald if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
3627*97dc5e69SMatthias Ringwald /* cannot track membership (out of memory) */
3628*97dc5e69SMatthias Ringwald err = ENOMEM;
3629*97dc5e69SMatthias Ringwald mld6_err = ERR_OK;
3630*97dc5e69SMatthias Ringwald } else {
3631*97dc5e69SMatthias Ringwald mld6_err = mld6_joingroup_netif(netif, &multi_addr);
3632*97dc5e69SMatthias Ringwald }
3633*97dc5e69SMatthias Ringwald } else {
3634*97dc5e69SMatthias Ringwald mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
3635*97dc5e69SMatthias Ringwald lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
3636*97dc5e69SMatthias Ringwald }
3637*97dc5e69SMatthias Ringwald if (mld6_err != ERR_OK) {
3638*97dc5e69SMatthias Ringwald err = EADDRNOTAVAIL;
3639*97dc5e69SMatthias Ringwald }
3640*97dc5e69SMatthias Ringwald }
3641*97dc5e69SMatthias Ringwald break;
3642*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6_MLD */
3643*97dc5e69SMatthias Ringwald default:
3644*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
3645*97dc5e69SMatthias Ringwald s, optname));
3646*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3647*97dc5e69SMatthias Ringwald break;
3648*97dc5e69SMatthias Ringwald } /* switch (optname) */
3649*97dc5e69SMatthias Ringwald break;
3650*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 */
3651*97dc5e69SMatthias Ringwald
3652*97dc5e69SMatthias Ringwald #if LWIP_UDP && LWIP_UDPLITE
3653*97dc5e69SMatthias Ringwald /* Level: IPPROTO_UDPLITE */
3654*97dc5e69SMatthias Ringwald case IPPROTO_UDPLITE:
3655*97dc5e69SMatthias Ringwald /* Special case: all IPPROTO_UDPLITE option take an int */
3656*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
3657*97dc5e69SMatthias Ringwald /* If this is no UDP lite socket, ignore any options. */
3658*97dc5e69SMatthias Ringwald if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
3659*97dc5e69SMatthias Ringwald done_socket(sock);
3660*97dc5e69SMatthias Ringwald return ENOPROTOOPT;
3661*97dc5e69SMatthias Ringwald }
3662*97dc5e69SMatthias Ringwald switch (optname) {
3663*97dc5e69SMatthias Ringwald case UDPLITE_SEND_CSCOV:
3664*97dc5e69SMatthias Ringwald if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
3665*97dc5e69SMatthias Ringwald /* don't allow illegal values! */
3666*97dc5e69SMatthias Ringwald sock->conn->pcb.udp->chksum_len_tx = 8;
3667*97dc5e69SMatthias Ringwald } else {
3668*97dc5e69SMatthias Ringwald sock->conn->pcb.udp->chksum_len_tx = (u16_t) * (const int *)optval;
3669*97dc5e69SMatthias Ringwald }
3670*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
3671*97dc5e69SMatthias Ringwald s, (*(const int *)optval)) );
3672*97dc5e69SMatthias Ringwald break;
3673*97dc5e69SMatthias Ringwald case UDPLITE_RECV_CSCOV:
3674*97dc5e69SMatthias Ringwald if ((*(const int *)optval != 0) && ((*(const int *)optval < 8) || (*(const int *)optval > 0xffff))) {
3675*97dc5e69SMatthias Ringwald /* don't allow illegal values! */
3676*97dc5e69SMatthias Ringwald sock->conn->pcb.udp->chksum_len_rx = 8;
3677*97dc5e69SMatthias Ringwald } else {
3678*97dc5e69SMatthias Ringwald sock->conn->pcb.udp->chksum_len_rx = (u16_t) * (const int *)optval;
3679*97dc5e69SMatthias Ringwald }
3680*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
3681*97dc5e69SMatthias Ringwald s, (*(const int *)optval)) );
3682*97dc5e69SMatthias Ringwald break;
3683*97dc5e69SMatthias Ringwald default:
3684*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
3685*97dc5e69SMatthias Ringwald s, optname));
3686*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3687*97dc5e69SMatthias Ringwald break;
3688*97dc5e69SMatthias Ringwald } /* switch (optname) */
3689*97dc5e69SMatthias Ringwald break;
3690*97dc5e69SMatthias Ringwald #endif /* LWIP_UDP */
3691*97dc5e69SMatthias Ringwald /* Level: IPPROTO_RAW */
3692*97dc5e69SMatthias Ringwald case IPPROTO_RAW:
3693*97dc5e69SMatthias Ringwald switch (optname) {
3694*97dc5e69SMatthias Ringwald #if LWIP_IPV6 && LWIP_RAW
3695*97dc5e69SMatthias Ringwald case IPV6_CHECKSUM:
3696*97dc5e69SMatthias Ringwald /* It should not be possible to disable the checksum generation with ICMPv6
3697*97dc5e69SMatthias Ringwald * as per RFC 3542 chapter 3.1 */
3698*97dc5e69SMatthias Ringwald if (sock->conn->pcb.raw->protocol == IPPROTO_ICMPV6) {
3699*97dc5e69SMatthias Ringwald done_socket(sock);
3700*97dc5e69SMatthias Ringwald return EINVAL;
3701*97dc5e69SMatthias Ringwald }
3702*97dc5e69SMatthias Ringwald
3703*97dc5e69SMatthias Ringwald LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW);
3704*97dc5e69SMatthias Ringwald if (*(const int *)optval < 0) {
3705*97dc5e69SMatthias Ringwald sock->conn->pcb.raw->chksum_reqd = 0;
3706*97dc5e69SMatthias Ringwald } else if (*(const int *)optval & 1) {
3707*97dc5e69SMatthias Ringwald /* Per RFC3542, odd offsets are not allowed */
3708*97dc5e69SMatthias Ringwald done_socket(sock);
3709*97dc5e69SMatthias Ringwald return EINVAL;
3710*97dc5e69SMatthias Ringwald } else {
3711*97dc5e69SMatthias Ringwald sock->conn->pcb.raw->chksum_reqd = 1;
3712*97dc5e69SMatthias Ringwald sock->conn->pcb.raw->chksum_offset = (u16_t) * (const int *)optval;
3713*97dc5e69SMatthias Ringwald }
3714*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n",
3715*97dc5e69SMatthias Ringwald s, sock->conn->pcb.raw->chksum_reqd));
3716*97dc5e69SMatthias Ringwald break;
3717*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6 && LWIP_RAW */
3718*97dc5e69SMatthias Ringwald default:
3719*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n",
3720*97dc5e69SMatthias Ringwald s, optname));
3721*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3722*97dc5e69SMatthias Ringwald break;
3723*97dc5e69SMatthias Ringwald } /* switch (optname) */
3724*97dc5e69SMatthias Ringwald break;
3725*97dc5e69SMatthias Ringwald default:
3726*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
3727*97dc5e69SMatthias Ringwald s, level, optname));
3728*97dc5e69SMatthias Ringwald err = ENOPROTOOPT;
3729*97dc5e69SMatthias Ringwald break;
3730*97dc5e69SMatthias Ringwald } /* switch (level) */
3731*97dc5e69SMatthias Ringwald
3732*97dc5e69SMatthias Ringwald done_socket(sock);
3733*97dc5e69SMatthias Ringwald return err;
3734*97dc5e69SMatthias Ringwald }
3735*97dc5e69SMatthias Ringwald
3736*97dc5e69SMatthias Ringwald int
lwip_ioctl(int s,long cmd,void * argp)3737*97dc5e69SMatthias Ringwald lwip_ioctl(int s, long cmd, void *argp)
3738*97dc5e69SMatthias Ringwald {
3739*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
3740*97dc5e69SMatthias Ringwald u8_t val;
3741*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVBUF
3742*97dc5e69SMatthias Ringwald int recv_avail;
3743*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVBUF */
3744*97dc5e69SMatthias Ringwald
3745*97dc5e69SMatthias Ringwald if (!sock) {
3746*97dc5e69SMatthias Ringwald return -1;
3747*97dc5e69SMatthias Ringwald }
3748*97dc5e69SMatthias Ringwald
3749*97dc5e69SMatthias Ringwald switch (cmd) {
3750*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
3751*97dc5e69SMatthias Ringwald case FIONREAD:
3752*97dc5e69SMatthias Ringwald if (!argp) {
3753*97dc5e69SMatthias Ringwald set_errno(EINVAL);
3754*97dc5e69SMatthias Ringwald done_socket(sock);
3755*97dc5e69SMatthias Ringwald return -1;
3756*97dc5e69SMatthias Ringwald }
3757*97dc5e69SMatthias Ringwald #if LWIP_FIONREAD_LINUXMODE
3758*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
3759*97dc5e69SMatthias Ringwald struct netbuf *nb;
3760*97dc5e69SMatthias Ringwald if (sock->lastdata.netbuf) {
3761*97dc5e69SMatthias Ringwald nb = sock->lastdata.netbuf;
3762*97dc5e69SMatthias Ringwald *((int *)argp) = nb->p->tot_len;
3763*97dc5e69SMatthias Ringwald } else {
3764*97dc5e69SMatthias Ringwald struct netbuf *rxbuf;
3765*97dc5e69SMatthias Ringwald err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
3766*97dc5e69SMatthias Ringwald if (err != ERR_OK) {
3767*97dc5e69SMatthias Ringwald *((int *)argp) = 0;
3768*97dc5e69SMatthias Ringwald } else {
3769*97dc5e69SMatthias Ringwald sock->lastdata.netbuf = rxbuf;
3770*97dc5e69SMatthias Ringwald *((int *)argp) = rxbuf->p->tot_len;
3771*97dc5e69SMatthias Ringwald }
3772*97dc5e69SMatthias Ringwald }
3773*97dc5e69SMatthias Ringwald done_socket(sock);
3774*97dc5e69SMatthias Ringwald return 0;
3775*97dc5e69SMatthias Ringwald }
3776*97dc5e69SMatthias Ringwald #endif /* LWIP_FIONREAD_LINUXMODE */
3777*97dc5e69SMatthias Ringwald
3778*97dc5e69SMatthias Ringwald #if LWIP_SO_RCVBUF
3779*97dc5e69SMatthias Ringwald /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
3780*97dc5e69SMatthias Ringwald SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
3781*97dc5e69SMatthias Ringwald if (recv_avail < 0) {
3782*97dc5e69SMatthias Ringwald recv_avail = 0;
3783*97dc5e69SMatthias Ringwald }
3784*97dc5e69SMatthias Ringwald
3785*97dc5e69SMatthias Ringwald /* Check if there is data left from the last recv operation. /maq 041215 */
3786*97dc5e69SMatthias Ringwald if (sock->lastdata.netbuf) {
3787*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
3788*97dc5e69SMatthias Ringwald recv_avail += sock->lastdata.pbuf->tot_len;
3789*97dc5e69SMatthias Ringwald } else {
3790*97dc5e69SMatthias Ringwald recv_avail += sock->lastdata.netbuf->p->tot_len;
3791*97dc5e69SMatthias Ringwald }
3792*97dc5e69SMatthias Ringwald }
3793*97dc5e69SMatthias Ringwald *((int *)argp) = recv_avail;
3794*97dc5e69SMatthias Ringwald
3795*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t *)argp)));
3796*97dc5e69SMatthias Ringwald set_errno(0);
3797*97dc5e69SMatthias Ringwald done_socket(sock);
3798*97dc5e69SMatthias Ringwald return 0;
3799*97dc5e69SMatthias Ringwald #else /* LWIP_SO_RCVBUF */
3800*97dc5e69SMatthias Ringwald break;
3801*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVBUF */
3802*97dc5e69SMatthias Ringwald #endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
3803*97dc5e69SMatthias Ringwald
3804*97dc5e69SMatthias Ringwald case (long)FIONBIO:
3805*97dc5e69SMatthias Ringwald val = 0;
3806*97dc5e69SMatthias Ringwald if (argp && *(int *)argp) {
3807*97dc5e69SMatthias Ringwald val = 1;
3808*97dc5e69SMatthias Ringwald }
3809*97dc5e69SMatthias Ringwald netconn_set_nonblocking(sock->conn, val);
3810*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
3811*97dc5e69SMatthias Ringwald set_errno(0);
3812*97dc5e69SMatthias Ringwald done_socket(sock);
3813*97dc5e69SMatthias Ringwald return 0;
3814*97dc5e69SMatthias Ringwald
3815*97dc5e69SMatthias Ringwald default:
3816*97dc5e69SMatthias Ringwald break;
3817*97dc5e69SMatthias Ringwald } /* switch (cmd) */
3818*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
3819*97dc5e69SMatthias Ringwald set_errno(ENOSYS); /* not yet implemented */
3820*97dc5e69SMatthias Ringwald done_socket(sock);
3821*97dc5e69SMatthias Ringwald return -1;
3822*97dc5e69SMatthias Ringwald }
3823*97dc5e69SMatthias Ringwald
3824*97dc5e69SMatthias Ringwald /** A minimal implementation of fcntl.
3825*97dc5e69SMatthias Ringwald * Currently only the commands F_GETFL and F_SETFL are implemented.
3826*97dc5e69SMatthias Ringwald * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
3827*97dc5e69SMatthias Ringwald * the flag O_NONBLOCK is implemented for F_SETFL.
3828*97dc5e69SMatthias Ringwald */
3829*97dc5e69SMatthias Ringwald int
lwip_fcntl(int s,int cmd,int val)3830*97dc5e69SMatthias Ringwald lwip_fcntl(int s, int cmd, int val)
3831*97dc5e69SMatthias Ringwald {
3832*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
3833*97dc5e69SMatthias Ringwald int ret = -1;
3834*97dc5e69SMatthias Ringwald int op_mode = 0;
3835*97dc5e69SMatthias Ringwald
3836*97dc5e69SMatthias Ringwald if (!sock) {
3837*97dc5e69SMatthias Ringwald return -1;
3838*97dc5e69SMatthias Ringwald }
3839*97dc5e69SMatthias Ringwald
3840*97dc5e69SMatthias Ringwald switch (cmd) {
3841*97dc5e69SMatthias Ringwald case F_GETFL:
3842*97dc5e69SMatthias Ringwald ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
3843*97dc5e69SMatthias Ringwald set_errno(0);
3844*97dc5e69SMatthias Ringwald
3845*97dc5e69SMatthias Ringwald if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
3846*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
3847*97dc5e69SMatthias Ringwald LOCK_TCPIP_CORE();
3848*97dc5e69SMatthias Ringwald #else
3849*97dc5e69SMatthias Ringwald SYS_ARCH_DECL_PROTECT(lev);
3850*97dc5e69SMatthias Ringwald /* the proper thing to do here would be to get into the tcpip_thread,
3851*97dc5e69SMatthias Ringwald but locking should be OK as well since we only *read* some flags */
3852*97dc5e69SMatthias Ringwald SYS_ARCH_PROTECT(lev);
3853*97dc5e69SMatthias Ringwald #endif
3854*97dc5e69SMatthias Ringwald #if LWIP_TCP
3855*97dc5e69SMatthias Ringwald if (sock->conn->pcb.tcp) {
3856*97dc5e69SMatthias Ringwald if (!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
3857*97dc5e69SMatthias Ringwald op_mode |= O_RDONLY;
3858*97dc5e69SMatthias Ringwald }
3859*97dc5e69SMatthias Ringwald if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
3860*97dc5e69SMatthias Ringwald op_mode |= O_WRONLY;
3861*97dc5e69SMatthias Ringwald }
3862*97dc5e69SMatthias Ringwald }
3863*97dc5e69SMatthias Ringwald #endif
3864*97dc5e69SMatthias Ringwald #if LWIP_TCPIP_CORE_LOCKING
3865*97dc5e69SMatthias Ringwald UNLOCK_TCPIP_CORE();
3866*97dc5e69SMatthias Ringwald #else
3867*97dc5e69SMatthias Ringwald SYS_ARCH_UNPROTECT(lev);
3868*97dc5e69SMatthias Ringwald #endif
3869*97dc5e69SMatthias Ringwald } else {
3870*97dc5e69SMatthias Ringwald op_mode |= O_RDWR;
3871*97dc5e69SMatthias Ringwald }
3872*97dc5e69SMatthias Ringwald
3873*97dc5e69SMatthias Ringwald /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
3874*97dc5e69SMatthias Ringwald ret |= (op_mode == (O_RDONLY | O_WRONLY)) ? O_RDWR : op_mode;
3875*97dc5e69SMatthias Ringwald
3876*97dc5e69SMatthias Ringwald break;
3877*97dc5e69SMatthias Ringwald case F_SETFL:
3878*97dc5e69SMatthias Ringwald /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
3879*97dc5e69SMatthias Ringwald val &= ~(O_RDONLY | O_WRONLY | O_RDWR);
3880*97dc5e69SMatthias Ringwald if ((val & ~O_NONBLOCK) == 0) {
3881*97dc5e69SMatthias Ringwald /* only O_NONBLOCK, all other bits are zero */
3882*97dc5e69SMatthias Ringwald netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
3883*97dc5e69SMatthias Ringwald ret = 0;
3884*97dc5e69SMatthias Ringwald set_errno(0);
3885*97dc5e69SMatthias Ringwald } else {
3886*97dc5e69SMatthias Ringwald set_errno(ENOSYS); /* not yet implemented */
3887*97dc5e69SMatthias Ringwald }
3888*97dc5e69SMatthias Ringwald break;
3889*97dc5e69SMatthias Ringwald default:
3890*97dc5e69SMatthias Ringwald LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
3891*97dc5e69SMatthias Ringwald set_errno(ENOSYS); /* not yet implemented */
3892*97dc5e69SMatthias Ringwald break;
3893*97dc5e69SMatthias Ringwald }
3894*97dc5e69SMatthias Ringwald done_socket(sock);
3895*97dc5e69SMatthias Ringwald return ret;
3896*97dc5e69SMatthias Ringwald }
3897*97dc5e69SMatthias Ringwald
3898*97dc5e69SMatthias Ringwald #if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
3899*97dc5e69SMatthias Ringwald int
fcntl(int s,int cmd,...)3900*97dc5e69SMatthias Ringwald fcntl(int s, int cmd, ...)
3901*97dc5e69SMatthias Ringwald {
3902*97dc5e69SMatthias Ringwald va_list ap;
3903*97dc5e69SMatthias Ringwald int val;
3904*97dc5e69SMatthias Ringwald
3905*97dc5e69SMatthias Ringwald va_start(ap, cmd);
3906*97dc5e69SMatthias Ringwald val = va_arg(ap, int);
3907*97dc5e69SMatthias Ringwald va_end(ap);
3908*97dc5e69SMatthias Ringwald return lwip_fcntl(s, cmd, val);
3909*97dc5e69SMatthias Ringwald }
3910*97dc5e69SMatthias Ringwald #endif
3911*97dc5e69SMatthias Ringwald
3912*97dc5e69SMatthias Ringwald const char *
lwip_inet_ntop(int af,const void * src,char * dst,socklen_t size)3913*97dc5e69SMatthias Ringwald lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
3914*97dc5e69SMatthias Ringwald {
3915*97dc5e69SMatthias Ringwald const char *ret = NULL;
3916*97dc5e69SMatthias Ringwald int size_int = (int)size;
3917*97dc5e69SMatthias Ringwald if (size_int < 0) {
3918*97dc5e69SMatthias Ringwald set_errno(ENOSPC);
3919*97dc5e69SMatthias Ringwald return NULL;
3920*97dc5e69SMatthias Ringwald }
3921*97dc5e69SMatthias Ringwald switch (af) {
3922*97dc5e69SMatthias Ringwald #if LWIP_IPV4
3923*97dc5e69SMatthias Ringwald case AF_INET:
3924*97dc5e69SMatthias Ringwald ret = ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
3925*97dc5e69SMatthias Ringwald if (ret == NULL) {
3926*97dc5e69SMatthias Ringwald set_errno(ENOSPC);
3927*97dc5e69SMatthias Ringwald }
3928*97dc5e69SMatthias Ringwald break;
3929*97dc5e69SMatthias Ringwald #endif
3930*97dc5e69SMatthias Ringwald #if LWIP_IPV6
3931*97dc5e69SMatthias Ringwald case AF_INET6:
3932*97dc5e69SMatthias Ringwald ret = ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
3933*97dc5e69SMatthias Ringwald if (ret == NULL) {
3934*97dc5e69SMatthias Ringwald set_errno(ENOSPC);
3935*97dc5e69SMatthias Ringwald }
3936*97dc5e69SMatthias Ringwald break;
3937*97dc5e69SMatthias Ringwald #endif
3938*97dc5e69SMatthias Ringwald default:
3939*97dc5e69SMatthias Ringwald set_errno(EAFNOSUPPORT);
3940*97dc5e69SMatthias Ringwald break;
3941*97dc5e69SMatthias Ringwald }
3942*97dc5e69SMatthias Ringwald return ret;
3943*97dc5e69SMatthias Ringwald }
3944*97dc5e69SMatthias Ringwald
3945*97dc5e69SMatthias Ringwald int
lwip_inet_pton(int af,const char * src,void * dst)3946*97dc5e69SMatthias Ringwald lwip_inet_pton(int af, const char *src, void *dst)
3947*97dc5e69SMatthias Ringwald {
3948*97dc5e69SMatthias Ringwald int err;
3949*97dc5e69SMatthias Ringwald switch (af) {
3950*97dc5e69SMatthias Ringwald #if LWIP_IPV4
3951*97dc5e69SMatthias Ringwald case AF_INET:
3952*97dc5e69SMatthias Ringwald err = ip4addr_aton(src, (ip4_addr_t *)dst);
3953*97dc5e69SMatthias Ringwald break;
3954*97dc5e69SMatthias Ringwald #endif
3955*97dc5e69SMatthias Ringwald #if LWIP_IPV6
3956*97dc5e69SMatthias Ringwald case AF_INET6: {
3957*97dc5e69SMatthias Ringwald /* convert into temporary variable since ip6_addr_t might be larger
3958*97dc5e69SMatthias Ringwald than in6_addr when scopes are enabled */
3959*97dc5e69SMatthias Ringwald ip6_addr_t addr;
3960*97dc5e69SMatthias Ringwald err = ip6addr_aton(src, &addr);
3961*97dc5e69SMatthias Ringwald if (err) {
3962*97dc5e69SMatthias Ringwald memcpy(dst, &addr.addr, sizeof(addr.addr));
3963*97dc5e69SMatthias Ringwald }
3964*97dc5e69SMatthias Ringwald break;
3965*97dc5e69SMatthias Ringwald }
3966*97dc5e69SMatthias Ringwald #endif
3967*97dc5e69SMatthias Ringwald default:
3968*97dc5e69SMatthias Ringwald err = -1;
3969*97dc5e69SMatthias Ringwald set_errno(EAFNOSUPPORT);
3970*97dc5e69SMatthias Ringwald break;
3971*97dc5e69SMatthias Ringwald }
3972*97dc5e69SMatthias Ringwald return err;
3973*97dc5e69SMatthias Ringwald }
3974*97dc5e69SMatthias Ringwald
3975*97dc5e69SMatthias Ringwald #if LWIP_IGMP
3976*97dc5e69SMatthias Ringwald /** Register a new IGMP membership. On socket close, the membership is dropped automatically.
3977*97dc5e69SMatthias Ringwald *
3978*97dc5e69SMatthias Ringwald * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
3979*97dc5e69SMatthias Ringwald *
3980*97dc5e69SMatthias Ringwald * @return 1 on success, 0 on failure
3981*97dc5e69SMatthias Ringwald */
3982*97dc5e69SMatthias Ringwald static int
lwip_socket_register_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)3983*97dc5e69SMatthias Ringwald lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
3984*97dc5e69SMatthias Ringwald {
3985*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
3986*97dc5e69SMatthias Ringwald int i;
3987*97dc5e69SMatthias Ringwald
3988*97dc5e69SMatthias Ringwald if (!sock) {
3989*97dc5e69SMatthias Ringwald return 0;
3990*97dc5e69SMatthias Ringwald }
3991*97dc5e69SMatthias Ringwald
3992*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
3993*97dc5e69SMatthias Ringwald if (socket_ipv4_multicast_memberships[i].sock == NULL) {
3994*97dc5e69SMatthias Ringwald socket_ipv4_multicast_memberships[i].sock = sock;
3995*97dc5e69SMatthias Ringwald ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr);
3996*97dc5e69SMatthias Ringwald ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr);
3997*97dc5e69SMatthias Ringwald done_socket(sock);
3998*97dc5e69SMatthias Ringwald return 1;
3999*97dc5e69SMatthias Ringwald }
4000*97dc5e69SMatthias Ringwald }
4001*97dc5e69SMatthias Ringwald done_socket(sock);
4002*97dc5e69SMatthias Ringwald return 0;
4003*97dc5e69SMatthias Ringwald }
4004*97dc5e69SMatthias Ringwald
4005*97dc5e69SMatthias Ringwald /** Unregister a previously registered membership. This prevents dropping the membership
4006*97dc5e69SMatthias Ringwald * on socket close.
4007*97dc5e69SMatthias Ringwald *
4008*97dc5e69SMatthias Ringwald * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4009*97dc5e69SMatthias Ringwald */
4010*97dc5e69SMatthias Ringwald static void
lwip_socket_unregister_membership(int s,const ip4_addr_t * if_addr,const ip4_addr_t * multi_addr)4011*97dc5e69SMatthias Ringwald lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr)
4012*97dc5e69SMatthias Ringwald {
4013*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
4014*97dc5e69SMatthias Ringwald int i;
4015*97dc5e69SMatthias Ringwald
4016*97dc5e69SMatthias Ringwald if (!sock) {
4017*97dc5e69SMatthias Ringwald return;
4018*97dc5e69SMatthias Ringwald }
4019*97dc5e69SMatthias Ringwald
4020*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4021*97dc5e69SMatthias Ringwald if ((socket_ipv4_multicast_memberships[i].sock == sock) &&
4022*97dc5e69SMatthias Ringwald ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) &&
4023*97dc5e69SMatthias Ringwald ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) {
4024*97dc5e69SMatthias Ringwald socket_ipv4_multicast_memberships[i].sock = NULL;
4025*97dc5e69SMatthias Ringwald ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
4026*97dc5e69SMatthias Ringwald ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
4027*97dc5e69SMatthias Ringwald break;
4028*97dc5e69SMatthias Ringwald }
4029*97dc5e69SMatthias Ringwald }
4030*97dc5e69SMatthias Ringwald done_socket(sock);
4031*97dc5e69SMatthias Ringwald }
4032*97dc5e69SMatthias Ringwald
4033*97dc5e69SMatthias Ringwald /** Drop all memberships of a socket that were not dropped explicitly via setsockopt.
4034*97dc5e69SMatthias Ringwald *
4035*97dc5e69SMatthias Ringwald * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
4036*97dc5e69SMatthias Ringwald */
4037*97dc5e69SMatthias Ringwald static void
lwip_socket_drop_registered_memberships(int s)4038*97dc5e69SMatthias Ringwald lwip_socket_drop_registered_memberships(int s)
4039*97dc5e69SMatthias Ringwald {
4040*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
4041*97dc5e69SMatthias Ringwald int i;
4042*97dc5e69SMatthias Ringwald
4043*97dc5e69SMatthias Ringwald if (!sock) {
4044*97dc5e69SMatthias Ringwald return;
4045*97dc5e69SMatthias Ringwald }
4046*97dc5e69SMatthias Ringwald
4047*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4048*97dc5e69SMatthias Ringwald if (socket_ipv4_multicast_memberships[i].sock == sock) {
4049*97dc5e69SMatthias Ringwald ip_addr_t multi_addr, if_addr;
4050*97dc5e69SMatthias Ringwald ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr);
4051*97dc5e69SMatthias Ringwald ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr);
4052*97dc5e69SMatthias Ringwald socket_ipv4_multicast_memberships[i].sock = NULL;
4053*97dc5e69SMatthias Ringwald ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr);
4054*97dc5e69SMatthias Ringwald ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr);
4055*97dc5e69SMatthias Ringwald
4056*97dc5e69SMatthias Ringwald netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE);
4057*97dc5e69SMatthias Ringwald }
4058*97dc5e69SMatthias Ringwald }
4059*97dc5e69SMatthias Ringwald done_socket(sock);
4060*97dc5e69SMatthias Ringwald }
4061*97dc5e69SMatthias Ringwald #endif /* LWIP_IGMP */
4062*97dc5e69SMatthias Ringwald
4063*97dc5e69SMatthias Ringwald #if LWIP_IPV6_MLD
4064*97dc5e69SMatthias Ringwald /** Register a new MLD6 membership. On socket close, the membership is dropped automatically.
4065*97dc5e69SMatthias Ringwald *
4066*97dc5e69SMatthias Ringwald * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4067*97dc5e69SMatthias Ringwald *
4068*97dc5e69SMatthias Ringwald * @return 1 on success, 0 on failure
4069*97dc5e69SMatthias Ringwald */
4070*97dc5e69SMatthias Ringwald static int
lwip_socket_register_mld6_membership(int s,unsigned int if_idx,const ip6_addr_t * multi_addr)4071*97dc5e69SMatthias Ringwald lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
4072*97dc5e69SMatthias Ringwald {
4073*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
4074*97dc5e69SMatthias Ringwald int i;
4075*97dc5e69SMatthias Ringwald
4076*97dc5e69SMatthias Ringwald if (!sock) {
4077*97dc5e69SMatthias Ringwald return 0;
4078*97dc5e69SMatthias Ringwald }
4079*97dc5e69SMatthias Ringwald
4080*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4081*97dc5e69SMatthias Ringwald if (socket_ipv6_multicast_memberships[i].sock == NULL) {
4082*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].sock = sock;
4083*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
4084*97dc5e69SMatthias Ringwald ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
4085*97dc5e69SMatthias Ringwald done_socket(sock);
4086*97dc5e69SMatthias Ringwald return 1;
4087*97dc5e69SMatthias Ringwald }
4088*97dc5e69SMatthias Ringwald }
4089*97dc5e69SMatthias Ringwald done_socket(sock);
4090*97dc5e69SMatthias Ringwald return 0;
4091*97dc5e69SMatthias Ringwald }
4092*97dc5e69SMatthias Ringwald
4093*97dc5e69SMatthias Ringwald /** Unregister a previously registered MLD6 membership. This prevents dropping the membership
4094*97dc5e69SMatthias Ringwald * on socket close.
4095*97dc5e69SMatthias Ringwald *
4096*97dc5e69SMatthias Ringwald * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK).
4097*97dc5e69SMatthias Ringwald */
4098*97dc5e69SMatthias Ringwald static void
lwip_socket_unregister_mld6_membership(int s,unsigned int if_idx,const ip6_addr_t * multi_addr)4099*97dc5e69SMatthias Ringwald lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr)
4100*97dc5e69SMatthias Ringwald {
4101*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
4102*97dc5e69SMatthias Ringwald int i;
4103*97dc5e69SMatthias Ringwald
4104*97dc5e69SMatthias Ringwald if (!sock) {
4105*97dc5e69SMatthias Ringwald return;
4106*97dc5e69SMatthias Ringwald }
4107*97dc5e69SMatthias Ringwald
4108*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4109*97dc5e69SMatthias Ringwald if ((socket_ipv6_multicast_memberships[i].sock == sock) &&
4110*97dc5e69SMatthias Ringwald (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
4111*97dc5e69SMatthias Ringwald ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
4112*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].sock = NULL;
4113*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
4114*97dc5e69SMatthias Ringwald ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
4115*97dc5e69SMatthias Ringwald break;
4116*97dc5e69SMatthias Ringwald }
4117*97dc5e69SMatthias Ringwald }
4118*97dc5e69SMatthias Ringwald done_socket(sock);
4119*97dc5e69SMatthias Ringwald }
4120*97dc5e69SMatthias Ringwald
4121*97dc5e69SMatthias Ringwald /** Drop all MLD6 memberships of a socket that were not dropped explicitly via setsockopt.
4122*97dc5e69SMatthias Ringwald *
4123*97dc5e69SMatthias Ringwald * ATTENTION: this function is NOT called from tcpip_thread (or under CORE_LOCK).
4124*97dc5e69SMatthias Ringwald */
4125*97dc5e69SMatthias Ringwald static void
lwip_socket_drop_registered_mld6_memberships(int s)4126*97dc5e69SMatthias Ringwald lwip_socket_drop_registered_mld6_memberships(int s)
4127*97dc5e69SMatthias Ringwald {
4128*97dc5e69SMatthias Ringwald struct lwip_sock *sock = get_socket(s);
4129*97dc5e69SMatthias Ringwald int i;
4130*97dc5e69SMatthias Ringwald
4131*97dc5e69SMatthias Ringwald if (!sock) {
4132*97dc5e69SMatthias Ringwald return;
4133*97dc5e69SMatthias Ringwald }
4134*97dc5e69SMatthias Ringwald
4135*97dc5e69SMatthias Ringwald for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) {
4136*97dc5e69SMatthias Ringwald if (socket_ipv6_multicast_memberships[i].sock == sock) {
4137*97dc5e69SMatthias Ringwald ip_addr_t multi_addr;
4138*97dc5e69SMatthias Ringwald u8_t if_idx;
4139*97dc5e69SMatthias Ringwald
4140*97dc5e69SMatthias Ringwald ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
4141*97dc5e69SMatthias Ringwald if_idx = socket_ipv6_multicast_memberships[i].if_idx;
4142*97dc5e69SMatthias Ringwald
4143*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].sock = NULL;
4144*97dc5e69SMatthias Ringwald socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
4145*97dc5e69SMatthias Ringwald ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
4146*97dc5e69SMatthias Ringwald
4147*97dc5e69SMatthias Ringwald netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
4148*97dc5e69SMatthias Ringwald }
4149*97dc5e69SMatthias Ringwald }
4150*97dc5e69SMatthias Ringwald done_socket(sock);
4151*97dc5e69SMatthias Ringwald }
4152*97dc5e69SMatthias Ringwald #endif /* LWIP_IPV6_MLD */
4153*97dc5e69SMatthias Ringwald
4154*97dc5e69SMatthias Ringwald #endif /* LWIP_SOCKET */
4155