Skip to content

Commit dcf1362

Browse files
committed
pex: add support for sending/receiving global PEX messages via unix socket
This can be used for allowing another protocol (e.g. DHT) to run on the same port, making it easier to deal with NAT Signed-off-by: Felix Fietkau <[email protected]>
1 parent 0c2f39e commit dcf1362

File tree

5 files changed

+228
-26
lines changed

5 files changed

+228
-26
lines changed

main.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ static void add_networks(void)
101101
int main(int argc, char **argv)
102102
{
103103
struct cmdline_network *net;
104+
const char *unix_socket = NULL;
104105
int ch;
105106

106-
while ((ch = getopt(argc, argv, "D:dh:M:N:P:")) != -1) {
107+
while ((ch = getopt(argc, argv, "D:dh:u:M:N:P:")) != -1) {
107108
switch (ch) {
108109
case 'D':
109110
data_dir = optarg;
@@ -126,13 +127,16 @@ int main(int argc, char **argv)
126127
case 'P':
127128
global_pex_port = atoi(optarg);
128129
break;
130+
case 'u':
131+
unix_socket = optarg;
132+
break;
129133
}
130134
}
131135

132136
uloop_init();
133137
unetd_ubus_init();
134138
unetd_write_hosts();
135-
global_pex_open();
139+
global_pex_open(unix_socket);
136140
add_networks();
137141
uloop_run();
138142
pex_close();

pex-msg.c

+185-10
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
*/
55
#include <sys/types.h>
66
#include <sys/socket.h>
7+
#include <sys/un.h>
78
#include <arpa/inet.h>
89
#include <errno.h>
910
#include <fcntl.h>
1011
#include <libubox/list.h>
1112
#include <libubox/uloop.h>
13+
#include <libubox/usock.h>
1214
#include <netinet/in.h>
1315
#include <netinet/ip.h>
1416
#include <netinet/ip6.h>
@@ -19,12 +21,33 @@
1921

2022
static char pex_tx_buf[PEX_BUF_SIZE];
2123
static FILE *pex_urandom;
22-
static struct uloop_fd pex_fd;
24+
static struct uloop_fd pex_fd, pex_unix_fd;
2325
static LIST_HEAD(requests);
2426
static struct uloop_timeout gc_timer;
2527
static int pex_raw_v4_fd = -1, pex_raw_v6_fd = -1;
2628

2729
static pex_recv_cb_t pex_recv_cb;
30+
static pex_recv_control_cb_t pex_control_cb;
31+
static int pex_unix_tx_fd = -1;
32+
33+
static const void *
34+
get_mapped_sockaddr(const void *addr)
35+
{
36+
static struct sockaddr_in6 sin6;
37+
const struct sockaddr_in *sin = addr;
38+
39+
if (!sin || sin->sin_family != AF_INET)
40+
return addr;
41+
42+
memset(&sin6, 0, sizeof(sin6));
43+
sin6.sin6_family = AF_INET6;
44+
sin6.sin6_addr.s6_addr[10] = 0xff;
45+
sin6.sin6_addr.s6_addr[11] = 0xff;
46+
memcpy(&sin6.sin6_addr.s6_addr[12], &sin->sin_addr, sizeof(struct in_addr));
47+
sin6.sin6_port = sin->sin_port;
48+
49+
return &sin6;
50+
}
2851

2952
struct pex_msg_update_recv_ctx {
3053
struct list_head list;
@@ -112,12 +135,20 @@ void *pex_msg_append(size_t len)
112135
static void
113136
pex_fd_cb(struct uloop_fd *fd, unsigned int events)
114137
{
115-
struct sockaddr_in6 sin6;
116-
static char buf[PEX_BUF_SIZE];
138+
static struct sockaddr_in6 sin6;
139+
static char buf[PEX_RX_BUF_SIZE];
117140
struct pex_hdr *hdr = (struct pex_hdr *)buf;
118141
ssize_t len;
119142

120143
while (1) {
144+
static struct iovec iov[2] = {
145+
{ .iov_base = &sin6 },
146+
{ .iov_base = buf },
147+
};
148+
static struct msghdr msg = {
149+
.msg_iov = iov,
150+
.msg_iovlen = ARRAY_SIZE(iov),
151+
};
121152
socklen_t slen = sizeof(sin6);
122153

123154
len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen);
@@ -135,6 +166,39 @@ pex_fd_cb(struct uloop_fd *fd, unsigned int events)
135166
if (!len)
136167
continue;
137168

169+
if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
170+
struct sockaddr_in *sin = (struct sockaddr_in *)&sin6;
171+
struct in_addr in = *(struct in_addr *)&sin6.sin6_addr.s6_addr[12];
172+
int port = sin6.sin6_port;
173+
174+
memset(&sin6, 0, sizeof(sin6));
175+
sin->sin_port = port;
176+
sin->sin_family = AF_INET;
177+
sin->sin_addr = in;
178+
slen = sizeof(*sin);
179+
}
180+
181+
retry:
182+
if (pex_unix_tx_fd >= 0) {
183+
iov[0].iov_len = slen;
184+
iov[1].iov_len = len;
185+
if (sendmsg(pex_unix_tx_fd, &msg, 0) < 0) {
186+
switch (errno) {
187+
case EINTR:
188+
goto retry;
189+
case EMSGSIZE:
190+
case ENOBUFS:
191+
case EAGAIN:
192+
continue;
193+
default:
194+
perror("sendmsg");
195+
close(pex_unix_tx_fd);
196+
pex_unix_tx_fd = -1;
197+
break;
198+
}
199+
}
200+
}
201+
138202
if (len < sizeof(*hdr) + sizeof(struct pex_ext_hdr))
139203
continue;
140204

@@ -146,6 +210,86 @@ pex_fd_cb(struct uloop_fd *fd, unsigned int events)
146210
}
147211
}
148212

213+
static void
214+
pex_unix_cb(struct uloop_fd *fd, unsigned int events)
215+
{
216+
static char buf[PEX_RX_BUF_SIZE];
217+
static struct iovec iov = {
218+
.iov_base = buf,
219+
.iov_len = sizeof(buf),
220+
};
221+
ssize_t len;
222+
223+
while (1) {
224+
const struct sockaddr *sa = (struct sockaddr *)buf;
225+
uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 };
226+
struct msghdr msg = {
227+
.msg_iov = &iov,
228+
.msg_iovlen = 1,
229+
.msg_control = fd_buf,
230+
.msg_controllen = CMSG_LEN(sizeof(int)),
231+
};
232+
struct cmsghdr *cmsg;
233+
socklen_t slen;
234+
int *pfd;
235+
236+
cmsg = CMSG_FIRSTHDR(&msg);
237+
cmsg->cmsg_type = SCM_RIGHTS;
238+
cmsg->cmsg_level = SOL_SOCKET;
239+
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
240+
241+
pfd = (int *)CMSG_DATA(cmsg);
242+
*pfd = -1;
243+
244+
len = recvmsg(fd->fd, &msg, 0);
245+
if (len < 0) {
246+
if (errno == EINTR)
247+
continue;
248+
249+
if (errno == EAGAIN)
250+
break;
251+
252+
pex_close();
253+
return;
254+
}
255+
256+
if (*pfd >= 0) {
257+
if (pex_unix_tx_fd >= 0)
258+
close(pex_unix_tx_fd);
259+
260+
pex_unix_tx_fd = *pfd;
261+
}
262+
263+
if (!len)
264+
continue;
265+
266+
if (len < sizeof(*sa))
267+
continue;
268+
269+
if (sa->sa_family == AF_LOCAL) {
270+
slen = sizeof(struct sockaddr);
271+
len -= slen;
272+
if (len < sizeof(struct pex_msg_local_control))
273+
continue;
274+
275+
if (pex_control_cb)
276+
pex_control_cb((struct pex_msg_local_control *)&buf[slen], len);
277+
278+
continue;
279+
}
280+
281+
if (sa->sa_family == AF_INET)
282+
slen = sizeof(struct sockaddr_in);
283+
else if (sa->sa_family == AF_INET6)
284+
slen = sizeof(struct sockaddr_in6);
285+
else
286+
continue;
287+
288+
sa = get_mapped_sockaddr(sa);
289+
sendto(pex_fd.fd, buf + slen, len - slen, 0, sa, sizeof(struct sockaddr_in6));
290+
}
291+
}
292+
149293
static inline uint32_t
150294
csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto)
151295
{
@@ -268,8 +412,10 @@ int __pex_msg_send(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen)
268412
hdr->len -= sizeof(struct pex_ext_hdr);
269413
if (ip_hdrlen)
270414
fd = sa->sa_family == AF_INET6 ? pex_raw_v6_fd : pex_raw_v4_fd;
271-
else
415+
else {
272416
fd = pex_fd.fd;
417+
sa = addr = get_mapped_sockaddr(addr);
418+
}
273419

274420
if (fd < 0)
275421
return -1;
@@ -612,21 +758,50 @@ int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server)
612758
return -1;
613759
}
614760

