blob: d96f664d458c89b230f3518fcec9351927246696 [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>
Harald Welte33cb71a2011-05-21 18:54:32 +020038
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +020039#include <sys/ioctl.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020040#include <sys/socket.h>
41#include <sys/types.h>
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +010042#include <sys/un.h>
Harald Welte33cb71a2011-05-21 18:54:32 +020043
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010044#include <netinet/in.h>
Harald Weltee30d7e62017-07-13 16:02:50 +020045#include <arpa/inet.h>
Holger Hans Peter Freyther47723482011-11-09 11:26:15 +010046
Harald Welte33cb71a2011-05-21 18:54:32 +020047#include <stdio.h>
48#include <unistd.h>
49#include <stdint.h>
50#include <string.h>
51#include <errno.h>
52#include <netdb.h>
53#include <ifaddrs.h>
54
Harald Weltedda70fc2017-04-08 20:52:33 +020055static struct addrinfo *addrinfo_helper(uint16_t family, uint16_t type, uint8_t proto,
56 const char *host, uint16_t port, bool passive)
57{
58 struct addrinfo hints, *result;
59 char portbuf[16];
60 int rc;
61
62 snprintf(portbuf, sizeof(portbuf), "%u", port);
63 memset(&hints, 0, sizeof(struct addrinfo));
64 hints.ai_family = family;
65 if (type == SOCK_RAW) {
66 /* Workaround for glibc, that returns EAI_SERVICE (-8) if
67 * SOCK_RAW and IPPROTO_GRE is used.
68 */
69 hints.ai_socktype = SOCK_DGRAM;
70 hints.ai_protocol = IPPROTO_UDP;
71 } else {
72 hints.ai_socktype = type;
73 hints.ai_protocol = proto;
74 }
75
76 if (passive)
77 hints.ai_flags |= AI_PASSIVE;
78
79 rc = getaddrinfo(host, portbuf, &hints, &result);
80 if (rc != 0) {
81 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
82 host, port, strerror(errno));
83 return NULL;
84 }
85
86 return result;
87}
88
89static int socket_helper(const struct addrinfo *rp, unsigned int flags)
90{
91 int sfd, on = 1;
92
93 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +020094 if (sfd == -1) {
95 LOGP(DLGLOBAL, LOGL_ERROR,
96 "unable to create socket: %s\n", strerror(errno));
Harald Weltedda70fc2017-04-08 20:52:33 +020097 return sfd;
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +020098 }
Harald Weltedda70fc2017-04-08 20:52:33 +020099 if (flags & OSMO_SOCK_F_NONBLOCK) {
100 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
101 LOGP(DLGLOBAL, LOGL_ERROR,
102 "cannot set this socket unblocking: %s\n",
103 strerror(errno));
104 close(sfd);
105 sfd = -EINVAL;
106 }
107 }
108 return sfd;
109}
110
111
Harald Weltec47bbda2017-07-13 16:13:26 +0200112static int osmo_sock_init_tail(int fd, uint16_t type, unsigned int flags)
113{
Harald Weltebc43a622017-07-13 16:20:21 +0200114 int rc;
Harald Weltec47bbda2017-07-13 16:13:26 +0200115
116 /* Make sure to call 'listen' on a bound, connection-oriented sock */
117 if ((flags & (OSMO_SOCK_F_BIND|OSMO_SOCK_F_CONNECT)) == OSMO_SOCK_F_BIND) {
118 switch (type) {
119 case SOCK_STREAM:
120 case SOCK_SEQPACKET:
121 rc = listen(fd, 10);
Harald Weltebc43a622017-07-13 16:20:21 +0200122 if (rc < 0) {
123 LOGP(DLGLOBAL, LOGL_ERROR, "unable to listen on socket: %s\n",
124 strerror(errno));
125 return rc;
126 }
127 break;
Harald Weltec47bbda2017-07-13 16:13:26 +0200128 }
129 }
130
Harald Weltebc43a622017-07-13 16:20:21 +0200131 if (flags & OSMO_SOCK_F_NO_MCAST_LOOP) {
132 rc = osmo_sock_mcast_loop_set(fd, false);
133 if (rc < 0) {
134 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable multicast loop: %s\n",
135 strerror(errno));
136 return rc;
137 }
138 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200139
Harald Welte37d204a2017-07-13 16:33:16 +0200140 if (flags & OSMO_SOCK_F_NO_MCAST_ALL) {
141 rc = osmo_sock_mcast_all_set(fd, false);
142 if (rc < 0) {
143 LOGP(DLGLOBAL, LOGL_ERROR, "unable to disable receive of all multicast: %s\n",
144 strerror(errno));
145 /* do not abort here, as this is just an
146 * optional additional optimization that only
147 * exists on Linux only */
148 }
149 }
Harald Weltebc43a622017-07-13 16:20:21 +0200150 return 0;
Harald Weltec47bbda2017-07-13 16:13:26 +0200151}
152
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200153/*! Initialize a socket (including bind and/or connect)
Harald Weltedda70fc2017-04-08 20:52:33 +0200154 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
155 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
156 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
157 * \param[in] local_host local host name or IP address in string form
158 * \param[in] local_port local port number in host byte order
159 * \param[in] remote_host remote host name or IP address in string form
160 * \param[in] remote_port remote port number in host byte order
161 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
162 * \returns socket file descriptor on success; negative on error
163 *
164 * This function creates a new socket of the designated \a family, \a
165 * type and \a proto and optionally binds it to the \a local_host and \a
166 * local_port as well as optionally connects it to the \a remote_host
167 * and \q remote_port, depending on the value * of \a flags parameter.
168 *
169 * As opposed to \ref osmo_sock_init(), this function allows to combine
170 * the \ref OSMO_SOCK_F_BIND and \ref OSMO_SOCK_F_CONNECT flags. This
171 * is useful if you want to connect to a remote host/port, but still
172 * want to bind that socket to either a specific local alias IP and/or a
173 * specific local source port.
174 *
175 * You must specify either \ref OSMO_SOCK_F_BIND, or \ref
176 * OSMO_SOCK_F_CONNECT, or both.
177 *
178 * If \ref OSMO_SOCK_F_NONBLOCK is specified, the socket will be set to
179 * non-blocking mode.
180 */
181int osmo_sock_init2(uint16_t family, uint16_t type, uint8_t proto,
182 const char *local_host, uint16_t local_port,
183 const char *remote_host, uint16_t remote_port, unsigned int flags)
184{
185 struct addrinfo *result, *rp;
186 int sfd = -1, rc, on = 1;
187
188 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) == 0) {
189 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: you have to specify either "
190 "BIND or CONNECT flags\n");
191 return -EINVAL;
192 }
193
194 /* figure out local side of socket */
195 if (flags & OSMO_SOCK_F_BIND) {
196 result = addrinfo_helper(family, type, proto, local_host, local_port, true);
197 if (!result)
198 return -EINVAL;
199
200 for (rp = result; rp != NULL; rp = rp->ai_next) {
201 /* Workaround for glibc again */
202 if (type == SOCK_RAW) {
203 rp->ai_socktype = SOCK_RAW;
204 rp->ai_protocol = proto;
205 }
206
207 sfd = socket_helper(rp, flags);
208 if (sfd < 0)
209 continue;
210
211 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
212 &on, sizeof(on));
213 if (rc < 0) {
214 LOGP(DLGLOBAL, LOGL_ERROR,
215 "cannot setsockopt socket:"
216 " %s:%u: %s\n",
217 local_host, local_port, strerror(errno));
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200218 close(sfd);
219 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200220 }
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200221 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
222 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket: %s:%u: %s\n",
223 local_host, local_port, strerror(errno));
224 close(sfd);
225 continue;
226 }
227 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200228 }
229 freeaddrinfo(result);
230 if (rp == NULL) {
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200231 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable local addr found for: %s:%u\n",
232 local_host, local_port);
Harald Weltedda70fc2017-04-08 20:52:33 +0200233 return -ENODEV;
234 }
235 }
236
Pau Espin Pedrol5d50fa52018-04-05 17:00:22 +0200237 /* Reached this point, if OSMO_SOCK_F_BIND then sfd is valid (>=0) or it
238 was already closed and func returned. If OSMO_SOCK_F_BIND is not
239 set, then sfd = -1 */
240
Harald Weltedda70fc2017-04-08 20:52:33 +0200241 /* figure out remote side of socket */
242 if (flags & OSMO_SOCK_F_CONNECT) {
243 result = addrinfo_helper(family, type, proto, remote_host, remote_port, false);
244 if (!result) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200245 if (sfd >= 0)
246 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200247 return -EINVAL;
248 }
249
250 for (rp = result; rp != NULL; rp = rp->ai_next) {
251 /* Workaround for glibc again */
252 if (type == SOCK_RAW) {
253 rp->ai_socktype = SOCK_RAW;
254 rp->ai_protocol = proto;
255 }
256
Harald Welte5cfa6dc2017-07-21 16:52:29 +0200257 if (sfd < 0) {
Harald Weltedda70fc2017-04-08 20:52:33 +0200258 sfd = socket_helper(rp, flags);
259 if (sfd < 0)
260 continue;
261 }
262
263 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200264 if (rc != 0 && errno != EINPROGRESS) {
265 LOGP(DLGLOBAL, LOGL_ERROR, "unable to connect socket: %s:%u: %s\n",
266 remote_host, remote_port, strerror(errno));
267 /* We want to maintain the bind socket if bind was enabled */
268 if (!(flags & OSMO_SOCK_F_BIND)) {
269 close(sfd);
270 sfd = -1;
271 }
272 continue;
273 }
274 break;
Harald Weltedda70fc2017-04-08 20:52:33 +0200275 }
276 freeaddrinfo(result);
277 if (rp == NULL) {
Pau Espin Pedrol27cf8df2018-04-05 17:49:08 +0200278 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable remote addr found for: %s:%u\n",
279 remote_host, remote_port);
280 if (sfd >= 0)
281 close(sfd);
Harald Weltedda70fc2017-04-08 20:52:33 +0200282 return -ENODEV;
283 }
284 }
285
Harald Weltec47bbda2017-07-13 16:13:26 +0200286 rc = osmo_sock_init_tail(sfd, type, flags);
287 if (rc < 0) {
288 close(sfd);
289 sfd = -1;
Harald Weltedda70fc2017-04-08 20:52:33 +0200290 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200291
Harald Weltedda70fc2017-04-08 20:52:33 +0200292 return sfd;
293}
294
295
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200296/*! Initialize a socket (including bind/connect)
Harald Welteba6988b2011-08-17 12:46:48 +0200297 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
298 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
299 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
300 * \param[in] host remote host name or IP address in string form
301 * \param[in] port remote port number in host byte order
302 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200303 * \returns socket file descriptor on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200304 *
305 * This function creates a new socket of the designated \a family, \a
306 * type and \a proto and optionally binds or connects it, depending on
307 * the value of \a flags parameter.
308 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200309int osmo_sock_init(uint16_t family, uint16_t type, uint8_t proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200310 const char *host, uint16_t port, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200311{
Harald Weltedda70fc2017-04-08 20:52:33 +0200312 struct addrinfo *result, *rp;
Harald Welte68b15742011-05-22 21:47:29 +0200313 int sfd, rc, on = 1;
Harald Welte33cb71a2011-05-21 18:54:32 +0200314
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200315 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200316 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100317 LOGP(DLGLOBAL, LOGL_ERROR, "invalid: both bind and connect flags set:"
Neels Hofmeyrb7f191f2016-08-29 11:22:03 +0200318 " %s:%u\n", host, port);
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200319 return -EINVAL;
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200320 }
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200321
Harald Weltedda70fc2017-04-08 20:52:33 +0200322 result = addrinfo_helper(family, type, proto, host, port, flags & OSMO_SOCK_F_BIND);
323 if (!result) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100324 LOGP(DLGLOBAL, LOGL_ERROR, "getaddrinfo returned NULL: %s:%u: %s\n",
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200325 host, port, strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200326 return -EINVAL;
327 }
328
329 for (rp = result; rp != NULL; rp = rp->ai_next) {
Pablo Neira Ayusoe04a14d2013-01-14 00:12:28 +0100330 /* Workaround for glibc again */
331 if (type == SOCK_RAW) {
332 rp->ai_socktype = SOCK_RAW;
333 rp->ai_protocol = proto;
334 }
335
Harald Weltedda70fc2017-04-08 20:52:33 +0200336 sfd = socket_helper(rp, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200337 if (sfd == -1)
338 continue;
Harald Weltedda70fc2017-04-08 20:52:33 +0200339
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200340 if (flags & OSMO_SOCK_F_CONNECT) {
341 rc = connect(sfd, rp->ai_addr, rp->ai_addrlen);
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200342 if (rc != 0 && errno != EINPROGRESS) {
343 close(sfd);
344 continue;
345 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200346 } else {
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200347 rc = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
348 &on, sizeof(on));
349 if (rc < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100350 LOGP(DLGLOBAL, LOGL_ERROR,
Neels Hofmeyrf0f07d92016-08-22 13:34:23 +0200351 "cannot setsockopt socket:"
352 " %s:%u: %s\n",
353 host, port, strerror(errno));
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200354 close(sfd);
355 continue;
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200356 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200357 if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == -1) {
358 LOGP(DLGLOBAL, LOGL_ERROR, "unable to bind socket:"
359 "%s:%u: %s\n",
360 host, port, strerror(errno));
361 close(sfd);
362 continue;
363 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200364 }
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200365 break;
Harald Welte33cb71a2011-05-21 18:54:32 +0200366 }
367 freeaddrinfo(result);
368
369 if (rp == NULL) {
Pau Espin Pedrol3a321472018-04-05 17:49:40 +0200370 LOGP(DLGLOBAL, LOGL_ERROR, "no suitable addr found for: %s:%u\n",
371 host, port);
Harald Welte33cb71a2011-05-21 18:54:32 +0200372 return -ENODEV;
373 }
Harald Welte68b15742011-05-22 21:47:29 +0200374
375 setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
376
Harald Weltec47bbda2017-07-13 16:13:26 +0200377 rc = osmo_sock_init_tail(sfd, type, flags);
378 if (rc < 0) {
379 close(sfd);
380 sfd = -1;
Harald Welte68b15742011-05-22 21:47:29 +0200381 }
Harald Weltec47bbda2017-07-13 16:13:26 +0200382
Harald Welte68b15742011-05-22 21:47:29 +0200383 return sfd;
384}
385
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200386/*! fill \ref osmo_fd for a give sfd
Max862ba652014-10-13 14:54:25 +0200387 * \param[out] ofd file descriptor (will be filled in)
388 * \param[in] sfd socket file descriptor
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200389 * \returns socket fd on success; negative on error
Max862ba652014-10-13 14:54:25 +0200390 *
391 * This function fills the \a ofd structure.
392 */
393static inline int osmo_fd_init_ofd(struct osmo_fd *ofd, int sfd)
394{
395 int rc;
396
397 if (sfd < 0)
398 return sfd;
399
400 ofd->fd = sfd;
401 ofd->when = BSC_FD_READ;
402
403 rc = osmo_fd_register(ofd);
404 if (rc < 0) {
405 close(sfd);
406 return rc;
407 }
408
409 return sfd;
410}
411
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200412/*! Initialize a socket and fill \ref osmo_fd
Katerina Barone-Adesic28c6a02013-02-15 13:27:59 +0100413 * \param[out] ofd file descriptor (will be filled in)
Harald Welteba6988b2011-08-17 12:46:48 +0200414 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
415 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
416 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
417 * \param[in] host remote host name or IP address in string form
418 * \param[in] port remote port number in host byte order
419 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200420 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200421 *
422 * This function creates (and optionall binds/connects) a socket using
423 * \ref osmo_sock_init, but also fills the \a ofd structure.
424 */
Harald Welte68b15742011-05-22 21:47:29 +0200425int osmo_sock_init_ofd(struct osmo_fd *ofd, int family, int type, int proto,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200426 const char *host, uint16_t port, unsigned int flags)
Harald Welte68b15742011-05-22 21:47:29 +0200427{
Max862ba652014-10-13 14:54:25 +0200428 return osmo_fd_init_ofd(ofd, osmo_sock_init(family, type, proto, host, port, flags));
Harald Welte33cb71a2011-05-21 18:54:32 +0200429}
430
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200431/*! Initialize a socket and fill \ref osmo_fd
Pau Espin Pedrol75989e62017-05-26 12:39:53 +0200432 * \param[out] ofd file descriptor (will be filled in)
433 * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
434 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
435 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
436 * \param[in] local_host local host name or IP address in string form
437 * \param[in] local_port local port number in host byte order
438 * \param[in] remote_host remote host name or IP address in string form
439 * \param[in] remote_port remote port number in host byte order
440 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
441 * \returns socket fd on success; negative on error
442 *
443 * This function creates (and optionall binds/connects) a socket using
444 * \ref osmo_sock_init2, but also fills the \a ofd structure.
445 */
446int osmo_sock_init2_ofd(struct osmo_fd *ofd, int family, int type, int proto,
447 const char *local_host, uint16_t local_port,
448 const char *remote_host, uint16_t remote_port, unsigned int flags)
449{
450 return osmo_fd_init_ofd(ofd, osmo_sock_init2(family, type, proto, local_host,
451 local_port, remote_host, remote_port, flags));
452}
453
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200454/*! Initialize a socket and fill \ref sockaddr
Harald Welteba6988b2011-08-17 12:46:48 +0200455 * \param[out] ss socket address (will be filled in)
456 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
457 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
458 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200459 * \returns socket fd on success; negative on error
Harald Welteba6988b2011-08-17 12:46:48 +0200460 *
461 * This function creates (and optionall binds/connects) a socket using
462 * \ref osmo_sock_init, but also fills the \a ss structure.
463 */
Harald Welte33cb71a2011-05-21 18:54:32 +0200464int osmo_sock_init_sa(struct sockaddr *ss, uint16_t type,
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200465 uint8_t proto, unsigned int flags)
Harald Welte33cb71a2011-05-21 18:54:32 +0200466{
467 char host[NI_MAXHOST];
468 uint16_t port;
469 struct sockaddr_in *sin;
470 struct sockaddr_in6 *sin6;
471 int s, sa_len;
472
473 /* determine port and host from ss */
474 switch (ss->sa_family) {
475 case AF_INET:
476 sin = (struct sockaddr_in *) ss;
477 sa_len = sizeof(struct sockaddr_in);
478 port = ntohs(sin->sin_port);
479 break;
480 case AF_INET6:
481 sin6 = (struct sockaddr_in6 *) ss;
482 sa_len = sizeof(struct sockaddr_in6);
483 port = ntohs(sin6->sin6_port);
484 break;
485 default:
486 return -EINVAL;
487 }
Harald Welte33cb71a2011-05-21 18:54:32 +0200488
489 s = getnameinfo(ss, sa_len, host, NI_MAXHOST,
490 NULL, 0, NI_NUMERICHOST);
491 if (s != 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100492 LOGP(DLGLOBAL, LOGL_ERROR, "getnameinfo failed:"
493 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200494 return s;
495 }
496
Pablo Neira Ayuso0849c9a2011-06-09 15:04:30 +0200497 return osmo_sock_init(ss->sa_family, type, proto, host, port, flags);
Harald Welte33cb71a2011-05-21 18:54:32 +0200498}
499
500static int sockaddr_equal(const struct sockaddr *a,
Harald Weltee4764422011-05-22 12:25:57 +0200501 const struct sockaddr *b, unsigned int len)
Harald Welte33cb71a2011-05-21 18:54:32 +0200502{
503 struct sockaddr_in *sin_a, *sin_b;
504 struct sockaddr_in6 *sin6_a, *sin6_b;
505
506 if (a->sa_family != b->sa_family)
507 return 0;
508
509 switch (a->sa_family) {
510 case AF_INET:
511 sin_a = (struct sockaddr_in *)a;
512 sin_b = (struct sockaddr_in *)b;
513 if (!memcmp(&sin_a->sin_addr, &sin_b->sin_addr,
514 sizeof(struct in_addr)))
515 return 1;
516 break;
517 case AF_INET6:
518 sin6_a = (struct sockaddr_in6 *)a;
519 sin6_b = (struct sockaddr_in6 *)b;
520 if (!memcmp(&sin6_a->sin6_addr, &sin6_b->sin6_addr,
521 sizeof(struct in6_addr)))
522 return 1;
523 break;
524 }
525 return 0;
526}
527
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200528/*! Determine if the given address is a local address
Harald Welteba6988b2011-08-17 12:46:48 +0200529 * \param[in] addr Socket Address
530 * \param[in] addrlen Length of socket address in bytes
531 * \returns 1 if address is local, 0 otherwise.
532 */
Harald Weltebc32d052012-04-08 11:31:32 +0200533int osmo_sockaddr_is_local(struct sockaddr *addr, unsigned int addrlen)
Harald Welte33cb71a2011-05-21 18:54:32 +0200534{
535 struct ifaddrs *ifaddr, *ifa;
536
537 if (getifaddrs(&ifaddr) == -1) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100538 LOGP(DLGLOBAL, LOGL_ERROR, "getifaddrs:"
539 " %s\n", strerror(errno));
Harald Welte33cb71a2011-05-21 18:54:32 +0200540 return -EIO;
541 }
542
543 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
Harald Welte4d3a7b12011-05-24 21:31:53 +0200544 if (!ifa->ifa_addr)
545 continue;
Harald Welte33cb71a2011-05-21 18:54:32 +0200546 if (sockaddr_equal(ifa->ifa_addr, addr, addrlen))
547 return 1;
548 }
549
550 return 0;
551}
Harald Weltee4764422011-05-22 12:25:57 +0200552
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200553/*! Initialize a unix domain socket (including bind/connect)
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100554 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
555 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
556 * \param[in] socket_path path to identify the socket
557 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200558 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100559 *
560 * This function creates a new unix domain socket, \a
561 * type and \a proto and optionally binds or connects it, depending on
562 * the value of \a flags parameter.
563 */
564int osmo_sock_unix_init(uint16_t type, uint8_t proto,
565 const char *socket_path, unsigned int flags)
566{
567 struct sockaddr_un local;
568 int sfd, rc, on = 1;
569 unsigned int namelen;
570
571 if ((flags & (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT)) ==
572 (OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT))
573 return -EINVAL;
574
575 local.sun_family = AF_UNIX;
576 strncpy(local.sun_path, socket_path, sizeof(local.sun_path));
577 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
578
579#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
580 local.sun_len = strlen(local.sun_path);
581#endif
582#if defined(BSD44SOCKETS) || defined(SUN_LEN)
583 namelen = SUN_LEN(&local);
584#else
585 namelen = strlen(local.sun_path) +
586 offsetof(struct sockaddr_un, sun_path);
587#endif
588
589 sfd = socket(AF_UNIX, type, proto);
590 if (sfd < 0)
591 return -1;
592
593 if (flags & OSMO_SOCK_F_CONNECT) {
594 rc = connect(sfd, (struct sockaddr *)&local, namelen);
595 if (rc < 0)
596 goto err;
597 } else {
598 unlink(local.sun_path);
599 rc = bind(sfd, (struct sockaddr *)&local, namelen);
600 if (rc < 0)
601 goto err;
602 }
603
604 if (flags & OSMO_SOCK_F_NONBLOCK) {
605 if (ioctl(sfd, FIONBIO, (unsigned char *)&on) < 0) {
Philipp Maier6f0f5602017-02-09 14:09:06 +0100606 LOGP(DLGLOBAL, LOGL_ERROR,
607 "cannot set this socket unblocking: %s\n",
608 strerror(errno));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100609 close(sfd);
610 return -EINVAL;
611 }
612 }
613
Harald Weltec47bbda2017-07-13 16:13:26 +0200614 rc = osmo_sock_init_tail(sfd, type, flags);
615 if (rc < 0) {
616 close(sfd);
617 sfd = -1;
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100618 }
619
620 return sfd;
621err:
622 close(sfd);
623 return -1;
624}
625
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200626/*! Initialize a unix domain socket and fill \ref osmo_fd
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100627 * \param[out] ofd file descriptor (will be filled in)
628 * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
629 * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
630 * \param[in] socket_path path to identify the socket
631 * \param[in] flags flags like \ref OSMO_SOCK_F_CONNECT
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200632 * \returns socket fd on success; negative on error
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100633 *
634 * This function creates (and optionall binds/connects) a socket using
635 * \ref osmo_sock_unix_init, but also fills the \a ofd structure.
636 */
637int osmo_sock_unix_init_ofd(struct osmo_fd *ofd, uint16_t type, uint8_t proto,
638 const char *socket_path, unsigned int flags)
639{
Max862ba652014-10-13 14:54:25 +0200640 return osmo_fd_init_ofd(ofd, osmo_sock_unix_init(type, proto, socket_path, flags));
Álvaro Neira Ayuso5ade61a2014-03-24 13:02:00 +0100641}
642
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200643/*! Get address/port information on soocket in dyn-alloc string
Harald Welte48f55832017-01-26 00:03:10 +0100644 * \param[in] ctx talloc context from which to allocate string buffer
645 * \param[in] fd file descriptor of socket
646 * \returns string identifying the connection of this socket
647 */
648char *osmo_sock_get_name(void *ctx, int fd)
649{
650 struct sockaddr sa_l, sa_r;
651 socklen_t sa_len_l = sizeof(sa_l);
652 socklen_t sa_len_r = sizeof(sa_r);
653 char hostbuf_l[64], hostbuf_r[64];
654 char portbuf_l[16], portbuf_r[16];
655 int rc;
656
657 rc = getsockname(fd, &sa_l, &sa_len_l);
658 if (rc < 0)
659 return NULL;
660
661 rc = getnameinfo(&sa_l, sa_len_l, hostbuf_l, sizeof(hostbuf_l),
662 portbuf_l, sizeof(portbuf_l),
663 NI_NUMERICHOST | NI_NUMERICSERV);
664 if (rc < 0)
665 return NULL;
666
667 rc = getpeername(fd, &sa_r, &sa_len_r);
668 if (rc < 0)
669 goto local_only;
670
671 rc = getnameinfo(&sa_r, sa_len_r, hostbuf_r, sizeof(hostbuf_r),
672 portbuf_r, sizeof(portbuf_r),
673 NI_NUMERICHOST | NI_NUMERICSERV);
674 if (rc < 0)
675 goto local_only;
676
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200677 return talloc_asprintf(ctx, "(r=%s:%s<->l=%s:%s)", hostbuf_r, portbuf_r,
Harald Welte48f55832017-01-26 00:03:10 +0100678 hostbuf_l, portbuf_l);
679
680local_only:
Neels Hofmeyr1f82d0a2017-06-21 22:54:46 +0200681 return talloc_asprintf(ctx, "(r=NULL<->l=%s:%s)", hostbuf_l, portbuf_l);
Harald Welte48f55832017-01-26 00:03:10 +0100682}
683
Harald Weltee30d7e62017-07-13 16:02:50 +0200684static int sock_get_domain(int fd)
685{
686 int domain;
687#ifdef SO_DOMAIN
688 socklen_t dom_len = sizeof(domain);
689 int rc;
690
691 rc = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &dom_len);
692 if (rc < 0)
693 return rc;
694#else
695 /* This of course sucks, but what shall we do on OSs like
696 * FreeBSD that don't seem to expose a method by which one can
697 * learn the address family of a socket? */
698 domain = AF_INET;
699#endif
700 return domain;
701}
702
703
704/*! Activate or de-activate local loop-back of transmitted multicast packets
705 * \param[in] fd file descriptor of related socket
706 * \param[in] enable Enable (true) or disable (false) loop-back
707 * \returns 0 on success; negative otherwise */
708int osmo_sock_mcast_loop_set(int fd, bool enable)
709{
710 int domain, loop = 0;
711
712 if (enable)
713 loop = 1;
714
715 domain = sock_get_domain(fd);
716 if (domain < 0)
717 return domain;
718
719 switch (domain) {
720 case AF_INET:
721 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));
722 case AF_INET6:
723 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop));
724 default:
725 return -EINVAL;
726 }
727}
728
729/*! Set the TTL of outbound multicast packets
730 * \param[in] fd file descriptor of related socket
731 * \param[in] ttl TTL of to-be-sent multicast packets
732 * \returns 0 on success; negative otherwise */
733int osmo_sock_mcast_ttl_set(int fd, uint8_t ttl)
734{
735 int domain, ttli = ttl;
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_TTL, &ttli, sizeof(ttli));
744 case AF_INET6:
745 return setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttli, sizeof(ttli));
746 default:
747 return -EINVAL;
748 }
749}
750
751/*! Enable/disable receiving all multicast packets, even for non-subscribed groups
752 * \param[in] fd file descriptor of related socket
753 * \param[in] enable Enable or Disable receiving of all packets
754 * \returns 0 on success; negative otherwise */
755int osmo_sock_mcast_all_set(int fd, bool enable)
756{
757 int domain, all = 0;
758
759 if (enable)
760 all = 1;
761
762 domain = sock_get_domain(fd);
763 if (domain < 0)
764 return domain;
765
766 switch (domain) {
767 case AF_INET:
768#ifdef IP_MULTICAST_ALL
769 return setsockopt(fd, IPPROTO_IP, IP_MULTICAST_ALL, &all, sizeof(all));
770#endif
771 case AF_INET6:
772 /* there seems no equivalent ?!? */
773 default:
774 return -EINVAL;
775 }
776}
777
778/* FreeBSD calls the socket option differently */
779#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
780#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
781#endif
782
783/*! Subscribe to the given IP multicast group
784 * \param[in] fd file descriptor of related scoket
785 * \param[in] grp_addr ASCII representation of the multicast group address
786 * \returns 0 on success; negative otherwise */
787int osmo_sock_mcast_subscribe(int fd, const char *grp_addr)
788{
789 int rc, domain;
790 struct ip_mreq mreq;
791 struct ipv6_mreq mreq6;
792 struct in6_addr i6a;
793
794 domain = sock_get_domain(fd);
795 if (domain < 0)
796 return domain;
797
798 switch (domain) {
799 case AF_INET:
800 memset(&mreq, 0, sizeof(mreq));
801 mreq.imr_multiaddr.s_addr = inet_addr(grp_addr);
802 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
803 return setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
804#ifdef IPV6_ADD_MEMBERSHIP
805 case AF_INET6:
806 memset(&mreq6, 0, sizeof(mreq6));
807 rc = inet_pton(AF_INET6, grp_addr, (void *)&i6a);
808 if (rc < 0)
809 return -EINVAL;
810 mreq6.ipv6mr_multiaddr = i6a;
811 return setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6));
812#endif
813 default:
814 return -EINVAL;
815 }
816}
817
Philipp Maier2d2490e2017-10-20 19:41:26 +0200818/*! Determine the matching local IP-address for a given remote IP-Address.
819 * \param[out] local_ip caller provided memory for resulting local IP-address
820 * \param[in] remote_ip remote IP-address
821 * \param[in] fd file descriptor of related scoket
822 * \returns 0 on success; negative otherwise
823 *
824 * The function accepts IPv4 and IPv6 address strings. The caller must provide
825 * at least INET6_ADDRSTRLEN bytes for local_ip if an IPv6 is expected as
826 * as result. For IPv4 addresses the required amount is INET_ADDRSTRLEN. */
827int osmo_sock_local_ip(char *local_ip, const char *remote_ip)
828{
829 int sfd;
830 int rc;
831 struct addrinfo addrinfo_hint;
832 struct addrinfo *addrinfo = NULL;
833 struct sockaddr_in local_addr;
834 socklen_t local_addr_len;
835 uint16_t family;
836
837 /* Find out the address family (AF_INET or AF_INET6?) */
838 memset(&addrinfo_hint, '\0', sizeof(addrinfo_hint));
839 addrinfo_hint.ai_family = PF_UNSPEC;
840 addrinfo_hint.ai_flags = AI_NUMERICHOST;
841 rc = getaddrinfo(remote_ip, NULL, &addrinfo_hint, &addrinfo);
842 if (rc)
843 return -EINVAL;
844 family = addrinfo->ai_family;
845 freeaddrinfo(addrinfo);
846
847 /* Connect a dummy socket to trick the kernel into determining the
848 * ip-address of the interface that would be used if we would send
849 * out an actual packet */
850 sfd = osmo_sock_init2(family, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, remote_ip, 0, OSMO_SOCK_F_CONNECT);
851 if (sfd < 0)
852 return -EINVAL;
853
854 /* Request the IP address of the interface that the kernel has
855 * actually choosen. */
856 memset(&local_addr, 0, sizeof(local_addr));
857 local_addr_len = sizeof(local_addr);
858 rc = getsockname(sfd, (struct sockaddr *)&local_addr, &local_addr_len);
Philipp Maier8b7975b2018-01-22 15:38:07 +0100859 close(sfd);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200860 if (rc < 0)
861 return -EINVAL;
862 if (local_addr.sin_family == AF_INET)
Philipp Maier91cfda82018-01-22 16:56:27 +0100863 inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200864 else if (local_addr.sin_family == AF_INET6)
Philipp Maier91cfda82018-01-22 16:56:27 +0100865 inet_ntop(AF_INET6, &local_addr.sin_addr, local_ip, INET6_ADDRSTRLEN);
Philipp Maier2d2490e2017-10-20 19:41:26 +0200866 else
867 return -EINVAL;
868
869 return 0;
870}
871
Harald Weltee4764422011-05-22 12:25:57 +0200872#endif /* HAVE_SYS_SOCKET_H */
Harald Welteba6988b2011-08-17 12:46:48 +0200873
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200874/*! @} */