1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2014 UnitedStack <haomai@unitedstack.com>
7 *
8 * Author: Haomai Wang <haomaiwang@gmail.com>
9 *
10 * This is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License version 2.1, as published by the Free Software
13 * Foundation. See file COPYING.
14 *
15 */
16
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <netinet/ip.h>
21 #include <netinet/tcp.h>
22 #include <arpa/inet.h>
23
24 #include "net_handler.h"
25 #include "common/debug.h"
26 #include "common/errno.h"
27 #include "include/compat.h"
28 #include "include/sock_compat.h"
29
30 #define dout_subsys ceph_subsys_ms
31 #undef dout_prefix
32 #define dout_prefix *_dout << "NetHandler "
33
34 namespace ceph{
35
36 int NetHandler::create_socket(int domain, bool reuse_addr)
37 {
38 int s;
39 int r = 0;
40
41 if ((s = socket_cloexec(domain, SOCK_STREAM, 0)) == -1) {
42 r = errno;
43 lderr(cct) << __func__ << " couldn't create socket " << cpp_strerror(r) << dendl;
44 return -r;
45 }
46
47 #if !defined(__FreeBSD__)
48 /* Make sure connection-intensive things like the benchmark
49 * will be able to close/open sockets a zillion of times */
50 if (reuse_addr) {
51 int on = 1;
52 if (::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
53 r = errno;
54 lderr(cct) << __func__ << " setsockopt SO_REUSEADDR failed: "
55 << strerror(r) << dendl;
56 close(s);
57 return -r;
58 }
59 }
60 #endif
61
62 return s;
63 }
64
65 int NetHandler::set_nonblock(int sd)
66 {
67 int flags;
68 int r = 0;
69
70 /* Set the socket nonblocking.
71 * Note that fcntl(2) for F_GETFL and F_SETFL can't be
72 * interrupted by a signal. */
73 if ((flags = fcntl(sd, F_GETFL)) < 0 ) {
74 r = errno;
75 lderr(cct) << __func__ << " fcntl(F_GETFL) failed: " << cpp_strerror(r) << dendl;
76 return -r;
77 }
78 if (fcntl(sd, F_SETFL, flags | O_NONBLOCK) < 0) {
79 r = errno;
80 lderr(cct) << __func__ << " fcntl(F_SETFL,O_NONBLOCK): " << cpp_strerror(r) << dendl;
81 return -r;
82 }
83
84 return 0;
85 }
86
87 int NetHandler::set_socket_options(int sd, bool nodelay, int size)
88 {
89 int r = 0;
90 // disable Nagle algorithm?
91 if (nodelay) {
92 int flag = 1;
93 r = ::setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
94 if (r < 0) {
95 r = errno;
96 ldout(cct, 0) << "couldn't set TCP_NODELAY: " << cpp_strerror(r) << dendl;
97 }
98 }
99 if (size) {
100 r = ::setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (void*)&size, sizeof(size));
101 if (r < 0) {
102 r = errno;
103 ldout(cct, 0) << "couldn't set SO_RCVBUF to " << size << ": " << cpp_strerror(r) << dendl;
104 }
105 }
106
107 // block ESIGPIPE
108 #ifdef CEPH_USE_SO_NOSIGPIPE
109 int val = 1;
110 r = ::setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val));
111 if (r) {
112 r = errno;
113 ldout(cct,0) << "couldn't set SO_NOSIGPIPE: " << cpp_strerror(r) << dendl;
114 }
115 #endif
116 return -r;
117 }
118
119 void NetHandler::set_priority(int sd, int prio, int domain)
120 {
121 #ifdef SO_PRIORITY
122 if (prio < 0) {
123 return;
124 }
125 #ifdef IPTOS_CLASS_CS6
126 int iptos = IPTOS_CLASS_CS6;
127 int r = -1;
128 switch (domain) {
129 case AF_INET:
130 r = ::setsockopt(sd, IPPROTO_IP, IP_TOS, &iptos, sizeof(iptos));
131 break;
132 case AF_INET6:
133 r = ::setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &iptos, sizeof(iptos));
134 break;
135 default:
136 lderr(cct) << "couldn't set ToS of unknown family (" << domain << ")"
137 << " to " << iptos << dendl;
138 return;
139 }
140 if (r < 0) {
141 r = errno;
142 ldout(cct,0) << "couldn't set TOS to " << iptos
143 << ": " << cpp_strerror(r) << dendl;
144 }
145
146 #endif // IPTOS_CLASS_CS6
147 // setsockopt(IPTOS_CLASS_CS6) sets the priority of the socket as 0.
148 // See http://goo.gl/QWhvsD and http://goo.gl/laTbjT
149 // We need to call setsockopt(SO_PRIORITY) after it.
150 r = ::setsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio));
151 if (r < 0) {
152 r = errno;
153 ldout(cct, 0) << __func__ << " couldn't set SO_PRIORITY to " << prio
154 << ": " << cpp_strerror(r) << dendl;
155 }
156 #else
157 return;
158 #endif // SO_PRIORITY
159 }
160
161 int NetHandler::generic_connect(const entity_addr_t& addr, const entity_addr_t &bind_addr, bool nonblock)
162 {
163 int ret;
164 int s = create_socket(addr.get_family());
165 if (s < 0)
166 return s;
167
168 if (nonblock) {
169 ret = set_nonblock(s);
170 if (ret < 0) {
171 close(s);
172 return ret;
173 }
174 }
175
176 set_socket_options(s, cct->_conf->ms_tcp_nodelay, cct->_conf->ms_tcp_rcvbuf);
177
178 {
(1) Event parameter_hidden: |
declaration hides parameter "addr" (declared at line 161) |
(2) Event caretline: |
^ |
179 entity_addr_t addr = bind_addr;
180 if (cct->_conf->ms_bind_before_connect && (!addr.is_blank_ip())) {
181 addr.set_port(0);
182 ret = ::bind(s, addr.get_sockaddr(), addr.get_sockaddr_len());
183 if (ret < 0) {
184 ret = errno;
185 ldout(cct, 2) << __func__ << " client bind error " << ", " << cpp_strerror(ret) << dendl;
186 close(s);
187 return -ret;
188 }
189 }
190 }
191
192 ret = ::connect(s, addr.get_sockaddr(), addr.get_sockaddr_len());
193 if (ret < 0) {
194 ret = errno;
195 if (errno == EINPROGRESS && nonblock)
196 return s;
197
198 ldout(cct, 10) << __func__ << " connect: " << cpp_strerror(ret) << dendl;
199 close(s);
200 return -ret;
201 }
202
203 return s;
204 }
205
206 int NetHandler::reconnect(const entity_addr_t &addr, int sd)
207 {
208 int r = 0;
209 int ret = ::connect(sd, addr.get_sockaddr(), addr.get_sockaddr_len());
210
211 if (ret < 0 && errno != EISCONN) {
212 r = errno;
213 ldout(cct, 10) << __func__ << " reconnect: " << strerror(r) << dendl;
214 if (r == EINPROGRESS || r == EALREADY)
215 return 1;
216 return -r;
217 }
218
219 return 0;
220 }
221
222 int NetHandler::connect(const entity_addr_t &addr, const entity_addr_t& bind_addr)
223 {
224 return generic_connect(addr, bind_addr, false);
225 }
226
227 int NetHandler::nonblock_connect(const entity_addr_t &addr, const entity_addr_t& bind_addr)
228 {
229 return generic_connect(addr, bind_addr, true);
230 }
231
232
233 }
234