615-
void pex_close(void)
761+
int pex_unix_open(const char *path, pex_recv_control_cb_t cb)
616762
{
617-
if (!pex_fd.cb)
618-
return;
763+
mode_t prev_mask;
764+
int fd;
765+
766+
pex_control_cb = cb;
767+
unlink(path);
619768

769+
prev_mask = umask(0177);
770+
fd = usock(USOCK_UDP | USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, path, NULL);
771+
umask(prev_mask);
772+
if (fd < 0)
773+
return -1;
774+
775+
pex_unix_fd.cb = pex_unix_cb;
776+
pex_unix_fd.fd = fd;
777+
uloop_fd_add(&pex_unix_fd, ULOOP_READ);
778+
779+
return 0;
780+
}
781+
782+
void pex_close(void)
783+
{
620784
if (pex_raw_v4_fd >= 0)
621785
close(pex_raw_v4_fd);
622786
if (pex_raw_v6_fd >= 0)
623787
close(pex_raw_v6_fd);
624788
pex_raw_v4_fd = -1;
625789
pex_raw_v6_fd = -1;
626790

627-
fclose(pex_urandom);
628-
uloop_fd_delete(&pex_fd);
629-
close(pex_fd.fd);
791+
if (pex_urandom)
792+
fclose(pex_urandom);
793+
794+
if (pex_fd.cb) {
795+
uloop_fd_delete(&pex_fd);
796+
close(pex_fd.fd);
797+
}
798+
799+
if (pex_unix_fd.cb) {
800+
uloop_fd_delete(&pex_unix_fd);
801+
close(pex_unix_fd.fd);
802+
}
803+
630804
pex_fd.cb = NULL;
805+
pex_unix_fd.cb = NULL;
631806
pex_urandom = NULL;
632807
}

