blob: dc5590c05f79cdab36d8ae1e73da12a4a6481170 [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;
60 char portbuf[16];
61 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
212 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
213 &on, sizeof(on));
214 if (rc < 0) {
215 LOGP(DLGLOBAL, LOGL_ERROR,
216 "cannot setsockopt socket:"
217 " %s:%u: %s\n",
218 local_host, local_port, strerror(errno));
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200219 close(sfd);
220 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200221 }
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200222 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
223 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
224 local_host, local_port, strerror(errno));
225 close(sfd);
226 continue;
227 }
228 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200229 }
230 freeaddrinfo(result);
231 if (rp == NULL) {
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200232 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
233 local_host, local_port);
Harald Weltedda70fc2017-04-08 20:52:33 +0200234 return -ENODEV;
235 }
236 }
237
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200238 /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
239 was already closed and func returned. If OSMO_SOCK_F_BIND is not
240 set, then sfd = -1 */
241
Harald Weltedda70fc2017-04-08 20:52:33 +0200242 /* figure out remote side of socket */
243 if (flags & OSMO_SOCK_F_CONNECT) {
244 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
245 if (!result) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200246 if (sfd >= 0)
247 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200248 return -EINVAL;
249 }
250
251 for (rp = result; rp != NULL; rp = rp->ai_next) {
252 /* Workaround for glibc again */
253 if (type == SOCK_RAW) {
254 rp->ai_socktype = SOCK_RAW;
255 rp->ai_protocol = proto;
256 }
257
Harald Welte5cfa6dc2017-07-21 16:52:29 +0200258 if (sfd < 0) {
Harald Weltedda70fc2017-04-08 20:52:33 +0200259 sfd = socket_helper(rp, flags);
260 if (sfd < 0)
261 continue;
262 }
263
264 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200265 if (rc != 0 && errno != EINPROGRESS) {
266 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
267 remote_host, remote_port, strerror(errno));
268 /* We want to maintain the bind socket if bind was enabled */
269 if (!(flags & OSMO_SOCK_F_BIND)) {
270 close(sfd);
271 sfd = -1;
272 }
273 continue;
274 }
275 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200276 }
277 freeaddrinfo(result);
278 if (rp == NULL) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200279 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
280 remote_host, remote_port);
281 if (sfd >= 0)
282 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200283 return -ENODEV;
284 }
285 }
286
Harald Weltec47bbda2017-07-13 16:13:26 +0200287 rc = osmo_sock_init_tail(sfd, type, flags);
288 if (rc < 0) {
289 close(sfd);
290 sfd = -1;
Harald Weltedda70fc2017-04-08 20:52:33 +0200291 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200292
Harald Weltedda70fc2017-04-08 20:52:33 +0200293 return sfd;
294}
295
296
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200297/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200298 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
299 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
300 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
301 * \param[in] host remote host name or IP address in string form
302 * \param[in] port remote port number in host byte order
303 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200304 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200305 *
306 * This function creates a new socket of the designated \a family, \a
307 * type and \a proto and optionally binds or connects it, depending on
308 * the value of \a flags parameter.
309 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200310int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200311 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200312{
Harald Weltedda70fc2017-04-08 20:52:33 +0200313 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200314 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200315
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200316 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200317 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100318 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200319 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200320 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200321 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200322
Harald Weltedda70fc2017-04-08 20:52:33 +0200323 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
324 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100325 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200326 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200327 return -EINVAL;
328 }
329
330 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100331 /* Workaround for glibc again */
332 if (type == SOCK_RAW) {
333 rp->ai_socktype = SOCK_RAW;
334 rp->ai_protocol = proto;
335 }
336
Harald Weltedda70fc2017-04-08 20:52:33 +0200337 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200338 if (sfd == -1)
339 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200340
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200341 if (flags & OSMO_SOCK_F_CONNECT) {
342 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200343 if (rc != 0 && errno != EINPROGRESS) {
344 close(sfd);
345 continue;
346 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200347 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200348 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
349 &on, sizeof(on));
350 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100351 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200352 "cannot setsockopt socket:"
353 " %s:%u: %s\n",
354 host, port, strerror(errno));
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200355 close(sfd);
356 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200357 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200358 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
359 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:"
360 "%s:%u: %s\n",
361 host, port, strerror(errno));
362 close(sfd);
363 continue;
364 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200365 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200366 break;
Harald Welte33cb71a2011-05-21 18:54:32 +0200367 }
368 freeaddrinfo(result);
369
370 if (rp == NULL) {
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200371 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n",
372 host, port);
Harald Welte33cb71a2011-05-21 18:54:32 +0200373 return -ENODEV;
374 }
Harald Welte68b15742011-05-22 21:47:29 +0200375
376 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
377
Harald Weltec47bbda2017-07-13 16:13:26 +0200378 rc = osmo_sock_init_tail(sfd, type, flags);
379 if (rc < 0) {
380 close(sfd);
381 sfd = -1;
Harald Welte68b15742011-05-22 21:47:29 +0200382 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200383
Harald Welte68b15742011-05-22 21:47:29 +0200384 return sfd;
385}
386
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200387/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200388 * \param[out] ofd file descriptor (will be filled in)
389 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200390 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200391 *
392 * This function fills the \a ofd structure.
393 */
394static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
395{
396 int rc;
397
398 if (sfd < 0)
399 return sfd;
400
401 ofd->fd = sfd;
402 ofd->when = BSC_FD_READ;
403
404 rc = osmo_fd_register(ofd);
405 if (rc < 0) {
406 close(sfd);
407 return rc;
408 }
409
410 return sfd;
411}
412
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200413/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100414 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200415 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
416 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
417 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
418 * \param[in] host remote host name or IP address in string form
419 * \param[in] port remote port number in host byte order
420 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200421 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200422 *
423 * This function creates (and optionall binds/connects) a socket using
424 * \ref osmo_sock_init, but also fills the \a ofd structure.
425 */
Harald Welte68b15742011-05-22 21:47:29 +0200426int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200427 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200428{
Max862ba652014-10-13 14:54:25 +0200429 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200430}
431
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200432/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200433 * \param[out] ofd file descriptor (will be filled in)
434 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
435 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
436 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
437 * \param[in] local_host local host name or IP address in string form
438 * \param[in] local_port local port number in host byte order
439 * \param[in] remote_host remote host name or IP address in string form
440 * \param[in] remote_port remote port number in host byte order
441 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
442 * \returns socket fd on success; negative on error
443 *
444 * This function creates (and optionall binds/connects) a socket using
445 * \ref osmo_sock_init2, but also fills the \a ofd structure.
446 */
447int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
448 const char *local_host, uint16_t local_port,
449 const char *remote_host, uint16_t remote_port, unsigned int flags)
450{
451 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
452 local_port, remote_host, remote_port, flags));
453}
454
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200455/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200456 * \param[out] ss socket address (will be filled in)
457 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
458 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
459 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200460 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200461 *
462 * This function creates (and optionall binds/connects) a socket using
463 * \ref osmo_sock_init, but also fills the \a ss structure.
464 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200465int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200466 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200467{
468 char host[NI_MAXHOST];
469 uint16_t port;
470 struct sockaddr_in *sin;
471 struct sockaddr_in6 *sin6;
472 int s, sa_len;
473
474 /* determine port and host from ss */
475 switch (ss->sa_family) {
476 case AF_INET:
477 sin = (struct sockaddr_in *) ss;
478 sa_len = sizeof(struct sockaddr_in);
479 port = ntohs(sin->sin_port);
480 break;
481 case AF_INET6:
482 sin6 = (struct sockaddr_in6 *) ss;
483 sa_len = sizeof(struct sockaddr_in6);
484 port = ntohs(sin6->sin6_port);
485 break;
486 default:
487 return -EINVAL;
488 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200489
490 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
491 NULL, 0, NI_NUMERICHOST);
492 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100493 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
494 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200495 return s;
496 }
497
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200498 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200499}
500
501static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200502 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200503{
504 struct sockaddr_in *sin_a, *sin_b;
505 struct sockaddr_in6 *sin6_a, *sin6_b;
506
507 if (a->sa_family != b->sa_family)
508 return 0;
509
510 switch (a->sa_family) {
511 case AF_INET:
512 sin_a = (struct sockaddr_in *)a;
513 sin_b = (struct sockaddr_in *)b;
514 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
515 sizeof(struct in_addr)))
516 return 1;
517 break;
518 case AF_INET6:
519 sin6_a = (struct sockaddr_in6 *)a;
520 sin6_b = (struct sockaddr_in6 *)b;
521 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
522 sizeof(struct in6_addr)))
523 return 1;
524 break;
525 }
526 return 0;
527}
528
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200529/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200530 * \param[in] addr Socket Address
531 * \param[in] addrlen Length of socket address in bytes
532 * \returns 1 if address is local, 0 otherwise.
533 */
Harald Weltebc32d052012-04-08 11:31:32 +0200534int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200535{
536 struct ifaddrs *ifaddr, *ifa;
537
538 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100539 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
540 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200541 return -EIO;
542 }
543
544 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200545 if (!ifa->ifa_addr)
546 continue;
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200547 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen)) {
548 freeifaddrs(ifaddr);
Harald Welte33cb71a2011-05-21 18:54:32 +0200549 return 1;
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200550 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200551 }
552
Pau Espin Pedrol15753e92018-04-18 19:57:41 +0200553 freeifaddrs(ifaddr);
Harald Welte33cb71a2011-05-21 18:54:32 +0200554 return 0;
555}
Harald Weltee4764422011-05-22 12:25:57 +0200556
Neels Hofmeyr59f4caf2018-07-19 22:13:19 +0200557/*! Convert sockaddr to IP address as char string and port as uint16_t.
558 * \param[out] addr String buffer to write IP address to, or NULL.
559 * \param[out] addr_len Size of \a addr.
560 * \param[out] port Pointer to uint16_t to write the port number to, or NULL.
561 * \param[in] sa Sockaddr to convert.
562 * \returns the required string buffer size, like osmo_strlcpy(), or 0 if \a addr is NULL.
563 */
564unsigned int osmo_sockaddr_to_str_and_uint(char *addr, unsigned int addr_len, uint16_t *port,
565 const struct sockaddr *sa)
566{
567 const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
568
569 if (port)
570 *port = ntohs(sin->sin_port);
571 if (addr)
572 return osmo_strlcpy(addr, inet_ntoa(sin->sin_addr), addr_len);
573 return 0;
574}
575
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200576/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100577 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
578 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
579 * \param[in] socket_path path to identify the socket
580 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200581 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100582 *
583 * This function creates a new unix domain socket, \a
584 * type and \a proto and optionally binds or connects it, depending on
585 * the value of \a flags parameter.
586 */
587int osmo_sock_unix_init(uint16_t type, uint8_t proto,
588 const char *socket_path, unsigned int flags)
589{
590 struct sockaddr_un local;
591 int sfd, rc, on = 1;
592 unsigned int namelen;
593
594 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
595 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
596 return -EINVAL;
597
598 local.sun_family = AF_UNIX;
Neels Hofmeyr1cea7b52018-07-26 17:13:50 +0200599 osmo_strlcpy(local.sun_path, socket_path, sizeof(local.sun_path));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100600
601#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
602 local.sun_len = strlen(local.sun_path);
603#endif
604#if defined(BSD44SOCKETS) || defined(SUN_LEN)
605 namelen = SUN_LEN(&local);
606#else
607 namelen = strlen(local.sun_path) +
608 offsetof(struct sockaddr_un, sun_path);
609#endif
610
611 sfd = socket(AF_UNIX, type, proto);
612 if (sfd < 0)
613 return -1;
614
615 if (flags & OSMO_SOCK_F_CONNECT) {
616 rc = connect(sfd, (struct sockaddr *)&local, namelen);
617 if (rc < 0)
618 goto err;
619 } else {
620 unlink(local.sun_path);
621 rc = bind(sfd, (struct sockaddr *)&local, namelen);
622 if (rc < 0)
623 goto err;
624 }
625
626 if (flags & OSMO_SOCK_F_NONBLOCK) {
627 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100628 LOGP(DLGLOBAL, LOGL_ERROR,
629 "cannot set this socket unblocking: %s\n",
630 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100631 close(sfd);
632 return -EINVAL;
633 }
634 }
635
Harald Weltec47bbda2017-07-13 16:13:26 +0200636 rc = osmo_sock_init_tail(sfd, type, flags);
637 if (rc < 0) {
638 close(sfd);
639 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100640 }
641
642 return sfd;
643err:
644 close(sfd);
645 return -1;
646}
647
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200648/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100649 * \param[out] ofd file descriptor (will be filled in)
650 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
651 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
652 * \param[in] socket_path path to identify the socket
653 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200654 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100655 *
656 * This function creates (and optionall binds/connects) a socket using
657 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
658 */
659int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
660 const char *socket_path, unsigned int flags)
661{
Max862ba652014-10-13 14:54:25 +0200662 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100663}
664
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200665/*! Get address/port information on soocket in dyn-alloc string
Harald Welte48f55832017-01-26 00:03:10 +0100666 * \param[in] ctx talloc context from which to allocate string buffer
667 * \param[in] fd file descriptor of socket
668 * \returns string identifying the connection of this socket
669 */
670char *osmo_sock_get_name(void *ctx, int fd)
671{
672 struct sockaddr sa_l, sa_r;
673 socklen_t sa_len_l = sizeof(sa_l);
674 socklen_t sa_len_r = sizeof(sa_r);
675 char hostbuf_l[64], hostbuf_r[64];
676 char portbuf_l[16], portbuf_r[16];
677 int rc;
678
679 rc = getsockname(fd, &sa_l, &sa_len_l);
680 if (rc < 0)
681 return NULL;
682
683 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
684 portbuf_l, sizeof(portbuf_l),
685 NI_NUMERICHOST | NI_NUMERICSERV);
686 if (rc < 0)
687 return NULL;
688
689 rc = getpeername(fd, &sa_r, &sa_len_r);
690 if (rc < 0)
691 goto local_only;
692
693 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
694 portbuf_r, sizeof(portbuf_r),
695 NI_NUMERICHOST | NI_NUMERICSERV);
696 if (rc < 0)
697 goto local_only;
698
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200699 return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r,
Harald Welte48f55832017-01-26 00:03:10 +0100700 hostbuf_l, portbuf_l);
701
702local_only:
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200703 return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100704}
705
Harald Weltee30d7e62017-07-13 16:02:50 +0200706static int sock_get_domain(int fd)
707{
708 int domain;
709#ifdef SO_DOMAIN
710 socklen_t dom_len = sizeof(domain);
711 int rc;
712
713 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
714 if (rc < 0)
715 return rc;
716#else
717 /* This of course sucks, but what shall we do on OSs like
718 * FreeBSD that don't seem to expose a method by which one can
719 * learn the address family of a socket? */
720 domain = AF_INET;
721#endif
722 return domain;
723}
724
725
726/*! Activate or de-activate local loop-back of transmitted multicast packets
727 * \param[in] fd file descriptor of related socket
728 * \param[in] enable Enable (true) or disable (false) loop-back
729 * \returns 0 on success; negative otherwise */
730int osmo_sock_mcast_loop_set(int fd, bool enable)
731{
732 int domain, loop = 0;
733
734 if (enable)
735 loop = 1;
736
737 domain = sock_get_domain(fd);
738 if (domain < 0)
739 return domain;
740
741 switch (domain) {
742 case AF_INET:
743 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
744 case AF_INET6:
745 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
746 default:
747 return -EINVAL;
748 }
749}
750
751/*! Set the TTL of outbound multicast packets
752 * \param[in] fd file descriptor of related socket
753 * \param[in] ttl TTL of to-be-sent multicast packets
754 * \returns 0 on success; negative otherwise */
755int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
756{
757 int domain, ttli = ttl;
758
759 domain = sock_get_domain(fd);
760 if (domain < 0)
761 return domain;
762
763 switch (domain) {
764 case AF_INET:
765 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
766 case AF_INET6:
767 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
768 default:
769 return -EINVAL;
770 }
771}
772
773/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
774 * \param[in] fd file descriptor of related socket
775 * \param[in] enable Enable or Disable receiving of all packets
776 * \returns 0 on success; negative otherwise */
777int osmo_sock_mcast_all_set(int fd, bool enable)
778{
779 int domain, all = 0;
780
781 if (enable)
782 all = 1;
783
784 domain = sock_get_domain(fd);
785 if (domain < 0)
786 return domain;
787
788 switch (domain) {
789 case AF_INET:
790#ifdef IP_MULTICAST_ALL
791 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
792#endif
793 case AF_INET6:
794 /* there seems no equivalent ?!? */
795 default:
796 return -EINVAL;
797 }
798}
799
800/* FreeBSD calls the socket option differently */
801#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
802#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
803#endif
804
805/*! Subscribe to the given IP multicast group
806 * \param[in] fd file descriptor of related scoket
807 * \param[in] grp_addr ASCII representation of the multicast group address
808 * \returns 0 on success; negative otherwise */
809int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
810{
811 int rc, domain;
812 struct ip_mreq mreq;
813 struct ipv6_mreq mreq6;
814 struct in6_addr i6a;
815
816 domain = sock_get_domain(fd);
817 if (domain < 0)
818 return domain;
819
820 switch (domain) {
821 case AF_INET:
822 memset(&mreq, 0, sizeof(mreq));
823 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
824 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
825 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
826#ifdef IPV6_ADD_MEMBERSHIP
827 case AF_INET6:
828 memset(&mreq6, 0, sizeof(mreq6));
829 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
830 if (rc < 0)
831 return -EINVAL;
832 mreq6.ipv6mr_multiaddr = i6a;
833 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
834#endif
835 default:
836 return -EINVAL;
837 }
838}
839
Philipp Maier2d2490e2017-10-20 19:41:26 +0200840/*! Determine the matching local IP-address for a given remote IP-Address.
841 * \param[out] local_ip caller provided memory for resulting local IP-address
842 * \param[in] remote_ip remote IP-address
843 * \param[in] fd file descriptor of related scoket
844 * \returns 0 on success; negative otherwise
845 *
846 * The function accepts IPv4 and IPv6 address strings. The caller must provide
847 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
848 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
849int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
850{
851 int sfd;
852 int rc;
853 struct addrinfo addrinfo_hint;
854 struct addrinfo *addrinfo = NULL;
855 struct sockaddr_in local_addr;
856 socklen_t local_addr_len;
857 uint16_t family;
858
859 /* Find out the address family (AF_INET or AF_INET6?) */
860 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
861 addrinfo_hint.ai_family = PF_UNSPEC;
862 addrinfo_hint.ai_flags = AI_NUMERICHOST;
863 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
864 if (rc)
865 return -EINVAL;
866 family = addrinfo->ai_family;
867 freeaddrinfo(addrinfo);
868
869 /* Connect a dummy socket to trick the kernel into determining the
870 * ip-address of the interface that would be used if we would send
871 * out an actual packet */
872 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
873 if (sfd < 0)
874 return -EINVAL;
875
876 /* Request the IP address of the interface that the kernel has
877 * actually choosen. */
878 memset(&local_addr, 0, sizeof(local_addr));
879 local_addr_len = sizeof(local_addr);
880 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +0100881 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200882 if (rc < 0)
883 return -EINVAL;
884 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +0100885 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200886 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +0100887 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200888 else
889 return -EINVAL;
890
891 return 0;
892}
893
Harald Weltee4764422011-05-22 12:25:57 +0200894#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200895
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200896/*! @} */