├── Makefile ├── README.md ├── TODO.org ├── doc ├── utp.dot └── utp_undescribed.md ├── include ├── log.hrl └── utp.hrl ├── rebar.config ├── src ├── Makefile ├── gen_utp.erl ├── gen_utp_decoder.erl ├── gen_utp_trace.erl ├── gen_utp_worker.erl ├── gen_utp_worker_pool.erl ├── utp.app.src ├── utp.erl ├── utp_app.erl ├── utp_buffer.erl ├── utp_file_map.erl ├── utp_filter.erl ├── utp_ledbat.erl ├── utp_network.erl ├── utp_process.erl ├── utp_proto.erl ├── utp_rtt.erl ├── utp_socket.erl ├── utp_sup.erl ├── utp_test.erl ├── utp_test_client.erl ├── utp_test_server_acceptor.erl ├── utp_test_server_pool_sup.erl ├── utp_test_server_sup.erl ├── utp_trace.erl └── utp_util.erl └── tools ├── impose_network.sh ├── linux_clear.sh ├── linux_network_1.sh ├── linux_network_2.sh ├── really_faulty.sh └── slightly_faulty.sh /Makefile: -------------------------------------------------------------------------------- 1 | all: compile 2 | 3 | compile: 4 | rebar compile 5 | 6 | get-deps: 7 | rebar get-deps 8 | 9 | clean: 10 | rebar clean 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | erlang-utp 2 | ========== 3 | 4 | uTP implementation in Erlang 5 | 6 | ## State 7 | 8 | This implementation is around 80% done. Specifically it lacks support for SACKs and support for Nagles algorithm. In addition the code has seen very little testing on a large scale. The tests it survives are mostly "in laboratory" tests. Before you use this for real, it might be advisable to test the code and report any bugs you find. 9 | 10 | ## TODO 11 | 12 | See the file TODO.org 13 | 14 | It contains a haphazard list of items that needs to be done, as well as everything done on the project. 15 | 16 | Also see the file utp.todo.md which is an older todo-list of things that needs to be done. I am afraid that both are rather hard to read for the uninitiated. Sorry. 17 | 18 | -------------------------------------------------------------------------------- /doc/utp.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | /* Initialization */ 3 | idle -> syn_sent [label="Connect()"]; 4 | syn_sent -> connected [label="r(syn_ack)"]; 5 | idle -> connected [label="Accept()"]; 6 | /* Close */ 7 | idle -> destroy [label="Close()"]; 8 | syn_sent -> syn_sent [label="Close()"]; 9 | connected -> fin_sent [label="Close()"]; 10 | connected_full -> fin_sent [label="Close()"]; 11 | got_fin -> destroy_delay [label="Close()"]; 12 | reset -> destroy [label="Close()"]; 13 | /* Connection SendBuf overflow */ 14 | connected -> connected_full; 15 | connected_full -> connected; 16 | /* Destroy */ 17 | destroy_delay -> destroy [label="timeout"]; 18 | got_fin -> reset [label="timeout"]; 19 | /* Finailization */ 20 | connected -> got_fin [label="r(st_fin)"]; 21 | connected_full -> got_fin [label="r(st_fin)"]; 22 | syn_sent -> got_fin [label="??"]; 23 | fin_sent -> got_fin [label="r(got_eof_pkt)"]; 24 | idle -> got_fin [label="??"]; 25 | reset -> got_fin [label="??"]; 26 | fin_sent -> destroy [label="timeout"]; 27 | /* Resetting */ 28 | syn_sent -> reset [label="timeout"]; 29 | connected -> reset [label="timeout"]; 30 | connected_full -> reset [label="timeout"]; 31 | 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /doc/utp_undescribed.md: -------------------------------------------------------------------------------- 1 | # Stuff in uTP which is not described fully 2 | 3 | This is a loose list of stuff which is not described fully in the BEP 4 | 29 spec of the uTP protocol. It serves as help to implementors in 5 | order to actually implement the protocol. 6 | 7 | The code is here: https://github.com/bittorrent/libutp 8 | 9 | * An open UDP socket acts as a TCP listen() point in the protocol. 10 | * There are two protocol versions. 0 which is deprecated and 1 which 11 | is the current one. You will have to ignore all protcol data with a 12 | 0 in the version field. 13 | * SYN packets (st_syn) contain the `ext_bits` extension field, where 14 | all 8 bytes (64 bits) are set to 0. 15 | * There is no mention of anything but the most important fields. It is 16 | assumed that all timestamp/rcv_window fields and so on are filled in 17 | by automation underneath the basic protocol. 18 | * There are keepalives in the protocol. You keep-alive a connection by 19 | ack'ing (ack_no - 1). 20 | * There are RST's in the protocol. You send an RST 21 | * The internal state CS_CONNECTED_FULL means that we are connected but 22 | the outgoing buffer is full, so we can't write anymore data out. 23 | * Naturally, you need to look up the MTU and set the packet size 24 | accordingly, in order to handle the question "What is the packet 25 | size allowed?" 26 | * `UTP_Create()` contains constants which are not documented. 27 | * If you get unknown NON-SYN packets with no conn, you should throw 28 | back an RST 29 | * There is a concept of fast_resends, not documented 30 | * There is a concept of closing the socket via RESET messages 31 | * There are of course socket options, and these must be maintained. 32 | 33 | # Good things to handle once and for all: 34 | 35 | * `send_ack()` for sending ACK's 36 | * `send_rst()` for sending RST's 37 | -------------------------------------------------------------------------------- /include/log.hrl: -------------------------------------------------------------------------------- 1 | -define(INFO(T), error_logger:info_report(T)). 2 | %% -define(INFO(T), ignore). 3 | -define(WARN(T), error_logger:warning_report( 4 | [process_info(self(), current_function), {line, ?LINE} | T])). 5 | %% -define(WARN(T), ignore). 6 | 7 | -define(ERR(T), error_logger:error_report( 8 | [process_info(self(), current_function), {line, ?LINE} | T])). 9 | %% -define(ERR(T), ignore). 10 | -------------------------------------------------------------------------------- /include/utp.hrl: -------------------------------------------------------------------------------- 1 | -type packet_type() :: st_data | st_fin | st_state | st_reset | st_syn. 2 | 3 | -type extension() :: {sack, binary()} | {ext_bits, binary()}. 4 | -type timestamp() :: integer(). 5 | 6 | -record(packet, { ty :: packet_type(), 7 | conn_id :: integer(), 8 | win_sz = 0 :: integer(), 9 | seq_no :: integer(), 10 | ack_no :: integer(), 11 | extension = [] :: [extension()], 12 | payload = <<>> :: binary() 13 | }). 14 | 15 | -type packet() :: #packet{}. 16 | 17 | -define(SEQ_NO_MASK, 16#FFFF). 18 | -define(ACK_NO_MASK, 16#FFFF). 19 | -define(REORDER_BUFFER_SIZE, 32). 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [ 2 | %% bin_opt_info, 3 | warn_format, 4 | warn_export_all, 5 | warn_export_vars, 6 | warn_obsolete_guard, 7 | %% warn_unused_import, 8 | warn_bif_clash, 9 | %% warn_missing_spec, 10 | warn_untyped_record, 11 | debug_info]}. 12 | 13 | {cover_enabled, true}. 14 | 15 | {dialyzer_opts, [{warnings, [error_handling, 16 | race_conditions, 17 | behaviours]}]}. 18 | 19 | {xref_checks, [undefined_function_calls, 20 | locals_not_used, 21 | exports_not_used]}. 22 | 23 | {deps, [ 24 | {gproc, ".*", 25 | {git, "git://github.com/esl/gproc.git", {branch, "master"}}} 26 | ]}. 27 | 28 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cd .. && $(MAKE) 3 | 4 | tags: 5 | etags *.erl ../include/*.hrl 6 | ctags *.erl ../include/*.hrl 7 | 8 | .PHONY: all tags 9 | -------------------------------------------------------------------------------- /src/gen_utp.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc Generic server interface for uTP 4 | %%% @end 5 | -module(gen_utp). 6 | 7 | -include("utp.hrl"). 8 | -include("log.hrl"). 9 | 10 | -behaviour(gen_server). 11 | 12 | %% API (Supervisor) 13 | -export([start_link/1, start_link/2]). 14 | 15 | %% API (Use) 16 | -export([connect/2, connect/3, 17 | close/1, 18 | send/2, send_msg/2, 19 | recv/2, recv_msg/1, 20 | listen/0, listen/1, 21 | accept/0]). 22 | 23 | %% Internally used API 24 | -export([register_process/2, 25 | reply/2, 26 | lookup_registrar/3, 27 | incoming_unknown/3]). 28 | 29 | -type port_number() :: 0..16#FFFF. 30 | -opaque({socket,{type,{30,21},tuple,[{atom,{30,22},utp_sock},{type,{30,32},pid,[]}]},[]}). 31 | -export_type([socket/0]). 32 | 33 | -type listen_opts() :: {backlog, integer()}. 34 | 35 | %% gen_server callbacks 36 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 37 | terminate/2, code_change/3]). 38 | 39 | -define(SERVER, ?MODULE). 40 | -define(TAB, ?MODULE). 41 | 42 | -record(accept_queue, 43 | { acceptors :: queue(), 44 | incoming_conns :: queue(), 45 | q_len :: integer(), 46 | max_q_len :: integer() }). 47 | 48 | -record(state, { monitored :: gb_tree(), 49 | socket :: inet:socket(), 50 | listen_queue :: closed | #accept_queue{}, 51 | listen_options = [] :: [listen_opts()]}). 52 | 53 | 54 | 55 | %%%=================================================================== 56 | 57 | %% @doc Starts the server 58 | %% Options is a proplist of options, given in the spec. 59 | %% @end 60 | %% @todo Strengthen spec 61 | -spec start_link(integer(), [{atom(), term()}]) -> any(). 62 | start_link(Port, Opts) -> 63 | gen_server:start_link({local, ?SERVER}, ?MODULE, [Port, Opts], []). 64 | 65 | %% @equiv start_link(Port, []) 66 | -spec start_link(integer()) -> any(). 67 | start_link(Port) -> 68 | start_link(Port, []). 69 | 70 | %% @equiv connect(Addr, Port, []) 71 | -spec connect(inet:ip_address() | inet:hostname(), port_number()) -> 72 | {ok, socket()} | {error, term()}. 73 | connect(Addr, Port) -> 74 | connect(Addr, Port, []). 75 | 76 | %% @doc Connect to a foreign uTP peer 77 | %% @end 78 | -spec connect(inet:ip_address() | inet:hostname(), port_number(), [term()]) -> 79 | {ok, socket()} | {error, term()}. 80 | connect(Addr, Port, Options) -> 81 | {ok, Socket} = get_socket(), 82 | {ok, Pid} = gen_utp_worker_pool:start_child(Socket, Addr, Port, Options), 83 | case gen_utp_worker:connect(Pid) of 84 | ok -> 85 | {ok, {utp_sock, Pid}}; 86 | {error, Reason} -> 87 | {error, Reason} 88 | end. 89 | 90 | %% @doc Accept an incoming connection. 91 | %% We let the gen_utp proxy make the accept. When we have a SYN packet an accept 92 | %% cannot fail from there on out, so we can just simply let the proxy to the work for now. 93 | %% There may be some things that changes when you add timeouts to connections. 94 | %% @end 95 | -spec accept() -> {ok, socket()}. 96 | accept() -> 97 | {ok, _Socket} = call(accept). 98 | 99 | %% @doc Send a message on a uTP Socket 100 | %% @end 101 | -spec send(socket(), iolist() | binary() | string()) -> ok | {error, term()}. 102 | send({utp_sock, Pid}, Msg) -> 103 | gen_utp_worker:send(Pid, iolist_to_binary(Msg)). 104 | 105 | -spec send_msg(socket(), term()) -> ok | {error, term()}. 106 | send_msg(Socket, Msg) -> 107 | EMsg = term_to_binary(Msg, [compressed]), 108 | Digest = crypto:sha(EMsg), 109 | Sz = byte_size(EMsg) + byte_size(Digest), 110 | send(Socket, <>). 111 | 112 | %% @doc Receive a message 113 | %% The `Length' parameter specifies the size of the data to wait for. Providing a value of 114 | %% `0' means to fetch all available data. There is currently no provision for timeouts on sockets, 115 | %% but that will be provided later on. 116 | %% @end 117 | -spec recv(socket(), integer()) -> 118 | {ok, binary()} | {error, term()}. 119 | recv({utp_sock, Pid}, Length) when Length >= 0 -> 120 | gen_utp_worker:recv(Pid, Length). 121 | 122 | recv_msg(Socket) -> 123 | case recv(Socket, 4) of 124 | {ok, <>} -> 125 | case recv(Socket, Sz) of 126 | {ok, <>} -> 127 | Digest = crypto:sha(EMsg), 128 | Term = binary_to_term(EMsg, [safe]), 129 | {ok, Term}; 130 | {error, Reason} -> 131 | {error, Reason} 132 | end; 133 | {error, Reason} -> 134 | {error, Reason} 135 | end. 136 | 137 | %% @doc Close down a socket (nonblocking) 138 | %% @end 139 | close({utp_sock, Pid}) -> 140 | gen_utp_worker:close(Pid). 141 | 142 | %% @doc Listen on socket, with queue length Q 143 | %% @end 144 | -spec listen([listen_opts()]) -> ok | {error, term()}. 145 | listen(Options) -> 146 | case validate_listen_opts(Options) of 147 | ok -> 148 | call({listen, Options}); 149 | badarg -> 150 | {error, badarg} 151 | end. 152 | 153 | %% @equiv listen(5) 154 | -spec listen() -> ok | {error, term()}. 155 | listen() -> 156 | listen([{backlog, 5}]). 157 | 158 | 159 | %% @doc New unknown incoming packet 160 | incoming_unknown(#packet { ty = st_syn } = Packet, Addr, Port) -> 161 | %% SYN packet, so pass it in 162 | gen_server:cast(?MODULE, {incoming_syn, Packet, Addr, Port}); 163 | incoming_unknown(#packet{ ty = st_reset } = _Packet, _Addr, _Port) -> 164 | %% Stray RST packet received, ignore since there is no connection for it 165 | ok; 166 | incoming_unknown(#packet{} = Packet, Addr, Port) -> 167 | %% Stray, RST it 168 | utp:report_event(95, us, unknown_packet, [Packet]), 169 | gen_server:cast(?MODULE, {generate_reset, Packet, Addr, Port}). 170 | 171 | %% @doc Register a process as the recipient of a given incoming message 172 | %% @end 173 | register_process(Pid, Conn) -> 174 | call({reg_proc, Pid, Conn}). 175 | 176 | %% @doc Look up the registrar underneath a given connection ID 177 | %% @end 178 | lookup_registrar(CID, Addr, Port) -> 179 | case ets:lookup(?TAB, {CID, Addr, Port}) of 180 | [] -> 181 | not_found; 182 | [{_, Pid}] -> 183 | {ok, Pid} 184 | end. 185 | 186 | %% @doc Reply back to a socket user 187 | %% @end 188 | reply(To, Msg) -> 189 | gen_fsm:reply(To, Msg). 190 | 191 | %%%=================================================================== 192 | %%% gen_server callbacks 193 | %%%=================================================================== 194 | 195 | %%-------------------------------------------------------------------- 196 | %% @private 197 | %% @doc 198 | %% Initializes the server 199 | %% 200 | %% @spec init(Args) -> {ok, State} | 201 | %% {ok, State, Timeout} | 202 | %% ignore | 203 | %% {stop, Reason} 204 | %% @end 205 | %%-------------------------------------------------------------------- 206 | init([Port, _Opts]) -> 207 | {ok, Socket} = gen_udp:open(Port, [binary, {active, once}]), 208 | ets:new(?TAB, [named_table, protected, set]), 209 | {ok, #state{ monitored = gb_trees:empty(), 210 | listen_queue = closed, 211 | socket = Socket }}. 212 | 213 | %% @private 214 | handle_call(accept, _From, #state { listen_queue = closed } = S) -> 215 | {reply, {error, no_listen}, S}; 216 | handle_call(accept, From, #state { listen_queue = Q, 217 | listen_options = ListenOpts, 218 | monitored = Monitored, 219 | socket = Socket } = S) -> 220 | false = Q =:= closed, 221 | {ok, Pairings, NewQ} = push_acceptor(From, Q), 222 | N_MonitorTree = pair_acceptors(ListenOpts, Socket, Pairings, Monitored), 223 | 224 | {noreply, S#state { listen_queue = NewQ, 225 | monitored = N_MonitorTree }}; 226 | handle_call({listen, Options}, _From, #state { listen_queue = closed } = S) -> 227 | QLen = proplists:get_value(backlog, Options), 228 | {reply, ok, S#state { listen_queue = new_accept_queue(QLen), 229 | listen_options = Options }}; 230 | handle_call({listen, _QLen}, _From, #state { listen_queue = #accept_queue{} } = S) -> 231 | {reply, {error, ealreadylistening}, S}; 232 | handle_call({reg_proc, Proc, CID}, _From, #state { monitored = Monitored } = State) -> 233 | Reply = reg_proc(Proc, CID), 234 | case Reply of 235 | ok -> 236 | Ref = erlang:monitor(process, Proc), 237 | {reply, ok, State#state { monitored = gb_trees:enter(Ref, CID, Monitored) }}; 238 | {error, Reason} -> 239 | {reply, {error, Reason}} 240 | end; 241 | handle_call(get_socket, _From, S) -> 242 | {reply, {ok, S#state.socket}, S}; 243 | handle_call(_Request, _From, State) -> 244 | Reply = ok, 245 | {reply, Reply, State}. 246 | 247 | pair_acceptors(ListenOpts, Socket, Pairings, Monitored) -> 248 | PidsCIDs = [accept_incoming_conn(Socket, Acc, SYN, ListenOpts) || {Acc, SYN} <- Pairings], 249 | lists:foldl(fun({Pid, CID}, MTree) -> 250 | Ref = erlang:monitor(process, Pid), 251 | gb_trees:enter(Ref, CID, MTree) 252 | end, 253 | Monitored, 254 | PidsCIDs). 255 | 256 | %% @private 257 | handle_cast({incoming_syn, _P, _Addr, _Port}, #state { listen_queue = closed } = S) -> 258 | %% Not listening on queue 259 | %% @todo RESET sent back here? 260 | ?WARN([incoming_syn_but_listen_closed]), 261 | {noreply, S}; 262 | handle_cast({incoming_syn, Packet, Addr, Port}, #state { listen_queue = Q, 263 | listen_options = ListenOpts, 264 | monitored = Monitored, 265 | socket = Socket } = S) -> 266 | Elem = {Packet, Addr, Port}, 267 | case push_syn(Elem, Q) of 268 | synq_full -> 269 | {noreply, S}; % @todo RESET sent back? 270 | duplicate -> 271 | {noreply, S}; 272 | {ok, Pairings, NewQ} -> 273 | N_Monitored = pair_acceptors(ListenOpts, Socket, Pairings, Monitored), 274 | {noreply, S#state { listen_queue = NewQ, 275 | monitored = N_Monitored }} 276 | end; 277 | handle_cast({generate_reset, #packet { conn_id = ConnID, 278 | seq_no = SeqNo }, Addr, Port}, 279 | #state { socket = Socket } = State) -> 280 | {ok, _} = utp_socket:send_reset(Socket, Addr, Port, ConnID, SeqNo, 281 | utp_buffer:mk_random_seq_no()), 282 | {noreply, State}; 283 | handle_cast(_Msg, State) -> 284 | {noreply, State}. 285 | 286 | %% @private 287 | handle_info({udp, _Socket, IP, Port, Datagram}, 288 | #state { socket = Socket } = S) -> 289 | %% @todo FLOW CONTROL here, because otherwise we may swamp the decoder. 290 | gen_utp_decoder:decode_and_dispatch(Datagram, IP, Port), 291 | %% Quirk out the next packet :) 292 | inet:setopts(Socket, [{active, once}]), 293 | {noreply, S}; 294 | handle_info({'DOWN', Ref, process, _Pid, _Reason}, #state { monitored = MM } = S) -> 295 | {CID, Addr, Port} = CAP = gb_trees:get(Ref, MM), 296 | error_logger:info_report([{down, CAP}]), 297 | utp:report_event(95, us, 'DOWN', [{monitored, MM}, {ref, Ref}, {cid, CID}]), 298 | ok = unreg(CID, Addr, Port), 299 | {noreply, S#state { monitored = gb_trees:delete(Ref, MM)}}; 300 | handle_info(_Info, State) -> 301 | ?ERR([unknown_handle_info, _Info, State]), 302 | {noreply, State}. 303 | 304 | %% @private 305 | terminate(_Reason, _State) -> 306 | ok. 307 | 308 | %% @private 309 | code_change(_OldVsn, State, _Extra) -> 310 | {ok, State}. 311 | 312 | %%%=================================================================== 313 | %%% Internal functions 314 | %%%=================================================================== 315 | 316 | push_acceptor(From, #accept_queue { acceptors = AQ } = Q) -> 317 | handle_queue(Q#accept_queue { acceptors = queue:in(From, AQ) }, []). 318 | 319 | push_syn(_SYNPacket, #accept_queue { 320 | q_len = QLen, 321 | max_q_len = MaxQ}) when QLen >= MaxQ -> 322 | synq_full; 323 | push_syn(SYNPacket, #accept_queue { incoming_conns = IC, 324 | q_len = QLen } = Q) -> 325 | case queue:member(SYNPacket, IC) of 326 | true -> 327 | duplicate; 328 | false -> 329 | handle_queue( 330 | Q#accept_queue { 331 | incoming_conns = queue:in(SYNPacket, IC), 332 | q_len = QLen + 1 }, []) 333 | end. 334 | 335 | 336 | handle_queue(#accept_queue { acceptors = AQ, 337 | incoming_conns = IC, 338 | q_len = QLen } = Q, Pairings) -> 339 | case {queue:out(AQ), queue:out(IC)} of 340 | {{{value, Acceptor}, AQ1}, {{value, SYN}, IC1}} -> 341 | handle_queue(Q#accept_queue { acceptors = AQ1, 342 | incoming_conns = IC1, 343 | q_len = QLen - 1 }, 344 | [{Acceptor, SYN} | Pairings]); 345 | _ -> 346 | {ok, Pairings, Q} % Can't do anymore work for now 347 | end. 348 | 349 | accept_incoming_conn(Socket, From, {SynPacket, Addr, Port}, ListenOpts) -> 350 | {ok, Pid} = gen_utp_worker_pool:start_child(Socket, Addr, Port, ListenOpts), 351 | %% We should register because we are the ones that can avoid 352 | %% the deadlock here 353 | %% @todo This call can in principle fail due 354 | %% to a conn_id being in use, but we will 355 | %% know if that happens. 356 | CID = {SynPacket#packet.conn_id, Addr, Port}, 357 | case reg_proc(Pid, CID) of 358 | ok -> 359 | ok = gen_utp_worker:accept(Pid, SynPacket), 360 | gen_server:reply(From, {ok, {utp_sock, Pid}}), 361 | {Pid, CID} 362 | end. 363 | 364 | new_accept_queue(QLen) -> 365 | #accept_queue { acceptors = queue:new(), 366 | incoming_conns = queue:new(), 367 | q_len = 0, 368 | max_q_len = QLen }. 369 | 370 | get_socket() -> 371 | call(get_socket). 372 | 373 | call(Msg) -> 374 | gen_server:call(?MODULE, Msg, infinity). 375 | 376 | reg_proc(Proc, {ConnId, Addr, Port}) -> 377 | case ets:member(?TAB, {ConnId, Addr, Port}) 378 | orelse ets:member(?TAB, {utp_util:bit16(ConnId+1), Addr, Port}) 379 | of 380 | true -> 381 | Rows = ets:match(?TAB, '$1'), 382 | {error, {conn_id_in_use, Rows}}; 383 | false -> 384 | true = ets:insert(?TAB, [{{ConnId, Addr, Port}, Proc}, 385 | {{utp_util:bit16(ConnId+1), Addr, Port}, Proc}]), 386 | ok 387 | end. 388 | 389 | unreg(CID, Addr, Port) -> 390 | true = ets:member(?TAB, {CID, Addr, Port}) 391 | orelse ets:member(?TAB, {utp_util:bit16(CID+1), Addr, Port}), 392 | true = ets:delete(?TAB, {CID, Addr, Port}), 393 | true = ets:delete(?TAB, {utp_util:bit16(CID+1), Addr, Port}), 394 | ok. 395 | 396 | -spec validate_listen_opts([listen_opts()]) -> ok | badarg. 397 | validate_listen_opts([]) -> 398 | ok; 399 | validate_listen_opts([{backlog, N} | R]) -> 400 | case is_integer(N) of 401 | true -> 402 | validate_listen_opts(R); 403 | false -> 404 | badarg 405 | end; 406 | validate_listen_opts([{force_seq_no, N} | R]) -> 407 | case is_integer(N) of 408 | true when N >= 0, 409 | N =< 16#FFFF -> 410 | validate_listen_opts(R); 411 | true -> 412 | badarg; 413 | false -> 414 | badarg 415 | end; 416 | validate_listen_opts([{trace_counters, B} | R]) when is_boolean(B) -> 417 | validate_listen_opts(R); 418 | validate_listen_opts([_U | R]) -> 419 | validate_listen_opts(R). 420 | -------------------------------------------------------------------------------- /src/gen_utp_decoder.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc uTP protocol decoder process 4 | %%% @end 5 | -module(gen_utp_decoder). 6 | 7 | -behaviour(gen_server). 8 | 9 | -include("utp.hrl"). 10 | 11 | %% API 12 | -export([start_link/0]). 13 | -export([decode_and_dispatch/3]). 14 | 15 | %% gen_server callbacks 16 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 17 | terminate/2, code_change/3]). 18 | 19 | -define(SERVER, ?MODULE). 20 | 21 | -record(state, {}). 22 | 23 | %%%=================================================================== 24 | 25 | %% @doc Starts the server 26 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 27 | %% @end 28 | start_link() -> 29 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 30 | 31 | decode_and_dispatch(Packet, IP, Port) -> 32 | gen_server:cast(?SERVER, {packet, Packet, IP, Port}). 33 | 34 | %%%=================================================================== 35 | 36 | %% @private 37 | %% @end 38 | init([]) -> 39 | {ok, #state{}}. 40 | 41 | %% @private 42 | handle_call(_Request, _From, State) -> 43 | Reply = ok, 44 | {reply, Reply, State}. 45 | 46 | %% @private 47 | handle_cast({packet, P, Addr, Port}, S) -> 48 | case utp_proto:decode(P) of 49 | {ok, 50 | {#packet { conn_id = CID, 51 | ty = PTy } = Packet, TS, TSDiff, RecvTime}} -> 52 | case PTy of 53 | st_reset -> 54 | case gen_utp:lookup_registrar(CID, Addr, Port) of 55 | {ok, Pid} -> 56 | gen_utp_worker:incoming(Pid, Packet, {TS, TSDiff, RecvTime}); 57 | not_found -> 58 | case gen_utp:lookup_registrar(CID+1, Addr, Port) of 59 | {ok, Pid} -> 60 | gen_utp_worker:incoming(Pid, Packet, {TS, TSDiff, RecvTime}); 61 | not_found -> 62 | gen_utp:incoming_unknown(Packet, Addr, Port) 63 | end 64 | end; 65 | _OtherState -> 66 | case gen_utp:lookup_registrar(CID, Addr, Port) of 67 | {ok, Pid} -> 68 | gen_utp_worker:incoming(Pid, Packet, {TS, TSDiff, RecvTime}); 69 | not_found -> 70 | gen_utp:incoming_unknown(Packet, Addr, Port) 71 | end 72 | end, 73 | {noreply, S}; 74 | {error, Reason} -> 75 | error_logger:info_report([decoder_error, Reason]), 76 | {noreply, S} 77 | end; 78 | handle_cast(_Msg, State) -> 79 | {noreply, State}. 80 | 81 | %% @private 82 | handle_info(_Info, State) -> 83 | {noreply, State}. 84 | 85 | %% @private 86 | terminate(_Reason, _State) -> 87 | ok. 88 | 89 | %% @private 90 | code_change(_OldVsn, State, _Extra) -> 91 | {ok, State}. 92 | 93 | %%%=================================================================== 94 | -------------------------------------------------------------------------------- /src/gen_utp_trace.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc Trace events in the uTP stack 4 | %%% @end 5 | -module(gen_utp_trace). 6 | 7 | -behaviour(gen_server). 8 | 9 | %% API 10 | -export([start_link/0, 11 | grab/0, 12 | tr/1]). 13 | 14 | %% gen_server callbacks 15 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 16 | terminate/2, code_change/3]). 17 | 18 | -define(SERVER, ?MODULE). 19 | 20 | -record(state, { trace_buffer = [] :: [term()] }). 21 | 22 | %%%=================================================================== 23 | 24 | %%-------------------------------------------------------------------- 25 | %% @doc 26 | %% Starts the server 27 | %% @end 28 | %%-------------------------------------------------------------------- 29 | start_link() -> 30 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 31 | 32 | tr(Msg) -> 33 | gen_server:cast(?SERVER, {trace, Msg}). 34 | 35 | grab() -> 36 | gen_server:call(?SERVER, grab). 37 | 38 | %%%=================================================================== 39 | 40 | %% @private 41 | init([]) -> 42 | {ok, #state{ trace_buffer = [] }}. 43 | 44 | %% @private 45 | handle_call(grab, _From, #state { trace_buffer = TB } = State) -> 46 | {reply, {ok, lists:reverse(TB)}, State#state { trace_buffer = [] }}; 47 | handle_call(_Request, _From, State) -> 48 | Reply = ok, 49 | {reply, Reply, State}. 50 | 51 | %% @private 52 | handle_cast({trace, Msg}, #state { trace_buffer = TB } = State) -> 53 | {noreply, State#state { trace_buffer = [Msg | TB] }}; 54 | handle_cast(_Msg, State) -> 55 | {noreply, State}. 56 | 57 | %% @private 58 | handle_info(_Info, State) -> 59 | {noreply, State}. 60 | 61 | %% @private 62 | terminate(_Reason, _State) -> 63 | ok. 64 | 65 | %% @private 66 | code_change(_OldVsn, State, _Extra) -> 67 | {ok, State}. 68 | 69 | %%%=================================================================== 70 | -------------------------------------------------------------------------------- /src/gen_utp_worker.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Jesper Louis andersen 3 | %%% @copyright (C) 2011, Jesper Louis andersen 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 19 Feb 2011 by Jesper Louis andersen 8 | %%%------------------------------------------------------------------- 9 | -module(gen_utp_worker). 10 | 11 | -include("log.hrl"). 12 | -include("utp.hrl"). 13 | 14 | -behaviour(gen_fsm). 15 | 16 | %% API 17 | -export([start_link/4]). 18 | 19 | %% Operations 20 | -export([connect/1, 21 | accept/2, 22 | close/1, 23 | 24 | recv/2, 25 | send/2 26 | ]). 27 | 28 | %% Internal API 29 | -export([ 30 | incoming/3, 31 | reply/2 32 | ]). 33 | 34 | %% gen_fsm callbacks 35 | -export([init/1, handle_event/3, 36 | handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). 37 | 38 | %% gen_fsm callback states 39 | -export([idle/2, idle/3, 40 | syn_sent/2, 41 | connected/2, connected/3, 42 | got_fin/2, got_fin/3, 43 | destroy_delay/2, 44 | fin_sent/2, fin_sent/3, 45 | reset/2, reset/3, 46 | destroy/2]). 47 | 48 | -type conn_state() :: idle | syn_sent | connected | got_fin 49 | | destroy_delay | fin_sent | reset | destroy. 50 | 51 | -type error_type() :: econnreset | econnrefused | etiemedout | emsgsize. 52 | -type ret_value() :: ok | {ok, binary()} | {error, error_type()}. 53 | 54 | -export_type([conn_state/0, 55 | error_type/0, 56 | ret_value/0]). 57 | 58 | -define(SERVER, ?MODULE). 59 | %% Default extensions to use when SYN/SYNACK'ing 60 | -define(SYN_EXTS, [{ext_bits, <<0:64/integer>>}]). 61 | 62 | %% Default SYN packet timeout 63 | -define(SYN_TIMEOUT, 3000). 64 | -define(DEFAULT_RETRANSMIT_TIMEOUT, 3000). 65 | -define(SYN_TIMEOUT_THRESHOLD, ?SYN_TIMEOUT*2). 66 | -define(RTT_VAR, 800). % Round trip time variance 67 | -define(PACKET_SIZE, 350). % @todo Probably dead! 68 | -define(MAX_WINDOW_USER, 255 * ?PACKET_SIZE). % Likewise! 69 | -define(DEFAULT_ACK_TIME, 16#70000000). % Default add to the future when an Ack is expected 70 | -define(DELAYED_ACK_BYTE_THRESHOLD, 2400). % bytes 71 | -define(DELAYED_ACK_TIME_THRESHOLD, 100). % milliseconds 72 | -define(KEEPALIVE_INTERVAL, 29000). % ms 73 | 74 | %% Number of bytes to increase max window size by, per RTT. This is 75 | %% scaled down linearly proportional to off_target. i.e. if all packets 76 | %% in one window have 0 delay, window size will increase by this number. 77 | %% Typically it's less. TCP increases one MSS per RTT, which is 1500 78 | -define(MAX_CWND_INCREASE_BYTES_PER_RTT, 3000). 79 | -define(CUR_DELAY_SIZE, 3). 80 | 81 | %% Default timeout value for sockets where we will destroy them! 82 | -define(RTO_DESTROY_VALUE, 30*1000). 83 | 84 | %% The delay to set on Zero Windows. It is awfully high, but that is what it has 85 | %% to be it seems. 86 | -define(ZERO_WINDOW_DELAY, 15*1000). 87 | 88 | %% Experiments suggest that a clock skew of 10 ms per 325 seconds 89 | %% is not impossible. Reset delay_base every 13 minutes. The clock 90 | %% skew is dealt with by observing the delay base in the other 91 | %% direction, and adjusting our own upwards if the opposite direction 92 | %% delay base keeps going down 93 | -define(DELAY_BASE_HISTORY, 13). 94 | -define(MAX_WINDOW_DECAY, 100). % ms 95 | 96 | -define(DEFAULT_OPT_RECV_SZ, 8192). %% @todo Fix this 97 | -define(DEFAULT_PACKET_SIZE, 350). %% @todo Fix, arbitrary at the moment 98 | 99 | -define(DEFAULT_FSM_TIMEOUT, 10*60*1000). 100 | %% STATE RECORDS 101 | %% ---------------------------------------------------------------------- 102 | -record(state, { network :: utp_network:t(), 103 | buffer :: utp_buffer:t(), 104 | process :: utp_process:t(), 105 | connector :: {{reference(), pid()}, [{pkt, #packet{}, term()}]}, 106 | zerowindow_timeout :: undefined | {set, reference()}, 107 | retransmit_timeout :: undefined | {set, reference()}, 108 | delayed_ack_timeout :: undefined | {set, integer(), reference()}, 109 | options = [] :: [{atom(), term()}] 110 | }). 111 | 112 | %%%=================================================================== 113 | 114 | %% @doc Create a worker for a peer endpoint 115 | %% @end 116 | start_link(Socket, Addr, Port, Options) -> 117 | gen_fsm:start_link(?MODULE, [Socket, Addr, Port, Options], []). 118 | 119 | %% @doc Send a connect event 120 | %% @end 121 | connect(Pid) -> 122 | utp:report_event(60, client, us, connect, []), 123 | sync_send_event(Pid, connect). 124 | 125 | %% @doc Send an accept event 126 | %% @end 127 | accept(Pid, SynPacket) -> 128 | utp:report_event(60, client, us, accept, [SynPacket]), 129 | sync_send_event(Pid, {accept, SynPacket}). 130 | 131 | %% @doc Receive some bytes from the socket. Blocks until the said amount of 132 | %% bytes have been read. 133 | %% @end 134 | recv(Pid, Amount) -> 135 | utp:report_event(60, client, us, {recv, Amount}, []), 136 | try 137 | gen_fsm:sync_send_event(Pid, {recv, Amount}, infinity) 138 | catch 139 | exit:{noproc, _} -> 140 | {error, enoconn} 141 | end. 142 | 143 | %% @doc Send some bytes from the socket. Blocks until the said amount of 144 | %% bytes have been sent and has been accepted by the underlying layer. 145 | %% @end 146 | send(Pid, Data) -> 147 | utp:report_event(60, client, us, {send, size(Data)}, [Data]), 148 | try 149 | gen_fsm:sync_send_event(Pid, {send, Data}, infinity) 150 | catch 151 | exit:{noproc, _} -> 152 | {error, enoconn} 153 | end. 154 | 155 | %% @doc Send a close event 156 | %% @end 157 | close(Pid) -> 158 | utp:report_event(60, client, us, close, []), 159 | %% Consider making it sync, but the de-facto implementation isn't 160 | gen_fsm:send_event(Pid, close). 161 | 162 | %% ---------------------------------------------------------------------- 163 | incoming(Pid, Packet, Timing) -> 164 | utp:report_event(50, peer, us, utp_proto:succinct_format_packet(Packet), [{packet, Packet}]), 165 | gen_fsm:send_event(Pid, {pkt, Packet, Timing}). 166 | 167 | reply(To, Msg) -> 168 | utp:report_event(60, us, client, reply, [Msg]), 169 | gen_fsm:reply(To, Msg). 170 | 171 | sync_send_event(Pid, Event) -> 172 | gen_fsm:sync_send_event(Pid, Event, ?DEFAULT_FSM_TIMEOUT). 173 | 174 | 175 | %%%=================================================================== 176 | %%% gen_fsm callbacks 177 | %%%=================================================================== 178 | 179 | %% @private 180 | init([Socket, Addr, Port, Options]) -> 181 | case validate_options(Options) of 182 | ok -> 183 | PktBuf = utp_buffer:mk(?DEFAULT_OPT_RECV_SZ), 184 | ProcInfo = utp_process:mk(), 185 | CanonAddr = utp_util:canonicalize_address(Addr), 186 | SockInfo = utp_socket:mk(CanonAddr, Options, Port, Socket), 187 | Network = utp_network:mk(?DEFAULT_PACKET_SIZE, SockInfo), 188 | {ok, report(idle), #state{ network = Network, 189 | buffer = PktBuf, 190 | process = ProcInfo, 191 | options= Options }}; 192 | badarg -> 193 | {report(stop), badarg} 194 | end. 195 | 196 | %% @private 197 | idle(close, S) -> 198 | {next_state, report(destroy), S, 0}; 199 | idle(_Msg, S) -> 200 | %% Ignore messages 201 | ?ERR([node(), async_message, idle, _Msg]), 202 | {next_state, idle, S}. 203 | 204 | %% @private 205 | syn_sent({pkt, #packet { ty = st_reset }, _}, 206 | #state { process = PRI, 207 | connector = {From, _} } = State) -> 208 | %% We received a reset packet in the connected state. This means an abrupt 209 | %% disconnect, so move to the RESET state right away after telling people 210 | %% we can't fulfill their requests. 211 | N_PRI = utp_process:error_all(PRI, econnrefused), 212 | %% Also handle the guy making the connection 213 | reply(From, econnrefused), 214 | {next_state, report(destroy), State#state { process = N_PRI }, 0}; 215 | syn_sent({pkt, #packet { ty = st_state, 216 | win_sz = WindowSize, 217 | seq_no = PktSeqNo }, 218 | {TS, TSDiff, RecvTime}}, 219 | #state { network = Network, 220 | buffer = PktBuf, 221 | connector = {From, Packets}, 222 | retransmit_timeout = RTimeout 223 | } = State) -> 224 | reply(From, ok), 225 | %% Empty the queue of packets for the new state 226 | %% We reverse the list so they are in the order we got them originally 227 | [incoming(self(), P, T) || {pkt, P, T} <- lists:reverse(Packets)], 228 | %% @todo Consider LEDBAT here 229 | ReplyMicro = utp_util:bit32(TS - RecvTime), 230 | set_ledbat_timer(), 231 | N2 = utp_network:handle_advertised_window(Network, WindowSize), 232 | N_Network = utp_network:update_our_ledbat(N2, TSDiff), 233 | {next_state, report(connected), 234 | State#state { network = utp_network:update_reply_micro(N_Network, ReplyMicro), 235 | retransmit_timeout = clear_retransmit_timer(RTimeout), 236 | buffer = utp_buffer:init_ackno(PktBuf, utp_util:bit16(PktSeqNo + 1))}}; 237 | syn_sent({pkt, _Packet, _Timing} = Pkt, 238 | #state { connector = {From, Packets}} = State) -> 239 | {next_state, syn_sent, 240 | State#state { 241 | connector = {From, [Pkt | Packets]}}}; 242 | syn_sent(close, #state { 243 | network = Network, 244 | retransmit_timeout = RTimeout 245 | } = State) -> 246 | clear_retransmit_timer(RTimeout), 247 | Gracetime = lists:min([60, utp_network:rto(Network) * 2]), 248 | Timer = set_retransmit_timer(Gracetime, undefined), 249 | {next_state, syn_sent, State#state { 250 | retransmit_timeout = Timer }}; 251 | syn_sent({timeout, TRef, {retransmit_timeout, N}}, 252 | #state { retransmit_timeout = {set, TRef}, 253 | network = Network, 254 | connector = {From, _}, 255 | buffer = PktBuf 256 | } = State) -> 257 | report_timer_trigger(retransmit), 258 | case N > ?SYN_TIMEOUT_THRESHOLD of 259 | true -> 260 | reply(From, {error, etimedout}), 261 | {next_state, report(reset), State#state {retransmit_timeout = undefined}}; 262 | false -> 263 | % Resend packet 264 | SynPacket = utp_proto:mk_syn(), 265 | Win = utp_buffer:advertised_window(PktBuf), 266 | {ok, _} = utp_network:send_pkt(Win, Network, SynPacket, conn_id_recv), 267 | {next_state, syn_sent, 268 | State#state { 269 | retransmit_timeout = set_retransmit_timer(N*2, undefined) 270 | }} 271 | end; 272 | syn_sent(_Msg, S) -> 273 | %% Ignore messages 274 | ?ERR([node(), async_message, syn_sent, _Msg]), 275 | {next_state, syn_sent, S}. 276 | 277 | 278 | %% @private 279 | connected({pkt, #packet { ty = st_reset }, _}, 280 | #state { process = PRI } = State) -> 281 | %% We received a reset packet in the connected state. This means an abrupt 282 | %% disconnect, so move to the RESET state right away after telling people 283 | %% we can't fulfill their requests. 284 | N_PRI = utp_process:error_all(PRI, econnreset), 285 | {next_state, report(reset), State#state { process = N_PRI }}; 286 | connected({pkt, #packet { ty = st_syn }, _}, State) -> 287 | ?INFO([duplicate_syn_packet, ignoring]), 288 | {next_state, connected, State}; 289 | connected({pkt, Pkt, {TS, TSDiff, RecvTime}}, State) -> 290 | {ok, Messages, N_Network, N_PB, N_PRI, ZWinTimeout, N_DelayAck, N_RetransTimer} = 291 | handle_packet_incoming(connected, 292 | Pkt, utp_util:bit32(TS - RecvTime), RecvTime, TSDiff, State), 293 | 294 | {NextState, 295 | N_ProcessInfo } = case proplists:get_value(got_fin, Messages) of 296 | true -> 297 | %% We need to tell all processes that had data to send that 298 | %% it is now in limbo. In particular we don't know if it will 299 | %% ever complete 300 | utp_process:apply_senders(N_PRI, 301 | fun(From) -> 302 | reply(From, {error, eclosed}) 303 | end), 304 | 305 | {report(got_fin), utp_process:clear_senders(N_PRI)}; 306 | undefined -> 307 | {connected, N_PRI} 308 | end, 309 | {next_state, NextState, 310 | State#state { buffer = N_PB, 311 | network = N_Network, 312 | retransmit_timeout = N_RetransTimer, 313 | zerowindow_timeout = ZWinTimeout, 314 | delayed_ack_timeout = N_DelayAck, 315 | process = N_ProcessInfo }}; 316 | connected(close, #state { network = Network, 317 | buffer = PktBuf } = State) -> 318 | NPBuf = utp_buffer:send_fin(Network, PktBuf), 319 | {next_state, report(fin_sent), State#state { buffer = NPBuf } }; 320 | connected({timeout, _, ledbat_timeout}, State) -> 321 | {next_state, connected, bump_ledbat(State)}; 322 | connected({timeout, Ref, send_delayed_ack}, State) -> 323 | {next_state, connected, trigger_delayed_ack(Ref, State)}; 324 | connected({timeout, Ref, {zerowindow_timeout, _N}}, 325 | #state { 326 | buffer = PktBuf, 327 | process = ProcessInfo, 328 | network = Network, 329 | zerowindow_timeout = {set, Ref}} = State) -> 330 | report_timer_trigger(zerowin), 331 | N_Network = utp_network:bump_window(Network), 332 | {_FillMessages, ZWinTimer, N_PktBuf, N_ProcessInfo} = 333 | fill_window(N_Network, ProcessInfo, PktBuf, undefined), 334 | {next_state, connected, 335 | State#state { 336 | zerowindow_timeout = ZWinTimer, 337 | buffer = N_PktBuf, 338 | process = N_ProcessInfo}}; 339 | connected({timeout, Ref, {retransmit_timeout, N}}, 340 | #state { 341 | buffer = PacketBuf, 342 | network = Network, 343 | retransmit_timeout = {set, Ref} = Timer} = State) -> 344 | report_timer_trigger(retransmit), 345 | case handle_timeout(Ref, N, PacketBuf, Network, Timer) of 346 | stray -> 347 | {next_state, connected, State}; 348 | gave_up -> 349 | {next_state, report(reset), State}; 350 | {reinstalled, N_Timer, N_PB, N_Network} -> 351 | {next_state, connected, State#state { retransmit_timeout = N_Timer, 352 | network = N_Network, 353 | buffer = N_PB }} 354 | end; 355 | connected(_Msg, State) -> 356 | %% Ignore messages 357 | ?ERR([node(), async_message, connected, _Msg]), 358 | {next_state, connected, State}. 359 | 360 | %% @private 361 | got_fin(close, #state { 362 | retransmit_timeout = Timer, 363 | network = Network } = State) -> 364 | N_Timer = set_retransmit_timer(utp_network:rto(Network), Timer), 365 | {next_state, report(destroy_delay), State#state { retransmit_timeout = N_Timer } }; 366 | got_fin({timeout, Ref, {retransmit_timeout, N}}, 367 | #state { 368 | buffer = PacketBuf, 369 | network = Network, 370 | retransmit_timeout = Timer} = State) -> 371 | report_timer_trigger(retransmit), 372 | case handle_timeout(Ref, N, PacketBuf, Network, Timer) of 373 | stray -> 374 | {next_state, got_fin, State}; 375 | gave_up -> 376 | {next_state, report(reset), State}; 377 | {reinstalled, N_Timer, N_PB, N_Network} -> 378 | {next_state, got_fin, State#state { retransmit_timeout = N_Timer, 379 | network = N_Network, 380 | buffer = N_PB }} 381 | end; 382 | got_fin({timeout, Ref, send_delayed_ack}, State) -> 383 | {next_state, got_fin, trigger_delayed_ack(Ref, State)}; 384 | got_fin({pkt, #packet { ty = st_state }, _}, State) -> 385 | %% State packets incoming can be ignored. Why? Because state packets from the other 386 | %% end doesn't matter at this point: We got the FIN completed, so we can't send or receive 387 | %% anymore. And all who were waiting are expunged from the receive buffer. No new can enter. 388 | %% Our Timeout will move us on (or a close). The other end is in the FIN_SENT state, so 389 | %% he will only send state packets when he needs to ack some of our stuff, which he wont. 390 | {next_state, got_fin, State}; 391 | got_fin({pkt, #packet {ty = st_reset}, _}, #state {process = Process} = State) -> 392 | %% The other end requests that we close down the socket? Ok, lets just comply: 393 | N_Process = utp_process:error_all(Process, econnreset), 394 | {next_state, report(destroy), State#state { process = N_Process }, 0}; 395 | got_fin({pkt, #packet { ty = st_fin }, _}, State) -> 396 | %% @todo We should probably send out an ACK for the FIN here since it is a retransmit 397 | {next_state, got_fin, State}; 398 | got_fin(_Msg, State) -> 399 | %% Ignore messages 400 | ?ERR([node(), async_message, got_fin, _Msg]), 401 | {next_state, got_fin, State}. 402 | 403 | %% @private 404 | destroy_delay({timeout, Ref, {retransmit_timeout, _N}}, 405 | #state { retransmit_timeout = {set, Ref} } = State) -> 406 | report_timer_trigger(retransmit), 407 | {next_state, report(destroy), State#state { retransmit_timeout = undefined }, 0}; 408 | destroy_delay({timeout, Ref, send_delayed_ack}, State) -> 409 | {next_state, destroy_delay, trigger_delayed_ack(Ref, State)}; 410 | destroy_delay({pkt, #packet { ty = st_fin }, _}, State) -> 411 | {next_state, destroy_delay, State}; 412 | destroy_delay(close, State) -> 413 | {next_state, report(destroy), State, 0}; 414 | destroy_delay(_Msg, State) -> 415 | %% Ignore messages 416 | ?ERR([node(), async_message, destroy_delay, _Msg]), 417 | {next_state, destroy_delay, State}. 418 | 419 | %% @private 420 | %% Die deliberately on close for now 421 | fin_sent({pkt, #packet { ty = st_syn }, _}, 422 | State) -> 423 | %% Quaff SYN packets if they arrive in this state. They are stray. 424 | %% I have seen it happen in tests, however unlikely that it happens in real life. 425 | {next_state, fin_sent, State}; 426 | fin_sent({pkt, #packet { ty = st_reset }, _}, 427 | #state { process = PRI } = State) -> 428 | %% We received a reset packet in the connected state. This means an abrupt 429 | %% disconnect, so move to the RESET state right away after telling people 430 | %% we can't fulfill their requests. 431 | N_PRI = utp_process:error_all(PRI, econnreset), 432 | {next_state, report(destroy), State#state { process = N_PRI }, 0}; 433 | fin_sent({pkt, Pkt, {TS, TSDiff, RecvTime}}, State) -> 434 | {ok, Messages, N_Network, N_PB, N_PRI, ZWinTimeout, N_DelayAck, N_RetransTimer} = 435 | handle_packet_incoming(fin_sent, 436 | Pkt, utp_util:bit32(TS - RecvTime), RecvTime, TSDiff, State), 437 | %% Calculate the next state 438 | N_State = State#state { 439 | buffer = N_PB, 440 | network = N_Network, 441 | retransmit_timeout = N_RetransTimer, 442 | zerowindow_timeout = ZWinTimeout, 443 | delayed_ack_timeout = N_DelayAck, 444 | process = N_PRI }, 445 | case proplists:get_value(fin_sent_acked, Messages) of 446 | true -> 447 | {next_state, report(destroy), N_State, 0}; 448 | undefined -> 449 | {next_state, fin_sent, N_State} 450 | end; 451 | fin_sent({timeout, _, ledbat_timeout}, State) -> 452 | {next_state, fin_sent, bump_ledbat(State)}; 453 | fin_sent({timeout, Ref, send_delayed_ack}, State) -> 454 | {next_state, fin_sent, trigger_delayed_ack(Ref, State)}; 455 | fin_sent({timeout, Ref, {retransmit_timeout, N}}, 456 | #state { buffer = PacketBuf, 457 | network = Network, 458 | retransmit_timeout = Timer} = State) -> 459 | report_timer_trigger(retransmit), 460 | case handle_timeout(Ref, N, PacketBuf, Network, Timer) of 461 | stray -> 462 | {next_state, fin_sent, State}; 463 | gave_up -> 464 | {next_state, report(destroy), State, 0}; 465 | {reinstalled, N_Timer, N_PB, N_Network} -> 466 | {next_state, fin_sent, State#state { retransmit_timeout = N_Timer, 467 | network = N_Network, 468 | buffer = N_PB }} 469 | end; 470 | fin_sent(_Msg, State) -> 471 | %% Ignore messages 472 | ?ERR([node(), async_message, fin_sent, _Msg]), 473 | {next_state, fin_sent, State}. 474 | 475 | %% @private 476 | reset(close, State) -> 477 | {next_state, report(destroy), State, 0}; 478 | reset(_Msg, State) -> 479 | %% Ignore messages 480 | ?ERR([node(), async_message, reset, _Msg]), 481 | {next_state, reset, State}. 482 | 483 | %% @private 484 | %% Die deliberately on close for now 485 | destroy(timeout, #state { process = ProcessInfo } = State) -> 486 | N_ProcessInfo = utp_process:error_all(ProcessInfo, econnreset), 487 | {report(stop), normal, State#state { process = N_ProcessInfo }}; 488 | destroy(_Msg, State) -> 489 | %% Ignore messages 490 | ?ERR([node(), async_message, destroy, _Msg]), 491 | {next_state, destroy, State, 0}. 492 | 493 | %% @private 494 | idle(connect, 495 | From, State = #state { network = Network, 496 | buffer = PktBuf}) -> 497 | {Address, Port} = utp_network:hostname_port(Network), 498 | Conn_id_recv = utp_proto:mk_connection_id(), 499 | gen_utp:register_process(self(), {Conn_id_recv, Address, Port}), 500 | N_Network = utp_network:set_conn_id(Conn_id_recv + 1, Network), 501 | 502 | SynPacket = utp_proto:mk_syn(), 503 | send_pkt(PktBuf, N_Network, SynPacket), 504 | 505 | {next_state, report(syn_sent), 506 | State#state { network = N_Network, 507 | retransmit_timeout = set_retransmit_timer(?SYN_TIMEOUT, undefined), 508 | buffer = utp_buffer:init_seqno(PktBuf, 2), 509 | connector = {From, []}}}; 510 | idle({accept, SYN}, _From, #state { network = Network, 511 | options = Options, 512 | buffer = PktBuf } = State) -> 513 | utp:report_event(50, peer, us, utp_proto:succinct_format_packet(SYN), [{packet, SYN}]), 514 | 1 = SYN#packet.seq_no, 515 | Conn_id_send = SYN#packet.conn_id, 516 | N_Network = utp_network:set_conn_id(Conn_id_send, Network), 517 | SeqNo = init_seq_no(Options), 518 | 519 | AckPacket = utp_proto:mk_ack(SeqNo, SYN#packet.seq_no), 520 | Win = utp_buffer:advertised_window(PktBuf), 521 | {ok, _} = utp_network:send_pkt(Win, N_Network, AckPacket), 522 | 523 | %% @todo retransmit timer here? 524 | set_ledbat_timer(), 525 | utp:report_event(60, us, client, ok, []), 526 | {reply, ok, report(connected), 527 | State#state { network = utp_network:handle_advertised_window(N_Network, SYN), 528 | buffer = utp_buffer:init_counters(PktBuf, 529 | utp_util:bit16(SeqNo + 1), 530 | utp_util:bit16(SYN#packet.seq_no + 1))}}; 531 | 532 | idle(_Msg, _From, State) -> 533 | {reply, idle, {error, enotconn}, State}. 534 | 535 | init_seq_no(Options) -> 536 | case proplists:get_value(force_seq_no, Options) of 537 | undefined -> utp_buffer:mk_random_seq_no(); 538 | K -> K 539 | end. 540 | 541 | 542 | send_pkt(PktBuf, N_Network, SynPacket) -> 543 | Win = utp_buffer:advertised_window(PktBuf), 544 | {ok, _} = utp_network:send_pkt(Win, N_Network, SynPacket, conn_id_recv). 545 | 546 | %% @private 547 | connected({recv, Length}, From, #state { process = PI, 548 | network = Network, 549 | delayed_ack_timeout = DelayAckT, 550 | buffer = PKB } = State) -> 551 | PI1 = utp_process:enqueue_receiver(From, Length, PI), 552 | case satisfy_recvs(PI1, PKB) of 553 | {_, N_PRI, N_PKB} -> 554 | N_Delay = case utp_buffer:view_zerowindow_reopen(PKB, N_PKB) of 555 | true -> 556 | handle_send_ack(Network, N_PKB, DelayAckT, 557 | [send_ack, no_piggyback], 0); 558 | false -> 559 | DelayAckT 560 | end, 561 | {next_state, connected, State#state { process = N_PRI, 562 | delayed_ack_timeout = N_Delay, 563 | buffer = N_PKB } } 564 | end; 565 | connected({send, Data}, From, #state { 566 | network = Network, 567 | process = PI, 568 | retransmit_timeout = RTimer, 569 | zerowindow_timeout = ZWinTimer, 570 | buffer = PKB } = State) -> 571 | ProcInfo = utp_process:enqueue_sender(From, Data, PI), 572 | {FillMessages, N_ZWinTimer, PKB1, ProcInfo1} = 573 | fill_window(Network, 574 | ProcInfo, 575 | PKB, 576 | ZWinTimer), 577 | N_RTimer = handle_send_retransmit_timer(FillMessages, Network, RTimer), 578 | {next_state, connected, State#state { 579 | zerowindow_timeout = N_ZWinTimer, 580 | retransmit_timeout = N_RTimer, 581 | process = ProcInfo1, 582 | buffer = PKB1 }}; 583 | connected(_Msg, _From, State) -> 584 | ?ERR([sync_message, connected, _Msg, _From]), 585 | {next_state, connected, State}. 586 | 587 | %% @private 588 | got_fin({recv, L}, _From,State) -> 589 | {ok, Reply, NewProcessState} = drain_buffer(L, State), 590 | {reply, Reply, got_fin, NewProcessState}; 591 | got_fin({send, _Data}, _From, State) -> 592 | utp:report_event(60, us, client, {error, econnreset}, []), 593 | {reply, {error, econnreset}, got_fin, State}. 594 | 595 | %% @private 596 | fin_sent({recv, L}, _From, State) -> 597 | {ok, Reply, NewProcessState} = drain_buffer(L, State), 598 | {reply, Reply, fin_sent, NewProcessState}; 599 | fin_sent({send, _Data}, _From, State) -> 600 | utp:report_event(60, us, client, {error, econnreset}), 601 | {reply, {error, econnreset}, fin_sent, State}. 602 | 603 | %% @private 604 | reset({recv, _L}, _From, State) -> 605 | utp:report_event(60, us, client, {error, econnreset}), 606 | {reply, {error, econnreset}, reset, State}; 607 | reset({send, _Data}, _From, State) -> 608 | utp:report_event(60, us, client, {error, econnreset}), 609 | {reply, {error, econnreset}, reset, State}. 610 | 611 | %% @private 612 | handle_event(_Event, StateName, State) -> 613 | ?ERR([unknown_handle_event, _Event, StateName, State]), 614 | {next_state, StateName, State}. 615 | 616 | %% @private 617 | handle_sync_event(_Event, _From, StateName, State) -> 618 | Reply = ok, 619 | {reply, Reply, StateName, State}. 620 | 621 | %% @private 622 | handle_info(_Info, StateName, State) -> 623 | {next_state, StateName, State}. 624 | 625 | %% @private 626 | terminate(_Reason, _StateName, _State) -> 627 | ok. 628 | 629 | %% @private 630 | code_change(_OldVsn, StateName, State, _Extra) -> 631 | {ok, StateName, State}. 632 | 633 | %%%=================================================================== 634 | 635 | satisfy_buffer(From, 0, Res, Buffer) -> 636 | reply(From, {ok, Res}), 637 | {ok, Buffer}; 638 | satisfy_buffer(From, Length, Res, Buffer) -> 639 | case utp_buffer:buffer_dequeue(Buffer) of 640 | {ok, Bin, N_Buffer} when byte_size(Bin) =< Length -> 641 | satisfy_buffer(From, Length - byte_size(Bin), <>, N_Buffer); 642 | {ok, Bin, N_Buffer} when byte_size(Bin) > Length -> 643 | <> = Bin, 644 | satisfy_buffer(From, 0, <>, 645 | utp_buffer:buffer_putback(Rest, N_Buffer)); 646 | empty -> 647 | {rb_drained, From, Length, Res, Buffer} 648 | end. 649 | 650 | satisfy_recvs(Processes, Buffer) -> 651 | case utp_process:dequeue_receiver(Processes) of 652 | {ok, {receiver, From, Length, Res}, N_Processes} -> 653 | case satisfy_buffer(From, Length, Res, Buffer) of 654 | {ok, N_Buffer} -> 655 | satisfy_recvs(N_Processes, N_Buffer); 656 | {rb_drained, F, L, R, N_Buffer} -> 657 | {rb_drained, utp_process:putback_receiver(F, L, R, N_Processes), N_Buffer} 658 | end; 659 | empty -> 660 | {ok, Processes, Buffer} 661 | end. 662 | 663 | set_retransmit_timer(N, Timer) -> 664 | set_retransmit_timer(N, N, Timer). 665 | 666 | set_retransmit_timer(N, K, undefined) -> 667 | report_timer_set(retransmit), 668 | Ref = gen_fsm:start_timer(N, {retransmit_timeout, K}), 669 | {set, Ref}; 670 | set_retransmit_timer(N, K, {set, Ref}) -> 671 | report_timer_bump(retransmit), 672 | gen_fsm:cancel_timer(Ref), 673 | N_Ref = gen_fsm:start_timer(N, {retransmit_timeout, K}), 674 | {set, N_Ref}. 675 | 676 | clear_retransmit_timer(undefined) -> 677 | undefined; 678 | clear_retransmit_timer({set, Ref}) -> 679 | report_timer_clear(retransmit), 680 | gen_fsm:cancel_timer(Ref), 681 | undefined. 682 | 683 | %% @doc Handle the retransmit timer in the send direction 684 | handle_send_retransmit_timer(Messages, Network, RetransTimer) -> 685 | case proplists:get_value(sent_data, Messages) of 686 | true -> 687 | set_retransmit_timer(utp_network:rto(Network), RetransTimer); 688 | undefined -> 689 | %% We sent nothing out, just use the current timer 690 | RetransTimer 691 | end. 692 | 693 | handle_recv_retransmit_timer(Messages, Network, RetransTimer) -> 694 | Analyzer = fun(L) -> lists:foldl(is_set(Messages), false, L) end, 695 | case Analyzer([data_inflight, fin_sent, sent_data]) of 696 | true -> 697 | set_retransmit_timer(utp_network:rto(Network), RetransTimer); 698 | false -> 699 | case Analyzer([all_acked]) of 700 | true -> 701 | clear_retransmit_timer(RetransTimer); 702 | false -> 703 | RetransTimer % Just pass it along with no update 704 | end 705 | end. 706 | 707 | is_set(Messages) -> 708 | fun(E, Acc) -> 709 | case proplists:get_value(E, Messages) of 710 | true -> 711 | true; 712 | undefined -> 713 | Acc 714 | end 715 | end. 716 | 717 | fill_window(Network, ProcessInfo, PktBuffer, ZWinTimer) -> 718 | {Messages, N_PktBuffer, N_ProcessInfo} = 719 | utp_buffer:fill_window(Network, 720 | ProcessInfo, 721 | PktBuffer), 722 | %% Capture and handle the case where the other end has given up in 723 | %% the space department of its receive buffer. 724 | case utp_network:view_zero_window(Network) of 725 | ok -> 726 | {Messages, cancel_zerowin_timer(ZWinTimer), N_PktBuffer, N_ProcessInfo}; 727 | zero -> 728 | {Messages, set_zerowin_timer(ZWinTimer), N_PktBuffer, N_ProcessInfo} 729 | end. 730 | 731 | cancel_zerowin_timer(undefined) -> undefined; 732 | cancel_zerowin_timer({set, Ref}) -> 733 | report_timer_clear(zerowin), 734 | gen_fsm:cancel_timer(Ref), 735 | undefined. 736 | 737 | set_zerowin_timer(undefined) -> 738 | report_timer_set(zerowin), 739 | Ref = gen_fsm:start_timer(?ZERO_WINDOW_DELAY, 740 | {zerowindow_timeout, ?ZERO_WINDOW_DELAY}), 741 | {set, Ref}; 742 | set_zerowin_timer({set, Ref}) -> {set, Ref}. % Already set, do nothing 743 | 744 | handle_packet_incoming(FSMState, Pkt, ReplyMicro, TimeAcked, TSDiff, 745 | #state { buffer = PB, 746 | process = PRI, 747 | network = Network, 748 | zerowindow_timeout = ZWin, 749 | retransmit_timeout = RetransTimer, 750 | delayed_ack_timeout = DelayAckT 751 | }) -> 752 | %% Handle the incoming packet 753 | try 754 | utp_buffer:handle_packet(FSMState, Pkt, Network, PB) 755 | of 756 | {ok, N_PB1, N_Network3, RecvMessages} -> 757 | N_Network2 = utp_network:update_window(N_Network3, ReplyMicro, TimeAcked, RecvMessages, TSDiff, Pkt), 758 | %% The packet may bump the advertised window from the peer, update 759 | %% The incoming datagram may have payload we can deliver to an application 760 | {_Drainage, N_PRI, N_PB} = satisfy_recvs(PRI, N_PB1), 761 | 762 | %% Fill up the send window again with the new information 763 | {FillMessages, ZWinTimeout, N_PB2, N_PRI2} = 764 | fill_window(N_Network2, N_PRI, N_PB, ZWin), 765 | 766 | Messages = RecvMessages ++ FillMessages, 767 | 768 | N_Network = utp_network:handle_maxed_out_window(Messages, N_Network2), 769 | %% @todo This ACK may be cancelled if we manage to push something out 770 | %% the window, etc., but the code is currently ready for it! 771 | %% The trick is to clear the message. 772 | 773 | %% Send out an ACK if needed 774 | AckedBytes = acked_bytes(Messages), 775 | N_DelayAckT = handle_send_ack(N_Network, N_PB2, 776 | DelayAckT, 777 | Messages, 778 | AckedBytes), 779 | N_RetransTimer = handle_recv_retransmit_timer(Messages, N_Network, RetransTimer), 780 | 781 | {ok, Messages, N_Network, N_PB2, N_PRI2, ZWinTimeout, N_DelayAckT, N_RetransTimer} 782 | catch 783 | throw:{error, is_far_in_future} -> 784 | utp:report_event(90, us, is_far_in_future, [Pkt]), 785 | {ok, [], Network, PB, PRI, ZWin, DelayAckT, RetransTimer} 786 | end. 787 | 788 | acked_bytes(Messages) -> 789 | case proplists:get_value(acked, Messages) of 790 | undefined -> 791 | 0; 792 | Acked when is_list(Acked) -> 793 | utp_buffer:extract_payload_size(Acked) 794 | end. 795 | 796 | handle_timeout(Ref, N, PacketBuf, Network, {set, Ref} = Timer) -> 797 | case N > ?RTO_DESTROY_VALUE of 798 | true -> 799 | gave_up; 800 | false -> 801 | N_Timer = set_retransmit_timer(N*2, Timer), 802 | N_PB = utp_buffer:retransmit_packet(PacketBuf, Network), 803 | N_Network = utp_network:reset_window(Network), 804 | {reinstalled, N_Timer, N_PB, N_Network} 805 | end; 806 | handle_timeout(_Ref, _N, _PacketBuf, _Network, _Timer) -> 807 | ?ERR([stray_retransmit_timer, _Ref, _N, _Timer]), 808 | stray. 809 | 810 | -spec validate_options([term()]) -> ok | badarg. 811 | validate_options([{backlog, N} | R]) -> 812 | case is_integer(N) of 813 | true -> 814 | validate_options(R); 815 | false -> 816 | badarg 817 | end; 818 | validate_options([{trace_counters, TF} | R]) when is_boolean(TF) -> 819 | validate_options(R); 820 | validate_options([{trace_counters, _} | _]) -> 821 | badarg; 822 | validate_options([{force_seq_no, N} | R]) -> 823 | case is_integer(N) of 824 | true when N >= 0, 825 | N =< 16#FFFF -> 826 | validate_options(R); 827 | true -> 828 | badarg; 829 | false -> 830 | badarg 831 | end; 832 | validate_options([]) -> 833 | ok; 834 | validate_options([_Unknown | R]) -> 835 | %% @todo Skip unknown options silently for now. 836 | validate_options(R). 837 | 838 | set_ledbat_timer() -> 839 | report_timer_set(ledbat), 840 | gen_fsm:start_timer(timer:seconds(60), ledbat_timeout). 841 | 842 | bump_ledbat(#state { network = Network } = State) -> 843 | report_timer_trigger(ledbat), 844 | N_Network = utp_network:bump_ledbat(Network), 845 | set_ledbat_timer(), 846 | State#state { network = N_Network }. 847 | 848 | trigger_delayed_ack(Ref, #state { 849 | buffer = PktBuf, 850 | network = Network, 851 | delayed_ack_timeout = {set, _, Ref} 852 | } = State) -> 853 | report_timer_trigger(delayed_ack), 854 | utp_buffer:send_ack(Network, PktBuf), 855 | State#state { delayed_ack_timeout = undefined }. 856 | 857 | cancel_delayed_ack(undefined) -> 858 | undefined; %% It was never there, ignore it 859 | cancel_delayed_ack({set, _Count, Ref}) -> 860 | report_timer_clear(delayed_ack), 861 | gen_fsm:cancel_timer(Ref), 862 | undefined. 863 | 864 | handle_delayed_ack(undefined, AckedBytes, Network, PktBuf) 865 | when AckedBytes >= ?DELAYED_ACK_BYTE_THRESHOLD -> 866 | utp_buffer:send_ack(Network, PktBuf), 867 | undefined; 868 | handle_delayed_ack(undefined, AckedBytes, _Network, _PktBuf) -> 869 | report_timer_set(delay_ack), 870 | Ref = gen_fsm:start_timer(?DELAYED_ACK_TIME_THRESHOLD, send_delayed_ack), 871 | {set, AckedBytes, Ref}; 872 | handle_delayed_ack({set, ByteCount, _Ref} = DelayAck, AckedBytes, Network, PktBuf) 873 | when ByteCount+AckedBytes >= ?DELAYED_ACK_BYTE_THRESHOLD -> 874 | utp_buffer:send_ack(Network, PktBuf), 875 | cancel_delayed_ack(DelayAck); 876 | handle_delayed_ack({set, ByteCount, Ref}, AckedBytes, _Network, _PktBuf) -> 877 | {set, ByteCount+AckedBytes, Ref}. 878 | 879 | %% @doc Consider if we should send out an ACK and do it if so 880 | %% @end 881 | handle_send_ack(Network, PktBuf, DelayAck, Messages, AckedBytes) -> 882 | case view_ack_messages(Messages) of 883 | nothing -> 884 | DelayAck; 885 | got_fin -> 886 | utp_buffer:send_ack(Network, PktBuf), 887 | cancel_delayed_ack(DelayAck); 888 | no_piggyback -> 889 | handle_delayed_ack(DelayAck, AckedBytes, Network, PktBuf); 890 | piggybacked -> 891 | %% The requested ACK is already sent as a piggyback on 892 | %% top of a data message. There is no reason to resend it. 893 | cancel_delayed_ack(DelayAck) 894 | end. 895 | 896 | view_ack_messages(Messages) -> 897 | case proplists:get_value(send_ack, Messages) of 898 | undefined -> 899 | nothing; 900 | true -> 901 | ack_analyze_further(Messages) 902 | end. 903 | 904 | ack_analyze_further(Messages) -> 905 | case proplists:get_value(got_fin, Messages) of 906 | true -> 907 | got_fin; 908 | undefined -> 909 | case proplists:get_value(no_piggyback, Messages) of 910 | true -> 911 | no_piggyback; 912 | undefined -> 913 | piggybacked 914 | end 915 | end. 916 | 917 | drain_buffer(L, #state { buffer = PktBuf, 918 | process = ProcInfo } = State) -> 919 | true = utp_process:recv_buffer_empty(ProcInfo), 920 | case utp_buffer:draining_receive(L, PktBuf) of 921 | {ok, Bin, N_PktBuf} -> 922 | utp:report_event(60, us, client, {ok, size(Bin)}, [Bin]), 923 | {ok, {ok, Bin}, State#state { buffer = N_PktBuf}}; 924 | empty -> 925 | utp:report_event(60, us, client, {error, eof}, []), 926 | {ok, {error, eof}, State}; 927 | {partial_read, Bin, N_PktBuf} -> 928 | utp:report_event(60, us, client, {error, {partial, size(Bin)}}, [Bin]), 929 | {ok, {error, {partial, Bin}}, State#state { buffer = N_PktBuf}} 930 | end. 931 | 932 | 933 | report_timer_clear(Type) -> 934 | utp:report_event(80, us, timer, {clear, Type}, []). 935 | 936 | report_timer_trigger(Type) -> 937 | utp:report_event(85, timer, us, {trigger, Type}, []). 938 | 939 | report_timer_set(Type) -> 940 | utp:report_event(80, us, timer, {set, Type}, []). 941 | 942 | report_timer_bump(Type) -> 943 | utp:report_event(80, us, timer, {bump, Type}, []). 944 | 945 | report(NewState) -> 946 | utp:report_event(55, us, NewState, []), 947 | NewState. 948 | 949 | -------------------------------------------------------------------------------- /src/gen_utp_worker_pool.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc Worker pool of gen_utp_workers 4 | %%% @end 5 | -module(gen_utp_worker_pool). 6 | 7 | -behaviour(supervisor). 8 | 9 | %% API 10 | -export([start_link/0]). 11 | -export([start_child/4]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -define(SERVER, ?MODULE). 17 | 18 | %%%=================================================================== 19 | 20 | %%-------------------------------------------------------------------- 21 | %% @doc 22 | %% Starts the supervisor 23 | %% 24 | %% @end 25 | %%-------------------------------------------------------------------- 26 | -spec start_link() -> {ok, pid()} | ignore | {error, term()}. 27 | start_link() -> 28 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 29 | 30 | %% @doc Start a worker child 31 | %% @end 32 | start_child(Socket, Addr, Port, Options) -> 33 | Pool = gproc:lookup_local_name(gen_utp_worker_pool), 34 | supervisor:start_child(Pool, [Socket, Addr, Port, Options]). 35 | 36 | %%%=================================================================== 37 | 38 | %% @private 39 | init([]) -> 40 | gproc:add_local_name(gen_utp_worker_pool), 41 | 42 | ChildSpec = {child, 43 | {gen_utp_worker, start_link, []}, 44 | temporary, 15000, worker, [gen_utp_worker]}, 45 | {ok, {{simple_one_for_one, 50, 3600}, [ChildSpec]}}. 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/utp.app.src: -------------------------------------------------------------------------------- 1 | {application, utp, 2 | [ 3 | {description, "The uTP protocol"}, 4 | {vsn, "0.1"}, 5 | {registered, 6 | [ 7 | ]}, 8 | {applications, [kernel, stdlib, crypto, gproc]}, 9 | {mod, {utp_app, []}}, 10 | {env, []} 11 | ] 12 | }. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/utp.erl: -------------------------------------------------------------------------------- 1 | -module(utp). 2 | 3 | -export([ 4 | start/1, 5 | start_app/1, start_app/2, 6 | 7 | report_event/4, report_event/5 8 | ]). 9 | 10 | %% @doc Manual startup of the uTP application 11 | start(Port) -> 12 | ok = ensure_started([sasl, gproc, crypto]), 13 | utp_sup:start_link(Port). 14 | 15 | start_app(Port) -> 16 | start_app(Port, []). 17 | 18 | start_app(Port, Opts) -> 19 | application:set_env(utp, udp_port, Port), 20 | application:set_env(utp, opts, Opts), 21 | ok = ensure_started([sasl, gproc, crypto]), 22 | application:start(utp). 23 | 24 | ensure_started([]) -> 25 | ok; 26 | ensure_started([App | R]) -> 27 | case application:start(App) of 28 | ok -> 29 | ensure_started(R); 30 | {error, {already_started, App}} -> 31 | ensure_started(R) 32 | end. 33 | 34 | report_event(DetailLevel, FromTo, Label, Contents) -> 35 | %% N.B External call 36 | ?MODULE:report_event(DetailLevel, FromTo, FromTo, Label, Contents). 37 | 38 | report_event(_DetailLevel, _From, _To, _Label, _Contents) -> 39 | hopefully_traced. 40 | 41 | -------------------------------------------------------------------------------- /src/utp_app.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc The UTP application driver 4 | %%% @end 5 | -module(utp_app). 6 | 7 | -behaviour(application). 8 | 9 | %% Application callbacks 10 | -export([start/2, stop/1]). 11 | 12 | %%%=================================================================== 13 | %%% Application callbacks 14 | %%%=================================================================== 15 | 16 | %% @private 17 | start(_StartType, _StartArgs) -> 18 | Port = case application:get_env(utp, udp_port) of 19 | {ok, P} -> P; 20 | undefined -> 3333 21 | end, 22 | Opts = case application:get_env(utp, opts) of 23 | {ok, O} -> O; 24 | undefined -> [] 25 | end, 26 | case utp_sup:start_link(Port, Opts) of 27 | {ok, Pid} -> 28 | {ok, Pid}; 29 | {error, Reason} -> 30 | {error, Reason} 31 | end. 32 | 33 | %% @private 34 | stop(_State) -> 35 | ok. 36 | -------------------------------------------------------------------------------- /src/utp_buffer.erl: -------------------------------------------------------------------------------- 1 | %% @doc Low level packet buffer management. 2 | -module(utp_buffer). 3 | 4 | -include("log.hrl"). 5 | -include("utp.hrl"). 6 | 7 | -export([ 8 | mk/1, 9 | 10 | init_counters/3, 11 | init_seqno/2, 12 | init_ackno/2, 13 | 14 | mk_random_seq_no/0, 15 | send_fin/2, 16 | send_ack/2, 17 | handle_packet/4, 18 | buffer_dequeue/1, 19 | buffer_putback/2, 20 | fill_window/3, 21 | 22 | advertised_window/1, 23 | 24 | extract_rtt/1, 25 | extract_payload_size/1, 26 | retransmit_packet/2 27 | ]). 28 | 29 | -export([view_zerowindow_reopen/2]). 30 | 31 | -export([draining_receive/2]). 32 | 33 | %% DEFINES 34 | %% ---------------------------------------------------------------------- 35 | 36 | %% The default RecvBuf size: 8K 37 | -define(OPT_RECV_BUF, 8192). 38 | -define(REORDER_BUFFER_MAX_SIZE, 511). 39 | 40 | %% The deafult delays of acks 41 | -define(DELAYED_ACK_BYTE_THRESHOLD, 2400). % bytes 42 | -define(DELAYED_ACK_TIME_THRESHOLD, 100). % ms 43 | 44 | %% TYPES 45 | %% ---------------------------------------------------------------------- 46 | -type message() :: send_ack. 47 | -type messages() :: [message()]. 48 | 49 | -record(pkt_wrap, { 50 | packet :: utp_proto:packet(), 51 | transmissions = 0 :: integer(), 52 | send_time = 0 :: integer(), 53 | need_resend = false :: boolean() 54 | }). 55 | -type pkt() :: #pkt_wrap{}. 56 | 57 | 58 | -record(buffer, { 59 | recv_buf = queue:new() :: queue(), 60 | reorder_buf = [] :: orddict:orddict(), 61 | %% When we have a working protocol, this retransmission queue is probably 62 | %% Optimization candidate 1 :) 63 | retransmission_queue = [] :: [#pkt_wrap{}], 64 | reorder_count = 0 :: integer(), % When and what to reorder 65 | next_expected_seq_no = 1 :: 0..16#FFFF, % Next expected packet 66 | seq_no = 1 :: 0..16#FFFF, % Next Sequence number to use when sending 67 | 68 | %% Did we receive a fin packet? 69 | fin_state = none :: none | {got_fin, 0..16#FFFF}, 70 | 71 | %% Packet buffer settings 72 | %% -------------------- 73 | %% Same, for the recv buffer 74 | opt_recv_buf_sz = ?OPT_RECV_BUF :: integer(), 75 | 76 | %% The maximal size of packets. 77 | %% @todo Discover this one 78 | pkt_size = 1000 :: integer() 79 | }). 80 | -opaque t() :: #buffer{}. 81 | 82 | %% Track send quota available 83 | -record(send_quota, { 84 | send_quota :: integer(), 85 | last_send_quota :: integer() 86 | }). 87 | -type quota() :: #send_quota{}. 88 | 89 | -export_type([pkt/0, 90 | t/0, 91 | messages/0, 92 | quota/0]). 93 | 94 | %% API 95 | %% ---------------------------------------------------------------------- 96 | 97 | %% PKT BUF INITIALIZATION 98 | %% ---------------------------------------------------------------------- 99 | mk(none) -> #buffer{}; 100 | mk(OptRecv) -> 101 | #buffer { 102 | opt_recv_buf_sz = OptRecv 103 | }. 104 | 105 | init_counters(#buffer{} = PBuf, SeqNo, NextExpected) 106 | when SeqNo >= 0, SeqNo < 65536, 107 | NextExpected >= 0, NextExpected < 65536 -> 108 | PBuf#buffer { seq_no = SeqNo, 109 | next_expected_seq_no = NextExpected}. 110 | 111 | init_seqno(#buffer {} = PBuf, SeqNo) when SeqNo >= 0, SeqNo < 65536-> 112 | PBuf#buffer { seq_no = SeqNo }. 113 | 114 | init_ackno(#buffer{} = PBuf, NextExpected) -> 115 | PBuf#buffer {next_expected_seq_no = NextExpected}. 116 | 117 | mk_random_seq_no() -> 118 | <> = crypto:rand_bytes(2), 119 | N. 120 | 121 | 122 | %% SEND SPECIFIC PACKET TYPES 123 | %% ---------------------------------------------------------------------- 124 | 125 | %% @doc Toss out an ACK packet on the Socket. 126 | %% @end 127 | send_ack(Network, 128 | #buffer { seq_no = SeqNo, 129 | next_expected_seq_no = AckNo 130 | } = Buf) -> 131 | %% @todo Send out an ack message here 132 | AckPacket = #packet { ty = st_state, 133 | seq_no = utp_util:bit16(SeqNo-1), % @todo Is this right? 134 | ack_no = utp_util:bit16(AckNo-1), % We are recording the next expected ack number 135 | extension = [] 136 | }, 137 | Win = advertised_window(Buf), 138 | case utp_network:send_pkt(Win, Network, AckPacket) of 139 | {ok, _} -> 140 | ok; 141 | {error, Reason} -> 142 | ?WARN([dropping_packet, {error, Reason}]), 143 | ok 144 | end. 145 | 146 | %% @doc Toss out a FIN packet on the Socket. 147 | %% @todo Reconsider this. It may be it should be a normally streamed pkt 148 | %% rather than this variant where we send a special packet with 149 | %% FIN set. 150 | %% @end 151 | send_fin(Network, Buf) -> 152 | send_packet(st_fin, <<>>, % Empty packet for now 153 | Buf, Network). 154 | 155 | send_packet(Bin, Buf, Network) -> 156 | send_packet(st_data, Bin, Buf, Network). 157 | 158 | send_packet(Ty, Bin, 159 | #buffer { seq_no = SeqNo, 160 | next_expected_seq_no = AckNo, 161 | retransmission_queue = RetransQueue } = Buf, 162 | Network) -> 163 | P = #packet { ty = Ty, 164 | seq_no = SeqNo, 165 | ack_no = utp_util:bit16(AckNo-1), 166 | extension = [], 167 | payload = Bin }, 168 | Win = advertised_window(Buf), 169 | {ok, SendTime} = utp_network:send_pkt(Win, Network, P), 170 | Wrap = #pkt_wrap { packet = P, 171 | transmissions = 1, 172 | send_time = SendTime, 173 | need_resend = false }, 174 | Buf#buffer { seq_no = utp_util:bit16(SeqNo+1), 175 | retransmission_queue = [Wrap | RetransQueue] 176 | }. 177 | 178 | %% RECEIVE PATH 179 | %% ---------------------------------------------------------------------- 180 | 181 | %% @doc Given a Sequence Number in a packet, validate it 182 | %% The `SeqNo' given is validated with respect to the current state of 183 | %% the connection. 184 | %% @end 185 | validate_seq_no(SeqNo, #buffer { next_expected_seq_no = NextExpected }) -> 186 | Diff = utp_util:bit16(SeqNo - NextExpected), 187 | DiffMinusOne = utp_util:bit16(SeqNo - (NextExpected - 1)), 188 | case Diff of 189 | _SeqAhead when DiffMinusOne == 0 -> 190 | {ok, no_data}; 191 | SeqAhead when SeqAhead >= ?REORDER_BUFFER_SIZE -> 192 | {error, is_far_in_future}; 193 | SeqAhead -> 194 | {ok, SeqAhead} 195 | end. 196 | 197 | %% @doc Assert that the current state is valid for Data packets 198 | %% @todo This may need to accept other packet types! 199 | %% @end 200 | -spec valid_state(atom()) -> ok. 201 | valid_state(State) -> 202 | case State of 203 | connected -> ok; 204 | fin_sent -> ok; 205 | _ -> throw({no_data, State}) 206 | end. 207 | 208 | %% @doc Consider if we should send out an ACK 209 | %% The Rule for ACK'ing is that the packet has altered the reorder buffer in any 210 | %% way for us. If the incoming packet has, we should let the other end know this. 211 | %% If the packet does not alter the reorder buffer however, we know it was either 212 | %% payload-less or duplicate (the latter is handled elsewhere). Payload-less packets 213 | %% are informational only, and if they generate ACK's it is not from this part of 214 | %% the code. 215 | %% @end 216 | consider_send_ack(#buffer { reorder_buf = RB1, 217 | next_expected_seq_no = Seq1 }, 218 | #buffer { reorder_buf = RB2, 219 | next_expected_seq_no = Seq2}) 220 | when RB1 =/= RB2 orelse Seq1 =/= Seq2 -> 221 | [{send_ack, true}]; 222 | consider_send_ack(_, _) -> []. 223 | 224 | %% @doc Update the receive buffer with Payload 225 | %% This function will update the receive buffer with some incoming payload. 226 | %% It will also return back to us a message if we should ack the incoming 227 | %% packet. As such, this function wraps some lower-level operations, 228 | %% with respect to incoming payload. 229 | %% @end 230 | handle_receive_buffer(SeqNo, Payload, PacketBuffer, State) -> 231 | case update_recv_buffer(SeqNo, Payload, PacketBuffer, State) of 232 | %% Force an ACK out in this case 233 | duplicate -> {PacketBuffer, [{send_ack, true}]}; 234 | {ok, #buffer{} = PB} -> {PB, consider_send_ack(PacketBuffer, PB)}; 235 | {got_fin, #buffer{} = PB} -> 236 | %% *Always* ACK the FIN packet! 237 | {PB, [{got_fin, true}, 238 | {send_ack, true}]} 239 | end. 240 | 241 | 242 | %% @doc Handle incoming Payload in datagrams 243 | %% A Datagram came in with SeqNo and Payload. This Payload and SeqNo 244 | %% updates the PacketBuffer if the SeqNo is valid for the current 245 | %% state of the connection. 246 | %% @end 247 | handle_incoming_datagram_payload(SeqNo, Payload, PacketBuffer, State) -> 248 | %% We got a packet in with a seq_no and some things to ack. 249 | %% Validate the sequence number. 250 | case validate_seq_no(SeqNo, PacketBuffer) of 251 | {ok, no_data} -> 252 | no_data; 253 | {ok, _Num} -> 254 | %% Handle the Payload by Dumping it into the packet buffer 255 | %% at the right point Returns a new PacketBuffer, and a 256 | %% list of Messages for the upper layer 257 | {ok, handle_receive_buffer(SeqNo, Payload, PacketBuffer, State)}; 258 | {error, Violation} -> 259 | throw({error, Violation}) 260 | end. 261 | 262 | 263 | 264 | 265 | %% @doc Update the Receive Buffer with Payload 266 | %% There are essentially two cases: Either the packet is the next 267 | %% packet in sequence, so we can simply push it directly to the 268 | %% receive buffer right away. Then we can check the reorder buffer to 269 | %% see if we can satisfy more packets from it. If it is not in 270 | %% sequence, it should go into the reorder buffer in the right spot. 271 | %% @end 272 | update_recv_buffer(SeqNo, <<>>, 273 | #buffer { fin_state = {got_fin, SeqNo}, 274 | next_expected_seq_no = SeqNo } = PacketBuffer, _State) -> 275 | {got_fin, PacketBuffer#buffer { next_expected_seq_no = utp_util:bit16(SeqNo+1)}}; 276 | update_recv_buffer(_SeqNo, <<>>, PB, _State) -> {ok, PB}; 277 | update_recv_buffer(SeqNo, Payload, #buffer { fin_state = {got_fin, SeqNo}, 278 | next_expected_seq_no = SeqNo } = PB, State) -> 279 | N_PB = recv_buffer_enqueue(State, Payload, PB), 280 | {got_fin, N_PB#buffer { next_expected_seq_no = utp_util:bit16(SeqNo+1)}}; 281 | update_recv_buffer(SeqNo, Payload, #buffer { next_expected_seq_no = SeqNo } = PB, State) -> 282 | N_PB = recv_buffer_enqueue(State, Payload, PB), 283 | satisfy_from_reorder_buffer( 284 | N_PB#buffer { next_expected_seq_no = utp_util:bit16(SeqNo+1) }, State); 285 | update_recv_buffer(SeqNo, Payload, PB, _State) when is_integer(SeqNo) -> 286 | reorder_buffer_in(SeqNo, Payload, PB). 287 | 288 | recv_buffer_enqueue(fin_sent, _, PB) -> PB; 289 | recv_buffer_enqueue(connected, Payload, PB) -> enqueue_payload(Payload, PB). 290 | 291 | %% @doc Try to satisfy the next_expected_seq_no directly from the reorder buffer. 292 | %% @end 293 | satisfy_from_reorder_buffer(#buffer { reorder_buf = [] } = PB, _State) -> 294 | {ok, PB}; 295 | satisfy_from_reorder_buffer(#buffer { next_expected_seq_no = AckNo, 296 | fin_state = {got_fin, AckNo}, 297 | reorder_buf = [{AckNo, PL} | R]} = PB, State) -> 298 | N_PB = recv_buffer_enqueue(State, PL, PB), 299 | {got_fin, N_PB#buffer { next_expected_seq_no = utp_util:bit16(AckNo+1), 300 | reorder_buf = R}}; 301 | satisfy_from_reorder_buffer(#buffer { next_expected_seq_no = AckNo, 302 | reorder_buf = [{AckNo, PL} | R]} = PB, 303 | State) -> 304 | N_PB = recv_buffer_enqueue(State, PL, PB), 305 | satisfy_from_reorder_buffer( 306 | N_PB#buffer { next_expected_seq_no = utp_util:bit16(AckNo+1), 307 | reorder_buf = R}, State); 308 | satisfy_from_reorder_buffer(#buffer { } = PB, _State) -> 309 | {ok, PB}. 310 | 311 | %% @doc Enter the packet into the reorder buffer, watching out for duplicates 312 | %% @end 313 | reorder_buffer_in(SeqNo, Payload, #buffer { reorder_buf = OD } = PB) -> 314 | case orddict:is_key(SeqNo, OD) of 315 | true -> duplicate; 316 | false -> {ok, PB#buffer { reorder_buf = orddict:store(SeqNo, Payload, OD) }} 317 | end. 318 | 319 | %% SEND PATH 320 | %% ---------------------------------------------------------------------- 321 | 322 | update_send_buffer(AckNo, #buffer { seq_no = NextSeqNo } = PB) -> 323 | SeqNo = utp_util:bit16(NextSeqNo - 1), 324 | WindowSize = send_window_count(PB), 325 | WindowStart = utp_util:bit16(SeqNo - WindowSize), 326 | case view_ack_no(AckNo, WindowStart, WindowSize) of 327 | {ok, AcksAhead} -> 328 | {Ret, AckedPs, PB1} = prune_acked(AcksAhead, WindowStart, PB), 329 | FinState = case Ret of 330 | ok -> []; 331 | fin_sent_acked -> [fin_sent_acked] 332 | end, 333 | {ok, FinState ++ view_ack_state(length(AckedPs), PB1), 334 | AckedPs, 335 | PB1}; 336 | {ack_is_old, _AcksAhead} -> 337 | {ok, [{old_ack, true}], [], PB} 338 | end. 339 | 340 | %% @doc Prune the retransmission queue for ACK'ed packets. 341 | %% Prune out all packets from `WindowStart' and `AcksAhead' in. Return a new packet 342 | %% buffer where the retransmission queue has been updated. 343 | %% @todo All this AcksAhead business, why? We could as well just work directly on 344 | %% the ack_no I think. 345 | %% @end 346 | prune_acked(AckAhead, WindowStart, 347 | #buffer { retransmission_queue = RQ } = PB) -> 348 | {AckedPs, N_RQ} = lists:partition( 349 | fun(#pkt_wrap { 350 | packet = #packet { seq_no = SeqNo } }) -> 351 | Distance = utp_util:bit16(SeqNo - WindowStart), 352 | Distance =< AckAhead 353 | end, 354 | RQ), 355 | 356 | RetState = case contains_st_fin(AckedPs) of 357 | true -> 358 | fin_sent_acked; 359 | false -> 360 | ok 361 | end, 362 | {RetState, AckedPs, PB#buffer { retransmission_queue = N_RQ }}. 363 | 364 | contains_st_fin([]) -> false; 365 | contains_st_fin([#pkt_wrap { 366 | packet = #packet { ty = st_fin }} | _]) -> 367 | true; 368 | contains_st_fin([_ | R]) -> 369 | contains_st_fin(R). 370 | 371 | view_ack_state(0, _PB) -> []; 372 | view_ack_state(N, PB) when is_integer(N) -> 373 | case has_inflight_data(PB) of 374 | true -> 375 | [{data_inflight, true}]; 376 | false -> 377 | [{all_acked, true}] 378 | end. 379 | 380 | has_inflight_data(#buffer { retransmission_queue = [] }) -> false; 381 | has_inflight_data(#buffer { retransmission_queue = [_|_] }) -> true. 382 | 383 | %% @doc View the state of the Ack 384 | %% Given the `AckNo' and when the `WindowStart' started, we scrutinize the Ack 385 | %% for correctness according to age. If the ACK is old, tell the caller. 386 | %% @end 387 | view_ack_no(AckNo, WindowStart, WindowSize) -> 388 | case utp_util:bit16(AckNo - WindowStart) of 389 | N when N > WindowSize -> 390 | %% The ack number is old, so do essentially nothing in the next part 391 | {ack_is_old, N}; 392 | N when is_integer(N) -> 393 | {ok, N} 394 | end. 395 | 396 | send_window_count(#buffer { retransmission_queue = RQ }) -> 397 | length(RQ). 398 | 399 | 400 | 401 | %% INCOMING PACKETS 402 | %% ---------------------------------------------------------------------- 403 | 404 | %% @doc Handle an incoming Packet 405 | %% We proceed to handle an incoming packet by first seeing if it has 406 | %% payload we are interested in, and if that payload advances our 407 | %% buffers in any way. Then, afterwards, we handle the AckNo and 408 | %% Advertised window of the packet to eventually send out more on the 409 | %% socket towards the other end. 410 | %% @end 411 | handle_packet(State, 412 | #packet { seq_no = SeqNo, 413 | ack_no = AckNo, 414 | payload = Payload, 415 | win_sz = WindowSize, 416 | ty = Type }, 417 | PktWindow, 418 | PacketBuffer) when PktWindow =/= undefined -> 419 | %% Assert that we are currently in a state eligible for receiving 420 | %% datagrams of this type. This assertion ought not to be 421 | %% triggered by our code. 422 | ok = valid_state(State), 423 | 424 | %% Some packets set a specific state we should handle in our end 425 | N_PacketBuffer = handle_packet_type(Type, SeqNo, PacketBuffer), 426 | 427 | %% Update the state by the receiving payload stuff. 428 | case handle_incoming_datagram_payload(SeqNo, Payload, N_PacketBuffer, State) of 429 | {ok, {N_PacketBuffer1, RecvMessages}} -> 430 | %% The Packet may have ACK'ed stuff from our send buffer. Update 431 | %% the send buffer accordingly 432 | {ok, SendMessages, AckedPs, N_PacketBuffer2} = 433 | update_send_buffer(AckNo, N_PacketBuffer1), 434 | 435 | {ok, N_PacketBuffer2, 436 | utp_network:handle_window_size(PktWindow, WindowSize), 437 | SendMessages ++ RecvMessages ++ [{acked, AckedPs}]}; 438 | no_data when Type == st_state orelse Type == st_data -> 439 | %% The packet has no data 440 | {ok, SendMessages, _AcksAhead, N_PacketBuffer2} = 441 | update_send_buffer(AckNo, N_PacketBuffer), 442 | 443 | {ok, N_PacketBuffer2, 444 | utp_network:handle_window_size(PktWindow, WindowSize), 445 | SendMessages} 446 | end. 447 | 448 | 449 | handle_packet_type(Type, SeqNo, Buf) -> 450 | case Type of 451 | st_fin -> 452 | et:trace_me(50, none, none, fin, [saw_st_fin, SeqNo]), 453 | Buf#buffer { fin_state = {got_fin, SeqNo} }; 454 | st_data -> 455 | Buf; 456 | st_state -> 457 | Buf 458 | end. 459 | 460 | %% PACKET TRANSMISSION 461 | %% ---------------------------------------------------------------------- 462 | 463 | %% @doc Build up a queue of payload to send 464 | %% This function builds up to `N' bytes to send out -- each packet up 465 | %% to the packet size. The functions satisfies data from the 466 | %% process_queue of processes waiting to get data sent. It returns an 467 | %% updates ProcessQueue record and a `queue' of the packets that are 468 | %% going out. 469 | %% @end 470 | fill_from_proc_queue(N, Buf, ProcQ) -> 471 | TxQ = queue:new(), 472 | fill_from_proc_queue(N, Buf#buffer.pkt_size, TxQ, ProcQ). 473 | 474 | %% @doc Worker for fill_from_proc_queue/3 475 | %% @end 476 | -spec fill_from_proc_queue(integer(), 477 | integer(), 478 | queue(), 479 | utp_process:t()) -> 480 | {window_maxed_out | ok, queue(), utp_process:t()}. 481 | fill_from_proc_queue(0, _Sz, Q, Proc) -> 482 | {window_maxed_out, Q, Proc}; 483 | fill_from_proc_queue(N, MaxPktSz, Q, Proc) -> 484 | ToFill = case N =< MaxPktSz of 485 | true -> N; 486 | false -> MaxPktSz 487 | end, 488 | case utp_process:fill_via_send_queue(ToFill, Proc) of 489 | {filled, Bin, Proc1} -> 490 | fill_from_proc_queue(N - ToFill, MaxPktSz, queue:in(Bin, Q), Proc1); 491 | {partial, Bin, Proc1} -> 492 | {ok, queue:in(Bin, Q), Proc1}; 493 | zero -> 494 | {ok, Q, Proc} 495 | end. 496 | 497 | %% @doc Given a queue of things to send, transmit packets from it 498 | %% @end 499 | transmit_queue(Q, Buf, Network) -> 500 | L = queue:to_list(Q), 501 | lists:foldl(fun(Data, B) -> 502 | send_packet(Data, B, Network) 503 | end, 504 | Buf, 505 | L). 506 | 507 | %% @doc Fill up the Window with packets in the outgoing direction 508 | %% @end 509 | fill_window(Network, ProcQueue, PktBuf) -> 510 | FreeInWindow = bytes_free_in_window(PktBuf, Network), 511 | %% Fill a queue of stuff to transmit 512 | {Res, TxQueue, NProcQueue} = fill_from_proc_queue(FreeInWindow, PktBuf, ProcQueue), 513 | MaxOut = case Res of 514 | ok -> 515 | []; 516 | window_maxed_out -> 517 | [window_maxed_out] 518 | end, 519 | %% Send out the queue of packets to transmit 520 | NBuf1 = transmit_queue(TxQueue, PktBuf, Network), 521 | %% Eventually shove the Nagled packet in the tail 522 | Result = case queue:is_empty(TxQueue) of 523 | true -> 524 | [no_piggyback]; 525 | false -> 526 | utp:report_event(90, us, sent_data, []), 527 | [sent_data] 528 | end, 529 | {Result ++ MaxOut, NBuf1, NProcQueue}. 530 | 531 | %% PACKET RETRANSMISSION 532 | %% ---------------------------------------------------------------------- 533 | 534 | retransmit_packet(PktBuf, Network) -> 535 | {Oldest, Rest} = pick_oldest_packet(PktBuf), 536 | #pkt_wrap { packet = Pkt, 537 | transmissions = N } = Oldest, 538 | Win = advertised_window(PktBuf), 539 | {ok, SendTime} = utp_network:send_pkt(Win, Network, Pkt), 540 | Wrap = Oldest#pkt_wrap { transmissions = N+1, 541 | send_time = SendTime}, 542 | PktBuf#buffer { retransmission_queue = [Wrap | Rest] }. 543 | 544 | pick_oldest_packet(#buffer { retransmission_queue = [Candidate | R] }) -> 545 | pick_oldest_packet(Candidate, R, []). 546 | 547 | pick_oldest_packet(Candidate, [], Accum) -> 548 | {Candidate, lists:reverse(Accum)}; 549 | pick_oldest_packet(#pkt_wrap { packet = P1 } = C, [#pkt_wrap { packet = P2 } = W | R], Accum) -> 550 | case utp_socket:order_packets(P1, P2) of 551 | [P1, P2] -> 552 | pick_oldest_packet(C, R, [W | Accum]); 553 | [P2, P1] -> 554 | pick_oldest_packet(W, R, [C | Accum]) 555 | end. 556 | 557 | %% INTERNAL FUNCTIONS 558 | %% ---------------------------------------------------------------------- 559 | 560 | %% @doc Return the size of the receive buffer 561 | %% @end 562 | recv_buf_size(Q) -> 563 | L = queue:to_list(Q), 564 | lists:sum([byte_size(Payload) || Payload <- L]). 565 | 566 | %% @doc Calculate the advertised window to use 567 | %% @end 568 | advertised_window(#buffer { recv_buf = Q, 569 | opt_recv_buf_sz = Sz }) -> 570 | FillValue = recv_buf_size(Q), 571 | case Sz - FillValue of 572 | N when N >= 0 -> 573 | N; 574 | N when N < 0 -> 575 | 0 % Case happens when the sender forces a packet through 576 | end. 577 | 578 | payload_size(#pkt_wrap { packet = Packet }) -> 579 | byte_size(Packet#packet.payload). 580 | 581 | 582 | view_inflight_bytes(#buffer{ retransmission_queue = [] }) -> 583 | buffer_empty; 584 | view_inflight_bytes(#buffer{ retransmission_queue = Q }) -> 585 | case lists:sum([payload_size(Pkt) || Pkt <- Q]) of 586 | Sum -> 587 | {ok, Sum} 588 | end. 589 | 590 | bytes_free_in_window(PktBuf, Network) -> 591 | MaxSend = utp_network:max_window_send(Network), 592 | case view_inflight_bytes(PktBuf) of 593 | buffer_empty -> 594 | MaxSend; 595 | {ok, Inflight} when Inflight =< MaxSend -> 596 | MaxSend - Inflight; 597 | {ok, _Inflight} -> 598 | 0 599 | end. 600 | 601 | enqueue_payload(Payload, #buffer { recv_buf = Q } = PB) -> 602 | PB#buffer { recv_buf = queue:in(Payload, Q) }. 603 | 604 | buffer_putback(B, #buffer { recv_buf = Q } = Buf) -> 605 | Buf#buffer { recv_buf = queue:in_r(B, Q) }. 606 | 607 | buffer_dequeue(#buffer { recv_buf = Q } = Buf) -> 608 | case queue:out(Q) of 609 | {{value, E}, Q1} -> 610 | {ok, E, Buf#buffer { recv_buf = Q1 }}; 611 | {empty, _} -> 612 | empty 613 | end. 614 | 615 | extract_rtt(Packets) -> 616 | [TS || #pkt_wrap { send_time = TS} = P <- Packets, 617 | P#pkt_wrap.transmissions == 1]. 618 | 619 | extract_payload_size(Packets) -> 620 | lists:sum([byte_size(Pl) || #pkt_wrap { packet = #packet { payload = Pl } } <- Packets]). 621 | 622 | 623 | 624 | 625 | 626 | 627 | view_zerowindow_reopen(Old, New) -> 628 | N = advertised_window(Old), 629 | K = advertised_window(New), 630 | N == 0 andalso K > 1000. % Only open up the window when we have processed a considerable amount 631 | 632 | draining_receive(L, PktBuf) -> 633 | case buffer_dequeue(PktBuf) of 634 | empty -> 635 | empty; 636 | {ok, Bin, N_Buffer} when byte_size(Bin) > L -> 637 | <> = Bin, 638 | {ok, Cut, buffer_putback(Rest, N_Buffer)}; 639 | {ok, Bin, N_Buffer} when byte_size(Bin) == L -> 640 | {ok, Bin, N_Buffer}; 641 | {ok, Bin, N_Buffer} when byte_size(Bin) < L -> 642 | case draining_receive(L - byte_size(Bin), N_Buffer) of 643 | empty -> 644 | {partial_read, Bin, N_Buffer}; 645 | {ok, Bin2, N_Buffer2} -> 646 | {ok, <>, N_Buffer2}; 647 | {partial_read, Bin2, N_Buffer} -> 648 | {partial_read, <>, N_Buffer} 649 | end 650 | end. 651 | -------------------------------------------------------------------------------- /src/utp_file_map.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Jesper Louis andersen <> 3 | %%% @copyright (C) 2011, Jesper Louis andersen 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 11 Aug 2011 by Jesper Louis andersen <> 8 | %%%------------------------------------------------------------------- 9 | -module(utp_file_map). 10 | 11 | -behaviour(gen_server). 12 | 13 | %% API 14 | -export([start_link/1, 15 | cmd/1]). 16 | 17 | %% gen_server callbacks 18 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 19 | terminate/2, code_change/3]). 20 | 21 | -define(SERVER, ?MODULE). 22 | 23 | -record(state, { file_map :: dict() }). 24 | 25 | %%%=================================================================== 26 | 27 | %% @doc 28 | %% Starts the server 29 | %% @end 30 | %%-------------------------------------------------------------------- 31 | start_link(DirToServe) -> 32 | gen_server:start_link({local, ?SERVER}, ?MODULE, [DirToServe], []). 33 | 34 | cmd(CMD) -> 35 | call({cmd, CMD}). 36 | 37 | %%%=================================================================== 38 | 39 | %% @private 40 | init([DirToServe]) -> 41 | FileMap = make_file_map(DirToServe), 42 | {ok, #state{ file_map = FileMap }}. 43 | 44 | %% @private 45 | handle_call({cmd, {get, H}}, _From, #state { file_map = FMap } = State) -> 46 | case dict:find(H, FMap) of 47 | error -> 48 | {reply, {error, not_found}, State}; 49 | {ok, FName} -> 50 | {reply, {ok, read(FName)}, State} 51 | end; 52 | handle_call({cmd, ls}, _From, #state { file_map = FMap } = State) -> 53 | Keys = dict:fetch_keys(FMap), 54 | {reply, {ok, Keys}, State}; 55 | handle_call(_Request, _From, State) -> 56 | Reply = ok, 57 | {reply, Reply, State}. 58 | 59 | %% @private 60 | handle_cast(_Msg, State) -> 61 | {noreply, State}. 62 | 63 | %% @private 64 | handle_info(_Info, State) -> 65 | {noreply, State}. 66 | 67 | %% @private 68 | terminate(_Reason, _State) -> 69 | ok. 70 | 71 | %% @private 72 | code_change(_OldVsn, State, _Extra) -> 73 | {ok, State}. 74 | 75 | %%%=================================================================== 76 | 77 | call(X) -> 78 | gen_server:call(?MODULE, X, infinity). 79 | 80 | read(FName) -> 81 | {ok, Data} = file:read_file(FName), 82 | Data. 83 | 84 | make_file_map(Dir) -> 85 | FileNames = filelib:fold_files(Dir, 86 | ".*\.jpg", false, 87 | fun(FN, Acc) -> 88 | [FN | Acc] 89 | end, 90 | []), 91 | L = [begin 92 | F = filename:basename(File), 93 | Path = filename:join(Dir, File), 94 | {F, Path} 95 | end || File <- FileNames], 96 | 97 | dict:from_list(L). 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/utp_filter.erl: -------------------------------------------------------------------------------- 1 | -module(utp_filter). 2 | 3 | -export([start/0, start/1, 4 | clear/1]). 5 | 6 | 7 | start() -> 8 | start([]). 9 | 10 | start(ExtraOptions) -> 11 | Options = 12 | [{event_order, event_ts}, 13 | {scale, 2}, 14 | {max_actors, 10}, 15 | {detail_level, 90}, 16 | {actors, [client, timer, us, peer]}, 17 | {trace_pattern, {utp, max}}, 18 | {trace_global, true}, 19 | {title, "uTP tracer"} | ExtraOptions], 20 | et_viewer:start(Options). 21 | 22 | 23 | clear(P) -> 24 | ColPid = et_viewer:get_collector_pid(P), 25 | ok = et_collector:clear_table(ColPid). 26 | -------------------------------------------------------------------------------- /src/utp_ledbat.erl: -------------------------------------------------------------------------------- 1 | %% @doc LEDBAT calculations 2 | %% 3 | %% == Overview == 4 | %% The code in this module maintains a LEDBAT calculation based upon 5 | %% delay samples. A sample is the difference between two clocks, A and 6 | %% B which we assume are moving onwards at the same speed, though we 7 | %% do account for skew. 8 | %% 9 | %% The difference between our and their clock (A and B) is our base 10 | %% delay. Any deviation from this difference in the positive direction 11 | %% means we have a queuing delay of that amount. A deviation in the 12 | %% negative direction means that we have to update our base delay to 13 | %% the new value. 14 | %% 15 | %% == Base Delay and Current Delay == 16 | %% When we take in samples, we store these samples historically. This 17 | %% is done such that small perturbances in the delay will not affect 18 | %% the greater scheme of things. We track two kinds of delays: The 19 | %% base history and the current (queue) history. Every minute, we 20 | %% update and rotate the history tables so we never latch onto a 21 | %% simplified view of the real world. 22 | %% @end 23 | -module(utp_ledbat). 24 | 25 | -define(BASE_DELAY_HISTORY_SIZE, 12). 26 | -define(CUR_DELAY_SIZE, 2). 27 | 28 | -export([ 29 | mk/1, 30 | add_sample/2, 31 | shift/2, 32 | base_delay/1, 33 | get_value/1, 34 | clock_tick/1, 35 | compare_less/2 36 | ]). 37 | 38 | -record(ledbat, { 39 | base_history_q :: queue(), 40 | delay_base :: integer(), 41 | last_sample :: integer(), 42 | cur_delay_history_q :: queue() }). 43 | 44 | -opaque t() :: #ledbat{}. 45 | 46 | -export_type([ 47 | t/0 48 | ]). 49 | 50 | %% @doc Create a new LEDBAT structure based upon the first sample 51 | %% @end 52 | mk(Sample) -> 53 | BaseQueue = lists:foldr(fun(_E, Q) -> queue:in(Sample, Q) end, 54 | queue:new(), 55 | lists:seq(1, ?BASE_DELAY_HISTORY_SIZE)), 56 | DelayQueue = lists:foldr(fun(_E, Q) -> queue:in(0, Q) end, 57 | queue:new(), 58 | lists:seq(1, ?CUR_DELAY_SIZE)), 59 | #ledbat { base_history_q = BaseQueue, 60 | delay_base = Sample, 61 | last_sample = Sample, 62 | cur_delay_history_q = DelayQueue}. 63 | 64 | %% @doc Shift the base delay by an `Offset' amount. Note that 65 | %% shifting by a positive value moves the base delay such that the 66 | %% queueing delay gets smaller. This is used in several spots to 67 | %% account for the fact that we just realized our queueing delay is 68 | %% too large. To make it smaller we shift the base delay up, which 69 | %% affects the queueing delay down. 70 | %% @end 71 | shift(#ledbat { base_history_q = BQ } = LEDBAT, Offset) -> 72 | New_Queue = queue_map(fun(E) -> 73 | utp_util:bit32(E + Offset) 74 | end, 75 | BQ), 76 | LEDBAT#ledbat { base_history_q = New_Queue }. 77 | 78 | %% @doc Add a new sample to the LEDBAT structure 79 | %% @end 80 | add_sample(none, Sample) -> 81 | mk(Sample); 82 | add_sample(#ledbat { base_history_q = BQ, 83 | delay_base = DelayBase, 84 | cur_delay_history_q = DQ } = LEDBAT, Sample) -> 85 | {{value, BaseIncumbent}, BQ2} = queue:out(BQ), 86 | N_BQ = case compare_less(Sample, BaseIncumbent) of 87 | true -> 88 | queue:in_r(Sample, BQ2); 89 | false -> 90 | BQ 91 | end, 92 | N_DelayBase = case compare_less(Sample, DelayBase) of 93 | true -> Sample; 94 | false -> DelayBase 95 | end, 96 | Delay = utp_util:bit32(Sample - N_DelayBase), 97 | N_DQ = update_history(Delay, DQ), 98 | LEDBAT#ledbat { base_history_q = N_BQ, 99 | delay_base = Delay, 100 | last_sample = Sample, 101 | cur_delay_history_q = N_DQ }. 102 | 103 | %% @doc Bump the internal structure by rotating the history tables 104 | %% @end 105 | clock_tick(none) -> 106 | none; 107 | clock_tick(#ledbat{ base_history_q = BaseQ, 108 | last_sample = Sample, 109 | delay_base = DelayBase } = LEDBAT) -> 110 | N_BaseQ = queue:in_r(Sample, queue:drop(rotate(BaseQ))), 111 | N_DelayBase = minimum_by(fun compare_less/2, queue:to_list(N_BaseQ)), 112 | LEDBAT#ledbat { base_history_q = N_BaseQ, 113 | delay_base = min(N_DelayBase, DelayBase) }. 114 | 115 | %% @doc Fetch the delay base of the LEDBAT structure 116 | %% @end 117 | base_delay(#ledbat { delay_base = DB}) -> 118 | DB. 119 | 120 | %% @doc Get out the current estimate. 121 | %% @end 122 | get_value(#ledbat { cur_delay_history_q = DelayQ }) -> 123 | lists:min(queue:to_list(DelayQ)). 124 | 125 | %% @doc Compare if L < R taking wrapping into account 126 | %% @end 127 | compare_less(L, R) -> 128 | %% To see why this is correct, imagine a unit circle 129 | %% One direction is walking downwards from the L clockwise until 130 | %% we hit the R 131 | %% The other direction is walking upwards, counter-clockwise 132 | Down = utp_util:bit32(L - R), 133 | Up = utp_util:bit32(R - L), 134 | 135 | %% If the walk-up distance is the shortest, L < R, otherwise R < L 136 | Up < Down. 137 | 138 | 139 | %% ---------------------------------------------------------------------- 140 | update_history(Sample, Queue) -> 141 | {{value, _ThrowAway}, RestOfQueue} = queue:out(Queue), 142 | queue:in(Sample, RestOfQueue). 143 | 144 | minimum_by(_F, []) -> 145 | error(badarg); 146 | minimum_by(F, [H | T]) -> 147 | minimum_by(F, T, H). 148 | 149 | minimum_by(_Comparator, [], M) -> 150 | M; 151 | minimum_by(Comparator, [H | T], M) -> 152 | case Comparator(H, M) of 153 | true -> 154 | minimum_by(Comparator, T, H); 155 | false -> 156 | minimum_by(Comparator, T, M) 157 | end. 158 | 159 | rotate(Q) -> 160 | {{value, E}, RQ} = queue:out(Q), 161 | queue:in(E, RQ). 162 | 163 | queue_map(F, Q) -> 164 | L = queue:to_list(Q), 165 | queue:from_list(lists:map(F, L)). 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/utp_network.erl: -------------------------------------------------------------------------------- 1 | %% @doc Handle anything network-related 2 | %% This module handles everything 3 | %% network-related on an uTP socket. This includes the peer 4 | %% information and the static information present on a socket. 5 | %% @end 6 | -module(utp_network). 7 | 8 | -include("utp.hrl"). 9 | -include("log.hrl"). 10 | 11 | -export([ 12 | handle_window_size/2, 13 | handle_advertised_window/2, 14 | handle_maxed_out_window/2, 15 | mk/2, 16 | 17 | update_reply_micro/2, 18 | 19 | ack_packet_rtt/3, 20 | update_rtt_ledbat/2, 21 | update_our_ledbat/2, 22 | update_window_maxed_out/1, 23 | bump_ledbat/1, 24 | 25 | view_zero_window/1, 26 | bump_window/1, 27 | rto/1, 28 | max_window_send/1, 29 | congestion_control/2, 30 | hostname_port/1, 31 | set_conn_id/2, 32 | decay_window/1, 33 | reset_window/1, 34 | 35 | handle_clock_skew/2, 36 | handle_estimate_exceed/1 37 | ]). 38 | 39 | -export([ 40 | send_pkt/3, send_pkt/4 41 | ]). 42 | 43 | -export([update_window/6]). 44 | 45 | -record(network, { 46 | %% Static Socket info information 47 | sock_info :: utp_socket:t(), 48 | 49 | %% Size of the window the Peer advertises to us. 50 | peer_advertised_window = 4096 :: integer(), 51 | 52 | %% The current window size in the send direction, in bytes. 53 | cur_window :: integer(), 54 | %% Maximal window size int the send direction, in bytes. 55 | %% Also known as the current congestion window 56 | cwnd :: integer(), 57 | 58 | %% Current packet size. We can alter the packet size if we want, but we 59 | %% cannot repacketize. 60 | packet_size :: integer(), 61 | 62 | %% Current value to reply back to the other end 63 | reply_micro :: integer(), 64 | 65 | %% Round trip time measurements and LEDBAT 66 | min_rtt = 30000000 :: integer(), 67 | round_trip :: utp_rtt:t() | none, 68 | rtt_ledbat = none :: none | utp_ledbat:t(), 69 | our_ledbat = none :: none | utp_ledbat:t(), 70 | their_ledbat = none :: none | utp_ledbat:t(), 71 | %% Timeouts, 72 | %% -------------------- 73 | %% Set when we update the zero window to 0 so we can reset it to 1. 74 | zero_window_timeout :: none | {set, reference()}, 75 | 76 | %% Timestamps 77 | %% -------------------- 78 | %% When was the window last totally full (in send direction) 79 | last_maxed_out_window :: integer(), 80 | last_window_decay :: integer() 81 | }). 82 | 83 | 84 | -opaque({t,{type,{82,16},record,[{atom,{82,17},network}]},[]}). 85 | -export_type([t/0]). 86 | 87 | -define(INITIAL_CWND, 3000). 88 | -define(MAX_WINDOW_DECAY, 100). % ms, we can only decay the window at this time 89 | %% ---------------------------------------------------------------------- 90 | -spec mk(integer(), 91 | utp_socket:t()) -> t(). 92 | mk(PacketSize, SockInfo) -> 93 | Now = utp_proto:current_time_ms(), 94 | #network { packet_size = PacketSize, 95 | sock_info = SockInfo, 96 | reply_micro = 0, 97 | round_trip = none, 98 | cwnd = ?INITIAL_CWND, 99 | 100 | last_maxed_out_window = Now - 300, 101 | last_window_decay = Now 102 | }. 103 | 104 | update_window_maxed_out(#network {} = NW) -> 105 | NW#network { 106 | last_maxed_out_window = utp_proto:current_time_ms() 107 | }. 108 | 109 | update_reply_micro(#network { their_ledbat = TL } = SockInfo, RU) -> 110 | SockInfo#network { reply_micro = RU, 111 | their_ledbat = utp_ledbat:add_sample(TL, RU) }. 112 | 113 | handle_advertised_window(Network, #packet { win_sz = Win }) -> 114 | handle_advertised_window(Network, Win); 115 | handle_advertised_window(#network{} = Network, NewWin) 116 | when is_integer(NewWin) -> 117 | Network#network { peer_advertised_window = NewWin }. 118 | 119 | handle_window_size(#network {} = PKI, WindowSize) -> 120 | PKI#network { peer_advertised_window = WindowSize }. 121 | 122 | max_window_send(#network { peer_advertised_window = AdvertisedWindow, 123 | sock_info = SI, 124 | cwnd = MaxSendWindow }) -> 125 | SendBufSz = utp_socket:send_buf_sz(SI), 126 | Min = lists:min([SendBufSz, AdvertisedWindow, MaxSendWindow]), 127 | utp_trace:trace(max_window, Min), 128 | Min. 129 | 130 | view_zero_window(#network { peer_advertised_window = N }) when N > 0 -> 131 | ok; % There is no reason to update the window 132 | view_zero_window(#network { peer_advertised_window = 0 }) -> 133 | zero. 134 | 135 | set_conn_id(ConnIDSend, #network { sock_info = SI } = NW) -> 136 | N = utp_socket:set_conn_id(ConnIDSend, SI), 137 | NW#network { sock_info = N }. 138 | 139 | bump_window(#network {} = Win) -> 140 | PacketSize = utp_socket:packet_size(todo), 141 | Win#network { 142 | peer_advertised_window = PacketSize 143 | }. 144 | 145 | hostname_port(#network { sock_info = SI}) -> 146 | utp_socket:hostname_port(SI). 147 | 148 | send_pkt(AdvWin, #network { sock_info = SockInfo } = Network, Packet) -> 149 | send_pkt(AdvWin, Network, Packet, utp_socket:conn_id(SockInfo)). 150 | 151 | send_pkt(AdvWin, #network { sock_info = SockInfo } = Network, Packet, conn_id_recv) -> 152 | send_pkt(AdvWin, Network, Packet, utp_socket:conn_id_recv(SockInfo)); 153 | send_pkt(AdvWin, 154 | #network { sock_info = SockInfo, 155 | reply_micro = TSDiff}, Packet, ConnId) 156 | when is_integer(ConnId) -> 157 | Pkt = Packet#packet { conn_id = ConnId, 158 | win_sz = AdvWin }, 159 | utp_socket:send_pkt(SockInfo, Pkt, TSDiff). 160 | 161 | rto(#network { round_trip = RTT }) -> 162 | utp_rtt:rto(RTT). 163 | 164 | ack_packet_rtt(#network { round_trip = RTT, 165 | min_rtt = MinRTT, 166 | rtt_ledbat = LedbatHistory } = NW, 167 | TimeSent, TimeAcked) -> 168 | {ok, _NewRTO, NewRTT, NewHistory} = utp_rtt:ack_packet(LedbatHistory, 169 | RTT, 170 | TimeSent, 171 | TimeAcked), 172 | NW#network { round_trip = NewRTT, 173 | min_rtt = min(TimeAcked - TimeSent, MinRTT), 174 | rtt_ledbat = NewHistory}. 175 | 176 | 177 | update_rtt_ledbat(#network { rtt_ledbat = none } = SockInfo, Sample) -> 178 | SockInfo#network { rtt_ledbat = utp_ledbat:mk(Sample) }; 179 | update_rtt_ledbat(#network { rtt_ledbat = Ledbat } = SockInfo, Sample) -> 180 | SockInfo#network { rtt_ledbat = utp_ledbat:add_sample(Ledbat, Sample) }. 181 | 182 | update_our_ledbat(#network { our_ledbat = none } = SockInfo, Sample) -> 183 | SockInfo#network { our_ledbat = utp_ledbat:mk(Sample) }; 184 | update_our_ledbat(#network { our_ledbat = Ledbat } = SockInfo, Sample) -> 185 | SockInfo#network { our_ledbat = utp_ledbat:add_sample(Ledbat, Sample) }. 186 | 187 | bump_ledbat(#network { rtt_ledbat = L, 188 | their_ledbat = Their, 189 | our_ledbat = Our} = SockInfo) -> 190 | SockInfo#network { rtt_ledbat = utp_ledbat:clock_tick(L), 191 | their_ledbat = utp_ledbat:clock_tick(Their), 192 | our_ledbat = utp_ledbat:clock_tick(Our)}. 193 | 194 | -define(CONGESTION_CONTROL_TARGET, 100). % ms, perhaps we should run this in us 195 | -define(MAX_CWND_INCREASE_BYTES_PER_RTT, 3000). % bytes 196 | -define(MIN_WINDOW_SIZE, 3000). % bytes 197 | congestion_control(#network {} = NW, 0) -> 198 | NW; %% Nothing acked, so skip maintaining the congestion control 199 | congestion_control(#network { cwnd = Cwnd, 200 | our_ledbat = OurHistory, 201 | sock_info = SockInfo, 202 | min_rtt = MinRtt, 203 | last_maxed_out_window = LastMaxedOutTime } = Network, 204 | BytesAcked) when BytesAcked > 0 -> 205 | case MinRtt of 206 | K when K > 0 -> 207 | ignore; 208 | K -> 209 | error({min_rtt_violated, K}) 210 | end, 211 | OurDelay = 212 | case min(MinRtt, utp_ledbat:get_value(OurHistory)) of 213 | O when O >= 0 -> 214 | O; 215 | Otherwise -> 216 | error({our_delay_violated, Otherwise}) 217 | end, 218 | utp_trace:trace(queue_delay, OurDelay), 219 | TargetDelay = ?CONGESTION_CONTROL_TARGET, 220 | 221 | TargetOffset = TargetDelay - OurDelay, 222 | utp_trace:trace(target_offset, TargetOffset), 223 | %% Compute the Window Factor. The window might have shrunk since 224 | %% last time, so take the minimum of the bytes acked and the 225 | %% window maximum. Divide by the maximal value of the Windows and 226 | %% the bytes acked. This will yield a ratio which tells us how 227 | %% full the window is. If the window is 30K and we just acked 10K, 228 | %% then this value will be 10/30 = 1/3 meaning we have just acked 229 | %% 1/3 of the window. If the window has shrunk, the same holds, 230 | %% but opposite. We must make sure that only the size of the 231 | %% window is considered, so we track the minimum. that is, if the 232 | %% window has shrunk from 30 to 10, we only allow an update of the 233 | %% size 1/3 because this is the factor we can safely consider. 234 | WindowFactor = min(BytesAcked, Cwnd) / max(Cwnd, BytesAcked), 235 | 236 | %% The delay factor is how much we are off the target: 237 | DelayFactor = TargetOffset / TargetDelay, 238 | 239 | %% How much is the scaled gain? 240 | ScaledGain = ?MAX_CWND_INCREASE_BYTES_PER_RTT * WindowFactor * DelayFactor, 241 | 242 | case ScaledGain =< 1 + ?MAX_CWND_INCREASE_BYTES_PER_RTT * min(BytesAcked, Cwnd) 243 | / max(Cwnd, BytesAcked) of 244 | true -> ignore; 245 | false -> 246 | error({scale_gain_violation, ScaledGain, BytesAcked, Cwnd}) 247 | end, 248 | 249 | Alteration = case consider_last_maxed_window(LastMaxedOutTime) of 250 | too_soon -> 251 | 0; 252 | ok -> 253 | ScaledGain 254 | end, 255 | NewCwnd = clamp(Cwnd + Alteration, ?MIN_WINDOW_SIZE, utp_socket:send_buf_sz(SockInfo)), 256 | 257 | utp_trace:trace(congestion_window, NewCwnd), 258 | Network#network { 259 | cwnd = round(NewCwnd) 260 | }. 261 | 262 | 263 | clamp(Val, Min, _Max) when Val < Min -> Min; 264 | clamp(Val, _Min, Max) when Val > Max -> Max; 265 | clamp(Val, _Min, _Max) -> Val. 266 | 267 | consider_last_maxed_window(LastMaxedOutTime) -> 268 | Now = utp_proto:current_time_ms(), 269 | case Now - LastMaxedOutTime > 300 of 270 | true -> 271 | too_soon; 272 | false -> 273 | ok 274 | end. 275 | 276 | reset_window(#network { 277 | packet_size = PacketSize 278 | } = NW) -> 279 | NW#network { cwnd = PacketSize }. 280 | 281 | 282 | decay_window(#network { 283 | last_window_decay = LastDecay, 284 | cwnd = Cwnd 285 | } = Network) -> 286 | Now = utp_proto:current_time_ms(), 287 | case Now - LastDecay of 288 | K when K >= ?MAX_WINDOW_DECAY -> 289 | Network#network { 290 | last_window_decay = Now, 291 | cwnd = max(round(Cwnd * 0.5), ?MIN_WINDOW_SIZE) 292 | }; 293 | _Otherwise -> 294 | Network 295 | end. 296 | 297 | handle_estimate_exceed(#network { min_rtt = MinRtt, 298 | our_ledbat = Ours 299 | } = NW) -> 300 | OurDelay = utp_ledbat:get_value(Ours), 301 | Diff = OurDelay - MinRtt, 302 | case Diff of 303 | K when K > 0 -> 304 | NW#network { 305 | our_ledbat = utp_ledbat:shift(Ours, K) }; 306 | _Otherwise -> 307 | NW 308 | end. 309 | 310 | handle_clock_skew(#network { their_ledbat = none }, NW) -> 311 | NW; 312 | handle_clock_skew(#network { 313 | their_ledbat = OldTheirs }, 314 | #network { 315 | their_ledbat = Theirs, 316 | our_ledbat = Ours 317 | } = NW) -> 318 | OldDelayBase = utp_ledbat:base_delay(OldTheirs), 319 | TheirBase = utp_ledbat:base_delay(Theirs), 320 | Diff = OldDelayBase - TheirBase, 321 | case utp_ledbat:compare_less( 322 | TheirBase, 323 | OldDelayBase) of 324 | true when Diff < 10000 -> 325 | NW#network { our_ledbat = utp_ledbat:shift(Ours, Diff) }; 326 | true -> 327 | NW; 328 | false -> 329 | NW 330 | end. 331 | 332 | handle_maxed_out_window(Messages, #network {} = NW) -> 333 | case proplists:get_value(window_maxed_out, Messages) of 334 | true -> 335 | NW#network { 336 | last_maxed_out_window = utp_proto:current_time_ms() 337 | }; 338 | undefined -> 339 | NW 340 | end. 341 | 342 | update_window(Network, ReplyMicro, TimeAcked, Messages, TSDiff, Pkt) -> 343 | N6 = update_reply_micro(Network, ReplyMicro), 344 | N5 = handle_clock_skew( 345 | Network, % Deliberately the old delay base 346 | N6), 347 | N4 = update_our_ledbat(N5, TSDiff), 348 | N3 = handle_estimate_exceed(N4), 349 | N2 = handle_advertised_window(N3, Pkt), 350 | case proplists:get_value(acked, Messages) of 351 | undefined -> 352 | N2; 353 | Packets when is_list(Packets) -> 354 | Eligible = utp_buffer:extract_rtt(Packets), 355 | N = lists:foldl(fun(TimeSent, Acc) -> 356 | ack_packet_rtt(Acc, 357 | TimeSent, 358 | TimeAcked) 359 | end, 360 | N2, 361 | Eligible), 362 | BytesAcked = utp_buffer:extract_payload_size(Packets), 363 | congestion_control(N, BytesAcked) 364 | end. 365 | -------------------------------------------------------------------------------- /src/utp_process.erl: -------------------------------------------------------------------------------- 1 | %% @doc Handle a queue of processes waiting on a socket 2 | %% 3 | %% This module abstracts a type of processes queues. When a process want to either send or 4 | %% receive on a socket, it enqueues itself on these queues and waits in line. When we want 5 | %% to feed data from the socket to rest of Erlang, we use processes from these queues to 6 | %% do it. 7 | %% @end 8 | -module(utp_process). 9 | 10 | -export([ 11 | mk/0, 12 | 13 | enqueue_sender/3, 14 | enqueue_receiver/3, 15 | putback_receiver/4, 16 | dequeue_receiver/1, 17 | 18 | fill_via_send_queue/2, 19 | bytes_in_recv_buffer/1, 20 | recv_buffer_empty/1, 21 | 22 | apply_all/2, 23 | apply_senders/2, 24 | apply_receivers/2, 25 | 26 | clear_senders/1 27 | ]). 28 | 29 | -export([error_all/2]). 30 | -record(proc_info, { 31 | receiver_q :: queue(), 32 | sender_q :: queue() 33 | }). 34 | -opaque({t,{type,{29,16},record,[{atom,{29,17},proc_info}]},[]}). 35 | -export_type([t/0]). 36 | 37 | mk() -> 38 | #proc_info { receiver_q = queue:new(), 39 | sender_q = queue:new() }. 40 | 41 | apply_all(PI, F) -> 42 | apply_senders(PI, F), 43 | apply_receivers(PI, F). 44 | 45 | apply_senders(PI, F) -> 46 | [F(From) || From <- all_senders(PI)]. 47 | 48 | apply_receivers(PI, F) -> 49 | [F(From) || From <- all_receivers(PI)]. 50 | 51 | clear_senders(PI) -> 52 | PI#proc_info { sender_q = queue:new() }. 53 | 54 | all_senders(#proc_info { sender_q = SQ }) -> 55 | [From || {sender, From, _Data} <- queue:to_list(SQ)]. 56 | 57 | all_receivers(#proc_info { receiver_q = RQ }) -> 58 | [From || {receiver, From, _, _} <- queue:to_list(RQ)]. 59 | 60 | -spec enqueue_receiver(term(), integer(), t()) -> t(). 61 | enqueue_receiver(From, Length, #proc_info { receiver_q = RQ } = PI) -> 62 | NQ = queue:in({receiver, From, Length, <<>>}, RQ), 63 | PI#proc_info { receiver_q = NQ }. 64 | 65 | -spec dequeue_receiver(t()) -> {ok, term(), t()} | empty. 66 | dequeue_receiver(#proc_info { receiver_q = RQ } = PI) -> 67 | case queue:out(RQ) of 68 | {{value, Item}, NQ} -> 69 | {ok, Item, PI#proc_info { receiver_q = NQ }}; 70 | {empty, _Q} -> 71 | empty 72 | end. 73 | 74 | putback_receiver(From, Length, Data, #proc_info { receiver_q = RQ} = PI) -> 75 | NQ = queue:in_r({receiver, From, Length, Data}, RQ), 76 | PI#proc_info { receiver_q = NQ }. 77 | 78 | -spec enqueue_sender({pid(), reference()}, binary(), t()) -> t(). 79 | enqueue_sender(From, Data, #proc_info { sender_q = SQ } = PI) when is_binary(Data) -> 80 | NQ = queue:in({sender, From, Data}, SQ), 81 | PI#proc_info { sender_q = NQ }. 82 | 83 | fill_via_send_queue(N, #proc_info { sender_q = SQ } = PI) when is_integer(N) -> 84 | case dequeue(N, SQ, <<>>) of 85 | {done, Bin, SQ1} -> 86 | {filled, Bin, PI#proc_info { sender_q = SQ1}}; 87 | {partial, Bin, SQ1} -> 88 | {partial, Bin, PI#proc_info { sender_q = SQ1}}; 89 | zero -> 90 | zero 91 | end. 92 | 93 | dequeue(0, _Q, <<>>) -> 94 | zero; 95 | dequeue(0, Q, Bin) -> 96 | {done, Bin, Q}; 97 | dequeue(N, Q, Acc) -> 98 | {R, NQ} = queue:out(Q), 99 | case R of 100 | empty when Acc == <<>> -> 101 | zero; 102 | empty when Acc =/= <<>> -> 103 | {partial, Acc, Q}; % Should really use NQ here, but the dialyzer dislikes it 104 | % probably due to an internal dialyzer mistake 105 | {value, {sender, From, Data}} when byte_size(Data) =< N -> 106 | gen_utp_worker:reply(From, ok), 107 | dequeue(N - byte_size(Data), NQ, <>); 108 | {value, {sender, From, Data}} when byte_size(Data) > N -> 109 | <> = Data, 110 | dequeue(0, queue:in_r({sender, From, Rest}, NQ), <>) 111 | end. 112 | 113 | %% @doc Predicate: is the receive buffer empty 114 | %% This function is a faster variant of `bytes_in_recv_buffer/1` for the 0 question case 115 | %% @end 116 | -spec recv_buffer_empty(t()) -> boolean(). 117 | recv_buffer_empty(#proc_info { receiver_q = RQ }) -> 118 | queue:is_empty(RQ). 119 | 120 | %% @doc Return how many bytes there are left in the receive buffer 121 | %% @end 122 | -spec bytes_in_recv_buffer(t()) -> integer(). 123 | bytes_in_recv_buffer(#proc_info { receiver_q = RQ }) -> 124 | L = queue:to_list(RQ), 125 | lists:sum([byte_size(Payload) || {receiver, _From, _Sz, Payload} <- L]). 126 | 127 | 128 | 129 | 130 | error_all(ProcessInfo, ErrorReason) -> 131 | F = fun(From) -> 132 | gen_fsm:reply(From, {error, ErrorReason}) 133 | end, 134 | apply_all(ProcessInfo, F), 135 | mk(). 136 | -------------------------------------------------------------------------------- /src/utp_proto.erl: -------------------------------------------------------------------------------- 1 | -module(utp_proto). 2 | 3 | -include("utp.hrl"). 4 | 5 | -ifdef(TEST). 6 | -ifdef(EQC). 7 | -include_lib("eqc/include/eqc.hrl"). 8 | -endif. 9 | -include_lib("eunit/include/eunit.hrl"). 10 | -endif. 11 | 12 | -export([format_pkt/1, succinct_format_packet/1, validate/1, encode/2, decode/1, 13 | mk_syn/0, mk_ack/2]). 14 | 15 | -export([current_time_us/0, current_time_ms/0]). 16 | 17 | -export([mk_connection_id/0, payload_size/1]). 18 | %% Default extensions to use when SYN/SYNACK'ing 19 | -define(SYN_EXTS, [{ext_bits, <<0:64/integer>>}]). 20 | 21 | -type conn_id() :: 0..16#FFFF. 22 | -export_type([packet/0, 23 | conn_id/0]). 24 | 25 | -define(EXT_SACK, 1). 26 | -define(EXT_BITS, 2). 27 | 28 | -define(ST_DATA, 0). 29 | -define(ST_FIN, 1). 30 | -define(ST_STATE, 2). 31 | -define(ST_RESET, 3). 32 | -define(ST_SYN, 4). 33 | 34 | succinct_format_packet(#packet { ty = Ty, win_sz = WinSz, 35 | seq_no = SeqNo, 36 | ack_no = AckNo, 37 | payload = PL }) -> 38 | {Ty, {s, SeqNo}, {a, AckNo}, {w, WinSz}, {sz, byte_size(PL)}}. 39 | 40 | format_pkt(#packet { ty = Ty, conn_id = ConnID, win_sz = WinSz, 41 | seq_no = SeqNo, 42 | ack_no = AckNo, 43 | extension = Exts, 44 | payload = Payload }) -> 45 | [{ty, Ty}, {conn_id, ConnID}, {win_sz, WinSz}, 46 | {seq_no, SeqNo}, {ack_no, AckNo}, {extension, Exts}, 47 | {payload, 48 | byte_size(Payload)}]. 49 | 50 | -spec mk_connection_id() -> conn_id(). 51 | mk_connection_id() -> 52 | <> = crypto:rand_bytes(2), 53 | N. 54 | 55 | payload_size(#packet { payload = PL }) -> 56 | byte_size(PL). 57 | 58 | -spec current_time_us() -> integer(). 59 | current_time_us() -> 60 | {M, S, Micro} = os:timestamp(), 61 | S1 = M*1000000 + S, 62 | Micro + S1*1000000. 63 | 64 | -spec current_time_ms() -> integer(). 65 | current_time_ms() -> 66 | {M, S, Micro} = os:timestamp(), 67 | (Micro div 1000) + S*1000 + M*1000000*1000. 68 | 69 | -spec encode(packet(), timestamp()) -> binary(). 70 | encode(#packet { ty = Type, 71 | conn_id = ConnID, 72 | win_sz = WSize, 73 | seq_no = SeqNo, 74 | ack_no = AckNo, 75 | extension = ExtList, 76 | payload = Payload}, TSDiff) -> 77 | {Extension, ExtBin} = encode_extensions(ExtList), 78 | EncTy = encode_type(Type), 79 | TS = current_time_us(), 80 | <<1:4/integer, EncTy:4/integer, Extension:8/integer, ConnID:16/integer, 81 | TS:32/integer, 82 | TSDiff:32/integer, 83 | WSize:32/integer, 84 | SeqNo:16/integer, AckNo:16/integer, 85 | ExtBin/binary, 86 | Payload/binary>>. 87 | 88 | 89 | -spec decode(binary()) -> {ok, {packet(), timestamp(), timestamp(), timestamp()}} 90 | | {error, term()}. 91 | decode(Packet) -> 92 | try 93 | P = decode_packet(Packet), 94 | {ok, P} 95 | catch 96 | error:Reason -> 97 | {error, Reason} 98 | end. 99 | 100 | -spec decode_packet(binary()) -> {packet(), timestamp(), timestamp(), timestamp()}. 101 | decode_packet(Packet) -> 102 | TS = current_time_us(), 103 | 104 | %% Decode packet 105 | <<1:4/integer, Type:4/integer, Extension:8/integer, ConnectionId:16/integer, 106 | TimeStamp:32/integer, 107 | TimeStampdiff:32/integer, 108 | WindowSize:32/integer, 109 | SeqNo:16/integer, 110 | AckNo:16/integer, 111 | ExtPayload/binary>> = Packet, 112 | {Extensions, Payload} = decode_extensions(Extension, ExtPayload, []), 113 | 114 | %% Validate packet contents 115 | Ty = decode_type(Type), 116 | ok = validate_packet_type(Ty, Payload), 117 | 118 | {#packet { ty = Ty, 119 | conn_id = ConnectionId, 120 | win_sz = WindowSize, 121 | seq_no = SeqNo, 122 | ack_no = AckNo, 123 | extension = Extensions, 124 | payload = Payload}, 125 | TimeStamp, 126 | TimeStampdiff, 127 | TS}. 128 | 129 | validate(#packet { seq_no = SeqNo, 130 | ack_no = AckNo } = Pkt) -> 131 | case (0 =< SeqNo andalso SeqNo =< 65535) 132 | andalso 133 | (0 =< AckNo andalso AckNo =< 65535) of 134 | true -> 135 | true; 136 | false -> 137 | format_pkt(Pkt), 138 | false 139 | end. 140 | 141 | 142 | validate_packet_type(Ty, Payload) -> 143 | case Ty of 144 | st_state when Payload == <<>> -> 145 | ok; 146 | st_data when Payload =/= <<>> -> 147 | ok; 148 | st_fin -> 149 | ok; 150 | st_syn -> 151 | ok; 152 | st_reset -> 153 | ok 154 | end. 155 | 156 | decode_extensions(0, Payload, Exts) -> 157 | {lists:reverse(Exts), Payload}; 158 | decode_extensions(?EXT_SACK, <>, Acc) -> 160 | <> = R, 161 | decode_extensions(Next, Rest, [{sack, Bits} | Acc]); 162 | decode_extensions(?EXT_BITS, <>, Acc) -> 164 | <> = R, 165 | decode_extensions(Next, Rest, [{ext_bits, ExtBits} | Acc]). 166 | 167 | encode_extensions([]) -> {0, <<>>}; 168 | encode_extensions([{sack, Bits} | R]) -> 169 | {Next, Bin} = encode_extensions(R), 170 | Sz = byte_size(Bits), 171 | {?EXT_SACK, <>}; 172 | encode_extensions([{ext_bits, Bits} | R]) -> 173 | {Next, Bin} = encode_extensions(R), 174 | Sz = byte_size(Bits), 175 | {?EXT_BITS, <>}. 176 | 177 | decode_type(?ST_DATA) -> st_data; 178 | decode_type(?ST_FIN) -> st_fin; 179 | decode_type(?ST_STATE) -> st_state; 180 | decode_type(?ST_RESET) -> st_reset; 181 | decode_type(?ST_SYN) -> st_syn. 182 | 183 | encode_type(st_data) -> ?ST_DATA; 184 | encode_type(st_fin) -> ?ST_FIN; 185 | encode_type(st_state) -> ?ST_STATE; 186 | encode_type(st_reset) -> ?ST_RESET; 187 | encode_type(st_syn) -> ?ST_SYN. 188 | 189 | -ifdef(EUNIT). 190 | -ifdef(EQC). 191 | 192 | g_type() -> 193 | oneof([st_data, st_fin, st_state, st_reset, st_syn]). 194 | 195 | g_timestamp() -> 196 | choose(0, 256*256*256*256-1). 197 | 198 | g_uint32() -> 199 | choose(0, 256*256*256*256-1). 200 | 201 | g_uint16() -> 202 | choose(0, 256*256-1). 203 | 204 | g_extension_one() -> 205 | ?LET({What, Bin}, {oneof([sack, ext_bits]), binary()}, 206 | {What, Bin}). 207 | 208 | g_extension() -> 209 | list(g_extension_one()). 210 | 211 | g_payload() -> 212 | ?LET(PayLoad, 213 | ?SUCHTHAT(PL, binary(), byte_size(PL) > 0), 214 | PayLoad). 215 | 216 | g_packet() -> 217 | ?LET({Ty, ConnID, WindowSize, SeqNo, AckNo, 218 | Extension, Payload}, 219 | {g_type(), g_uint16(), g_uint32(), g_uint16(), g_uint16(), 220 | g_extension(), g_payload()}, 221 | #packet { ty = Ty, conn_id = ConnID, win_sz = WindowSize, 222 | seq_no = SeqNo, ack_no = AckNo, extension = Extension, 223 | payload = case Ty of st_state -> <<>>; _ -> Payload end }). 224 | 225 | prop_ext_dec_inv() -> 226 | ?FORALL(E, g_extension(), 227 | begin 228 | {Next, B} = encode_extensions(E), 229 | {E, <<>>} =:= decode_extensions(Next, B, []) 230 | end). 231 | 232 | prop_decode_inv() -> 233 | ?FORALL({P, T1}, {g_packet(), g_timestamp()}, 234 | begin 235 | Encoded = encode(P, T1), 236 | {Packet, _, _, _} = decode(Encoded), 237 | P =:= Packet 238 | 239 | %%{P1, _, T11, _} = decode(encode(P, T1)), 240 | %%{P1, T11} =:= {P, T1} 241 | end). 242 | 243 | inverse_extension_test() -> 244 | ?assert(eqc:quickcheck(prop_ext_dec_inv())). 245 | 246 | inverse_decode_test() -> 247 | ?assert(eqc:quickcheck(prop_decode_inv())). 248 | 249 | -endif. 250 | -endif. 251 | 252 | 253 | 254 | mk_syn() -> 255 | #packet { ty = st_syn, 256 | seq_no = 1, 257 | ack_no = 0, 258 | extension = ?SYN_EXTS 259 | }. % Rest are defaults 260 | 261 | mk_ack(SeqNo, AckNo) -> 262 | #packet {ty = st_state, 263 | seq_no = SeqNo, 264 | ack_no = AckNo, 265 | extension = ?SYN_EXTS 266 | }. 267 | -------------------------------------------------------------------------------- /src/utp_rtt.erl: -------------------------------------------------------------------------------- 1 | -module(utp_rtt). 2 | 3 | -export([ 4 | rto/1, 5 | ack_packet/4 6 | ]). 7 | 8 | -define(MAX_WINDOW_INCREASE, 3000). 9 | -define(DEFAULT_RTT_TIMEOUT, 500). 10 | 11 | -record(rtt, {rtt = 500 :: integer(), 12 | var = 800 :: integer() 13 | }). 14 | 15 | -opaque t() :: #rtt{}. 16 | -export_type([t/0]). 17 | 18 | %% Every packet that is ACKed, either by falling in the range 19 | %% (last_ack_nr, ack_nr] or by explicitly being acked by a Selective 20 | %% ACK message, should be used to update an rtt (round trip time) and 21 | %% rtt_var (rtt variance) measurement. last_ack_nr here is the last 22 | %% ack_nr received on the socket before the current packet, and ack_nr 23 | %% is the field in the currently received packet. 24 | 25 | %% The rtt and rtt_var is only updated for packets that were sent only 26 | %% once. This avoids problems with figuring out which packet was 27 | %% acked, the first or the second one. 28 | 29 | %% rtt and rtt_var are calculated by the following formula, every time 30 | %% a packet is ACKed: 31 | 32 | update(Estimate, RTT) -> 33 | utp_trace:trace(rtt_estimate, Estimate), 34 | case RTT of 35 | none -> 36 | case Estimate < 6000 of 37 | true -> 38 | #rtt { rtt = round(Estimate), 39 | var = round(Estimate / 2)}; 40 | false -> 41 | error({estimate_too_high, Estimate}) 42 | end; 43 | #rtt { rtt = LastRTT, var = Var} -> 44 | Delta = LastRTT - Estimate, 45 | #rtt { rtt = round(LastRTT - LastRTT/8 + Estimate/8), 46 | var = round(Var + (abs(Delta) - Var) / 4) } 47 | end. 48 | 49 | %% The default timeout for packets associated with the socket is also 50 | %% updated every time rtt and rtt_var is updated. It is set to: 51 | rto(none) -> 52 | utp_trace:trace(rtt_rto, ?DEFAULT_RTT_TIMEOUT), 53 | ?DEFAULT_RTT_TIMEOUT; 54 | rto(#rtt { rtt = RTT, var = Var}) -> 55 | RTO = max(RTT + Var * 4, ?DEFAULT_RTT_TIMEOUT), 56 | utp_trace:trace(rtt_rto, RTO), 57 | RTO. 58 | 59 | 60 | %% ACKnowledge an incoming packet 61 | ack_packet(History, RTT, TimeSent, TimeAcked) -> 62 | true = TimeAcked >= TimeSent, 63 | Estimate = utp_util:bit32(TimeAcked - TimeSent) div 1000, 64 | 65 | NewRTT = update(Estimate, RTT), 66 | NewHistory = case RTT of 67 | none -> 68 | History; 69 | #rtt{} -> 70 | utp_ledbat:add_sample(History, Estimate) 71 | end, 72 | NewRTO = rto(NewRTT), 73 | {ok, NewRTO, NewRTT, NewHistory}. 74 | 75 | %% Every time a socket sends or receives a packet, it updates its 76 | %% timeout counter. If no packet has arrived within timeout number of 77 | %% milliseconds from the last timeout counter reset, the socket 78 | %% triggers a timeout. It will set its packet_size and max_window to 79 | %% the smallest packet size (150 bytes). This allows it to send one 80 | %% more packet, and this is how the socket gets started again if the 81 | %% window size goes down to zero. 82 | 83 | %% The initial timeout is set to 1000 milliseconds, and later updated 84 | %% according to the formula above. For every packet consecutive 85 | %% subsequent packet that times out, the timeout is doubled. 86 | 87 | -------------------------------------------------------------------------------- /src/utp_socket.erl: -------------------------------------------------------------------------------- 1 | -module(utp_socket). 2 | 3 | -include("log.hrl"). 4 | -include("utp.hrl"). 5 | 6 | -export([ 7 | mk/4, 8 | set_conn_id/2, 9 | packet_size/1, 10 | send_buf_sz/1, 11 | 12 | send_pkt/3, 13 | order_packets/2, 14 | conn_id_recv/1, 15 | conn_id/1, 16 | send_reset/6, 17 | hostname_port/1 18 | ]). 19 | 20 | -type ip_address() :: inet:ip4_address(). 21 | -type port_number() :: 0..16#FFFF. 22 | -define(OUTGOING_BUFFER_MAX_SIZE, 511). 23 | -define(PACKET_SIZE, 350). 24 | -define(OPT_SEND_BUF, ?OUTGOING_BUFFER_MAX_SIZE * ?PACKET_SIZE). 25 | 26 | -record(sock_info, { 27 | %% Stuff pertaining to the socket: 28 | addr :: string() | ip_address(), 29 | opts :: [{atom(), term()}], %% Options on the socket 30 | port :: port_number(), 31 | socket :: inet:socket(), 32 | conn_id_send :: 'not_set' | integer(), 33 | 34 | %% Size of the outgoing buffer on the socket 35 | opt_snd_buf_sz = ?OPT_SEND_BUF :: integer() 36 | 37 | }). 38 | -opaque t() :: #sock_info{}. 39 | -export_type([t/0]). 40 | 41 | %% ---------------------------------------------------------------------- 42 | 43 | mk(Addr, Opts, Port, Socket) -> 44 | #sock_info { addr = Addr, 45 | opts = Opts, 46 | port = Port, 47 | socket = Socket, 48 | conn_id_send = not_set 49 | }. 50 | 51 | send_buf_sz(#sock_info { opt_snd_buf_sz = SBZ }) -> 52 | SBZ. 53 | 54 | hostname_port(#sock_info { addr = Addr, port = Port }) -> 55 | {Addr, Port}. 56 | 57 | conn_id(#sock_info { conn_id_send = ConnId }) -> 58 | ConnId. 59 | 60 | conn_id_recv(#sock_info { conn_id_send = ConnId }) -> 61 | ConnId - 1. % This is the receiver conn_id we use at the SYN point. 62 | 63 | send_reset(Socket, Addr, Port, ConnIDSend, AckNo, SeqNo) -> 64 | Packet = 65 | #packet { ty = st_reset, 66 | ack_no = AckNo, 67 | seq_no = SeqNo, 68 | win_sz = 0, 69 | extension = [], 70 | conn_id = ConnIDSend }, 71 | TSDiff = 0, 72 | send(Socket, Addr, Port, Packet, TSDiff). 73 | 74 | send_pkt(#sock_info { 75 | addr = Addr, 76 | port = Port, 77 | socket = Socket }, 78 | Pkt, TSDiff) -> 79 | send(Socket, Addr, Port, Pkt, TSDiff). 80 | 81 | send(Socket, Addr, Port, Packet, TSDiff) -> 82 | utp_proto:validate(Packet), 83 | utp:report_event(50, us, peer, utp_proto:succinct_format_packet(Packet), [{addr_port, Addr, Port}, 84 | {packet, Packet}]), 85 | send_aux(1, Socket, Addr, Port, utp_proto:encode(Packet, TSDiff)). 86 | 87 | send_aux(0, Socket, Addr, Port, Payload) -> 88 | gen_udp:send(Socket, Addr, Port, Payload); 89 | send_aux(N, Socket, Addr, Port, Payload) -> 90 | case gen_udp:send(Socket, Addr, Port, Payload) of 91 | ok -> 92 | {ok, utp_proto:current_time_us()}; 93 | {error, enobufs} -> 94 | %% Special case this 95 | timer:sleep(150), % Wait a bit for the queue to clear 96 | send_aux(N-1, Socket, Addr, Port, Payload); 97 | {error, Reason} -> 98 | {error, Reason} 99 | end. 100 | 101 | 102 | set_conn_id(Cid, SockInfo) -> 103 | SockInfo#sock_info { conn_id_send = Cid }. 104 | 105 | order_packets(#packet { seq_no = S1 } = P1, #packet { seq_no = S2 } = P2) -> 106 | case S1 < S2 of 107 | true -> 108 | [P1, P2]; 109 | false -> 110 | [P2, P1] 111 | end. 112 | 113 | packet_size(_Socket) -> 114 | %% @todo FIX get_packet_size/1 to actually work! 115 | 1000. 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/utp_sup.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Jesper Louis andersen 3 | %%% @copyright (C) 2011, Jesper Louis andersen 4 | %%% @doc Supervisor for the gen_utp framework 5 | %%% @end 6 | -module(utp_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | %% API 11 | -export([start_link/1, start_link/2]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -define(SERVER, ?MODULE). 17 | 18 | %%%=================================================================== 19 | %%% API functions 20 | %%%=================================================================== 21 | 22 | %%-------------------------------------------------------------------- 23 | %% @doc 24 | %% Starts the supervisor 25 | %% 26 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 27 | %% @end 28 | %%-------------------------------------------------------------------- 29 | start_link(Port, Opts) -> 30 | supervisor:start_link({local, ?SERVER}, ?MODULE, [Port, Opts]). 31 | 32 | %% @equiv start_link(Port, []) 33 | start_link(Port) -> 34 | start_link(Port, []). 35 | 36 | %%%=================================================================== 37 | 38 | %% @private 39 | init([Port, Opts]) -> 40 | RestartStrategy = one_for_all, 41 | MaxRestarts = 10, 42 | MaxSecondsBetweenRestarts = 3600, 43 | 44 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 45 | 46 | CountTracer = {utp_trace, {utp_trace, start_link, [Opts]}, 47 | permanent, 2000, worker, [utp_trace]}, 48 | Tracer = {gen_utp_trace, {gen_utp_trace, start_link, []}, 49 | permanent, 2000, worker, [gen_utp_trace]}, 50 | GenUTP = {gen_utp, {gen_utp, start_link, [Port, Opts]}, 51 | permanent, 15000, worker, [gen_utp]}, 52 | GenUTPDecoder = {gen_utp_decoder, {gen_utp_decoder, start_link, []}, 53 | permanent, 15000, worker, [gen_utp_decoder]}, 54 | WorkerPool = {gen_utp_worker_pool, {gen_utp_worker_pool, start_link, []}, 55 | transient, infinity, supervisor, [gen_utp_worker_pool]}, 56 | io:format("Starting up~n"), 57 | {ok, {SupFlags, [CountTracer, Tracer, WorkerPool, GenUTPDecoder, GenUTP]}}. 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/utp_test.erl: -------------------------------------------------------------------------------- 1 | -module(utp_test). 2 | 3 | -include("log.hrl"). 4 | -include_lib("common_test/include/ct.hrl"). 5 | 6 | -export([ 7 | test_connect_n_communicate_connect/1, test_connect_n_communicate_listen/1, 8 | test_backwards_connect/1, test_backwards_listen/1, 9 | test_full_duplex_out/1, test_full_duplex_in/1, 10 | test_close_in_1/1, test_close_out_1/1, 11 | test_close_in_2/1, test_close_out_2/1, test_close_out_2_et/1, test_close_in_2_et/1, 12 | test_close_in_3/1, test_close_out_3/1, 13 | test_send_large_file/2, test_recv_large_file/2, 14 | 15 | test_piggyback_in/2, test_piggyback_out/2, 16 | 17 | test_rwin_in/2, test_rwin_out/2, 18 | 19 | get/1, 20 | opt_seq_no/0, 21 | 22 | c/0, c/1, c/2, l/0, l/1, l/2, 23 | rc/1, rc/2, rl/1, rl/2, 24 | run_event_trace/3 25 | ]). 26 | 27 | %% ---------------------------------------------------------------------- 28 | 29 | run_event_trace(F, Config, Name) -> 30 | case proplists:get_value(event_trace_dir, Config) of 31 | undefined -> 32 | F(Config); 33 | Dir -> 34 | {ok, ColPid} = et_collector:start_link([]), 35 | try 36 | F(Config) 37 | after 38 | et_collector:save_event_file(ColPid, filename:join([Dir, Name]), []), 39 | et_collector:clear_table(ColPid) 40 | end 41 | end. 42 | 43 | test_connect_n_communicate_connect(Opts) -> 44 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 45 | ok = gen_utp:send(Sock, "HELLO"), 46 | ok = gen_utp:send(Sock, "WORLD"), 47 | timer:sleep(6000), 48 | ok = gen_utp:close(Sock), 49 | {ok, gen_utp_trace:grab()}. 50 | 51 | test_connect_n_communicate_listen(Options) -> 52 | ok = listen(Options), 53 | {ok, Port} = gen_utp:accept(), 54 | timer:sleep(5000), 55 | {ok, R1} = gen_utp:recv(Port, 5), 56 | {ok, R2} = gen_utp:recv(Port, 5), 57 | ok = gen_utp:close(Port), 58 | {<<"HELLO">>, <<"WORLD">>} = {R1, R2}, 59 | {ok, gen_utp_trace:grab()}. 60 | 61 | test_backwards_connect(Opts) -> 62 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 63 | case gen_utp:recv(Sock, 10) of 64 | {ok, <<"HELLOWORLD">>} -> 65 | ok = gen_utp:close(Sock), 66 | {ok, gen_utp_trace:grab()}; 67 | {error, econnreset} -> 68 | {{error, econnreset}, gen_utp_trace:grab()} 69 | end. 70 | 71 | test_backwards_listen(Options) -> 72 | ok = listen(Options), 73 | {ok, Sock} = gen_utp:accept(), 74 | ok = gen_utp:send(Sock, <<"HELLO">>), 75 | ok = gen_utp:send(Sock, <<"WORLD">>), 76 | ok = gen_utp:close(Sock), 77 | {ok, gen_utp_trace:grab()}. 78 | 79 | test_full_duplex_in(Options) -> 80 | ok = listen(Options), 81 | {ok, Sock} = gen_utp:accept(), 82 | ok = gen_utp:send(Sock, <<"HELLO">>), 83 | ok = gen_utp:send(Sock, <<"WORLD">>), 84 | case gen_utp:recv(Sock, 10) of 85 | {ok, <<"1234567890">>} -> ok; 86 | {error, econnreset} -> ok % Happens on bad networks 87 | end, 88 | ok = gen_utp:close(Sock), 89 | {ok, gen_utp_trace:grab()}. 90 | 91 | test_full_duplex_out(Opts) -> 92 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 93 | ok = gen_utp:send(Sock, "12345"), 94 | ok = gen_utp:send(Sock, "67890"), 95 | case gen_utp:recv(Sock, 10) of 96 | {ok, <<"HELLOWORLD">>} -> ok; 97 | {error, econnreset} -> ok % Happens on bad networks 98 | end, 99 | ok = gen_utp:close(Sock), 100 | {ok, gen_utp_trace:grab()}. 101 | 102 | test_close_out_1(Opts) -> 103 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 104 | ok = gen_utp:close(Sock), 105 | {error, econnreset} = gen_utp:send(Sock, <<"HELLO">>), 106 | {ok, gen_utp_trace:grab()}. 107 | 108 | test_close_in_1(Options) -> 109 | ok = listen(Options), 110 | {ok, Sock} = gen_utp:accept(), 111 | timer:sleep(3000), 112 | case gen_utp:send(Sock, <<"HELLO">>) of 113 | {error, econnreset} -> 114 | ignore; 115 | {error, enoconn} -> 116 | ignore 117 | end, 118 | ok = gen_utp:close(Sock), 119 | {ok, gen_utp_trace:grab()}. 120 | 121 | test_close_out_2_et(Opts) -> 122 | run_event_trace( 123 | fun test_close_out_2/1, 124 | Opts, 125 | "close_2.out"). 126 | 127 | test_close_out_2(Opts) -> 128 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 129 | timer:sleep(3000), 130 | case gen_utp:send(Sock, <<"HELLO">>) of 131 | ok -> 132 | ignore; 133 | {error, econnreset} -> 134 | ignore; 135 | {error, enoconn} -> 136 | ignore 137 | end, 138 | ok = gen_utp:close(Sock), 139 | {ok, gen_utp_trace:grab()}. 140 | 141 | test_close_in_2_et(Options) -> 142 | run_event_trace( 143 | fun test_close_in_2/1, 144 | Options, 145 | "close_2.in"). 146 | 147 | test_close_in_2(Options) -> 148 | ok = listen(Options), 149 | {ok, Sock} = gen_utp:accept(), 150 | ok = gen_utp:close(Sock), 151 | case gen_utp:recv(Sock, 5) of 152 | {error, eof} -> 153 | ignore; 154 | {error, econnreset} -> 155 | ignore 156 | end, 157 | {ok, gen_utp_trace:grab()}. 158 | 159 | test_close_out_3(Opts) -> 160 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 161 | ok = gen_utp:send(Sock, <<"HELLO">>), 162 | {ok, <<"WORLD">>} = gen_utp:recv(Sock, 5), 163 | ok = gen_utp:close(Sock), 164 | {ok, gen_utp_trace:grab()}. 165 | 166 | test_close_in_3(Options) -> 167 | ok = listen(Options), 168 | {ok, Sock} = gen_utp:accept(), 169 | ok = gen_utp:send(Sock, "WORLD"), 170 | {ok, <<"HELLO">>} = gen_utp:recv(Sock, 5), 171 | ok = gen_utp:close(Sock), 172 | {ok, gen_utp_trace:grab()}. 173 | 174 | test_send_large_file(Data, Opts) -> 175 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 176 | ok = gen_utp:send(Sock, Data), 177 | ok = gen_utp:close(Sock), 178 | {ok, gen_utp_trace:grab()}. 179 | 180 | %% Infinitely repeat connecting. A timetrap will capture the problem 181 | -spec repeating_connect(term(), integer(), [term()]) -> no_return() | {ok, any()}. 182 | repeating_connect(Host, Port, Opts) -> 183 | case gen_utp:connect(Host, Port, Opts) of 184 | {ok, Sock} -> 185 | {ok, Sock}; 186 | {error, etimedout} -> 187 | repeating_connect(Host, Port, Opts) 188 | end. 189 | 190 | test_rwin_in(Data, Options) -> 191 | Sz = byte_size(Data), 192 | ok = listen(Options), 193 | {ok, Sock} = gen_utp:accept(), 194 | case rwin_recv(Sock, Sz, <<>>) of 195 | early_close -> 196 | done; 197 | Data -> 198 | ok = gen_utp:close(Sock) 199 | end, 200 | {ok, gen_utp_trace:grab()}. 201 | 202 | rwin_recv(_Sock, 0, Binary) -> 203 | Binary; 204 | rwin_recv(Sock, Sz, Acc) when Sz =< 10000 -> 205 | timer:sleep(3000), 206 | case gen_utp:recv(Sock, Sz) of 207 | {ok, B} -> 208 | rwin_recv(Sock, 0, <>); 209 | {error, enoconn} -> 210 | early_close 211 | end; 212 | rwin_recv(Sock, Sz, Acc) when Sz > 10000 -> 213 | timer:sleep(3000), 214 | case gen_utp:recv(Sock, 10000) of 215 | {ok, B} -> 216 | rwin_recv(Sock, Sz - 10000, <>); 217 | {error, enoconn} -> 218 | early_close 219 | end. 220 | 221 | test_rwin_out(Data, Opts) -> 222 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 223 | case gen_utp:send(Sock, Data) of 224 | ok -> ok = gen_utp:close(Sock); 225 | {error, enoconn} -> done 226 | end, 227 | {ok, gen_utp_trace:grab()}. 228 | 229 | test_piggyback_out(Data, Opts) -> 230 | Sz = byte_size(Data), 231 | {ok, Sock} = repeating_connect("localhost", 3333, Opts), 232 | {Recv, Ref} = {self(), make_ref()}, 233 | spawn_link(fun() -> 234 | ok = gen_utp:send(Sock, Data), 235 | Recv ! {done, Ref} 236 | end), 237 | {ok, Data} = gen_utp:recv(Sock, Sz), 238 | gen_utp_trace:tr("Received out, waiting for done"), 239 | receive 240 | {done, Ref} -> 241 | ok 242 | end, 243 | {ok, Sock, gen_utp_trace:grab()}. 244 | 245 | 246 | test_piggyback_in(Data, Options) -> 247 | Sz = byte_size(Data), 248 | ok = listen(Options), 249 | {ok, Sock} = gen_utp:accept(), 250 | {Recv, Ref} = {self(), make_ref()}, 251 | spawn_link(fun() -> 252 | ok = gen_utp:send(Sock, Data), 253 | Recv ! {done, Ref} 254 | end), 255 | {ok, Data} = gen_utp:recv(Sock, Sz), 256 | gen_utp_trace:tr("Received in, waiting for done"), 257 | receive 258 | {done, Ref} -> 259 | ok 260 | end, 261 | {ok, Sock, gen_utp_trace:grab()}. 262 | 263 | test_recv_large_file(Data, Options) -> 264 | Sz = byte_size(Data), 265 | ok = listen(Options), 266 | {ok, Port} = gen_utp:accept(), 267 | {ok, Data} = gen_utp:recv(Port, Sz), 268 | ok = gen_utp:close(Port), 269 | {ok, gen_utp_trace:grab()}. 270 | 271 | get(N) when is_integer(N) -> 272 | {ok, Sock} = gen_utp:connect("port1394.ds1-vby.adsl.cybercity.dk", 3333), 273 | ok = gen_utp:send(Sock, <>), 274 | {ok, <>} = gen_utp:recv(Sock, 4), 275 | {ok, FName} = gen_utp:recv(Sock, FnLen), 276 | {ok, <>} = gen_utp:recv(Sock, 4), 277 | {ok, Data} = gen_utp:recv(Sock, Len), 278 | file:write_file(FName, Data), 279 | ok = gen_utp:close(Sock), 280 | {ok, gen_utp_trace:grab()}. 281 | 282 | listen(Config) -> 283 | Opts = case proplists:get_value(force_seq_no, Config) of 284 | K when is_integer(K), 285 | K >= 0, 286 | K =< 16#FFFF -> 287 | [{force_seq_no, K}]; 288 | _Otherwise -> 289 | [] 290 | end, 291 | case gen_utp:listen(Opts) of 292 | ok -> 293 | ok; 294 | {error, ealreadylistening} -> 295 | ok 296 | end. 297 | 298 | %% ---------------------------------------------------------------------- 299 | large_data() -> 300 | binary:copy(<<"HELLOHELLO">>, 5000). 301 | 302 | l() -> 303 | l(test_large, []). 304 | 305 | opt_seq_no() -> 306 | {force_seq_no, 65535}. 307 | 308 | rl(T) -> 309 | rl(T, []). 310 | 311 | rl(T, Opts) -> 312 | utp:start_app(3333, Opts), 313 | {ok, Pid} = utp_filter:start(), 314 | Fun = parse_listen(T), 315 | case proplists:get_value(rounds, Opts) of 316 | undefined -> 317 | repeat(50, Pid, Fun, Opts); 318 | N -> 319 | repeat(N, Pid, Fun, Opts) 320 | end. 321 | 322 | repeat(0, _, _, _) -> 323 | ok; 324 | repeat(N, VPid, Fun, Opts) -> 325 | io:format("Running ~B~n", [N]), 326 | case Fun(Opts) of 327 | {ok, _} -> ok; 328 | {ok, _, _} -> ok; 329 | ok -> ok 330 | end, 331 | utp_filter:clear(VPid), 332 | repeat(N-1, VPid, Fun, Opts). 333 | 334 | l(T) -> 335 | l(T, []). 336 | 337 | l(T, Opts) -> 338 | utp:start_app(3333, Opts), 339 | utp_filter:start(), 340 | (parse_listen(T))(Opts). 341 | 342 | parse_listen(test_large) -> 343 | fun(O) -> 344 | test_recv_large_file(large_data(), O) 345 | end; 346 | parse_listen(test_rwin) -> 347 | fun(O) -> 348 | test_rwin_in(large_data(), O) 349 | end; 350 | parse_listen(test_piggyback) -> 351 | fun(O) -> 352 | {ok, Sock, _} = test_piggyback_in(large_data(), O), 353 | gen_utp:close(Sock) 354 | end; 355 | parse_listen(test_close_3) -> 356 | fun test_close_in_3/1; 357 | parse_listen(test_close_2) -> 358 | fun test_close_in_2/1; 359 | parse_listen(test_close_2_et) -> 360 | fun test_close_in_2_et/1; 361 | parse_listen(test_close_1) -> 362 | fun test_close_in_1/1; 363 | parse_listen(test_full_duplex) -> 364 | fun test_full_duplex_in/1; 365 | parse_listen(test_backwards) -> 366 | fun test_backwards_listen/1; 367 | parse_listen(test_connect_n_communicate) -> 368 | fun test_connect_n_communicate_listen/1. 369 | 370 | c() -> 371 | c(test_large). 372 | 373 | c(T) -> 374 | c(T, []). 375 | 376 | c(T, Opts) -> 377 | utp:start_app(3334, Opts), 378 | utp_filter:start(), 379 | (parse_connect(T))(Opts). 380 | 381 | rc(T) -> 382 | rc(T, []). 383 | 384 | rc(T, Opts) -> 385 | utp:start_app(3334, Opts), 386 | {ok, Pid} = utp_filter:start(), 387 | Fun = parse_connect(T), 388 | case proplists:get_value(rounds, Opts) of 389 | undefined -> 390 | repeat(50, Pid, Fun, Opts); 391 | N -> 392 | repeat(N, Pid, Fun, Opts) 393 | end. 394 | 395 | parse_connect(test_large) -> 396 | fun (O) -> 397 | test_send_large_file(large_data(), O) 398 | end; 399 | parse_connect(test_rwin) -> 400 | fun (O) -> 401 | test_rwin_out(large_data(), O) 402 | end; 403 | parse_connect(test_piggyback) -> 404 | fun (O) -> 405 | {ok, Sock, _} = test_piggyback_out(large_data(), O), 406 | gen_utp:close(Sock) 407 | end; 408 | parse_connect(test_close_3) -> 409 | fun test_close_out_3/1; 410 | parse_connect(test_close_2) -> 411 | fun test_close_out_2/1; 412 | parse_connect(test_close_2_et) -> 413 | fun test_close_out_2_et/1; 414 | parse_connect(test_close_1) -> 415 | fun test_close_out_1/1; 416 | parse_connect(test_full_duplex) -> 417 | fun test_full_duplex_out/1; 418 | parse_connect(test_backwards) -> 419 | fun test_backwards_connect/1; 420 | parse_connect(test_connect_n_communicate) -> 421 | fun test_connect_n_communicate_connect/1. 422 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /src/utp_test_client.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Jesper Louis andersen 3 | %%% @copyright (C) 2011, Jesper Louis andersen 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 1 Jun 2011 by Jesper Louis andersen 8 | %%%------------------------------------------------------------------- 9 | -module(utp_test_client). 10 | 11 | -behaviour(gen_server). 12 | 13 | %% API 14 | -export([start_link/0, start_link/2, 15 | 16 | ls/0, 17 | get/1, 18 | cmd/1]). 19 | 20 | %% gen_server callbacks 21 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 22 | terminate/2, code_change/3]). 23 | 24 | -define(SERVER, ?MODULE). 25 | 26 | -record(state, { socket :: gen_utp:socket() }). 27 | 28 | %%%=================================================================== 29 | %%% API 30 | %%%=================================================================== 31 | 32 | %%-------------------------------------------------------------------- 33 | %% @doc 34 | %% Starts the server 35 | %% 36 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error} 37 | %% @end 38 | %%-------------------------------------------------------------------- 39 | start_link() -> 40 | Host = "horus.0x90.dk", Port = 3838, 41 | start_link(Host, Port). 42 | 43 | 44 | start_link(Host, Port) -> 45 | gen_server:start_link({local, ?SERVER}, ?MODULE, [Host, Port], []). 46 | 47 | ls() -> 48 | cmd(ls). 49 | 50 | get(File) -> 51 | case cmd({get, File}) of 52 | {ok, Data} -> 53 | file:write_file(File, Data, [exclusive]); 54 | {error, Reason} -> 55 | {error, Reason} 56 | end. 57 | 58 | cmd(Cmd) -> 59 | gen_server:call(?SERVER, {cmd, Cmd}, infinity). 60 | 61 | %%%=================================================================== 62 | %%% gen_server callbacks 63 | %%%=================================================================== 64 | 65 | %%-------------------------------------------------------------------- 66 | %% @private 67 | %% @doc 68 | %% Initializes the server 69 | %% 70 | %% @spec init(Args) -> {ok, State} | 71 | %% {ok, State, Timeout} | 72 | %% ignore | 73 | %% {stop, Reason} 74 | %% @end 75 | %%-------------------------------------------------------------------- 76 | init([Host, Port]) -> 77 | case gen_utp:connect(Host, Port) of 78 | {ok, Socket} -> 79 | {ok, #state{ socket = Socket }}; 80 | {error, Reason} -> 81 | {stop, Reason} 82 | end. 83 | 84 | %%-------------------------------------------------------------------- 85 | %% @private 86 | %% @doc 87 | %% Handling call messages 88 | %% 89 | %% @spec handle_call(Request, From, State) -> 90 | %% {reply, Reply, State} | 91 | %% {reply, Reply, State, Timeout} | 92 | %% {noreply, State} | 93 | %% {noreply, State, Timeout} | 94 | %% {stop, Reason, Reply, State} | 95 | %% {stop, Reason, State} 96 | %% @end 97 | %%-------------------------------------------------------------------- 98 | handle_call({cmd, Cmd}, _From, #state { socket = Sock } = State) -> 99 | ok = gen_utp:send_msg(Sock, Cmd), 100 | case gen_utp:recv_msg(Sock) of 101 | {ok, Msg} -> 102 | {reply, Msg, State}; 103 | {error, Reason} -> 104 | {reply, {error, Reason}, State} 105 | end; 106 | handle_call(_Request, _From, State) -> 107 | Reply = ok, 108 | {reply, Reply, State}. 109 | 110 | %%-------------------------------------------------------------------- 111 | %% @private 112 | %% @doc 113 | %% Handling cast messages 114 | %% 115 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 116 | %% {noreply, State, Timeout} | 117 | %% {stop, Reason, State} 118 | %% @end 119 | %%-------------------------------------------------------------------- 120 | handle_cast(_Msg, State) -> 121 | {noreply, State}. 122 | 123 | %%-------------------------------------------------------------------- 124 | %% @private 125 | %% @doc 126 | %% Handling all non call/cast messages 127 | %% 128 | %% @spec handle_info(Info, State) -> {noreply, State} | 129 | %% {noreply, State, Timeout} | 130 | %% {stop, Reason, State} 131 | %% @end 132 | %%-------------------------------------------------------------------- 133 | handle_info(_Info, State) -> 134 | {noreply, State}. 135 | 136 | %%-------------------------------------------------------------------- 137 | %% @private 138 | %% @doc 139 | %% This function is called by a gen_server when it is about to 140 | %% terminate. It should be the opposite of Module:init/1 and do any 141 | %% necessary cleaning up. When it returns, the gen_server terminates 142 | %% with Reason. The return value is ignored. 143 | %% 144 | %% @spec terminate(Reason, State) -> void() 145 | %% @end 146 | %%-------------------------------------------------------------------- 147 | terminate(_Reason, _State) -> 148 | ok. 149 | 150 | %%-------------------------------------------------------------------- 151 | %% @private 152 | %% @doc 153 | %% Convert process state when code is changed 154 | %% 155 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 156 | %% @end 157 | %%-------------------------------------------------------------------- 158 | code_change(_OldVsn, State, _Extra) -> 159 | {ok, State}. 160 | 161 | %%%=================================================================== 162 | %%% Internal functions 163 | %%%=================================================================== 164 | -------------------------------------------------------------------------------- /src/utp_test_server_acceptor.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen <> 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc 4 | %%% Accept and handle a child connection for the test server 5 | %%% @end 6 | %%% Created : 12 Aug 2011 by Jesper Louis andersen <> 7 | -module(utp_test_server_acceptor). 8 | 9 | -behaviour(gen_server). 10 | 11 | %% API 12 | -export([start_link/0]). 13 | 14 | %% gen_server callbacks 15 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 16 | terminate/2, code_change/3]). 17 | 18 | -define(SERVER, ?MODULE). 19 | 20 | -record(state, { socket = none :: none | gen_utp:socket() }). 21 | 22 | %%%=================================================================== 23 | %%% API 24 | %%%=================================================================== 25 | 26 | %% @doc 27 | %% Starts the server 28 | %% @end 29 | start_link() -> 30 | gen_server:start_link(?MODULE, [], []). 31 | 32 | %%%=================================================================== 33 | 34 | %% @private 35 | init([]) -> 36 | {ok, #state{ }, 0}. 37 | 38 | %%-------------------------------------------------------------------- 39 | %% @private 40 | %% @doc 41 | %% Handling call messages 42 | %% 43 | %% @spec handle_call(Request, From, State) -> 44 | %% {reply, Reply, State} | 45 | %% {reply, Reply, State, Timeout} | 46 | %% {noreply, State} | 47 | %% {noreply, State, Timeout} | 48 | %% {stop, Reason, Reply, State} | 49 | %% {stop, Reason, State} 50 | %% @end 51 | %%-------------------------------------------------------------------- 52 | handle_call(_Request, _From, State) -> 53 | Reply = ok, 54 | {reply, Reply, State}. 55 | 56 | %%-------------------------------------------------------------------- 57 | %% @private 58 | %% @doc 59 | %% Handling cast messages 60 | %% 61 | %% @spec handle_cast(Msg, State) -> {noreply, State} | 62 | %% {noreply, State, Timeout} | 63 | %% {stop, Reason, State} 64 | %% @end 65 | %%-------------------------------------------------------------------- 66 | handle_cast(_Msg, State) -> 67 | {noreply, State}. 68 | 69 | %% @private 70 | handle_info(timeout, #state { socket = none} = State) -> 71 | error_logger:info_report([accepting]), 72 | case gen_utp:accept() of 73 | {ok, Sock} -> 74 | error_logger:info_report([accepted]), 75 | {ok, _Pid} = utp_test_server_pool_sup:start_child(), 76 | {noreply, State#state { socket = Sock }, 0} 77 | %% {error, Something} -> 78 | %% error_logger:info_report([accept_error, Something]), 79 | %% {ok, _Pid} = utp_test_server_pool_sup:start_child(), 80 | %% {stop, normal, State} 81 | end; 82 | handle_info(timeout, #state { socket = Sock } = State) -> 83 | %% Read from the socket. 84 | {ok, Cmd} = gen_utp:recv_msg(Sock), 85 | case validate_message(Cmd) of 86 | ok -> 87 | Res = utp_file_map:cmd(Cmd), 88 | gen_utp:send_msg(Sock, Res), 89 | {noreply, State, 0}; 90 | error -> 91 | error_logger:info_report([invalid_cmd, Cmd]), 92 | gen_utp:close(Sock), 93 | {stop, normal, State} 94 | end; 95 | handle_info(_Info, State) -> 96 | {noreply, State}. 97 | 98 | %%-------------------------------------------------------------------- 99 | %% @private 100 | %% @doc 101 | %% This function is called by a gen_server when it is about to 102 | %% terminate. It should be the opposite of Module:init/1 and do any 103 | %% necessary cleaning up. When it returns, the gen_server terminates 104 | %% with Reason. The return value is ignored. 105 | %% 106 | %% @spec terminate(Reason, State) -> void() 107 | %% @end 108 | %%-------------------------------------------------------------------- 109 | terminate(_Reason, _State) -> 110 | ok. 111 | 112 | %%-------------------------------------------------------------------- 113 | %% @private 114 | %% @doc 115 | %% Convert process state when code is changed 116 | %% 117 | %% @spec code_change(OldVsn, State, Extra) -> {ok, NewState} 118 | %% @end 119 | %%-------------------------------------------------------------------- 120 | code_change(_OldVsn, State, _Extra) -> 121 | {ok, State}. 122 | 123 | %%%=================================================================== 124 | 125 | validate_message(ls) -> 126 | ok; 127 | validate_message({get, _FName}) -> 128 | ok; 129 | validate_message(_Otherwise) -> 130 | error. 131 | 132 | -------------------------------------------------------------------------------- /src/utp_test_server_pool_sup.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen <> 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc 4 | %%% Supervise a server for testing uTP 5 | %%% @end 6 | %%% Created : 12 Aug 2011 by Jesper Louis andersen <> 7 | %%%------------------------------------------------------------------- 8 | -module(utp_test_server_pool_sup). 9 | 10 | -behaviour(supervisor). 11 | 12 | %% API 13 | -export([start_link/0, 14 | start_child/0]). 15 | 16 | %% Supervisor callbacks 17 | -export([init/1]). 18 | 19 | -define(SERVER, ?MODULE). 20 | 21 | %%%=================================================================== 22 | 23 | %% @doc 24 | %% Starts the supervisor 25 | %% @end 26 | start_link() -> 27 | {ok, P} = supervisor:start_link({local, ?SERVER}, ?MODULE, []), 28 | {ok, _} = start_child(), 29 | {ok, P}. 30 | 31 | start_child() -> 32 | supervisor:start_child(?MODULE, []). 33 | 34 | %%%=================================================================== 35 | 36 | %% @private 37 | init([]) -> 38 | utp:start_app(3838, []), 39 | ok = gen_utp:listen([]), 40 | AcceptChild = 41 | {accept_child, {utp_test_server_acceptor, start_link, 42 | []}, 43 | temporary, brutal_kill, worker, [utp_test_server_acceptor]}, 44 | RestartStrategy = {simple_one_for_one, 100, 3600}, 45 | {ok, {RestartStrategy, [AcceptChild]}}. 46 | 47 | %%%=================================================================== 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/utp_test_server_sup.erl: -------------------------------------------------------------------------------- 1 | %%% @author Jesper Louis andersen <> 2 | %%% @copyright (C) 2011, Jesper Louis andersen 3 | %%% @doc 4 | %%% Supervise a server for testing uTP 5 | %%% @end 6 | %%% Created : 12 Aug 2011 by Jesper Louis andersen <> 7 | %%%------------------------------------------------------------------- 8 | -module(utp_test_server_sup). 9 | 10 | -behaviour(supervisor). 11 | 12 | %% API 13 | -export([start_link/1]). 14 | 15 | %% Supervisor callbacks 16 | -export([init/1]). 17 | 18 | -define(SERVER, ?MODULE). 19 | 20 | %%%=================================================================== 21 | 22 | %% @doc 23 | %% Starts the supervisor 24 | %% @end 25 | start_link(Dir) -> 26 | supervisor:start_link({local, ?SERVER}, ?MODULE, [Dir]). 27 | 28 | %%%=================================================================== 29 | 30 | %% @private 31 | init([Dir]) -> 32 | PoolChild = 33 | {pool, {utp_test_server_pool_sup, start_link, []}, 34 | permanent, infinity, supervisor, [utp_test_server_pool_sup]}, 35 | FileMapper = 36 | {file_mapper, {utp_file_map, start_link, [Dir]}, 37 | permanent, 5*1000, worker, [utp_file_map]}, 38 | RestartStrategy = {one_for_one, 100, 3600}, 39 | {ok, {RestartStrategy, [PoolChild, FileMapper]}}. 40 | 41 | %%%=================================================================== 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/utp_trace.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Jesper Louis andersen <> 3 | %%% @copyright (C) 2011, Jesper Louis andersen 4 | %%% @doc Trace counters for connections 5 | %%% @end 6 | %%% Created : 25 Jul 2011 by Jesper Louis andersen <> 7 | %%%------------------------------------------------------------------- 8 | -module(utp_trace). 9 | 10 | -behaviour(gen_server). 11 | 12 | -include("log.hrl"). 13 | 14 | %% API 15 | -export([ 16 | close_all/0, 17 | 18 | start_link/1, 19 | trace/2 20 | ]). 21 | 22 | %% gen_server callbacks 23 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, 24 | terminate/2, code_change/3]). 25 | 26 | -define(SERVER, ?MODULE). 27 | 28 | -record(state, { enabled = false :: boolean(), 29 | map = dict:new():: dict() }). 30 | 31 | %%%=================================================================== 32 | %%% API 33 | %%%=================================================================== 34 | 35 | %% @doc Starts the server 36 | %% @end 37 | %%-------------------------------------------------------------------- 38 | start_link(Options) -> 39 | gen_server:start_link({local, ?SERVER}, ?MODULE, [Options], []). 40 | 41 | trace(Counter, Count) -> 42 | Now = erlang:now(), 43 | gen_server:cast(?SERVER, {trace_point, Now, self(), Counter, Count}). 44 | 45 | close_all() -> 46 | gen_server:cast(?SERVER, close_all). 47 | 48 | %%%=================================================================== 49 | 50 | %% @private 51 | init([Options]) -> 52 | case proplists:get_value(trace_counters, Options) of 53 | true -> 54 | error_logger:info_report(tracer_enabled), 55 | {ok, #state { enabled = true, 56 | map = dict:new() }}; 57 | undefined -> 58 | error_logger:info_report(tracer_disabled), 59 | {ok, #state{ enabled = false}} 60 | end. 61 | 62 | %% @private 63 | handle_call(_Request, _From, State) -> 64 | Reply = ok, 65 | {reply, Reply, State}. 66 | 67 | %% @private 68 | handle_cast(_Msg, #state { enabled = false } = State) -> 69 | {noreply, State}; 70 | handle_cast({trace_point, TimeStamp, Connection, Counter, Count}, 71 | #state { enabled = true, map = Map } = State) -> 72 | {N_Map, Handle} = find_handle({Connection, Counter}, Map), 73 | Msg = format_message(TimeStamp, Count), 74 | trace_message(Handle, Msg), 75 | {noreply, State#state { map = N_Map }}; 76 | handle_cast(close_all, #state { enabled = true, map = M }) -> 77 | [file:close(H) || {_K, H} <- dict:to_list(M)], 78 | {noreply, #state { enabled = true, map = dict:new() }}; 79 | handle_cast(Msg, State) -> 80 | ?ERR([unknown_handle_case, Msg, State]), 81 | {noreply, State}. 82 | 83 | %% @private 84 | handle_info(_Info, State) -> 85 | {noreply, State}. 86 | 87 | %% @private 88 | terminate(_Reason, _State) -> 89 | ok. 90 | 91 | %% @private 92 | code_change(_OldVsn, State, _Extra) -> 93 | {ok, State}. 94 | 95 | %%%=================================================================== 96 | 97 | find_handle({Connection, Counter} = Id, Map) when is_atom(Counter) -> 98 | case dict:find(Id, Map) of 99 | error -> 100 | find_handle(Id, 101 | dict:store({Connection, Counter}, 102 | create_handle(Connection, Counter), Map)); 103 | {ok, Handle} -> 104 | {Map, Handle} 105 | end. 106 | 107 | format_conn(Connection) -> 108 | pid_to_list(Connection). 109 | 110 | create_handle(Connection, Counter) -> 111 | FName = [os:getpid(), "-", format_conn(Connection), "-", atom_to_list(Counter)], 112 | {ok, Handle} = file:open(FName, [write, raw, binary]), 113 | Handle. 114 | 115 | format_message({Mega, Secs, Micro}, Count) -> 116 | Time = [integer_to_list(Mega*1000000+Secs), ".", io_lib:format("~6..0B", [Micro])], 117 | [Time, "|", integer_to_list(Count), "\n"]. 118 | 119 | trace_message(Handle, Event) -> 120 | ok = file:write(Handle, Event). 121 | 122 | -------------------------------------------------------------------------------- /src/utp_util.erl: -------------------------------------------------------------------------------- 1 | -module(utp_util). 2 | 3 | -export([ 4 | bit16/1, 5 | bit32/1 6 | ]). 7 | 8 | -export([canonicalize_address/1]). 9 | 10 | %% @doc `bit16(Expr)' performs `Expr' modulo 65536 11 | %% @end 12 | -spec bit16(integer()) -> integer(). 13 | bit16(N) when is_integer(N) -> 14 | N band 16#FFFF. 15 | 16 | -spec bit32(integer()) -> integer(). 17 | bit32(N) when is_integer(N) -> 18 | N band 16#FFFFFFFF. 19 | 20 | 21 | %% ---------------------------------------------------------------------- 22 | 23 | canonicalize_address(S) when is_list(S) -> 24 | {ok, CAddr} = inet:getaddr(S, inet), 25 | CAddr; 26 | canonicalize_address({_, _, _, _} = Addr) -> 27 | Addr. -------------------------------------------------------------------------------- /tools/impose_network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | from=${1-127.0.0.1} 4 | to=${2-127.0.0.1} 5 | 6 | ipfw delete 100 7 | ipfw delete 200 8 | ipfw delete 300 9 | ipfw delete 400 10 | ipfw delete 500 11 | 12 | ipfw add 1000 allow all from any to any 13 | 14 | ipfw add 100 pipe 10 udp from ${from} to ${to} 3333 in 15 | ipfw add 200 pipe 20 udp from ${to} 3333 to ${from} out 16 | 17 | ## Pipes starting with 10 is one way 18 | ipfw pipe 10 config bw 10Mbit/s 19 | ipfw pipe 10 config delay 100ms 20 | ipfw pipe 10 config plr 0.1 21 | 22 | ## Pipes starting with 20 is the other way 23 | ipfw pipe 20 config bw 10Mbit/s 24 | ipfw pipe 20 config delay 100ms 25 | ipfw pipe 20 config plr 0.1 26 | 27 | -------------------------------------------------------------------------------- /tools/linux_clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | tc qdisc del dev lo root 4 | -------------------------------------------------------------------------------- /tools/linux_network_1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Traffic-shape a test network 4 | 5 | tc qdisc replace dev lo root netem \ 6 | delay 100ms 20ms distribution normal \ 7 | reorder 25% 50% \ 8 | loss 1.5% 25% \ 9 | duplicate 3% 10 | 11 | -------------------------------------------------------------------------------- /tools/linux_network_2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Traffic-shape a test network 4 | 5 | tc qdisc replace dev lo root netem \ 6 | delay 10ms 5ms distribution normal \ 7 | reorder 25% 50% \ 8 | loss 1.5% 25% \ 9 | duplicate 3% 10 | 11 | -------------------------------------------------------------------------------- /tools/really_faulty.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## Create a network where something around the 10th packet are lost. 4 | ## This is a somewhat realistic network condition and it does happen. 5 | 6 | from=${1-127.0.0.1} 7 | to=${2-127.0.0.1} 8 | 9 | ipfw delete 100 10 | ipfw delete 200 11 | ipfw delete 300 12 | ipfw delete 400 13 | ipfw delete 500 14 | 15 | ipfw add 1000 allow all from any to any 16 | 17 | ipfw add 100 pipe 10 udp from ${from} to ${to} 3333 in 18 | ipfw add 200 pipe 20 udp from ${to} 3333 to ${from} out 19 | 20 | ## Pipes starting with 10 is one way 21 | ipfw pipe 10 config bw 10Mbit/s 22 | ipfw pipe 10 config delay 100ms 23 | ipfw pipe 10 config plr 0.1 24 | 25 | ## Pipes starting with 20 is the other way 26 | ipfw pipe 20 config bw 10Mbit/s 27 | ipfw pipe 20 config delay 100ms 28 | ipfw pipe 20 config plr 0.1 29 | 30 | -------------------------------------------------------------------------------- /tools/slightly_faulty.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## Create a network where something around the 100th packet are lost. 4 | ## This is a somewhat realistic network condition and it does happen. 5 | 6 | from=${1-127.0.0.1} 7 | to=${2-127.0.0.1} 8 | 9 | ipfw delete 100 10 | ipfw delete 200 11 | ipfw delete 300 12 | ipfw delete 400 13 | ipfw delete 500 14 | 15 | ipfw add 1000 allow all from any to any 16 | 17 | ipfw add 100 pipe 10 udp from ${from} to ${to} 3333 in 18 | ipfw add 200 pipe 20 udp from ${to} 3333 to ${from} out 19 | 20 | ## Pipes starting with 10 is one way 21 | ipfw pipe 10 config bw 10Mbit/s 22 | ipfw pipe 10 config delay 100ms 23 | ipfw pipe 10 config plr 0.01 24 | 25 | ## Pipes starting with 20 is the other way 26 | ipfw pipe 20 config bw 10Mbit/s 27 | ipfw pipe 20 config delay 100ms 28 | ipfw pipe 20 config plr 0.01 29 | 30 | --------------------------------------------------------------------------------