girl 0.75.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of girl might be problematic. Click here for more details.

@@ -0,0 +1,300 @@
1
+ require 'girl/version'
2
+ require 'socket'
3
+
4
+ ##
5
+ # Girl::Udp - udp透明转发,近端。
6
+ #
7
+ # usage
8
+ # ======
9
+ #
10
+ # Girl::Udpd.new( 3030 ).looping # 远端
11
+ #
12
+ # Girl::Udp.new( 'your.server.ip', 3030, 1313 ).looping # 近端
13
+ #
14
+ # iptables -t nat -A PREROUTING -p udp -d game.server.ip -j REDIRECT --to-ports 1313
15
+ #
16
+ module Girl
17
+ class Udp
18
+
19
+ def initialize( udpd_host, udpd_port = 3030, redir_port = 1313 )
20
+ ctlr, ctlw = IO.pipe
21
+
22
+ redir = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
23
+ redir.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
24
+ redir.bind( Socket.sockaddr_in( redir_port, '0.0.0.0' ) )
25
+ puts "redir bound on #{ redir_port } #{ Time.new }"
26
+
27
+ @mutex = Mutex.new
28
+ @udpd_host = udpd_host
29
+ @udpd_addr = Socket.sockaddr_in( udpd_port, udpd_host )
30
+ @ctlw = ctlw
31
+ @redir = redir
32
+ @reads = [ ctlr, redir ]
33
+ @writes = []
34
+ @closings = []
35
+ @roles = {
36
+ ctlr => :ctlr, # :ctlr / :redir / :tun
37
+ redir => :redir
38
+ }
39
+ @redir_wbuffs = [] # [ src_addr data ] ...
40
+ @tuns = {} # [ orig_src_addr dst_addr ]=> tun
41
+ @mappings = {} # src_addr => [ orig_src_addr dst_addr ]
42
+ @tun_infos = {} # tun => {}
43
+ # orig_src_addr: sockaddr
44
+ # dst_addr: sockaddr
45
+ # src_addr: sockaddr
46
+ # tund_addr: sockaddr
47
+ # rbuffs: []
48
+ # wbuffs: []
49
+ # last_traff_at: now
50
+ end
51
+
52
+ def looping
53
+ loop_expire
54
+
55
+ loop do
56
+ rs, ws = IO.select( @reads, @writes )
57
+
58
+ @mutex.synchronize do
59
+ rs.each do | sock |
60
+ case @roles[ sock ]
61
+ when :ctlr
62
+ read_ctlr( sock )
63
+ when :redir
64
+ read_redir( sock )
65
+ when :tun
66
+ read_tun( sock )
67
+ end
68
+ end
69
+
70
+ ws.each do | sock |
71
+ case @roles[ sock ]
72
+ when :redir
73
+ write_redir( sock )
74
+ when :tun
75
+ write_tun( sock )
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def quit!
83
+ exit
84
+ end
85
+
86
+ private
87
+
88
+ def read_ctlr( ctlr )
89
+ od_addr = ctlr.read( 32 )
90
+ tun = @tuns[ od_addr ]
91
+
92
+ if tun
93
+ add_closing( tun )
94
+ end
95
+ end
96
+
97
+ def read_redir( redir )
98
+ data, addrinfo, rflags, *controls = redir.recvmsg
99
+ src_addr = addrinfo.to_sockaddr
100
+ is_hit_cache = false
101
+ now = Time.new
102
+ # puts "debug redir recv #{ data.inspect } from #{ addrinfo.inspect }"
103
+
104
+ if @mappings.include?( src_addr )
105
+ orig_src_addr, dst_addr, timeout, read_at = @mappings[ src_addr ]
106
+
107
+ if now - read_at < timeout
108
+ # puts "debug hit cache #{ addrinfo.inspect }"
109
+ is_hit_cache = true
110
+ else
111
+ # puts "debug cache timeout #{ addrinfo.inspect }"
112
+ @mappings.delete( src_addr )
113
+ end
114
+ end
115
+
116
+ unless is_hit_cache
117
+ # 2 udp 4 timeout 5 src 7 sport 9 [UNREPLIED] 11 dst 13 dport
118
+ # 2 udp 4 timeout 5 src 7 sport 10 dst 12 dport
119
+ bin = IO.binread( '/proc/net/nf_conntrack' )
120
+ rows = bin.split( "\n" ).map { | line | line.split( ' ' ) }
121
+ row = rows.find { | _row | _row[ 2 ] == 'udp' && ( ( _row[ 10 ].split( '=' )[ 1 ] == addrinfo.ip_address && _row[ 12 ].split( '=' )[ 1 ].to_i == addrinfo.ip_port ) || ( _row[ 9 ] == '[UNREPLIED]' && _row[ 11 ].split( '=' )[ 1 ] == addrinfo.ip_address && _row[ 13 ].split( '=' )[ 1 ].to_i == addrinfo.ip_port ) ) }
122
+
123
+ unless row
124
+ puts "miss conntrack #{ addrinfo.inspect } #{ Time.new }"
125
+ IO.binwrite( '/tmp/nf_conntrack', bin )
126
+ return
127
+ end
128
+
129
+ timeout = row[ 4 ].to_i
130
+ orig_src_ip = row[ 5 ].split( '=' )[ 1 ]
131
+ orig_src_port = row[ 7 ].split( '=' )[ 1 ].to_i
132
+ dst_ip = row[ 6 ].split( '=' )[ 1 ]
133
+ dst_port = row[ 8 ].split( '=' )[ 1 ].to_i
134
+ orig_src_addr = Socket.sockaddr_in( orig_src_port, orig_src_ip )
135
+ dst_addr = Socket.sockaddr_in( dst_port, dst_ip )
136
+
137
+ if Addrinfo.new( dst_addr ).ipv4_private?
138
+ puts "dst is private? #{ Addrinfo.new( dst_addr ).inspect } #{ Addrinfo.new( src_addr ).inspect } #{ Addrinfo.new( orig_src_addr ).inspect } #{ Time.new }"
139
+ add_redir_wbuff( redir, dst_addr, data )
140
+ return
141
+ end
142
+
143
+ # puts "debug save cache #{ addrinfo.inspect } #{ Addrinfo.new( orig_src_addr ).inspect } #{ Addrinfo.new( dst_addr ).inspect } #{ timeout } #{ now }"
144
+ @mappings[ src_addr ] = [ orig_src_addr, dst_addr, timeout, now ]
145
+ end
146
+
147
+ tun = @tuns[ [ orig_src_addr, dst_addr ].join ]
148
+
149
+ unless tun
150
+ tun = new_a_tun( orig_src_addr, dst_addr, src_addr )
151
+
152
+ # puts "debug tun send to udpd #{ Addrinfo.new( orig_src_addr ).inspect } #{ Addrinfo.new( dst_addr ).inspect }"
153
+ ctlmsg = [ orig_src_addr, dst_addr ].join
154
+ add_tun_wbuff( tun, @udpd_addr, ctlmsg )
155
+ end
156
+
157
+ tun_info = @tun_infos[ tun ]
158
+ add_tun_wbuff( tun, tun_info[ :tund_addr ], data )
159
+ end
160
+
161
+ def read_tun( tun )
162
+ data, addrinfo, rflags, *controls = tun.recvmsg
163
+ from_addr = addrinfo.to_sockaddr
164
+ tun_info = @tun_infos[ tun ]
165
+ tun_info[ :last_traff_at ] = Time.new
166
+
167
+ if from_addr == @udpd_addr
168
+ tund_port = data[ 0, 2 ].unpack( 'n' ).first
169
+ tund_addr = Socket.sockaddr_in( tund_port, @udpd_host )
170
+ tun_info[ :tund_addr ] = tund_addr
171
+
172
+ if tun_info[ :rbuffs ].any?
173
+ tun_info[ :wbuffs ] += tun_info[ :rbuffs ].map{ | rbuff | [ tund_addr, rbuff ] }
174
+ tun_info[ :rbuffs ].clear
175
+ add_write( tun )
176
+ end
177
+
178
+ elsif from_addr == tun_info[ :tund_addr ]
179
+ add_redir_wbuff( @redir, tun_info[ :src_addr ], data )
180
+ end
181
+ end
182
+
183
+ def write_redir( redir )
184
+ if @redir_wbuffs.empty?
185
+ @writes.delete( redir )
186
+ return
187
+ end
188
+
189
+ src_addr, data = @redir_wbuffs.shift
190
+ redir.sendmsg( data, 0, src_addr )
191
+ end
192
+
193
+ def write_tun( tun )
194
+ if @closings.include?( tun )
195
+ close_tun( tun )
196
+ return
197
+ end
198
+
199
+ tun_info = @tun_infos[ tun ]
200
+
201
+ if tun_info[ :wbuffs ].empty?
202
+ @writes.delete( tun )
203
+ return
204
+ end
205
+
206
+ to_addr, data = tun_info[ :wbuffs ].shift
207
+ tun.sendmsg( data, 0, to_addr )
208
+ end
209
+
210
+ def add_redir_wbuff( redir, to_addr, data )
211
+ @redir_wbuffs << [ to_addr, data ]
212
+ add_write( redir )
213
+ end
214
+
215
+ def add_tun_wbuff( tun, to_addr, data )
216
+ tun_info = @tun_infos[ tun ]
217
+
218
+ if to_addr
219
+ tun_info[ :wbuffs ] << [ to_addr, data ]
220
+ add_write( tun )
221
+ else
222
+ tun_info[ :rbuffs ] << data
223
+ end
224
+ end
225
+
226
+ def add_write( sock )
227
+ unless @writes.include?( sock )
228
+ @writes << sock
229
+ end
230
+ end
231
+
232
+ def add_closing( tun )
233
+ unless @closings.include?( tun )
234
+ @closings << tun
235
+ end
236
+
237
+ add_write( tun )
238
+ end
239
+
240
+ def close_tun( tun )
241
+ tun.close
242
+ @reads.delete( tun )
243
+ @writes.delete( tun )
244
+ @closings.delete( tun )
245
+ @roles.delete( tun )
246
+ tun_info = @tun_infos.delete( tun )
247
+ @tuns.delete( [ tun_info[ :orig_src_addr ], tun_info[ :dst_addr ] ].join )
248
+
249
+ if @mappings.include?( tun_info[ :src_addr ] )
250
+ orig_src_addr, dst_addr, timeout, read_at = @mappings[ tun_info[ :src_addr ] ]
251
+
252
+ if orig_src_addr == tun_info[ :orig_src_addr ] && dst_addr == tun_info[ :dst_addr ]
253
+ @mappings.delete( tun_info[ :src_addr ] )
254
+ end
255
+ end
256
+ end
257
+
258
+ def new_a_tun( orig_src_addr, dst_addr, src_addr )
259
+ tun = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
260
+ tun.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
261
+ tun.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
262
+
263
+ @tun_infos[ tun ] = {
264
+ orig_src_addr: orig_src_addr,
265
+ dst_addr: dst_addr,
266
+ src_addr: src_addr,
267
+ tund_addr: nil,
268
+ rbuffs: [],
269
+ wbuffs: [],
270
+ last_traff_at: Time.new
271
+ }
272
+
273
+ @tuns[ [ orig_src_addr, dst_addr ].join ] = tun
274
+ @roles[ tun ] = :tun
275
+ @reads << tun
276
+
277
+ tun
278
+ end
279
+
280
+ def loop_expire
281
+ Thread.new do
282
+ loop do
283
+ sleep 30
284
+
285
+ @mutex.synchronize do
286
+ now = Time.new
287
+
288
+ @tun_infos.values.each do | tun_info |
289
+ # net.netfilter.nf_conntrack_udp_timeout_stream
290
+ if now - tun_info[ :last_traff_at ] > 180
291
+ @ctlw.write( [ tun_info[ :orig_src_addr ], tun_info[ :dst_addr ] ].join )
292
+ end
293
+ end
294
+ end
295
+ end
296
+ end
297
+ end
298
+
299
+ end
300
+ end
@@ -0,0 +1,286 @@
1
+ require 'girl/version'
2
+ require 'socket'
3
+
4
+ ##
5
+ # Girl::Udpd - udp透明转发,远端。
6
+ #
7
+ module Girl
8
+ class Udpd
9
+
10
+ def initialize( port = 3030 )
11
+ ctlr, ctlw = IO.pipe
12
+
13
+ udpd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
14
+ udpd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
15
+ udpd.bind( Socket.sockaddr_in( port, '0.0.0.0' ) )
16
+ puts "udpd bound on #{ port } #{ Time.new }"
17
+
18
+ @mutex = Mutex.new
19
+ @ctlw = ctlw
20
+ @udpd = udpd
21
+ @reads = [ ctlr, udpd ]
22
+ @writes = []
23
+ @closings = []
24
+ @roles = {
25
+ ctlr => :ctlr, # :ctlr / :udpd / :tund
26
+ udpd => :udpd
27
+ }
28
+ @udpd_wbuffs = [] # [ tun_addr ctlmsg ] ...
29
+ @tunds = {} # [ tun_ip_addr orig_src_addr ] => tund
30
+ @tund_infos = {} # tund => {}
31
+ # port: port
32
+ # tun_ip_addr: sockaddr
33
+ # orig_src_addr: sockaddr
34
+ # wbuffs: [] # [ to_addr, data ] ...
35
+ # dst_addrs: { tun_addr => dst_addr }
36
+ # tun_addrs: { dst_addr => tun_addr }
37
+ # is_tunneleds: { [ tun_addr dst_addr ] => false }
38
+ # unpaired_dst_rbuffs: { dst_addr => [] }
39
+ # last_traff_at: now
40
+ end
41
+
42
+ def looping
43
+ loop_expire
44
+
45
+ loop do
46
+ rs, ws = IO.select( @reads, @writes )
47
+
48
+ @mutex.synchronize do
49
+ rs.each do | sock |
50
+ case @roles[ sock ]
51
+ when :ctlr
52
+ read_ctlr( sock )
53
+ when :udpd
54
+ read_udpd( sock )
55
+ when :tund
56
+ read_tund( sock )
57
+ end
58
+ end
59
+
60
+ ws.each do | sock |
61
+ case @roles[ sock ]
62
+ when :udpd
63
+ write_udpd( sock )
64
+ when :tund
65
+ write_tund( sock )
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def quit!
73
+ exit
74
+ end
75
+
76
+ private
77
+
78
+ def read_ctlr( ctlr )
79
+ to_addr = ctlr.read( 32 )
80
+ tund = @tunds[ to_addr ]
81
+
82
+ if tund
83
+ add_closing( tund )
84
+ end
85
+ end
86
+
87
+ def read_udpd( udpd )
88
+ data, addrinfo, rflags, *controls = udpd.recvmsg
89
+ # puts "debug udpd recv #{ data.inspect } from #{ addrinfo.inspect }"
90
+ orig_src_addr = data[ 0, 16 ]
91
+ dst_addr = data[ 16, 16 ]
92
+ tun_addr = addrinfo.to_sockaddr
93
+ tun_ip_addr = Addrinfo.ip( addrinfo.ip_address ).to_sockaddr
94
+
95
+ return unless Addrinfo.new( orig_src_addr ).ipv4?
96
+
97
+ dst_addrinfo = Addrinfo.new( dst_addr )
98
+ return unless dst_addrinfo.ipv4?
99
+ return if dst_addrinfo.ipv4_private?
100
+
101
+ tund = pair_tund( tun_addr, tun_ip_addr, orig_src_addr, dst_addr )
102
+ tund_info = @tund_infos[ tund ]
103
+ tund_port = tund_info[ :port ]
104
+
105
+ # puts "debug udpd send to tun #{ tund_port } #{ addrinfo.inspect }"
106
+ ctlmsg = [ tund_port ].pack( 'n' )
107
+ @udpd_wbuffs << [ tun_addr, ctlmsg ]
108
+ add_write( udpd )
109
+ end
110
+
111
+ def read_tund( tund )
112
+ data, addrinfo, rflags, *controls = tund.recvmsg
113
+ from_addr = addrinfo.to_sockaddr
114
+ tund_info = @tund_infos[ tund ]
115
+ tund_info[ :last_traff_at ] = Time.new
116
+ to_addr = tund_info[ :dst_addrs ][ from_addr ]
117
+
118
+ if to_addr
119
+ # 来自tun,发给dst。
120
+ td_addr = [ from_addr, to_addr ].join
121
+ is_tunneled = tund_info[ :is_tunneleds ][ td_addr ]
122
+
123
+ unless is_tunneled
124
+ # puts "debug first traffic from tun #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
125
+ # 发暂存
126
+ if tund_info[ :unpaired_dst_rbuffs ].include?( to_addr )
127
+ rbuffs = tund_info[ :unpaired_dst_rbuffs ].delete( to_addr )
128
+ # puts "debug move tund.dst.rbuffs to tund.wbuffs #{ rbuffs.inspect }"
129
+ tund_info[ :wbuffs ] += rbuffs.map{ | rbuff | [ from_addr, rbuff ] }
130
+ end
131
+
132
+ tund_info[ :is_tunneleds ][ td_addr ] = true
133
+ end
134
+
135
+ # 如果对面没来过流量,且在nat里,nat规则是只对去过的目的地做接收,那么,先过去的流量会撞死。
136
+ # 没关系,撞死的流量通常是打洞数据,在应用计算之内,打洞数据通常是连发的。
137
+ # puts "debug #{ data.inspect } from #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
138
+ add_tund_wbuff( tund, to_addr, data )
139
+ return
140
+ end
141
+
142
+ to_addr = tund_info[ :tun_addrs ][ from_addr ]
143
+
144
+ if to_addr
145
+ # 来自dst,发给tun。
146
+ # puts "debug #{ data.inspect } from #{ addrinfo.inspect } to #{ Addrinfo.new( to_addr ).inspect }"
147
+
148
+ td_addr = [ to_addr, from_addr ].join
149
+ is_tunneled = tund_info[ :is_tunneleds ][ td_addr ]
150
+
151
+ if is_tunneled
152
+ add_tund_wbuff( tund, to_addr, data )
153
+ return
154
+ end
155
+
156
+ # puts "debug #{ Addrinfo.new( to_addr ).inspect } #{ addrinfo.inspect } not tunneled"
157
+ end
158
+
159
+ # 来自未知的地方,或者对应的tun还没来流量,记暂存
160
+ unless tund_info[ :unpaired_dst_rbuffs ][ from_addr ]
161
+ tund_info[ :unpaired_dst_rbuffs ][ from_addr ] = []
162
+ end
163
+
164
+ # 暂存5条(连发打洞数据,不需要存多)。
165
+ if tund_info[ :unpaired_dst_rbuffs ][ from_addr ].size < 5
166
+ # puts "debug save other dst rbuff #{ addrinfo.inspect } #{ data.inspect }"
167
+ tund_info[ :unpaired_dst_rbuffs ][ from_addr ] << data
168
+ end
169
+ end
170
+
171
+ def write_udpd( udpd )
172
+ if @udpd_wbuffs.empty?
173
+ @writes.delete( udpd )
174
+ return
175
+ end
176
+
177
+ tun_addr, ctlmsg = @udpd_wbuffs.shift
178
+ udpd.sendmsg( ctlmsg, 0, tun_addr )
179
+ end
180
+
181
+ def write_tund( tund )
182
+ if @closings.include?( tund )
183
+ close_tund( tund )
184
+ return
185
+ end
186
+
187
+ tund_info = @tund_infos[ tund ]
188
+
189
+ if tund_info[ :wbuffs ].empty?
190
+ @writes.delete( tund )
191
+ return
192
+ end
193
+
194
+ to_addr, data = tund_info[ :wbuffs ].shift
195
+ tund.sendmsg( data, 0, to_addr )
196
+ end
197
+
198
+ def add_tund_wbuff( tund, to_addr, data )
199
+ tund_info = @tund_infos[ tund ]
200
+ tund_info[ :wbuffs ] << [ to_addr, data ]
201
+
202
+ add_write( tund )
203
+ end
204
+
205
+ def add_write( sock )
206
+ unless @writes.include?( sock )
207
+ @writes << sock
208
+ end
209
+ end
210
+
211
+ def add_closing( tund )
212
+ unless @closings.include?( tund )
213
+ @closings << tund
214
+ end
215
+
216
+ add_write( tund )
217
+ end
218
+
219
+ def close_tund( tund )
220
+ tund.close
221
+ @reads.delete( tund )
222
+ @writes.delete( tund )
223
+ @closings.delete( tund )
224
+ @roles.delete( tund )
225
+ tund_info = @tund_infos.delete( tund )
226
+ @tunds.delete( [ tund_info[ :tun_ip_addr ], tund_info[ :orig_src_addr ] ].join )
227
+ end
228
+
229
+ def pair_tund( tun_addr, tun_ip_addr, orig_src_addr, dst_addr )
230
+ from_addr = [ tun_ip_addr, orig_src_addr ].join
231
+ td_addr = [ tun_addr, dst_addr ].join
232
+ tund = @tunds[ from_addr ]
233
+
234
+ if tund
235
+ tund_info = @tund_infos[ tund ]
236
+ tund_info[ :dst_addrs ][ tun_addr ] = dst_addr
237
+ tund_info[ :tun_addrs ][ dst_addr ] = tun_addr
238
+ tund_info[ :is_tunneleds ][ td_addr ] = false
239
+ else
240
+ tund = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
241
+ tund.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEPORT, 1 )
242
+ tund.setsockopt( Socket::SOL_SOCKET, Socket::SO_BROADCAST, 1 )
243
+ tund.bind( Socket.sockaddr_in( 0, '0.0.0.0' ) )
244
+ tund_port = tund.local_address.ip_unpack.last
245
+
246
+ @tund_infos[ tund ] = {
247
+ port: tund_port,
248
+ tun_ip_addr: tun_ip_addr,
249
+ orig_src_addr: orig_src_addr,
250
+ wbuffs: [],
251
+ dst_addrs: { tun_addr => dst_addr },
252
+ tun_addrs: { dst_addr => tun_addr },
253
+ is_tunneleds: { td_addr => false },
254
+ unpaired_dst_rbuffs: {},
255
+ last_traff_at: Time.new
256
+ }
257
+
258
+ @roles[ tund ] = :tund
259
+ @reads << tund
260
+ @tunds[ from_addr ] = tund
261
+ end
262
+
263
+ tund
264
+ end
265
+
266
+ def loop_expire
267
+ Thread.new do
268
+ loop do
269
+ sleep 30
270
+
271
+ @mutex.synchronize do
272
+ now = Time.new
273
+
274
+ @tund_infos.values.each do | tund_info |
275
+ # net.netfilter.nf_conntrack_udp_timeout_stream
276
+ if now - tund_info[ :last_traff_at ] > 180
277
+ @ctlw.write( [ tund_info[ :tun_ip_addr ], tund_info[ :orig_src_addr ] ].join )
278
+ end
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ end
286
+ end