blob: c817e723db924e3daca05e8a9d5d1c89e552f3fc [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 */
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 */
Harald Weltec0dfc9d2019-03-18 18:29:43 +0100784char *osmo_sock_get_name(const void *ctx, int fd)
Harald Welte48f55832017-01-26 00:03:10 +0100785{
Philipp Maier64b51eb2019-01-14 11:59:11 +0100786 char str[OSMO_SOCK_NAME_MAXLEN];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100787 int rc;
788 rc = osmo_sock_get_name_buf(str, sizeof(str), fd);
789 if (rc <= 0)
790 return NULL;
791 return talloc_asprintf(ctx, "(%s)", str);
792}
793
794/*! Get address/port information on socket in provided string buffer, like "r=1.2.3.4:5<->l=6.7.8.9:10".
795 * This does not include braces like osmo_sock_get_name().
796 * \param[out] str Destination string buffer.
797 * \param[in] str_len sizeof(str).
798 * \param[in] fd File descriptor of socket.
799 * \return String length as returned by snprintf(), or negative on error.
800 */
801int osmo_sock_get_name_buf(char *str, size_t str_len, int fd)
802{
Oliver Smith860651e2018-10-30 14:31:57 +0100803 char hostbuf_l[INET6_ADDRSTRLEN], hostbuf_r[INET6_ADDRSTRLEN];
804 char portbuf_l[6], portbuf_r[6];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100805 int rc;
Harald Welte48f55832017-01-26 00:03:10 +0100806
Oliver Smith7acd5d02018-10-25 11:16:36 +0200807 /* get local */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100808 if ((rc = osmo_sock_get_ip_and_port(fd, hostbuf_l, sizeof(hostbuf_l), portbuf_l, sizeof(portbuf_l), true)))
809 return rc;
Harald Welte48f55832017-01-26 00:03:10 +0100810
Oliver Smith7acd5d02018-10-25 11:16:36 +0200811 /* get remote */
Neels Hofmeyr01457512018-12-12 01:48:54 +0100812 if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) != 0)
813 return snprintf(str, str_len, "r=NULL<->l=%s:%s", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100814
Neels Hofmeyr01457512018-12-12 01:48:54 +0100815 return snprintf(str, str_len, "r=%s:%s<->l=%s:%s", hostbuf_r, portbuf_r, hostbuf_l, portbuf_l);
816}
817
818/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
819 * This does not include braces like osmo_sock_get_name().
820 * \param[in] fd File descriptor of socket.
821 * \return Static string buffer containing the result.
822 */
823const char *osmo_sock_get_name2(int fd)
824{
Philipp Maier64b51eb2019-01-14 11:59:11 +0100825 static char str[OSMO_SOCK_NAME_MAXLEN];
Neels Hofmeyr01457512018-12-12 01:48:54 +0100826 osmo_sock_get_name_buf(str, sizeof(str), fd);
827 return str;
Harald Welte48f55832017-01-26 00:03:10 +0100828}
829
Harald Welte179f3572019-03-18 18:38:47 +0100830/*! Get address/port information on socket in static string, like "r=1.2.3.4:5<->l=6.7.8.9:10".
831 * This does not include braces like osmo_sock_get_name().
832 * \param[in] fd File descriptor of socket.
833 * \return Static string buffer containing the result.
834 */
835char *osmo_sock_get_name2_c(const void *ctx, int fd)
836{
837 char *str = talloc_size(ctx, OSMO_SOCK_NAME_MAXLEN);
838 if (!str)
839 return NULL;
840 osmo_sock_get_name_buf(str, sizeof(str), fd);
841 return str;
842}
843
Harald Weltee30d7e62017-07-13 16:02:50 +0200844static int sock_get_domain(int fd)
845{
846 int domain;
847#ifdef SO_DOMAIN
848 socklen_t dom_len = sizeof(domain);
849 int rc;
850
851 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
852 if (rc < 0)
853 return rc;
854#else
855 /* This of course sucks, but what shall we do on OSs like
856 * FreeBSD that don't seem to expose a method by which one can
857 * learn the address family of a socket? */
858 domain = AF_INET;
859#endif
860 return domain;
861}
862
863
864/*! Activate or de-activate local loop-back of transmitted multicast packets
865 * \param[in] fd file descriptor of related socket
866 * \param[in] enable Enable (true) or disable (false) loop-back
867 * \returns 0 on success; negative otherwise */
868int osmo_sock_mcast_loop_set(int fd, bool enable)
869{
870 int domain, loop = 0;
871
872 if (enable)
873 loop = 1;
874
875 domain = sock_get_domain(fd);
876 if (domain < 0)
877 return domain;
878
879 switch (domain) {
880 case AF_INET:
881 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
882 case AF_INET6:
883 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
884 default:
885 return -EINVAL;
886 }
887}
888
889/*! Set the TTL of outbound multicast packets
890 * \param[in] fd file descriptor of related socket
891 * \param[in] ttl TTL of to-be-sent multicast packets
892 * \returns 0 on success; negative otherwise */
893int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
894{
895 int domain, ttli = ttl;
896
897 domain = sock_get_domain(fd);
898 if (domain < 0)
899 return domain;
900
901 switch (domain) {
902 case AF_INET:
903 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
904 case AF_INET6:
905 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
906 default:
907 return -EINVAL;
908 }
909}
910
911/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
912 * \param[in] fd file descriptor of related socket
913 * \param[in] enable Enable or Disable receiving of all packets
914 * \returns 0 on success; negative otherwise */
915int osmo_sock_mcast_all_set(int fd, bool enable)
916{
917 int domain, all = 0;
918
919 if (enable)
920 all = 1;
921
922 domain = sock_get_domain(fd);
923 if (domain < 0)
924 return domain;
925
926 switch (domain) {
927 case AF_INET:
928#ifdef IP_MULTICAST_ALL
929 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
930#endif
931 case AF_INET6:
932 /* there seems no equivalent ?!? */
933 default:
934 return -EINVAL;
935 }
936}
937
938/* FreeBSD calls the socket option differently */
939#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
940#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
941#endif
942
943/*! Subscribe to the given IP multicast group
944 * \param[in] fd file descriptor of related scoket
945 * \param[in] grp_addr ASCII representation of the multicast group address
946 * \returns 0 on success; negative otherwise */
947int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
948{
949 int rc, domain;
950 struct ip_mreq mreq;
951 struct ipv6_mreq mreq6;
952 struct in6_addr i6a;
953
954 domain = sock_get_domain(fd);
955 if (domain < 0)
956 return domain;
957
958 switch (domain) {
959 case AF_INET:
960 memset(&mreq, 0, sizeof(mreq));
961 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
962 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
963 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
964#ifdef IPV6_ADD_MEMBERSHIP
965 case AF_INET6:
966 memset(&mreq6, 0, sizeof(mreq6));
967 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
968 if (rc < 0)
969 return -EINVAL;
970 mreq6.ipv6mr_multiaddr = i6a;
971 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
972#endif
973 default:
974 return -EINVAL;
975 }
976}
977
Philipp Maier2d2490e2017-10-20 19:41:26 +0200978/*! Determine the matching local IP-address for a given remote IP-Address.
979 * \param[out] local_ip caller provided memory for resulting local IP-address
980 * \param[in] remote_ip remote IP-address
981 * \param[in] fd file descriptor of related scoket
982 * \returns 0 on success; negative otherwise
983 *
984 * The function accepts IPv4 and IPv6 address strings. The caller must provide
985 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
986 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
987int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
988{
989 int sfd;
990 int rc;
991 struct addrinfo addrinfo_hint;
992 struct addrinfo *addrinfo = NULL;
993 struct sockaddr_in local_addr;
994 socklen_t local_addr_len;
995 uint16_t family;
996
997 /* Find out the address family (AF_INET or AF_INET6?) */
998 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
999 addrinfo_hint.ai_family = PF_UNSPEC;
1000 addrinfo_hint.ai_flags = AI_NUMERICHOST;
1001 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
1002 if (rc)
1003 return -EINVAL;
1004 family = addrinfo->ai_family;
1005 freeaddrinfo(addrinfo);
1006
1007 /* Connect a dummy socket to trick the kernel into determining the
1008 * ip-address of the interface that would be used if we would send
1009 * out an actual packet */
1010 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
1011 if (sfd < 0)
1012 return -EINVAL;
1013
1014 /* Request the IP address of the interface that the kernel has
1015 * actually choosen. */
1016 memset(&local_addr, 0, sizeof(local_addr));
1017 local_addr_len = sizeof(local_addr);
1018 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +01001019 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001020 if (rc < 0)
1021 return -EINVAL;
1022 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +01001023 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001024 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +01001025 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +02001026 else
1027 return -EINVAL;
1028
1029 return 0;
1030}
1031
Harald Weltee4764422011-05-22 12:25:57 +02001032#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +02001033
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001034/*! @} */