]>
Commit | Line | Data |
---|---|---|
16a7e6cb | 1 | /* |
a1bbd0e1 | 2 | * Copyright (C) 2016-2019 Red Hat, Inc. All rights reserved. |
16a7e6cb FDN |
3 | * |
4 | * Author: Christine Caulfield <ccaulfie@redhat.com> | |
5 | * | |
dd52554d | 6 | * This software licensed under LGPL-2.0+ |
16a7e6cb FDN |
7 | */ |
8 | ||
e3e37ffd CC |
9 | #include "config.h" |
10 | ||
e3e37ffd CC |
11 | #include <string.h> |
12 | #include <unistd.h> | |
13 | #include <errno.h> | |
14 | #include <pthread.h> | |
e3e37ffd CC |
15 | #include <sys/types.h> |
16 | #include <sys/socket.h> | |
f762bb6e | 17 | #include <stdlib.h> |
e3e37ffd | 18 | |
f762bb6e | 19 | #include "compat.h" |
e3e37ffd | 20 | #include "host.h" |
eee77df6 | 21 | #include "links.h" |
d2333aab | 22 | #include "links_acl.h" |
0cb8e8ba | 23 | #include "links_acl_ip.h" |
e3e37ffd CC |
24 | #include "logging.h" |
25 | #include "common.h" | |
4ac6704d | 26 | #include "transport_common.h" |
82d1d36e | 27 | #include "threads_common.h" |
e3e37ffd | 28 | |
3c62178c FDN |
29 | #ifdef HAVE_NETINET_SCTP_H |
30 | #include <netinet/sctp.h> | |
4ac6704d | 31 | #include "transport_sctp.h" |
e3e37ffd | 32 | |
e3e37ffd | 33 | typedef struct sctp_handle_info { |
82d1d36e FDN |
34 | struct knet_list_head listen_links_list; |
35 | struct knet_list_head connect_links_list; | |
e3e37ffd | 36 | int connect_epollfd; |
82d1d36e | 37 | int connectsockfd[2]; |
e3e37ffd | 38 | int listen_epollfd; |
82d1d36e | 39 | int listensockfd[2]; |
e3e37ffd CC |
40 | pthread_t connect_thread; |
41 | pthread_t listen_thread; | |
57ebf58c FW |
42 | socklen_t event_subscribe_kernel_size; |
43 | char *event_subscribe_buffer; | |
e3e37ffd CC |
44 | } sctp_handle_info_t; |
45 | ||
82d1d36e FDN |
46 | /* |
47 | * use by fd_tracker data type | |
48 | */ | |
d56e3615 FDN |
49 | #define SCTP_NO_LINK_INFO 0 |
50 | #define SCTP_LISTENER_LINK_INFO 1 | |
51 | #define SCTP_ACCEPTED_LINK_INFO 2 | |
52 | #define SCTP_CONNECT_LINK_INFO 3 | |
82d1d36e FDN |
53 | |
54 | /* | |
55 | * this value is per listener | |
56 | */ | |
57 | #define MAX_ACCEPTED_SOCKS 256 | |
58 | ||
59 | typedef struct sctp_listen_link_info { | |
60 | struct knet_list_head list; | |
e3e37ffd CC |
61 | int listen_sock; |
62 | int accepted_socks[MAX_ACCEPTED_SOCKS]; | |
82d1d36e FDN |
63 | struct sockaddr_storage src_address; |
64 | int on_listener_epoll; | |
65 | int on_rx_epoll; | |
66 | } sctp_listen_link_info_t; | |
67 | ||
d56e3615 FDN |
68 | typedef struct sctp_accepted_link_info { |
69 | char mread_buf[KNET_DATABUFSIZE]; | |
70 | ssize_t mread_len; | |
71 | sctp_listen_link_info_t *link_info; | |
72 | } sctp_accepted_link_info_t ; | |
73 | ||
82d1d36e | 74 | typedef struct sctp_connect_link_info { |
b715c049 | 75 | struct knet_list_head list; |
82d1d36e FDN |
76 | sctp_listen_link_info_t *listener; |
77 | struct knet_link *link; | |
78 | struct sockaddr_storage dst_address; | |
79 | int connect_sock; | |
80 | int on_connected_epoll; | |
81 | int on_rx_epoll; | |
82 | int close_sock; | |
83 | } sctp_connect_link_info_t; | |
e3e37ffd | 84 | |
82d1d36e FDN |
85 | /* |
86 | * socket handling functions | |
87 | * | |
88 | * those functions do NOT perform locking. locking | |
89 | * should be handled in the right context from callers | |
90 | */ | |
91 | ||
92 | /* | |
93 | * sockets are removed from rx_epoll from callers | |
94 | * see also error handling functions | |
95 | */ | |
8a142f48 | 96 | static int _close_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link) |
e3e37ffd | 97 | { |
82d1d36e | 98 | int err = 0, savederrno = 0; |
8a142f48 | 99 | sctp_connect_link_info_t *info = kn_link->transport_link; |
82d1d36e FDN |
100 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
101 | struct epoll_event ev; | |
e3e37ffd | 102 | |
82d1d36e FDN |
103 | if (info->on_connected_epoll) { |
104 | memset(&ev, 0, sizeof(struct epoll_event)); | |
105 | ev.events = EPOLLOUT; | |
106 | ev.data.fd = info->connect_sock; | |
107 | if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) { | |
108 | savederrno = errno; | |
109 | err = -1; | |
110 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from the epoll pool: %s", | |
111 | strerror(errno)); | |
112 | goto exit_error; | |
113 | } | |
114 | info->on_connected_epoll = 0; | |
e3e37ffd CC |
115 | } |
116 | ||
82d1d36e FDN |
117 | exit_error: |
118 | if (info->connect_sock != -1) { | |
119 | if (_set_fd_tracker(knet_h, info->connect_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { | |
120 | savederrno = errno; | |
121 | err = -1; | |
122 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
123 | strerror(savederrno)); | |
124 | goto exit_error; | |
125 | } | |
126 | close(info->connect_sock); | |
127 | info->connect_sock = -1; | |
e3e37ffd CC |
128 | } |
129 | ||
82d1d36e FDN |
130 | errno = savederrno; |
131 | return err; | |
132 | } | |
133 | ||
134 | static int _enable_sctp_notifications(knet_handle_t knet_h, int sock, const char *type) | |
135 | { | |
136 | int err = 0, savederrno = 0; | |
57ebf58c FW |
137 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
138 | ||
139 | if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, | |
140 | handle_info->event_subscribe_buffer, | |
141 | handle_info->event_subscribe_kernel_size) < 0) { | |
e3e37ffd CC |
142 | savederrno = errno; |
143 | err = -1; | |
815929bf | 144 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to enable %s events: %s", |
e3e37ffd | 145 | type, strerror(savederrno)); |
e3e37ffd CC |
146 | } |
147 | ||
82d1d36e | 148 | errno = savederrno; |
e3e37ffd CC |
149 | return err; |
150 | } | |
151 | ||
c58507f9 | 152 | static int _configure_sctp_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, uint64_t flags, const char *type) |
e3e37ffd | 153 | { |
82d1d36e FDN |
154 | int err = 0, savederrno = 0; |
155 | int value; | |
f762bb6e CC |
156 | int level; |
157 | ||
158 | #ifdef SOL_SCTP | |
159 | level = SOL_SCTP; | |
160 | #else | |
161 | level = IPPROTO_SCTP; | |
162 | #endif | |
e3e37ffd | 163 | |
c58507f9 | 164 | if (_configure_transport_socket(knet_h, sock, address, flags, type) < 0) { |
82d1d36e FDN |
165 | savederrno = errno; |
166 | err = -1; | |
167 | goto exit_error; | |
e3e37ffd CC |
168 | } |
169 | ||
82d1d36e | 170 | value = 1; |
f762bb6e | 171 | if (setsockopt(sock, level, SCTP_NODELAY, &value, sizeof(value)) < 0) { |
82d1d36e FDN |
172 | savederrno = errno; |
173 | err = -1; | |
174 | log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set sctp nodelay: %s", | |
175 | strerror(savederrno)); | |
176 | goto exit_error; | |
e3e37ffd CC |
177 | } |
178 | ||
82d1d36e FDN |
179 | if (_enable_sctp_notifications(knet_h, sock, type) < 0) { |
180 | savederrno = errno; | |
181 | err = -1; | |
769deace FDN |
182 | } |
183 | ||
82d1d36e FDN |
184 | exit_error: |
185 | errno = savederrno; | |
186 | return err; | |
e3e37ffd CC |
187 | } |
188 | ||
940833bf | 189 | static int _reconnect_socket(knet_handle_t knet_h, struct knet_link *kn_link) |
e3e37ffd | 190 | { |
82d1d36e | 191 | int err = 0, savederrno = 0; |
940833bf | 192 | sctp_connect_link_info_t *info = kn_link->transport_link; |
82d1d36e | 193 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
e3e37ffd | 194 | struct epoll_event ev; |
e3e37ffd | 195 | |
940833bf | 196 | if (connect(info->connect_sock, (struct sockaddr *)&kn_link->dst_addr, sockaddr_len(&kn_link->dst_addr)) < 0) { |
82d1d36e | 197 | if ((errno != EALREADY) && (errno != EINPROGRESS) && (errno != EISCONN)) { |
e3e37ffd | 198 | savederrno = errno; |
82d1d36e FDN |
199 | err = -1; |
200 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to connect SCTP socket %d: %s", | |
201 | info->connect_sock, strerror(savederrno)); | |
e3e37ffd CC |
202 | goto exit_error; |
203 | } | |
e3e37ffd CC |
204 | } |
205 | ||
82d1d36e FDN |
206 | if (!info->on_connected_epoll) { |
207 | memset(&ev, 0, sizeof(struct epoll_event)); | |
208 | ev.events = EPOLLOUT; | |
209 | ev.data.fd = info->connect_sock; | |
210 | if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, info->connect_sock, &ev)) { | |
e3e37ffd | 211 | savederrno = errno; |
82d1d36e FDN |
212 | err = -1; |
213 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add send/recv to epoll pool: %s", | |
e3e37ffd CC |
214 | strerror(savederrno)); |
215 | goto exit_error; | |
216 | } | |
82d1d36e | 217 | info->on_connected_epoll = 1; |
e3e37ffd CC |
218 | } |
219 | ||
82d1d36e FDN |
220 | exit_error: |
221 | errno = savederrno; | |
222 | return err; | |
223 | } | |
224 | ||
940833bf | 225 | static int _create_connect_socket(knet_handle_t knet_h, struct knet_link *kn_link) |
82d1d36e FDN |
226 | { |
227 | int err = 0, savederrno = 0; | |
940833bf | 228 | sctp_connect_link_info_t *info = kn_link->transport_link; |
82d1d36e FDN |
229 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
230 | struct epoll_event ev; | |
231 | int connect_sock; | |
232 | ||
940833bf | 233 | connect_sock = socket(kn_link->dst_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP); |
82d1d36e | 234 | if (connect_sock < 0) { |
e3e37ffd | 235 | savederrno = errno; |
82d1d36e FDN |
236 | err = -1; |
237 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create send/recv socket: %s", | |
e3e37ffd CC |
238 | strerror(savederrno)); |
239 | goto exit_error; | |
240 | } | |
241 | ||
c58507f9 | 242 | if (_configure_sctp_socket(knet_h, connect_sock, &kn_link->dst_addr, kn_link->flags, "SCTP connect") < 0) { |
82d1d36e FDN |
243 | savederrno = errno; |
244 | err = -1; | |
245 | goto exit_error; | |
246 | } | |
247 | ||
248 | if (_set_fd_tracker(knet_h, connect_sock, KNET_TRANSPORT_SCTP, SCTP_CONNECT_LINK_INFO, info) < 0) { | |
249 | savederrno = errno; | |
250 | err = -1; | |
251 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
252 | strerror(savederrno)); | |
253 | goto exit_error; | |
254 | } | |
b715c049 | 255 | |
82d1d36e FDN |
256 | info->connect_sock = connect_sock; |
257 | info->close_sock = 0; | |
940833bf | 258 | if (_reconnect_socket(knet_h, kn_link) < 0) { |
82d1d36e FDN |
259 | savederrno = errno; |
260 | err = -1; | |
261 | goto exit_error; | |
262 | } | |
e3e37ffd | 263 | |
e3e37ffd | 264 | exit_error: |
82d1d36e FDN |
265 | if (err) { |
266 | if (info->on_connected_epoll) { | |
267 | epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev); | |
268 | } | |
269 | if (connect_sock >= 0) { | |
270 | close(connect_sock); | |
271 | } | |
272 | } | |
273 | errno = savederrno; | |
274 | return err; | |
e3e37ffd CC |
275 | } |
276 | ||
4ac6704d | 277 | int sctp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) |
22d0a556 | 278 | { |
6a6d337d FDN |
279 | sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data; |
280 | sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data; | |
281 | sctp_listen_link_info_t *listen_info; | |
282 | ||
22d0a556 | 283 | if (recv_err < 0) { |
6a6d337d FDN |
284 | switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) { |
285 | case SCTP_CONNECT_LINK_INFO: | |
286 | if (connect_info->link->transport_connected == 0) { | |
287 | return -1; | |
288 | } | |
289 | break; | |
290 | case SCTP_ACCEPTED_LINK_INFO: | |
291 | listen_info = accepted_info->link_info; | |
292 | if (listen_info->listen_sock != sockfd) { | |
293 | if (listen_info->on_rx_epoll == 0) { | |
294 | return -1; | |
295 | } | |
296 | } | |
297 | break; | |
298 | } | |
22d0a556 | 299 | if (recv_errno == EAGAIN) { |
63ceb592 | 300 | #ifdef DEBUG |
22d0a556 | 301 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Sock: %d is overloaded. Slowing TX down", sockfd); |
63ceb592 | 302 | #endif |
c49af4e3 CC |
303 | /* Don't hold onto the lock while sleeping */ |
304 | pthread_rwlock_unlock(&knet_h->global_rwlock); | |
63ceb592 | 305 | usleep(KNET_THREADS_TIMERES / 16); |
c49af4e3 | 306 | pthread_rwlock_rdlock(&knet_h->global_rwlock); |
22d0a556 FDN |
307 | return 1; |
308 | } | |
309 | return -1; | |
310 | } | |
311 | return 0; | |
312 | } | |
313 | ||
82d1d36e FDN |
314 | /* |
315 | * socket error management functions | |
316 | * | |
317 | * both called with global read lock. | |
318 | * | |
319 | * NOTE: we need to remove the fd from the epoll as soon as possible | |
320 | * even before we notify the respective thread to take care of it | |
321 | * because scheduling can make it so that this thread will overload | |
322 | * and the threads supposed to take care of the error will never | |
323 | * be able to take action. | |
324 | * we CANNOT handle FDs here diretly (close/reconnect/etc) due | |
325 | * to locking context. We need to delegate that to their respective | |
326 | * management threads within global write lock. | |
327 | * | |
328 | * this function is called from: | |
329 | * - RX thread with recv_err <= 0 directly on recvmmsg error | |
330 | * - transport_rx_is_data when msg_len == 0 (recv_err = 1) | |
331 | * - transport_rx_is_data on notification (recv_err = 2) | |
332 | * | |
333 | * basically this small abouse of recv_err is to detect notifications | |
334 | * generated by sockets created by listen(). | |
335 | */ | |
4ac6704d | 336 | int sctp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno) |
e3e37ffd | 337 | { |
e3e37ffd | 338 | struct epoll_event ev; |
82d1d36e | 339 | sctp_connect_link_info_t *connect_info = knet_h->knet_transport_fd_tracker[sockfd].data; |
d56e3615 FDN |
340 | sctp_accepted_link_info_t *accepted_info = knet_h->knet_transport_fd_tracker[sockfd].data; |
341 | sctp_listen_link_info_t *listen_info; | |
82d1d36e FDN |
342 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
343 | ||
344 | switch (knet_h->knet_transport_fd_tracker[sockfd].data_type) { | |
345 | case SCTP_CONNECT_LINK_INFO: | |
346 | /* | |
347 | * all connect link have notifications enabled | |
348 | * and we accept only data from notification and | |
349 | * generic recvmmsg errors. | |
350 | * | |
351 | * Errors generated by msg_len 0 can be ignored because | |
352 | * they follow a notification (double notification) | |
353 | */ | |
354 | if (recv_err != 1) { | |
355 | connect_info->link->transport_connected = 0; | |
356 | if (connect_info->on_rx_epoll) { | |
357 | memset(&ev, 0, sizeof(struct epoll_event)); | |
358 | ev.events = EPOLLIN; | |
359 | ev.data.fd = sockfd; | |
360 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) { | |
361 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", | |
362 | strerror(errno)); | |
363 | return -1; | |
364 | } | |
365 | connect_info->on_rx_epoll = 0; | |
366 | } | |
367 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying connect thread that sockfd %d received an error", sockfd); | |
368 | if (sendto(handle_info->connectsockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) { | |
369 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify connect thread: %s", strerror(errno)); | |
370 | } | |
371 | } | |
372 | break; | |
d56e3615 FDN |
373 | case SCTP_ACCEPTED_LINK_INFO: |
374 | listen_info = accepted_info->link_info; | |
82d1d36e FDN |
375 | if (listen_info->listen_sock != sockfd) { |
376 | if (recv_err != 1) { | |
377 | if (listen_info->on_rx_epoll) { | |
378 | memset(&ev, 0, sizeof(struct epoll_event)); | |
379 | ev.events = EPOLLIN; | |
380 | ev.data.fd = sockfd; | |
381 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sockfd, &ev)) { | |
382 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", | |
383 | strerror(errno)); | |
384 | return -1; | |
385 | } | |
386 | listen_info->on_rx_epoll = 0; | |
387 | } | |
388 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Notifying listen thread that sockfd %d received an error", sockfd); | |
389 | if (sendto(handle_info->listensockfd[1], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(int)) { | |
390 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to notify listen thread: %s", strerror(errno)); | |
391 | } | |
392 | } | |
393 | } else { | |
394 | /* | |
395 | * this means the listen() socket has generated | |
396 | * a notification. now what? :-) | |
397 | */ | |
398 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen() socket %d", sockfd); | |
399 | } | |
400 | break; | |
401 | default: | |
45a6124b | 402 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received unknown notification? %d", sockfd); |
82d1d36e FDN |
403 | break; |
404 | } | |
45a6124b FDN |
405 | /* |
406 | * Under RX pressure we need to give time to IPC to pick up the message | |
407 | */ | |
c49af4e3 CC |
408 | |
409 | /* Don't hold onto the lock while sleeping */ | |
410 | pthread_rwlock_unlock(&knet_h->global_rwlock); | |
45a6124b | 411 | usleep(KNET_THREADS_TIMERES / 2); |
c49af4e3 | 412 | pthread_rwlock_rdlock(&knet_h->global_rwlock); |
82d1d36e FDN |
413 | return 0; |
414 | } | |
415 | ||
416 | /* | |
417 | * NOTE: sctp_transport_rx_is_data is called with global rdlock | |
418 | * delegate any FD error management to sctp_transport_rx_sock_error | |
419 | * and keep this code to parsing incoming data only | |
420 | */ | |
4ac6704d | 421 | int sctp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg) |
82d1d36e | 422 | { |
801bb12d | 423 | size_t i; |
d56e3615 FDN |
424 | struct iovec *iov = msg->msg_hdr.msg_iov; |
425 | size_t iovlen = msg->msg_hdr.msg_iovlen; | |
82d1d36e FDN |
426 | struct sctp_assoc_change *sac; |
427 | union sctp_notification *snp; | |
d56e3615 | 428 | sctp_accepted_link_info_t *info = knet_h->knet_transport_fd_tracker[sockfd].data; |
82d1d36e | 429 | |
d56e3615 FDN |
430 | if (!(msg->msg_hdr.msg_flags & MSG_NOTIFICATION)) { |
431 | if (msg->msg_len == 0) { | |
82d1d36e FDN |
432 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "received 0 bytes len packet: %d", sockfd); |
433 | /* | |
434 | * NOTE: with event notification enabled, we receive error twice: | |
435 | * 1) from the event notification | |
436 | * 2) followed by a 0 byte msg_len | |
437 | * | |
438 | * This is generally not a problem if not for causing extra | |
439 | * handling for the same issue. Should we drop notifications | |
440 | * and keep the code generic (handle all errors via msg_len = 0) | |
441 | * or keep the duplication as safety measure, or drop msg_len = 0 | |
442 | * handling (what about sockets without events enabled?) | |
443 | */ | |
444 | sctp_transport_rx_sock_error(knet_h, sockfd, 1, 0); | |
445 | return 1; | |
446 | } | |
d56e3615 FDN |
447 | /* |
448 | * missing MSG_EOR has to be treated as a short read | |
449 | * from the socket and we need to fill in the mread buf | |
450 | * while we wait for MSG_EOR | |
451 | */ | |
452 | if (!(msg->msg_hdr.msg_flags & MSG_EOR)) { | |
453 | /* | |
454 | * copy the incoming data into mread_buf + mread_len (incremental) | |
455 | * and increase mread_len | |
456 | */ | |
457 | memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len); | |
458 | info->mread_len = info->mread_len + msg->msg_len; | |
459 | return 0; | |
460 | } | |
461 | /* | |
462 | * got EOR. | |
463 | * if mread_len is > 0 we are completing a packet from short reads | |
464 | * complete reassembling the packet in mread_buf, copy it back in the iov | |
465 | * and set the iov/msg len numbers (size) correctly | |
466 | */ | |
467 | if (info->mread_len) { | |
468 | /* | |
469 | * add last fragment to mread_buf | |
470 | */ | |
471 | memmove(info->mread_buf + info->mread_len, iov->iov_base, msg->msg_len); | |
472 | info->mread_len = info->mread_len + msg->msg_len; | |
473 | /* | |
474 | * move all back into the iovec | |
475 | */ | |
476 | memmove(iov->iov_base, info->mread_buf, info->mread_len); | |
477 | msg->msg_len = info->mread_len; | |
478 | info->mread_len = 0; | |
479 | } | |
82d1d36e FDN |
480 | return 2; |
481 | } | |
482 | ||
d56e3615 | 483 | if (!(msg->msg_hdr.msg_flags & MSG_EOR)) { |
82d1d36e FDN |
484 | return 1; |
485 | } | |
486 | ||
487 | for (i=0; i< iovlen; i++) { | |
488 | snp = iov[i].iov_base; | |
489 | ||
490 | switch (snp->sn_header.sn_type) { | |
491 | case SCTP_ASSOC_CHANGE: | |
492 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change"); | |
493 | sac = &snp->sn_assoc_change; | |
494 | if (sac->sac_state == SCTP_COMM_LOST) { | |
495 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp assoc change: comm_lost"); | |
496 | sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0); | |
497 | } | |
498 | break; | |
499 | case SCTP_SHUTDOWN_EVENT: | |
500 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp shutdown event"); | |
501 | sctp_transport_rx_sock_error(knet_h, sockfd, 2, 0); | |
502 | break; | |
503 | case SCTP_SEND_FAILED: | |
504 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp send failed"); | |
505 | break; | |
506 | case SCTP_PEER_ADDR_CHANGE: | |
507 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp peer addr change"); | |
508 | break; | |
509 | case SCTP_REMOTE_ERROR: | |
510 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] sctp remote error"); | |
511 | break; | |
512 | default: | |
513 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "[event] unknown sctp event type: %hu\n", snp->sn_header.sn_type); | |
514 | break; | |
515 | } | |
516 | } | |
517 | return 0; | |
518 | } | |
519 | ||
520 | /* | |
521 | * connect / outgoing socket management thread | |
522 | */ | |
523 | ||
524 | /* | |
525 | * _handle_connected_sctp* are called with a global write lock | |
526 | * from the connect_thread | |
527 | */ | |
528 | static void _handle_connected_sctp(knet_handle_t knet_h, int connect_sock) | |
529 | { | |
e3e37ffd | 530 | int err; |
82d1d36e | 531 | struct epoll_event ev; |
e3e37ffd | 532 | unsigned int status, len = sizeof(status); |
82d1d36e FDN |
533 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; |
534 | sctp_connect_link_info_t *info = knet_h->knet_transport_fd_tracker[connect_sock].data; | |
940833bf | 535 | struct knet_link *kn_link = info->link; |
e3e37ffd | 536 | |
82d1d36e FDN |
537 | err = getsockopt(connect_sock, SOL_SOCKET, SO_ERROR, &status, &len); |
538 | if (err) { | |
539 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP getsockopt() on connecting socket %d failed: %s", | |
540 | connect_sock, strerror(errno)); | |
541 | return; | |
542 | } | |
e3e37ffd | 543 | |
82d1d36e | 544 | if (info->close_sock) { |
940833bf | 545 | if (_close_connect_socket(knet_h, kn_link) < 0) { |
82d1d36e FDN |
546 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close sock %d from _handle_connected_sctp: %s", connect_sock, strerror(errno)); |
547 | return; | |
e3e37ffd | 548 | } |
82d1d36e | 549 | info->close_sock = 0; |
940833bf | 550 | if (_create_connect_socket(knet_h, kn_link) < 0) { |
82d1d36e FDN |
551 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to recreate connecting sock! %s", strerror(errno)); |
552 | return; | |
e3e37ffd | 553 | } |
82d1d36e FDN |
554 | } |
555 | ||
556 | if (status) { | |
557 | log_info(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect on %d to %s port %s failed: %s", | |
940833bf | 558 | connect_sock, kn_link->status.dst_ipaddr, kn_link->status.dst_port, |
82d1d36e FDN |
559 | strerror(status)); |
560 | ||
561 | /* | |
562 | * No need to create a new socket if connect failed, | |
563 | * just retry connect | |
564 | */ | |
565 | _reconnect_socket(knet_h, info->link); | |
e3e37ffd CC |
566 | return; |
567 | } | |
568 | ||
82d1d36e FDN |
569 | /* |
570 | * Connected - Remove us from the connect epoll | |
571 | */ | |
572 | memset(&ev, 0, sizeof(struct epoll_event)); | |
e3e37ffd | 573 | ev.events = EPOLLOUT; |
82d1d36e FDN |
574 | ev.data.fd = connect_sock; |
575 | if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, connect_sock, &ev)) { | |
815929bf | 576 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket %d from epoll pool: %s", |
82d1d36e | 577 | connect_sock, strerror(errno)); |
e3e37ffd | 578 | } |
82d1d36e FDN |
579 | info->on_connected_epoll = 0; |
580 | ||
940833bf CC |
581 | kn_link->transport_connected = 1; |
582 | kn_link->outsock = info->connect_sock; | |
e3e37ffd | 583 | |
82d1d36e | 584 | memset(&ev, 0, sizeof(struct epoll_event)); |
d26576a8 | 585 | ev.events = EPOLLIN; |
82d1d36e FDN |
586 | ev.data.fd = connect_sock; |
587 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, connect_sock, &ev)) { | |
815929bf | 588 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connected socket to epoll pool: %s", |
d26576a8 | 589 | strerror(errno)); |
e3e37ffd | 590 | } |
82d1d36e | 591 | info->on_rx_epoll = 1; |
b715c049 | 592 | |
82d1d36e FDN |
593 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP handler fd %d now connected to %s port %s", |
594 | connect_sock, | |
940833bf | 595 | kn_link->status.dst_ipaddr, kn_link->status.dst_port); |
e3e37ffd CC |
596 | } |
597 | ||
82d1d36e | 598 | static void _handle_connected_sctp_errors(knet_handle_t knet_h) |
e3e37ffd | 599 | { |
82d1d36e FDN |
600 | int sockfd = -1; |
601 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
602 | sctp_connect_link_info_t *info; | |
e3e37ffd | 603 | |
82d1d36e FDN |
604 | if (recv(handle_info->connectsockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) { |
605 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on connectsockfd"); | |
606 | return; | |
607 | } | |
e3e37ffd | 608 | |
82d1d36e FDN |
609 | if (_is_valid_fd(knet_h, sockfd) < 1) { |
610 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for connected socket fd error"); | |
611 | return; | |
612 | } | |
e3e37ffd | 613 | |
82d1d36e | 614 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing connected error on socket: %d", sockfd); |
e3e37ffd | 615 | |
82d1d36e | 616 | info = knet_h->knet_transport_fd_tracker[sockfd].data; |
e3e37ffd | 617 | |
82d1d36e FDN |
618 | info->close_sock = 1; |
619 | info->link->transport_connected = 0; | |
620 | _reconnect_socket(knet_h, info->link); | |
621 | } | |
e3e37ffd CC |
622 | |
623 | static void *_sctp_connect_thread(void *data) | |
624 | { | |
82d1d36e | 625 | int savederrno; |
e3e37ffd | 626 | int i, nev; |
82d1d36e FDN |
627 | knet_handle_t knet_h = (knet_handle_t) data; |
628 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
e3e37ffd CC |
629 | struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; |
630 | ||
6394d892 | 631 | set_thread_status(knet_h, KNET_THREAD_SCTP_CONN, KNET_THREAD_STARTED); |
aed6b5db | 632 | |
82d1d36e | 633 | while (!shutdown_in_progress(knet_h)) { |
aed6b5db FDN |
634 | nev = epoll_wait(handle_info->connect_epollfd, events, KNET_EPOLL_MAX_EVENTS, KNET_THREADS_TIMERES / 1000); |
635 | ||
636 | /* | |
637 | * we use timeout to detect if thread is shutting down | |
638 | */ | |
639 | if (nev == 0) { | |
640 | continue; | |
641 | } | |
e3e37ffd | 642 | |
82d1d36e FDN |
643 | if (nev < 0) { |
644 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect handler EPOLL ERROR: %s", | |
645 | strerror(errno)); | |
646 | continue; | |
e3e37ffd CC |
647 | } |
648 | ||
82d1d36e FDN |
649 | /* |
650 | * Sort out which FD has a connection | |
651 | */ | |
1dfc8220 | 652 | savederrno = get_global_wrlock(knet_h); |
82d1d36e FDN |
653 | if (savederrno) { |
654 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s", | |
655 | strerror(savederrno)); | |
e3e37ffd CC |
656 | continue; |
657 | } | |
658 | ||
82d1d36e FDN |
659 | /* |
660 | * minor optimization: deduplicate events | |
661 | * | |
662 | * in some cases we can receive multiple notifcations | |
663 | * of the same FD having issues or need handling. | |
664 | * It's enough to process it once even tho it's safe | |
665 | * to handle them multiple times. | |
666 | */ | |
e3e37ffd | 667 | for (i = 0; i < nev; i++) { |
82d1d36e FDN |
668 | if (events[i].data.fd == handle_info->connectsockfd[0]) { |
669 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for connected socket"); | |
670 | _handle_connected_sctp_errors(knet_h); | |
671 | } else { | |
672 | if (_is_valid_fd(knet_h, events[i].data.fd) == 1) { | |
673 | _handle_connected_sctp(knet_h, events[i].data.fd); | |
674 | } else { | |
675 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for dead fd %d\n", events[i].data.fd); | |
676 | } | |
677 | } | |
e3e37ffd | 678 | } |
82d1d36e FDN |
679 | pthread_rwlock_unlock(&knet_h->global_rwlock); |
680 | /* | |
681 | * this thread can generate events for itself. | |
682 | * we need to sleep in between loops to allow other threads | |
683 | * to be scheduled | |
684 | */ | |
047e0362 | 685 | usleep(knet_h->reconnect_int * 1000); |
e3e37ffd | 686 | } |
aed6b5db FDN |
687 | |
688 | set_thread_status(knet_h, KNET_THREAD_SCTP_CONN, KNET_THREAD_STOPPED); | |
689 | ||
e3e37ffd CC |
690 | return NULL; |
691 | } | |
692 | ||
693 | /* | |
82d1d36e FDN |
694 | * listen/incoming connections management thread |
695 | */ | |
696 | ||
697 | /* | |
698 | * Listener received a new connection | |
699 | * called with a write lock from main thread | |
e3e37ffd | 700 | */ |
82d1d36e | 701 | static void _handle_incoming_sctp(knet_handle_t knet_h, int listen_sock) |
e3e37ffd | 702 | { |
82d1d36e FDN |
703 | int err = 0, savederrno = 0; |
704 | int new_fd; | |
705 | int i = -1; | |
706 | sctp_listen_link_info_t *info = knet_h->knet_transport_fd_tracker[listen_sock].data; | |
707 | struct epoll_event ev; | |
708 | struct sockaddr_storage ss; | |
709 | socklen_t sock_len = sizeof(ss); | |
762c0098 FDN |
710 | char addr_str[KNET_MAX_HOST_LEN]; |
711 | char port_str[KNET_MAX_PORT_LEN]; | |
d56e3615 | 712 | sctp_accepted_link_info_t *accept_info = NULL; |
e3e37ffd | 713 | |
82d1d36e FDN |
714 | new_fd = accept(listen_sock, (struct sockaddr *)&ss, &sock_len); |
715 | if (new_fd < 0) { | |
716 | savederrno = errno; | |
717 | err = -1; | |
718 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accept error: %s", strerror(errno)); | |
719 | goto exit_error; | |
e3e37ffd CC |
720 | } |
721 | ||
762c0098 FDN |
722 | if (knet_addrtostr(&ss, sizeof(ss), |
723 | addr_str, KNET_MAX_HOST_LEN, | |
724 | port_str, KNET_MAX_PORT_LEN) < 0) { | |
82d1d36e FDN |
725 | savederrno = errno; |
726 | err = -1; | |
727 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to gather socket info"); | |
728 | goto exit_error; | |
729 | } | |
e3e37ffd | 730 | |
82d1d36e | 731 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: received connection from: %s port: %s", |
762c0098 | 732 | addr_str, port_str); |
d2333aab | 733 | if (knet_h->use_access_lists) { |
fb462d4f | 734 | if (!check_validate(knet_h, listen_sock, KNET_TRANSPORT_SCTP, &ss)) { |
d2333aab | 735 | savederrno = EINVAL; |
d2333aab FDN |
736 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Connection rejected from %s/%s", addr_str, port_str); |
737 | close(new_fd); | |
0cb8e8ba FDN |
738 | errno = savederrno; |
739 | return; | |
d2333aab FDN |
740 | } |
741 | } | |
e3e37ffd | 742 | |
82d1d36e FDN |
743 | /* |
744 | * Keep a track of all accepted FDs | |
745 | */ | |
746 | for (i=0; i<MAX_ACCEPTED_SOCKS; i++) { | |
747 | if (info->accepted_socks[i] == -1) { | |
748 | info->accepted_socks[i] = new_fd; | |
749 | break; | |
750 | } | |
751 | } | |
e3e37ffd | 752 | |
82d1d36e FDN |
753 | if (i == MAX_ACCEPTED_SOCKS) { |
754 | errno = EBUSY; | |
755 | err = -1; | |
756 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: too many connections!"); | |
757 | goto exit_error; | |
758 | } | |
e3e37ffd | 759 | |
c58507f9 | 760 | if (_configure_common_socket(knet_h, new_fd, 0, "SCTP incoming") < 0) { /* Inherit flags from listener? */ |
82d1d36e FDN |
761 | savederrno = errno; |
762 | err = -1; | |
763 | goto exit_error; | |
764 | } | |
765 | ||
766 | if (_enable_sctp_notifications(knet_h, new_fd, "Incoming connection") < 0) { | |
767 | savederrno = errno; | |
768 | err = -1; | |
769 | goto exit_error; | |
770 | } | |
e3e37ffd | 771 | |
d56e3615 FDN |
772 | accept_info = malloc(sizeof(sctp_accepted_link_info_t)); |
773 | if (!accept_info) { | |
774 | savederrno = errno; | |
775 | err = -1; | |
776 | goto exit_error; | |
777 | } | |
778 | memset(accept_info, 0, sizeof(sctp_accepted_link_info_t)); | |
779 | ||
780 | accept_info->link_info = info; | |
781 | ||
782 | if (_set_fd_tracker(knet_h, new_fd, KNET_TRANSPORT_SCTP, SCTP_ACCEPTED_LINK_INFO, accept_info) < 0) { | |
82d1d36e FDN |
783 | savederrno = errno; |
784 | err = -1; | |
785 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
786 | strerror(errno)); | |
787 | goto exit_error; | |
788 | } | |
e3e37ffd | 789 | |
82d1d36e FDN |
790 | memset(&ev, 0, sizeof(struct epoll_event)); |
791 | ev.events = EPOLLIN; | |
792 | ev.data.fd = new_fd; | |
793 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, new_fd, &ev)) { | |
794 | savederrno = errno; | |
795 | err = -1; | |
796 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to add accepted socket %d to epoll pool: %s", | |
797 | new_fd, strerror(errno)); | |
798 | goto exit_error; | |
799 | } | |
800 | info->on_rx_epoll = 1; | |
e3e37ffd | 801 | |
762c0098 FDN |
802 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accepted new fd %d for %s/%s (listen fd: %d). index: %d", |
803 | new_fd, addr_str, port_str, info->listen_sock, i); | |
82d1d36e FDN |
804 | |
805 | exit_error: | |
82d1d36e FDN |
806 | if (err) { |
807 | if ((i >= 0) || (i < MAX_ACCEPTED_SOCKS)) { | |
808 | info->accepted_socks[i] = -1; | |
e3e37ffd | 809 | } |
82d1d36e | 810 | _set_fd_tracker(knet_h, new_fd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL); |
d56e3615 | 811 | free(accept_info); |
82d1d36e | 812 | close(new_fd); |
e3e37ffd | 813 | } |
82d1d36e FDN |
814 | errno = savederrno; |
815 | return; | |
e3e37ffd CC |
816 | } |
817 | ||
82d1d36e FDN |
818 | /* |
819 | * Listen thread received a notification of a bad socket that needs closing | |
820 | * called with a write lock from main thread | |
821 | */ | |
822 | static void _handle_listen_sctp_errors(knet_handle_t knet_h) | |
e3e37ffd | 823 | { |
82d1d36e FDN |
824 | int sockfd = -1; |
825 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
d56e3615 | 826 | sctp_accepted_link_info_t *accept_info; |
82d1d36e | 827 | sctp_listen_link_info_t *info; |
f66ebfd6 FDN |
828 | struct knet_host *host; |
829 | int link_idx; | |
82d1d36e | 830 | int i; |
e3e37ffd | 831 | |
82d1d36e FDN |
832 | if (recv(handle_info->listensockfd[0], &sockfd, sizeof(int), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(int)) { |
833 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Short read on listensockfd"); | |
834 | return; | |
e3e37ffd | 835 | } |
e3e37ffd | 836 | |
82d1d36e FDN |
837 | if (_is_valid_fd(knet_h, sockfd) < 1) { |
838 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received stray notification for listen socket fd error"); | |
839 | return; | |
840 | } | |
e3e37ffd | 841 | |
82d1d36e | 842 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Processing listen error on socket: %d", sockfd); |
e3e37ffd | 843 | |
d56e3615 FDN |
844 | accept_info = knet_h->knet_transport_fd_tracker[sockfd].data; |
845 | info = accept_info->link_info; | |
e3e37ffd | 846 | |
f66ebfd6 FDN |
847 | /* |
848 | * clear all links using this accepted socket as | |
849 | * outbound dynamically connected socket | |
850 | */ | |
851 | ||
852 | for (host = knet_h->host_head; host != NULL; host = host->next) { | |
853 | for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { | |
854 | if ((host->link[link_idx].dynamic == KNET_LINK_DYNIP) && | |
855 | (host->link[link_idx].outsock == sockfd)) { | |
856 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Found dynamic connection on host %d link %d (%d)", | |
857 | host->host_id, link_idx, sockfd); | |
858 | host->link[link_idx].status.dynconnected = 0; | |
859 | host->link[link_idx].transport_connected = 0; | |
860 | host->link[link_idx].outsock = 0; | |
861 | memset(&host->link[link_idx].dst_addr, 0, sizeof(struct sockaddr_storage)); | |
862 | } | |
863 | } | |
864 | } | |
865 | ||
82d1d36e FDN |
866 | for (i=0; i<MAX_ACCEPTED_SOCKS; i++) { |
867 | if (sockfd == info->accepted_socks[i]) { | |
868 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Closing accepted socket %d", sockfd); | |
869 | _set_fd_tracker(knet_h, sockfd, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL); | |
870 | info->accepted_socks[i] = -1; | |
d56e3615 | 871 | free(accept_info); |
82d1d36e | 872 | close(sockfd); |
ded574d1 | 873 | break; /* Keeps covscan happy */ |
82d1d36e FDN |
874 | } |
875 | } | |
e3e37ffd CC |
876 | } |
877 | ||
82d1d36e | 878 | static void *_sctp_listen_thread(void *data) |
e3e37ffd | 879 | { |
82d1d36e FDN |
880 | int savederrno; |
881 | int i, nev; | |
882 | knet_handle_t knet_h = (knet_handle_t) data; | |
883 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
884 | struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; | |
e3e37ffd | 885 | |
6394d892 | 886 | set_thread_status(knet_h, KNET_THREAD_SCTP_LISTEN, KNET_THREAD_STARTED); |
aed6b5db | 887 | |
82d1d36e | 888 | while (!shutdown_in_progress(knet_h)) { |
aed6b5db FDN |
889 | nev = epoll_wait(handle_info->listen_epollfd, events, KNET_EPOLL_MAX_EVENTS, KNET_THREADS_TIMERES / 1000); |
890 | ||
891 | /* | |
892 | * we use timeout to detect if thread is shutting down | |
893 | */ | |
894 | if (nev == 0) { | |
895 | continue; | |
896 | } | |
e3e37ffd | 897 | |
82d1d36e FDN |
898 | if (nev < 0) { |
899 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listen handler EPOLL ERROR: %s", | |
900 | strerror(errno)); | |
901 | continue; | |
902 | } | |
e3e37ffd | 903 | |
1dfc8220 | 904 | savederrno = get_global_wrlock(knet_h); |
82d1d36e FDN |
905 | if (savederrno) { |
906 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to get write lock: %s", | |
907 | strerror(savederrno)); | |
908 | continue; | |
909 | } | |
910 | /* | |
911 | * Sort out which FD has an incoming connection | |
912 | */ | |
913 | for (i = 0; i < nev; i++) { | |
914 | if (events[i].data.fd == handle_info->listensockfd[0]) { | |
915 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received notification from rx_error for listener/accepted socket"); | |
916 | _handle_listen_sctp_errors(knet_h); | |
917 | } else { | |
918 | if (_is_valid_fd(knet_h, events[i].data.fd) == 1) { | |
919 | _handle_incoming_sctp(knet_h, events[i].data.fd); | |
920 | } else { | |
921 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Received listen notification from invalid socket"); | |
922 | } | |
923 | } | |
e3e37ffd | 924 | |
82d1d36e FDN |
925 | } |
926 | pthread_rwlock_unlock(&knet_h->global_rwlock); | |
e3e37ffd | 927 | } |
aed6b5db FDN |
928 | |
929 | set_thread_status(knet_h, KNET_THREAD_SCTP_LISTEN, KNET_THREAD_STOPPED); | |
930 | ||
82d1d36e | 931 | return NULL; |
e3e37ffd CC |
932 | } |
933 | ||
82d1d36e FDN |
934 | /* |
935 | * sctp_link_listener_start/stop are called in global write lock | |
936 | * context from set_config and clear_config. | |
937 | */ | |
8a142f48 | 938 | static sctp_listen_link_info_t *sctp_link_listener_start(knet_handle_t knet_h, struct knet_link *kn_link) |
e3e37ffd | 939 | { |
82d1d36e FDN |
940 | int err = 0, savederrno = 0; |
941 | int listen_sock = -1; | |
e3e37ffd | 942 | struct epoll_event ev; |
82d1d36e FDN |
943 | sctp_listen_link_info_t *info = NULL; |
944 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
945 | ||
946 | /* | |
947 | * Only allocate a new listener if src address is different | |
948 | */ | |
949 | knet_list_for_each_entry(info, &handle_info->listen_links_list, list) { | |
8a142f48 | 950 | if (memcmp(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) { |
6373dd23 | 951 | if ((check_add(knet_h, info->listen_sock, KNET_TRANSPORT_SCTP, -1, |
9a5babce | 952 | &kn_link->dst_addr, &kn_link->dst_addr, CHECK_TYPE_ADDRESS, CHECK_ACCEPT) < 0) && (errno != EEXIST)) { |
d2333aab FDN |
953 | return NULL; |
954 | } | |
82d1d36e FDN |
955 | return info; |
956 | } | |
957 | } | |
e3e37ffd | 958 | |
82d1d36e FDN |
959 | info = malloc(sizeof(sctp_listen_link_info_t)); |
960 | if (!info) { | |
961 | err = -1; | |
962 | goto exit_error; | |
963 | } | |
964 | ||
965 | memset(info, 0, sizeof(sctp_listen_link_info_t)); | |
966 | ||
967 | memset(info->accepted_socks, -1, sizeof(info->accepted_socks)); | |
535a4bf7 | 968 | memmove(&info->src_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)); |
e3e37ffd | 969 | |
8a142f48 | 970 | listen_sock = socket(kn_link->src_addr.ss_family, SOCK_STREAM, IPPROTO_SCTP); |
e3e37ffd CC |
971 | if (listen_sock < 0) { |
972 | savederrno = errno; | |
973 | err = -1; | |
815929bf | 974 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create listener socket: %s", |
e3e37ffd CC |
975 | strerror(savederrno)); |
976 | goto exit_error; | |
977 | } | |
978 | ||
c58507f9 | 979 | if (_configure_sctp_socket(knet_h, listen_sock, &kn_link->src_addr, kn_link->flags, "SCTP listener") < 0) { |
82d1d36e FDN |
980 | savederrno = errno; |
981 | err = -1; | |
e3e37ffd CC |
982 | goto exit_error; |
983 | } | |
984 | ||
8a142f48 | 985 | if (bind(listen_sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr)) < 0) { |
e3e37ffd CC |
986 | savederrno = errno; |
987 | err = -1; | |
815929bf | 988 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to bind listener socket: %s", |
e3e37ffd CC |
989 | strerror(savederrno)); |
990 | goto exit_error; | |
991 | } | |
992 | ||
993 | if (listen(listen_sock, 5) < 0) { | |
994 | savederrno = errno; | |
995 | err = -1; | |
815929bf | 996 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to listen on listener socket: %s", |
e3e37ffd CC |
997 | strerror(savederrno)); |
998 | goto exit_error; | |
999 | } | |
1000 | ||
d56e3615 | 1001 | if (_set_fd_tracker(knet_h, listen_sock, KNET_TRANSPORT_SCTP, SCTP_LISTENER_LINK_INFO, info) < 0) { |
82d1d36e FDN |
1002 | savederrno = errno; |
1003 | err = -1; | |
1004 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
1005 | strerror(savederrno)); | |
1006 | goto exit_error; | |
1007 | } | |
1008 | ||
6373dd23 | 1009 | if ((check_add(knet_h, listen_sock, KNET_TRANSPORT_SCTP, -1, |
9a5babce | 1010 | &kn_link->dst_addr, &kn_link->dst_addr, CHECK_TYPE_ADDRESS, CHECK_ACCEPT) < 0) && (errno != EEXIST)) { |
d2333aab FDN |
1011 | savederrno = errno; |
1012 | err = -1; | |
1013 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to configure default access lists: %s", | |
1014 | strerror(savederrno)); | |
1015 | goto exit_error; | |
1016 | } | |
1017 | ||
e3e37ffd CC |
1018 | memset(&ev, 0, sizeof(struct epoll_event)); |
1019 | ev.events = EPOLLIN; | |
82d1d36e | 1020 | ev.data.fd = listen_sock; |
e3e37ffd CC |
1021 | if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, listen_sock, &ev)) { |
1022 | savederrno = errno; | |
1023 | err = -1; | |
815929bf | 1024 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listener to epoll pool: %s", |
e3e37ffd CC |
1025 | strerror(savederrno)); |
1026 | goto exit_error; | |
1027 | } | |
82d1d36e FDN |
1028 | info->on_listener_epoll = 1; |
1029 | ||
e3e37ffd | 1030 | info->listen_sock = listen_sock; |
82d1d36e | 1031 | knet_list_add(&info->list, &handle_info->listen_links_list); |
b715c049 | 1032 | |
8a142f48 | 1033 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Listening on fd %d for %s:%s", listen_sock, kn_link->status.src_ipaddr, kn_link->status.src_port); |
b715c049 | 1034 | |
82d1d36e FDN |
1035 | exit_error: |
1036 | if (err) { | |
021e57d3 | 1037 | if ((info) && (info->on_listener_epoll)) { |
82d1d36e FDN |
1038 | epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, listen_sock, &ev); |
1039 | } | |
1040 | if (listen_sock >= 0) { | |
95c6335c | 1041 | check_rmall(knet_h, listen_sock, KNET_TRANSPORT_SCTP); |
82d1d36e FDN |
1042 | close(listen_sock); |
1043 | } | |
1044 | if (info) { | |
1045 | free(info); | |
1046 | info = NULL; | |
1047 | } | |
1048 | } | |
1049 | errno = savederrno; | |
1050 | return info; | |
1051 | } | |
1052 | ||
8a142f48 | 1053 | static int sctp_link_listener_stop(knet_handle_t knet_h, struct knet_link *kn_link) |
82d1d36e FDN |
1054 | { |
1055 | int err = 0, savederrno = 0; | |
1056 | int found = 0, i; | |
1057 | struct knet_host *host; | |
1058 | int link_idx; | |
1059 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
8a142f48 | 1060 | sctp_connect_link_info_t *this_link_info = kn_link->transport_link; |
82d1d36e FDN |
1061 | sctp_listen_link_info_t *info = this_link_info->listener; |
1062 | sctp_connect_link_info_t *link_info; | |
1063 | struct epoll_event ev; | |
1064 | ||
1065 | for (host = knet_h->host_head; host != NULL; host = host->next) { | |
1066 | for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { | |
8a142f48 | 1067 | if (&host->link[link_idx] == kn_link) |
82d1d36e FDN |
1068 | continue; |
1069 | ||
1070 | link_info = host->link[link_idx].transport_link; | |
1071 | if ((link_info) && | |
a9ca6afa | 1072 | (link_info->listener == info)) { |
82d1d36e FDN |
1073 | found = 1; |
1074 | break; | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | ||
9a5babce FDN |
1079 | if ((check_rm(knet_h, info->listen_sock, KNET_TRANSPORT_SCTP, |
1080 | &kn_link->dst_addr, &kn_link->dst_addr, CHECK_TYPE_ADDRESS, CHECK_ACCEPT) < 0) && (errno != ENOENT)) { | |
d2333aab FDN |
1081 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove default access lists for %d", info->listen_sock); |
1082 | } | |
1083 | ||
82d1d36e FDN |
1084 | if (found) { |
1085 | this_link_info->listener = NULL; | |
1086 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listener socket %d still in use", info->listen_sock); | |
1087 | savederrno = EBUSY; | |
1088 | err = -1; | |
1089 | goto exit_error; | |
1090 | } | |
1091 | ||
1092 | if (info->on_listener_epoll) { | |
1093 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1094 | ev.events = EPOLLIN; | |
1095 | ev.data.fd = info->listen_sock; | |
1096 | if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, info->listen_sock, &ev)) { | |
1097 | savederrno = errno; | |
1098 | err = -1; | |
1099 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener to epoll pool: %s", | |
1100 | strerror(savederrno)); | |
1101 | goto exit_error; | |
1102 | } | |
1103 | info->on_listener_epoll = 0; | |
1104 | } | |
1105 | ||
1106 | if (_set_fd_tracker(knet_h, info->listen_sock, KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { | |
1107 | savederrno = errno; | |
1108 | err = -1; | |
1109 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
1110 | strerror(savederrno)); | |
1111 | goto exit_error; | |
1112 | } | |
1113 | ||
0cb8e8ba | 1114 | check_rmall(knet_h, info->listen_sock, KNET_TRANSPORT_SCTP); |
d2333aab | 1115 | |
82d1d36e FDN |
1116 | close(info->listen_sock); |
1117 | ||
1118 | for (i=0; i< MAX_ACCEPTED_SOCKS; i++) { | |
1119 | if (info->accepted_socks[i] > -1) { | |
1120 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1121 | ev.events = EPOLLIN; | |
1122 | ev.data.fd = info->accepted_socks[i]; | |
1123 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->accepted_socks[i], &ev)) { | |
1124 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove EOFed socket from epoll pool: %s", | |
1125 | strerror(errno)); | |
1126 | } | |
1127 | info->on_rx_epoll = 0; | |
d56e3615 | 1128 | free(knet_h->knet_transport_fd_tracker[info->accepted_socks[i]].data); |
82d1d36e FDN |
1129 | close(info->accepted_socks[i]); |
1130 | if (_set_fd_tracker(knet_h, info->accepted_socks[i], KNET_MAX_TRANSPORTS, SCTP_NO_LINK_INFO, NULL) < 0) { | |
1131 | savederrno = errno; | |
1132 | err = -1; | |
1133 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set fd tracker: %s", | |
1134 | strerror(savederrno)); | |
1135 | goto exit_error; | |
1136 | } | |
1137 | info->accepted_socks[i] = -1; | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | knet_list_del(&info->list); | |
1142 | free(info); | |
1143 | this_link_info->listener = NULL; | |
e3e37ffd CC |
1144 | |
1145 | exit_error: | |
1146 | errno = savederrno; | |
1147 | return err; | |
1148 | } | |
1149 | ||
82d1d36e FDN |
1150 | /* |
1151 | * Links config/clear. Both called with global wrlock from link_set_config/clear_config | |
1152 | */ | |
4ac6704d | 1153 | int sctp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link) |
e3e37ffd | 1154 | { |
82d1d36e FDN |
1155 | int savederrno = 0, err = 0; |
1156 | sctp_connect_link_info_t *info; | |
1157 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
e3e37ffd | 1158 | |
82d1d36e | 1159 | info = malloc(sizeof(sctp_connect_link_info_t)); |
e3e37ffd | 1160 | if (!info) { |
82d1d36e | 1161 | goto exit_error; |
e3e37ffd | 1162 | } |
82d1d36e FDN |
1163 | |
1164 | memset(info, 0, sizeof(sctp_connect_link_info_t)); | |
1165 | ||
8a142f48 FDN |
1166 | kn_link->transport_link = info; |
1167 | info->link = kn_link; | |
e3e37ffd | 1168 | |
535a4bf7 | 1169 | memmove(&info->dst_address, &kn_link->dst_addr, sizeof(struct sockaddr_storage)); |
82d1d36e FDN |
1170 | info->on_connected_epoll = 0; |
1171 | info->connect_sock = -1; | |
1172 | ||
8a142f48 | 1173 | info->listener = sctp_link_listener_start(knet_h, kn_link); |
82d1d36e FDN |
1174 | if (!info->listener) { |
1175 | savederrno = errno; | |
1176 | err = -1; | |
1177 | goto exit_error; | |
e3e37ffd CC |
1178 | } |
1179 | ||
8a142f48 FDN |
1180 | if (kn_link->dynamic == KNET_LINK_STATIC) { |
1181 | if (_create_connect_socket(knet_h, kn_link) < 0) { | |
f66ebfd6 FDN |
1182 | savederrno = errno; |
1183 | err = -1; | |
1184 | goto exit_error; | |
1185 | } | |
8a142f48 | 1186 | kn_link->outsock = info->connect_sock; |
e3e37ffd CC |
1187 | } |
1188 | ||
82d1d36e | 1189 | knet_list_add(&info->list, &handle_info->connect_links_list); |
e3e37ffd | 1190 | |
e3e37ffd | 1191 | exit_error: |
82d1d36e FDN |
1192 | if (err) { |
1193 | if (info) { | |
1194 | if (info->connect_sock) { | |
1195 | close(info->connect_sock); | |
1196 | } | |
1197 | if (info->listener) { | |
8a142f48 | 1198 | sctp_link_listener_stop(knet_h, kn_link); |
82d1d36e | 1199 | } |
8a142f48 | 1200 | kn_link->transport_link = NULL; |
82d1d36e FDN |
1201 | free(info); |
1202 | } | |
1203 | } | |
e3e37ffd CC |
1204 | errno = savederrno; |
1205 | return err; | |
1206 | } | |
1207 | ||
82d1d36e FDN |
1208 | /* |
1209 | * called with global wrlock | |
82d1d36e | 1210 | */ |
4ac6704d | 1211 | int sctp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link) |
e3e37ffd | 1212 | { |
82d1d36e FDN |
1213 | int err = 0, savederrno = 0; |
1214 | sctp_connect_link_info_t *info; | |
d26576a8 CC |
1215 | struct epoll_event ev; |
1216 | ||
8a142f48 | 1217 | if (!kn_link) { |
82d1d36e FDN |
1218 | errno = EINVAL; |
1219 | return -1; | |
d26576a8 | 1220 | } |
82d1d36e | 1221 | |
8a142f48 | 1222 | info = kn_link->transport_link; |
82d1d36e FDN |
1223 | |
1224 | if (!info) { | |
1225 | errno = EINVAL; | |
1226 | return -1; | |
d26576a8 | 1227 | } |
e3e37ffd | 1228 | |
8a142f48 | 1229 | if ((sctp_link_listener_stop(knet_h, kn_link) <0) && (errno != EBUSY)) { |
82d1d36e FDN |
1230 | savederrno = errno; |
1231 | err = -1; | |
1232 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove listener trasport: %s", | |
1233 | strerror(savederrno)); | |
1234 | goto exit_error; | |
1235 | } | |
1236 | ||
1237 | if (info->on_rx_epoll) { | |
1238 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1239 | ev.events = EPOLLIN; | |
1240 | ev.data.fd = info->connect_sock; | |
1241 | if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->connect_sock, &ev)) { | |
1242 | savederrno = errno; | |
1243 | err = -1; | |
1244 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from epoll pool: %s", | |
1245 | strerror(savederrno)); | |
1246 | goto exit_error; | |
e3e37ffd | 1247 | } |
82d1d36e FDN |
1248 | info->on_rx_epoll = 0; |
1249 | } | |
1250 | ||
8a142f48 | 1251 | if (_close_connect_socket(knet_h, kn_link) < 0) { |
82d1d36e FDN |
1252 | savederrno = errno; |
1253 | err = -1; | |
1254 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to close connected socket: %s", | |
1255 | strerror(savederrno)); | |
1256 | goto exit_error; | |
e3e37ffd CC |
1257 | } |
1258 | ||
b715c049 | 1259 | knet_list_del(&info->list); |
e3e37ffd | 1260 | |
82d1d36e | 1261 | free(info); |
8a142f48 | 1262 | kn_link->transport_link = NULL; |
e3e37ffd | 1263 | |
82d1d36e FDN |
1264 | exit_error: |
1265 | errno = savederrno; | |
1266 | return err; | |
e3e37ffd CC |
1267 | } |
1268 | ||
82d1d36e FDN |
1269 | /* |
1270 | * transport_free and transport_init are | |
1271 | * called only from knet_handle_new and knet_handle_free. | |
1272 | * all resources (hosts/links) should have been already freed at this point | |
1273 | * and they are called in a write locked context, hence they | |
1274 | * don't need their own locking. | |
1275 | */ | |
1276 | ||
4ac6704d | 1277 | int sctp_transport_free(knet_handle_t knet_h) |
e3e37ffd | 1278 | { |
82d1d36e FDN |
1279 | sctp_handle_info_t *handle_info; |
1280 | void *thread_status; | |
1281 | struct epoll_event ev; | |
e3e37ffd | 1282 | |
82d1d36e FDN |
1283 | if (!knet_h->transports[KNET_TRANSPORT_SCTP]) { |
1284 | errno = EINVAL; | |
1285 | return -1; | |
1286 | } | |
1287 | ||
1288 | handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
1289 | ||
1290 | /* | |
1291 | * keep it here while we debug list usage and such | |
1292 | */ | |
1293 | if (!knet_list_empty(&handle_info->listen_links_list)) { | |
1294 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. listen links list is not empty"); | |
1295 | } | |
1296 | if (!knet_list_empty(&handle_info->connect_links_list)) { | |
1297 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Internal error. connect links list is not empty"); | |
1298 | } | |
1299 | ||
1300 | if (handle_info->listen_thread) { | |
1301 | pthread_cancel(handle_info->listen_thread); | |
1302 | pthread_join(handle_info->listen_thread, &thread_status); | |
1303 | } | |
1304 | ||
1305 | if (handle_info->connect_thread) { | |
1306 | pthread_cancel(handle_info->connect_thread); | |
1307 | pthread_join(handle_info->connect_thread, &thread_status); | |
e3e37ffd | 1308 | } |
82d1d36e FDN |
1309 | |
1310 | if (handle_info->listensockfd[0] >= 0) { | |
1311 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1312 | ev.events = EPOLLIN; | |
1313 | ev.data.fd = handle_info->listensockfd[0]; | |
1314 | epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, handle_info->listensockfd[0], &ev); | |
1315 | } | |
1316 | ||
1317 | if (handle_info->connectsockfd[0] >= 0) { | |
1318 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1319 | ev.events = EPOLLIN; | |
1320 | ev.data.fd = handle_info->connectsockfd[0]; | |
1321 | epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, handle_info->connectsockfd[0], &ev); | |
1322 | } | |
1323 | ||
1324 | _close_socketpair(knet_h, handle_info->connectsockfd); | |
1325 | _close_socketpair(knet_h, handle_info->listensockfd); | |
1326 | ||
1327 | if (handle_info->listen_epollfd >= 0) { | |
1328 | close(handle_info->listen_epollfd); | |
1329 | } | |
1330 | ||
1331 | if (handle_info->connect_epollfd >= 0) { | |
1332 | close(handle_info->connect_epollfd); | |
1333 | } | |
1334 | ||
57ebf58c | 1335 | free(handle_info->event_subscribe_buffer); |
82d1d36e FDN |
1336 | free(handle_info); |
1337 | knet_h->transports[KNET_TRANSPORT_SCTP] = NULL; | |
1338 | ||
1339 | return 0; | |
e3e37ffd CC |
1340 | } |
1341 | ||
57ebf58c FW |
1342 | static int _sctp_subscribe_init(knet_handle_t knet_h) |
1343 | { | |
1344 | int test_socket, savederrno; | |
1345 | sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; | |
1346 | char dummy_events[100]; | |
1347 | struct sctp_event_subscribe *events; | |
1348 | /* Below we set the first 6 fields of this expanding struct. | |
1349 | * SCTP_EVENTS is deprecated, but SCTP_EVENT is not available | |
1350 | * on Linux; on the other hand, FreeBSD and old Linux does not | |
1351 | * accept small transfers, so we can't simply use this minimum | |
1352 | * everywhere. Thus we query and store the native size. */ | |
1353 | const unsigned int subscribe_min = 6; | |
1354 | ||
1355 | test_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP); | |
1356 | if (test_socket < 0) { | |
276499a1 FW |
1357 | if (errno == EPROTONOSUPPORT) { |
1358 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP not supported, skipping initialization"); | |
1359 | return 0; | |
1360 | } | |
57ebf58c FW |
1361 | savederrno = errno; |
1362 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create test socket: %s", | |
1363 | strerror(savederrno)); | |
1364 | return savederrno; | |
1365 | } | |
1366 | handle_info->event_subscribe_kernel_size = sizeof dummy_events; | |
1367 | if (getsockopt(test_socket, IPPROTO_SCTP, SCTP_EVENTS, &dummy_events, | |
1368 | &handle_info->event_subscribe_kernel_size)) { | |
1369 | close(test_socket); | |
1370 | savederrno = errno; | |
1371 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to query kernel size of struct sctp_event_subscribe: %s", | |
1372 | strerror(savederrno)); | |
1373 | return savederrno; | |
1374 | } | |
1375 | close(test_socket); | |
1376 | if (handle_info->event_subscribe_kernel_size < subscribe_min) { | |
1377 | savederrno = ERANGE; | |
1378 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, | |
1379 | "No kernel support for the necessary notifications: struct sctp_event_subscribe is %u bytes, %u needed", | |
1380 | handle_info->event_subscribe_kernel_size, subscribe_min); | |
1381 | return savederrno; | |
1382 | } | |
1383 | events = malloc(handle_info->event_subscribe_kernel_size); | |
1384 | if (!events) { | |
1385 | savederrno = errno; | |
1386 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, | |
1387 | "Failed to allocate event subscribe buffer: %s", strerror(savederrno)); | |
1388 | return savederrno; | |
1389 | } | |
1390 | memset(events, 0, handle_info->event_subscribe_kernel_size); | |
1391 | events->sctp_data_io_event = 1; | |
1392 | events->sctp_association_event = 1; | |
1393 | events->sctp_address_event = 1; | |
1394 | events->sctp_send_failure_event = 1; | |
1395 | events->sctp_peer_error_event = 1; | |
1396 | events->sctp_shutdown_event = 1; | |
1397 | handle_info->event_subscribe_buffer = (char *)events; | |
1398 | log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Size of struct sctp_event_subscribe is %u in kernel, %zu in user space", | |
1399 | handle_info->event_subscribe_kernel_size, sizeof(struct sctp_event_subscribe)); | |
1400 | return 0; | |
1401 | } | |
1402 | ||
4ac6704d | 1403 | int sctp_transport_init(knet_handle_t knet_h) |
e3e37ffd | 1404 | { |
82d1d36e FDN |
1405 | int err = 0, savederrno = 0; |
1406 | sctp_handle_info_t *handle_info; | |
1407 | struct epoll_event ev; | |
e3e37ffd | 1408 | |
82d1d36e FDN |
1409 | if (knet_h->transports[KNET_TRANSPORT_SCTP]) { |
1410 | errno = EEXIST; | |
1411 | return -1; | |
1412 | } | |
1413 | ||
1414 | handle_info = malloc(sizeof(sctp_handle_info_t)); | |
1415 | if (!handle_info) { | |
1416 | return -1; | |
1417 | } | |
1418 | ||
1419 | memset(handle_info, 0,sizeof(sctp_handle_info_t)); | |
1420 | ||
1421 | knet_h->transports[KNET_TRANSPORT_SCTP] = handle_info; | |
1422 | ||
57ebf58c FW |
1423 | savederrno = _sctp_subscribe_init(knet_h); |
1424 | if (savederrno) { | |
1425 | err = -1; | |
1426 | goto exit_fail; | |
1427 | } | |
1428 | ||
82d1d36e FDN |
1429 | knet_list_init(&handle_info->listen_links_list); |
1430 | knet_list_init(&handle_info->connect_links_list); | |
1431 | ||
1432 | handle_info->listen_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); | |
d14d75d2 FW |
1433 | if (handle_info->listen_epollfd < 0) { |
1434 | savederrno = errno; | |
82d1d36e | 1435 | err = -1; |
d14d75d2 FW |
1436 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll listen fd: %s", |
1437 | strerror(savederrno)); | |
1438 | goto exit_fail; | |
82d1d36e FDN |
1439 | } |
1440 | ||
1441 | if (_fdset_cloexec(handle_info->listen_epollfd)) { | |
1442 | savederrno = errno; | |
1443 | err = -1; | |
1444 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on listen_epollfd: %s", | |
1445 | strerror(savederrno)); | |
1446 | goto exit_fail; | |
1447 | } | |
1448 | ||
1449 | handle_info->connect_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); | |
1450 | if (handle_info->connect_epollfd < 0) { | |
1451 | savederrno = errno; | |
1452 | err = -1; | |
1453 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll connect fd: %s", | |
1454 | strerror(savederrno)); | |
1455 | goto exit_fail; | |
1456 | } | |
1457 | ||
1458 | if (_fdset_cloexec(handle_info->connect_epollfd)) { | |
1459 | savederrno = errno; | |
1460 | err = -1; | |
1461 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to set CLOEXEC on connect_epollfd: %s", | |
1462 | strerror(savederrno)); | |
1463 | goto exit_fail; | |
1464 | } | |
e3e37ffd | 1465 | |
82d1d36e FDN |
1466 | if (_init_socketpair(knet_h, handle_info->connectsockfd) < 0) { |
1467 | savederrno = errno; | |
1468 | err = -1; | |
1469 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init connect socketpair: %s", | |
1470 | strerror(savederrno)); | |
1471 | goto exit_fail; | |
1472 | } | |
e3e37ffd | 1473 | |
82d1d36e FDN |
1474 | memset(&ev, 0, sizeof(struct epoll_event)); |
1475 | ev.events = EPOLLIN; | |
1476 | ev.data.fd = handle_info->connectsockfd[0]; | |
1477 | if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, handle_info->connectsockfd[0], &ev)) { | |
1478 | savederrno = errno; | |
1479 | err = -1; | |
1480 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connectsockfd[0] to connect epoll pool: %s", | |
1481 | strerror(savederrno)); | |
1482 | goto exit_fail; | |
1483 | } | |
1484 | ||
1485 | if (_init_socketpair(knet_h, handle_info->listensockfd) < 0) { | |
1486 | savederrno = errno; | |
1487 | err = -1; | |
1488 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to init listen socketpair: %s", | |
1489 | strerror(savederrno)); | |
1490 | goto exit_fail; | |
1491 | } | |
1492 | ||
1493 | memset(&ev, 0, sizeof(struct epoll_event)); | |
1494 | ev.events = EPOLLIN; | |
1495 | ev.data.fd = handle_info->listensockfd[0]; | |
1496 | if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, handle_info->listensockfd[0], &ev)) { | |
1497 | savederrno = errno; | |
1498 | err = -1; | |
1499 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listensockfd[0] to listen epoll pool: %s", | |
1500 | strerror(savederrno)); | |
1501 | goto exit_fail; | |
1502 | } | |
1503 | ||
1504 | /* | |
1505 | * Start connect & listener threads | |
1506 | */ | |
6394d892 | 1507 | set_thread_status(knet_h, KNET_THREAD_SCTP_LISTEN, KNET_THREAD_REGISTERED); |
82d1d36e | 1508 | savederrno = pthread_create(&handle_info->listen_thread, 0, _sctp_listen_thread, (void *) knet_h); |
d56e3615 | 1509 | if (savederrno) { |
82d1d36e | 1510 | err = -1; |
d56e3615 FDN |
1511 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp listen thread: %s", |
1512 | strerror(savederrno)); | |
1513 | goto exit_fail; | |
1514 | } | |
82d1d36e | 1515 | |
6394d892 | 1516 | set_thread_status(knet_h, KNET_THREAD_SCTP_CONN, KNET_THREAD_REGISTERED); |
82d1d36e | 1517 | savederrno = pthread_create(&handle_info->connect_thread, 0, _sctp_connect_thread, (void *) knet_h); |
d56e3615 | 1518 | if (savederrno) { |
82d1d36e | 1519 | err = -1; |
d56e3615 FDN |
1520 | log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp connect thread: %s", |
1521 | strerror(savederrno)); | |
1522 | goto exit_fail; | |
1523 | } | |
82d1d36e FDN |
1524 | |
1525 | exit_fail: | |
1526 | if (err < 0) { | |
1527 | sctp_transport_free(knet_h); | |
1528 | } | |
1529 | errno = savederrno; | |
1530 | return err; | |
1531 | } | |
1532 | ||
4ac6704d | 1533 | int sctp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link) |
f66ebfd6 FDN |
1534 | { |
1535 | kn_link->outsock = sockfd; | |
1536 | kn_link->status.dynconnected = 1; | |
1537 | kn_link->transport_connected = 1; | |
1538 | return 0; | |
1539 | } | |
2eb0040c FDN |
1540 | |
1541 | int sctp_transport_link_get_acl_fd(knet_handle_t knet_h, struct knet_link *kn_link) | |
1542 | { | |
1543 | sctp_connect_link_info_t *this_link_info = kn_link->transport_link; | |
1544 | sctp_listen_link_info_t *info = this_link_info->listener; | |
1545 | return info->listen_sock; | |
1546 | } | |
3c62178c | 1547 | #endif |