pex-msg.h

+11
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
#include <stdio.h>
77
#include "curve25519.h"
88
#include "siphash.h"
9+
#include "utils.h"
910

1011
#define UNETD_GLOBAL_PEX_PORT 51819
1112
#define PEX_BUF_SIZE 1024
13+
#define PEX_RX_BUF_SIZE 16384
1214
#define UNETD_NET_DATA_SIZE_MAX (128 * 1024)
1315

1416
enum pex_opcode {
@@ -85,9 +87,18 @@ struct pex_msg_update_send_ctx {
8587
int rem;
8688
};
8789

90+
struct pex_msg_local_control {
91+
int msg_type;
92+
uint8_t auth_id[PEX_ID_LEN];
93+
union network_endpoint ep;
94+
int timeout;
95+
};
96+
8897
typedef void (*pex_recv_cb_t)(struct pex_hdr *hdr, struct sockaddr_in6 *addr);
98+
typedef void (*pex_recv_control_cb_t)(struct pex_msg_local_control *msg, int len);
8999

90100
int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server);
101+
int pex_unix_open(const char *path, pex_recv_control_cb_t cb);
91102
void pex_close(void);
92103

93104
uint64_t pex_network_hash(const uint8_t *auth_key, uint64_t req_id);

pex.c

+25-13
Original file line numberDiff line numberDiff line change
@@ -879,17 +879,6 @@ global_pex_recv(struct pex_hdr *hdr, struct sockaddr_in6 *addr)
879879
if (!peer)
880880
break;
881881

882-
if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) {
883-
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
884-
struct in_addr in = *(struct in_addr *)&addr->sin6_addr.s6_addr[12];
885-
int port = addr->sin6_port;
886-
887-
memset(addr, 0, sizeof(*addr));
888-
sin->sin_port = port;
889-
sin->sin_family = AF_INET;
890-
sin->sin_addr = in;
891-
}
892-
893882
D_PEER(net, peer, "receive endpoint notification from %s",
894883
inet_ntop(addr->sin6_family, network_endpoint_addr((void *)addr, &addr_len),
895884
buf, sizeof(buf)));
@@ -899,12 +888,35 @@ global_pex_recv(struct pex_hdr *hdr, struct sockaddr_in6 *addr)
899888
}
900889
}
901890

902-
int global_pex_open(void)
891+
static void
892+
pex_recv_control(struct pex_msg_local_control *msg, int len)
893+
{
894+
struct network *net;
895+
896+
if (msg->msg_type != 0)
897+
return;
898+
899+
net = global_pex_find_network(msg->auth_id);
900+
if (!net)
901+
return;
902+
903+
if (!msg->timeout)
904+
msg->timeout = 60;
905+
network_pex_create_host(net, &msg->ep, msg->timeout);
906+
}
907+
908+
int global_pex_open(const char *unix_path)
903909
{
904910
struct sockaddr_in6 sin6 = {};
911+
int ret;
905912

906913
sin6.sin6_family = AF_INET6;
907914
sin6.sin6_port = htons(global_pex_port);
908915

909-
return pex_open(&sin6, sizeof(sin6), global_pex_recv, true);
916+
ret = pex_open(&sin6, sizeof(sin6), global_pex_recv, true);
917+
918+
if (unix_path)
919+
pex_unix_open(unix_path, pex_recv_control);
920+
921+
return ret;
910922
}

pex.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ static inline bool network_pex_active(struct network_pex *pex)
4343
return pex->fd.fd >= 0;
4444
}
4545

46-
int global_pex_open(void);
46+
int global_pex_open(const char *unix_path);
4747

4848
#endif

0 commit comments

Comments
 (0)