source: trunk/libtransmission/tr-udp.c @ 12184

Last change on this file since 12184 was 12184, checked in by jordan, 11 years ago

(trunk libT) better shutdown management of libutp and UDP trackers in tr_sessionClose().

This is a little overlapping since the utp code can be closed more-or-less immediately, but the udp manager needs to stay open in order to process the udp tracker connection requests before sending out event=stopped. Moreover DNS resolver can be shut down after the UDP tracker is shutdown.

File size: 8.8 KB
Line 
1/*
2Copyright (c) 2010 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21
22*/
23
24#include <unistd.h>
25#include <assert.h>
26#include <string.h> /* memcmp(), memcpy(), memset() */
27#include <stdlib.h> /* malloc(), free() */
28
29#include <event2/event.h>
30
31#include "transmission.h"
32#include "net.h"
33#include "session.h"
34#include "tr-dht.h"
35#include "tr-utp.h"
36#include "tr-udp.h"
37
38/* Since we use a single UDP socket in order to implement multiple
39   uTP sockets, try to set up huge buffers. */
40
41#define RECV_BUFFER_SIZE (4 * 1024 * 1024)
42#define SEND_BUFFER_SIZE (1 * 1024 * 1024)
43#define SMALL_BUFFER_SIZE (32 * 1024)
44
45static void
46set_socket_buffers(int fd, int large)
47{
48    int size, rbuf, sbuf, rc;
49    socklen_t rbuf_len = sizeof(rbuf), sbuf_len = sizeof(sbuf);
50
51    size = large ? RECV_BUFFER_SIZE : SMALL_BUFFER_SIZE;
52    rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
53    if(rc < 0)
54        tr_nerr("UDP", "Failed to set receive buffer: %s",
55                tr_strerror(errno));
56
57    size = large ? SEND_BUFFER_SIZE : SMALL_BUFFER_SIZE;
58    rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
59    if(rc < 0)
60        tr_nerr("UDP", "Failed to set send buffer: %s",
61                tr_strerror(errno));
62
63    if(large) {
64        rc = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &rbuf_len);
65        if(rc < 0)
66            rbuf = 0;
67
68        rc = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &sbuf_len);
69        if(rc < 0)
70            sbuf = 0;
71
72        if(rbuf < RECV_BUFFER_SIZE) {
73            tr_nerr("UDP", "Failed to set receive buffer: requested %d, got %d",
74                    RECV_BUFFER_SIZE, rbuf);
75#ifdef __linux__
76            tr_ninf("UDP",
77                    "Please add the line "
78                    "\"net.core.rmem_max = %d\" to /etc/sysctl.conf",
79                    RECV_BUFFER_SIZE);
80#endif
81        }
82
83        if(sbuf < SEND_BUFFER_SIZE) {
84            tr_nerr("UDP", "Failed to set send buffer: requested %d, got %d",
85                    SEND_BUFFER_SIZE, sbuf);
86#ifdef __linux__
87            tr_ninf("UDP",
88                    "Please add the line "
89                    "\"net.core.wmem_max = %d\" to /etc/sysctl.conf",
90                    SEND_BUFFER_SIZE);
91#endif
92        }
93    }
94}
95
96void
97tr_udpSetSocketBuffers(tr_session *session)
98{
99    tr_bool utp = tr_sessionIsUTPEnabled(session);
100    if(session->udp_socket >= 0)
101        set_socket_buffers(session->udp_socket, utp);
102    if(session->udp6_socket >= 0)
103        set_socket_buffers(session->udp6_socket, utp);
104}
105
106
107
108
109/* BEP-32 has a rather nice explanation of why we need to bind to one
110   IPv6 address, if I may say so myself. */
111
112static void
113rebind_ipv6(tr_session *ss, tr_bool force)
114{
115    tr_bool is_default;
116    const struct tr_address * public_addr;
117    struct sockaddr_in6 sin6;
118    const unsigned char *ipv6 = tr_globalIPv6();
119    int s = -1, rc;
120    int one = 1;
121
122    /* We currently have no way to enable or disable IPv6 after initialisation.
123       No way to fix that without some surgery to the DHT code itself. */
124    if(ipv6 == NULL || (!force && ss->udp6_socket < 0)) {
125        if(ss->udp6_bound) {
126            free(ss->udp6_bound);
127            ss->udp6_bound = NULL;
128        }
129        return;
130    }
131
132    if(ss->udp6_bound != NULL && memcmp(ipv6, ss->udp6_bound, 16) == 0)
133        return;
134
135    s = socket(PF_INET6, SOCK_DGRAM, 0);
136    if(s < 0)
137        goto fail;
138
139#ifdef IPV6_V6ONLY
140        /* Since we always open an IPv4 socket on the same port, this
141           shouldn't matter.  But I'm superstitious. */
142        setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
143#endif
144
145    memset(&sin6, 0, sizeof(sin6));
146    sin6.sin6_family = AF_INET6;
147    if(ipv6)
148        memcpy(&sin6.sin6_addr, ipv6, 16);
149    sin6.sin6_port = htons(ss->udp_port);
150    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET6, &is_default);
151    if(public_addr && !is_default)
152        sin6.sin6_addr = public_addr->addr.addr6;
153
154    rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
155    if(rc < 0)
156        goto fail;
157
158    if(ss->udp6_socket < 0) {
159        ss->udp6_socket = s;
160        s = -1;
161    } else {
162        rc = dup2(s, ss->udp6_socket);
163        if(rc < 0)
164            goto fail;
165        close(s);
166        s = -1;
167    }
168
169    if(ss->udp6_bound == NULL)
170        ss->udp6_bound = malloc(16);
171    if(ss->udp6_bound)
172        memcpy(ss->udp6_bound, ipv6, 16);
173
174    return;
175
176 fail:
177    /* Something went wrong.  It's difficult to recover, so let's simply
178       set things up so that we try again next time. */
179    tr_nerr("UDP", "Couldn't rebind IPv6 socket");
180    if(s >= 0)
181        close(s);
182    if(ss->udp6_bound) {
183        free(ss->udp6_bound);
184        ss->udp6_bound = NULL;
185    }
186}
187
188static void
189event_callback(int s, short type UNUSED, void *sv)
190{
191    tr_session *ss = sv;
192    unsigned char *buf;
193    struct sockaddr_storage from;
194    socklen_t fromlen;
195    int rc;
196
197    assert(tr_isSession(sv));
198    assert(type == EV_READ);
199
200    buf = malloc(4096);
201    if(buf == NULL) {
202        tr_nerr("UDP", "Couldn't allocate buffer");
203        return;
204    }
205
206    fromlen = sizeof(from);
207    rc = recvfrom(s, buf, 4096 - 1, 0,
208                  (struct sockaddr*)&from, &fromlen);
209    if(rc > 0) {
210        if( tau_handle_message( ss, buf, rc ) ) {
211            tr_ndbg("UDP", "Received UDP Tracker packet");
212        }
213        else if( buf[0] == 'd' ) {
214            /* DHT packet. */
215            buf[rc] = '\0';
216            tr_dhtCallback(buf, rc, (struct sockaddr*)&from, fromlen, sv);
217        } else {
218            rc = tr_utpPacket(buf, rc, (struct sockaddr*)&from, fromlen, ss);
219            if(!rc)
220                tr_ndbg("UDP", "Unexpected UDP packet");
221        }
222    }
223
224    free(buf);
225}
226
227void
228tr_udpInit(tr_session *ss)
229{
230    tr_bool is_default;
231    const struct tr_address * public_addr;
232    struct sockaddr_in sin;
233    int rc;
234
235    assert(ss->udp_socket < 0);
236    assert(ss->udp6_socket < 0);
237
238    ss->udp_port = tr_sessionGetPeerPort(ss);
239    if(ss->udp_port <= 0)
240        return;
241
242    ss->udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
243    if(ss->udp_socket < 0) {
244        tr_nerr("UDP", "Couldn't create IPv4 socket");
245        goto ipv6;
246    }
247
248    memset(&sin, 0, sizeof(sin));
249    sin.sin_family = AF_INET;
250    public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET, &is_default);
251    if(public_addr && !is_default)
252        memcpy(&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr));
253    sin.sin_port = htons(ss->udp_port);
254    rc = bind(ss->udp_socket, (struct sockaddr*)&sin, sizeof(sin));
255    if(rc < 0) {
256        tr_nerr("UDP", "Couldn't bind IPv4 socket");
257        close(ss->udp_socket);
258        ss->udp_socket = -1;
259        goto ipv6;
260    }
261    ss->udp_event =
262        event_new(ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
263                  event_callback, ss);
264    if( ss->udp_event == NULL )
265        tr_nerr("UDP", "Couldn't allocate IPv4 event");
266
267 ipv6:
268    if(tr_globalIPv6())
269        rebind_ipv6(ss, TRUE);
270    if(ss->udp6_socket >= 0) {
271        ss->udp6_event =
272            event_new(ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
273                      event_callback, ss);
274        if(ss->udp6_event == NULL)
275            tr_nerr("UDP", "Couldn't allocate IPv6 event");
276    }
277
278    tr_udpSetSocketBuffers(ss);
279
280    if(ss->isDHTEnabled)
281        tr_dhtInit(ss);
282
283    if(ss->udp_event)
284        event_add(ss->udp_event, NULL);
285    if(ss->udp6_event)
286        event_add(ss->udp6_event, NULL);
287}
288
289void
290tr_udpUninit(tr_session *ss)
291{
292    if(ss->udp_socket >= 0) {
293        tr_netCloseSocket( ss->udp_socket );
294        ss->udp_socket = -1;
295    }
296
297    if(ss->udp_event) {
298        event_free(ss->udp_event);
299        ss->udp_event = NULL;
300    }
301
302    if(ss->udp6_socket >= 0) {
303        tr_netCloseSocket( ss->udp6_socket );
304        ss->udp6_socket = -1;
305    }
306
307    if(ss->udp6_event) {
308        event_free(ss->udp6_event);
309        ss->udp6_event = NULL;
310    }
311
312    if(ss->udp6_bound) {
313        free(ss->udp6_bound);
314        ss->udp6_bound = NULL;
315    }
316}
Note: See TracBrowser for help on using the repository browser.