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

Last change on this file since 14187 was 14187, checked in by jordan, 9 years ago

use evutil_socket_t instead of int to define socket types. Patch my mikedLd :)

File size: 9.6 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 <assert.h>
25#include <string.h> /* memcmp (), memcpy (), memset () */
26#include <stdlib.h> /* malloc (), free () */
27
28#include <unistd.h> /* close () */
29
30#include <event2/event.h>
31
32#include <libutp/utp.h>
33
34#include "transmission.h"
35#include "log.h"
36#include "net.h"
37#include "session.h"
38#include "tr-dht.h"
39#include "tr-utp.h"
40#include "tr-udp.h"
41
42/* Since we use a single UDP socket in order to implement multiple
43   uTP sockets, try to set up huge buffers. */
44
45#define RECV_BUFFER_SIZE (4 * 1024 * 1024)
46#define SEND_BUFFER_SIZE (1 * 1024 * 1024)
47#define SMALL_BUFFER_SIZE (32 * 1024)
48
49static void
50set_socket_buffers (int fd, int large)
51{
52    int size, rbuf, sbuf, rc;
53    socklen_t rbuf_len = sizeof (rbuf), sbuf_len = sizeof (sbuf);
54
55    size = large ? RECV_BUFFER_SIZE : SMALL_BUFFER_SIZE;
56    rc = setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size));
57    if (rc < 0)
58        tr_logAddNamedError ("UDP", "Failed to set receive buffer: %s",
59                tr_strerror (errno));
60
61    size = large ? SEND_BUFFER_SIZE : SMALL_BUFFER_SIZE;
62    rc = setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size));
63    if (rc < 0)
64        tr_logAddNamedError ("UDP", "Failed to set send buffer: %s",
65                tr_strerror (errno));
66
67    if (large) {
68        rc = getsockopt (fd, SOL_SOCKET, SO_RCVBUF, &rbuf, &rbuf_len);
69        if (rc < 0)
70            rbuf = 0;
71
72        rc = getsockopt (fd, SOL_SOCKET, SO_SNDBUF, &sbuf, &sbuf_len);
73        if (rc < 0)
74            sbuf = 0;
75
76        if (rbuf < RECV_BUFFER_SIZE) {
77            tr_logAddNamedError ("UDP", "Failed to set receive buffer: requested %d, got %d",
78                    RECV_BUFFER_SIZE, rbuf);
79#ifdef __linux__
80            tr_logAddNamedInfo ("UDP",
81                    "Please add the line "
82                    "\"net.core.rmem_max = %d\" to /etc/sysctl.conf",
83                    RECV_BUFFER_SIZE);
84#endif
85        }
86
87        if (sbuf < SEND_BUFFER_SIZE) {
88            tr_logAddNamedError ("UDP", "Failed to set send buffer: requested %d, got %d",
89                    SEND_BUFFER_SIZE, sbuf);
90#ifdef __linux__
91            tr_logAddNamedInfo ("UDP",
92                    "Please add the line "
93                    "\"net.core.wmem_max = %d\" to /etc/sysctl.conf",
94                    SEND_BUFFER_SIZE);
95#endif
96        }
97    }
98}
99
100void
101tr_udpSetSocketBuffers (tr_session *session)
102{
103    bool utp = tr_sessionIsUTPEnabled (session);
104    if (session->udp_socket >= 0)
105        set_socket_buffers (session->udp_socket, utp);
106    if (session->udp6_socket >= 0)
107        set_socket_buffers (session->udp6_socket, utp);
108}
109
110
111
112
113/* BEP-32 has a rather nice explanation of why we need to bind to one
114   IPv6 address, if I may say so myself. */
115
116static void
117rebind_ipv6 (tr_session *ss, bool force)
118{
119    bool is_default;
120    const struct tr_address * public_addr;
121    struct sockaddr_in6 sin6;
122    const unsigned char *ipv6 = tr_globalIPv6 ();
123    int s = -1, rc;
124    int one = 1;
125
126    /* We currently have no way to enable or disable IPv6 after initialisation.
127       No way to fix that without some surgery to the DHT code itself. */
128    if (ipv6 == NULL || (!force && ss->udp6_socket < 0)) {
129        if (ss->udp6_bound) {
130            free (ss->udp6_bound);
131            ss->udp6_bound = NULL;
132        }
133        return;
134    }
135
136    if (ss->udp6_bound != NULL && memcmp (ipv6, ss->udp6_bound, 16) == 0)
137        return;
138
139    s = socket (PF_INET6, SOCK_DGRAM, 0);
140    if (s < 0)
141        goto fail;
142
143#ifdef IPV6_V6ONLY
144        /* Since we always open an IPv4 socket on the same port, this
145           shouldn't matter.  But I'm superstitious. */
146        setsockopt (s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof (one));
147#endif
148
149    memset (&sin6, 0, sizeof (sin6));
150    sin6.sin6_family = AF_INET6;
151    if (ipv6)
152        memcpy (&sin6.sin6_addr, ipv6, 16);
153    sin6.sin6_port = htons (ss->udp_port);
154    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET6, &is_default);
155    if (public_addr && !is_default)
156        sin6.sin6_addr = public_addr->addr.addr6;
157
158    rc = bind (s, (struct sockaddr*)&sin6, sizeof (sin6));
159    if (rc < 0)
160        goto fail;
161
162    if (ss->udp6_socket < 0) {
163        ss->udp6_socket = s;
164    } else {
165        rc = dup2 (s, ss->udp6_socket);
166        if (rc < 0)
167            goto fail;
168        close (s);
169    }
170
171    if (ss->udp6_bound == NULL)
172        ss->udp6_bound = malloc (16);
173    if (ss->udp6_bound)
174        memcpy (ss->udp6_bound, ipv6, 16);
175
176    return;
177
178 fail:
179    /* Something went wrong.  It's difficult to recover, so let's simply
180       set things up so that we try again next time. */
181    tr_logAddNamedError ("UDP", "Couldn't rebind IPv6 socket");
182    if (s >= 0)
183        close (s);
184    if (ss->udp6_bound) {
185        free (ss->udp6_bound);
186        ss->udp6_bound = NULL;
187    }
188}
189
190static void
191event_callback (evutil_socket_t s, short type UNUSED, void *sv)
192{
193    int rc;
194    socklen_t fromlen;
195    unsigned char buf[4096];
196    struct sockaddr_storage from;
197    tr_session *ss = sv;
198
199    assert (tr_isSession (sv));
200    assert (type == EV_READ);
201
202    fromlen = sizeof (from);
203    rc = recvfrom (s, buf, 4096 - 1, 0,
204                (struct sockaddr*)&from, &fromlen);
205
206    /* Since most packets we receive here are µTP, make quick inline
207       checks for the other protocols.  The logic is as follows:
208       - all DHT packets start with 'd';
209       - all UDP tracker packets start with a 32-bit (!) "action", which
210         is between 0 and 3;
211       - the above cannot be µTP packets, since these start with a 4-bit
212         version number (1). */
213    if (rc > 0) {
214        if (buf[0] == 'd') {
215            if (tr_sessionAllowsDHT (ss)) {
216                buf[rc] = '\0'; /* required by the DHT code */
217                tr_dhtCallback (buf, rc, (struct sockaddr*)&from, fromlen, sv);
218            }
219        } else if (rc >= 8 &&
220                   buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] <= 3) {
221            rc = tau_handle_message (ss, buf, rc);
222            if (!rc)
223                tr_logAddNamedDbg ("UDP", "Couldn't parse UDP tracker packet.");
224        } else {
225            if (tr_sessionIsUTPEnabled (ss)) {
226                rc = tr_utpPacket (buf, rc, (struct sockaddr*)&from, fromlen, ss);
227                if (!rc)
228                    tr_logAddNamedDbg ("UDP", "Unexpected UDP packet");
229            }
230        }
231    }
232}
233
234void
235tr_udpInit (tr_session *ss)
236{
237    bool is_default;
238    const struct tr_address * public_addr;
239    struct sockaddr_in sin;
240    int rc;
241
242    assert (ss->udp_socket < 0);
243    assert (ss->udp6_socket < 0);
244
245    ss->udp_port = tr_sessionGetPeerPort (ss);
246    if (ss->udp_port <= 0)
247        return;
248
249    ss->udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
250    if (ss->udp_socket < 0) {
251        tr_logAddNamedError ("UDP", "Couldn't create IPv4 socket");
252        goto ipv6;
253    }
254
255    memset (&sin, 0, sizeof (sin));
256    sin.sin_family = AF_INET;
257    public_addr = tr_sessionGetPublicAddress (ss, TR_AF_INET, &is_default);
258    if (public_addr && !is_default)
259        memcpy (&sin.sin_addr, &public_addr->addr.addr4, sizeof (struct in_addr));
260    sin.sin_port = htons (ss->udp_port);
261    rc = bind (ss->udp_socket, (struct sockaddr*)&sin, sizeof (sin));
262    if (rc < 0) {
263        tr_logAddNamedError ("UDP", "Couldn't bind IPv4 socket");
264        close (ss->udp_socket);
265        ss->udp_socket = -1;
266        goto ipv6;
267    }
268    ss->udp_event =
269        event_new (ss->event_base, ss->udp_socket, EV_READ | EV_PERSIST,
270                  event_callback, ss);
271    if (ss->udp_event == NULL)
272        tr_logAddNamedError ("UDP", "Couldn't allocate IPv4 event");
273
274 ipv6:
275    if (tr_globalIPv6 ())
276        rebind_ipv6 (ss, true);
277    if (ss->udp6_socket >= 0) {
278        ss->udp6_event =
279            event_new (ss->event_base, ss->udp6_socket, EV_READ | EV_PERSIST,
280                      event_callback, ss);
281        if (ss->udp6_event == NULL)
282            tr_logAddNamedError ("UDP", "Couldn't allocate IPv6 event");
283    }
284
285    tr_udpSetSocketBuffers (ss);
286
287    if (ss->isDHTEnabled)
288        tr_dhtInit (ss);
289
290    if (ss->udp_event)
291        event_add (ss->udp_event, NULL);
292    if (ss->udp6_event)
293        event_add (ss->udp6_event, NULL);
294}
295
296void
297tr_udpUninit (tr_session *ss)
298{
299    tr_dhtUninit (ss);
300
301    if (ss->udp_socket >= 0) {
302        tr_netCloseSocket (ss->udp_socket);
303        ss->udp_socket = -1;
304    }
305
306    if (ss->udp_event) {
307        event_free (ss->udp_event);
308        ss->udp_event = NULL;
309    }
310
311    if (ss->udp6_socket >= 0) {
312        tr_netCloseSocket (ss->udp6_socket);
313        ss->udp6_socket = -1;
314    }
315
316    if (ss->udp6_event) {
317        event_free (ss->udp6_event);
318        ss->udp6_event = NULL;
319    }
320
321    if (ss->udp6_bound) {
322        free (ss->udp6_bound);
323        ss->udp6_bound = NULL;
324    }
325}
Note: See TracBrowser for help on using the repository browser.