|
@@ -24,43 +24,135 @@
|
24
|
24
|
#include <arpa/inet.h>
|
25
|
25
|
#include <errno.h>
|
26
|
26
|
|
|
27
|
+#include "util.h"
|
27
|
28
|
#include "udp.h"
|
28
|
29
|
|
|
30
|
+#ifndef IPV6_ADD_MEMBERSHIP
|
|
31
|
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
|
32
|
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
|
|
33
|
+#endif
|
|
34
|
+
|
|
35
|
+static int is_multicast(struct sockaddr_storage *addr) {
|
|
36
|
+ int ret = 0;
|
|
37
|
+ switch (addr->ss_family) {
|
|
38
|
+ case AF_INET: {
|
|
39
|
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
|
|
40
|
+ ret = IN_MULTICAST(ntohl(addr4->sin_addr.s_addr));
|
|
41
|
+ break;
|
|
42
|
+ }
|
|
43
|
+ case AF_INET6: {
|
|
44
|
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
|
|
45
|
+ ret = IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr);
|
|
46
|
+ break;
|
|
47
|
+ } }
|
|
48
|
+ return ret;
|
|
49
|
+}
|
|
50
|
+
|
|
51
|
+extern int ai_family;
|
|
52
|
+
|
|
53
|
+static int bind_addr(const char *hostname, const char *service, int socktype, struct sockaddr_storage *addr, int *addrlen, int *sock) {
|
|
54
|
+ struct addrinfo hints, *res, *ressave;
|
|
55
|
+ int n, ret = -1;
|
|
56
|
+
|
|
57
|
+ memset(&hints, 0, sizeof(struct addrinfo));
|
|
58
|
+ hints.ai_family = ai_family;
|
|
59
|
+ hints.ai_socktype = socktype;
|
|
60
|
+
|
|
61
|
+ n = getaddrinfo(hostname, service, &hints, &res);
|
|
62
|
+ if (n < 0) {
|
|
63
|
+ ts_LOGf("ERROR: getaddrinfo(%s): %s\n", hostname, gai_strerror(n));
|
|
64
|
+ return ret;
|
|
65
|
+ }
|
|
66
|
+
|
|
67
|
+ ressave = res;
|
|
68
|
+ while (res) {
|
|
69
|
+ *sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
70
|
+ if (*sock > -1) {
|
|
71
|
+ if (bind(*sock, res->ai_addr, res->ai_addrlen) == 0) {
|
|
72
|
+ memcpy(addr, res->ai_addr, sizeof(*addr));
|
|
73
|
+ *addrlen = res->ai_addrlen;
|
|
74
|
+ ret = 0;
|
|
75
|
+ goto OUT;
|
|
76
|
+ } else {
|
|
77
|
+ char str_addr[INET6_ADDRSTRLEN];
|
|
78
|
+ my_inet_ntop(res->ai_family, res->ai_addr, str_addr, sizeof(str_addr));
|
|
79
|
+ ts_LOGf("ERROR: bind: %s:%s (%s): %s\n",
|
|
80
|
+ hostname, service, str_addr, strerror(errno));
|
|
81
|
+ }
|
|
82
|
+ close(*sock);
|
|
83
|
+ *sock = -1;
|
|
84
|
+ }
|
|
85
|
+ res = res->ai_next;
|
|
86
|
+ }
|
|
87
|
+OUT:
|
|
88
|
+ freeaddrinfo(ressave);
|
|
89
|
+
|
|
90
|
+ if (*sock > -1) {
|
|
91
|
+ int on = 1;
|
|
92
|
+ setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
|
93
|
+ set_sock_nonblock(*sock);
|
|
94
|
+ }
|
|
95
|
+
|
|
96
|
+ return ret;
|
|
97
|
+}
|
|
98
|
+
|
|
99
|
+static int join_multicast_group(int sock, int ttl, struct sockaddr_storage *addr) {
|
|
100
|
+ switch (addr->ss_family) {
|
|
101
|
+ case AF_INET: {
|
|
102
|
+ struct ip_mreq mreq;
|
|
103
|
+ mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
|
|
104
|
+ mreq.imr_interface.s_addr = INADDR_ANY;
|
|
105
|
+
|
|
106
|
+ if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const void *)&mreq, sizeof(mreq)) < 0) {
|
|
107
|
+ ts_LOGf("ERROR: setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
|
|
108
|
+ return -1;
|
|
109
|
+ }
|
|
110
|
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
|
|
111
|
+ ts_LOGf("ERROR: setsockopt(IP_MULTICAST_TTL %d): %s\n", ttl, strerror(errno));
|
|
112
|
+ }
|
|
113
|
+ break;
|
|
114
|
+ }
|
|
115
|
+
|
|
116
|
+ case AF_INET6: {
|
|
117
|
+ struct ipv6_mreq mreq6;
|
|
118
|
+ memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
|
|
119
|
+ mreq6.ipv6mr_interface = 0; // interface index, will be set later
|
|
120
|
+
|
|
121
|
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
|
|
122
|
+ ts_LOGf("ERROR: setsockopt(IPV6_ADD_MEMBERSHIP): %s\n", strerror(errno));
|
|
123
|
+ return -1;
|
|
124
|
+ }
|
|
125
|
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
|
|
126
|
+ ts_LOGf("ERROR: setsockopt(IPV6_MULTICAST_HOPS %d): %s\n", ttl, strerror(errno));
|
|
127
|
+ }
|
|
128
|
+ break;
|
|
129
|
+ }
|
|
130
|
+ }
|
|
131
|
+
|
|
132
|
+ return 0;
|
|
133
|
+}
|
|
134
|
+
|
29
|
135
|
int udp_connect_input(struct io *io) {
|
30
|
|
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
31
|
|
- if (sock < 0) {
|
32
|
|
- ts_LOGf("socket(SOCK_DGRAM): %s\n", strerror(errno));
|
|
136
|
+ struct sockaddr_storage addr;
|
|
137
|
+ int addrlen = sizeof(addr);
|
|
138
|
+ int sock = -1;
|
|
139
|
+
|
|
140
|
+ memset(&addr, 0, sizeof(addr));
|
|
141
|
+
|
|
142
|
+ ts_LOGf("Connecting input to %s port %s\n", io->hostname, io->service);
|
|
143
|
+ if (bind_addr(io->hostname, io->service, SOCK_DGRAM, &addr, &addrlen, &sock) < 0)
|
33
|
144
|
return -1;
|
34
|
|
- }
|
35
|
|
-
|
36
|
|
- ts_LOGf("Connecting input to udp://%s:%d/\n", inet_ntoa(io->addr), io->port);
|
37
|
|
- int on = 1;
|
38
|
|
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
39
|
145
|
|
40
|
146
|
/* Set receive buffer size to ~2.0MB */
|
41
|
147
|
int bufsize = (2000000 / 1316) * 1316;
|
42
|
148
|
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&bufsize, sizeof(bufsize));
|
43
|
149
|
|
44
|
|
- // subscribe to multicast group
|
45
|
|
- if (IN_MULTICAST(ntohl(io->addr.s_addr))) {
|
46
|
|
- struct ip_mreq mreq;
|
47
|
|
- memcpy(&mreq.imr_multiaddr, &io->addr, sizeof(struct in_addr));
|
48
|
|
- mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
49
|
|
- if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
|
50
|
|
- ts_LOGf("setsockopt(IP_ADD_MEMBERSHIP %s): %s\n", inet_ntoa(io->addr), strerror(errno));
|
|
150
|
+ if (is_multicast(&addr)) {
|
|
151
|
+ if (join_multicast_group(sock, io->ttl, &addr) < 0) {
|
|
152
|
+ close(sock);
|
51
|
153
|
return -1;
|
52
|
154
|
}
|
53
|
155
|
}
|
54
|
|
- // bind to the socket so data can be read
|
55
|
|
- struct sockaddr_in receiving_from;
|
56
|
|
- memset(&receiving_from, 0, sizeof(receiving_from));
|
57
|
|
- receiving_from.sin_family = AF_INET;
|
58
|
|
- receiving_from.sin_addr = io->addr;
|
59
|
|
- receiving_from.sin_port = htons(io->port);
|
60
|
|
- if (bind(sock, (struct sockaddr *) &receiving_from, sizeof(receiving_from)) < 0) {
|
61
|
|
- ts_LOGf("bind(): %s\n", strerror(errno));
|
62
|
|
- return -1;
|
63
|
|
- }
|
64
|
156
|
|
65
|
157
|
io->fd = sock;
|
66
|
158
|
ts_LOGf("Input connected to fd:%d\n", io->fd);
|
|
@@ -69,51 +161,51 @@ int udp_connect_input(struct io *io) {
|
69
|
161
|
}
|
70
|
162
|
|
71
|
163
|
int udp_connect_output(struct io *io) {
|
72
|
|
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
73
|
|
- if (sock < 0) {
|
74
|
|
- ts_LOGf("socket(SOCK_DGRAM): %s\n", strerror(errno));
|
|
164
|
+ struct sockaddr_storage addr;
|
|
165
|
+ int addrlen = sizeof(addr);
|
|
166
|
+ int sock = -1;
|
|
167
|
+
|
|
168
|
+ memset(&addr, 0, sizeof(addr));
|
|
169
|
+
|
|
170
|
+ ts_LOGf("Connecting output to %s port %s ttl: %d\n",
|
|
171
|
+ io->hostname, io->service, io->ttl);
|
|
172
|
+ if (bind_addr(io->hostname, io->service, SOCK_DGRAM, &addr, &addrlen, &sock) < 0)
|
75
|
173
|
return -1;
|
76
|
|
- }
|
77
|
174
|
|
78
|
|
- ts_LOGf("Connecting output to udp://%s:%d ttl:%d\n",
|
79
|
|
- inet_ntoa(io->addr), io->port, io->ttl);
|
80
|
|
-
|
81
|
|
- int on = 1;
|
82
|
|
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
83
|
|
- set_sock_nonblock(sock);
|
84
|
|
-
|
85
|
|
- /* Set receive buffer size to ~2.0MB */
|
|
175
|
+ /* Set send buffer size to ~2.0MB */
|
86
|
176
|
int bufsize = (2000000 / 1316) * 1316;
|
87
|
177
|
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&bufsize, sizeof(bufsize));
|
88
|
178
|
|
89
|
|
- // subscribe to multicast group
|
90
|
|
- if (IN_MULTICAST(ntohl(io->addr.s_addr))) {
|
91
|
|
- int ttl = io->ttl;
|
92
|
|
- if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
|
93
|
|
- ts_LOGf("setsockopt(IP_MUTICAST_TTL): %s\n", strerror(errno));
|
94
|
|
- close(sock);
|
95
|
|
- return -1;
|
96
|
|
- }
|
97
|
|
- if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &io->intf, sizeof(io->intf)) < 0) {
|
98
|
|
- ts_LOGf("setsockopt(IP_MUTICAST_IF %s): %s\n", inet_ntoa(io->intf), strerror(errno));
|
|
179
|
+ if (is_multicast(&addr)) {
|
|
180
|
+ if (join_multicast_group(sock, io->ttl, &addr) < 0) {
|
99
|
181
|
close(sock);
|
100
|
182
|
return -1;
|
|
183
|
+ } else {
|
|
184
|
+ if (addr.ss_family == AF_INET) {
|
|
185
|
+ if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &io->intf, sizeof(io->intf)) < 0) {
|
|
186
|
+ ts_LOGf("ERROR: setsockopt(IP_MUTICAST_IF %s): %s\n", inet_ntoa(io->intf), strerror(errno));
|
|
187
|
+ close(sock);
|
|
188
|
+ return -1;
|
|
189
|
+ }
|
|
190
|
+ }
|
|
191
|
+ if (addr.ss_family == AF_INET6 && io->v6_if_index > -1) {
|
|
192
|
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&io->v6_if_index, sizeof(io->v6_if_index)) < 0) {
|
|
193
|
+ ts_LOGf("ERROR: setsockopt(IPV6_MUTICAST_IF %d): %s\n", io->v6_if_index, strerror(errno));
|
|
194
|
+ close(sock);
|
|
195
|
+ return -1;
|
|
196
|
+ }
|
|
197
|
+ }
|
101
|
198
|
}
|
102
|
199
|
}
|
103
|
200
|
|
104
|
|
- if (io->tos > -1) {
|
|
201
|
+ if (addr.ss_family == AF_INET && io->tos > -1) {
|
105
|
202
|
if (setsockopt(sock, IPPROTO_IP, IP_TOS, &io->tos, sizeof(io->tos)) < 0) {
|
106
|
|
- ts_LOGf("setsockopt(IP_TOS 0x%02x): %s\n", io->tos, strerror(errno));
|
|
203
|
+ ts_LOGf("ERROR: setsockopt(IP_TOS 0x%02x): %s\n", io->tos, strerror(errno));
|
107
|
204
|
}
|
108
|
205
|
}
|
109
|
206
|
|
110
|
|
- struct sockaddr_in sockaddr;
|
111
|
|
- memset(&sockaddr, 0, sizeof(sockaddr));
|
112
|
|
- sockaddr.sin_family = AF_INET;
|
113
|
|
- sockaddr.sin_addr.s_addr = io->addr.s_addr;
|
114
|
|
- sockaddr.sin_port = htons(io->port);
|
115
|
|
- if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
|
116
|
|
- ts_LOGf("udp_connect() error: %s\n", strerror(errno));
|
|
207
|
+ if (connect(sock, (struct sockaddr *)&addr, addrlen) < 0) {
|
|
208
|
+ ts_LOGf("ERROR: udp_connect(): %s\n", strerror(errno));
|
117
|
209
|
close(sock);
|
118
|
210
|
return -1;
|
119
|
211
|
}
|