blob: 4f3b1cab10a57b85f48c9725b42698273f0aae33 [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;
417 ofd->when = BSC_FD_READ;
418
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 */
617int osmo_sock_unix_init(uint16_t type, uint8_t proto,
618 const char *socket_path, unsigned int flags)
619{
620 struct sockaddr_un local;
621 int sfd, rc, on = 1;
622 unsigned int namelen;
623
624 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
625 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
626 return -EINVAL;
627
628 local.sun_family = AF_UNIX;
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200629 /* When an AF_UNIX socket is bound, sun_path should be NUL-terminated. See unix(7) man page. */
630 if (osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path)) >= sizeof(local.sun_path)) {
Stefan Sperling896ff6d2018-08-28 14:34:17 +0200631 LOGP(DLGLOBAL, LOGL_ERROR, "Socket path exceeds maximum length of %zd bytes: %s\n",
632 sizeof(local.sun_path), socket_path);
633 return -ENOSPC;
634 }
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100635
636#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200637 local.sun_len = strlen(local.sun_path);
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100638#endif
639#if defined(BSD44SOCKETS) || defined(SUN_LEN)
640 namelen = SUN_LEN(&local);
641#else
Stefan Sperling6afb3f52018-09-20 17:21:05 +0200642 namelen = strlen(local.sun_path) +
643 offsetof(struct sockaddr_un, sun_path);
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100644#endif
645
646 sfd = socket(AF_UNIX, type, proto);
647 if (sfd < 0)
648 return -1;
649
650 if (flags & OSMO_SOCK_F_CONNECT) {
651 rc = connect(sfd, (struct sockaddr *)&local, namelen);
652 if (rc < 0)
653 goto err;
654 } else {
655 unlink(local.sun_path);
656 rc = bind(sfd, (struct sockaddr *)&local, namelen);
657 if (rc < 0)
658 goto err;
659 }
660
661 if (flags & OSMO_SOCK_F_NONBLOCK) {
662 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100663 LOGP(DLGLOBAL, LOGL_ERROR,
664 "cannot set this socket unblocking: %s\n",
665 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100666 close(sfd);
667 return -EINVAL;
668 }
669 }
670
Harald Weltec47bbda2017-07-13 16:13:26 +0200671 rc = osmo_sock_init_tail(sfd, type, flags);
672 if (rc < 0) {
673 close(sfd);
674 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100675 }
676
677 return sfd;
678err:
679 close(sfd);
680 return -1;
681}
682
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200683/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100684 * \param[out] ofd file descriptor (will be filled in)
685 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
686 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
687 * \param[in] socket_path path to identify the socket
688 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200689 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100690 *
691 * This function creates (and optionall binds/connects) a socket using
692 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
693 */
694int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
695 const char *socket_path, unsigned int flags)
696{
Max862ba652014-10-13 14:54:25 +0200697 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100698}
699
Neels Hofmeyr01457512018-12-12 01:48:54 +0100700/*! Get the IP and/or port number on socket in separate string buffers.
Oliver Smith7acd5d02018-10-25 11:16:36 +0200701 * \param[in] fd file descriptor of socket
702 * \param[out] ip IP address (will be filled in when not NULL)
703 * \param[in] ip_len length of the ip buffer
704 * \param[out] port number (will be filled in when not NULL)
705 * \param[in] port_len length of the port buffer
706 * \param[in] local (true) or remote (false) name will get looked at
707 * \returns 0 on success; negative otherwise
708 */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100709int 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 +0200710{
711 struct sockaddr sa;
712 socklen_t len = sizeof(sa);
Oliver Smith860651e2018-10-30 14:31:57 +0100713 char ipbuf[INET6_ADDRSTRLEN], portbuf[6];
Oliver Smith7acd5d02018-10-25 11:16:36 +0200714 int rc;
715
716 rc = local ? getsockname(fd, &sa, &len) : getpeername(fd, &sa, &len);
717 if (rc < 0)
718 return rc;
719
720 rc = getnameinfo(&sa, len, ipbuf, sizeof(ipbuf),
721 portbuf, sizeof(portbuf),
722 NI_NUMERICHOST | NI_NUMERICSERV);
723 if (rc < 0)
724 return rc;
725
726 if (ip)
727 strncpy(ip, ipbuf, ip_len);
728 if (port)
729 strncpy(port, portbuf, port_len);
730 return 0;
731}
732
733/*! Get local IP address on socket
734 * \param[in] fd file descriptor of socket
735 * \param[out] ip IP address (will be filled in)
736 * \param[in] len length of the output buffer
737 * \returns 0 on success; negative otherwise
738 */
739int osmo_sock_get_local_ip(int fd, char *ip, size_t len)
740{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100741 return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, true);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200742}
743
744/*! Get local port on socket
745 * \param[in] fd file descriptor of socket
746 * \param[out] port number (will be filled in)
747 * \param[in] len length of the output buffer
748 * \returns 0 on success; negative otherwise
749 */
750int osmo_sock_get_local_ip_port(int fd, char *port, size_t len)
751{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100752 return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, true);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200753}
754
755/*! Get remote IP address on socket
756 * \param[in] fd file descriptor of socket
757 * \param[out] ip IP address (will be filled in)
758 * \param[in] len length of the output buffer
759 * \returns 0 on success; negative otherwise
760 */
761int osmo_sock_get_remote_ip(int fd, char *ip, size_t len)
762{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100763 return osmo_sock_get_ip_and_port(fd, ip, len, NULL, 0, false);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200764}
765
766/*! Get remote port on socket
767 * \param[in] fd file descriptor of socket
768 * \param[out] port number (will be filled in)
769 * \param[in] len length of the output buffer
770 * \returns 0 on success; negative otherwise
771 */
772int osmo_sock_get_remote_ip_port(int fd, char *port, size_t len)
773{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100774 return osmo_sock_get_ip_and_port(fd, NULL, 0, port, len, false);
Oliver Smith7acd5d02018-10-25 11:16:36 +0200775}
776
Neels Hofmeyr01457512018-12-12 01:48:54 +0100777/*! Get address/port information on socket in dyn-alloc string like "(r=1.2.3.4:5<->l=6.7.8.9:10)".
778 * Usually, it is better to use osmo_sock_get_name2() for a static string buffer or osmo_sock_get_name_buf() for a
779 * caller provided string buffer, to avoid the dynamic talloc allocation.
Harald Welte48f55832017-01-26 00:03:10 +0100780 * \param[in] ctx talloc context from which to allocate string buffer
781 * \param[in] fd file descriptor of socket
Neels Hofmeyr01457512018-12-12 01:48:54 +0100782 * \returns string identifying the connection of this socket, talloc'd from ctx.
Harald Welte48f55832017-01-26 00:03:10 +0100783 */
784char *osmo_sock_get_name(void *ctx, int fd)
785{
Neels Hofmeyr01457512018-12-12 01:48:54 +0100786 /* "r=1.2.3.4:123<->l=5.6.7.8:987" */
787 char str[2 + INET6_ADDRSTRLEN + 1 + 5 + 3 + 2 + INET6_ADDRSTRLEN + 1 + 5 + 1];
788 int rc;
789 rc = osmo_sock_get_name_buf(str, sizeof(str), fd);
790 if (rc <= 0)
791 return NULL;
792 return talloc_asprintf(ctx, "(%s)", str);
793}
794
795/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
796 * This does not include braces like osmo_sock_get_name().
797 * \param[out] str Destination string buffer.
798 * \param[in] str_len sizeof(str).
799 * \param[in] fd File descriptor of socket.
800 * \return String length as returned by snprintf(), or negative on error.
801 */
802int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
803{
Oliver Smith860651e2018-10-30 14:31:57 +0100804 char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
805 char portbuf_l[6], portbuf_r[6];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100806 int rc;
Harald Welte48f55832017-01-26 00:03:10 +0100807
Oliver Smith7acd5d02018-10-25 11:16:36 +0200808 /* get local */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100809 if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true)))
810 return rc;
Harald Welte48f55832017-01-26 00:03:10 +0100811
Oliver Smith7acd5d02018-10-25 11:16:36 +0200812 /* get remote */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100813 if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0)
814 return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100815
Neels Hofmeyr01457512018-12-12 01:48:54 +0100816 return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
817}
818
819/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
820 * This does not include braces like osmo_sock_get_name().
821 * \param[in] fd File descriptor of socket.
822 * \return Static string buffer containing the result.
823 */
824const char *osmo_sock_get_name2(int fd)
825{
826 /* "r=1.2.3.4:123<->l=5.6.7.8:987" */
827 static char str[2 + INET6_ADDRSTRLEN + 1 + 5 + 3 + 2 + INET6_ADDRSTRLEN + 1 + 5 + 1];
828 osmo_sock_get_name_buf(str, sizeof(str), fd);
829 return str;
Harald Welte48f55832017-01-26 00:03:10 +0100830}
831
Harald Weltee30d7e62017-07-13 16:02:50 +0200832static int sock_get_domain(int fd)
833{
834 int domain;
835#ifdef SO_DOMAIN
836 socklen_t dom_len = sizeof(domain);
837 int rc;
838
839 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
840 if (rc < 0)
841 return rc;
842#else
843 /* This of course sucks, but what shall we do on OSs like
844 * FreeBSD that don't seem to expose a method by which one can
845 * learn the address family of a socket? */
846 domain = AF_INET;
847#endif
848 return domain;
849}
850
851
852/*! Activate or de-activate local loop-back of transmitted multicast packets
853 * \param[in] fd file descriptor of related socket
854 * \param[in] enable Enable (true) or disable (false) loop-back
855 * \returns 0 on success; negative otherwise */
856int osmo_sock_mcast_loop_set(int fd, bool enable)
857{
858 int domain, loop = 0;
859
860 if (enable)
861 loop = 1;
862
863 domain = sock_get_domain(fd);
864 if (domain < 0)
865 return domain;
866
867 switch (domain) {
868 case AF_INET:
869 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
870 case AF_INET6:
871 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
872 default:
873 return -EINVAL;
874 }
875}
876
877/*! Set the TTL of outbound multicast packets
878 * \param[in] fd file descriptor of related socket
879 * \param[in] ttl TTL of to-be-sent multicast packets
880 * \returns 0 on success; negative otherwise */
881int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
882{
883 int domain, ttli = ttl;
884
885 domain = sock_get_domain(fd);
886 if (domain < 0)
887 return domain;
888
889 switch (domain) {
890 case AF_INET:
891 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
892 case AF_INET6:
893 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
894 default:
895 return -EINVAL;
896 }
897}
898
899/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
900 * \param[in] fd file descriptor of related socket
901 * \param[in] enable Enable or Disable receiving of all packets
902 * \returns 0 on success; negative otherwise */
903int osmo_sock_mcast_all_set(int fd, bool enable)
904{
905 int domain, all = 0;
906
907 if (enable)
908 all = 1;
909
910 domain = sock_get_domain(fd);
911 if (domain < 0)
912 return domain;
913
914 switch (domain) {
915 case AF_INET:
916#ifdef IP_MULTICAST_ALL
917 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
918#endif
919 case AF_INET6:
920 /* there seems no equivalent ?!? */
921 default:
922 return -EINVAL;
923 }
924}
925
926/* FreeBSD calls the socket option differently */
927#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
928#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
929#endif
930
931/*! Subscribe to the given IP multicast group
932 * \param[in] fd file descriptor of related scoket
933 * \param[in] grp_addr ASCII representation of the multicast group address
934 * \returns 0 on success; negative otherwise */
935int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
936{
937 int rc, domain;
938 struct ip_mreq mreq;
939 struct ipv6_mreq mreq6;
940 struct in6_addr i6a;
941
942 domain = sock_get_domain(fd);
943 if (domain < 0)
944 return domain;
945
946 switch (domain) {
947 case AF_INET:
948 memset(&mreq, 0, sizeof(mreq));
949 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
950 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
951 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
952#ifdef IPV6_ADD_MEMBERSHIP
953 case AF_INET6:
954 memset(&mreq6, 0, sizeof(mreq6));
955 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
956 if (rc < 0)
957 return -EINVAL;
958 mreq6.ipv6mr_multiaddr = i6a;
959 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
960#endif
961 default:
962 return -EINVAL;
963 }
964}
965
Philipp Maier2d2490e2017-10-20 19:41:26 +0200966/*! Determine the matching local IP-address for a given remote IP-Address.
967 * \param[out] local_ip caller provided memory for resulting local IP-address
968 * \param[in] remote_ip remote IP-address
969 * \param[in] fd file descriptor of related scoket
970 * \returns 0 on success; negative otherwise
971 *
972 * The function accepts IPv4 and IPv6 address strings. The caller must provide
973 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
974 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
975int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
976{
977 int sfd;
978 int rc;
979 struct addrinfo addrinfo_hint;
980 struct addrinfo *addrinfo = NULL;
981 struct sockaddr_in local_addr;
982 socklen_t local_addr_len;
983 uint16_t family;
984
985 /* Find out the address family (AF_INET or AF_INET6?) */
986 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
987 addrinfo_hint.ai_family = PF_UNSPEC;
988 addrinfo_hint.ai_flags = AI_NUMERICHOST;
989 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
990 if (rc)
991 return -EINVAL;
992 family = addrinfo->ai_family;
993 freeaddrinfo(addrinfo);
994
995 /* Connect a dummy socket to trick the kernel into determining the
996 * ip-address of the interface that would be used if we would send
997 * out an actual packet */
998 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
999 if (sfd < 0)
1000 return -EINVAL;
1001
1002 /* Request the IP address of the interface that the kernel has
1003 * actually choosen. */
1004 memset(&local_addr, 0, sizeof(local_addr));
1005 local_addr_len = sizeof(local_addr);
1006 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +01001007 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001008 if (rc < 0)
1009 return -EINVAL;
1010 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +01001011 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001012 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +01001013 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001014 else
1015 return -EINVAL;
1016
1017 return 0;
1018}
1019
Harald Weltee4764422011-05-22 12:25:57 +02001020#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +02001021
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001022/*! @} */