PageRenderTime 11ms CodeModel.GetById 4ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/of_driver_tests.erl

https://github.com/chinnurtb/of_driver
Erlang | 624 lines | 509 code | 73 blank | 42 comment | 2 complexity | ade680eb0f48230010208f346a96ec0f MD5 | raw file
  1%%------------------------------------------------------------------------------
  2%% Copyright 2014 FlowForwarding.org
  3%%
  4%% Licensed under the Apache License, Version 2.0 (the "License");
  5%% you may not use this file except in compliance with the License.
  6%% You may obtain a copy of the License at
  7%%
  8%%     http://www.apache.org/licenses/LICENSE-2.0
  9%%
 10%% Unless required by applicable law or agreed to in writing, software
 11%% distributed under the License is distributed on an "AS IS" BASIS,
 12%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13%% See the License for the specific language governing permissions and
 14%% limitations under the License.
 15%%-----------------------------------------------------------------------------
 16
 17%% @author Erlang Solutions Ltd. <openflow@erlang-solutions.com>
 18%% @copyright 2014 FlowForwarding.org
 19
 20-module (of_driver_tests).
 21
 22-include_lib("eunit/include/eunit.hrl").
 23-include_lib("of_protocol/include/of_protocol.hrl").
 24-include_lib("of_protocol/include/ofp_v4.hrl").
 25
 26-define(LISTEN_PORT, 15578).
 27-define(DATAPATH_ID, 1).
 28-define(DATAPATH_UNPARSED,<<8,0,39,150,212,121>>).
 29-define(DATAPATH_HEX,"00:01:08:00:27:96:D4:79").
 30
 31-export([trace/0]).
 32
 33%%------------------------------------------------------------------------------
 34
 35of_driver_test_() ->
 36    {setup,
 37     fun setup/0,
 38     fun cleanup/1,
 39     fun(S) ->
 40        {foreach, fun test_setup/0,
 41         [{"set_xid",          fun set_xid/0},
 42          {"main_connect",     fun main_connect/0},
 43          {"main_terminate",   fun main_terminate/0},
 44          {"early_message",    fun early_message/0},
 45          {"close_connection", fun close_connection/0}|
 46          [{N, fun() -> F(S) end} || {N, F} <- 
 47                [{"gen_xid",                       fun gen_xid/1}
 48                ,{"aux_connect",                   fun aux_connect/1}
 49                ,{"in_message",                    fun in_message/1}
 50                ,{"echo_request",                  fun echo_request/1}
 51                ,{"echo_request with data",        fun echo_request2/1}
 52                ,{"send",                          fun send/1}
 53                ,{"sync_send",                     fun sync_send/1}
 54                ,{"sync_send_no_reply",            fun sync_send_no_reply/1}
 55                ,{"sync_send_non_reply",           fun sync_send_non_reply/1}
 56                ,{"sync_send_multipart",           fun sync_send_multipart/1}
 57                ,{"send_list",                     fun send_list/1}
 58                ,{"sync_send_list",                fun sync_send_list/1}
 59                ,{"sync_send_list_no_reply",       fun sync_send_list_no_reply/1}
 60                ,{"sync_send_list_multipart",      fun sync_send_list_multipart/1}
 61                ,{"multiple_sync_send",            fun multiple_sync_send/1}
 62                ,{"send_unsupported_version",      fun send_unsupported_version/1}
 63                ,{"sync_send_unsupported_version", fun sync_send_unsupported_version/1}
 64                ,{"send_bad_message",              fun send_bad_message/1}
 65                ,{"sync_send_bad_message",         fun sync_send_bad_message/1}
 66                ,{"send_list_bad_message",         fun send_list_bad_message/1}
 67                ,{"sync_send_list_bad_message",    fun sync_send_list_bad_message/1}
 68                ]]]} end
 69    }.
 70
 71setup() ->
 72    ok = meck:new(of_driver_handler_mock, [passthrough]),
 73    ConnTable = ets:new(conn_table, [set, public]),
 74    [application:set_env(of_driver, K, V) || {K, V} <-
 75                                [{callback_module, of_driver_handler_mock},
 76                                 {listen_port, ?LISTEN_PORT},
 77                                 {init_opt, ConnTable}]],
 78    ok = application:start(eenum),
 79    ok = application:start(of_protocol),
 80    ok = application:start(lager),
 81    ok = application:start(of_driver),
 82    Socket = connect(),
 83    {Socket, ConnTable}.
 84
 85cleanup({Socket, _ConnTable}) ->
 86    ok = gen_tcp:close(Socket),
 87    % wait for of_driver to exit before unloading mocks
 88    meck:wait(of_driver_handler_mock, terminate, '_', 1000),
 89    meck:unload().
 90
 91test_setup() ->
 92    meck:reset(of_driver_handler_mock).
 93
 94trace() ->
 95    dbg:start(),
 96    dbg:tracer(),
 97    dbg:p(all, c)
 98    % ,dbg:tpl(ofp_v4_encode, [])
 99    % ,dbg:tpl(ofp_v4_encode, [{'_', [], [{return_trace}]}])
