blob: c50af55b74d6e96f5fc405c1eba494900f178960 [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>
Neels Hofmeyr59f4caf2018-07-19 22:13:19 +020038#include <osmocom/core/utils.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020039
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020040#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020041#include <sys/socket.h>
42#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010043#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020044
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010045#include <netinet/in.h>
Harald Weltee30d7e62017-07-13 16:02:50 +020046#include <arpa/inet.h>
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010047
Harald Welte33cb71a2011-05-21 18:54:32 +020048#include <stdio.h>
49#include <unistd.h>
50#include <stdint.h>
51#include <string.h>
52#include <errno.h>
53#include <netdb.h>
54#include <ifaddrs.h>
55
Harald Weltedda70fc2017-04-08 20:52:33 +020056static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
57 const char *host, uint16_t port, bool passive)
58{
59 struct addrinfo hints, *result;
Oliver Smith860651e2018-10-30 14:31:57 +010060 char portbuf[6];
Harald Weltedda70fc2017-04-08 20:52:33 +020061 int rc;
62
63 snprintf(portbuf, sizeof(portbuf), "%u", port);
64 memset(&hints, 0, sizeof(struct addrinfo));
65 hints.ai_family = family;
66 if (type == SOCK_RAW) {
67 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
68 * SOCK_RAW and IPPROTO_GRE is used.
69 */
70 hints.ai_socktype = SOCK_DGRAM;
71 hints.ai_protocol = IPPROTO_UDP;
72 } else {
73 hints.ai_socktype = type;
74 hints.ai_protocol = proto;
75 }
76
77 if (passive)
78 hints.ai_flags |= AI_PASSIVE;
79
80 rc = getaddrinfo(host, portbuf, &hints, &result);
81 if (rc != 0) {
82 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
83 host, port, strerror(errno));
84 return NULL;
85 }
86
87 return result;
88}
89
90static int socket_helper(const struct addrinfo *rp, unsigned int flags)
91{
92 int sfd, on = 1;
93
94 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +020095 if (sfd == -1) {
96 LOGP(DLGLOBAL, LOGL_ERROR,
97 "unable to create socket: %s\n", strerror(errno));
Harald Weltedda70fc2017-04-08 20:52:33 +020098 return sfd;
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +020099 }
Harald Weltedda70fc2017-04-08 20:52:33 +0200100 if (flags & OSMO_SOCK_F_NONBLOCK) {
101 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
102 LOGP(DLGLOBAL, LOGL_ERROR,
103 "cannot set this socket unblocking: %s\n",
104 strerror(errno));
105 close(sfd);
106 sfd = -EINVAL;
107 }
108 }
109 return sfd;
110}
111
112
Harald Weltec47bbda2017-07-13 16:13:26 +0200113static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
114{
Harald Weltebc43a622017-07-13 16:20:21 +0200115 int rc;
Harald Weltec47bbda2017-07-13 16:13:26 +0200116
117 /* Make sure to call 'listen' on a bound, connection-oriented sock */
118 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
119 switch (type) {
120 case SOCK_STREAM:
121 case SOCK_SEQPACKET:
122 rc = listen(fd, 10);
Harald Weltebc43a622017-07-13 16:20:21 +0200123 if (rc < 0) {
124 LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
125 strerror(errno));
126 return rc;
127 }
128 break;
Harald Weltec47bbda2017-07-13 16:13:26 +0200129 }
130 }
131
Harald Weltebc43a622017-07-13 16:20:21 +0200132 if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
133 rc = osmo_sock_mcast_loop_set(fd, false);
134 if (rc < 0) {
135 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
136 strerror(errno));
137 return rc;
138 }
139 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200140
Harald Welte37d204a2017-07-13 16:33:16 +0200141 if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
142 rc = osmo_sock_mcast_all_set(fd, false);
143 if (rc < 0) {
144 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
145 strerror(errno));
146 /* do not abort here, as this is just an
147 * optional additional optimization that only
148 * exists on Linux only */
149 }
150 }
Harald Weltebc43a622017-07-13 16:20:21 +0200151 return 0;
Harald Weltec47bbda2017-07-13 16:13:26 +0200152}
153
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200154/*! Initialize a socket (including bind and/or connect)
Harald Weltedda70fc2017-04-08 20:52:33 +0200155 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
156 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
157 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
158 * \param[in] local_host local host name or IP address in string form
159 * \param[in] local_port local port number in host byte order
160 * \param[in] remote_host remote host name or IP address in string form
161 * \param[in] remote_port remote port number in host byte order
162 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
163 * \returns socket file descriptor on success; negative on error
164 *
165 * This function creates a new socket of the designated \a family, \a
166 * type and \a proto and optionally binds it to the \a local_host and \a
167 * local_port as well as optionally connects it to the \a remote_host
168 * and \q remote_port, depending on the value * of \a flags parameter.
169 *
170 * As opposed to \ref osmo_sock_init(), this function allows to combine
171 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
172 * is useful if you want to connect to a remote host/port, but still
173 * want to bind that socket to either a specific local alias IP and/or a
174 * specific local source port.
175 *
176 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
177 * OSMO_SOCK_F_CONNECT, or both.
178 *
179 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
180 * non-blocking mode.
181 */
182int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
183 const char *local_host, uint16_t local_port,
184 const char *remote_host, uint16_t remote_port, unsigned int flags)
185{
186 struct addrinfo *result, *rp;
187 int sfd = -1, rc, on = 1;
188
189 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
190 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
191 "BIND or CONNECT flags\n");
192 return -EINVAL;
193 }
194
195 /* figure out local side of socket */
196 if (flags & OSMO_SOCK_F_BIND) {
197 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
198 if (!result)
199 return -EINVAL;
200
201 for (rp = result; rp != NULL; rp = rp->ai_next) {
202 /* Workaround for glibc again */
203 if (type == SOCK_RAW) {
204 rp->ai_socktype = SOCK_RAW;
205 rp->ai_protocol = proto;
206 }
207
208 sfd = socket_helper(rp, flags);
209 if (sfd < 0)
210 continue;
211
Philipp Maier73196e72018-08-23 20:11:50 +0200212 if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
Philipp Maier99f706d2018-08-01 12:40:36 +0200213 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
214 &on, sizeof(on));
215 if (rc < 0) {
216 LOGP(DLGLOBAL, LOGL_ERROR,
217 "cannot setsockopt socket:"
218 " %s:%u: %s\n",
219 local_host, local_port,
220 strerror(errno));
221 close(sfd);
222 continue;
223 }
Harald Weltedda70fc2017-04-08 20:52:33 +0200224 }
Philipp Maier99f706d2018-08-01 12:40:36 +0200225
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200226 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
227 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
228 local_host, local_port, strerror(errno));
229 close(sfd);
230 continue;
231 }
232 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200233 }
234 freeaddrinfo(result);
235 if (rp == NULL) {
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200236 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
237 local_host, local_port);
Harald Weltedda70fc2017-04-08 20:52:33 +0200238 return -ENODEV;
239 }
240 }
241
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200242 /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
243 was already closed and func returned. If OSMO_SOCK_F_BIND is not
244 set, then sfd = -1 */
245
Harald Weltedda70fc2017-04-08 20:52:33 +0200246 /* figure out remote side of socket */
247 if (flags & OSMO_SOCK_F_CONNECT) {
248 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
249 if (!result) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200250 if (sfd >= 0)
251 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200252 return -EINVAL;
253 }
254
255 for (rp = result; rp != NULL; rp = rp->ai_next) {
256 /* Workaround for glibc again */
257 if (type == SOCK_RAW) {
258 rp->ai_socktype = SOCK_RAW;
259 rp->ai_protocol = proto;
260 }
261
Harald Welte5cfa6dc2017-07-21 16:52:29 +0200262 if (sfd < 0) {
Harald Weltedda70fc2017-04-08 20:52:33 +0200263 sfd = socket_helper(rp, flags);
264 if (sfd < 0)
265 continue;
266 }
267
268 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200269 if (rc != 0 && errno != EINPROGRESS) {
270 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
271 remote_host, remote_port, strerror(errno));
272 /* We want to maintain the bind socket if bind was enabled */
273 if (!(flags & OSMO_SOCK_F_BIND)) {
274 close(sfd);
275 sfd = -1;
276 }
277 continue;
278 }
279 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200280 }
281 freeaddrinfo(result);
282 if (rp == NULL) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200283 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
284 remote_host, remote_port);
285 if (sfd >= 0)
286 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200287 return -ENODEV;
288 }
289 }
290
Harald Weltec47bbda2017-07-13 16:13:26 +0200291 rc = osmo_sock_init_tail(sfd, type, flags);
292 if (rc < 0) {
293 close(sfd);
294 sfd = -1;
Harald Weltedda70fc2017-04-08 20:52:33 +0200295 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200296
Harald Weltedda70fc2017-04-08 20:52:33 +0200297 return sfd;
298}
299
300
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200301/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200302 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
303 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
304 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
305 * \param[in] host remote host name or IP address in string form
306 * \param[in] port remote port number in host byte order
307 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200308 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200309 *
310 * This function creates a new socket of the designated \a family, \a
311 * type and \a proto and optionally binds or connects it, depending on
312 * the value of \a flags parameter.
313 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200314int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200315 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200316{
Harald Weltedda70fc2017-04-08 20:52:33 +0200317 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200318 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200319
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200320 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200321 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100322 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200323 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200324 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200325 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200326
Harald Weltedda70fc2017-04-08 20:52:33 +0200327 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
328 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100329 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200330 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200331 return -EINVAL;
332 }
333
334 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100335 /* Workaround for glibc again */
336 if (type == SOCK_RAW) {
337 rp->ai_socktype = SOCK_RAW;
338 rp->ai_protocol = proto;
339 }
340
Harald Weltedda70fc2017-04-08 20:52:33 +0200341 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200342 if (sfd == -1)
343 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200344
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200345 if (flags & OSMO_SOCK_F_CONNECT) {
346 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200347 if (rc != 0 && errno != EINPROGRESS) {
348 close(sfd);
349 continue;
350 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200351 } else {
Philipp Maier73196e72018-08-23 20:11:50 +0200352 if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
Philipp Maier99f706d2018-08-01 12:40:36 +0200353 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
354 &on, sizeof(on));
355 if (rc < 0) {
356 LOGP(DLGLOBAL, LOGL_ERROR,
357 "cannot setsockopt socket:"
358 " %s:%u: %s\n",
359 host, port, strerror(errno));
360 close(sfd);
361 continue;
362 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200363 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200364 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
365 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:"
366 "%s:%u: %s\n",
367 host, port, strerror(errno));
368 close(sfd);
369 continue;
370 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200371 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200372 break;
Harald Welte33cb71a2011-05-21 18:54:32 +0200373 }
374 freeaddrinfo(result);
375
376 if (rp == NULL) {
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200377 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n",
378 host, port);
Harald Welte33cb71a2011-05-21 18:54:32 +0200379 return -ENODEV;
380 }
Harald Welte68b15742011-05-22 21:47:29 +0200381
Philipp Maier73196e72018-08-23 20:11:50 +0200382 if (proto != IPPROTO_UDP || flags & OSMO_SOCK_F_UDP_REUSEADDR) {
Philipp Maier99f706d2018-08-01 12:40:36 +0200383 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
384 if (rc < 0) {
385 LOGP(DLGLOBAL, LOGL_ERROR,
386 "cannot setsockopt socket: %s:%u: %s\n", host,
387 port, strerror(errno));
388 close(sfd);
389 sfd = -1;
390 }
Philipp Maier0659c5d2018-08-01 12:43:08 +0200391 }
Harald Welte68b15742011-05-22 21:47:29 +0200392
Harald Weltec47bbda2017-07-13 16:13:26 +0200393 rc = osmo_sock_init_tail(sfd, type, flags);
394 if (rc < 0) {
395 close(sfd);
396 sfd = -1;
Harald Welte68b15742011-05-22 21:47:29 +0200397 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200398
Harald Welte68b15742011-05-22 21:47:29 +0200399 return sfd;
400}
401
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200402/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200403 * \param[out] ofd file descriptor (will be filled in)
404 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200405 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200406 *
407 * This function fills the \a ofd structure.
408 */
409static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
410{
411 int rc;
412
413 if (sfd < 0)
414 return sfd;
415
416 ofd->fd = sfd;
Harald Welte16886992019-03-20 10:26:39 +0100417 ofd->when = OSMO_FD_READ;
Max862ba652014-10-13 14:54:25 +0200418
419 rc = osmo_fd_register(ofd);
420 if (rc < 0) {
421 close(sfd);
422 return rc;
423 }
424
425 return sfd;
426}
427
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200428/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100429 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200430 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
431 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
432 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
433 * \param[in] host remote host name or IP address in string form
434 * \param[in] port remote port number in host byte order
435 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200436 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200437 *
438 * This function creates (and optionall binds/connects) a socket using
439 * \ref osmo_sock_init, but also fills the \a ofd structure.
440 */
Harald Welte68b15742011-05-22 21:47:29 +0200441int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200442 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200443{
Max862ba652014-10-13 14:54:25 +0200444 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200445}
446
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200447/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200448 * \param[out] ofd file descriptor (will be filled in)
449 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
450 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
451 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
452 * \param[in] local_host local host name or IP address in string form
453 * \param[in] local_port local port number in host byte order
454 * \param[in] remote_host remote host name or IP address in string form
455 * \param[in] remote_port remote port number in host byte order
456 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
457 * \returns socket fd on success; negative on error
458 *
459 * This function creates (and optionall binds/connects) a socket using
460 * \ref osmo_sock_init2, but also fills the \a ofd structure.
461 */
462int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
463 const char *local_host, uint16_t local_port,
464 const char *remote_host, uint16_t remote_port, unsigned int flags)
465{
466 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
467 local_port, remote_host, remote_port, flags));
468}
469
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200470/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200471 * \param[out] ss socket address (will be filled in)
472 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
473 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
474 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200475 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200476 *
477 * This function creates (and optionall binds/connects) a socket using
478 * \ref osmo_sock_init, but also fills the \a ss structure.
479 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200480int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200481 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200482{
483 char host[NI_MAXHOST];
484 uint16_t port;
485 struct sockaddr_in *sin;
486 struct sockaddr_in6 *sin6;
487 int s, sa_len;
488
489 /* determine port and host from ss */
490 switch (ss->sa_family) {
491 case AF_INET:
492 sin = (struct sockaddr_in *) ss;
493 sa_len = sizeof(struct sockaddr_in);
494 port = ntohs(sin->sin_port);
495 break;
496 case AF_INET6:
497 sin6 = (struct sockaddr_in6 *) ss;
498 sa_len = sizeof(struct sockaddr_in6);
499 port = ntohs(sin6->sin6_port);
500 break;
501 default:
502 return -EINVAL;
503 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200504
505 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
506 NULL, 0, NI_NUMERICHOST);
507 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100508 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
509 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200510 return s;
511 }
512
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200513 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200514}
515
516static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200517 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200518{
519 struct sockaddr_in *sin_a, *sin_b;
520 struct sockaddr_in6 *sin6_a, *sin6_b;
521
522 if (a->sa_family != b->sa_family)
523 return 0;
524
525 switch (a->sa_family) {
526 case AF_INET:
527 sin_a = (struct sockaddr_in *)a;
528 sin_b = (struct sockaddr_in *)b;
529 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
530 sizeof(struct in_addr)))
531 return 1;
532 break;
533 case AF_INET6:
534 sin6_a = (struct sockaddr_in6 *)a;
535 sin6_b = (struct sockaddr_in6 *)b;
536 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
537 sizeof(struct in6_addr)))
538 return 1;
539 break;
540 }
541 return 0;
542}
543
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200544/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200545 * \param[in] addr Socket Address
546 * \param[in] addrlen Length of socket address in bytes
547 * \returns 1 if address is local, 0 otherwise.
548 */
Harald Weltebc32d052012-04-08 11:31:32 +0200549int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200550{
551 struct ifaddrs *ifaddr, *ifa;
552
553 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100554 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
555 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200556 return -EIO;
557 }
558
559 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200560 if (!ifa->ifa_addr)
561 continue;
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200562 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) {
563 freeifaddrs(ifaddr);
Harald Welte33cb71a2011-05-21 18:54:32 +0200564 return 1;
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200565 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200566 }
567
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200568 freeifaddrs(ifaddr);
Harald Welte33cb71a2011-05-21 18:54:32 +0200569 return 0;
570}
Harald Weltee4764422011-05-22 12:25:57 +0200571
Max9d7a2472018-11-20 15:18:31 +0100572/*! Convert sockaddr_in to IP address as char string and port as uint16_t.
573 * \param[out] addr String buffer to write IP address to, or NULL.
574 * \param[out] addr_len Size of \a addr.
575 * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
576 * \param[in] sin Sockaddr to convert.
577 * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
578 */
579size_t osmo_sockaddr_in_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
580 const struct sockaddr_in *sin)
581{
582 if (port)
583 *port = ntohs(sin->sin_port);
584
585 if (addr)
586 return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len);
587
588 return 0;
589}
590
Neels Hofmeyr59f4caf2018-07-19 22:13:19 +0200591/*! Convert sockaddr to IP address as char string and port as uint16_t.
592 * \param[out] addr String buffer to write IP address to, or NULL.
593 * \param[out] addr_len Size of \a addr.
594 * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
595 * \param[in] sa Sockaddr to convert.
596 * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
597 */
598unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
599 const struct sockaddr *sa)
600{
601 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
602
Max9d7a2472018-11-20 15:18:31 +0100603 return osmo_sockaddr_in_to_str_and_uint(addr, addr_len, port, sin);
Neels Hofmeyr59f4caf2018-07-19 22:13:19 +0200604}
605
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200606/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100607 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
608 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
609 * \param[in] socket_path path to identify the socket
610 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200611 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100612 *
613 * This function creates a new unix domain socket, \a
614 * type and \a proto and optionally binds or connects it, depending on
615 * the value of \a flags parameter.
616 */
Eric Wildeb5769b2019-06-27 15:31:17 +0200617#if defined(__clang__) && defined(SUN_LEN)
618__attribute__((no_sanitize("undefined")))
619#endif
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100620int osmo_sock_unix_init(uint16_t type, uint8_t proto,
621 const char *socket_path, unsigned int flags)
622{
623 struct sockaddr_un local;
624 int sfd, rc, on = 1;
625 unsigned int namelen;
626
627 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
628 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
629 return -EINVAL;
630
631 local.sun_family = AF_UNIX;
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200632 /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */
633 if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) {
Stefan Sperling896ff6d2018-08-28 14:34:17 +0200634 LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n",
635 sizeof(local.sun_path), socket_path);
636 return -ENOSPC;
637 }
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100638
639#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200640 local.sun_len = strlen(local.sun_path);
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100641#endif
642#if defined(BSD44SOCKETS) || defined(SUN_LEN)
643 namelen = SUN_LEN(&local);
644#else
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200645 namelen = strlen(local.sun_path) +
646 offsetof(struct sockaddr_un, sun_path);
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100647#endif
648
649 sfd = socket(AF_UNIX, type, proto);
650 if (sfd < 0)
651 return -1;
652
653 if (flags & OSMO_SOCK_F_CONNECT) {
654 rc = connect(sfd, (struct sockaddr *)&local, namelen);
655 if (rc < 0)
656 goto err;
657 } else {
658 unlink(local.sun_path);
659 rc = bind(sfd, (struct sockaddr *)&local, namelen);
660 if (rc < 0)
661 goto err;
662 }
663
664 if (flags & OSMO_SOCK_F_NONBLOCK) {
665 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100666 LOGP(DLGLOBAL, LOGL_ERROR,
667 "cannot set this socket unblocking: %s\n",
668 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100669 close(sfd);
670 return -EINVAL;
671 }
672 }
673
Harald Weltec47bbda2017-07-13 16:13:26 +0200674 rc = osmo_sock_init_tail(sfd, type, flags);
675 if (rc < 0) {
676 close(sfd);
677 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100678 }
679
680 return sfd;
681err:
682 close(sfd);
683 return -1;
684}
685
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200686/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100687 * \param[out] ofd file descriptor (will be filled in)
688 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
689 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
690 * \param[in] socket_path path to identify the socket
691 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200692 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100693 *
Vadim Yanitskiyb606d762019-06-01 19:02:47 +0700694 * This function creates (and optionally binds/connects) a socket
695 * using osmo_sock_unix_init, but also fills the ofd structure.
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100696 */
697int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
698 const char *socket_path, unsigned int flags)
699{
Max862ba652014-10-13 14:54:25 +0200700 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100701}
702
Neels Hofmeyr01457512018-12-12 01:48:54 +0100703/*! Get the IP and/or port number on socket in separate string buffers.
Oliver Smith7acd5d02018-10-25 11:16:36 +0200704 * \param[in] fd file descriptor of socket
705 * \param[out] ip IP address (will be filled in when not NULL)
706 * \param[in] ip_len length of the ip buffer
707 * \param[out] port number (will be filled in when not NULL)
708 * \param[in] port_len length of the port buffer
709 * \param[in] local (true) or remote (false) name will get looked at
710 * \returns 0 on success; negative otherwise
711 */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100712int osmo_sock_get_ip_and_port(int fd, char *ip, size_t ip_len, char *port, size_t port_len, bool local)
Oliver Smith7acd5d02018-10-25 11:16:36 +0200713{
714 struct sockaddr sa;
715 socklen_t len = sizeof(sa);
Oliver Smith860651e2018-10-30 14:31:57 +0100716 char ipbuf[INET6_ADDRSTRLEN], portbuf[6];
Oliver Smith7acd5d02018-10-25 11:16:36 +0200717 int rc;
718
719 rc = local ? getsockname(fd, &sa, &len) : getpeername(fd, &sa, &len);
720 if (rc < 0)
721 return rc;
722
723 rc = getnameinfo(&sa, len, ipbuf, sizeof(ipbuf),
724 portbuf, sizeof(portbuf),
725 NI_NUMERICHOST | NI_NUMERICSERV);
726 if (rc < 0)
727 return rc;
728
729 if (ip)
730 strncpy(ip, ipbuf, ip_len);
731 if (port)
732 strncpy(port, portbuf, port_len);
733 return 0;
734}
735
736/*! Get local IP address on socket
737 * \param[in] fd file descriptor of socket
738 * \param[out] ip IP address (will be filled in)
739 * \param[in] len length of the output buffer
740 * \returns 0 on success; negative otherwise
741 */
742int osmo_sock_get_local_ip(int fd, char *ip, size_t len)
743{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100744 return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200745}
746
747/*! Get local port on socket
748 * \param[in] fd file descriptor of socket
749 * \param[out] port number (will be filled in)
750 * \param[in] len length of the output buffer
751 * \returns 0 on success; negative otherwise
752 */
753int osmo_sock_get_local_ip_port(int fd, char *port, size_t len)
754{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100755 return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200756}
757
758/*! Get remote IP address on socket
759 * \param[in] fd file descriptor of socket
760 * \param[out] ip IP address (will be filled in)
761 * \param[in] len length of the output buffer
762 * \returns 0 on success; negative otherwise
763 */
764int osmo_sock_get_remote_ip(int fd, char *ip, size_t len)
765{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100766 return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200767}
768
769/*! Get remote port on socket
770 * \param[in] fd file descriptor of socket
771 * \param[out] port number (will be filled in)
772 * \param[in] len length of the output buffer
773 * \returns 0 on success; negative otherwise
774 */
775int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len)
776{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100777 return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200778}
779
Neels Hofmeyr01457512018-12-12 01:48:54 +0100780/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)".
781 * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a
782 * caller provided string buffer, to avoid the dynamic talloc allocation.
Harald Welte48f55832017-01-26 00:03:10 +0100783 * \param[in] ctx talloc context from which to allocate string buffer
784 * \param[in] fd file descriptor of socket
Neels Hofmeyr01457512018-12-12 01:48:54 +0100785 * \returns string identifying the connection of this socket, talloc'd from ctx.
Harald Welte48f55832017-01-26 00:03:10 +0100786 */
Harald Weltec0dfc9d2019-03-18 18:29:43 +0100787char *osmo_sock_get_name(const void *ctx, int fd)
Harald Welte48f55832017-01-26 00:03:10 +0100788{
Philipp Maier64b51eb2019-01-14 11:59:11 +0100789 char str[OSMO_SOCK_NAME_MAXLEN];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100790 int rc;
791 rc = osmo_sock_get_name_buf(str, sizeof(str), fd);
792 if (rc <= 0)
793 return NULL;
794 return talloc_asprintf(ctx, "(%s)", str);
795}
796
797/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
798 * This does not include braces like osmo_sock_get_name().
799 * \param[out] str Destination string buffer.
800 * \param[in] str_len sizeof(str).
801 * \param[in] fd File descriptor of socket.
802 * \return String length as returned by snprintf(), or negative on error.
803 */
804int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
805{
Oliver Smith860651e2018-10-30 14:31:57 +0100806 char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
807 char portbuf_l[6], portbuf_r[6];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100808 int rc;
Harald Welte48f55832017-01-26 00:03:10 +0100809
Oliver Smith7acd5d02018-10-25 11:16:36 +0200810 /* get local */
Harald Welteea9ea522019-05-10 09:28:24 +0200811 if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true))) {
812 osmo_strlcpy(str, "<error-in-getsockname>", str_len);
Neels Hofmeyr01457512018-12-12 01:48:54 +0100813 return rc;
Harald Welteea9ea522019-05-10 09:28:24 +0200814 }
Harald Welte48f55832017-01-26 00:03:10 +0100815
Oliver Smith7acd5d02018-10-25 11:16:36 +0200816 /* get remote */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100817 if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0)
818 return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100819
Neels Hofmeyr01457512018-12-12 01:48:54 +0100820 return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
821}
822
823/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
824 * This does not include braces like osmo_sock_get_name().
825 * \param[in] fd File descriptor of socket.
826 * \return Static string buffer containing the result.
827 */
828const char *osmo_sock_get_name2(int fd)
829{
Harald Welte171ef822019-03-28 10:49:05 +0100830 static __thread char str[OSMO_SOCK_NAME_MAXLEN];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100831 osmo_sock_get_name_buf(str, sizeof(str), fd);
832 return str;
Harald Welte48f55832017-01-26 00:03:10 +0100833}
834
Harald Welte179f3572019-03-18 18:38:47 +0100835/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
836 * This does not include braces like osmo_sock_get_name().
837 * \param[in] fd File descriptor of socket.
838 * \return Static string buffer containing the result.
839 */
840char *osmo_sock_get_name2_c(const void *ctx, int fd)
841{
842 char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN);
843 if (!str)
844 return NULL;
Vadim Yanitskiy4f619c22019-04-12 21:48:07 +0700845 osmo_sock_get_name_buf(str, OSMO_SOCK_NAME_MAXLEN, fd);
Harald Welte179f3572019-03-18 18:38:47 +0100846 return str;
847}
848
Harald Weltee30d7e62017-07-13 16:02:50 +0200849static int sock_get_domain(int fd)
850{
851 int domain;
852#ifdef SO_DOMAIN
853 socklen_t dom_len = sizeof(domain);
854 int rc;
855
856 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
857 if (rc < 0)
858 return rc;
859#else
860 /* This of course sucks, but what shall we do on OSs like
861 * FreeBSD that don't seem to expose a method by which one can
862 * learn the address family of a socket? */
863 domain = AF_INET;
864#endif
865 return domain;
866}
867
868
869/*! Activate or de-activate local loop-back of transmitted multicast packets
870 * \param[in] fd file descriptor of related socket
871 * \param[in] enable Enable (true) or disable (false) loop-back
872 * \returns 0 on success; negative otherwise */
873int osmo_sock_mcast_loop_set(int fd, bool enable)
874{
875 int domain, loop = 0;
876
877 if (enable)
878 loop = 1;
879
880 domain = sock_get_domain(fd);
881 if (domain < 0)
882 return domain;
883
884 switch (domain) {
885 case AF_INET:
886 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
887 case AF_INET6:
888 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
889 default:
890 return -EINVAL;
891 }
892}
893
894/*! Set the TTL of outbound multicast packets
895 * \param[in] fd file descriptor of related socket
896 * \param[in] ttl TTL of to-be-sent multicast packets
897 * \returns 0 on success; negative otherwise */
898int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
899{
900 int domain, ttli = ttl;
901
902 domain = sock_get_domain(fd);
903 if (domain < 0)
904 return domain;
905
906 switch (domain) {
907 case AF_INET:
908 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
909 case AF_INET6:
910 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
911 default:
912 return -EINVAL;
913 }
914}
915
916/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
917 * \param[in] fd file descriptor of related socket
918 * \param[in] enable Enable or Disable receiving of all packets
919 * \returns 0 on success; negative otherwise */
920int osmo_sock_mcast_all_set(int fd, bool enable)
921{
922 int domain, all = 0;
923
924 if (enable)
925 all = 1;
926
927 domain = sock_get_domain(fd);
928 if (domain < 0)
929 return domain;
930
931 switch (domain) {
932 case AF_INET:
933#ifdef IP_MULTICAST_ALL
934 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
935#endif
936 case AF_INET6:
937 /* there seems no equivalent ?!? */
938 default:
939 return -EINVAL;
940 }
941}
942
943/* FreeBSD calls the socket option differently */
944#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
945#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
946#endif
947
948/*! Subscribe to the given IP multicast group
949 * \param[in] fd file descriptor of related scoket
950 * \param[in] grp_addr ASCII representation of the multicast group address
951 * \returns 0 on success; negative otherwise */
952int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
953{
954 int rc, domain;
955 struct ip_mreq mreq;
956 struct ipv6_mreq mreq6;
957 struct in6_addr i6a;
958
959 domain = sock_get_domain(fd);
960 if (domain < 0)
961 return domain;
962
963 switch (domain) {
964 case AF_INET:
965 memset(&mreq, 0, sizeof(mreq));
966 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
967 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
968 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
969#ifdef IPV6_ADD_MEMBERSHIP
970 case AF_INET6:
971 memset(&mreq6, 0, sizeof(mreq6));
972 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
973 if (rc < 0)
974 return -EINVAL;
975 mreq6.ipv6mr_multiaddr = i6a;
976 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
977#endif
978 default:
979 return -EINVAL;
980 }
981}
982
Philipp Maier2d2490e2017-10-20 19:41:26 +0200983/*! Determine the matching local IP-address for a given remote IP-Address.
984 * \param[out] local_ip caller provided memory for resulting local IP-address
985 * \param[in] remote_ip remote IP-address
986 * \param[in] fd file descriptor of related scoket
987 * \returns 0 on success; negative otherwise
988 *
989 * The function accepts IPv4 and IPv6 address strings. The caller must provide
990 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
991 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
992int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
993{
994 int sfd;
995 int rc;
996 struct addrinfo addrinfo_hint;
997 struct addrinfo *addrinfo = NULL;
998 struct sockaddr_in local_addr;
999 socklen_t local_addr_len;
1000 uint16_t family;
1001
1002 /* Find out the address family (AF_INET or AF_INET6?) */
1003 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
1004 addrinfo_hint.ai_family = PF_UNSPEC;
1005 addrinfo_hint.ai_flags = AI_NUMERICHOST;
1006 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
1007 if (rc)
1008 return -EINVAL;
1009 family = addrinfo->ai_family;
1010 freeaddrinfo(addrinfo);
1011
1012 /* Connect a dummy socket to trick the kernel into determining the
1013 * ip-address of the interface that would be used if we would send
1014 * out an actual packet */
1015 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
1016 if (sfd < 0)
1017 return -EINVAL;
1018
1019 /* Request the IP address of the interface that the kernel has
1020 * actually choosen. */
1021 memset(&local_addr, 0, sizeof(local_addr));
1022 local_addr_len = sizeof(local_addr);
1023 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +01001024 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001025 if (rc < 0)
1026 return -EINVAL;
1027 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +01001028 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001029 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +01001030 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001031 else
1032 return -EINVAL;
1033
1034 return 0;
1035}
1036
Harald Weltee4764422011-05-22 12:25:57 +02001037#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +02001038
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001039/*! @} */