blob: b516abfd949ae9d615d1aae6fc844785a29994f7 [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 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
Harald Welte33cb71a2011-05-21 18:54:32 +020022#include "../config.h"
23
Harald Welteba6988b2011-08-17 12:46:48 +020024/*! \addtogroup socket
25 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020026 * Osmocom socket convenience functions.
27 *
28 * \file socket.c */
Harald Welte96e2a002017-06-12 21:44:18 +020029
Harald Weltee4764422011-05-22 12:25:57 +020030#ifdef HAVE_SYS_SOCKET_H
31
Harald Welte33cb71a2011-05-21 18:54:32 +020032#include <osmocom/core/logging.h>
33#include <osmocom/core/select.h>
34#include <osmocom/core/socket.h>
Harald Welte48f55832017-01-26 00:03:10 +010035#include <osmocom/core/talloc.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020036
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020037#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020038#include <sys/socket.h>
39#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010040#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020041
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010042#include <netinet/in.h>
Harald Weltee30d7e62017-07-13 16:02:50 +020043#include <arpa/inet.h>
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010044
Harald Welte33cb71a2011-05-21 18:54:32 +020045#include <stdio.h>
46#include <unistd.h>
47#include <stdint.h>
48#include <string.h>
49#include <errno.h>
50#include <netdb.h>
51#include <ifaddrs.h>
52
Harald Weltedda70fc2017-04-08 20:52:33 +020053static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
54 const char *host, uint16_t port, bool passive)
55{
56 struct addrinfo hints, *result;
57 char portbuf[16];
58 int rc;
59
60 snprintf(portbuf, sizeof(portbuf), "%u", port);
61 memset(&hints, 0, sizeof(struct addrinfo));
62 hints.ai_family = family;
63 if (type == SOCK_RAW) {
64 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
65 * SOCK_RAW and IPPROTO_GRE is used.
66 */
67 hints.ai_socktype = SOCK_DGRAM;
68 hints.ai_protocol = IPPROTO_UDP;
69 } else {
70 hints.ai_socktype = type;
71 hints.ai_protocol = proto;
72 }
73
74 if (passive)
75 hints.ai_flags |= AI_PASSIVE;
76
77 rc = getaddrinfo(host, portbuf, &hints, &result);
78 if (rc != 0) {
79 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
80 host, port, strerror(errno));
81 return NULL;
82 }
83
84 return result;
85}
86
87static int socket_helper(const struct addrinfo *rp, unsigned int flags)
88{
89 int sfd, on = 1;
90
91 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
92 if (sfd == -1)
93 return sfd;
94 if (flags & OSMO_SOCK_F_NONBLOCK) {
95 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
96 LOGP(DLGLOBAL, LOGL_ERROR,
97 "cannot set this socket unblocking: %s\n",
98 strerror(errno));
99 close(sfd);
100 sfd = -EINVAL;
101 }
102 }
103 return sfd;
104}
105
106
Harald Weltec47bbda2017-07-13 16:13:26 +0200107static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
108{
Harald Weltebc43a622017-07-13 16:20:21 +0200109 int rc;
Harald Weltec47bbda2017-07-13 16:13:26 +0200110
111 /* Make sure to call 'listen' on a bound, connection-oriented sock */
112 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
113 switch (type) {
114 case SOCK_STREAM:
115 case SOCK_SEQPACKET:
116 rc = listen(fd, 10);
Harald Weltebc43a622017-07-13 16:20:21 +0200117 if (rc < 0) {
118 LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
119 strerror(errno));
120 return rc;
121 }
122 break;
Harald Weltec47bbda2017-07-13 16:13:26 +0200123 }
124 }
125
Harald Weltebc43a622017-07-13 16:20:21 +0200126 if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
127 rc = osmo_sock_mcast_loop_set(fd, false);
128 if (rc < 0) {
129 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
130 strerror(errno));
131 return rc;
132 }
133 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200134
Harald Welte37d204a2017-07-13 16:33:16 +0200135 if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
136 rc = osmo_sock_mcast_all_set(fd, false);
137 if (rc < 0) {
138 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
139 strerror(errno));
140 /* do not abort here, as this is just an
141 * optional additional optimization that only
142 * exists on Linux only */
143 }
144 }
Harald Weltebc43a622017-07-13 16:20:21 +0200145 return 0;
Harald Weltec47bbda2017-07-13 16:13:26 +0200146}
147
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200148/*! Initialize a socket (including bind and/or connect)
Harald Weltedda70fc2017-04-08 20:52:33 +0200149 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
150 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
151 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
152 * \param[in] local_host local host name or IP address in string form
153 * \param[in] local_port local port number in host byte order
154 * \param[in] remote_host remote host name or IP address in string form
155 * \param[in] remote_port remote port number in host byte order
156 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
157 * \returns socket file descriptor on success; negative on error
158 *
159 * This function creates a new socket of the designated \a family, \a
160 * type and \a proto and optionally binds it to the \a local_host and \a
161 * local_port as well as optionally connects it to the \a remote_host
162 * and \q remote_port, depending on the value * of \a flags parameter.
163 *
164 * As opposed to \ref osmo_sock_init(), this function allows to combine
165 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
166 * is useful if you want to connect to a remote host/port, but still
167 * want to bind that socket to either a specific local alias IP and/or a
168 * specific local source port.
169 *
170 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
171 * OSMO_SOCK_F_CONNECT, or both.
172 *
173 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
174 * non-blocking mode.
175 */
176int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
177 const char *local_host, uint16_t local_port,
178 const char *remote_host, uint16_t remote_port, unsigned int flags)
179{
180 struct addrinfo *result, *rp;
181 int sfd = -1, rc, on = 1;
182
183 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
184 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
185 "BIND or CONNECT flags\n");
186 return -EINVAL;
187 }
188
189 /* figure out local side of socket */
190 if (flags & OSMO_SOCK_F_BIND) {
191 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
192 if (!result)
193 return -EINVAL;
194
195 for (rp = result; rp != NULL; rp = rp->ai_next) {
196 /* Workaround for glibc again */
197 if (type == SOCK_RAW) {
198 rp->ai_socktype = SOCK_RAW;
199 rp->ai_protocol = proto;
200 }
201
202 sfd = socket_helper(rp, flags);
203 if (sfd < 0)
204 continue;
205
206 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
207 &on, sizeof(on));
208 if (rc < 0) {
209 LOGP(DLGLOBAL, LOGL_ERROR,
210 "cannot setsockopt socket:"
211 " %s:%u: %s\n",
212 local_host, local_port, strerror(errno));
213 break;
214 }
215 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
216 break;
217 close(sfd);
218 }
219 freeaddrinfo(result);
220 if (rp == NULL) {
221 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
222 local_host, local_port, strerror(errno));
223 return -ENODEV;
224 }
225 }
226
227 /* figure out remote side of socket */
228 if (flags & OSMO_SOCK_F_CONNECT) {
229 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
230 if (!result) {
231 close(sfd);
232 return -EINVAL;
233 }
234
235 for (rp = result; rp != NULL; rp = rp->ai_next) {
236 /* Workaround for glibc again */
237 if (type == SOCK_RAW) {
238 rp->ai_socktype = SOCK_RAW;
239 rp->ai_protocol = proto;
240 }
241
Harald Welte5cfa6dc2017-07-21 16:52:29 +0200242 if (sfd < 0) {
Harald Weltedda70fc2017-04-08 20:52:33 +0200243 sfd = socket_helper(rp, flags);
244 if (sfd < 0)
245 continue;
246 }
247
248 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
249 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
250 break;
251
252 close(sfd);
253 sfd = -1;
254 }
255 freeaddrinfo(result);
256 if (rp == NULL) {
257 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
258 remote_host, remote_port, strerror(errno));
259 return -ENODEV;
260 }
261 }
262
Harald Weltec47bbda2017-07-13 16:13:26 +0200263 rc = osmo_sock_init_tail(sfd, type, flags);
264 if (rc < 0) {
265 close(sfd);
266 sfd = -1;
Harald Weltedda70fc2017-04-08 20:52:33 +0200267 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200268
Harald Weltedda70fc2017-04-08 20:52:33 +0200269 return sfd;
270}
271
272
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200273/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200274 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
275 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
276 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
277 * \param[in] host remote host name or IP address in string form
278 * \param[in] port remote port number in host byte order
279 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200280 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200281 *
282 * This function creates a new socket of the designated \a family, \a
283 * type and \a proto and optionally binds or connects it, depending on
284 * the value of \a flags parameter.
285 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200286int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200287 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200288{
Harald Weltedda70fc2017-04-08 20:52:33 +0200289 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200290 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200291
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200292 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200293 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100294 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200295 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200296 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200297 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200298
Harald Weltedda70fc2017-04-08 20:52:33 +0200299 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
300 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100301 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200302 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200303 return -EINVAL;
304 }
305
306 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100307 /* Workaround for glibc again */
308 if (type == SOCK_RAW) {
309 rp->ai_socktype = SOCK_RAW;
310 rp->ai_protocol = proto;
311 }
312
Harald Weltedda70fc2017-04-08 20:52:33 +0200313 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200314 if (sfd == -1)
315 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200316
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200317 if (flags & OSMO_SOCK_F_CONNECT) {
318 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
319 if (rc != -1 || (rc == -1 && errno == EINPROGRESS))
Harald Welte33cb71a2011-05-21 18:54:32 +0200320 break;
321 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200322 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
323 &on, sizeof(on));
324 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100325 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200326 "cannot setsockopt socket:"
327 " %s:%u: %s\n",
328 host, port, strerror(errno));
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200329 break;
330 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200331 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
332 break;
333 }
334 close(sfd);
335 }
336 freeaddrinfo(result);
337
338 if (rp == NULL) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100339 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect/bind socket: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200340 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200341 return -ENODEV;
342 }
Harald Welte68b15742011-05-22 21:47:29 +0200343
344 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
345
Harald Weltec47bbda2017-07-13 16:13:26 +0200346 rc = osmo_sock_init_tail(sfd, type, flags);
347 if (rc < 0) {
348 close(sfd);
349 sfd = -1;
Harald Welte68b15742011-05-22 21:47:29 +0200350 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200351
Harald Welte68b15742011-05-22 21:47:29 +0200352 return sfd;
353}
354
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200355/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200356 * \param[out] ofd file descriptor (will be filled in)
357 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200358 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200359 *
360 * This function fills the \a ofd structure.
361 */
362static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
363{
364 int rc;
365
366 if (sfd < 0)
367 return sfd;
368
369 ofd->fd = sfd;
370 ofd->when = BSC_FD_READ;
371
372 rc = osmo_fd_register(ofd);
373 if (rc < 0) {
374 close(sfd);
375 return rc;
376 }
377
378 return sfd;
379}
380
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200381/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100382 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200383 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
384 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
385 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
386 * \param[in] host remote host name or IP address in string form
387 * \param[in] port remote port number in host byte order
388 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200389 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200390 *
391 * This function creates (and optionall binds/connects) a socket using
392 * \ref osmo_sock_init, but also fills the \a ofd structure.
393 */
Harald Welte68b15742011-05-22 21:47:29 +0200394int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200395 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200396{
Max862ba652014-10-13 14:54:25 +0200397 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200398}
399
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200400/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200401 * \param[out] ofd file descriptor (will be filled in)
402 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
403 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
404 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
405 * \param[in] local_host local host name or IP address in string form
406 * \param[in] local_port local port number in host byte order
407 * \param[in] remote_host remote host name or IP address in string form
408 * \param[in] remote_port remote port number in host byte order
409 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
410 * \returns socket fd on success; negative on error
411 *
412 * This function creates (and optionall binds/connects) a socket using
413 * \ref osmo_sock_init2, but also fills the \a ofd structure.
414 */
415int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
416 const char *local_host, uint16_t local_port,
417 const char *remote_host, uint16_t remote_port, unsigned int flags)
418{
419 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
420 local_port, remote_host, remote_port, flags));
421}
422
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200423/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200424 * \param[out] ss socket address (will be filled in)
425 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
426 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
427 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200428 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200429 *
430 * This function creates (and optionall binds/connects) a socket using
431 * \ref osmo_sock_init, but also fills the \a ss structure.
432 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200433int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200434 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200435{
436 char host[NI_MAXHOST];
437 uint16_t port;
438 struct sockaddr_in *sin;
439 struct sockaddr_in6 *sin6;
440 int s, sa_len;
441
442 /* determine port and host from ss */
443 switch (ss->sa_family) {
444 case AF_INET:
445 sin = (struct sockaddr_in *) ss;
446 sa_len = sizeof(struct sockaddr_in);
447 port = ntohs(sin->sin_port);
448 break;
449 case AF_INET6:
450 sin6 = (struct sockaddr_in6 *) ss;
451 sa_len = sizeof(struct sockaddr_in6);
452 port = ntohs(sin6->sin6_port);
453 break;
454 default:
455 return -EINVAL;
456 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200457
458 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
459 NULL, 0, NI_NUMERICHOST);
460 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100461 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
462 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200463 return s;
464 }
465
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200466 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200467}
468
469static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200470 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200471{
472 struct sockaddr_in *sin_a, *sin_b;
473 struct sockaddr_in6 *sin6_a, *sin6_b;
474
475 if (a->sa_family != b->sa_family)
476 return 0;
477
478 switch (a->sa_family) {
479 case AF_INET:
480 sin_a = (struct sockaddr_in *)a;
481 sin_b = (struct sockaddr_in *)b;
482 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
483 sizeof(struct in_addr)))
484 return 1;
485 break;
486 case AF_INET6:
487 sin6_a = (struct sockaddr_in6 *)a;
488 sin6_b = (struct sockaddr_in6 *)b;
489 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
490 sizeof(struct in6_addr)))
491 return 1;
492 break;
493 }
494 return 0;
495}
496
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200497/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200498 * \param[in] addr Socket Address
499 * \param[in] addrlen Length of socket address in bytes
500 * \returns 1 if address is local, 0 otherwise.
501 */
Harald Weltebc32d052012-04-08 11:31:32 +0200502int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200503{
504 struct ifaddrs *ifaddr, *ifa;
505
506 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100507 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
508 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200509 return -EIO;
510 }
511
512 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200513 if (!ifa->ifa_addr)
514 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200515 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
516 return 1;
517 }
518
519 return 0;
520}
Harald Weltee4764422011-05-22 12:25:57 +0200521
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200522/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100523 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
524 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
525 * \param[in] socket_path path to identify the socket
526 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200527 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100528 *
529 * This function creates a new unix domain socket, \a
530 * type and \a proto and optionally binds or connects it, depending on
531 * the value of \a flags parameter.
532 */
533int osmo_sock_unix_init(uint16_t type, uint8_t proto,
534 const char *socket_path, unsigned int flags)
535{
536 struct sockaddr_un local;
537 int sfd, rc, on = 1;
538 unsigned int namelen;
539
540 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
541 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
542 return -EINVAL;
543
544 local.sun_family = AF_UNIX;
545 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
546 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
547
548#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
549 local.sun_len = strlen(local.sun_path);
550#endif
551#if defined(BSD44SOCKETS) || defined(SUN_LEN)
552 namelen = SUN_LEN(&local);
553#else
554 namelen = strlen(local.sun_path) +
555 offsetof(struct sockaddr_un, sun_path);
556#endif
557
558 sfd = socket(AF_UNIX, type, proto);
559 if (sfd < 0)
560 return -1;
561
562 if (flags & OSMO_SOCK_F_CONNECT) {
563 rc = connect(sfd, (struct sockaddr *)&local, namelen);
564 if (rc < 0)
565 goto err;
566 } else {
567 unlink(local.sun_path);
568 rc = bind(sfd, (struct sockaddr *)&local, namelen);
569 if (rc < 0)
570 goto err;
571 }
572
573 if (flags & OSMO_SOCK_F_NONBLOCK) {
574 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100575 LOGP(DLGLOBAL, LOGL_ERROR,
576 "cannot set this socket unblocking: %s\n",
577 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100578 close(sfd);
579 return -EINVAL;
580 }
581 }
582
Harald Weltec47bbda2017-07-13 16:13:26 +0200583 rc = osmo_sock_init_tail(sfd, type, flags);
584 if (rc < 0) {
585 close(sfd);
586 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100587 }
588
589 return sfd;
590err:
591 close(sfd);
592 return -1;
593}
594
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200595/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100596 * \param[out] ofd file descriptor (will be filled in)
597 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
598 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
599 * \param[in] socket_path path to identify the socket
600 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200601 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100602 *
603 * This function creates (and optionall binds/connects) a socket using
604 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
605 */
606int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
607 const char *socket_path, unsigned int flags)
608{
Max862ba652014-10-13 14:54:25 +0200609 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100610}
611
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200612/*! Get address/port information on soocket in dyn-alloc string
Harald Welte48f55832017-01-26 00:03:10 +0100613 * \param[in] ctx talloc context from which to allocate string buffer
614 * \param[in] fd file descriptor of socket
615 * \returns string identifying the connection of this socket
616 */
617char *osmo_sock_get_name(void *ctx, int fd)
618{
619 struct sockaddr sa_l, sa_r;
620 socklen_t sa_len_l = sizeof(sa_l);
621 socklen_t sa_len_r = sizeof(sa_r);
622 char hostbuf_l[64], hostbuf_r[64];
623 char portbuf_l[16], portbuf_r[16];
624 int rc;
625
626 rc = getsockname(fd, &sa_l, &sa_len_l);
627 if (rc < 0)
628 return NULL;
629
630 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
631 portbuf_l, sizeof(portbuf_l),
632 NI_NUMERICHOST | NI_NUMERICSERV);
633 if (rc < 0)
634 return NULL;
635
636 rc = getpeername(fd, &sa_r, &sa_len_r);
637 if (rc < 0)
638 goto local_only;
639
640 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
641 portbuf_r, sizeof(portbuf_r),
642 NI_NUMERICHOST | NI_NUMERICSERV);
643 if (rc < 0)
644 goto local_only;
645
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200646 return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r,
Harald Welte48f55832017-01-26 00:03:10 +0100647 hostbuf_l, portbuf_l);
648
649local_only:
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200650 return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100651}
652
Harald Weltee30d7e62017-07-13 16:02:50 +0200653static int sock_get_domain(int fd)
654{
655 int domain;
656#ifdef SO_DOMAIN
657 socklen_t dom_len = sizeof(domain);
658 int rc;
659
660 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
661 if (rc < 0)
662 return rc;
663#else
664 /* This of course sucks, but what shall we do on OSs like
665 * FreeBSD that don't seem to expose a method by which one can
666 * learn the address family of a socket? */
667 domain = AF_INET;
668#endif
669 return domain;
670}
671
672
673/*! Activate or de-activate local loop-back of transmitted multicast packets
674 * \param[in] fd file descriptor of related socket
675 * \param[in] enable Enable (true) or disable (false) loop-back
676 * \returns 0 on success; negative otherwise */
677int osmo_sock_mcast_loop_set(int fd, bool enable)
678{
679 int domain, loop = 0;
680
681 if (enable)
682 loop = 1;
683
684 domain = sock_get_domain(fd);
685 if (domain < 0)
686 return domain;
687
688 switch (domain) {
689 case AF_INET:
690 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
691 case AF_INET6:
692 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
693 default:
694 return -EINVAL;
695 }
696}
697
698/*! Set the TTL of outbound multicast packets
699 * \param[in] fd file descriptor of related socket
700 * \param[in] ttl TTL of to-be-sent multicast packets
701 * \returns 0 on success; negative otherwise */
702int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
703{
704 int domain, ttli = ttl;
705
706 domain = sock_get_domain(fd);
707 if (domain < 0)
708 return domain;
709
710 switch (domain) {
711 case AF_INET:
712 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttli, sizeof(ttli));
713 case AF_INET6:
714 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
715 default:
716 return -EINVAL;
717 }
718}
719
720/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
721 * \param[in] fd file descriptor of related socket
722 * \param[in] enable Enable or Disable receiving of all packets
723 * \returns 0 on success; negative otherwise */
724int osmo_sock_mcast_all_set(int fd, bool enable)
725{
726 int domain, all = 0;
727
728 if (enable)
729 all = 1;
730
731 domain = sock_get_domain(fd);
732 if (domain < 0)
733 return domain;
734
735 switch (domain) {
736 case AF_INET:
737#ifdef IP_MULTICAST_ALL
738 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
739#endif
740 case AF_INET6:
741 /* there seems no equivalent ?!? */
742 default:
743 return -EINVAL;
744 }
745}
746
747/* FreeBSD calls the socket option differently */
748#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
749#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
750#endif
751
752/*! Subscribe to the given IP multicast group
753 * \param[in] fd file descriptor of related scoket
754 * \param[in] grp_addr ASCII representation of the multicast group address
755 * \returns 0 on success; negative otherwise */
756int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
757{
758 int rc, domain;
759 struct ip_mreq mreq;
760 struct ipv6_mreq mreq6;
761 struct in6_addr i6a;
762
763 domain = sock_get_domain(fd);
764 if (domain < 0)
765 return domain;
766
767 switch (domain) {
768 case AF_INET:
769 memset(&mreq, 0, sizeof(mreq));
770 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
771 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
772 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
773#ifdef IPV6_ADD_MEMBERSHIP
774 case AF_INET6:
775 memset(&mreq6, 0, sizeof(mreq6));
776 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
777 if (rc < 0)
778 return -EINVAL;
779 mreq6.ipv6mr_multiaddr = i6a;
780 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
781#endif
782 default:
783 return -EINVAL;
784 }
785}
786
Philipp Maier2d2490e2017-10-20 19:41:26 +0200787/*! Determine the matching local IP-address for a given remote IP-Address.
788 * \param[out] local_ip caller provided memory for resulting local IP-address
789 * \param[in] remote_ip remote IP-address
790 * \param[in] fd file descriptor of related scoket
791 * \returns 0 on success; negative otherwise
792 *
793 * The function accepts IPv4 and IPv6 address strings. The caller must provide
794 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
795 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
796int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
797{
798 int sfd;
799 int rc;
800 struct addrinfo addrinfo_hint;
801 struct addrinfo *addrinfo = NULL;
802 struct sockaddr_in local_addr;
803 socklen_t local_addr_len;
804 uint16_t family;
805
806 /* Find out the address family (AF_INET or AF_INET6?) */
807 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
808 addrinfo_hint.ai_family = PF_UNSPEC;
809 addrinfo_hint.ai_flags = AI_NUMERICHOST;
810 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
811 if (rc)
812 return -EINVAL;
813 family = addrinfo->ai_family;
814 freeaddrinfo(addrinfo);
815
816 /* Connect a dummy socket to trick the kernel into determining the
817 * ip-address of the interface that would be used if we would send
818 * out an actual packet */
819 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
820 if (sfd < 0)
821 return -EINVAL;
822
823 /* Request the IP address of the interface that the kernel has
824 * actually choosen. */
825 memset(&local_addr, 0, sizeof(local_addr));
826 local_addr_len = sizeof(local_addr);
827 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
828 if (rc < 0)
829 return -EINVAL;
830 if (local_addr.sin_family == AF_INET)
831 strncpy(local_ip, inet_ntoa(local_addr.sin_addr), INET_ADDRSTRLEN);
832 else if (local_addr.sin_family == AF_INET6)
833 strncpy(local_ip, inet_ntoa(local_addr.sin_addr), INET6_ADDRSTRLEN);
834 else
835 return -EINVAL;
836
837 return 0;
838}
839
Harald Weltee4764422011-05-22 12:25:57 +0200840#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200841
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200842/*! @} */