100    % ,dbg:tpl(of_driver_utils, [])
101    % ,dbg:tpl(of_driver_connection, [])
102    ,dbg:tpl(of_driver_connection, [{'_', [], [{return_trace}]}])
103    % ,dbg:tpl(of_driver_datapath, [])
104    % ,dbg:tpl(gen_tcp, [])
105    % ,dbg:tpl(gen_tcp, [{'_', [], [{return_trace}]}])
106    % ,dbg:tpl(?MODULE, [])
107    .
108
109%%------------------------------------------------------------------------------
110
111set_xid() ->
112    NewXid = 9999,
113    Msg = of_msg_lib:get_features(4),
114    ?assertNotEqual(NewXid, Msg#ofp_message.xid),
115    NewMsg = of_driver:set_xid(Msg, NewXid),
116    ?assertEqual(NewXid, NewMsg#ofp_message.xid).
117
118main_connect() ->
119    ExpectedDatapathId = ?DATAPATH_ID,
120    meck:expect(of_driver_handler_mock, init,
121        fun(_IpAddr, DatapathId, Features, Version, _Connection, _Opt) ->
122            ?assertMatch(#ofp_features_reply{ 
123                            datapath_mac = ?DATAPATH_UNPARSED,
124                            datapath_id = ExpectedDatapathId}, Features),
125            ?assertEqual(DatapathId,?DATAPATH_HEX),
126            ?assertEqual(Version, ?VERSION),
127            {ok, callback_state} 
128        end),
129    meck:expect(of_driver_handler_mock, terminate, fun(_Reason, callback_state) -> ok end),
130    Socket = connect(ExpectedDatapathId),
131    gen_tcp:close(Socket),
132    ?assert(meck:validate(of_driver_handler_mock)).
133
134main_terminate() ->
135    ExpectedDatapathId = ?DATAPATH_ID,
136    ExpectedAuxId = 1,
137    meck:expect(of_driver_handler_mock, init,
138        fun(_IpAddr, DatapathId, Features, Version, _Connection, _Opt) ->
139            ?assertMatch(#ofp_features_reply{ 
140                            datapath_mac = ?DATAPATH_UNPARSED,
141                            datapath_id = ExpectedDatapathId}, Features),
142            ?assertEqual(DatapathId,?DATAPATH_HEX),
143            ?assertEqual(Version, ?VERSION),
144            {ok, callback_state} 
145        end),
146    meck:expect(of_driver_handler_mock, terminate, fun(_Reason, callback_state) -> ok end),
147    meck:expect(of_driver_handler_mock, handle_connect,
148        fun(_IpAddr, DatapathId, Features, Version, _Connection, AuxId, _Opt) ->
149            ?assertMatch(#ofp_features_reply{ 
150                            datapath_mac = ?DATAPATH_UNPARSED,
151                            datapath_id = ExpectedDatapathId}, Features),
152            ?assertEqual(DatapathId,?DATAPATH_HEX),
153            ?assertEqual(Version, ?VERSION),
154            ?assertEqual(AuxId, ExpectedAuxId),
155            {ok, aux_callback_state} 
156        end),
157    meck:expect(of_driver_handler_mock, handle_disconnect, fun(_Reason, aux_callback_state) -> ok end),
158    Socket = connect(ExpectedDatapathId),
159    _AuxSocket = connect(ExpectedDatapathId, ExpectedAuxId),
160    gen_tcp:close(Socket),
161    ?assert(meck:validate(of_driver_handler_mock)).
162
163early_message() ->
164    ExpectedDatapathId = ?DATAPATH_ID,
165    meck:expect(of_driver_handler_mock, init,
166        fun(_IpAddr, DatapathId, Features, Version, _Connection, _Opt) ->
167            ?assertMatch(#ofp_features_reply{ 
168                            datapath_mac = ?DATAPATH_UNPARSED,
169                            datapath_id = ExpectedDatapathId}, Features),
170            ?assertEqual(DatapathId,?DATAPATH_HEX),
171            ?assertEqual(Version, ?VERSION),
172            {ok, callback_state} 
173        end),
174    meck:expect(of_driver_handler_mock, terminate, fun(_Reason, callback_state) -> ok end),
175    {ok, Socket} = gen_tcp:connect({127,0,0,1}, ?LISTEN_PORT,
176                                            [binary, {active, false}], 5000),
177    send_msg(Socket, packet_in()),
178    send_msg(Socket, create_hello(?VERSION)),
179    {#ofp_message{type = hello}, Rest} = receive_msg(Socket, <<>>),
180    {#ofp_message{type = features_request, xid = FXID}, <<>>} = receive_msg(Socket, Rest),
181    send_msg(Socket, packet_in()),
182    send_msg(Socket, features_reply(FXID, ExpectedDatapathId, 0)),
183    gen_tcp:close(Socket),
184    ?assert(meck:validate(of_driver_handler_mock)).
185
186close_connection() ->
187    ExpectedDatapathId = ?DATAPATH_ID,
188    Me = self(),
189    meck:expect(of_driver_handler_mock, init,
190        fun(_IpAddr, _DatapathId, _Features, _Version, Connection, _Opt) ->
191            Me ! {connection, Connection},
192            {ok, callback_state} 
193        end),
194    meck:expect(of_driver_handler_mock, terminate, fun(_Reason, callback_state) -> ok end),
195    _Socket = connect(ExpectedDatapathId),
196    Conn = receive
197        {connection, C} ->
198            C
199    end,
200    of_driver:close_connection(Conn),
201    ?assert(meck:validate(of_driver_handler_mock)).
202
203gen_xid({_Socket, ConnTable}) ->
204    Connection = get_connection(ConnTable),
205    Count = 4,
206    Set = lists:foldl(
207                fun(_C, S) ->
208                    sets:add_element(of_driver:gen_xid(Connection), S)
209                end, sets:new(), lists:seq(1, Count)),
210    ?debugVal(sets:to_list(Set)),
211    ?assertEqual(Count, sets:size(Set)).
212
213aux_connect({_Socket, _ConnTable}) ->
214    ExpectedAuxId = 1,
215    meck:expect(of_driver_handler_mock, handle_connect,
216        fun(_IpAddr, DatapathId, Features, Version, _Connection, AuxId, _Opt) ->
217            % ?assertMatch(#ofp_features_reply{ 
218            %                 datapath_mac = ?DATAPATH_UNPARSED,
219            %                 datapath_id = ?DATAPATH_ID }, Features),
220            ?assertMatch(#ofp_features_reply{ 
221                            datapath_mac = ?DATAPATH_UNPARSED,
222                            datapath_id = ?DATAPATH_ID }, Features),
223            ?assertEqual(DatapathId,?DATAPATH_HEX),
224            ?assertEqual(Version, ?VERSION),
225            ?assertEqual(AuxId, ExpectedAuxId),
226            {ok, aux_callback_state} 
227        end),
228    meck:expect(of_driver_handler_mock, handle_disconnect, fun(_Reason, aux_callback_state) -> ok end),
229    AuxSocket = connect_aux(?DATAPATH_ID,ExpectedAuxId),
230    gen_tcp:close(AuxSocket),
231    ?assertNot(meck:called(of_driver_handler_mock, terminate, '_')),
232    ?assert(meck:validate(of_driver_handler_mock)).
233
234in_message({Socket, _ConnTable}) ->
235    meck:expect(of_driver_handler_mock, handle_message,
236                    fun(#ofp_message{type = packet_in}, State) -> {ok, State} end),
237    send_msg(Socket, packet_in()),
238    ?assert(meck:validate(of_driver_handler_mock)).
239
240echo_request({Socket, _ConnTable}) ->
241    Xid = 1234,
242    Data = <<>>,
243    send_msg(Socket, echo_request(Xid, Data)),
244    {#ofp_message{type = echo_reply, xid = Xid, body = Body}, <<>>} = receive_msg(Socket, <<>>),
245    ?assertMatch(#ofp_echo_reply{data = Data}, Body).
246
247echo_request2({Socket, _ConnTable}) ->
248    Xid = 1234,
249    Data = <<"somedata">>,
250    send_msg(Socket, echo_request(Xid, Data)),
251    {#ofp_message{type = echo_reply, xid = Xid, body = Body}, <<>>} = receive_msg(Socket, <<>>),
252    ?assertMatch(#ofp_echo_reply{data = Data}, Body).
253
254send({Socket, ConnTable}) ->
255    Hello = create_hello(4),
256    Connection = get_connection(ConnTable),
257    ok = of_driver:send(Connection, Hello),
258    {Recv, <<>>} = receive_msg(Socket, <<>>),
259    ?assertEqual(Hello, Recv),
260    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
261
262sync_send({Socket, ConnTable}) ->
263    Connection = get_connection(ConnTable),
264    Msg = of_msg_lib:get_features(4),
265    Future = future(of_driver, sync_send, [Connection, Msg]),
266    {#ofp_message{type = features_request, xid = RXID}, Rest} = receive_msg(Socket, <<>>),
267    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest),
268    send_msg(Socket, features_reply(RXID)),
269    send_msg(Socket, barrier_reply(BXID)),
270    {ok, Reply} = wait_future(Future),
271    ?assertMatch(#ofp_message{type = features_reply}, Reply),
272    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
273
274sync_send_no_reply({Socket, ConnTable}) ->
275    Connection = get_connection(ConnTable),
276    Msg = of_msg_lib:get_features(4),
277    Future = future(of_driver, sync_send, [Connection, Msg]),
278    {#ofp_message{type = features_request}, Rest} = receive_msg(Socket, <<>>),
279    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest),
280    send_msg(Socket, barrier_reply(BXID)),
281    {ok, Reply} = wait_future(Future),
282    ?assertEqual(noreply, Reply),
283    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
284
285sync_send_non_reply({Socket, ConnTable}) ->
286    % sync_send and message is received that is not a reply to the request
287    % (XID doesn't match).
288    meck:expect(of_driver_handler_mock, handle_message,
289                fun(#ofp_message{type = features_reply, xid = 9999}, State) ->
290                    {ok, State} 
291                end),
292    Connection = get_connection(ConnTable),
293    Msg = of_msg_lib:get_features(4),
294    Future = future(of_driver, sync_send, [Connection, Msg]),
295    {#ofp_message{type = features_request}, Rest} = receive_msg(Socket, <<>>),
296    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest),
297    send_msg(Socket, features_reply(9999)),
298    send_msg(Socket, barrier_reply(BXID)),
299    {ok, Reply} = wait_future(Future),
300    ?assertEqual(noreply, Reply),
301    ?assertEqual(1, meck:num_calls(of_driver_handler_mock, handle_message, '_')),
302    ?assert(meck:validate(of_driver_handler_mock)).
303
304sync_send_multipart({Socket, ConnTable}) ->
305    Connection = get_connection(ConnTable),
306    Msg = of_msg_lib:get_port_descriptions(4),
307    Future = future(of_driver, sync_send, [Connection, Msg]),
308
309    {#ofp_message{type = multipart_request, xid = RXID}, Rest} = receive_msg(Socket, <<>>),
310    {#ofp_message{type = barrier_request,   xid = BXID}, <<>>} = receive_msg(Socket, Rest),
311
312    send_msg(Socket, multipart_reply(ofp_port_desc_reply,RXID,[more])),
313    send_msg(Socket, multipart_reply(ofp_port_desc_reply,RXID)),
314    send_msg(Socket, barrier_reply(BXID)),
315
316    {ok, Reply} = wait_future(Future),
317    ?assertMatch(#ofp_message{type = multipart_reply,
318                              xid  = RXID,
319                              body = #ofp_port_desc_reply{ body = [#ofp_port{} = _InnerBody1,
320                                                                   #ofp_port{} = _InnerBody2] }
321                            }, Reply),
322    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
323
324send_list({Socket, ConnTable}) ->
325    Connection = get_connection(ConnTable),
326    Hello = create_hello(4),
327    Features = of_msg_lib:get_features(4),
328    ok = of_driver:send_list(Connection, [Hello, Features, Hello]),
329    {Recv0, Rest0} = receive_msg(Socket, <<>>),
330    {Recv1, Rest1} = receive_msg(Socket, Rest0),
331    {Recv2, <<>>} = receive_msg(Socket, Rest1),
332    ?assertMatch(#ofp_message{type = hello}, Recv0),
333    ?assertMatch(#ofp_message{type = features_request}, Recv1),
334    ?assertMatch(#ofp_message{type = hello}, Recv2).
335
336sync_send_list({Socket, ConnTable}) ->
337    Connection = get_connection(ConnTable),
338    Msg = of_msg_lib:get_features(4),
339    Future = future(of_driver, sync_send_list, [Connection, [Msg, Msg, Msg]]),
340    {#ofp_message{type = features_request, xid = RXID0}, Rest0} = receive_msg(Socket, <<>>),
341    {#ofp_message{type = features_request, xid = RXID1}, Rest1} = receive_msg(Socket, Rest0),
342    {#ofp_message{type = features_request, xid = RXID2}, Rest2} = receive_msg(Socket, Rest1),
343    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest2),
344    send_msg(Socket, features_reply(RXID0)),
345    send_msg(Socket, features_reply(RXID1)),
346    send_msg(Socket, features_reply(RXID2)),
347    send_msg(Socket, barrier_reply(BXID)),
348    {ok, [Reply0, Reply1, Reply2]} = wait_future(Future),
349    ?assertMatch({ok, #ofp_message{type = features_reply, xid = RXID0}}, Reply0),
350    ?assertMatch({ok, #ofp_message{type = features_reply, xid = RXID1}}, Reply1),
351    ?assertMatch({ok, #ofp_message{type = features_reply, xid = RXID2}}, Reply2),
352    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
353
354sync_send_list_no_reply({Socket, ConnTable}) ->
355    meck:expect(of_driver_handler_mock, handle_message,
356                fun(#ofp_message{type = features_reply, xid = 9999}, State) ->
357                    {ok, State} 
358                end),
359    Connection = get_connection(ConnTable),
360    Msg = of_msg_lib:get_features(4),
361    Future = future(of_driver, sync_send_list, [Connection, [Msg, Msg, Msg]]),
362    {#ofp_message{type = features_request, xid = _RXID0}, Rest0} = receive_msg(Socket, <<>>),
363    {#ofp_message{type = features_request, xid = _RXID1}, Rest1} = receive_msg(Socket, Rest0),
364    {#ofp_message{type = features_request, xid = RXID2}, Rest2} = receive_msg(Socket, Rest1),
365    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest2),
366    % no reply to first features request
367    send_msg(Socket, features_reply(9999)),
368    send_msg(Socket, features_reply(RXID2)),
369    send_msg(Socket, barrier_reply(BXID)),
370    {ok, [Reply0, Reply1, Reply2]} = wait_future(Future),
371    ?assertMatch({ok, noreply}, Reply0),
372    ?assertMatch({ok, noreply}, Reply1),
373    ?assertMatch({ok, #ofp_message{type = features_reply, xid = RXID2}}, Reply2),
374    ?assert(meck:validate(of_driver_handler_mock)).
375
376sync_send_list_multipart({Socket, ConnTable}) ->
377    Connection = get_connection(ConnTable),
378
379    Msg  = of_msg_lib:get_port_descriptions(4),
380    Msg2 = of_msg_lib:get_queue_statistics(4,any,all),
381
382    Future = future(of_driver, sync_send_list, [Connection, [Msg, Msg2]]),
383
384    %% {ofp_message,4,multipart_request,20,{ofp_port_desc_request,[]}}
385    {#ofp_message{type = multipart_request, xid = RXID0}, Rest0} = receive_msg(Socket, <<>>),
386    {#ofp_message{type = multipart_request, xid = RXID1}, Rest1} = receive_msg(Socket, Rest0),
387    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest1),
388
389    send_msg(Socket, multipart_reply(ofp_port_desc_reply,RXID0,[more])),
390    send_msg(Socket, multipart_reply(ofp_port_desc_reply,RXID0)),
391
392    send_msg(Socket, multipart_reply(ofp_queue_stats_reply,RXID1,[more])),
393    send_msg(Socket, multipart_reply(ofp_queue_stats_reply,RXID1)),
394
395    send_msg(Socket, barrier_reply(BXID)),
396
397    {ok, [Reply0, Reply1]} = wait_future(Future),
398
399    %% io:format("Reply0 ~p\n",[Reply0]),
400    %% io:format("Reply1 ~p\n",[Reply1]),
401
402    ?assertMatch({ok,#ofp_message{type = multipart_reply,
403                                 xid  = RXID0,
404                                  body = #ofp_port_desc_reply{ body = [#ofp_port{} = _InnerBody1,
405                                                                       #ofp_port{} = _InnerBody2] }
406                                 }}, Reply0),
407    ?assertMatch({ok,#ofp_message{type = multipart_reply,
408                                 xid  = RXID1,
409                                  body = #ofp_queue_stats_reply{ body = [#ofp_queue_stats{} = _InnerBody3,
410                                                                         #ofp_queue_stats{} = _InnerBody4] }
411                                }}, Reply1),
412
413    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
414
415multiple_sync_send({Socket, ConnTable}) ->
416    Connection = get_connection(ConnTable),
417    Msg = of_msg_lib:get_features(4),
418    Future1 = future(of_driver, sync_send, [Connection, Msg]),
419    {#ofp_message{type = features_request, xid = RXID1}, Rest1} = receive_msg(Socket, <<>>),
420    {#ofp_message{type = barrier_request, xid = BXID1}, <<>>} = receive_msg(Socket, Rest1),
421    Future2 = future(of_driver, sync_send, [Connection, Msg]),
422    {#ofp_message{type = features_request, xid = RXID2}, Rest2} = receive_msg(Socket, <<>>),
423    {#ofp_message{type = barrier_request, xid = BXID2}, <<>>} = receive_msg(Socket, Rest2),
424    send_msg(Socket, features_reply(RXID2)),
425    send_msg(Socket, features_reply(RXID1)),
426    send_msg(Socket, barrier_reply(BXID1)),
427    send_msg(Socket, barrier_reply(BXID2)),
428    {ok, Reply1} = wait_future(Future1),
429    {ok, Reply2} = wait_future(Future2),
430    ?assertMatch(#ofp_message{type = features_reply, xid = RXID1}, Reply1),
431    ?assertMatch(#ofp_message{type = features_reply, xid = RXID2}, Reply2),
432    ?assertNot(meck:called(of_driver_handler_mock, handle_message, '_')).
433
434send_unsupported_version({_Socket, ConnTable}) ->
435    Connection = get_connection(ConnTable),
436    BadMsg = (of_msg_lib:get_features(4))#ofp_message{version = -1},
437    R = of_driver:send(Connection, BadMsg),
438    ?assertMatch({error, unsupported_version}, R).
439
440sync_send_unsupported_version({Socket, ConnTable}) ->
441    Connection = get_connection(ConnTable),
442    BadMsg = (of_msg_lib:get_features(4))#ofp_message{version = -1},
443    Future = future(of_driver, sync_send, [Connection, BadMsg]),
444    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, <<>>),
445    send_msg(Socket, barrier_reply(BXID)),
446    ?assertMatch({error, unsupported_version}, wait_future(Future)).
447
448send_bad_message({_Socket, ConnTable}) ->
449    Connection = get_connection(ConnTable),
450    R = of_driver:send(Connection, #ofp_message{type = not_a_valid_message, version = 4}),
451    ?assertMatch({error, {bad_message, _}}, R).
452
453sync_send_bad_message({Socket, ConnTable}) ->
454    Connection = get_connection(ConnTable),
455    Future = future(of_driver, sync_send, [Connection, #ofp_message{type = not_a_valid_message, version = 4}]),
456    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, <<>>),
457    send_msg(Socket, barrier_reply(BXID)),
458    ?assertMatch({error, _}, wait_future(Future)).
459
460send_list_bad_message({Socket, ConnTable}) ->
461    Connection = get_connection(ConnTable),
462    GoodMsg = of_msg_lib:get_features(4),
463    BadMsg = #ofp_message{type = not_a_valid_message, version = 4},
464    R = of_driver:send_list(Connection, [GoodMsg, BadMsg]),
465    ?assertMatch({error, [ok, {error, {bad_message, _}}]}, R),
466    {#ofp_message{type = features_request}, <<>>} = receive_msg(Socket, <<>>).
467
468sync_send_list_bad_message({Socket, ConnTable}) ->
469    Connection = get_connection(ConnTable),
470    GoodMsg = of_msg_lib:get_features(4),
471    BadMsg = #ofp_message{type = not_a_valid_message, version = 4},
472    Future = future(of_driver, sync_send_list, [Connection, [GoodMsg, BadMsg]]),
473    {#ofp_message{type = features_request}, Rest} = receive_msg(Socket, <<>>),
474    {#ofp_message{type = barrier_request, xid = BXID}, <<>>} = receive_msg(Socket, Rest),
475    send_msg(Socket, barrier_reply(BXID)),
476    Reply = wait_future(Future),
477    ?assertMatch({error, [{ok, noreply}, {error,{bad_message, _}}]}, Reply).
478
479%%------------------------------------------------------------------------------
480
481get_connection(ConnTable) ->
482    get_connection(ConnTable, 0).
483    
484get_connection(ConnTable, AuxId) ->
485    [{AuxId, Connection}] = ets:lookup(ConnTable, AuxId),
486    Connection.
487
488receive_msg(Socket, <<>>) ->
489    {ok, MsgBin} = gen_tcp:recv(Socket, 0),
490    {ok, OfpMsg, LeftOvers} = of_protocol:decode(<<MsgBin/binary>>),
491%   ?debugFmt("~n~nreceive message: ~p~n", [OfpMsg]),
492    {OfpMsg, LeftOvers};
493receive_msg(_Socket, MsgBin) ->
494    {ok, OfpMsg, LeftOvers} = of_protocol:decode(<<MsgBin/binary>>),
495%   ?debugFmt("~n~nreceive message: ~p~n", [OfpMsg]),
496    {OfpMsg, LeftOvers}.
497
498send_msg(Socket, Msg) ->
499%   ?debugFmt("~n~nsend message: ~p~n", [Msg]),
500    {ok, Bin} = of_protocol:encode(Msg),
501    ok = gen_tcp:send(Socket, Bin).
502
503connect_aux(AuxId) ->
504    connect(0, AuxId).
505
506connect_aux(DatapathID,AuxId) ->
507    connect(DatapathID,AuxId).
508
509connect() ->
510    connect(0, 0).
511
512connect(DatapathId) ->
513    connect(DatapathId, 0).
514
515connect(DatapathId, AuxId) ->
516    {ok, Socket} = gen_tcp:connect({127,0,0,1}, ?LISTEN_PORT,
517                                            [binary, {active, false}], 5000),
518    send_msg(Socket, create_hello(?VERSION)),
519    {#ofp_message{type = hello}, Rest} = receive_msg(Socket, <<>>),
520    {#ofp_message{type = features_request, xid = XID}, <<>>} = receive_msg(Socket, Rest),
521    send_msg(Socket, features_reply(XID, DatapathId, AuxId)),
522    Socket.
523
524future(M, F, A) ->
525    Token = make_ref(),
526    Parent = self(),
527    spawn(fun() -> future_call(Parent, Token, M, F, A) end),
528    Token.
529
530wait_future(Token) ->
531    receive
532        {future, Token, R} -> R
533    end.
534
535future_call(Parent, Token, M, F, A) ->
536    R = apply(M, F, A),
537    Parent ! {future, Token, R}.
538
539%%------------------------------------------------------------------------------
540
541create_hello(Version) ->
542        #ofp_message{version = Version, xid = 0,
543                type = hello,
544                body = #ofp_hello{elements = [{versionbitmap, [Version]}]}}.
545
546barrier_reply(XID) ->
547    #ofp_message{
548        version = ?VERSION,
549        type = barrier_reply,
550        xid = XID,
551        body = #ofp_barrier_reply{}
552    }.
553
554features_reply(XID) ->
555    features_reply(XID, ?DATAPATH_ID, 0).
556
557features_reply(XID, DatapathId, AuxId) ->
558    #ofp_message{
559        version = ?VERSION,
560        type = features_reply,
561        xid = XID,
562        body = #ofp_features_reply{
563            datapath_mac = ?DATAPATH_UNPARSED,
564            datapath_id = DatapathId,
565            n_buffers = 0,
566            n_tables = 255,
567            auxiliary_id = AuxId,
568            capabilities = [flow_stats,table_stats,port_stats,group_stats,queue_stats]
569        }
570    }.
571
572echo_request(XID, Data) ->
573    #ofp_message{
574        version = ?VERSION,
575        type = echo_request,
576        xid = XID,
577        body = #ofp_echo_request{
578            data = Data
579        }
580    }.
581
582packet_in() ->
583    #ofp_message{
584        version = ?VERSION, 
585        type = packet_in,
586        body = #ofp_packet_in{
587            buffer_id = no_buffer,
588            reason = action,
589            table_id = 1,
590            cookie = <<0:64>>,
591            match = #ofp_match{fields = []},
592            data = <<"abcd">>
593        }
594    }.
595
596multipart_reply(Type,Xid) ->
597    multipart_reply(Type,Xid,[]).
598    
599multipart_reply(ofp_port_desc_reply,Xid,Flags) ->
600    #ofp_message{
601        version = ?VERSION,
602        type = multipart_reply,
603        xid = Xid,
604        body = #ofp_port_desc_reply{ flags = Flags,
605                                     body = [{ofp_port,714,
606                                                 <<14,7,73,8,219,149>>,
607                                                 <<"Port714">>,[],
608                                                 [live],
609                                                 ['100mb_fd',copper,autoneg],
610                                                 [copper,autoneg],
611                                                 ['100mb_fd',copper,autoneg],
612                                                 ['100mb_fd',copper,autoneg],
613                                                 5000,5000}]
614                                    }
615    };
616multipart_reply(ofp_queue_stats_reply,Xid,_Flags) ->
617    #ofp_message{
618        version = ?VERSION,
619        type = multipart_reply,
620        xid = Xid,
621        body = #ofp_queue_stats_reply{flags = [more],
622                                      body  = [{ofp_queue_stats,1,664,0,0,0,13980,651246000}]
623                }
624    }.