blob: 037897001bf9189647c5834f5e85af2262726fa0 [file] [log] [blame]
Harald Welte468b6432014-09-11 13:05:51 +08001/*
Harald Welte48f55832017-01-26 00:03:10 +01002 * (C) 2011-2017 by Harald Welte <laforge@gnumonks.org>
Harald Welte468b6432014-09-11 13:05:51 +08003 *
4 * All Rights Reserved
5 *
Harald Weltee08da972017-11-13 01:00:26 +09006 * SPDX-License-Identifier: GPL-2.0+
7 *
Harald Welte468b6432014-09-11 13:05:51 +08008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
Harald Welte33cb71a2011-05-21 18:54:32 +020024#include "../config.h"
25
Harald Welteba6988b2011-08-17 12:46:48 +020026/*! \addtogroup socket
27 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020028 * Osmocom socket convenience functions.
29 *
30 * \file socket.c */
Harald Welte96e2a002017-06-12 21:44:18 +020031
Harald Weltee4764422011-05-22 12:25:57 +020032#ifdef HAVE_SYS_SOCKET_H
33
Harald Welte33cb71a2011-05-21 18:54:32 +020034#include <osmocom/core/logging.h>
35#include <osmocom/core/select.h>
36#include <osmocom/core/socket.h>
Harald Welte48f55832017-01-26 00:03:10 +010037#include <osmocom/core/talloc.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020038
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020039#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020040#include <sys/socket.h>
41#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010042#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020043
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010044#include <netinet/in.h>
Harald Weltee30d7e62017-07-13 16:02:50 +020045#include <arpa/inet.h>
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010046
Harald Welte33cb71a2011-05-21 18:54:32 +020047#include <stdio.h>
48#include <unistd.h>
49#include <stdint.h>
50#include <string.h>
51#include <errno.h>
52#include <netdb.h>
53#include <ifaddrs.h>
54
Harald Weltedda70fc2017-04-08 20:52:33 +020055static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
56 const char *host, uint16_t port, bool passive)
57{
58 struct addrinfo hints, *result;
59 char portbuf[16];
60 int rc;
61
62 snprintf(portbuf, sizeof(portbuf), "%u", port);
63 memset(&hints, 0, sizeof(struct addrinfo));
64 hints.ai_family = family;
65 if (type == SOCK_RAW) {
66 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
67 * SOCK_RAW and IPPROTO_GRE is used.
68 */
69 hints.ai_socktype = SOCK_DGRAM;
70 hints.ai_protocol = IPPROTO_UDP;
71 } else {
72 hints.ai_socktype = type;
73 hints.ai_protocol = proto;
74 }
75
76 if (passive)
77 hints.ai_flags |= AI_PASSIVE;
78
79 rc = getaddrinfo(host, portbuf, &hints, &result);
80 if (rc != 0) {
81 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
82 host, port, strerror(errno));
83 return NULL;
84 }
85
86 return result;
87}
88
89static int socket_helper(const struct addrinfo *rp, unsigned int flags)
90{
91 int sfd, on = 1;
92
93 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
94 if (sfd == -1)
95 return sfd;
96 if (flags & OSMO_SOCK_F_NONBLOCK) {
97 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
98 LOGP(DLGLOBAL, LOGL_ERROR,
99 "cannot set this socket unblocking: %s\n",
100 strerror(errno));
101 close(sfd);
102 sfd = -EINVAL;
103 }
104 }
105 return sfd;
106}
107
108
Harald Weltec47bbda2017-07-13 16:13:26 +0200109static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
110{
Harald Weltebc43a622017-07-13 16:20:21 +0200111 int rc;
Harald Weltec47bbda2017-07-13 16:13:26 +0200112
113 /* Make sure to call 'listen' on a bound, connection-oriented sock */
114 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
115 switch (type) {
116 case SOCK_STREAM:
117 case SOCK_SEQPACKET:
118 rc = listen(fd, 10);
Harald Weltebc43a622017-07-13 16:20:21 +0200119 if (rc < 0) {
120 LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
121 strerror(errno));
122 return rc;
123 }
124 break;
Harald Weltec47bbda2017-07-13 16:13:26 +0200125 }
126 }
127
Harald Weltebc43a622017-07-13 16:20:21 +0200128 if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
129 rc = osmo_sock_mcast_loop_set(fd, false);
130 if (rc < 0) {
131 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
132 strerror(errno));
133 return rc;
134 }
135 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200136
Harald Welte37d204a2017-07-13 16:33:16 +0200137 if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
138 rc = osmo_sock_mcast_all_set(fd, false);
139 if (rc < 0) {
140 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
141 strerror(errno));
142 /* do not abort here, as this is just an
143 * optional additional optimization that only
144 * exists on Linux only */
145 }
146 }
Harald Weltebc43a622017-07-13 16:20:21 +0200147 return 0;
Harald Weltec47bbda2017-07-13 16:13:26 +0200148}
149
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200150/*! Initialize a socket (including bind and/or connect)
Harald Weltedda70fc2017-04-08 20:52:33 +0200151 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
152 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
153 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
154 * \param[in] local_host local host name or IP address in string form
155 * \param[in] local_port local port number in host byte order
156 * \param[in] remote_host remote host name or IP address in string form
157 * \param[in] remote_port remote port number in host byte order
158 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
159 * \returns socket file descriptor on success; negative on error
160 *
161 * This function creates a new socket of the designated \a family, \a
162 * type and \a proto and optionally binds it to the \a local_host and \a
163 * local_port as well as optionally connects it to the \a remote_host
164 * and \q remote_port, depending on the value * of \a flags parameter.
165 *
166 * As opposed to \ref osmo_sock_init(), this function allows to combine
167 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
168 * is useful if you want to connect to a remote host/port, but still
169 * want to bind that socket to either a specific local alias IP and/or a
170 * specific local source port.
171 *
172 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
173 * OSMO_SOCK_F_CONNECT, or both.
174 *
175 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
176 * non-blocking mode.
177 */
178int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
179 const char *local_host, uint16_t local_port,
180 const char *remote_host, uint16_t remote_port, unsigned int flags)
181{
182 struct addrinfo *result, *rp;
183 int sfd = -1, rc, on = 1;
184
185 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
186 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
187 "BIND or CONNECT flags\n");
188 return -EINVAL;
189 }
190
191 /* figure out local side of socket */
192 if (flags & OSMO_SOCK_F_BIND) {
193 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
194 if (!result)
195 return -EINVAL;
196
197 for (rp = result; rp != NULL; rp = rp->ai_next) {
198 /* Workaround for glibc again */
199 if (type == SOCK_RAW) {
200 rp->ai_socktype = SOCK_RAW;
201 rp->ai_protocol = proto;
202 }
203
204 sfd = socket_helper(rp, flags);
205 if (sfd < 0)
206 continue;
207
208 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
209 &on, sizeof(on));
210 if (rc < 0) {
211 LOGP(DLGLOBAL, LOGL_ERROR,
212 "cannot setsockopt socket:"
213 " %s:%u: %s\n",
214 local_host, local_port, strerror(errno));
215 break;
216 }
217 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
218 break;
219 close(sfd);
220 }
221 freeaddrinfo(result);
222 if (rp == NULL) {
223 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
224 local_host, local_port, strerror(errno));
225 return -ENODEV;
226 }
227 }
228
229 /* figure out remote side of socket */
230 if (flags & OSMO_SOCK_F_CONNECT) {
231 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
232 if (!result) {
233 close(sfd);
234 return -EINVAL;
235 }
236
237 for (rp = result; rp != NULL; rp = rp->ai_next) {
238 /* Workaround for glibc again */
239 if (type == SOCK_RAW) {
240 rp->ai_socktype = SOCK_RAW;
241 rp->ai_protocol = proto;
242 }
243
Harald Welte5cfa6dc2017-07-21 16:52:29 +0200244 if (sfd < 0) {
Harald Weltedda70fc2017-04-08 20:52:33 +0200245 sfd = socket_helper(rp, flags);
246 if (sfd < 0)
247 continue;
248 }
249
250 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
251 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
252 break;
253
254 close(sfd);
255 sfd = -1;
256 }
257 freeaddrinfo(result);
258 if (rp == NULL) {
259 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
260 remote_host, remote_port, strerror(errno));
261 return -ENODEV;
262 }
263 }
264
Harald Weltec47bbda2017-07-13 16:13:26 +0200265 rc = osmo_sock_init_tail(sfd, type, flags);
266 if (rc < 0) {
267 close(sfd);
268 sfd = -1;
Harald Weltedda70fc2017-04-08 20:52:33 +0200269 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200270
Harald Weltedda70fc2017-04-08 20:52:33 +0200271 return sfd;
272}
273
274
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200275/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200276 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
277 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
278 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
279 * \param[in] host remote host name or IP address in string form
280 * \param[in] port remote port number in host byte order
281 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200282 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200283 *
284 * This function creates a new socket of the designated \a family, \a
285 * type and \a proto and optionally binds or connects it, depending on
286 * the value of \a flags parameter.
287 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200288int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200289 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200290{
Harald Weltedda70fc2017-04-08 20:52:33 +0200291 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200292 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200293
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200294 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200295 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100296 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200297 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200298 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200299 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200300
Harald Weltedda70fc2017-04-08 20:52:33 +0200301 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
302 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100303 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200304 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200305 return -EINVAL;
306 }
307
308 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100309 /* Workaround for glibc again */
310 if (type == SOCK_RAW) {
311 rp->ai_socktype = SOCK_RAW;
312 rp->ai_protocol = proto;
313 }
314
Harald Weltedda70fc2017-04-08 20:52:33 +0200315 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200316 if (sfd == -1)
317 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200318
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200319 if (flags & OSMO_SOCK_F_CONNECT) {
320 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
321 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200322 break;
323 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200324 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
325 &on, sizeof(on));
326 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100327 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200328 "cannot setsockopt socket:"
329 " %s:%u: %s\n",
330 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200331 break;
332 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200333 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
334 break;
335 }
336 close(sfd);
337 }
338 freeaddrinfo(result);
339
340 if (rp == NULL) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100341 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect/bind socket: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200342 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200343 return -ENODEV;
344 }
Harald Welte68b15742011-05-22 21:47:29 +0200345
346 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
347
Harald Weltec47bbda2017-07-13 16:13:26 +0200348 rc = osmo_sock_init_tail(sfd, type, flags);
349 if (rc < 0) {
350 close(sfd);
351 sfd = -1;
Harald Welte68b15742011-05-22 21:47:29 +0200352 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200353
Harald Welte68b15742011-05-22 21:47:29 +0200354 return sfd;
355}
356
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200357/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200358 * \param[out] ofd file descriptor (will be filled in)
359 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200360 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200361 *
362 * This function fills the \a ofd structure.
363 */
364static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
365{
366 int rc;
367
368 if (sfd < 0)
369 return sfd;
370
371 ofd->fd = sfd;
372 ofd->when = BSC_FD_READ;
373
374 rc = osmo_fd_register(ofd);
375 if (rc < 0) {
376 close(sfd);
377 return rc;
378 }
379
380 return sfd;
381}
382
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200383/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100384 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200385 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
386 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
387 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
388 * \param[in] host remote host name or IP address in string form
389 * \param[in] port remote port number in host byte order
390 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200391 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200392 *
393 * This function creates (and optionall binds/connects) a socket using
394 * \ref osmo_sock_init, but also fills the \a ofd structure.
395 */
Harald Welte68b15742011-05-22 21:47:29 +0200396int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200397 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200398{
Max862ba652014-10-13 14:54:25 +0200399 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200400}
401
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200402/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200403 * \param[out] ofd file descriptor (will be filled in)
404 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
405 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
406 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
407 * \param[in] local_host local host name or IP address in string form
408 * \param[in] local_port local port number in host byte order
409 * \param[in] remote_host remote host name or IP address in string form
410 * \param[in] remote_port remote port number in host byte order
411 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
412 * \returns socket fd on success; negative on error
413 *
414 * This function creates (and optionall binds/connects) a socket using
415 * \ref osmo_sock_init2, but also fills the \a ofd structure.
416 */
417int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
418 const char *local_host, uint16_t local_port,
419 const char *remote_host, uint16_t remote_port, unsigned int flags)
420{
421 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
422 local_port, remote_host, remote_port, flags));
423}
424
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200425/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200426 * \param[out] ss socket address (will be filled in)
427 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
428 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
429 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200430 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200431 *
432 * This function creates (and optionall binds/connects) a socket using
433 * \ref osmo_sock_init, but also fills the \a ss structure.
434 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200435int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200436 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200437{
438 char host[NI_MAXHOST];
439 uint16_t port;
440 struct sockaddr_in *sin;
441 struct sockaddr_in6 *sin6;
442 int s, sa_len;
443
444 /* determine port and host from ss */
445 switch (ss->sa_family) {
446 case AF_INET:
447 sin = (struct sockaddr_in *) ss;
448 sa_len = sizeof(struct sockaddr_in);
449 port = ntohs(sin->sin_port);
450 break;
451 case AF_INET6:
452 sin6 = (struct sockaddr_in6 *) ss;
453 sa_len = sizeof(struct sockaddr_in6);
454 port = ntohs(sin6->sin6_port);
455 break;
456 default:
457 return -EINVAL;
458 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200459
460 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
461 NULL, 0, NI_NUMERICHOST);
462 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100463 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
464 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200465 return s;
466 }
467
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200468 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200469}
470
471static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200472 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200473{
474 struct sockaddr_in *sin_a, *sin_b;
475 struct sockaddr_in6 *sin6_a, *sin6_b;
476
477 if (a->sa_family != b->sa_family)
478 return 0;
479
480 switch (a->sa_family) {
481 case AF_INET:
482 sin_a = (struct sockaddr_in *)a;
483 sin_b = (struct sockaddr_in *)b;
484 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
485 sizeof(struct in_addr)))
486 return 1;
487 break;
488 case AF_INET6:
489 sin6_a = (struct sockaddr_in6 *)a;
490 sin6_b = (struct sockaddr_in6 *)b;
491 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
492 sizeof(struct in6_addr)))
493 return 1;
494 break;
495 }
496 return 0;
497}
498
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200499/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200500 * \param[in] addr Socket Address
501 * \param[in] addrlen Length of socket address in bytes
502 * \returns 1 if address is local, 0 otherwise.
503 */
Harald Weltebc32d052012-04-08 11:31:32 +0200504int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200505{
506 struct ifaddrs *ifaddr, *ifa;
507
508 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100509 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
510 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200511 return -EIO;
512 }
513
514 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200515 if (!ifa->ifa_addr)
516 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200517 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
518 return 1;
519 }
520
521 return 0;
522}
Harald Weltee4764422011-05-22 12:25:57 +0200523
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200524/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100525 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
526 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
527 * \param[in] socket_path path to identify the socket
528 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200529 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100530 *
531 * This function creates a new unix domain socket, \a
532 * type and \a proto and optionally binds or connects it, depending on
533 * the value of \a flags parameter.
534 */
535int osmo_sock_unix_init(uint16_t type, uint8_t proto,
536 const char *socket_path, unsigned int flags)
537{
538 struct sockaddr_un local;
539 int sfd, rc, on = 1;
540 unsigned int namelen;
541
542 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
543 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
544 return -EINVAL;
545
546 local.sun_family = AF_UNIX;
547 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
548 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
549
550#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
551 local.sun_len = strlen(local.sun_path);
552#endif
553#if defined(BSD44SOCKETS) || defined(SUN_LEN)
554 namelen = SUN_LEN(&local);
555#else
556 namelen = strlen(local.sun_path) +
557 offsetof(struct sockaddr_un, sun_path);
558#endif
559
560 sfd = socket(AF_UNIX, type, proto);
561 if (sfd < 0)
562 return -1;
563
564 if (flags & OSMO_SOCK_F_CONNECT) {
565 rc = connect(sfd, (struct sockaddr *)&local, namelen);
566 if (rc < 0)
567 goto err;
568 } else {
569 unlink(local.sun_path);
570 rc = bind(sfd, (struct sockaddr *)&local, namelen);
571 if (rc < 0)
572 goto err;
573 }
574
575 if (flags & OSMO_SOCK_F_NONBLOCK) {
576 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100577 LOGP(DLGLOBAL, LOGL_ERROR,
578 "cannot set this socket unblocking: %s\n",
579 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100580 close(sfd);
581 return -EINVAL;
582 }
583 }
584
Harald Weltec47bbda2017-07-13 16:13:26 +0200585 rc = osmo_sock_init_tail(sfd, type, flags);
586 if (rc < 0) {
587 close(sfd);
588 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100589 }
590
591 return sfd;
592err:
593 close(sfd);
594 return -1;
595}
596
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200597/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100598 * \param[out] ofd file descriptor (will be filled in)
599 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
600 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
601 * \param[in] socket_path path to identify the socket
602 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200603 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100604 *
605 * This function creates (and optionall binds/connects) a socket using
606 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
607 */
608int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
609 const char *socket_path, unsigned int flags)
610{
Max862ba652014-10-13 14:54:25 +0200611 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100612}
613
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200614/*! Get address/port information on soocket in dyn-alloc string
Harald Welte48f55832017-01-26 00:03:10 +0100615 * \param[in] ctx talloc context from which to allocate string buffer
616 * \param[in] fd file descriptor of socket
617 * \returns string identifying the connection of this socket
618 */
619char *osmo_sock_get_name(void *ctx, int fd)
620{
621 struct sockaddr sa_l, sa_r;
622 socklen_t sa_len_l = sizeof(sa_l);
623 socklen_t sa_len_r = sizeof(sa_r);
624 char hostbuf_l[64], hostbuf_r[64];
625 char portbuf_l[16], portbuf_r[16];
626 int rc;
627
628 rc = getsockname(fd, &sa_l, &sa_len_l);
629 if (rc < 0)
630 return NULL;
631
632 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
633 portbuf_l, sizeof(portbuf_l),
634 NI_NUMERICHOST | NI_NUMERICSERV);
635 if (rc < 0)
636 return NULL;
637
638 rc = getpeername(fd, &sa_r, &sa_len_r);
639 if (rc < 0)
640 goto local_only;
641
642 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
643 portbuf_r, sizeof(portbuf_r),
644 NI_NUMERICHOST | NI_NUMERICSERV);
645 if (rc < 0)
646 goto local_only;
647
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200648 return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r,
Harald Welte48f55832017-01-26 00:03:10 +0100649 hostbuf_l, portbuf_l);
650
651local_only:
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200652 return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100653}
654
Harald Weltee30d7e62017-07-13 16:02:50 +0200655static int sock_get_domain(int fd)
656{
657 int domain;
658#ifdef SO_DOMAIN
659 socklen_t dom_len = sizeof(domain);
660 int rc;
661
662 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
663 if (rc < 0)
664 return rc;
665#else
666 /* This of course sucks, but what shall we do on OSs like
667 * FreeBSD that don't seem to expose a method by which one can
668 * learn the address family of a socket? */
669 domain = AF_INET;
670#endif
671 return domain;
672}
673
674
675/*! Activate or de-activate local loop-back of transmitted multicast packets
676 * \param[in] fd file descriptor of related socket
677 * \param[in] enable Enable (true) or disable (false) loop-back
678 * \returns 0 on success; negative otherwise */
679int osmo_sock_mcast_loop_set(int fd, bool enable)
680{
681 int domain, loop = 0;
682
683 if (enable)
684 loop = 1;
685
686 domain = sock_get_domain(fd);
687 if (domain < 0)
688 return domain;
689
690 switch (domain) {
691 case AF_INET:
692 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
693 case AF_INET6:
694 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
695 default:
696 return -EINVAL;
697 }
698}
699
700/*! Set the TTL of outbound multicast packets
701 * \param[in] fd file descriptor of related socket
702 * \param[in] ttl TTL of to-be-sent multicast packets
703 * \returns 0 on success; negative otherwise */
704int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
705{
706 int domain, ttli = ttl;
707
708 domain = sock_get_domain(fd);
709 if (domain < 0)
710 return domain;
711
712 switch (domain) {
713 case AF_INET:
714 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
715 case AF_INET6:
716 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
717 default:
718 return -EINVAL;
719 }
720}
721
722/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
723 * \param[in] fd file descriptor of related socket
724 * \param[in] enable Enable or Disable receiving of all packets
725 * \returns 0 on success; negative otherwise */
726int osmo_sock_mcast_all_set(int fd, bool enable)
727{
728 int domain, all = 0;
729
730 if (enable)
731 all = 1;
732
733 domain = sock_get_domain(fd);
734 if (domain < 0)
735 return domain;
736
737 switch (domain) {
738 case AF_INET:
739#ifdef IP_MULTICAST_ALL
740 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
741#endif
742 case AF_INET6:
743 /* there seems no equivalent ?!? */
744 default:
745 return -EINVAL;
746 }
747}
748
749/* FreeBSD calls the socket option differently */
750#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
751#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
752#endif
753
754/*! Subscribe to the given IP multicast group
755 * \param[in] fd file descriptor of related scoket
756 * \param[in] grp_addr ASCII representation of the multicast group address
757 * \returns 0 on success; negative otherwise */
758int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
759{
760 int rc, domain;
761 struct ip_mreq mreq;
762 struct ipv6_mreq mreq6;
763 struct in6_addr i6a;
764
765 domain = sock_get_domain(fd);
766 if (domain < 0)
767 return domain;
768
769 switch (domain) {
770 case AF_INET:
771 memset(&mreq, 0, sizeof(mreq));
772 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
773 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
774 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
775#ifdef IPV6_ADD_MEMBERSHIP
776 case AF_INET6:
777 memset(&mreq6, 0, sizeof(mreq6));
778 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
779 if (rc < 0)
780 return -EINVAL;
781 mreq6.ipv6mr_multiaddr = i6a;
782 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
783#endif
784 default:
785 return -EINVAL;
786 }
787}
788
Philipp Maier2d2490e2017-10-20 19:41:26 +0200789/*! Determine the matching local IP-address for a given remote IP-Address.
790 * \param[out] local_ip caller provided memory for resulting local IP-address
791 * \param[in] remote_ip remote IP-address
792 * \param[in] fd file descriptor of related scoket
793 * \returns 0 on success; negative otherwise
794 *
795 * The function accepts IPv4 and IPv6 address strings. The caller must provide
796 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
797 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
798int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
799{
800 int sfd;
801 int rc;
802 struct addrinfo addrinfo_hint;
803 struct addrinfo *addrinfo = NULL;
804 struct sockaddr_in local_addr;
805 socklen_t local_addr_len;
806 uint16_t family;
807
808 /* Find out the address family (AF_INET or AF_INET6?) */
809 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
810 addrinfo_hint.ai_family = PF_UNSPEC;
811 addrinfo_hint.ai_flags = AI_NUMERICHOST;
812 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
813 if (rc)
814 return -EINVAL;
815 family = addrinfo->ai_family;
816 freeaddrinfo(addrinfo);
817
818 /* Connect a dummy socket to trick the kernel into determining the
819 * ip-address of the interface that would be used if we would send
820 * out an actual packet */
821 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
822 if (sfd < 0)
823 return -EINVAL;
824
825 /* Request the IP address of the interface that the kernel has
826 * actually choosen. */
827 memset(&local_addr, 0, sizeof(local_addr));
828 local_addr_len = sizeof(local_addr);
829 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +0100830 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200831 if (rc < 0)
832 return -EINVAL;
833 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +0100834 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200835 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +0100836 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200837 else
838 return -EINVAL;
839
840 return 0;
841}
842
Harald Weltee4764422011-05-22 12:25:57 +0200843#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200844
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200845/*! @} */